github.com/openshift/installer@v1.4.17/pkg/destroy/ibmcloud/iam.go (about)

     1  package ibmcloud
     2  
     3  import (
     4  	"net/http"
     5  	"strings"
     6  
     7  	"github.com/IBM/platform-services-go-sdk/iampolicymanagementv1"
     8  	"github.com/pkg/errors"
     9  )
    10  
    11  const iamAuthorizationTypeName = "iam authorization"
    12  
    13  // listIAMAuthorizations lists IAM authorizations
    14  func (o *ClusterUninstaller) listIAMAuthorizations() (cloudResources, error) {
    15  	o.Logger.Debugf("Listing IAM authorizations")
    16  	ctx, cancel := o.contextWithTimeout()
    17  	defer cancel()
    18  
    19  	options := o.iamPolicyManagementSvc.NewListPoliciesOptions(o.AccountID)
    20  	options.SetType("authorization")
    21  	resources, _, err := o.iamPolicyManagementSvc.ListPoliciesWithContext(ctx, options)
    22  	if err != nil {
    23  		return nil, errors.Wrapf(err, "Failed to list IAM authorizations")
    24  	}
    25  
    26  	result := []cloudResource{}
    27  	for _, policy := range resources.Policies {
    28  		if o.policyMatches(policy) {
    29  			result = append(result, cloudResource{
    30  				key:      *policy.ID,
    31  				name:     *policy.ID,
    32  				status:   *policy.State,
    33  				typeName: iamAuthorizationTypeName,
    34  				id:       *policy.ID,
    35  			})
    36  		}
    37  	}
    38  
    39  	return cloudResources{}.insert(result...), nil
    40  }
    41  
    42  // policyMatches returns true if the IAM Policy matches the one set up to allow
    43  // the VPC service to read from the COS bucket containing the uploaded RHCOS image.
    44  func (o *ClusterUninstaller) policyMatches(policy iampolicymanagementv1.PolicyTemplateMetaData) bool {
    45  	// Ideally we would match using the description field of the Policy type. However,
    46  	// this is not currently supported in the IBM Terraform Provider. An issue has
    47  	// been opened for this: https://github.com/IBM-Cloud/terraform-provider-ibm/issues/2894
    48  	// Once implemented, this code can be updaed to check for a well-known
    49  	// description that includes the cluster's InfraID or something equivalent.
    50  	onlyOneSubject := len(policy.Subjects) == 1
    51  	subjectMatches := len(policy.Subjects[0].Attributes) == 3 &&
    52  		*policy.Subjects[0].Attributes[0].Name == "accountId" &&
    53  		*policy.Subjects[0].Attributes[0].Value == o.AccountID &&
    54  		*policy.Subjects[0].Attributes[1].Name == "serviceName" &&
    55  		*policy.Subjects[0].Attributes[1].Value == "is" &&
    56  		*policy.Subjects[0].Attributes[2].Name == "resourceType" &&
    57  		*policy.Subjects[0].Attributes[2].Value == "image"
    58  
    59  	onlyOneResource := len(policy.Resources) == 1
    60  	cosInstanceID, err := o.COSInstanceID()
    61  	if err != nil {
    62  		o.Logger.Warn("Unable to determine IAM policy match. Failed to obtain COS instance ID. ", err)
    63  		return false
    64  	}
    65  	resourceMatches := len(policy.Resources[0].Attributes) == 3 &&
    66  		*policy.Resources[0].Attributes[0].Name == "accountId" &&
    67  		*policy.Resources[0].Attributes[0].Value == o.AccountID &&
    68  		*policy.Resources[0].Attributes[1].Name == "serviceName" &&
    69  		*policy.Resources[0].Attributes[1].Value == "cloud-object-storage" &&
    70  		*policy.Resources[0].Attributes[2].Name == "serviceInstance" &&
    71  		strings.Contains(cosInstanceID, *policy.Resources[0].Attributes[2].Value) &&
    72  		*policy.Resources[0].Attributes[2].Operator == "stringEquals"
    73  
    74  	return onlyOneSubject && onlyOneResource && subjectMatches && resourceMatches
    75  }
    76  
    77  func (o *ClusterUninstaller) deleteIAMAuthorization(item cloudResource) error {
    78  	o.Logger.Debugf("Deleting IAM authorization %q", item.name)
    79  	ctx, cancel := o.contextWithTimeout()
    80  	defer cancel()
    81  
    82  	options := o.iamPolicyManagementSvc.NewDeletePolicyOptions(item.id)
    83  	details, err := o.iamPolicyManagementSvc.DeletePolicyWithContext(ctx, options)
    84  
    85  	if err != nil && details != nil && details.StatusCode == http.StatusNotFound {
    86  		// The resource is gone
    87  		o.deletePendingItems(item.typeName, []cloudResource{item})
    88  		o.Logger.Infof("Deleted IAM authorization %q", item.name)
    89  		return nil
    90  	}
    91  
    92  	if err != nil && details != nil && details.StatusCode != http.StatusNotFound {
    93  		return errors.Wrapf(err, "Failed to delete IAM authorization %s", item.name)
    94  	}
    95  
    96  	return nil
    97  }
    98  
    99  // destroyIAMAuthorizations removes all IAM authorization pertaining to the cluster ID.
   100  func (o *ClusterUninstaller) destroyIAMAuthorizations() error {
   101  	found, err := o.listIAMAuthorizations()
   102  	if err != nil {
   103  		return err
   104  	}
   105  
   106  	items := o.insertPendingItems(iamAuthorizationTypeName, found.list())
   107  
   108  	for _, item := range items {
   109  		if _, ok := found[item.key]; !ok {
   110  			// This item has finished deletion.
   111  			o.deletePendingItems(item.typeName, []cloudResource{item})
   112  			o.Logger.Infof("Deleted IAM authorization %q", item.name)
   113  			continue
   114  		}
   115  		err = o.deleteIAMAuthorization(item)
   116  		if err != nil {
   117  			o.errorTracker.suppressWarning(item.key, err, o.Logger)
   118  		}
   119  	}
   120  
   121  	if items = o.getPendingItems(iamAuthorizationTypeName); len(items) > 0 {
   122  		return errors.Errorf("%d items pending", len(items))
   123  	}
   124  	return nil
   125  }