diff --git a/CHANGELOG.md b/CHANGELOG.md index 9d6068564..e0d99b09e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -97,6 +97,7 @@ ENHANCEMENTS: ## 2.5.1 (April 06, 2020) BUGFIXES: + * Support for soft-delete of groups and projects in Gitlab Enterprise Edition ([#282](https://github.com/gitlabhq/terraform-provider-gitlab/issues/282), [#283](https://github.com/gitlabhq/terraform-provider-gitlab/issues/283), diff --git a/gitlab/provider.go b/gitlab/provider.go index 2f6eb564f..fb30d8e5b 100644 --- a/gitlab/provider.go +++ b/gitlab/provider.go @@ -90,6 +90,7 @@ func Provider() terraform.ResourceProvider { "gitlab_project_share_group": resourceGitlabProjectShareGroup(), "gitlab_group_cluster": resourceGitlabGroupCluster(), "gitlab_group_ldap_link": resourceGitlabGroupLdapLink(), + "gitlab_instance_cluster": resourceGitlabInstanceCluster(), "gitlab_project_mirror": resourceGitlabProjectMirror(), "gitlab_project_level_mr_approvals": resourceGitlabProjectLevelMRApprovals(), }, diff --git a/gitlab/resource_gitlab_instance_cluster.go b/gitlab/resource_gitlab_instance_cluster.go new file mode 100644 index 000000000..7579e8834 --- /dev/null +++ b/gitlab/resource_gitlab_instance_cluster.go @@ -0,0 +1,254 @@ +package gitlab + +import ( + "fmt" + "log" + "strconv" + + "github.com/hashicorp/terraform-plugin-sdk/helper/schema" + "github.com/hashicorp/terraform-plugin-sdk/helper/validation" + "github.com/xanzy/go-gitlab" +) + +func resourceGitlabInstanceCluster() *schema.Resource { + return &schema.Resource{ + Create: resourceGitlabInstanceClusterCreate, + Read: resourceGitlabInstanceClusterRead, + Update: resourceGitlabInstanceClusterUpdate, + Delete: resourceGitlabInstanceClusterDelete, + Importer: &schema.ResourceImporter{ + State: schema.ImportStatePassthrough, + }, + + Schema: map[string]*schema.Schema{ + "name": { + Type: schema.TypeString, + Required: true, + }, + "domain": { + Type: schema.TypeString, + Optional: true, + }, + "enabled": { + Type: schema.TypeBool, + Optional: true, + Default: true, + ForceNew: true, + }, + "managed": { + Type: schema.TypeBool, + Optional: true, + Default: true, + ForceNew: true, + }, + "created_at": { + Type: schema.TypeString, + Computed: true, + }, + "provider_type": { + Type: schema.TypeString, + Computed: true, + }, + "platform_type": { + Type: schema.TypeString, + Computed: true, + }, + "environment_scope": { + Type: schema.TypeString, + Optional: true, + Default: "*", + }, + "cluster_type": { + Type: schema.TypeString, + Computed: true, + }, + "kubernetes_api_url": { + Type: schema.TypeString, + Required: true, + }, + "kubernetes_token": { + Type: schema.TypeString, + Required: true, + Sensitive: true, + }, + "kubernetes_ca_cert": { + Type: schema.TypeString, + Optional: true, + }, + "kubernetes_namespace": { + Type: schema.TypeString, + Optional: true, + }, + "kubernetes_authorization_type": { + Type: schema.TypeString, + Optional: true, + ForceNew: true, + Default: "rbac", + ValidateFunc: validation.StringInSlice([]string{"rbac", "abac", "unknown_authorization"}, false), + }, + "management_project_id": { + Type: schema.TypeString, + Optional: true, + }, + }, + } +} + +func resourceGitlabInstanceClusterCreate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*gitlab.Client) + + pk := gitlab.AddPlatformKubernetesOptions{ + APIURL: gitlab.String(d.Get("kubernetes_api_url").(string)), + Token: gitlab.String(d.Get("kubernetes_token").(string)), + } + + if v, ok := d.GetOk("kubernetes_ca_cert"); ok { + pk.CaCert = gitlab.String(v.(string)) + } + + if v, ok := d.GetOk("kubernetes_authorization_type"); ok { + pk.AuthorizationType = gitlab.String(v.(string)) + } + + options := &gitlab.AddClusterOptions{ + Name: gitlab.String(d.Get("name").(string)), + Enabled: gitlab.Bool(d.Get("enabled").(bool)), + Managed: gitlab.Bool(d.Get("managed").(bool)), + PlatformKubernetes: &pk, + } + + if v, ok := d.GetOk("domain"); ok { + options.Domain = gitlab.String(v.(string)) + } + + if v, ok := d.GetOk("environment_scope"); ok { + options.EnvironmentScope = gitlab.String(v.(string)) + } + + if v, ok := d.GetOk("management_project_id"); ok { + options.ManagementProjectID = gitlab.String(v.(string)) + } + + log.Printf("[DEBUG] create gitlab instance cluster %q", *options.Name) + + cluster, _, err := client.InstanceCluster.AddCluster(options) + + if err != nil { + return err + } + + clusterIdString := fmt.Sprintf("%d", cluster.ID) + d.SetId(clusterIdString) + + return resourceGitlabInstanceClusterRead(d, meta) +} + +func resourceGitlabInstanceClusterRead(d *schema.ResourceData, meta interface{}) error { + client := meta.(*gitlab.Client) + + clusterId, err := strconv.Atoi(d.Id()) + if err != nil { + return err + } + + log.Printf("[DEBUG] read gitlab instance cluster %d", clusterId) + + cluster, _, err := client.InstanceCluster.GetCluster(clusterId) + if err != nil { + return err + } + + d.Set("name", cluster.Name) + d.Set("domain", cluster.Domain) + d.Set("created_at", cluster.CreatedAt.String()) + d.Set("provider_type", cluster.ProviderType) + d.Set("platform_type", cluster.PlatformType) + d.Set("environment_scope", cluster.EnvironmentScope) + d.Set("cluster_type", cluster.ClusterType) + + d.Set("kubernetes_api_url", cluster.PlatformKubernetes.APIURL) + d.Set("kubernetes_ca_cert", cluster.PlatformKubernetes.CaCert) + d.Set("kubernetes_namespace", cluster.PlatformKubernetes.Namespace) + d.Set("kubernetes_authorization_type", cluster.PlatformKubernetes.AuthorizationType) + + if cluster.ManagementProject == nil { + d.Set("management_project_id", "") + } else { + d.Set("management_project_id", strconv.Itoa(cluster.ManagementProject.ID)) + } + + return nil +} + +func resourceGitlabInstanceClusterUpdate(d *schema.ResourceData, meta interface{}) error { + client := meta.(*gitlab.Client) + + clusterId, err := strconv.Atoi(d.Id()) + if err != nil { + return err + } + + options := &gitlab.EditClusterOptions{} + + if d.HasChange("name") { + options.Name = gitlab.String(d.Get("name").(string)) + } + + if d.HasChange("domain") { + options.Domain = gitlab.String(d.Get("domain").(string)) + } + + if d.HasChange("environment_scope") { + options.EnvironmentScope = gitlab.String(d.Get("environment_scope").(string)) + } + + pk := &gitlab.EditPlatformKubernetesOptions{} + + if d.HasChange("kubernetes_api_url") { + pk.APIURL = gitlab.String(d.Get("kubernetes_api_url").(string)) + } + + if d.HasChange("kubernetes_token") { + pk.Token = gitlab.String(d.Get("kubernetes_token").(string)) + } + + if d.HasChange("kubernetes_ca_cert") { + pk.CaCert = gitlab.String(d.Get("kubernetes_ca_cert").(string)) + } + + if d.HasChange("namespace") { + pk.Namespace = gitlab.String(d.Get("namespace").(string)) + } + + if *pk != (gitlab.EditPlatformKubernetesOptions{}) { + options.PlatformKubernetes = pk + } + + if d.HasChange("management_project_id") { + options.ManagementProjectID = gitlab.String(d.Get("management_project_id").(string)) + } + + if *options != (gitlab.EditClusterOptions{}) { + log.Printf("[DEBUG] update gitlab instance cluster %d", clusterId) + _, _, err := client.InstanceCluster.EditCluster(clusterId, options) + if err != nil { + return err + } + } + + return resourceGitlabInstanceClusterRead(d, meta) +} + +func resourceGitlabInstanceClusterDelete(d *schema.ResourceData, meta interface{}) error { + client := meta.(*gitlab.Client) + clusterId, err := strconv.Atoi(d.Id()) + if err != nil { + return err + } + + log.Printf("[DEBUG] delete gitlab instance cluster %d", clusterId) + + _, err = client.InstanceCluster.DeleteCluster(clusterId) + + return err +} diff --git a/gitlab/resource_gitlab_instance_cluster_test.go b/gitlab/resource_gitlab_instance_cluster_test.go new file mode 100644 index 000000000..cac4c3aaf --- /dev/null +++ b/gitlab/resource_gitlab_instance_cluster_test.go @@ -0,0 +1,275 @@ +package gitlab + +import ( + "fmt" + "strconv" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/terraform" + "github.com/xanzy/go-gitlab" +) + +func TestAccGitlabInstanceCluster_basic(t *testing.T) { + var cluster gitlab.InstanceCluster + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGitlabInstanceClusterDestroy, + Steps: []resource.TestStep{ + // Create an instance cluster with default options + { + Config: testAccGitlabInstanceClusterConfig(rInt, true), + Check: resource.ComposeTestCheckFunc( + testAccCheckGitlabInstanceClusterExists("gitlab_instance_cluster.foo", &cluster), + testAccCheckGitlabInstanceClusterAttributes(&cluster, &testAccGitlabInstanceClusterExpectedAttributes{ + Name: fmt.Sprintf("foo-cluster-%d", rInt), + Domain: "example.com", + EnvironmentScope: "*", + KubernetesApiURL: "https://123.123.123", + KubernetesCACert: instanceClusterFakeCert, + KubernetesAuthorizationType: "abac", + }), + ), + }, + // create an unmanaged cluster + { + Config: testAccGitlabInstanceClusterConfig(rInt, false), + Check: resource.ComposeTestCheckFunc( + testAccCheckGitlabInstanceClusterExists("gitlab_instance_cluster.foo", &cluster), + testAccCheckGitlabInstanceClusterAttributes(&cluster, &testAccGitlabInstanceClusterExpectedAttributes{ + Name: fmt.Sprintf("foo-cluster-%d", rInt), + Domain: "example.com", + EnvironmentScope: "*", + KubernetesApiURL: "https://123.123.123", + KubernetesCACert: instanceClusterFakeCert, + KubernetesAuthorizationType: "abac", + Managed: false, + }), + ), + }, + // Update cluster + { + Config: testAccGitlabInstanceClusterUpdateConfig(rInt, "abac"), + Check: resource.ComposeTestCheckFunc( + testAccCheckGitlabInstanceClusterExists("gitlab_instance_cluster.foo", &cluster), + testAccCheckGitlabInstanceClusterAttributes(&cluster, &testAccGitlabInstanceClusterExpectedAttributes{ + Name: fmt.Sprintf("foo-cluster-%d", rInt), + Domain: "example-new.com", + EnvironmentScope: "*", + KubernetesApiURL: "https://124.124.124", + KubernetesCACert: instanceClusterFakeCert, + KubernetesAuthorizationType: "abac", + }), + ), + }, + // Update authorization type cluster + { + Config: testAccGitlabInstanceClusterUpdateConfig(rInt, "rbac"), + Check: resource.ComposeTestCheckFunc( + testAccCheckGitlabInstanceClusterExists("gitlab_instance_cluster.foo", &cluster), + testAccCheckGitlabInstanceClusterAttributes(&cluster, &testAccGitlabInstanceClusterExpectedAttributes{ + Name: fmt.Sprintf("foo-cluster-%d", rInt), + Domain: "example-new.com", + EnvironmentScope: "*", + KubernetesApiURL: "https://124.124.124", + KubernetesCACert: instanceClusterFakeCert, + KubernetesAuthorizationType: "rbac", + }), + ), + }, + // Create cluster with management_project_id + { + Config: testAccGitlabInstanceClusterConfig(rInt, true), + Check: resource.ComposeTestCheckFunc( + testAccCheckGitlabInstanceClusterExists("gitlab_instance_cluster.foo", &cluster), + testAccCheckGitlabInstanceClusterAttributes(&cluster, &testAccGitlabInstanceClusterExpectedAttributes{ + Name: fmt.Sprintf("foo-cluster-%d", rInt), + Domain: "example.com", + EnvironmentScope: "*", + KubernetesApiURL: "https://123.123.123", + KubernetesCACert: instanceClusterFakeCert, + KubernetesAuthorizationType: "abac", + }), + ), + }, + }, + }) +} + +func TestAccGitlabInstanceCluster_import(t *testing.T) { + rInt := acctest.RandInt() + + resource.Test(t, resource.TestCase{ + PreCheck: func() { testAccPreCheck(t) }, + Providers: testAccProviders, + CheckDestroy: testAccCheckGitlabInstanceClusterDestroy, + Steps: []resource.TestStep{ + { + Config: testAccGitlabInstanceClusterConfig(rInt, true), + }, + { + ResourceName: "gitlab_instance_cluster.foo", + ImportState: true, + ImportStateVerify: true, + ImportStateVerifyIgnore: []string{"enabled", "kubernetes_token", "managed"}, + }, + }, + }) +} + +type testAccGitlabInstanceClusterExpectedAttributes struct { + Name string + Domain string + EnvironmentScope string + KubernetesApiURL string + KubernetesCACert string + KubernetesAuthorizationType string + ManagementProjectID string + Managed bool +} + +func testAccCheckGitlabInstanceClusterExists(n string, cluster *gitlab.InstanceCluster) resource.TestCheckFunc { + return func(s *terraform.State) error { + rs, ok := s.RootModule().Resources[n] + if !ok { + return fmt.Errorf("not found: %q", n) + } + + instanceClusterID, err := strconv.Atoi(rs.Primary.ID) + if err != nil { + return err + } + + conn := testAccProvider.Meta().(*gitlab.Client) + + gotCluster, _, err := conn.InstanceCluster.GetCluster(instanceClusterID) + if err != nil { + return err + } + + *cluster = *gotCluster + + return nil + } +} + +func testAccCheckGitlabInstanceClusterDestroy(s *terraform.State) error { + conn := testAccProvider.Meta().(*gitlab.Client) + + for _, rs := range s.RootModule().Resources { + if rs.Type != "gitlab_instance_cluster" { + continue + } + + instanceClusterID, err := strconv.Atoi(rs.Primary.ID) + if err != nil { + return err + } + + gotCluster, resp, err := conn.InstanceCluster.GetCluster(instanceClusterID) + + if err == nil { + if gotCluster != nil && gotCluster.ID == instanceClusterID { + return fmt.Errorf("instance cluster still exists") + } + } + if resp.StatusCode != 404 { + return err + } + } + + return nil +} + +func testAccCheckGitlabInstanceClusterAttributes(cluster *gitlab.InstanceCluster, want *testAccGitlabInstanceClusterExpectedAttributes) resource.TestCheckFunc { + return func(s *terraform.State) error { + if cluster.Name != want.Name { + return fmt.Errorf("got name %q; want %q", cluster.Name, want.Name) + } + + if cluster.Domain != want.Domain { + return fmt.Errorf("got domain %q; want %q", cluster.Domain, want.Domain) + } + + if cluster.EnvironmentScope != want.EnvironmentScope { + return fmt.Errorf("got environment scope %q; want %q", cluster.EnvironmentScope, want.EnvironmentScope) + } + + if cluster.PlatformKubernetes.APIURL != want.KubernetesApiURL { + return fmt.Errorf("got kubernetes api url %q; want %q", cluster.PlatformKubernetes.APIURL, want.KubernetesApiURL) + } + + if cluster.PlatformKubernetes.CaCert != want.KubernetesCACert { + return fmt.Errorf("got kubernetes ca cert %q; want %q", cluster.PlatformKubernetes.CaCert, want.KubernetesCACert) + } + + if cluster.PlatformKubernetes.AuthorizationType != want.KubernetesAuthorizationType { + return fmt.Errorf("got kubernetes authorization type %q; want %q", cluster.PlatformKubernetes.AuthorizationType, want.KubernetesAuthorizationType) + } + + return nil + } +} + +func testAccGitlabInstanceClusterConfig(rInt int, managed bool) string { + m := fmt.Sprintf("%t", managed) + fmt.Println(m) + + return fmt.Sprintf(` +variable "cert" { + default = <> gitlab_project_variable + > + gitlab_instance_cluster + > gitlab_service_github