Skip to content

Commit

Permalink
Add cleanup for VPC load balancers
Browse files Browse the repository at this point in the history
  • Loading branch information
Amulyam24 committed Feb 13, 2023
1 parent 3e30fa4 commit 2d4fde4
Show file tree
Hide file tree
Showing 4 changed files with 210 additions and 0 deletions.
4 changes: 4 additions & 0 deletions internal/ibmcloud-janitor/resources/types.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ var PowervsResources = []Resource{

var VpcResources = []Resource{
VPCInstance{},
VPCLoadBalancer{},
VPCNetwork{},
VPCs{},
}
Expand Down Expand Up @@ -74,6 +75,9 @@ type VPC interface {
GetSubnetPublicGateway(options *vpcv1.GetSubnetPublicGatewayOptions) (*vpcv1.PublicGateway, *core.DetailedResponse, error)
DeletePublicGateway(options *vpcv1.DeletePublicGatewayOptions) (*core.DetailedResponse, error)
UnsetSubnetPublicGateway(options *vpcv1.UnsetSubnetPublicGatewayOptions) (*core.DetailedResponse, error)
DeleteLoadBalancer(options *vpcv1.DeleteLoadBalancerOptions) (*core.DetailedResponse, error)
ListLoadBalancers(options *vpcv1.ListLoadBalancersOptions) (*vpcv1.LoadBalancerCollection, *core.DetailedResponse, error)
GetLoadBalancer(options *vpcv1.GetLoadBalancerOptions) (result *vpcv1.LoadBalancer, response *core.DetailedResponse, err error)
}

type ServiceIDClient interface {
Expand Down
67 changes: 67 additions & 0 deletions internal/ibmcloud-janitor/resources/util.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package resources

import (
"net/url"

"github.com/pkg/errors"
)

// pagingHelper is used while listing resources. It parses and fetches the start token for
// getting the next set of resources if nextURL is returned by func f.
// func f has start as a parameter and returns the following:
// isDone bool - true denotes that iterating is not needed as there is no next set of resources
// nextURL string - denotes a URL for a page of resources which is parsed for fetching start token for next iteration
// e error - will break and return error if e is not nil
func pagingHelper(f func(string) (bool, string, error)) (err error) {
start := ""

getStartToken := func(nextURL string) (string, error) {
url, err := url.Parse(nextURL)
if err != nil || url == nil {
return "", errors.Wrapf(err, "failed to parse next url for getting next resources")
}

start := url.Query().Get("start")
return start, nil
}

for {
isDone, nextURL, e := f(start)

if e != nil {
err = e
break
}

if isDone {
break
}

if nextURL != "" {
start, err = getStartToken(nextURL)
if err != nil {
break
}
} else {
break
}
}

return
}
12 changes: 12 additions & 0 deletions internal/ibmcloud-janitor/resources/vpc_client.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,18 @@ func (c *IBMVPCClient) UnsetSubnetPublicGateway(options *vpcv1.UnsetSubnetPublic
return c.vpcService.UnsetSubnetPublicGateway(options)
}

func (c *IBMVPCClient) DeleteLoadBalancer(options *vpcv1.DeleteLoadBalancerOptions) (*core.DetailedResponse, error) {
return c.vpcService.DeleteLoadBalancer(options)
}

func (c *IBMVPCClient) ListLoadBalancers(options *vpcv1.ListLoadBalancersOptions) (*vpcv1.LoadBalancerCollection, *core.DetailedResponse, error) {
return c.vpcService.ListLoadBalancers(options)
}

func (c *IBMVPCClient) GetLoadBalancer(options *vpcv1.GetLoadBalancerOptions) (result *vpcv1.LoadBalancer, response *core.DetailedResponse, err error) {
return c.vpcService.GetLoadBalancer(options)
}

// Creates a new VPC Client
func NewVPCClient(options *CleanupOptions) (*IBMVPCClient, error) {
client := &IBMVPCClient{}
Expand Down
127 changes: 127 additions & 0 deletions internal/ibmcloud-janitor/resources/vpc_lbs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
/*
Copyright 2022 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package resources

import (
"strings"
"time"

"github.com/IBM/vpc-go-sdk/vpcv1"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
kerrors "k8s.io/apimachinery/pkg/util/errors"
"k8s.io/apimachinery/pkg/util/wait"
)

type VPCLoadBalancer struct{}

var (
lbDeletionTimeout = time.Minute * 4
lbPollingInterval = time.Second * 30
resourceLogger *logrus.Entry
)

// Cleans up the load balancers in a given region
func (VPCLoadBalancer) cleanup(options *CleanupOptions) error {
resourceLogger = logrus.WithFields(logrus.Fields{"resource": options.Resource.Name})
resourceLogger.Info("Cleaning up the load balancers")
client, err := NewVPCClient(options)
if err != nil {
return errors.Wrap(err, "couldn't create VPC client")
}

var deletedLBList []string
var errs []error

f := func(start string) (bool, string, error) {
listLbOpts := &vpcv1.ListLoadBalancersOptions{}
if start != "" {
listLbOpts.Start = &start
}

loadBalancers, _, err := client.ListLoadBalancers(listLbOpts)
if err != nil {
return false, "", errors.Wrap(err, "failed to list the load balancers")
}

if loadBalancers == nil || len(loadBalancers.LoadBalancers) <= 0 {
resourceLogger.Info("there are no available load balancers to delete")
return true, "", nil
}

for _, lb := range loadBalancers.LoadBalancers {
if *lb.ResourceGroup.ID == client.ResourceGroupID {
if _, err := client.DeleteLoadBalancer(&vpcv1.DeleteLoadBalancerOptions{
ID: lb.ID,
}); err != nil {
resourceLogger.WithField("name", *lb.Name).Error("failed to delete load balancer")
errs = append(errs, err)
continue
}
deletedLBList = append(deletedLBList, *lb.ID)
resourceLogger.WithField("name", *lb.Name).Info("load balancer deletetion triggered")
}
}

if loadBalancers.Next != nil && *loadBalancers.Next.Href != "" {
return false, *loadBalancers.Next.Href, nil
}

if len(errs) > 0 {
return false, "", kerrors.NewAggregate(errs)
}

return true, "", nil
}

if err = pagingHelper(f); err != nil {
return errors.Wrapf(err, "failed to clean up the load balancers")
}

// check if the LBs are properly deleted
if err := checkLBs(deletedLBList, client); err != nil {
return errors.Wrapf(err, "failed to verify the deletion of load balancers")
}

resourceLogger.Info("Successfully deleted the load balancers")
return nil
}

func checkLBs(list []string, client *IBMVPCClient) error {
var errs []error
check := func(id string) error {
return wait.PollImmediate(lbPollingInterval, lbDeletionTimeout, func() (bool, error) {
_, _, err := client.GetLoadBalancer(&vpcv1.GetLoadBalancerOptions{ID: &id})
if err != nil && strings.Contains(err.Error(), "cannot be found") {
return true, nil
}
return false, err
})
}

for _, lb := range list {
if err := check(lb); err != nil {
resourceLogger.WithField("ID", lb).Error("failed to check the deletion of load balancer")
errs = append(errs, err)
}
}

if len(errs) > 0 {
return kerrors.NewAggregate(errs)
}
return nil
}

0 comments on commit 2d4fde4

Please sign in to comment.