github.com/openshift/installer@v1.4.17/pkg/destroy/ibmcloud/cloudobjectstorage.go (about) 1 package ibmcloud 2 3 import ( 4 "fmt" 5 "net/http" 6 7 "github.com/IBM/platform-services-go-sdk/resourcecontrollerv2" 8 "github.com/pkg/errors" 9 ) 10 11 const ( 12 cosTypeName = "cos instance" 13 // reclamationReclaim will delete the resource, reclaim it. 14 reclamationReclaim = "reclaim" 15 ) 16 17 // Resource ID collected via following command using IBM Cloud CLI: 18 // $ ibmcloud catalog service cloud-object-storage --output json | jq -r '.[].id' . 19 const cosResourceID = "dff97f5c-bc5e-4455-b470-411c3edbe49c" 20 21 // findCOSInstanceReclamation Checks current reclamations for one that matches the COS instance name. 22 func (o *ClusterUninstaller) findCOSInstanceReclamation(instance cloudResource) (*resourcecontrollerv2.Reclamation, error) { 23 o.Logger.Debugf("Searching for COS reclamations for instance %s", instance.name) 24 ctx, cancel := o.contextWithTimeout() 25 defer cancel() 26 27 reclamationOptions := o.controllerSvc.NewListReclamationsOptions() 28 resources, _, err := o.controllerSvc.ListReclamationsWithContext(ctx, reclamationOptions) 29 if err != nil { 30 return nil, errors.Wrapf(err, "Failed listing reclamations for instance %s", instance.name) 31 } 32 33 o.Logger.Debugf("Checking reclamations that match instance %s", instance.name) 34 for _, reclamation := range resources.Resources { 35 getOptions := o.controllerSvc.NewGetResourceInstanceOptions(*reclamation.ResourceInstanceID) 36 cosInstance, _, err := o.controllerSvc.GetResourceInstanceWithContext(ctx, getOptions) 37 if err != nil { 38 return nil, errors.Wrapf(err, "Failed checking reclamation %s", *reclamation.ResourceInstanceID) 39 } 40 if *cosInstance.Name == instance.name { 41 o.Logger.Debugf("Found COS instance reclamation %s - %s", instance.name, *reclamation.ID) 42 return &reclamation, nil 43 } 44 } 45 46 return nil, nil 47 } 48 49 // reclaimCOSInstanceReclamation reclaims (deletes) a reclamation from a deleted COS instance. 50 func (o *ClusterUninstaller) reclaimCOSInstanceReclamation(reclamationID string) error { 51 o.Logger.Debugf("Reclaming COS instance reclamation %s", reclamationID) 52 ctx, cancel := o.contextWithTimeout() 53 defer cancel() 54 55 options := o.controllerSvc.NewRunReclamationActionOptions(reclamationID, reclamationReclaim) 56 _, response, err := o.controllerSvc.RunReclamationActionWithContext(ctx, options) 57 if err != nil { 58 // If reclaim attempt failed because the reclamation doesn't exist (404) don't return an error 59 if response != nil && response.StatusCode == http.StatusNotFound { 60 o.Logger.Debugf("Reclamation not found, it has likely already been reclaimed %s", reclamationID) 61 return nil 62 } 63 return errors.Wrapf(err, "Failed to reclaim COS instance reclamation %s", reclamationID) 64 } 65 66 o.Logger.Infof("Reclaimed %s", reclamationID) 67 return nil 68 } 69 70 // listCOSInstances lists COS service instances. 71 func (o *ClusterUninstaller) listCOSInstances() (cloudResources, error) { 72 o.Logger.Debugf("Listing COS instances") 73 ctx, cancel := o.contextWithTimeout() 74 defer cancel() 75 76 resourceGroupID, err := o.ResourceGroupID() 77 if err != nil { 78 return nil, err 79 } 80 81 options := o.controllerSvc.NewListResourceInstancesOptions() 82 options.SetResourceGroupID(resourceGroupID) 83 options.SetResourceID(cosResourceID) 84 options.SetType("service_instance") 85 resources, _, err := o.controllerSvc.ListResourceInstancesWithContext(ctx, options) 86 if err != nil { 87 return nil, errors.Wrapf(err, "Failed to list COS instances") 88 } 89 90 result := []cloudResource{} 91 for _, instance := range resources.Resources { 92 // Match the COS instances created by both the installer and the 93 // cluster-image-registry-operator. 94 if fmt.Sprintf("%s-cos", o.InfraID) == *instance.Name || 95 fmt.Sprintf("%s-image-registry", o.InfraID) == *instance.Name { 96 result = append(result, cloudResource{ 97 key: *instance.ID, 98 name: *instance.Name, 99 status: *instance.State, 100 typeName: cosTypeName, 101 id: *instance.ID, 102 }) 103 } 104 } 105 106 return cloudResources{}.insert(result...), nil 107 } 108 109 func (o *ClusterUninstaller) deleteCOSInstance(item cloudResource) error { 110 o.Logger.Debugf("Deleting COS instance %s", item.name) 111 ctx, cancel := o.contextWithTimeout() 112 defer cancel() 113 114 options := o.controllerSvc.NewDeleteResourceInstanceOptions(item.id) 115 options.SetRecursive(true) 116 details, err := o.controllerSvc.DeleteResourceInstanceWithContext(ctx, options) 117 if err != nil { 118 // If the deletion attempt failed because the COS instance doesn't exist (404) don't return an error 119 if details != nil && details.StatusCode == http.StatusNotFound { 120 return nil 121 } 122 return errors.Wrapf(err, "Failed to delete COS Instance %s", item.name) 123 } 124 125 return nil 126 } 127 128 // destroyCOSInstances removes the COS service instance resources that have a 129 // name prefixed with the cluster's infra ID. 130 func (o *ClusterUninstaller) destroyCOSInstances() error { 131 found, err := o.listCOSInstances() 132 if err != nil { 133 return err 134 } 135 136 items := o.insertPendingItems(cosTypeName, found.list()) 137 138 for _, item := range items { 139 if _, ok := found[item.key]; !ok { 140 // Check if a COS reclamation was created for the deleted instance 141 reclamation, err := o.findCOSInstanceReclamation(item) 142 if err != nil { 143 return err 144 } 145 if reclamation != nil { 146 err = o.reclaimCOSInstanceReclamation(*reclamation.ID) 147 if err != nil { 148 return err 149 } 150 continue 151 } 152 o.Logger.Debugf("No reclamations found for COS instance %s", item.name) 153 // This item has finished deletion. 154 o.deletePendingItems(item.typeName, []cloudResource{item}) 155 o.Logger.Infof("Deleted COS instance %s", item.name) 156 continue 157 } 158 err = o.deleteCOSInstance(item) 159 if err != nil { 160 o.errorTracker.suppressWarning(item.key, err, o.Logger) 161 } 162 } 163 164 if items = o.getPendingItems(cosTypeName); len(items) > 0 { 165 return errors.Errorf("%d items pending", len(items)) 166 } 167 return nil 168 } 169 170 // COSInstanceID returns the ID of the Cloud Object Storage service instance 171 // created by the installer during installation. 172 func (o *ClusterUninstaller) COSInstanceID() (string, error) { 173 if o.cosInstanceID != "" { 174 return o.cosInstanceID, nil 175 } 176 cosInstances, err := o.listCOSInstances() 177 if err != nil { 178 return "", err 179 } 180 instanceList := cosInstances.list() 181 if len(instanceList) == 0 { 182 return "", errors.Errorf("COS instance not found") 183 } 184 185 // Locate the installer's COS instance by name. 186 for _, instance := range instanceList { 187 if instance.name == fmt.Sprintf("%s-cos", o.InfraID) { 188 o.cosInstanceID = instance.id 189 return instance.id, nil 190 } 191 } 192 return "", errors.Errorf("COS instance not found") 193 }