github.com/openshift/installer@v1.4.17/pkg/destroy/powervs/image.go (about) 1 package powervs 2 3 import ( 4 "context" 5 "fmt" 6 "math" 7 "strings" 8 "time" 9 10 "github.com/IBM-Cloud/power-go-client/power/models" 11 "k8s.io/apimachinery/pkg/util/wait" 12 ) 13 14 const imageTypeName = "image" 15 16 // listImages lists images in the vpc. 17 func (o *ClusterUninstaller) listImages() (cloudResources, error) { 18 o.Logger.Debugf("Listing images") 19 20 if o.imageClient == nil { 21 o.Logger.Infof("Skipping deleting images because no service instance was found") 22 result := []cloudResource{} 23 return cloudResources{}.insert(result...), nil 24 } 25 26 ctx, cancel := o.contextWithTimeout() 27 defer cancel() 28 29 select { 30 case <-ctx.Done(): 31 o.Logger.Debugf("listImages: case <-ctx.Done()") 32 return nil, o.Context.Err() // we're cancelled, abort 33 default: 34 } 35 36 images, err := o.imageClient.GetAll() 37 if err != nil { 38 return nil, fmt.Errorf("failed to list images: %w", err) 39 } 40 41 var foundOne = false 42 43 result := []cloudResource{} 44 for _, image := range images.Images { 45 if strings.Contains(*image.Name, o.InfraID) { 46 foundOne = true 47 o.Logger.Debugf("listImages: FOUND: %s, %s, %s", *image.ImageID, *image.Name, *image.State) 48 result = append(result, cloudResource{ 49 key: *image.ImageID, 50 name: *image.Name, 51 status: *image.State, 52 typeName: imageTypeName, 53 id: *image.ImageID, 54 }) 55 } 56 } 57 if !foundOne { 58 o.Logger.Debugf("listImages: NO matching image against: %s", o.InfraID) 59 for _, image := range images.Images { 60 o.Logger.Debugf("listImages: image: %s", *image.Name) 61 } 62 } 63 64 return cloudResources{}.insert(result...), nil 65 } 66 67 func (o *ClusterUninstaller) deleteImage(item cloudResource) error { 68 var img *models.Image 69 var err error 70 71 ctx, cancel := o.contextWithTimeout() 72 defer cancel() 73 74 select { 75 case <-ctx.Done(): 76 o.Logger.Debugf("deleteImage: case <-ctx.Done()") 77 return o.Context.Err() // we're cancelled, abort 78 default: 79 } 80 81 img, err = o.imageClient.Get(item.id) 82 if err != nil { 83 o.Logger.Debugf("listImages: deleteImage: image %q no longer exists", item.name) 84 o.deletePendingItems(item.typeName, []cloudResource{item}) 85 o.Logger.Infof("Deleted Image %q", item.name) 86 return nil 87 } 88 89 if !strings.EqualFold(img.State, "active") { 90 o.Logger.Debugf("Waiting for image %q to delete", item.name) 91 return nil 92 } 93 94 err = o.imageClient.Delete(item.id) 95 if err != nil { 96 return fmt.Errorf("failed to delete image %s: %w", item.name, err) 97 } 98 99 o.Logger.Infof("Deleted Image %q", item.name) 100 o.deletePendingItems(item.typeName, []cloudResource{item}) 101 102 return nil 103 } 104 105 // destroyImages removes all image resources that have a name prefixed 106 // with the cluster's infra ID. 107 func (o *ClusterUninstaller) destroyImages() error { 108 firstPassList, err := o.listImages() 109 if err != nil { 110 return err 111 } 112 113 if len(firstPassList.list()) == 0 { 114 return nil 115 } 116 117 items := o.insertPendingItems(imageTypeName, firstPassList.list()) 118 for _, item := range items { 119 o.Logger.Debugf("destroyImages: firstPassList: %v / %v", item.name, item.id) 120 } 121 122 ctx, cancel := o.contextWithTimeout() 123 defer cancel() 124 125 for _, item := range items { 126 select { 127 case <-ctx.Done(): 128 o.Logger.Debugf("destroyImages: case <-ctx.Done()") 129 return o.Context.Err() // we're cancelled, abort 130 default: 131 } 132 133 backoff := wait.Backoff{ 134 Duration: 15 * time.Second, 135 Factor: 1.1, 136 Cap: leftInContext(ctx), 137 Steps: math.MaxInt32} 138 err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) { 139 err2 := o.deleteImage(item) 140 if err2 == nil { 141 return true, err2 142 } 143 o.errorTracker.suppressWarning(item.key, err2, o.Logger) 144 return false, err2 145 }) 146 if err != nil { 147 o.Logger.Fatal("destroyImages: ExponentialBackoffWithContext (destroy) returns ", err) 148 } 149 } 150 151 if items = o.getPendingItems(imageTypeName); len(items) > 0 { 152 for _, item := range items { 153 o.Logger.Debugf("destroyImages: found %s in pending items", item.name) 154 } 155 return fmt.Errorf("destroyImages: %d undeleted items pending", len(items)) 156 } 157 158 backoff := wait.Backoff{ 159 Duration: 15 * time.Second, 160 Factor: 1.1, 161 Cap: leftInContext(ctx), 162 Steps: math.MaxInt32} 163 err = wait.ExponentialBackoffWithContext(ctx, backoff, func(context.Context) (bool, error) { 164 secondPassList, err2 := o.listImages() 165 if err2 != nil { 166 return false, err2 167 } 168 if len(secondPassList) == 0 { 169 // We finally don't see any remaining instances! 170 return true, nil 171 } 172 for _, item := range secondPassList { 173 o.Logger.Debugf("destroyImages: found %s in second pass", item.name) 174 } 175 return false, nil 176 }) 177 if err != nil { 178 o.Logger.Fatal("destroyImages: ExponentialBackoffWithContext (list) returns ", err) 179 } 180 181 return nil 182 }