From e258f6c15f5216e210933a7ce37cdbcbe042c2db Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Mon, 6 May 2024 17:24:21 +0000 Subject: [PATCH 01/14] refactor: moved CustomVCSInput and CustomVCSUpdateInput to separate package vcs --- .../bitbucket_datacenter_integration.go | 19 ------------------- spacelift/internal/structs/vcs/input.go | 12 ++++++++++++ spacelift/internal/structs/vcs/update.go | 11 +++++++++++ ...source_bitbucket_datacenter_integration.go | 5 +++-- 4 files changed, 26 insertions(+), 21 deletions(-) create mode 100644 spacelift/internal/structs/vcs/input.go create mode 100644 spacelift/internal/structs/vcs/update.go diff --git a/spacelift/internal/structs/bitbucket_datacenter_integration.go b/spacelift/internal/structs/bitbucket_datacenter_integration.go index ff5c3559..deea1601 100644 --- a/spacelift/internal/structs/bitbucket_datacenter_integration.go +++ b/spacelift/internal/structs/bitbucket_datacenter_integration.go @@ -1,7 +1,5 @@ package structs -import "github.com/shurcooL/graphql" - // BitbucketDatacenterIntegration represents the bitbucket datacenter integration data relevant to the provider. type BitbucketDatacenterIntegration struct { ID string `graphql:"id"` @@ -18,20 +16,3 @@ type BitbucketDatacenterIntegration struct { WebhookSecret string `graphql:"webhookSecret"` WebhookURL string `graphql:"webhookURL"` } - -// CustomVCSInput represents the custom VCS input data. -type CustomVCSInput struct { - Name graphql.String `json:"name"` - SpaceID graphql.ID `json:"spaceID"` - Labels *[]graphql.String `json:"labels"` - Description *graphql.String `json:"description"` - IsDefault *graphql.Boolean `json:"isDefault"` -} - -// CustomVCSUpdateInput represents the custom VCS update input data. -type CustomVCSUpdateInput struct { - ID graphql.ID `json:"id"` - SpaceID graphql.ID `json:"space"` - Labels *[]graphql.String `json:"labels"` - Description *graphql.String `json:"description"` -} diff --git a/spacelift/internal/structs/vcs/input.go b/spacelift/internal/structs/vcs/input.go new file mode 100644 index 00000000..55dc52dc --- /dev/null +++ b/spacelift/internal/structs/vcs/input.go @@ -0,0 +1,12 @@ +package vcs + +import "github.com/shurcooL/graphql" + +// CustomVCSInput represents the custom VCS input data. +type CustomVCSInput struct { + Name graphql.String `json:"name"` + SpaceID graphql.ID `json:"spaceID"` + Labels *[]graphql.String `json:"labels"` + Description *graphql.String `json:"description"` + IsDefault *graphql.Boolean `json:"isDefault"` +} diff --git a/spacelift/internal/structs/vcs/update.go b/spacelift/internal/structs/vcs/update.go new file mode 100644 index 00000000..fb7ecf03 --- /dev/null +++ b/spacelift/internal/structs/vcs/update.go @@ -0,0 +1,11 @@ +package vcs + +import "github.com/shurcooL/graphql" + +// CustomVCSUpdateInput represents the custom VCS update input data. +type CustomVCSUpdateInput struct { + ID graphql.ID `json:"id"` + SpaceID graphql.ID `json:"space"` + Labels *[]graphql.String `json:"labels"` + Description *graphql.String `json:"description"` +} diff --git a/spacelift/resource_bitbucket_datacenter_integration.go b/spacelift/resource_bitbucket_datacenter_integration.go index 5521e9d7..ca862f9b 100644 --- a/spacelift/resource_bitbucket_datacenter_integration.go +++ b/spacelift/resource_bitbucket_datacenter_integration.go @@ -8,6 +8,7 @@ import ( "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal" "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal/structs" + "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal/structs/vcs" "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal/validations" ) @@ -107,7 +108,7 @@ func resourceBitbucketDatacenterIntegrationCreate(ctx context.Context, d *schema } variables := map[string]interface{}{ - "customInput": &structs.CustomVCSInput{ + "customInput": &vcs.CustomVCSInput{ Name: toString(d.Get(bitbucketDatacenterName)), IsDefault: toOptionalBool(d.Get(bitbucketDatacenterIsDefault)), SpaceID: toString(d.Get(bitbucketDatacenterSpaceID)), @@ -158,7 +159,7 @@ func resourceBitbucketDatacenterIntegrationUpdate(ctx context.Context, d *schema "userFacingHost": toString(d.Get(bitbucketDatacenterUserFacingHost)), "username": toOptionalString(d.Get(bitbucketDatacenterUsername)), "accessToken": toOptionalString(d.Get(bitbucketDatacenterAccessToken)), - "customInput": &structs.CustomVCSUpdateInput{ + "customInput": &vcs.CustomVCSUpdateInput{ ID: toID(d.Id()), SpaceID: toString(d.Get(bitbucketDatacenterSpaceID)), Description: toOptionalString(d.Get(bitbucketDatacenterDescription)), From fbd47a6023c3607e8cbb22d741c876dc8be23626 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Mon, 6 May 2024 17:28:50 +0000 Subject: [PATCH 02/14] refactor: moved GitLab schema field name constants to gitlab_integration.go --- spacelift/data_gitlab_integration.go | 59 +++++++++++----------------- spacelift/gitlab_integration.go | 16 ++++++++ 2 files changed, 39 insertions(+), 36 deletions(-) create mode 100644 spacelift/gitlab_integration.go diff --git a/spacelift/data_gitlab_integration.go b/spacelift/data_gitlab_integration.go index 689bf52b..0a99dcec 100644 --- a/spacelift/data_gitlab_integration.go +++ b/spacelift/data_gitlab_integration.go @@ -9,19 +9,6 @@ import ( "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal" ) -const ( - gitlabId = "id" - gitlabName = "name" - gitlabDescription = "description" - gitlabIsDefault = "is_default" - gitlabLabels = "labels" - gitlabSpaceID = "space_id" - gitlabAppID = "app_id" - gitlabAPIHost = "api_host" - gitlabWebhookSecret = "webhook_secret" - gitlabWebhookURL = "webhook_url" -) - func dataGitlabIntegration() *schema.Resource { return &schema.Resource{ Description: "`spacelift_gitlab_integration` returns details about Gitlab integration", @@ -29,27 +16,27 @@ func dataGitlabIntegration() *schema.Resource { ReadContext: dataGitlabIntegrationRead, Schema: map[string]*schema.Schema{ - gitlabId: { + gitLabID: { Type: schema.TypeString, Description: "Gitlab integration id. If not provided, the default integration will be returned", Optional: true, }, - gitlabName: { + gitLabName: { Type: schema.TypeString, Description: "Gitlab integration name", Computed: true, }, - gitlabDescription: { + gitLabDescription: { Type: schema.TypeString, Description: "Gitlab integration description", Computed: true, }, - gitlabIsDefault: { + gitLabIsDefault: { Type: schema.TypeBool, Description: "Gitlab integration is default", Computed: true, }, - gitlabLabels: { + gitLabLabels: { Type: schema.TypeList, Description: "Gitlab integration labels", Computed: true, @@ -57,22 +44,22 @@ func dataGitlabIntegration() *schema.Resource { Type: schema.TypeString, }, }, - gitlabSpaceID: { + gitLabSpaceID: { Type: schema.TypeString, Description: "Gitlab integration space id", Computed: true, }, - gitlabAPIHost: { + gitLabAPIHost: { Type: schema.TypeString, Description: "Gitlab integration api host", Computed: true, }, - gitlabWebhookSecret: { + gitLabWebhookSecret: { Type: schema.TypeString, Description: "Gitlab integration webhook secret", Computed: true, }, - gitlabWebhookURL: { + gitLabWebhookURL: { Type: schema.TypeString, Description: "Gitlab integration webhook url", Computed: true, @@ -101,7 +88,7 @@ func dataGitlabIntegrationRead(ctx context.Context, d *schema.ResourceData, meta variables := map[string]interface{}{"id": ""} - if id, ok := d.GetOk(gitlabId); ok && id != "" { + if id, ok := d.GetOk(gitLabID); ok && id != "" { variables["id"] = toID(id) } @@ -109,27 +96,27 @@ func dataGitlabIntegrationRead(ctx context.Context, d *schema.ResourceData, meta return diag.Errorf("could not query for gitlab integration: %v", err) } - gitlabIntegration := query.GitlabIntegration - if gitlabIntegration == nil { + gitLabIntegration := query.GitlabIntegration + if gitLabIntegration == nil { return diag.Errorf("gitlab integration not found") } - d.SetId(gitlabIntegration.ID) - d.Set(gitlabAPIHost, gitlabIntegration.APIHost) - d.Set(gitlabWebhookSecret, gitlabIntegration.WebhookSecret) - d.Set(gitlabWebhookURL, gitlabIntegration.WebhookURL) - d.Set(gitlabId, gitlabIntegration.ID) - d.Set(gitlabName, gitlabIntegration.Name) - d.Set(gitlabDescription, gitlabIntegration.Description) - d.Set(gitlabIsDefault, gitlabIntegration.IsDefault) - d.Set(gitlabSpaceID, gitlabIntegration.Space.ID) + d.SetId(gitLabIntegration.ID) + d.Set(gitLabAPIHost, gitLabIntegration.APIHost) + d.Set(gitLabWebhookSecret, gitLabIntegration.WebhookSecret) + d.Set(gitLabWebhookURL, gitLabIntegration.WebhookURL) + d.Set(gitLabID, gitLabIntegration.ID) + d.Set(gitLabName, gitLabIntegration.Name) + d.Set(gitLabDescription, gitLabIntegration.Description) + d.Set(gitLabIsDefault, gitLabIntegration.IsDefault) + d.Set(gitLabSpaceID, gitLabIntegration.Space.ID) labels := schema.NewSet(schema.HashString, []interface{}{}) - for _, label := range gitlabIntegration.Labels { + for _, label := range gitLabIntegration.Labels { labels.Add(label) } - d.Set(gitlabLabels, labels) + d.Set(gitLabLabels, labels) return nil } diff --git a/spacelift/gitlab_integration.go b/spacelift/gitlab_integration.go new file mode 100644 index 00000000..cfcd5637 --- /dev/null +++ b/spacelift/gitlab_integration.go @@ -0,0 +1,16 @@ +package spacelift + +const ( + gitLabID = "id" + gitLabName = "name" + gitLabDescription = "description" + gitLabIsDefault = "is_default" + gitLabLabels = "labels" + gitLabSpaceID = "space_id" + gitLabUserFacingHost = "user_facing_host" + gitLabAppID = "app_id" + gitLabAPIHost = "api_host" + gitLabToken = "private_token" + gitLabWebhookURL = "webhook_url" + gitLabWebhookSecret = "webhook_secret" +) From 0a3807f91f1228fa0d85c24d74e0c4f2ebc8e132 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Mon, 6 May 2024 17:29:34 +0000 Subject: [PATCH 03/14] feat: implemented spacelift_gitlab_integration resource --- .../internal/structs/gitlab_integration.go | 19 ++ spacelift/provider.go | 1 + spacelift/resource_gitlab_integration.go | 226 ++++++++++++++++++ 3 files changed, 246 insertions(+) create mode 100644 spacelift/internal/structs/gitlab_integration.go create mode 100644 spacelift/resource_gitlab_integration.go diff --git a/spacelift/internal/structs/gitlab_integration.go b/spacelift/internal/structs/gitlab_integration.go new file mode 100644 index 00000000..1b192fb5 --- /dev/null +++ b/spacelift/internal/structs/gitlab_integration.go @@ -0,0 +1,19 @@ +package structs + +// GitLabIntegration represents an GitLab identity provided by the Spacelift +// integration. +type GitLabIntegration struct { + ID string `graphql:"id"` + Name string `graphql:"name"` + Space struct { + ID string `graphql:"id"` + } `graphql:"space"` + IsDefault bool `graphql:"isDefault"` + Labels []string `graphql:"labels"` + Description *string `graphql:"description"` + APIHost string `graphql:"apiHost"` + UserFacingHost string `graphql:"userFacingHost"` + Token string `graphql:"privateToken"` + WebhookSecret string `graphql:"webhookSecret"` + WebhookURL string `graphql:"webhookURL"` +} diff --git a/spacelift/provider.go b/spacelift/provider.go index 8f840e44..f67a3544 100644 --- a/spacelift/provider.go +++ b/spacelift/provider.go @@ -113,6 +113,7 @@ func Provider(commit, version string) plugin.ProviderFunc { "spacelift_drift_detection": resourceDriftDetection(), "spacelift_environment_variable": resourceEnvironmentVariable(), "spacelift_gcp_service_account": resourceGCPServiceAccount(), + "spacelift_gitlab_integration": resourceGitLabIntegration(), "spacelift_idp_group_mapping": resourceIdpGroupMapping(), "spacelift_module": resourceModule(), "spacelift_mounted_file": resourceMountedFile(), diff --git a/spacelift/resource_gitlab_integration.go b/spacelift/resource_gitlab_integration.go new file mode 100644 index 00000000..9d351c0b --- /dev/null +++ b/spacelift/resource_gitlab_integration.go @@ -0,0 +1,226 @@ +package spacelift + +import ( + "context" + + "github.com/hashicorp/terraform-plugin-sdk/v2/diag" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" + + "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal" + "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal/structs" + "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal/structs/vcs" + "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal/validations" +) + +func resourceGitLabIntegration() *schema.Resource { + return &schema.Resource{ + Description: "`spacelift_gitlab_integration` represents an integration with an GitLab instance", + CreateContext: resourceGitLabIntegrationCreate, + ReadContext: resourceGitLabIntegrationRead, + UpdateContext: resourceGitLabIntegrationUpdate, + DeleteContext: resourceGitLabIntegrationDelete, + + Importer: &schema.ResourceImporter{ + StateContext: schema.ImportStatePassthroughContext, + }, + + Schema: map[string]*schema.Schema{ + gitLabID: { + Type: schema.TypeString, + Description: "GitLab integration id.", + Computed: true, + }, + gitLabName: { + Type: schema.TypeString, + Description: "The friendly name of the integration", + Required: true, + ForceNew: true, + ValidateDiagFunc: validations.DisallowEmptyString, + }, + gitLabDescription: { + Type: schema.TypeString, + Description: "The friendly name of the integration", + Optional: true, + ValidateDiagFunc: validations.DisallowEmptyString, + }, + gitLabAPIHost: { + Type: schema.TypeString, + Required: true, + Description: "API host URL", + ValidateFunc: validation.IsURLWithHTTPS, + }, + gitLabUserFacingHost: { + Type: schema.TypeString, + Optional: true, + Computed: true, + Description: "User facing host URL. If not set defaults to `" + gitLabAPIHost + "`", + ValidateFunc: validation.IsURLWithHTTPS, + }, + gitLabToken: { + Type: schema.TypeString, + Description: "The GitLab API Token", + Required: true, + ValidateDiagFunc: validations.DisallowEmptyString, + }, + gitLabLabels: { + Type: schema.TypeSet, + Description: "Labels to set on the integration", + Elem: &schema.Schema{ + Type: schema.TypeString, + ValidateDiagFunc: validations.DisallowEmptyString, + }, + Optional: true, + }, + gitLabSpaceID: { + Type: schema.TypeString, + Description: "ID (slug) of the space the integration is in.", + Optional: true, + Computed: true, + ForceNew: true, + ValidateDiagFunc: validations.DisallowEmptyString, + }, + gitLabIsDefault: { + Type: schema.TypeBool, + Description: "Is the GitLab integration the default for all spaces? If set to `true` a a space must be specified in `" + gitLabSpaceID + "`", + Optional: true, + Default: false, + }, + gitLabWebhookSecret: { + Type: schema.TypeString, + Description: "Secret for webhooks originating from GitLab repositories", + Computed: true, + Sensitive: true, + }, + gitLabWebhookURL: { + Type: schema.TypeString, + Description: "URL for webhooks originating from GitLab repositories", + Computed: true, + }, + }, + } +} + +func resourceGitLabIntegrationCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var mutation struct { + CreateGitLabIntegration structs.GitLabIntegration `graphql:"gitlabIntegrationCreate(apiHost: $apiHost, userFacingHost: $userFacingHost, privateToken: $token, customInput: $customInput)"` + } + + spaceID, hasSpaceID := d.GetOk(gitLabSpaceID) + if !hasSpaceID { + spaceID = "root" + } + userFacingHost, hasUserFacingHost := d.GetOk(gitLabUserFacingHost) + if !hasUserFacingHost { + userFacingHost = d.Get(gitLabAPIHost) + } + variables := map[string]interface{}{ + "customInput": &vcs.CustomVCSInput{ + Name: toString(d.Get(gitLabName)), + IsDefault: toOptionalBool(d.Get(gitLabIsDefault)), + SpaceID: toString(spaceID), + Labels: toOptionalStringList(d.Get(gitLabLabels)), + Description: toOptionalString(d.Get(gitLabDescription)), + }, + "apiHost": toString(d.Get(gitLabAPIHost)), + "userFacingHost": toString(userFacingHost), + "token": toString(d.Get(gitLabToken)), + } + + if err := meta.(*internal.Client).Mutate(ctx, "GitLabIntegrationCreate", &mutation, variables); err != nil { + return diag.Errorf("could not create the GitLab integration: %v", internal.FromSpaceliftError(err)) + } + + fillGitLabIntegrationResults(d, &mutation.CreateGitLabIntegration) + + return nil +} + +func resourceGitLabIntegrationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var query struct { + GitLabIntegration *structs.GitLabIntegration `graphql:"gitLabIntegration(id: $id)"` + } + + variables := map[string]interface{}{"id": d.Id()} + if err := meta.(*internal.Client).Query(ctx, "GitLabIntegrationRead", &query, variables); err != nil { + return diag.Errorf("could not query for the bitbucket datacenter integration: %v", err) + } + + if query.GitLabIntegration == nil { + d.SetId("") + } else { + fillGitLabIntegrationResults(d, query.GitLabIntegration) + } + + return nil +} + +func resourceGitLabIntegrationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var mutation struct { + UpdateGitLabIntegration structs.GitLabIntegration `graphql:"gitLabIntegrationUpdate(apiHost: $apiHost, userFacingHost: $userFacingHost, username: $username, accessToken: $accessToken, customInput: $customInput)"` + } + + userFacingHost, hasUserFacingHost := d.GetOk(gitLabUserFacingHost) + if !hasUserFacingHost { + userFacingHost = d.Get(gitLabAPIHost) + } + + variables := map[string]interface{}{ + "privateToken": toOptionalString(d.Get(gitLabToken)), + "apiHost": toString(d.Get(gitLabAPIHost)), + "userFacingHost": toString(userFacingHost), + "customInput": &vcs.CustomVCSUpdateInput{ + ID: toID(d.Id()), + SpaceID: toString(d.Get(gitLabSpaceID)), + Description: toOptionalString(d.Get(gitLabDescription)), + Labels: toOptionalStringList(d.Get(gitLabLabels)), + }, + } + + var ret diag.Diagnostics + + if err := meta.(*internal.Client).Mutate(ctx, "GitLabIntegrationUpdate", &mutation, variables); err != nil { + ret = append(ret, diag.Errorf("could not update the GitLab integration: %v", internal.FromSpaceliftError(err))...) + } + + fillGitLabIntegrationResults(d, &mutation.UpdateGitLabIntegration) + + return ret +} + +func resourceGitLabIntegrationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + var mutation struct { + DeleteGitLabIntegration *structs.GitLabIntegration `graphql:"gitLabIntegrationDelete(id: $id)"` + } + + variables := map[string]interface{}{ + "id": toID(d.Id()), + } + + if err := meta.(*internal.Client).Mutate(ctx, "GitLabIntegrationDelete", &mutation, variables); err != nil { + return diag.Errorf("could not delete GitLab integration: %v", internal.FromSpaceliftError(err)) + } + + d.SetId("") + + return nil +} + +func fillGitLabIntegrationResults(d *schema.ResourceData, gitLabIntegration *structs.GitLabIntegration) { + d.SetId(gitLabIntegration.ID) + d.Set(gitLabName, gitLabIntegration.Name) + d.Set(gitLabSpaceID, gitLabIntegration.Space.ID) + d.Set(gitLabIsDefault, gitLabIntegration.IsDefault) + d.Set(gitLabDescription, gitLabIntegration.Description) + d.Set(gitLabAPIHost, gitLabIntegration.APIHost) + d.Set(gitLabUserFacingHost, gitLabIntegration.UserFacingHost) + d.Set(gitLabToken, gitLabIntegration.Token) + d.Set(gitLabWebhookURL, gitLabIntegration.WebhookURL) + d.Set(gitLabWebhookSecret, gitLabIntegration.WebhookSecret) + + labels := schema.NewSet(schema.HashString, []interface{}{}) + for _, label := range gitLabIntegration.Labels { + labels.Add(label) + } + d.Set(gitLabLabels, labels) +} From e75e9744e5cfd4c93bd446bdde04ba5f739bb60d Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Mon, 6 May 2024 17:30:01 +0000 Subject: [PATCH 04/14] test: added tests for spacelift_gitlab_integration resource --- spacelift/config_test.go | 2 + spacelift/resource_gitlab_integration_test.go | 137 ++++++++++++++++++ 2 files changed, 139 insertions(+) create mode 100644 spacelift/resource_gitlab_integration_test.go diff --git a/spacelift/config_test.go b/spacelift/config_test.go index 515f02f7..a1f49e3d 100644 --- a/spacelift/config_test.go +++ b/spacelift/config_test.go @@ -108,6 +108,7 @@ var testConfig struct { Default struct { Name string ID string + Token string APIHost string WebhookSecret string WebhookURL string @@ -117,6 +118,7 @@ var testConfig struct { ID string Space string APIHost string + Token string WebhookSecret string WebhookURL string } diff --git a/spacelift/resource_gitlab_integration_test.go b/spacelift/resource_gitlab_integration_test.go new file mode 100644 index 00000000..ac77049c --- /dev/null +++ b/spacelift/resource_gitlab_integration_test.go @@ -0,0 +1,137 @@ +package spacelift + +import ( + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + + . "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal/testhelpers" +) + +func TestGitLabIntegrationResource(t *testing.T) { + const resourceName = "spacelift_gitlab_integration.test" + + t.Run("creates and updates a bitbucket datacenter integration without an error", func(t *testing.T) { + random := func() string { return acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) } + + var ( + name = "my-test-gitlab-integration-" + random() + host = "https://gitlab.com/" + random() + token = "access-" + random() + descr = "description " + random() + labels = `["label1", "label2"]` + ) + + configGitLab := func(host, token, descr, labels string) string { + return ` + resource "spacelift_gitlab_integration" "test" { + name = "` + name + `" + api_host = "` + host + `" + user_facing_host = "` + host + `" + token = "` + token + `" + description = "` + descr + `" + labels = ` + labels + ` + } + ` + } + + configStack := func() string { + return ` + resource "spacelift_worker_pool" "test" { + name = "Let's create a dummy worker pool to avoid running the job ` + name + `" + space_id = "` + testConfig.SourceCode.Gitlab.SpaceLevel.Space + `" + } + + resource "spacelift_stack" "test" { + name = "stack-for-` + name + `" + repository = "` + testConfig.SourceCode.Gitlab.Repository.Name + `" + branch = "` + testConfig.SourceCode.Gitlab.Repository.Branch + `" + space_id = "` + testConfig.SourceCode.Gitlab.SpaceLevel.Space + `" + administrative = false + worker_pool_id = spacelift_worker_pool.test.id + gitlab { + namespace = "` + testConfig.SourceCode.Gitlab.Repository.Namespace + `" + id = spacelift_gitlab_integration.test.id + } + } + ` + } + + configRun := func() string { + return ` + resource "spacelift_run" "test" { + stack_id = spacelift_stack.test.id + keepers = { "branch" = "` + testConfig.SourceCode.Gitlab.Repository.Branch + `" } + } + ` + } + + spaceLevel := testConfig.SourceCode.Gitlab.SpaceLevel + + testSteps(t, []resource.TestStep{ + { + Config: configGitLab(host, token, descr, "null"), + Check: Resource( + resourceName, + Attribute(gitLabID, Equals(name)), + Attribute(gitLabID, Equals(host)), + Attribute(gitLabUserFacingHost, Equals(host)), + Attribute(gitLabToken, Equals(token)), + Attribute(gitLabWebhookURL, IsNotEmpty()), + Attribute(gitLabWebhookSecret, IsNotEmpty()), + Attribute(gitLabIsDefault, Equals("false")), + Attribute(gitLabDescription, Equals(descr)), + Attribute("labels.#", Equals("0")), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{gitLabToken}, // specified only in the config + }, + { + Config: configGitLab(host, token, "new descr", `["new label1"]`), + Check: Resource( + resourceName, + Attribute(gitLabAPIHost, Equals(host)), + Attribute(gitLabUserFacingHost, Equals(host)), + Attribute(gitLabIsDefault, Equals("false")), + Attribute(gitLabDescription, Equals("new descr")), + Attribute(gitLabLabels+".#", Equals("1")), + Attribute(gitLabLabels+".0", Equals("new label1")), + ), + }, + { + Config: configGitLab(spaceLevel.APIHost, spaceLevel.Token, descr, labels), + Check: Resource( + resourceName, + Attribute(gitLabAPIHost, Equals(spaceLevel.APIHost)), + Attribute(gitLabUserFacingHost, Equals(spaceLevel.APIHost)), + Attribute(gitLabIsDefault, Equals("false")), + Attribute(gitLabToken, Equals(spaceLevel.Token)), + Attribute(gitLabDescription, Equals(descr)), + Attribute(gitLabLabels+".#", Equals("2")), + Attribute(gitLabLabels+".0", Equals("label1")), + Attribute(gitLabLabels+".1", Equals("label2")), + ), + }, + { + Config: configGitLab(spaceLevel.APIHost, spaceLevel.Token, descr, labels) + configStack(), + Check: Resource( + "spacelift_stack.test", + Attribute("gitlab.0.id", Equals(name)), + ), + }, + { + Config: configGitLab(spaceLevel.APIHost, spaceLevel.Token, descr, labels) + configStack() + configRun(), + Check: Resource( + "spacelift_run.test", + Attribute(gitLabID, IsNotEmpty()), + Attribute("stack_id", Equals("stack-for-"+name)), + ), + }, + }) + }) +} From 772c067c5e569534f341f5f796c568646229e401 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Mon, 6 May 2024 17:30:38 +0000 Subject: [PATCH 05/14] docs: added examples and docs for spacelift_gitlab_integration resource --- docs/resources/gitlab_integration.md | 64 +++++++++++++++++++ .../spacelift_gitlab_integration/import.sh | 1 + .../spacelift_gitlab_integration/resource.tf | 17 +++++ 3 files changed, 82 insertions(+) create mode 100644 docs/resources/gitlab_integration.md create mode 100644 examples/resources/spacelift_gitlab_integration/import.sh create mode 100644 examples/resources/spacelift_gitlab_integration/resource.tf diff --git a/docs/resources/gitlab_integration.md b/docs/resources/gitlab_integration.md new file mode 100644 index 00000000..5a1bb875 --- /dev/null +++ b/docs/resources/gitlab_integration.md @@ -0,0 +1,64 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "spacelift_gitlab_integration Resource - terraform-provider-spacelift" +subcategory: "" +description: |- + spacelift_gitlab_integration represents an integration with an GitLab instance +--- + +# spacelift_gitlab_integration (Resource) + +`spacelift_gitlab_integration` represents an integration with an GitLab instance + +## Example Usage + +```terraform +resource "spacelift_gitlab_integration" "example" { + name = "Bitbucket integration" + space_id = "root" + api_host = "https://mygitlab.myorg.com" + user_facing_host = "https://mygitlab.myorg.com" + username = "bitbucket_user_name" + access_token = "ABCD-EFGhiJKlMNoPQrSTuVWxYz0123456789abCDefGhiJkL" +} + +resource "spacelift_gitlab_integration" "private-example" { + name = "GitLab Default integration" + is_default = true + api_host = "https://mygitlab.myorg.com" + user_facing_host = "https://mybitbucket.myorg.com" + token = "gitlab-token" + access_token = "ABCD-EFGhiJKlMNoPQrSTuVWxYz0123456789abCDefGhiJkL" +} +``` + + +## Schema + +### Required + +- `api_host` (String) API host URL +- `name` (String) The friendly name of the integration +- `private_token` (String) The GitLab API Token + +### Optional + +- `description` (String) The friendly name of the integration +- `is_default` (Boolean) Is the GitLab integration the default for all spaces? If set to `true` a a space must be specified in `space_id` +- `labels` (Set of String) Labels to set on the integration +- `space_id` (String) ID (slug) of the space the integration is in. +- `user_facing_host` (String) User facing host URL. If not set defaults to `api_host` + +### Read-Only + +- `id` (String) GitLab integration id. +- `webhook_secret` (String, Sensitive) Secret for webhooks originating from GitLab repositories +- `webhook_url` (String) URL for webhooks originating from GitLab repositories + +## Import + +Import is supported using the following syntax: + +```shell +terraform import spacelift_gitlab_integration.example spacelift_gitlab_integration_id +``` diff --git a/examples/resources/spacelift_gitlab_integration/import.sh b/examples/resources/spacelift_gitlab_integration/import.sh new file mode 100644 index 00000000..c46c6070 --- /dev/null +++ b/examples/resources/spacelift_gitlab_integration/import.sh @@ -0,0 +1 @@ +terraform import spacelift_gitlab_integration.example spacelift_gitlab_integration_id diff --git a/examples/resources/spacelift_gitlab_integration/resource.tf b/examples/resources/spacelift_gitlab_integration/resource.tf new file mode 100644 index 00000000..a26e087e --- /dev/null +++ b/examples/resources/spacelift_gitlab_integration/resource.tf @@ -0,0 +1,17 @@ +resource "spacelift_gitlab_integration" "example" { + name = "Bitbucket integration" + space_id = "root" + api_host = "https://mygitlab.myorg.com" + user_facing_host = "https://mygitlab.myorg.com" + username = "bitbucket_user_name" + access_token = "ABCD-EFGhiJKlMNoPQrSTuVWxYz0123456789abCDefGhiJkL" +} + +resource "spacelift_gitlab_integration" "private-example" { + name = "GitLab Default integration" + is_default = true + api_host = "https://mygitlab.myorg.com" + user_facing_host = "https://mybitbucket.myorg.com" + token = "gitlab-token" + access_token = "ABCD-EFGhiJKlMNoPQrSTuVWxYz0123456789abCDefGhiJkL" +} From 9b416ea68d3894053aabd3b6350b5f47de923a48 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Mon, 6 May 2024 17:33:13 +0000 Subject: [PATCH 06/14] chore: added .envrc for easier setup of environment variables from .env file --- .envrc | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .envrc diff --git a/.envrc b/.envrc new file mode 100644 index 00000000..992b4ccd --- /dev/null +++ b/.envrc @@ -0,0 +1,3 @@ +# shellcheck disable=all + +dotenv_if_exists From 3381d0f08b825cc97929c9ffec28d5e1911f1d4f Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Mon, 6 May 2024 17:33:58 +0000 Subject: [PATCH 07/14] cicd: added SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_TOKEN and SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_TOKEN to GitHub test workflows --- .github/workflows/test-prod.yml | 4 +++- .github/workflows/test.yml | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/.github/workflows/test-prod.yml b/.github/workflows/test-prod.yml index 3a1e2b6d..da045a33 100644 --- a/.github/workflows/test-prod.yml +++ b/.github/workflows/test-prod.yml @@ -12,7 +12,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - + - name: Install Go uses: actions/setup-go@v5 with: { go-version-file: go.mod } @@ -83,12 +83,14 @@ jobs: SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_NAME: "GitLab Default" SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_ID: "gitlab-default" SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_APIHOST: "https://gitlab.com" + SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_TOKEN: ${{ secrets.COMMON_SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_TOKEN }} SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_WEBHOOKSECRET: ${{ secrets.PROD_SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_WEBHOOKSECRET }} SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_WEBHOOKURL: ${{ secrets.PROD_SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_WEBHOOKURL }} SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_NAME: "GitLab Space Level" SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_ID: "gitlab-space-level" SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_SPACE: "tests-01HPE6H08F8HR8PJR78DPYR3TC" SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_APIHOST: "https://gitlab.com" + SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_TOKEN: ${{ secrets.COMMON_SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_TOKEN }} SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_WEBHOOKSECRET: ${{ secrets.PROD_SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_WEBHOOKSECRET }} SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_WEBHOOKURL: ${{ secrets.PROD_SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_WEBHOOKURL }} SPACELIFT_PROVIDER_TEST_SOURCECODE_AZUREDEVOPS_REPOSITORY_NAME: "spacelift-ci" diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index 4b90503b..aaf05b7b 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -14,7 +14,7 @@ jobs: steps: - name: Checkout uses: actions/checkout@v4 - + - name: Install Go uses: actions/setup-go@v5 with: { go-version-file: go.mod } @@ -85,12 +85,14 @@ jobs: SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_NAME: "GitLab Default" SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_ID: "gitlab-default" SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_APIHOST: "https://gitlab.com" + SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_TOKEN: ${{ secrets.COMMON_SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_TOKEN }} SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_WEBHOOKSECRET: ${{ secrets.PREPROD_SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_WEBHOOKSECRET }} SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_WEBHOOKURL: ${{ secrets.PREPROD_SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_DEFAULT_WEBHOOKURL }} SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_NAME: "GitLab Space Level" SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_ID: "gitlab-space-level" SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_SPACE: "tests-01HPE6ENR1AZZ638QSRQRVW4DH" SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_APIHOST: "https://gitlab.com" + SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_TOKEN: ${{ secrets.COMMON_SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_TOKEN }} SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_WEBHOOKSECRET: ${{ secrets.PREPROD_SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_WEBHOOKSECRET }} SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_WEBHOOKURL: ${{ secrets.PREPROD_SPACELIFT_PROVIDER_TEST_SOURCECODE_GITLAB_SPACELEVEL_WEBHOOKURL }} SPACELIFT_PROVIDER_TEST_SOURCECODE_AZUREDEVOPS_REPOSITORY_NAME: "spacelift-ci" From 3611a29d7be51638dd56fd22e5840a9847a147f8 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Mon, 6 May 2024 19:19:14 +0000 Subject: [PATCH 08/14] fix: fix typo in GraphQL query for GitLabIntegration and add ForceNew flag to isDefault field in schema in spacelift/resource_gitlab_integration.go --- spacelift/resource_gitlab_integration.go | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spacelift/resource_gitlab_integration.go b/spacelift/resource_gitlab_integration.go index 9d351c0b..fe6b06f4 100644 --- a/spacelift/resource_gitlab_integration.go +++ b/spacelift/resource_gitlab_integration.go @@ -85,6 +85,7 @@ func resourceGitLabIntegration() *schema.Resource { Description: "Is the GitLab integration the default for all spaces? If set to `true` a a space must be specified in `" + gitLabSpaceID + "`", Optional: true, Default: false, + ForceNew: true, // unable to update isDefault flag }, gitLabWebhookSecret: { Type: schema.TypeString, @@ -138,7 +139,7 @@ func resourceGitLabIntegrationCreate(ctx context.Context, d *schema.ResourceData func resourceGitLabIntegrationRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var query struct { - GitLabIntegration *structs.GitLabIntegration `graphql:"gitLabIntegration(id: $id)"` + GitLabIntegration *structs.GitLabIntegration `graphql:"gitlabIntegration(id: $id)"` } variables := map[string]interface{}{"id": d.Id()} @@ -157,7 +158,7 @@ func resourceGitLabIntegrationRead(ctx context.Context, d *schema.ResourceData, func resourceGitLabIntegrationUpdate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var mutation struct { - UpdateGitLabIntegration structs.GitLabIntegration `graphql:"gitLabIntegrationUpdate(apiHost: $apiHost, userFacingHost: $userFacingHost, username: $username, accessToken: $accessToken, customInput: $customInput)"` + UpdateGitLabIntegration structs.GitLabIntegration `graphql:"gitlabIntegrationUpdate(apiHost: $apiHost, userFacingHost: $userFacingHost, privateToken: $privateToken, customInput: $customInput)"` } userFacingHost, hasUserFacingHost := d.GetOk(gitLabUserFacingHost) @@ -190,7 +191,7 @@ func resourceGitLabIntegrationUpdate(ctx context.Context, d *schema.ResourceData func resourceGitLabIntegrationDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { var mutation struct { - DeleteGitLabIntegration *structs.GitLabIntegration `graphql:"gitLabIntegrationDelete(id: $id)"` + DeleteGitLabIntegration *structs.GitLabIntegration `graphql:"gitlabIntegrationDelete(id: $id)"` } variables := map[string]interface{}{ @@ -214,7 +215,6 @@ func fillGitLabIntegrationResults(d *schema.ResourceData, gitLabIntegration *str d.Set(gitLabDescription, gitLabIntegration.Description) d.Set(gitLabAPIHost, gitLabIntegration.APIHost) d.Set(gitLabUserFacingHost, gitLabIntegration.UserFacingHost) - d.Set(gitLabToken, gitLabIntegration.Token) d.Set(gitLabWebhookURL, gitLabIntegration.WebhookURL) d.Set(gitLabWebhookSecret, gitLabIntegration.WebhookSecret) From 1bc581fd8065a37ac74ef37d47e3813afb0eeec1 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Mon, 6 May 2024 19:20:01 +0000 Subject: [PATCH 09/14] refactor: rename "webhookURL" field to "webhookUrl" in GitLabIntegration struct and remove "Token" field --- spacelift/internal/structs/gitlab_integration.go | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spacelift/internal/structs/gitlab_integration.go b/spacelift/internal/structs/gitlab_integration.go index 1b192fb5..f74f72e1 100644 --- a/spacelift/internal/structs/gitlab_integration.go +++ b/spacelift/internal/structs/gitlab_integration.go @@ -13,7 +13,6 @@ type GitLabIntegration struct { Description *string `graphql:"description"` APIHost string `graphql:"apiHost"` UserFacingHost string `graphql:"userFacingHost"` - Token string `graphql:"privateToken"` WebhookSecret string `graphql:"webhookSecret"` - WebhookURL string `graphql:"webhookURL"` + WebhookURL string `graphql:"webhookUrl"` } From 1e3431291ee09f46c9954b84aa45bc2596293a9c Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Tue, 7 May 2024 12:14:29 +0000 Subject: [PATCH 10/14] refactor: changes to spacelift/resource_gitlab_integration.go * add CustomizeDiff function ensure space is set to root if the integration should be the default * make gitLabUserFacingHost required and removed code to derive it from gitLabAPIHost if not set * set default value for gitLabSpaceID to "root" --- spacelift/resource_gitlab_integration.go | 42 +++++++++++------------- 1 file changed, 20 insertions(+), 22 deletions(-) diff --git a/spacelift/resource_gitlab_integration.go b/spacelift/resource_gitlab_integration.go index fe6b06f4..7f513d53 100644 --- a/spacelift/resource_gitlab_integration.go +++ b/spacelift/resource_gitlab_integration.go @@ -2,6 +2,7 @@ package spacelift import ( "context" + "fmt" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -21,6 +22,17 @@ func resourceGitLabIntegration() *schema.Resource { UpdateContext: resourceGitLabIntegrationUpdate, DeleteContext: resourceGitLabIntegrationDelete, + CustomizeDiff: func(ctx context.Context, diff *schema.ResourceDiff, meta interface{}) error { + if diff.HasChange(gitLabIsDefault) { + isDefault := diff.Get(gitLabIsDefault).(bool) + spaceID := diff.Get(gitLabSpaceID).(string) + if isDefault && spaceID != "root" { + return fmt.Errorf(`The default integration must be in the space "root" not in %q`, spaceID) + } + } + return nil + }, + Importer: &schema.ResourceImporter{ StateContext: schema.ImportStatePassthroughContext, }, @@ -52,9 +64,8 @@ func resourceGitLabIntegration() *schema.Resource { }, gitLabUserFacingHost: { Type: schema.TypeString, - Optional: true, - Computed: true, - Description: "User facing host URL. If not set defaults to `" + gitLabAPIHost + "`", + Required: true, + Description: "User facing host URL.", ValidateFunc: validation.IsURLWithHTTPS, }, gitLabToken: { @@ -74,15 +85,15 @@ func resourceGitLabIntegration() *schema.Resource { }, gitLabSpaceID: { Type: schema.TypeString, - Description: "ID (slug) of the space the integration is in.", + Description: "ID (slug) of the space the integration is in; Default: `root`", Optional: true, - Computed: true, ForceNew: true, + Default: "root", ValidateDiagFunc: validations.DisallowEmptyString, }, gitLabIsDefault: { Type: schema.TypeBool, - Description: "Is the GitLab integration the default for all spaces? If set to `true` a a space must be specified in `" + gitLabSpaceID + "`", + Description: "Is the GitLab integration the default for all spaces? If set to `true` the space must be set to `root` in `" + gitLabSpaceID + "` or left empty which uses the default", Optional: true, Default: false, ForceNew: true, // unable to update isDefault flag @@ -107,24 +118,16 @@ func resourceGitLabIntegrationCreate(ctx context.Context, d *schema.ResourceData CreateGitLabIntegration structs.GitLabIntegration `graphql:"gitlabIntegrationCreate(apiHost: $apiHost, userFacingHost: $userFacingHost, privateToken: $token, customInput: $customInput)"` } - spaceID, hasSpaceID := d.GetOk(gitLabSpaceID) - if !hasSpaceID { - spaceID = "root" - } - userFacingHost, hasUserFacingHost := d.GetOk(gitLabUserFacingHost) - if !hasUserFacingHost { - userFacingHost = d.Get(gitLabAPIHost) - } variables := map[string]interface{}{ "customInput": &vcs.CustomVCSInput{ Name: toString(d.Get(gitLabName)), IsDefault: toOptionalBool(d.Get(gitLabIsDefault)), - SpaceID: toString(spaceID), + SpaceID: toString(d.Get(gitLabSpaceID)), Labels: toOptionalStringList(d.Get(gitLabLabels)), Description: toOptionalString(d.Get(gitLabDescription)), }, "apiHost": toString(d.Get(gitLabAPIHost)), - "userFacingHost": toString(userFacingHost), + "userFacingHost": toString(d.Get(gitLabUserFacingHost)), "token": toString(d.Get(gitLabToken)), } @@ -161,15 +164,10 @@ func resourceGitLabIntegrationUpdate(ctx context.Context, d *schema.ResourceData UpdateGitLabIntegration structs.GitLabIntegration `graphql:"gitlabIntegrationUpdate(apiHost: $apiHost, userFacingHost: $userFacingHost, privateToken: $privateToken, customInput: $customInput)"` } - userFacingHost, hasUserFacingHost := d.GetOk(gitLabUserFacingHost) - if !hasUserFacingHost { - userFacingHost = d.Get(gitLabAPIHost) - } - variables := map[string]interface{}{ "privateToken": toOptionalString(d.Get(gitLabToken)), "apiHost": toString(d.Get(gitLabAPIHost)), - "userFacingHost": toString(userFacingHost), + "userFacingHost": toString(d.Get(gitLabUserFacingHost)), "customInput": &vcs.CustomVCSUpdateInput{ ID: toID(d.Id()), SpaceID: toString(d.Get(gitLabSpaceID)), From 6f25be567d07871e4e99797c77f98253a0599b1c Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Tue, 7 May 2024 12:15:21 +0000 Subject: [PATCH 11/14] docs: updated docs for spacelift_gitlab_integration --- docs/resources/gitlab_integration.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/resources/gitlab_integration.md b/docs/resources/gitlab_integration.md index 5a1bb875..5a05dade 100644 --- a/docs/resources/gitlab_integration.md +++ b/docs/resources/gitlab_integration.md @@ -40,14 +40,14 @@ resource "spacelift_gitlab_integration" "private-example" { - `api_host` (String) API host URL - `name` (String) The friendly name of the integration - `private_token` (String) The GitLab API Token +- `user_facing_host` (String) User facing host URL. ### Optional - `description` (String) The friendly name of the integration -- `is_default` (Boolean) Is the GitLab integration the default for all spaces? If set to `true` a a space must be specified in `space_id` +- `is_default` (Boolean) Is the GitLab integration the default for all spaces? If set to `true` the space must be set to `root` in `space_id` or left empty which uses the default - `labels` (Set of String) Labels to set on the integration -- `space_id` (String) ID (slug) of the space the integration is in. -- `user_facing_host` (String) User facing host URL. If not set defaults to `api_host` +- `space_id` (String) ID (slug) of the space the integration is in; Default: `root` ### Read-Only From 3740d4d44257852aaf5549d43c2776837a0d75a8 Mon Sep 17 00:00:00 2001 From: Thomas Meckel Date: Tue, 7 May 2024 12:15:37 +0000 Subject: [PATCH 12/14] tests: updated tests for spacelift_gitlab_integration --- spacelift/resource_gitlab_integration_test.go | 68 +++++++++++++++++-- 1 file changed, 63 insertions(+), 5 deletions(-) diff --git a/spacelift/resource_gitlab_integration_test.go b/spacelift/resource_gitlab_integration_test.go index ac77049c..eb09a37f 100644 --- a/spacelift/resource_gitlab_integration_test.go +++ b/spacelift/resource_gitlab_integration_test.go @@ -12,7 +12,7 @@ import ( func TestGitLabIntegrationResource(t *testing.T) { const resourceName = "spacelift_gitlab_integration.test" - t.Run("creates and updates a bitbucket datacenter integration without an error", func(t *testing.T) { + t.Run("creates and updates a GitLab integration without an error", func(t *testing.T) { random := func() string { return acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) } var ( @@ -29,7 +29,7 @@ func TestGitLabIntegrationResource(t *testing.T) { name = "` + name + `" api_host = "` + host + `" user_facing_host = "` + host + `" - token = "` + token + `" + private_token = "` + token + `" description = "` + descr + `" labels = ` + labels + ` } @@ -74,15 +74,15 @@ func TestGitLabIntegrationResource(t *testing.T) { Config: configGitLab(host, token, descr, "null"), Check: Resource( resourceName, - Attribute(gitLabID, Equals(name)), - Attribute(gitLabID, Equals(host)), + Attribute(gitLabName, Equals(name)), + Attribute(gitLabAPIHost, Equals(host)), Attribute(gitLabUserFacingHost, Equals(host)), Attribute(gitLabToken, Equals(token)), Attribute(gitLabWebhookURL, IsNotEmpty()), Attribute(gitLabWebhookSecret, IsNotEmpty()), Attribute(gitLabIsDefault, Equals("false")), Attribute(gitLabDescription, Equals(descr)), - Attribute("labels.#", Equals("0")), + AttributeNotPresent(gitLabLabels), ), }, { @@ -135,3 +135,61 @@ func TestGitLabIntegrationResource(t *testing.T) { }) }) } + +func TestGitLabIntegrationClearLabels(t *testing.T) { + const resourceName = "spacelift_gitlab_integration.test" + + t.Run("creates and updates a GitLab integration without an error", func(t *testing.T) { + random := func() string { return acctest.RandStringFromCharSet(5, acctest.CharSetAlphaNum) } + + var ( + name = "my-test-gitlab-integration-" + random() + host = "https://gitlab.com/" + random() + token = "access-" + random() + descr = "description " + random() + ) + + configGitLab := func(host, token, descr, labels string) string { + return ` + resource "spacelift_gitlab_integration" "test" { + name = "` + name + `" + api_host = "` + host + `" + user_facing_host = "` + host + `" + private_token = "` + token + `" + description = "` + descr + `" + labels = ` + labels + ` + } + ` + } + testSteps(t, []resource.TestStep{ + { + Config: configGitLab(host, token, descr, "null"), + Check: Resource( + resourceName, + AttributeNotPresent(gitLabLabels), + ), + }, + { + ResourceName: resourceName, + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{gitLabToken}, // specified only in the config + }, + { + Config: configGitLab(host, token, "new descr", `["new label1"]`), + Check: Resource( + resourceName, + Attribute(gitLabLabels+".#", Equals("1")), + Attribute(gitLabLabels+".0", Equals("new label1")), + ), + }, + { + Config: configGitLab(host, host, descr, "null"), + Check: Resource( + resourceName, + AttributeNotPresent(gitLabLabels), + ), + }, + }) + }) +} From 374cfaa4bf8039fa0c0b31b39bd123ca87363313 Mon Sep 17 00:00:00 2001 From: Piotr Truszkowski Date: Mon, 27 May 2024 13:15:15 +0200 Subject: [PATCH 13/14] added .envrc into .gitignore --- .envrc | 3 --- .gitignore | 1 + 2 files changed, 1 insertion(+), 3 deletions(-) delete mode 100644 .envrc diff --git a/.envrc b/.envrc deleted file mode 100644 index 992b4ccd..00000000 --- a/.envrc +++ /dev/null @@ -1,3 +0,0 @@ -# shellcheck disable=all - -dotenv_if_exists diff --git a/.gitignore b/.gitignore index 114c876f..c614ff05 100644 --- a/.gitignore +++ b/.gitignore @@ -24,3 +24,4 @@ key.* dist/ test.env +.envrc From 1c86fb32d3428f692a46d851d346b7de63e42ba7 Mon Sep 17 00:00:00 2001 From: Piotr Truszkowski Date: Mon, 27 May 2024 13:19:06 +0200 Subject: [PATCH 14/14] minor fixes: naming, messaging, verifying --- docs/resources/gitlab_integration.md | 22 ++++++++-------- .../spacelift_gitlab_integration/resource.tf | 14 +++++------ spacelift/resource_gitlab_integration.go | 25 +++++++++---------- 3 files changed, 28 insertions(+), 33 deletions(-) diff --git a/docs/resources/gitlab_integration.md b/docs/resources/gitlab_integration.md index 5a05dade..d6bd6063 100644 --- a/docs/resources/gitlab_integration.md +++ b/docs/resources/gitlab_integration.md @@ -3,32 +3,30 @@ page_title: "spacelift_gitlab_integration Resource - terraform-provider-spacelift" subcategory: "" description: |- - spacelift_gitlab_integration represents an integration with an GitLab instance + spacelift_gitlab_integration represents an integration with a GitLab instance --- # spacelift_gitlab_integration (Resource) -`spacelift_gitlab_integration` represents an integration with an GitLab instance +`spacelift_gitlab_integration` represents an integration with a GitLab instance ## Example Usage ```terraform resource "spacelift_gitlab_integration" "example" { - name = "Bitbucket integration" + name = "GitLab integration (public)" space_id = "root" api_host = "https://mygitlab.myorg.com" user_facing_host = "https://mygitlab.myorg.com" - username = "bitbucket_user_name" - access_token = "ABCD-EFGhiJKlMNoPQrSTuVWxYz0123456789abCDefGhiJkL" + private_token = "gitlab-token" } resource "spacelift_gitlab_integration" "private-example" { - name = "GitLab Default integration" + name = "GitLab integration (private)" is_default = true - api_host = "https://mygitlab.myorg.com" - user_facing_host = "https://mybitbucket.myorg.com" - token = "gitlab-token" - access_token = "ABCD-EFGhiJKlMNoPQrSTuVWxYz0123456789abCDefGhiJkL" + api_host = "private://mygitlab" + user_facing_host = "https://mygitlab.myorg.com" + private_token = "gitlab-token" } ``` @@ -39,12 +37,12 @@ resource "spacelift_gitlab_integration" "private-example" { - `api_host` (String) API host URL - `name` (String) The friendly name of the integration -- `private_token` (String) The GitLab API Token +- `private_token` (String, Sensitive) The GitLab API Token - `user_facing_host` (String) User facing host URL. ### Optional -- `description` (String) The friendly name of the integration +- `description` (String) Description of the integration - `is_default` (Boolean) Is the GitLab integration the default for all spaces? If set to `true` the space must be set to `root` in `space_id` or left empty which uses the default - `labels` (Set of String) Labels to set on the integration - `space_id` (String) ID (slug) of the space the integration is in; Default: `root` diff --git a/examples/resources/spacelift_gitlab_integration/resource.tf b/examples/resources/spacelift_gitlab_integration/resource.tf index a26e087e..709d2791 100644 --- a/examples/resources/spacelift_gitlab_integration/resource.tf +++ b/examples/resources/spacelift_gitlab_integration/resource.tf @@ -1,17 +1,15 @@ resource "spacelift_gitlab_integration" "example" { - name = "Bitbucket integration" + name = "GitLab integration (public)" space_id = "root" api_host = "https://mygitlab.myorg.com" user_facing_host = "https://mygitlab.myorg.com" - username = "bitbucket_user_name" - access_token = "ABCD-EFGhiJKlMNoPQrSTuVWxYz0123456789abCDefGhiJkL" + private_token = "gitlab-token" } resource "spacelift_gitlab_integration" "private-example" { - name = "GitLab Default integration" + name = "GitLab integration (private)" is_default = true - api_host = "https://mygitlab.myorg.com" - user_facing_host = "https://mybitbucket.myorg.com" - token = "gitlab-token" - access_token = "ABCD-EFGhiJKlMNoPQrSTuVWxYz0123456789abCDefGhiJkL" + api_host = "private://mygitlab" + user_facing_host = "https://mygitlab.myorg.com" + private_token = "gitlab-token" } diff --git a/spacelift/resource_gitlab_integration.go b/spacelift/resource_gitlab_integration.go index 7f513d53..6339b46a 100644 --- a/spacelift/resource_gitlab_integration.go +++ b/spacelift/resource_gitlab_integration.go @@ -6,7 +6,6 @@ import ( "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation" "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal" "github.com/spacelift-io/terraform-provider-spacelift/spacelift/internal/structs" @@ -16,7 +15,7 @@ import ( func resourceGitLabIntegration() *schema.Resource { return &schema.Resource{ - Description: "`spacelift_gitlab_integration` represents an integration with an GitLab instance", + Description: "`spacelift_gitlab_integration` represents an integration with a GitLab instance", CreateContext: resourceGitLabIntegrationCreate, ReadContext: resourceGitLabIntegrationRead, UpdateContext: resourceGitLabIntegrationUpdate, @@ -52,26 +51,27 @@ func resourceGitLabIntegration() *schema.Resource { }, gitLabDescription: { Type: schema.TypeString, - Description: "The friendly name of the integration", + Description: "Description of the integration", Optional: true, ValidateDiagFunc: validations.DisallowEmptyString, }, gitLabAPIHost: { - Type: schema.TypeString, - Required: true, - Description: "API host URL", - ValidateFunc: validation.IsURLWithHTTPS, + Type: schema.TypeString, + Required: true, + Description: "API host URL", + ValidateDiagFunc: validations.DisallowEmptyString, }, gitLabUserFacingHost: { - Type: schema.TypeString, - Required: true, - Description: "User facing host URL.", - ValidateFunc: validation.IsURLWithHTTPS, + Type: schema.TypeString, + Required: true, + Description: "User facing host URL.", + ValidateDiagFunc: validations.DisallowEmptyString, }, gitLabToken: { Type: schema.TypeString, Description: "The GitLab API Token", Required: true, + Sensitive: true, ValidateDiagFunc: validations.DisallowEmptyString, }, gitLabLabels: { @@ -87,7 +87,6 @@ func resourceGitLabIntegration() *schema.Resource { Type: schema.TypeString, Description: "ID (slug) of the space the integration is in; Default: `root`", Optional: true, - ForceNew: true, Default: "root", ValidateDiagFunc: validations.DisallowEmptyString, }, @@ -147,7 +146,7 @@ func resourceGitLabIntegrationRead(ctx context.Context, d *schema.ResourceData, variables := map[string]interface{}{"id": d.Id()} if err := meta.(*internal.Client).Query(ctx, "GitLabIntegrationRead", &query, variables); err != nil { - return diag.Errorf("could not query for the bitbucket datacenter integration: %v", err) + return diag.Errorf("could not query for the gitlab integration: %v", err) } if query.GitLabIntegration == nil {