sigs.k8s.io/cluster-api-provider-aws@v1.5.5/cmd/clusterawsadm/gc/gc.go (about)

     1  /*
     2  Copyright 2022 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package gc
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
    24  	"k8s.io/apimachinery/pkg/runtime"
    25  	_ "k8s.io/client-go/plugin/pkg/client/auth/exec"
    26  	_ "k8s.io/client-go/plugin/pkg/client/auth/oidc"
    27  	"k8s.io/client-go/tools/clientcmd"
    28  	"sigs.k8s.io/controller-runtime/pkg/client"
    29  
    30  	infrav1 "sigs.k8s.io/cluster-api-provider-aws/api/v1beta1"
    31  	ekscontrolplanev1 "sigs.k8s.io/cluster-api-provider-aws/controlplane/eks/api/v1beta1"
    32  	expinfrav1 "sigs.k8s.io/cluster-api-provider-aws/exp/api/v1beta1"
    33  	"sigs.k8s.io/cluster-api-provider-aws/pkg/annotations"
    34  	clusterv1 "sigs.k8s.io/cluster-api/api/v1beta1"
    35  	"sigs.k8s.io/cluster-api/controllers/external"
    36  	"sigs.k8s.io/cluster-api/util/patch"
    37  )
    38  
    39  var (
    40  	scheme = runtime.NewScheme()
    41  )
    42  
    43  func init() {
    44  	_ = clusterv1.AddToScheme(scheme)
    45  	_ = infrav1.AddToScheme(scheme)
    46  	_ = ekscontrolplanev1.AddToScheme(scheme)
    47  }
    48  
    49  // CmdProcessor handles the garbage collection commands.
    50  type CmdProcessor struct {
    51  	client client.Client
    52  
    53  	clusterName string
    54  	namespace   string
    55  }
    56  
    57  // GCInput holds the configuration for the command processor.
    58  type GCInput struct {
    59  	ClusterName    string
    60  	Namespace      string
    61  	KubeconfigPath string
    62  }
    63  
    64  // CmdProcessorOption is a function type to supply options when creating the command processor.
    65  type CmdProcessorOption func(proc *CmdProcessor) error
    66  
    67  // WithClient is an option that enable you to explicitly supply a client.
    68  func WithClient(client client.Client) CmdProcessorOption {
    69  	return func(proc *CmdProcessor) error {
    70  		proc.client = client
    71  
    72  		return nil
    73  	}
    74  }
    75  
    76  // New creates a new instance of the command processor.
    77  func New(input GCInput, opts ...CmdProcessorOption) (*CmdProcessor, error) {
    78  	cmd := &CmdProcessor{
    79  		clusterName: input.ClusterName,
    80  		namespace:   input.Namespace,
    81  	}
    82  
    83  	for _, opt := range opts {
    84  		if err := opt(cmd); err != nil {
    85  			return nil, fmt.Errorf("applying option: %w", err)
    86  		}
    87  	}
    88  
    89  	if cmd.client == nil {
    90  		config, err := clientcmd.BuildConfigFromFlags("", input.KubeconfigPath)
    91  		if err != nil {
    92  			return nil, fmt.Errorf("building client config: %w", err)
    93  		}
    94  
    95  		cl, err := client.New(config, client.Options{Scheme: scheme})
    96  		if err != nil {
    97  			return nil, fmt.Errorf("creating new client: %w", err)
    98  		}
    99  
   100  		cmd.client = cl
   101  	}
   102  
   103  	return cmd, nil
   104  }
   105  
   106  // Enable is used to enable external resource garbage collection for a cluster.
   107  func (c *CmdProcessor) Enable(ctx context.Context) error {
   108  	if err := c.setAnnotationAndPatch(ctx, "true"); err != nil {
   109  		return fmt.Errorf("setting gc annotation to true: %w", err)
   110  	}
   111  
   112  	return nil
   113  }
   114  
   115  // Disable is used to disable external resource garbage collection for a cluster.
   116  func (c *CmdProcessor) Disable(ctx context.Context) error {
   117  	if err := c.setAnnotationAndPatch(ctx, "false"); err != nil {
   118  		return fmt.Errorf("setting gc annotation to false: %w", err)
   119  	}
   120  
   121  	return nil
   122  }
   123  
   124  func (c *CmdProcessor) setAnnotationAndPatch(ctx context.Context, annotationValue string) error {
   125  	infraObj, err := c.getInfraCluster(ctx)
   126  	if err != nil {
   127  		return err
   128  	}
   129  
   130  	patchHelper, err := patch.NewHelper(infraObj, c.client)
   131  	if err != nil {
   132  		return fmt.Errorf("creating patch helper: %w", err)
   133  	}
   134  
   135  	annotations.Set(infraObj, expinfrav1.ExternalResourceGCAnnotation, annotationValue)
   136  
   137  	if err := patchHelper.Patch(ctx, infraObj); err != nil {
   138  		return fmt.Errorf("patching infra cluster with gc annotation: %w", err)
   139  	}
   140  
   141  	return nil
   142  }
   143  
   144  func (c *CmdProcessor) getInfraCluster(ctx context.Context) (*unstructured.Unstructured, error) {
   145  	cluster := &clusterv1.Cluster{}
   146  
   147  	key := client.ObjectKey{
   148  		Name:      c.clusterName,
   149  		Namespace: c.namespace,
   150  	}
   151  
   152  	if err := c.client.Get(ctx, key, cluster); err != nil {
   153  		return nil, fmt.Errorf("getting capi cluster %s/%s: %w", c.namespace, c.clusterName, err)
   154  	}
   155  
   156  	ref := cluster.Spec.InfrastructureRef
   157  	obj, err := external.Get(ctx, c.client, ref, cluster.Namespace)
   158  	if err != nil {
   159  		return nil, fmt.Errorf("getting infra cluster %s/%s: %w", ref.Namespace, ref.Name, err)
   160  	}
   161  
   162  	return obj, nil
   163  }