@@ -12,6 +12,7 @@ import (
1212 "github.com/aws/aws-sdk-go/aws/session"
1313 "github.com/aws/aws-sdk-go/service/ec2"
1414 "github.com/aws/aws-sdk-go/service/elbv2"
15+ "github.com/aws/aws-sdk-go/service/s3"
1516 "github.com/sirupsen/logrus"
1617 "k8s.io/apimachinery/pkg/util/wait"
1718 "k8s.io/utils/ptr"
@@ -30,12 +31,15 @@ var (
3031 _ clusterapi.PreProvider = (* Provider )(nil )
3132 _ clusterapi.InfraReadyProvider = (* Provider )(nil )
3233 _ clusterapi.BootstrapDestroyer = (* Provider )(nil )
34+ _ clusterapi.PostDestroyer = (* Provider )(nil )
3335
3436 errNotFound = errors .New ("not found" )
3537)
3638
3739// Provider implements AWS CAPI installation.
38- type Provider struct {}
40+ type Provider struct {
41+ bestEffortDeleteIgnition bool
42+ }
3943
4044// Name gives the name of the provider, AWS.
4145func (* Provider ) Name () string { return awstypes .Name }
@@ -220,25 +224,28 @@ func getHostedZoneIDForNLB(ctx context.Context, awsSession *session.Session, reg
220224
221225// DestroyBootstrap removes aws bootstrap resources not handled
222226// by the deletion of the bootstrap machine by the capi controllers.
223- func (* Provider ) DestroyBootstrap (ctx context.Context , in clusterapi.BootstrapDestroyInput ) error {
224- if err := removeSSHRule (ctx , in .Client , in .Metadata .InfraID ); err != nil {
225- return fmt .Errorf ("failed to remove bootstrap SSH rule: %w" , err )
226- }
227- return nil
228- }
229-
230- // removeSSHRule removes the SSH rule for accessing the bootstrap node
231- // by removing the rule from the cluster spec and updating the object.
232- func removeSSHRule (ctx context.Context , cl k8sClient.Client , infraID string ) error {
227+ func (p * Provider ) DestroyBootstrap (ctx context.Context , in clusterapi.BootstrapDestroyInput ) error {
233228 awsCluster := & capa.AWSCluster {}
234229 key := k8sClient.ObjectKey {
235- Name : infraID ,
230+ Name : in . Metadata . InfraID ,
236231 Namespace : capiutils .Namespace ,
237232 }
238- if err := cl .Get (ctx , key , awsCluster ); err != nil {
233+ if err := in . Client .Get (ctx , key , awsCluster ); err != nil {
239234 return fmt .Errorf ("failed to get AWSCluster: %w" , err )
240235 }
241236
237+ // Save this value for use in the post-destroy hook since we don't have capi running anymore by that point.
238+ p .bestEffortDeleteIgnition = ptr .Deref (awsCluster .Spec .S3Bucket .BestEffortDeleteObjects , false )
239+
240+ if err := removeSSHRule (ctx , in .Client , in .Metadata .InfraID , awsCluster ); err != nil {
241+ return fmt .Errorf ("failed to remove bootstrap SSH rule: %w" , err )
242+ }
243+ return nil
244+ }
245+
246+ // removeSSHRule removes the SSH rule for accessing the bootstrap node
247+ // by removing the rule from the cluster spec and updating the object.
248+ func removeSSHRule (ctx context.Context , cl k8sClient.Client , infraID string , awsCluster * capa.AWSCluster ) error {
242249 postBootstrapRules := []capa.IngressRule {}
243250 for _ , rule := range awsCluster .Spec .NetworkSpec .AdditionalControlPlaneIngressRules {
244251 if strings .EqualFold (rule .Description , awsmanifest .BootstrapSSHDescription ) {
@@ -254,6 +261,10 @@ func removeSSHRule(ctx context.Context, cl k8sClient.Client, infraID string) err
254261 }
255262 logrus .Debug ("Updated AWSCluster to remove bootstrap SSH rule" )
256263
264+ key := k8sClient.ObjectKey {
265+ Name : infraID ,
266+ Namespace : capiutils .Namespace ,
267+ }
257268 timeout := 15 * time .Minute
258269 untilTime := time .Now ().Add (timeout )
259270 warnTime := time .Now ().Add (5 * time .Minute )
@@ -299,3 +310,41 @@ func removeSSHRule(ctx context.Context, cl k8sClient.Client, infraID string) err
299310
300311 return nil
301312}
313+
314+ // PostDestroy deletes the ignition bucket after capi stopped running, so it won't try to reconcile the bucket.
315+ func (p * Provider ) PostDestroy (ctx context.Context , in clusterapi.PostDestroyerInput ) error {
316+ region := in .Metadata .AWS .Region
317+ session , err := awsconfig .GetSessionWithOptions (
318+ awsconfig .WithRegion (region ),
319+ awsconfig .WithServiceEndpoints (region , in .Metadata .AWS .ServiceEndpoints ),
320+ )
321+ if err != nil {
322+ return fmt .Errorf ("failed to create aws session: %w" , err )
323+ }
324+
325+ bucketName := fmt .Sprintf ("openshift-bootstrap-data-%s" , in .Metadata .InfraID )
326+ if err := removeS3Bucket (ctx , session , bucketName ); err != nil {
327+ if p .bestEffortDeleteIgnition {
328+ logrus .Warnf ("failed to delete ignition bucket %s: %v" , bucketName , err )
329+ return nil
330+ }
331+ return fmt .Errorf ("failed to delete ignition bucket %s: %w" , bucketName , err )
332+ }
333+
334+ return nil
335+ }
336+
337+ // removeS3Bucket deletes an s3 bucket given its name.
338+ func removeS3Bucket (ctx context.Context , session * session.Session , bucketName string ) error {
339+ client := s3 .New (session )
340+ _ , err := client .DeleteBucketWithContext (ctx , & s3.DeleteBucketInput {Bucket : aws .String (bucketName )})
341+ if err != nil {
342+ var awsErr awserr.Error
343+ if errors .As (err , & awsErr ) && awsErr .Code () == s3 .ErrCodeNoSuchBucket {
344+ logrus .Debugf ("bucket %q already deleted" , bucketName )
345+ return nil
346+ }
347+ return err
348+ }
349+ return nil
350+ }
0 commit comments