github.com/openshift/installer@v1.4.17/pkg/asset/ignition/bootstrap/gcp/storage.go (about)

     1  package gcp
     2  
     3  import (
     4  	"context"
     5  	"fmt"
     6  	"time"
     7  
     8  	"cloud.google.com/go/storage"
     9  	"google.golang.org/api/option"
    10  
    11  	"github.com/openshift/installer/pkg/asset/installconfig"
    12  	gcpic "github.com/openshift/installer/pkg/asset/installconfig/gcp"
    13  	gcpconsts "github.com/openshift/installer/pkg/constants/gcp"
    14  )
    15  
    16  const (
    17  	bootstrapIgnitionBucketObjName = "bootstrap.ign"
    18  )
    19  
    20  // GetBootstrapStorageName gets the name of the storage bucket for the bootstrap process.
    21  func GetBootstrapStorageName(clusterID string) string {
    22  	return fmt.Sprintf("%s-bootstrap-ignition", clusterID)
    23  }
    24  
    25  // NewStorageClient creates a new Google storage client.
    26  func NewStorageClient(ctx context.Context) (*storage.Client, error) {
    27  	ssn, err := gcpic.GetSession(ctx)
    28  	if err != nil {
    29  		return nil, fmt.Errorf("failed to get session while creating gcp storage client: %w", err)
    30  	}
    31  
    32  	client, err := storage.NewClient(ctx, option.WithCredentials(ssn.Credentials))
    33  	if err != nil {
    34  		return nil, fmt.Errorf("failed to create client: %w", err)
    35  	}
    36  
    37  	return client, nil
    38  }
    39  
    40  // CreateStorage creates the gcp bucket/storage. The storage bucket does Not include the bucket object. The
    41  // bucket object is created as a separate process/function, so that the two are not tied together, and
    42  // the data stored inside the object can be set at a later time.
    43  func CreateStorage(ctx context.Context, ic *installconfig.InstallConfig, bucketHandle *storage.BucketHandle, clusterID string) error {
    44  	labels := map[string]string{}
    45  	labels[fmt.Sprintf(gcpconsts.ClusterIDLabelFmt, clusterID)] = "owned"
    46  	for _, label := range ic.Config.GCP.UserLabels {
    47  		labels[label.Key] = label.Value
    48  	}
    49  
    50  	bucketAttrs := storage.BucketAttrs{
    51  		UniformBucketLevelAccess: storage.UniformBucketLevelAccess{
    52  			Enabled: true,
    53  		},
    54  		Location: ic.Config.GCP.Region,
    55  		Labels:   labels,
    56  	}
    57  
    58  	ctx, cancel := context.WithTimeout(ctx, time.Minute*1)
    59  	defer cancel()
    60  
    61  	if err := bucketHandle.Create(ctx, ic.Config.GCP.ProjectID, &bucketAttrs); err != nil {
    62  		return fmt.Errorf("failed to create bucket: %w", err)
    63  	}
    64  	return nil
    65  }
    66  
    67  // CreateSignedURL creates a signed url and correlates the signed url with a storage bucket.
    68  func CreateSignedURL(clusterID string) (string, error) {
    69  	bucketName := GetBootstrapStorageName(clusterID)
    70  
    71  	client, err := NewStorageClient(context.TODO())
    72  	if err != nil {
    73  		return "", fmt.Errorf("failed to create storage client: %w", err)
    74  	}
    75  
    76  	// Signing a URL requires credentials authorized to sign a URL. You can pass
    77  	// these in through SignedURLOptions with a Google Access ID with
    78  	// iam.serviceAccounts.signBlob permissions.
    79  	opts := storage.SignedURLOptions{
    80  		Scheme:  storage.SigningSchemeV4,
    81  		Method:  "GET",
    82  		Expires: time.Now().Add(time.Minute * 60),
    83  	}
    84  
    85  	// The object has not been created yet. This is ok, it is expected to be created after this call.
    86  	// However, if the object is never created this could cause major issues.
    87  	url, err := client.Bucket(bucketName).SignedURL(bootstrapIgnitionBucketObjName, &opts)
    88  	if err != nil {
    89  		return "", fmt.Errorf("failed to create a signed url: %w", err)
    90  	}
    91  
    92  	return url, nil
    93  }
    94  
    95  // FillBucket will add the contents to the bootstrap storage bucket object.
    96  func FillBucket(ctx context.Context, bucketHandle *storage.BucketHandle, contents string) error {
    97  	ctx, cancel := context.WithTimeout(ctx, time.Minute*1)
    98  	defer cancel()
    99  
   100  	objWriter := bucketHandle.Object(bootstrapIgnitionBucketObjName).NewWriter(ctx)
   101  	if _, err := fmt.Fprint(objWriter, contents); err != nil {
   102  		return fmt.Errorf("failed to store content in bucket object: %w", err)
   103  	}
   104  
   105  	if err := objWriter.Close(); err != nil {
   106  		return fmt.Errorf("failed to close bucket object writer: %w", err)
   107  	}
   108  
   109  	return nil
   110  }
   111  
   112  // DestroyStorage Destroy the bucket and the bucket objects that are associated with the bucket.
   113  func DestroyStorage(ctx context.Context, clusterID string) error {
   114  	client, err := NewStorageClient(ctx)
   115  	if err != nil {
   116  		return fmt.Errorf("failed to create storage client: %w", err)
   117  	}
   118  	bucketName := GetBootstrapStorageName(clusterID)
   119  
   120  	ctx, cancel := context.WithTimeout(ctx, time.Minute*1)
   121  	defer cancel()
   122  	if err := client.Bucket(bucketName).Object(bootstrapIgnitionBucketObjName).Delete(ctx); err != nil {
   123  		return fmt.Errorf("failed to delete bucket object %s: %w", bootstrapIgnitionBucketObjName, err)
   124  	}
   125  
   126  	// Deleting a bucket will delete the managed folders and bucket objects.
   127  	if err := client.Bucket(bucketName).Delete(ctx); err != nil {
   128  		return fmt.Errorf("failed to delete bucket %s: %w", bucketName, err)
   129  	}
   130  	return nil
   131  }