From 6216d522c872f7fe00d1e7c6031cd16b9de74405 Mon Sep 17 00:00:00 2001 From: Manjunath Kumatagi Date: Fri, 4 Feb 2022 17:40:08 -0600 Subject: [PATCH] Add IBM Power VS: tf plugin for ibm presign This plugin is needed in order to securely host the ignition config files for bootstrap machines. It uses the COS offering from IBM Cloud, which is s3-compatible. For more background on IPI on Power VS, refer to the enhancement proposal here: openshift/enhancements#736 Older discussions on some of the code here can be found in #5224 Signed-off-by: Christy Norman --- pkg/terraform/exec/plugins/ibms3presign.go | 15 ++ .../exec/plugins/ibms3presign/provider.go | 15 ++ .../plugins/ibms3presign/resource_presign.go | 154 ++++++++++++++++++ 3 files changed, 184 insertions(+) create mode 100644 pkg/terraform/exec/plugins/ibms3presign.go create mode 100644 pkg/terraform/exec/plugins/ibms3presign/provider.go create mode 100644 pkg/terraform/exec/plugins/ibms3presign/resource_presign.go diff --git a/pkg/terraform/exec/plugins/ibms3presign.go b/pkg/terraform/exec/plugins/ibms3presign.go new file mode 100644 index 00000000000..21a0f742caf --- /dev/null +++ b/pkg/terraform/exec/plugins/ibms3presign.go @@ -0,0 +1,15 @@ +package plugins + +import ( + "github.com/hashicorp/terraform-plugin-sdk/plugin" + "github.com/openshift/installer/pkg/terraform/exec/plugins/ibms3presign" +) + +func init() { + ibmS3PresignProvider := func() { + plugin.Serve(&plugin.ServeOpts{ + ProviderFunc: ibms3presign.Provider, + }) + } + KnownPlugins["terraform-provider-ibms3presign"] = ibmS3PresignProvider +} diff --git a/pkg/terraform/exec/plugins/ibms3presign/provider.go b/pkg/terraform/exec/plugins/ibms3presign/provider.go new file mode 100644 index 00000000000..35a8beef52a --- /dev/null +++ b/pkg/terraform/exec/plugins/ibms3presign/provider.go @@ -0,0 +1,15 @@ +package ibms3presign + +import ( + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/terraform" +) + +// Provider returns a terraform.ResourceProvider. +func Provider() terraform.ResourceProvider { + return &schema.Provider{ + ResourcesMap: map[string]*schema.Resource{ + "ibms3presign": resourcePresign(), + }, + } +} diff --git a/pkg/terraform/exec/plugins/ibms3presign/resource_presign.go b/pkg/terraform/exec/plugins/ibms3presign/resource_presign.go new file mode 100644 index 00000000000..f780866ce10 --- /dev/null +++ b/pkg/terraform/exec/plugins/ibms3presign/resource_presign.go @@ -0,0 +1,154 @@ +package ibms3presign + +import ( + "fmt" + "time" + + "github.com/aws/aws-sdk-go/aws" + "github.com/aws/aws-sdk-go/aws/credentials" + "github.com/aws/aws-sdk-go/aws/session" + "github.com/aws/aws-sdk-go/service/s3" + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" +) + +var storageClasses = []string{ + "standard", "vault", "cold", "flex", "smart", +} + +var endpointTypes = []string{ + "public", "private", "direct", +} + +func resourcePresign() *schema.Resource { + return &schema.Resource{ + Create: resourceIBMCOSBucketObjectPresignCreate, + Read: resourceIBMCOSBucketObjectPresignCreate, + Delete: resourceIBMCOSBucketObjectPresignDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + Schema: map[string]*schema.Schema{ + "access_key_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, + Description: "access_key_id", + }, + "secret_access_key": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Sensitive: true, + Description: "secret_access_key", + }, + "bucket_name": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "COS Bucket name", + }, + "key": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "COS object key", + }, + "region_location": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + Description: "Region Location info.", + }, + "storage_class": { + Type: schema.TypeString, + Required: true, + ValidateFunc: validateAllowedStringValue(storageClasses), + ForceNew: true, + Description: "Storage class info", + }, + "endpoint_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + ValidateFunc: validateAllowedStringValue(endpointTypes), + Description: "public, private or direct", + Default: "public", + }, + "presigned_url": { + Type: schema.TypeString, + Computed: true, + Optional: true, + Sensitive: true, + Description: "Presigned URL", + }, + "expire": { + Type: schema.TypeInt, + Optional: true, + ForceNew: true, + Description: "Set expire time for URL in minutes", + Default: 60, + }, + }, + } +} + +func validateAllowedStringValue(validValues []string) schema.SchemaValidateFunc { + return func(v interface{}, k string) (ws []string, errors []error) { + input := v.(string) + existed := false + for _, s := range validValues { + if s == input { + existed = true + break + } + } + if !existed { + errors = append(errors, fmt.Errorf( + "%q must contain a value from %#v, got %q", + k, validValues, input)) + } + return + + } +} + +// resourceIBMCOSBucketObjectPresignDelete is just a placeholder NOP function to satisfy the schema resource +func resourceIBMCOSBucketObjectPresignDelete(d *schema.ResourceData, m interface{}) error { + return nil +} + +func resourceIBMCOSBucketObjectPresignCreate(d *schema.ResourceData, m interface{}) error { + storageClass := d.Get("storage_class").(string) + bucketName := d.Get("bucket_name").(string) + object := d.Get("key").(string) + regionLocation := d.Get("region_location").(string) + endpointType := d.Get("endpoint_type").(string) + accessKey := d.Get("access_key_id").(string) + secretKey := d.Get("secret_access_key").(string) + expire := d.Get("expire").(int) + var svcEndpoint string + if endpointType != "public" { + regionLocation = fmt.Sprintf("%s.%s", endpointType, regionLocation) + } + svcEndpoint = fmt.Sprintf("https://s3.%s.cloud-object-storage.appdomain.cloud", regionLocation) + region := fmt.Sprintf("%s-%s", regionLocation, storageClass) + conf := aws.NewConfig(). + WithRegion(region). + WithEndpoint(svcEndpoint). + WithS3ForcePathStyle(true). + WithCredentials(credentials.NewStaticCredentials(accessKey, secretKey, "")) + sess := session.Must(session.NewSession()) // Creating a new session + client := s3.New(sess, conf) + req, _ := client.GetObjectRequest(&s3.GetObjectInput{ + Bucket: aws.String(bucketName), + Key: aws.String(object), + }) + presignedURL, err := req.Presign(time.Duration(expire) * time.Minute) + if err != nil { + return err + } + d.Set("presigned_url", presignedURL) + d.SetId(fmt.Sprintf("%s/%s", bucketName, object)) + return nil +}