From 49111227387e8611738747bcfecf4eb4d186c16c Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Tue, 16 Aug 2022 12:56:48 -0400 Subject: [PATCH 1/9] Add SAML Group link resource --- internal/provider/access_level_helpers.go | 8 + .../resource_gitlab_group_saml_link.go | 163 +++++++++++++++ .../resource_gitlab_group_saml_link_test.go | 189 ++++++++++++++++++ 3 files changed, 360 insertions(+) create mode 100644 internal/provider/resource_gitlab_group_saml_link.go create mode 100644 internal/provider/resource_gitlab_group_saml_link_test.go diff --git a/internal/provider/access_level_helpers.go b/internal/provider/access_level_helpers.go index 9b82c47f5..2d80a06f3 100644 --- a/internal/provider/access_level_helpers.go +++ b/internal/provider/access_level_helpers.go @@ -64,6 +64,14 @@ var validProjectEnvironmentStates = []string{ "available", "stopped", } +var validGroupSamlLinkAccessLevelNames = []string{ + "Guest", + "Reporter", + "Developer", + "Maintainer", + "Owner" +} + var accessLevelNameToValue = map[string]gitlab.AccessLevelValue{ "no one": gitlab.NoPermissions, "minimal": gitlab.MinimalAccessPermissions, diff --git a/internal/provider/resource_gitlab_group_saml_link.go b/internal/provider/resource_gitlab_group_saml_link.go new file mode 100644 index 000000000..99bc1411e --- /dev/null +++ b/internal/provider/resource_gitlab_group_saml_link.go @@ -0,0 +1,163 @@ +package provider + +import ( + "context" + "fmt" + "log" + "strings" + + "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" + gitlab "github.com/xanzy/go-gitlab" +) + +var _ = registerResource("gitlab_group_saml_link", func() *schema.Resource { + return &schema.Resource{ + Description: `The ` + "`gitlab_group_saml_link`" + ` resource allows to manage the lifecycle of an SAML integration with a group. + +**Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/ee/api/groups.html#saml-group-links)`, + + CreateContext: resourceGitlabGroupSamlLinkCreate, + ReadContext: resourceGitlabGroupSamlLinkRead, + DeleteContext: resourceGitlabGroupSamlLinkDelete, + Importer: &schema.ResourceImporter{ + StateContext: resourceGitlabGroupSamlLinkImporter, + }, + + Schema: map[string]*schema.Schema{ + "group_id": { + Description: "The id of the GitLab group.", + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "access_level": { + Description: fmt.Sprintf("Minimum access level for members of the SAML group. Valid values are: %s", renderValueListForDocs(validGroupSamlLinkAccessLevelNames)), + Type: schema.TypeString, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(validGroupSamlLinkAccessLevelNames, false)), + Required: true, + ForceNew: true, + }, + "saml_group_name": { + Description: "The name of the SAML group.", + Type: schema.TypeString, + Required: true, + ForceNew: true, + }, + "force": { + Description: "If true, then delete and replace an existing SAML link if one exists.", + Type: schema.TypeBool, + Optional: true, + Default: false, + ForceNew: true, + }, + }, + } +}) + +func resourceGitlabGroupSamlLinkCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*gitlab.Client) + + groupId := d.Get("group_id").(string) + accessLevel := d.Get("access_level").(string) + samlGroupName := d.Get("saml_group_name").(string) + force := d.Get("force").(bool) + + options := &gitlab.AddGroupSAMLLinkOptions{ + AccessLevel: &accessLevel, + SamlGroupName: &samlGroupName, + } + + if force { + if err := resourceGitlabGroupSamlLinkDelete(ctx, d, meta); err != nil { + return err + } + } + + log.Printf("[DEBUG] Create GitLab group SamlLink %s", d.Id()) + SamlLink, _, err := client.Groups.AddGroupSAMLLink(groupId, options, gitlab.WithContext(ctx)) + if err != nil { + return diag.FromErr(err) + } + + d.SetId(buildTwoPartID(&groupId, &SamlLink.Name)) + + return resourceGitlabGroupSamlLinkRead(ctx, d, meta) +} + +func resourceGitlabGroupSamlLinkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*gitlab.Client) + groupId := d.Get("group_id").(string) + + // Try to fetch all group links from GitLab + log.Printf("[DEBUG] Read GitLab group SamlLinks %s", groupId) + samlLinks, _, err := client.Groups.ListGroupSAMLLinks(groupId, nil, gitlab.WithContext(ctx)) + if err != nil { + return diag.FromErr(err) + } + + // If we got here and don't have links, assume GitLab is below version 12.8 and skip the check + if samlLinks != nil { + // Check if the LDAP link exists in the returned list of links + found := false + for _, samlLink := range samlLinks { + if buildTwoPartID(&groupId, &samlLink.Name) == d.Id() { + d.Set("group_id", groupId) + d.Set("access_level", samlLink.AccessLevel]) + d.Set("saml_group_name", samlLink.Name) + found = true + break + } + } + + if !found { + d.SetId("") + return diag.Errorf("SamlLink %s does not exist.", d.Id()) + } + } + + return nil +} + +func resourceGitlabGroupSamlLinkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { + client := meta.(*gitlab.Client) + groupId := d.Get("group_id").(string) + samlGroupName := d.Get("saml_group_name").(string) + + log.Printf("[DEBUG] Delete GitLab group SamlLink %s", d.Id()) + _, err := client.Groups.DeleteGroupSAMLLink(groupId, samlGroupName, cn, gitlab.WithContext(ctx)) + if err != nil { + switch err.(type) { // nolint // TODO: Resolve this golangci-lint issue: S1034: assigning the result of this type assertion to a variable (switch err := err.(type)) could eliminate type assertions in switch cases (gosimple) + case *gitlab.ErrorResponse: + // Ignore SAML links that don't exist + if strings.Contains(string(err.(*gitlab.ErrorResponse).Message), "Linked SAML group not found") { // nolint // TODO: Resolve this golangci-lint issue: S1034(related information): could eliminate this type assertion (gosimple) + log.Printf("[WARNING] %s", err) + } else { + return diag.FromErr(err) + } + default: + return diag.FromErr(err) + } + } + + return nil +} + +func resourceGitlabGroupSamlLinkImporter(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { + parts := strings.SplitN(d.Id(), ":", 2) + if len(parts) != 2 { + return nil, fmt.Errorf("invalid saml link import id (should be :): %s", d.Id()) + } + + groupId, samlGroupName := parts[0], parts[1] + d.SetId(buildTwoPartID(&groupId, &samlGroupName)) + d.Set("group_id", groupId) + d.Set("force", false) + + diag := resourceGitlabGroupSamlLinkRead(ctx, d, meta) + if diag.HasError() { + return nil, fmt.Errorf("%s", diag[0].Summary) + } + return []*schema.ResourceData{d}, nil +} diff --git a/internal/provider/resource_gitlab_group_saml_link_test.go b/internal/provider/resource_gitlab_group_saml_link_test.go new file mode 100644 index 000000000..478abdda9 --- /dev/null +++ b/internal/provider/resource_gitlab_group_saml_link_test.go @@ -0,0 +1,189 @@ +//go:build acceptance +// +build acceptance + +package provider + +import ( + "errors" + "fmt" + "testing" + + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" + "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" + "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" + "github.com/xanzy/go-gitlab" +) + +func TestAccGitlabGroupSamlLink_basic(t *testing.T) { + rInt := acctest.RandInt() + resourceName := "gitlab_group_saml_link.foo" + + // PreCheck runs after Config so load test data here + var samlLink gitlab.SAMLGroupLink + testSamlLink := gitlab.SAMLGroupLink{ + Name: "test_saml_group" + } + + resource.ParallelTest(t, resource.TestCase{ + ProviderFactories: providerFactories, + CheckDestroy: testAccCheckGitlabGroupSamlLinkDestroy, + Steps: []resource.TestStep{ + + // Create a group SAML link as a developer (uses testAccGitlabGroupLdapSamlCreateConfig for Config) + { + SkipFunc: isRunningInCE, + Config: testAccGitlabGroupSamlLinkCreateConfig(rInt, &testSamlLink), + Check: resource.ComposeTestCheckFunc( + testAccCheckGitlabGroupSamlLinkExists(resourceName, &samlLink)), + }, + + // Import the group SAML link (re-uses testAccGitlabGroupSamlLinkCreateConfig for Config) + { + SkipFunc: isRunningInCE, + ResourceName: resourceName, + ImportStateIdFunc: getGitlabGroupSamlLinkImportID(resourceName), + ImportState: true, + ImportStateVerify: true, + }, + + // Update the group SAML link to change the access level (uses testAccGitlabGroupSamlLinkUpdateConfig for Config) + { + SkipFunc: isRunningInCE, + Config: testAccGitlabGroupSamlLinkUpdateConfig(rInt, &testSamlLink), + Check: resource.ComposeTestCheckFunc( + testAccCheckGitlabGroupSamlLinkExists(resourceName, &samlLink)) + }, + }, + }) +} + +func getGitlabGroupSamlLinkImportID(resourceName string) resource.ImportStateIdFunc { + return func(s *terraform.State) (string, error) { + rs, ok := s.RootModule().Resources[resourceName] + if !ok { + return "", fmt.Errorf("Not Found: %s", resourceName) + } + + groupID := rs.Primary.Attributes["group_id"] + if groupID == "" { + return "", fmt.Errorf("No group ID is set") + } + samlGroupName := rs.Primary.Attributes["saml_group_name"] + if samlGroupName == "" { + return "", fmt.Errorf("No SAML group name is set") + } + + return fmt.Sprintf("%s:%s", groupID, samlGroupName), nil + } +} + +func testAccCheckGitlabGroupSamlLinkExists(resourceName string, samlLink *gitlab.SAMLGroupLink) resource.TestCheckFunc { + return func(s *terraform.State) error { + // Clear the "found" SAML link before checking for existence + *samlLink = gitlab.SAMLGroupLink{} + + resourceState, ok := s.RootModule().Resources[resourceName] + if !ok { + return fmt.Errorf("Not found: %s", resourceName) + } + + err := testAccGetGitlabGroupSamlLink(samlLink, resourceState) + if err != nil { + return err + } + + return nil + } +} + +func testAccCheckGitlabGroupSamlLinkDestroy(s *terraform.State) error { + // Can't check for links if the group is destroyed so make sure all groups are destroyed instead + for _, resourceState := range s.RootModule().Resources { + if resourceState.Type != "gitlab_group" { + continue + } + + group, _, err := testGitlabClient.Groups.GetGroup(resourceState.Primary.ID, nil) + if err == nil { + if group != nil && fmt.Sprintf("%d", group.ID) == resourceState.Primary.ID { + if group.MarkedForDeletionOn == nil { + return fmt.Errorf("Group still exists") + } + } + } + if !is404(err) { + return err + } + return nil + } + return nil +} + +func testAccGetGitlabGroupSamlLink(samlLink *gitlab.SAMLGroupLink, resourceState *terraform.ResourceState) error { + groupId := resourceState.Primary.Attributes["group_id"] + if groupId == "" { + return fmt.Errorf("No group ID is set") + } + + // Construct our desired SAML Link from the config values + desiredSamlLink := gitlab.SAMLGroupLink{ + AccessLevel: resourceState.Primary.Attributes["access_level"], + Name: resourceState.Primary.Attributes["saml_group_name"], + } + + desiredSamlLinkId := buildTwoPartID(&groupId, &desiredSamlLink.Name) + + // Try to fetch all group links from GitLab + currentSamlLinks, _, err := testGitlabClient.Groups.ListGroupSamlLinks(groupId, nil) + if err != nil { + return err + } + + found := false + + // Check if the SAML link exists in the returned list of links + for _, currentSamlLink := range currentSamlLinks { + if buildTwoPartID(&groupId, ¤tSamlLink.Name) == desiredSamlLinkId { + found = true + *samlLink = *currentSamlLink + break + } + } + + if !found { + return errors.New(fmt.Sprintf("SamlLink %s does not exist.", desiredSamlLinkId)) // nolint // TODO: Resolve this golangci-lint issue: S1028: should use fmt.Errorf(...) instead of errors.New(fmt.Sprintf(...)) (gosimple) + } + + return nil +} + +func testAccGitlabGroupSamlLinkCreateConfig(rInt int, testSamlLink *gitlab.SAMLGroupLink) string { + return fmt.Sprintf(` +resource "gitlab_group" "foo" { + name = "foo%d" + path = "foo%d" + description = "Terraform acceptance test - Group SAML Links 1" +} + +resource "gitlab_group_saml_link" "foo" { + group_id = "${gitlab_group.foo.id}" + access_level = "Developer" + saml_group_name = "%s" + +}`, rInt, rInt, testSamlLink.Name) +} + +func testAccGitlabGroupSamlLinkUpdateConfig(rInt int, testSamlLink *gitlab.SAMLGroupLink) string { + return fmt.Sprintf(` +resource "gitlab_group" "foo" { + name = "foo%d" + path = "foo%d" + description = "Terraform acceptance test - Group SAML Links 2" +} + +resource "gitlab_group_saml_link" "foo" { + group_id = "${gitlab_group.foo.id}" + access_level = "Maintainer" + saml_group_name = "%s" +}`, rInt, rInt, testSamlLink.Name) +} From 118db31f23f988999b9f7156fff9d9715b83073d Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Tue, 16 Aug 2022 13:03:35 -0400 Subject: [PATCH 2/9] Add examples for Group SAML links --- examples/resources/gitlab_group_saml_link/import.sh | 2 ++ examples/resources/gitlab_group_saml_link/resource.tf | 5 +++++ 2 files changed, 7 insertions(+) create mode 100644 examples/resources/gitlab_group_saml_link/import.sh create mode 100644 examples/resources/gitlab_group_saml_link/resource.tf diff --git a/examples/resources/gitlab_group_saml_link/import.sh b/examples/resources/gitlab_group_saml_link/import.sh new file mode 100644 index 000000000..46f957744 --- /dev/null +++ b/examples/resources/gitlab_group_saml_link/import.sh @@ -0,0 +1,2 @@ +# GitLab group saml links can be imported using an id made up of `group_id:saml_group_name`, e.g. +terraform import gitlab_group_saml_link.test "12345:samlgroupname1" diff --git a/examples/resources/gitlab_group_saml_link/resource.tf b/examples/resources/gitlab_group_saml_link/resource.tf new file mode 100644 index 000000000..8a9f87977 --- /dev/null +++ b/examples/resources/gitlab_group_saml_link/resource.tf @@ -0,0 +1,5 @@ +resource "gitlab_group_saml_link" "test" { + group_id = "12345" + access_level = "Developer" + saml_group_name = "samlgroupname1" +} From 056a6480339fbcca1ea2b72939bf032b8e8f69b0 Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Tue, 16 Aug 2022 13:14:59 -0400 Subject: [PATCH 3/9] Fix comma error --- internal/provider/resource_gitlab_group_saml_link_test.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/provider/resource_gitlab_group_saml_link_test.go b/internal/provider/resource_gitlab_group_saml_link_test.go index 478abdda9..d434a11c1 100644 --- a/internal/provider/resource_gitlab_group_saml_link_test.go +++ b/internal/provider/resource_gitlab_group_saml_link_test.go @@ -21,7 +21,7 @@ func TestAccGitlabGroupSamlLink_basic(t *testing.T) { // PreCheck runs after Config so load test data here var samlLink gitlab.SAMLGroupLink testSamlLink := gitlab.SAMLGroupLink{ - Name: "test_saml_group" + Name: "test_saml_group", } resource.ParallelTest(t, resource.TestCase{ @@ -51,7 +51,7 @@ func TestAccGitlabGroupSamlLink_basic(t *testing.T) { SkipFunc: isRunningInCE, Config: testAccGitlabGroupSamlLinkUpdateConfig(rInt, &testSamlLink), Check: resource.ComposeTestCheckFunc( - testAccCheckGitlabGroupSamlLinkExists(resourceName, &samlLink)) + testAccCheckGitlabGroupSamlLinkExists(resourceName, &samlLink)), }, }, }) From b290a365f885ec8ddf1ad40b6215fadf8cd87b80 Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Tue, 16 Aug 2022 13:23:47 -0400 Subject: [PATCH 4/9] Fix other formatting errors --- internal/provider/access_level_helpers.go | 2 +- internal/provider/resource_gitlab_group_saml_link.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/provider/access_level_helpers.go b/internal/provider/access_level_helpers.go index 2d80a06f3..a3f365b4f 100644 --- a/internal/provider/access_level_helpers.go +++ b/internal/provider/access_level_helpers.go @@ -69,7 +69,7 @@ var validGroupSamlLinkAccessLevelNames = []string{ "Reporter", "Developer", "Maintainer", - "Owner" + "Owner", } var accessLevelNameToValue = map[string]gitlab.AccessLevelValue{ diff --git a/internal/provider/resource_gitlab_group_saml_link.go b/internal/provider/resource_gitlab_group_saml_link.go index 99bc1411e..8961ee70e 100644 --- a/internal/provider/resource_gitlab_group_saml_link.go +++ b/internal/provider/resource_gitlab_group_saml_link.go @@ -104,7 +104,7 @@ func resourceGitlabGroupSamlLinkRead(ctx context.Context, d *schema.ResourceData for _, samlLink := range samlLinks { if buildTwoPartID(&groupId, &samlLink.Name) == d.Id() { d.Set("group_id", groupId) - d.Set("access_level", samlLink.AccessLevel]) + d.Set("access_level", samlLink.AccessLevel) d.Set("saml_group_name", samlLink.Name) found = true break From 3cbc960f187b207d9f038811b93e2bf5ff368f6b Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Tue, 16 Aug 2022 19:28:39 +0000 Subject: [PATCH 5/9] Updates from review notes --- internal/provider/access_level_helpers.go | 8 -- .../resource_gitlab_group_saml_link.go | 96 +++++++------------ .../resource_gitlab_group_saml_link_test.go | 48 +++------- 3 files changed, 49 insertions(+), 103 deletions(-) diff --git a/internal/provider/access_level_helpers.go b/internal/provider/access_level_helpers.go index a3f365b4f..9b82c47f5 100644 --- a/internal/provider/access_level_helpers.go +++ b/internal/provider/access_level_helpers.go @@ -64,14 +64,6 @@ var validProjectEnvironmentStates = []string{ "available", "stopped", } -var validGroupSamlLinkAccessLevelNames = []string{ - "Guest", - "Reporter", - "Developer", - "Maintainer", - "Owner", -} - var accessLevelNameToValue = map[string]gitlab.AccessLevelValue{ "no one": gitlab.NoPermissions, "minimal": gitlab.MinimalAccessPermissions, diff --git a/internal/provider/resource_gitlab_group_saml_link.go b/internal/provider/resource_gitlab_group_saml_link.go index 8961ee70e..f600d1ba4 100644 --- a/internal/provider/resource_gitlab_group_saml_link.go +++ b/internal/provider/resource_gitlab_group_saml_link.go @@ -4,7 +4,6 @@ import ( "context" "fmt" "log" - "strings" "github.com/hashicorp/terraform-plugin-sdk/v2/diag" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema" @@ -12,6 +11,14 @@ import ( gitlab "github.com/xanzy/go-gitlab" ) +var validGroupSamlLinkAccessLevelNames = []string{ + "Guest", + "Reporter", + "Developer", + "Maintainer", + "Owner", +} + var _ = registerResource("gitlab_group_saml_link", func() *schema.Resource { return &schema.Resource{ Description: `The ` + "`gitlab_group_saml_link`" + ` resource allows to manage the lifecycle of an SAML integration with a group. @@ -22,12 +29,12 @@ var _ = registerResource("gitlab_group_saml_link", func() *schema.Resource { ReadContext: resourceGitlabGroupSamlLinkRead, DeleteContext: resourceGitlabGroupSamlLinkDelete, Importer: &schema.ResourceImporter{ - StateContext: resourceGitlabGroupSamlLinkImporter, + StateContext: schema.ImportStatePassthroughContext, }, Schema: map[string]*schema.Schema{ - "group_id": { - Description: "The id of the GitLab group.", + "group": { + Description: "The ID or path of the group to add the SAML Group Link to.", Type: schema.TypeString, Required: true, ForceNew: true, @@ -45,13 +52,6 @@ var _ = registerResource("gitlab_group_saml_link", func() *schema.Resource { Required: true, ForceNew: true, }, - "force": { - Description: "If true, then delete and replace an existing SAML link if one exists.", - Type: schema.TypeBool, - Optional: true, - Default: false, - ForceNew: true, - }, }, } }) @@ -59,51 +59,46 @@ var _ = registerResource("gitlab_group_saml_link", func() *schema.Resource { func resourceGitlabGroupSamlLinkCreate(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*gitlab.Client) - groupId := d.Get("group_id").(string) + group := d.Get("group").(string) accessLevel := d.Get("access_level").(string) samlGroupName := d.Get("saml_group_name").(string) - force := d.Get("force").(bool) options := &gitlab.AddGroupSAMLLinkOptions{ - AccessLevel: &accessLevel, - SamlGroupName: &samlGroupName, - } - - if force { - if err := resourceGitlabGroupSamlLinkDelete(ctx, d, meta); err != nil { - return err - } + AccessLevel: gitlab.String(accessLevel), + SamlGroupName: gitlab.String(samlGroupName), } log.Printf("[DEBUG] Create GitLab group SamlLink %s", d.Id()) - SamlLink, _, err := client.Groups.AddGroupSAMLLink(groupId, options, gitlab.WithContext(ctx)) + SamlLink, _, err := client.Groups.AddGroupSAMLLink(group, options, gitlab.WithContext(ctx)) if err != nil { return diag.FromErr(err) } - d.SetId(buildTwoPartID(&groupId, &SamlLink.Name)) + d.SetId(buildTwoPartID(&group, &SamlLink.Name)) return resourceGitlabGroupSamlLinkRead(ctx, d, meta) } func resourceGitlabGroupSamlLinkRead(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*gitlab.Client) - groupId := d.Get("group_id").(string) + group, samlGroupName, parse_err := parseTwoPartID(d.Id()) + if parse_err != nil { + return diag.FromErr(parse_err) + } // Try to fetch all group links from GitLab - log.Printf("[DEBUG] Read GitLab group SamlLinks %s", groupId) - samlLinks, _, err := client.Groups.ListGroupSAMLLinks(groupId, nil, gitlab.WithContext(ctx)) + log.Printf("[DEBUG] Read GitLab group SamlLinks %s", group) + samlLinks, _, err := client.Groups.ListGroupSAMLLinks(group, nil, gitlab.WithContext(ctx)) if err != nil { return diag.FromErr(err) } - // If we got here and don't have links, assume GitLab is below version 12.8 and skip the check if samlLinks != nil { - // Check if the LDAP link exists in the returned list of links + // Check if the SAML link exists in the returned list of links found := false for _, samlLink := range samlLinks { - if buildTwoPartID(&groupId, &samlLink.Name) == d.Id() { - d.Set("group_id", groupId) + if samlLink.Name == samlGroupName { + d.Set("group", group) d.Set("access_level", samlLink.AccessLevel) d.Set("saml_group_name", samlLink.Name) found = true @@ -112,8 +107,9 @@ func resourceGitlabGroupSamlLinkRead(ctx context.Context, d *schema.ResourceData } if !found { + log.Printf("[DEBUG] GitLab SAML Group Link %d, group ID %s not found, removing from state", samlGroupName, group) d.SetId("") - return diag.Errorf("SamlLink %s does not exist.", d.Id()) + return nil } } @@ -122,42 +118,20 @@ func resourceGitlabGroupSamlLinkRead(ctx context.Context, d *schema.ResourceData func resourceGitlabGroupSamlLinkDelete(ctx context.Context, d *schema.ResourceData, meta interface{}) diag.Diagnostics { client := meta.(*gitlab.Client) - groupId := d.Get("group_id").(string) - samlGroupName := d.Get("saml_group_name").(string) + group, samlGroupName, parse_err := parseTwoPartID(d.Id()) + if parse_err != nil { + return diag.FromErr(parse_err) + } log.Printf("[DEBUG] Delete GitLab group SamlLink %s", d.Id()) - _, err := client.Groups.DeleteGroupSAMLLink(groupId, samlGroupName, cn, gitlab.WithContext(ctx)) + _, err := client.Groups.DeleteGroupSAMLLink(group, samlGroupName, gitlab.WithContext(ctx)) if err != nil { - switch err.(type) { // nolint // TODO: Resolve this golangci-lint issue: S1034: assigning the result of this type assertion to a variable (switch err := err.(type)) could eliminate type assertions in switch cases (gosimple) - case *gitlab.ErrorResponse: - // Ignore SAML links that don't exist - if strings.Contains(string(err.(*gitlab.ErrorResponse).Message), "Linked SAML group not found") { // nolint // TODO: Resolve this golangci-lint issue: S1034(related information): could eliminate this type assertion (gosimple) - log.Printf("[WARNING] %s", err) - } else { - return diag.FromErr(err) - } - default: + if is404(err) { + log.Printf("[WARNING] %s", err) + } else { return diag.FromErr(err) } } return nil } - -func resourceGitlabGroupSamlLinkImporter(ctx context.Context, d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) { - parts := strings.SplitN(d.Id(), ":", 2) - if len(parts) != 2 { - return nil, fmt.Errorf("invalid saml link import id (should be :): %s", d.Id()) - } - - groupId, samlGroupName := parts[0], parts[1] - d.SetId(buildTwoPartID(&groupId, &samlGroupName)) - d.Set("group_id", groupId) - d.Set("force", false) - - diag := resourceGitlabGroupSamlLinkRead(ctx, d, meta) - if diag.HasError() { - return nil, fmt.Errorf("%s", diag[0].Summary) - } - return []*schema.ResourceData{d}, nil -} diff --git a/internal/provider/resource_gitlab_group_saml_link_test.go b/internal/provider/resource_gitlab_group_saml_link_test.go index d434a11c1..70f0b2d85 100644 --- a/internal/provider/resource_gitlab_group_saml_link_test.go +++ b/internal/provider/resource_gitlab_group_saml_link_test.go @@ -32,7 +32,13 @@ func TestAccGitlabGroupSamlLink_basic(t *testing.T) { // Create a group SAML link as a developer (uses testAccGitlabGroupLdapSamlCreateConfig for Config) { SkipFunc: isRunningInCE, - Config: testAccGitlabGroupSamlLinkCreateConfig(rInt, &testSamlLink), + Config: fmt.Sprintf(` + resource "gitlab_group_saml_link" "foo" { + group_id = "%d" + access_level = "Developer" + saml_group_name = "%s" + + }`, rInt, rInt, testSamlLink.Name), Check: resource.ComposeTestCheckFunc( testAccCheckGitlabGroupSamlLinkExists(resourceName, &samlLink)), }, @@ -49,7 +55,12 @@ func TestAccGitlabGroupSamlLink_basic(t *testing.T) { // Update the group SAML link to change the access level (uses testAccGitlabGroupSamlLinkUpdateConfig for Config) { SkipFunc: isRunningInCE, - Config: testAccGitlabGroupSamlLinkUpdateConfig(rInt, &testSamlLink), + Config: fmt.Sprintf(` + resource "gitlab_group_saml_link" "foo" { + group_id = "%d" + access_level = "Maintainer" + saml_group_name = "%s" + }`, rInt, rInt, testSamlLink.Name), Check: resource.ComposeTestCheckFunc( testAccCheckGitlabGroupSamlLinkExists(resourceName, &samlLink)), }, @@ -155,35 +166,4 @@ func testAccGetGitlabGroupSamlLink(samlLink *gitlab.SAMLGroupLink, resourceState } return nil -} - -func testAccGitlabGroupSamlLinkCreateConfig(rInt int, testSamlLink *gitlab.SAMLGroupLink) string { - return fmt.Sprintf(` -resource "gitlab_group" "foo" { - name = "foo%d" - path = "foo%d" - description = "Terraform acceptance test - Group SAML Links 1" -} - -resource "gitlab_group_saml_link" "foo" { - group_id = "${gitlab_group.foo.id}" - access_level = "Developer" - saml_group_name = "%s" - -}`, rInt, rInt, testSamlLink.Name) -} - -func testAccGitlabGroupSamlLinkUpdateConfig(rInt int, testSamlLink *gitlab.SAMLGroupLink) string { - return fmt.Sprintf(` -resource "gitlab_group" "foo" { - name = "foo%d" - path = "foo%d" - description = "Terraform acceptance test - Group SAML Links 2" -} - -resource "gitlab_group_saml_link" "foo" { - group_id = "${gitlab_group.foo.id}" - access_level = "Maintainer" - saml_group_name = "%s" -}`, rInt, rInt, testSamlLink.Name) -} +} \ No newline at end of file From 6045e89333975c0766ced4a716ebcd3bda29cb46 Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Wed, 17 Aug 2022 13:34:55 +0000 Subject: [PATCH 6/9] Update SAML link read to use new Get endpoint --- .../resource_gitlab_group_saml_link.go | 25 ++++++------------- 1 file changed, 7 insertions(+), 18 deletions(-) diff --git a/internal/provider/resource_gitlab_group_saml_link.go b/internal/provider/resource_gitlab_group_saml_link.go index f600d1ba4..0dda4082e 100644 --- a/internal/provider/resource_gitlab_group_saml_link.go +++ b/internal/provider/resource_gitlab_group_saml_link.go @@ -88,31 +88,20 @@ func resourceGitlabGroupSamlLinkRead(ctx context.Context, d *schema.ResourceData // Try to fetch all group links from GitLab log.Printf("[DEBUG] Read GitLab group SamlLinks %s", group) - samlLinks, _, err := client.Groups.ListGroupSAMLLinks(group, nil, gitlab.WithContext(ctx)) + samlLink, _, err := client.Groups.GetGroupSAMLLink(group, samlGroupName, nil, gitlab.WithContext(ctx)) if err != nil { - return diag.FromErr(err) - } - - if samlLinks != nil { - // Check if the SAML link exists in the returned list of links - found := false - for _, samlLink := range samlLinks { - if samlLink.Name == samlGroupName { - d.Set("group", group) - d.Set("access_level", samlLink.AccessLevel) - d.Set("saml_group_name", samlLink.Name) - found = true - break - } - } - - if !found { + if is404(err) { log.Printf("[DEBUG] GitLab SAML Group Link %d, group ID %s not found, removing from state", samlGroupName, group) d.SetId("") return nil } + return diag.FromErr(err) } + d.Set("group", group) + d.Set("access_level", samlLink.AccessLevel) + d.Set("saml_group_name", samlLink.Name) + return nil } From ce93dd153a9847183578f5cc9d668b175d691c15 Mon Sep 17 00:00:00 2001 From: Timo Furrer Date: Tue, 23 Aug 2022 08:50:43 +0200 Subject: [PATCH 7/9] Various fixes in SAML group link resource --- docs/resources/group_saml_link.md | 46 ++++++ .../resource_gitlab_group_saml_link.go | 49 +++--- .../resource_gitlab_group_saml_link_test.go | 146 ++++-------------- 3 files changed, 98 insertions(+), 143 deletions(-) create mode 100644 docs/resources/group_saml_link.md diff --git a/docs/resources/group_saml_link.md b/docs/resources/group_saml_link.md new file mode 100644 index 000000000..012505253 --- /dev/null +++ b/docs/resources/group_saml_link.md @@ -0,0 +1,46 @@ +--- +# generated by https://github.com/hashicorp/terraform-plugin-docs +page_title: "gitlab_group_saml_link Resource - terraform-provider-gitlab" +subcategory: "" +description: |- + The gitlab_group_saml_link resource allows to manage the lifecycle of an SAML integration with a group. + Upstream API: GitLab REST API docs https://docs.gitlab.com/ee/api/groups.html#saml-group-links +--- + +# gitlab_group_saml_link (Resource) + +The `gitlab_group_saml_link` resource allows to manage the lifecycle of an SAML integration with a group. + +**Upstream API**: [GitLab REST API docs](https://docs.gitlab.com/ee/api/groups.html#saml-group-links) + +## Example Usage + +```terraform +resource "gitlab_group_saml_link" "test" { + group_id = "12345" + access_level = "Developer" + saml_group_name = "samlgroupname1" +} +``` + + +## Schema + +### Required + +- `access_level` (String) Access level for members of the SAML group. Valid values are: `Guest`, `Reporter`, `Developer`, `Maintainer`, `Owner`. +- `group` (String) The ID or path of the group to add the SAML Group Link to. +- `saml_group_name` (String) The name of the SAML group. + +### Read-Only + +- `id` (String) The ID of this resource. + +## Import + +Import is supported using the following syntax: + +```shell +# GitLab group saml links can be imported using an id made up of `group_id:saml_group_name`, e.g. +terraform import gitlab_group_saml_link.test "12345:samlgroupname1" +``` diff --git a/internal/provider/resource_gitlab_group_saml_link.go b/internal/provider/resource_gitlab_group_saml_link.go index 0dda4082e..c759e54da 100644 --- a/internal/provider/resource_gitlab_group_saml_link.go +++ b/internal/provider/resource_gitlab_group_saml_link.go @@ -11,15 +11,15 @@ import ( gitlab "github.com/xanzy/go-gitlab" ) -var validGroupSamlLinkAccessLevelNames = []string{ - "Guest", - "Reporter", - "Developer", - "Maintainer", - "Owner", -} - var _ = registerResource("gitlab_group_saml_link", func() *schema.Resource { + validGroupSamlLinkAccessLevelNames := []string{ + "Guest", + "Reporter", + "Developer", + "Maintainer", + "Owner", + } + return &schema.Resource{ Description: `The ` + "`gitlab_group_saml_link`" + ` resource allows to manage the lifecycle of an SAML integration with a group. @@ -39,19 +39,19 @@ var _ = registerResource("gitlab_group_saml_link", func() *schema.Resource { Required: true, ForceNew: true, }, - "access_level": { - Description: fmt.Sprintf("Minimum access level for members of the SAML group. Valid values are: %s", renderValueListForDocs(validGroupSamlLinkAccessLevelNames)), - Type: schema.TypeString, - ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(validGroupSamlLinkAccessLevelNames, false)), - Required: true, - ForceNew: true, - }, "saml_group_name": { Description: "The name of the SAML group.", Type: schema.TypeString, Required: true, ForceNew: true, }, + "access_level": { + Description: fmt.Sprintf("Access level for members of the SAML group. Valid values are: %s.", renderValueListForDocs(validGroupSamlLinkAccessLevelNames)), + Type: schema.TypeString, + ValidateDiagFunc: validation.ToDiagFunc(validation.StringInSlice(validGroupSamlLinkAccessLevelNames, false)), + Required: true, + ForceNew: true, + }, }, } }) @@ -60,22 +60,21 @@ func resourceGitlabGroupSamlLinkCreate(ctx context.Context, d *schema.ResourceDa client := meta.(*gitlab.Client) group := d.Get("group").(string) - accessLevel := d.Get("access_level").(string) samlGroupName := d.Get("saml_group_name").(string) + accessLevel := accessLevelNameToValue[d.Get("access_level").(string)] options := &gitlab.AddGroupSAMLLinkOptions{ - AccessLevel: gitlab.String(accessLevel), - SamlGroupName: gitlab.String(samlGroupName), + SAMLGroupName: gitlab.String(samlGroupName), + AccessLevel: gitlab.AccessLevel(accessLevel), } - log.Printf("[DEBUG] Create GitLab group SamlLink %s", d.Id()) + log.Printf("[DEBUG] Create GitLab Group SAML Link for group %q with name %q", group, samlGroupName) SamlLink, _, err := client.Groups.AddGroupSAMLLink(group, options, gitlab.WithContext(ctx)) if err != nil { return diag.FromErr(err) } d.SetId(buildTwoPartID(&group, &SamlLink.Name)) - return resourceGitlabGroupSamlLinkRead(ctx, d, meta) } @@ -87,11 +86,11 @@ func resourceGitlabGroupSamlLinkRead(ctx context.Context, d *schema.ResourceData } // Try to fetch all group links from GitLab - log.Printf("[DEBUG] Read GitLab group SamlLinks %s", group) + log.Printf("[DEBUG] Read GitLab Group SAML Link for group %q", group) samlLink, _, err := client.Groups.GetGroupSAMLLink(group, samlGroupName, nil, gitlab.WithContext(ctx)) if err != nil { if is404(err) { - log.Printf("[DEBUG] GitLab SAML Group Link %d, group ID %s not found, removing from state", samlGroupName, group) + log.Printf("[DEBUG] GitLab SAML Group Link %s for group ID %s not found, removing from state", samlGroupName, group) d.SetId("") return nil } @@ -99,9 +98,9 @@ func resourceGitlabGroupSamlLinkRead(ctx context.Context, d *schema.ResourceData } d.Set("group", group) - d.Set("access_level", samlLink.AccessLevel) + d.Set("access_level", accessLevelValueToName[samlLink.AccessLevel]) d.Set("saml_group_name", samlLink.Name) - + return nil } @@ -112,7 +111,7 @@ func resourceGitlabGroupSamlLinkDelete(ctx context.Context, d *schema.ResourceDa return diag.FromErr(parse_err) } - log.Printf("[DEBUG] Delete GitLab group SamlLink %s", d.Id()) + log.Printf("[DEBUG] Delete GitLab Group SAML Link for group %q with name %q", group, samlGroupName) _, err := client.Groups.DeleteGroupSAMLLink(group, samlGroupName, gitlab.WithContext(ctx)) if err != nil { if is404(err) { diff --git a/internal/provider/resource_gitlab_group_saml_link_test.go b/internal/provider/resource_gitlab_group_saml_link_test.go index 70f0b2d85..c9ee7b897 100644 --- a/internal/provider/resource_gitlab_group_saml_link_test.go +++ b/internal/provider/resource_gitlab_group_saml_link_test.go @@ -4,122 +4,70 @@ package provider import ( - "errors" "fmt" "testing" - "github.com/hashicorp/terraform-plugin-sdk/v2/helper/acctest" "github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource" "github.com/hashicorp/terraform-plugin-sdk/v2/terraform" - "github.com/xanzy/go-gitlab" ) func TestAccGitlabGroupSamlLink_basic(t *testing.T) { - rInt := acctest.RandInt() - resourceName := "gitlab_group_saml_link.foo" + testAccCheckEE(t) - // PreCheck runs after Config so load test data here - var samlLink gitlab.SAMLGroupLink - testSamlLink := gitlab.SAMLGroupLink{ - Name: "test_saml_group", - } + testGroup := testAccCreateGroups(t, 1)[0] resource.ParallelTest(t, resource.TestCase{ ProviderFactories: providerFactories, CheckDestroy: testAccCheckGitlabGroupSamlLinkDestroy, Steps: []resource.TestStep{ - // Create a group SAML link as a developer (uses testAccGitlabGroupLdapSamlCreateConfig for Config) + // Create a group SAML link as a developer { - SkipFunc: isRunningInCE, Config: fmt.Sprintf(` - resource "gitlab_group_saml_link" "foo" { - group_id = "%d" - access_level = "Developer" - saml_group_name = "%s" - - }`, rInt, rInt, testSamlLink.Name), - Check: resource.ComposeTestCheckFunc( - testAccCheckGitlabGroupSamlLinkExists(resourceName, &samlLink)), - }, + resource "gitlab_group_saml_link" "this" { + group = "%d" + access_level = "Developer" + saml_group_name = "test_saml_group" - // Import the group SAML link (re-uses testAccGitlabGroupSamlLinkCreateConfig for Config) + } + `, testGroup.ID), + }, + // Verify Import { - SkipFunc: isRunningInCE, - ResourceName: resourceName, - ImportStateIdFunc: getGitlabGroupSamlLinkImportID(resourceName), + ResourceName: "gitlab_group_saml_link.this", ImportState: true, ImportStateVerify: true, }, - - // Update the group SAML link to change the access level (uses testAccGitlabGroupSamlLinkUpdateConfig for Config) + // Update the group SAML link to change the access level { - SkipFunc: isRunningInCE, Config: fmt.Sprintf(` - resource "gitlab_group_saml_link" "foo" { - group_id = "%d" - access_level = "Maintainer" - saml_group_name = "%s" - }`, rInt, rInt, testSamlLink.Name), - Check: resource.ComposeTestCheckFunc( - testAccCheckGitlabGroupSamlLinkExists(resourceName, &samlLink)), + resource "gitlab_group_saml_link" "this" { + group = "%d" + access_level = "Maintainer" + saml_group_name = "test_saml_group" + + } + `, testGroup.ID), }, }, }) } -func getGitlabGroupSamlLinkImportID(resourceName string) resource.ImportStateIdFunc { - return func(s *terraform.State) (string, error) { - rs, ok := s.RootModule().Resources[resourceName] - if !ok { - return "", fmt.Errorf("Not Found: %s", resourceName) - } - - groupID := rs.Primary.Attributes["group_id"] - if groupID == "" { - return "", fmt.Errorf("No group ID is set") - } - samlGroupName := rs.Primary.Attributes["saml_group_name"] - if samlGroupName == "" { - return "", fmt.Errorf("No SAML group name is set") - } - - return fmt.Sprintf("%s:%s", groupID, samlGroupName), nil - } -} - -func testAccCheckGitlabGroupSamlLinkExists(resourceName string, samlLink *gitlab.SAMLGroupLink) resource.TestCheckFunc { - return func(s *terraform.State) error { - // Clear the "found" SAML link before checking for existence - *samlLink = gitlab.SAMLGroupLink{} - - resourceState, ok := s.RootModule().Resources[resourceName] - if !ok { - return fmt.Errorf("Not found: %s", resourceName) +func testAccCheckGitlabGroupSamlLinkDestroy(s *terraform.State) error { + for _, resourceState := range s.RootModule().Resources { + if resourceState.Type != "gitlab_group_saml_link" { + continue } - err := testAccGetGitlabGroupSamlLink(samlLink, resourceState) + group, samlGroupName, err := parseTwoPartID(resourceState.Primary.ID) if err != nil { return err } - return nil - } -} - -func testAccCheckGitlabGroupSamlLinkDestroy(s *terraform.State) error { - // Can't check for links if the group is destroyed so make sure all groups are destroyed instead - for _, resourceState := range s.RootModule().Resources { - if resourceState.Type != "gitlab_group" { - continue - } - - group, _, err := testGitlabClient.Groups.GetGroup(resourceState.Primary.ID, nil) + samlGroupLink, _, err := testGitlabClient.Groups.GetGroupSAMLLink(group, samlGroupName) if err == nil { - if group != nil && fmt.Sprintf("%d", group.ID) == resourceState.Primary.ID { - if group.MarkedForDeletionOn == nil { - return fmt.Errorf("Group still exists") - } + if samlGroupLink != nil { + return fmt.Errorf("SAML Group Link still exists") } } if !is404(err) { @@ -129,41 +77,3 @@ func testAccCheckGitlabGroupSamlLinkDestroy(s *terraform.State) error { } return nil } - -func testAccGetGitlabGroupSamlLink(samlLink *gitlab.SAMLGroupLink, resourceState *terraform.ResourceState) error { - groupId := resourceState.Primary.Attributes["group_id"] - if groupId == "" { - return fmt.Errorf("No group ID is set") - } - - // Construct our desired SAML Link from the config values - desiredSamlLink := gitlab.SAMLGroupLink{ - AccessLevel: resourceState.Primary.Attributes["access_level"], - Name: resourceState.Primary.Attributes["saml_group_name"], - } - - desiredSamlLinkId := buildTwoPartID(&groupId, &desiredSamlLink.Name) - - // Try to fetch all group links from GitLab - currentSamlLinks, _, err := testGitlabClient.Groups.ListGroupSamlLinks(groupId, nil) - if err != nil { - return err - } - - found := false - - // Check if the SAML link exists in the returned list of links - for _, currentSamlLink := range currentSamlLinks { - if buildTwoPartID(&groupId, ¤tSamlLink.Name) == desiredSamlLinkId { - found = true - *samlLink = *currentSamlLink - break - } - } - - if !found { - return errors.New(fmt.Sprintf("SamlLink %s does not exist.", desiredSamlLinkId)) // nolint // TODO: Resolve this golangci-lint issue: S1028: should use fmt.Errorf(...) instead of errors.New(fmt.Sprintf(...)) (gosimple) - } - - return nil -} \ No newline at end of file From ef998f2f297daa164a11e76b41c9bfb825fdfd89 Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Tue, 23 Aug 2022 19:04:32 +0000 Subject: [PATCH 8/9] Don't run acceptance tests on less than 15.3 --- internal/provider/resource_gitlab_group_saml_link_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/internal/provider/resource_gitlab_group_saml_link_test.go b/internal/provider/resource_gitlab_group_saml_link_test.go index c9ee7b897..f9030c327 100644 --- a/internal/provider/resource_gitlab_group_saml_link_test.go +++ b/internal/provider/resource_gitlab_group_saml_link_test.go @@ -12,7 +12,7 @@ import ( ) func TestAccGitlabGroupSamlLink_basic(t *testing.T) { - testAccCheckEE(t) + testAccRequiresAtLeast(t, "15.3") testGroup := testAccCreateGroups(t, 1)[0] From 857a9c4eb2e17244d6c2815a6044ab422be2aef7 Mon Sep 17 00:00:00 2001 From: Matt Hodgson Date: Wed, 24 Aug 2022 16:05:21 +0000 Subject: [PATCH 9/9] Requested updates to SAML link resource --- docs/resources/group_saml_link.md | 2 +- internal/provider/resource_gitlab_group_saml_link.go | 10 +++++----- .../provider/resource_gitlab_group_saml_link_test.go | 5 +++-- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/resources/group_saml_link.md b/docs/resources/group_saml_link.md index 012505253..3b681b561 100644 --- a/docs/resources/group_saml_link.md +++ b/docs/resources/group_saml_link.md @@ -28,7 +28,7 @@ resource "gitlab_group_saml_link" "test" { ### Required -- `access_level` (String) Access level for members of the SAML group. Valid values are: `Guest`, `Reporter`, `Developer`, `Maintainer`, `Owner`. +- `access_level` (String) Access level for members of the SAML group. Valid values are: `guest`, `reporter`, `developer`, `maintainer`, `owner`. - `group` (String) The ID or path of the group to add the SAML Group Link to. - `saml_group_name` (String) The name of the SAML group. diff --git a/internal/provider/resource_gitlab_group_saml_link.go b/internal/provider/resource_gitlab_group_saml_link.go index c759e54da..12d1b6fcd 100644 --- a/internal/provider/resource_gitlab_group_saml_link.go +++ b/internal/provider/resource_gitlab_group_saml_link.go @@ -13,11 +13,11 @@ import ( var _ = registerResource("gitlab_group_saml_link", func() *schema.Resource { validGroupSamlLinkAccessLevelNames := []string{ - "Guest", - "Reporter", - "Developer", - "Maintainer", - "Owner", + "guest", + "reporter", + "developer", + "maintainer", + "owner", } return &schema.Resource{ diff --git a/internal/provider/resource_gitlab_group_saml_link_test.go b/internal/provider/resource_gitlab_group_saml_link_test.go index f9030c327..0cd5486de 100644 --- a/internal/provider/resource_gitlab_group_saml_link_test.go +++ b/internal/provider/resource_gitlab_group_saml_link_test.go @@ -12,6 +12,7 @@ import ( ) func TestAccGitlabGroupSamlLink_basic(t *testing.T) { + testAccCheckEE(t) testAccRequiresAtLeast(t, "15.3") testGroup := testAccCreateGroups(t, 1)[0] @@ -26,7 +27,7 @@ func TestAccGitlabGroupSamlLink_basic(t *testing.T) { Config: fmt.Sprintf(` resource "gitlab_group_saml_link" "this" { group = "%d" - access_level = "Developer" + access_level = "developer" saml_group_name = "test_saml_group" } @@ -43,7 +44,7 @@ func TestAccGitlabGroupSamlLink_basic(t *testing.T) { Config: fmt.Sprintf(` resource "gitlab_group_saml_link" "this" { group = "%d" - access_level = "Maintainer" + access_level = "maintainer" saml_group_name = "test_saml_group" }