diff --git a/docs/resources/ims_image_share_v1.md b/docs/resources/ims_image_share_v1.md new file mode 100644 index 000000000..5d72df434 --- /dev/null +++ b/docs/resources/ims_image_share_v1.md @@ -0,0 +1,49 @@ +--- +subcategory: "Image Management Service (IMS)" +layout: "opentelekomcloud" +page_title: "OpenTelekomCloud: opentelekomcloud_ims_image_share_v1" +sidebar_current: "docs-opentelekomcloud-resource-ims-image-share-v1" +description: |- + Manages a IMS Image Share resource within OpenTelekomCloud. +--- + +# opentelekomcloud_ims_image_share_v1 + +Manages an IMS image share resource within OpenTelekomCloud. + +## Example Usage + +```hcl +variable "source_image_id" {} +variable "target_project_ids" {} + +resource "opentelekomcloud_ims_image_share_v1" "share" { + source_image_id = var.source_image_id + target_project_ids = var.target_project_ids +} +``` + +## Argument Reference + +The following arguments are supported: +* `source_image_id` - (Required, String, ForceNew) Specifies the ID of the source image. The source image must be in the + same region as the current resource. + + Changing this parameter will create a new resource. + +* `target_project_ids` - (Required, List) Specifies the IDs of the target projects. + +## Attribute Reference + +In addition to all arguments above, the following attributes are exported: + +* `id` - The resource ID, same as `source_image_id`. + +* `region` - The region in which resource is located. + +## Timeouts + +This resource provides the following timeouts configuration options: + +* `create` - Default is 5 minutes. +* `delete` - Default is 5 minutes. diff --git a/opentelekomcloud/acceptance/ims/resource_opentelekomcloud_ims_image_share_v1_test.go b/opentelekomcloud/acceptance/ims/resource_opentelekomcloud_ims_image_share_v1_test.go new file mode 100644 index 000000000..24d737128 --- /dev/null +++ b/opentelekomcloud/acceptance/ims/resource_opentelekomcloud_ims_image_share_v1_test.go @@ -0,0 +1,89 @@ +package acceptance + +import ( + "fmt" + "os" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/ims/v2/images" + "github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/acceptance/common" + "github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/acceptance/env" + "github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/common/cfg" +) + +func getImsImageShareResourceFunc(cfg *cfg.Config, state *terraform.ResourceState) (interface{}, error) { + c, err := cfg.ImageV2Client(env.OS_REGION_NAME) + if err != nil { + return nil, fmt.Errorf("error creating OpenTelekomCloud image V2 client: %s", err) + } + imgs, err := images.ListImages(c, + images.ListImagesOpts{ + Id: state.Primary.ID, + ImageType: "shared"}) + if err != nil || len(imgs) < 1 { + return nil, fmt.Errorf("unable to retrieve images: %s", err) + } + img := imgs[0] + return img, nil +} + +func TestAccImsImageShare_basic(t *testing.T) { + privateImageID := os.Getenv("OS_PRIVATE_IMAGE_ID") + shareProjectID := os.Getenv("OS_PROJECT_ID_2") + shareProjectID2 := os.Getenv("OS_PROJECT_ID_3") + if privateImageID == "" || shareProjectID == "" { + t.Skip("OS_PRIVATE_IMAGE_ID, OS_PROJECT_ID_2 or OS_PROJECT_ID_3 are empty, but test requires") + } + + var obj interface{} + + rName := "opentelekomcloud_ims_image_share_v1.share_1" + + rc := common.InitResourceCheck( + rName, + &obj, + getImsImageShareResourceFunc, + ) + + resource.ParallelTest(t, resource.TestCase{ + PreCheck: func() { + common.TestAccPreCheck(t) + }, + ProviderFactories: common.TestAccProviderFactories, + CheckDestroy: rc.CheckResourceDestroy(), + Steps: []resource.TestStep{ + { + Config: testImsImageShare_basic(privateImageID, shareProjectID), + Check: resource.ComposeTestCheckFunc( + rc.CheckResourceExists(), + ), + }, + { + Config: testImsImageShare_update(privateImageID, shareProjectID, shareProjectID2), + Check: resource.ComposeTestCheckFunc( + rc.CheckResourceExists(), + ), + }, + }, + }) +} + +func testImsImageShare_basic(privateImageID, projectToShare string) string { + return fmt.Sprintf(` +resource "opentelekomcloud_ims_image_share_v1" "share_1" { + source_image_id = "%[1]s" + target_project_ids = ["%[2]s"] +} +`, privateImageID, projectToShare) +} + +func testImsImageShare_update(privateImageID, projectToShare, projectToShare2 string) string { + return fmt.Sprintf(` +resource "opentelekomcloud_ims_image_share_v1" "share_1" { + source_image_id = "%[1]s" + target_project_ids = ["%[2]s", "%[3]s"] +} +`, privateImageID, projectToShare, projectToShare2) +} diff --git a/opentelekomcloud/provider.go b/opentelekomcloud/provider.go index 69ff8098b..81b33e939 100644 --- a/opentelekomcloud/provider.go +++ b/opentelekomcloud/provider.go @@ -494,6 +494,7 @@ func Provider() *schema.Provider { "opentelekomcloud_images_image_v2": ims.ResourceImagesImageV2(), "opentelekomcloud_ims_data_image_v2": ims.ResourceImsDataImageV2(), "opentelekomcloud_ims_image_v2": ims.ResourceImsImageV2(), + "opentelekomcloud_ims_image_share_v1": ims.ResourceImsImageShareV1(), "opentelekomcloud_kms_grant_v1": kms.ResourceKmsGrantV1(), "opentelekomcloud_kms_key_v1": kms.ResourceKmsKeyV1(), "opentelekomcloud_kms_key_material_v1": kms.ResourceKmsKeyMaterialV1(), diff --git a/opentelekomcloud/services/ims/resource_opentelekomcloud_ims_image_share_v1.go b/opentelekomcloud/services/ims/resource_opentelekomcloud_ims_image_share_v1.go new file mode 100644 index 000000000..bbf48753a --- /dev/null +++ b/opentelekomcloud/services/ims/resource_opentelekomcloud_ims_image_share_v1.go @@ -0,0 +1,177 @@ +package ims + +import ( + "context" + "fmt" + "time" + + "github.com/hashicorp/go-multierror" + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + golangsdk "github.com/opentelekomcloud/gophertelekomcloud" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/ims/v1/members" + "github.com/opentelekomcloud/gophertelekomcloud/openstack/ims/v1/others" + "github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/common" + "github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/common/cfg" + "github.com/opentelekomcloud/terraform-provider-opentelekomcloud/opentelekomcloud/common/fmterr" +) + +func ResourceImsImageShareV1() *schema.Resource { + return &schema.Resource{ + CreateContext: resourceImsImageShareCreate, + UpdateContext: resourceImsImageShareUpdate, + ReadContext: resourceImsImageShareRead, + DeleteContext: resourceImsImageShareDelete, + + Timeouts: &schema.ResourceTimeout{ + Create: schema.DefaultTimeout(5 * time.Minute), + Delete: schema.DefaultTimeout(5 * time.Minute), + }, + + Schema: map[string]*schema.Schema{ + "source_image_id": { + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "target_project_ids": { + Type: schema.TypeSet, + Required: true, + Elem: &schema.Schema{Type: schema.TypeString}, + }, + "region": { + Type: schema.TypeString, + Computed: true, + }, + }, + } +} + +func resourceImsImageShareCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*cfg.Config) + client, err := config.ImageV1Client(config.GetRegion(d)) + if err != nil { + return fmterr.Errorf("error creating OpenTelekomCloud image client: %s", err) + } + + sourceImageId := d.Get("source_image_id").(string) + jobId, err := members.BatchAddMembers(client, members.BatchMembersOpts{ + Images: []string{d.Get("source_image_id").(string)}, + Projects: common.ExpandToStringSlice(d.Get("target_project_ids").(*schema.Set).List()), + }) + if err != nil { + return fmterr.Errorf("error requesting OpenTelekomCloud ims share for private image: %w", err) + } + err = waitForImageShareOrAcceptJobSuccess(ctx, d, client, *jobId, schema.TimeoutCreate) + if err != nil { + return fmterr.Errorf("error while waiting OpenTelekomCloud ims share for private image to become active: %w", err) + } + d.SetId(sourceImageId) + + return resourceImsImageShareRead(ctx, d, meta) +} + +func resourceImsImageShareRead(_ context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*cfg.Config) + mErr := multierror.Append( + nil, + d.Set("region", config.GetRegion(d)), + ) + + return diag.FromErr(mErr.ErrorOrNil()) +} + +func resourceImsImageShareUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*cfg.Config) + client, err := config.ImageV1Client(config.GetRegion(d)) + if err != nil { + return fmterr.Errorf("error creating OpenTelekomCloud image client: %s", err) + } + + if d.HasChange("target_project_ids") { + oProjectIdsRaw, nProjectIdsRaw := d.GetChange("target_project_ids") + shareProjectIds := nProjectIdsRaw.(*schema.Set).Difference(oProjectIdsRaw.(*schema.Set)) + unShareProjectIds := oProjectIdsRaw.(*schema.Set).Difference(nProjectIdsRaw.(*schema.Set)) + if shareProjectIds.Len() > 0 { + jobId, err := members.BatchAddMembers(client, members.BatchMembersOpts{ + Images: []string{d.Id()}, + Projects: common.ExpandToStringSlice(shareProjectIds.List()), + }) + if err != nil { + return fmterr.Errorf("error requesting OpenTelekomCloud ims share for private image: %w", err) + } + err = waitForImageShareOrAcceptJobSuccess(ctx, d, client, *jobId, schema.TimeoutCreate) + if err != nil { + return fmterr.Errorf("error while waiting OpenTelekomCloud ims share for private image to become active: %w", err) + } + } + if unShareProjectIds.Len() > 0 { + jobId, err := members.BatchDeleteMembers(client, members.BatchMembersOpts{ + Images: []string{d.Id()}, + Projects: common.ExpandToStringSlice(unShareProjectIds.List()), + }) + if err != nil { + return fmterr.Errorf("error requesting OpenTelekomCloud ims share for private image: %w", err) + } + err = waitForImageShareOrAcceptJobSuccess(ctx, d, client, *jobId, schema.TimeoutDelete) + if err != nil { + return fmterr.Errorf("error while waiting OpenTelekomCloud ims share for private image to become deleted: %w", err) + } + } + } + + return resourceImsImageShareRead(ctx, d, meta) +} + +func resourceImsImageShareDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + config := meta.(*cfg.Config) + client, err := config.ImageV1Client(config.GetRegion(d)) + if err != nil { + return fmterr.Errorf("error creating OpenTelekomCloud image client: %s", err) + } + + projectIds := d.Get("target_project_ids") + jobId, err := members.BatchDeleteMembers(client, members.BatchMembersOpts{ + Images: []string{d.Id()}, + Projects: common.ExpandToStringSlice(projectIds.(*schema.Set).List()), + }) + if err != nil { + return fmterr.Errorf("error requesting delete OpenTelekomCloud ims share for private image: %w", err) + } + err = waitForImageShareOrAcceptJobSuccess(ctx, d, client, *jobId, schema.TimeoutDelete) + if err != nil { + return fmterr.Errorf("error while waiting OpenTelekomCloud ims share for private image to become deleted: %w", err) + } + + return nil +} + +func waitForImageShareOrAcceptJobSuccess(ctx context.Context, d *schema.ResourceData, client *golangsdk.ServiceClient, + jobId, timeout string) error { + stateConf := &resource.StateChangeConf{ + Pending: []string{"INIT", "RUNNING"}, + Target: []string{"SUCCESS"}, + Refresh: imageShareOrAcceptJobStatusRefreshFunc(jobId, client), + Timeout: d.Timeout(timeout), + Delay: 1 * time.Second, + MinTimeout: 1 * time.Second, + } + + _, err := stateConf.WaitForStateContext(ctx) + if err != nil { + return fmt.Errorf("error waiting for job (%s) success: %s", jobId, err) + } + + return nil +} + +func imageShareOrAcceptJobStatusRefreshFunc(jobId string, client *golangsdk.ServiceClient) resource.StateRefreshFunc { + return func() (interface{}, string, error) { + n, err := others.ShowJob(client, jobId) + if err != nil { + return nil, "", err + } + return n, n.Status, nil + } +} diff --git a/releasenotes/notes/ims-share-v1-044ea8869e46c762.yaml b/releasenotes/notes/ims-share-v1-044ea8869e46c762.yaml new file mode 100644 index 000000000..0320ab1be --- /dev/null +++ b/releasenotes/notes/ims-share-v1-044ea8869e46c762.yaml @@ -0,0 +1,4 @@ +--- +features: + - | + **[IMS]** New ``resource/opentelekomcloud_ims_image_share_v1`` (`#2745 `_)