github.com/openshift/installer@v1.4.17/pkg/destroy/powervs/cloud-instance.go (about) 1 package powervs 2 3 import ( 4 "context" 5 "fmt" 6 "math" 7 "strings" 8 "time" 9 10 "github.com/IBM/go-sdk-core/v5/core" 11 "github.com/IBM/vpc-go-sdk/vpcv1" 12 "k8s.io/apimachinery/pkg/util/wait" 13 ) 14 15 const ( 16 cloudInstanceTypeName = "cloudInstance" 17 ) 18 19 // listCloudInstances lists instances in the cloud server. 20 func (o *ClusterUninstaller) listCloudInstances() (cloudResources, error) { 21 o.Logger.Debugf("Listing virtual Cloud service instances") 22 23 ctx, cancel := o.contextWithTimeout() 24 defer cancel() 25 26 options := o.vpcSvc.NewListInstancesOptions() 27 28 // https://raw.githubusercontent.com/IBM/vpc-go-sdk/master/vpcv1/vpc_v1.go 29 resources, _, err := o.vpcSvc.ListInstancesWithContext(ctx, options) 30 if err != nil { 31 o.Logger.Warnf("Error o.vpcSvc.ListInstancesWithContext: %v", err) 32 return nil, err 33 } 34 35 var foundOne = false 36 37 result := []cloudResource{} 38 for _, instance := range resources.Instances { 39 if strings.Contains(*instance.Name, o.InfraID) { 40 foundOne = true 41 o.Logger.Debugf("listCloudInstances: FOUND: %s, %s, %s", *instance.ID, *instance.Name, *instance.Status) 42 result = append(result, cloudResource{ 43 key: *instance.ID, 44 name: *instance.Name, 45 status: *instance.Status, 46 typeName: cloudInstanceTypeName, 47 id: *instance.ID, 48 }) 49 } 50 } 51 if !foundOne { 52 o.Logger.Debugf("listCloudInstances: NO matching virtual instance against: %s", o.InfraID) 53 for _, instance := range resources.Instances { 54 o.Logger.Debugf("listCloudInstances: only found virtual instance: %s", *instance.Name) 55 } 56 } 57 58 return cloudResources{}.insert(result...), nil 59 } 60 61 // destroyCloudInstance deletes a given instance. 62 func (o *ClusterUninstaller) destroyCloudInstance(item cloudResource) error { 63 var ( 64 ctx context.Context 65 err error 66 getInstanceOptions *vpcv1.GetInstanceOptions 67 deleteInstanceOptions *vpcv1.DeleteInstanceOptions 68 response *core.DetailedResponse 69 ) 70 71 ctx, cancel := o.contextWithTimeout() 72 defer cancel() 73 74 getInstanceOptions = o.vpcSvc.NewGetInstanceOptions(item.id) 75 76 _, _, err = o.vpcSvc.GetInstanceWithContext(ctx, getInstanceOptions) 77 if err != nil { 78 o.deletePendingItems(item.typeName, []cloudResource{item}) 79 o.Logger.Infof("Deleted Cloud Instance %q", item.name) 80 return nil 81 } 82 83 o.Logger.Debugf("Deleting Cloud instance %q", item.name) 84 85 deleteInstanceOptions = o.vpcSvc.NewDeleteInstanceOptions(item.id) 86 87 response, err = o.vpcSvc.DeleteInstanceWithContext(ctx, deleteInstanceOptions) 88 if err != nil { 89 o.Logger.Infof("Error: o.vpcSvc.DeleteInstanceWithContext: %q %q", err, response) 90 return err 91 } 92 93 o.deletePendingItems(item.typeName, []cloudResource{item}) 94 o.Logger.Infof("Deleted Cloud Instance %q", item.name) 95 96 return nil 97 } 98 99 // destroyCloudInstances searches for Cloud instances that have a name that starts with 100 // the cluster's infra ID. 101 func (o *ClusterUninstaller) destroyCloudInstances() error { 102 firstPassList, err := o.listCloudInstances() 103 if err != nil { 104 return err 105 } 106 107 if len(firstPassList.list()) == 0 { 108 return nil 109 } 110 111 items := o.insertPendingItems(cloudInstanceTypeName, firstPassList.list()) 112 ctx, cancel := o.contextWithTimeout() 113 defer cancel() 114 for _, item := range items { 115 select { 116 case <-ctx.Done(): 117 o.Logger.Debugf("destroyCloudInstances: case <-ctx.Done()") 118 return o.Context.Err() // we're cancelled, abort 119 default: 120 } 121 122 backoff := wait.Backoff{ 123 Duration: 15 * time.Second, 124 Factor: 1.1, 125 Cap: leftInContext(ctx), 126 Steps: math.MaxInt32} 127 err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) { 128 err2 := o.destroyCloudInstance(item) 129 if err2 == nil { 130 return true, err2 131 } 132 o.errorTracker.suppressWarning(item.key, err2, o.Logger) 133 return false, err2 134 }) 135 if err != nil { 136 o.Logger.Fatal("destroyCloudInstances: ExponentialBackoffWithContext (destroy) returns ", err) 137 } 138 } 139 140 if items = o.getPendingItems(cloudInstanceTypeName); len(items) > 0 { 141 for _, item := range items { 142 o.Logger.Debugf("destroyCloudInstances: found %s in pending items", item.name) 143 } 144 return fmt.Errorf("destroyCloudInstances: %d undeleted items pending", len(items)) 145 } 146 147 backoff := wait.Backoff{ 148 Duration: 15 * time.Second, 149 Factor: 1.1, 150 Cap: leftInContext(ctx), 151 Steps: math.MaxInt32} 152 err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) { 153 secondPassList, err2 := o.listCloudInstances() 154 if err2 != nil { 155 return false, err2 156 } 157 if len(secondPassList) == 0 { 158 // We finally don't see any remaining instances! 159 return true, nil 160 } 161 for _, item := range secondPassList { 162 o.Logger.Debugf("destroyCloudInstances: found %s in second pass", item.name) 163 } 164 return false, nil 165 }) 166 if err != nil { 167 o.Logger.Fatal("destroyCloudInstances: ExponentialBackoffWithContext (list) returns ", err) 168 } 169 170 return nil 171 }