Skip to content

Commit 994ebf6

Browse files
Merge pull request #138 from cloudsmith-io/ceng-447-support-disabling-default-entitlement-token-without-import
CENG-447: Add enabled/disable entitlement token resource
2 parents fe1e008 + 284f980 commit 994ebf6

File tree

5 files changed

+385
-0
lines changed

5 files changed

+385
-0
lines changed

cloudsmith/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,7 @@ func Provider() *schema.Provider {
6464
"cloudsmith_saml": resourceSAML(),
6565
"cloudsmith_saml_auth": resourceSAMLAuth(),
6666
"cloudsmith_repository_retention_rule": resourceRepoRetentionRule(),
67+
"cloudsmith_entitlement_control": resourceEntitlementControl(),
6768
},
6869
}
6970

cloudsmith/resource_entitlement.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,7 @@ func resourceEntitlementRead(d *schema.ResourceData, m interface{}) error {
9797
d.Set("limit_path_query", entitlement.GetLimitPathQuery())
9898
d.Set("name", entitlement.GetName())
9999
d.Set("token", entitlement.GetToken())
100+
d.Set("slug_perm", entitlement.GetSlugPerm())
100101

101102
// namespace and repository are not returned from the entitlement read
102103
// endpoint, so we can use the values stored in resource state. We rely on
@@ -260,6 +261,11 @@ func resourceEntitlement() *schema.Resource {
260261
ForceNew: true,
261262
ValidateFunc: validation.StringIsNotEmpty,
262263
},
264+
"slug_perm": {
265+
Type: schema.TypeString,
266+
Description: "The permanent slug identifier for the entitlement.",
267+
Computed: true,
268+
},
263269
"token": {
264270
Type: schema.TypeString,
265271
Description: "The literal value of the token to be created.",
Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
package cloudsmith
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"strings"
7+
8+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/validation"
10+
)
11+
12+
func entitlementControlImport(ctx context.Context, d *schema.ResourceData, m interface{}) ([]*schema.ResourceData, error) {
13+
idParts := strings.Split(d.Id(), ".")
14+
if len(idParts) != 3 {
15+
return nil, fmt.Errorf(
16+
"invalid import ID, must be of the form <namespace>.<repository>.<identifier>, got: %s", d.Id(),
17+
)
18+
}
19+
20+
d.Set("namespace", idParts[0])
21+
d.Set("repository", idParts[1])
22+
d.SetId(idParts[2])
23+
return []*schema.ResourceData{d}, nil
24+
}
25+
26+
func entitlementControlCreate(d *schema.ResourceData, m interface{}) error {
27+
pc := m.(*providerConfig)
28+
29+
namespace := requiredString(d, "namespace")
30+
repository := requiredString(d, "repository")
31+
identifier := requiredString(d, "identifier")
32+
enabled := requiredBool(d, "enabled")
33+
34+
if enabled {
35+
req := pc.APIClient.EntitlementsApi.EntitlementsEnable(pc.Auth, namespace, repository, identifier)
36+
_, err := pc.APIClient.EntitlementsApi.EntitlementsEnableExecute(req)
37+
if err != nil {
38+
return err
39+
}
40+
} else {
41+
req := pc.APIClient.EntitlementsApi.EntitlementsDisable(pc.Auth, namespace, repository, identifier)
42+
_, err := pc.APIClient.EntitlementsApi.EntitlementsDisableExecute(req)
43+
if err != nil {
44+
return err
45+
}
46+
}
47+
48+
d.SetId(identifier)
49+
return entitlementControlRead(d, m)
50+
}
51+
52+
func entitlementControlRead(d *schema.ResourceData, m interface{}) error {
53+
pc := m.(*providerConfig)
54+
namespace := requiredString(d, "namespace")
55+
repository := requiredString(d, "repository")
56+
57+
req := pc.APIClient.EntitlementsApi.EntitlementsRead(pc.Auth, namespace, repository, d.Id())
58+
entitlement, resp, err := pc.APIClient.EntitlementsApi.EntitlementsReadExecute(req)
59+
60+
if err != nil {
61+
if is404(resp) {
62+
d.SetId("")
63+
return nil
64+
}
65+
return err
66+
}
67+
68+
d.Set("enabled", entitlement.GetIsActive())
69+
return nil
70+
}
71+
72+
func entitlementControlUpdate(d *schema.ResourceData, m interface{}) error {
73+
pc := m.(*providerConfig)
74+
75+
namespace := requiredString(d, "namespace")
76+
repository := requiredString(d, "repository")
77+
enabled := requiredBool(d, "enabled")
78+
79+
if enabled {
80+
req := pc.APIClient.EntitlementsApi.EntitlementsEnable(pc.Auth, namespace, repository, d.Id())
81+
_, err := pc.APIClient.EntitlementsApi.EntitlementsEnableExecute(req)
82+
if err != nil {
83+
return err
84+
}
85+
} else {
86+
req := pc.APIClient.EntitlementsApi.EntitlementsDisable(pc.Auth, namespace, repository, d.Id())
87+
_, err := pc.APIClient.EntitlementsApi.EntitlementsDisableExecute(req)
88+
if err != nil {
89+
return err
90+
}
91+
}
92+
93+
return entitlementControlRead(d, m)
94+
}
95+
96+
func entitlementControlDelete(d *schema.ResourceData, m interface{}) error {
97+
// We don't actually delete the entitlement, just disable it
98+
pc := m.(*providerConfig)
99+
namespace := requiredString(d, "namespace")
100+
repository := requiredString(d, "repository")
101+
102+
req := pc.APIClient.EntitlementsApi.EntitlementsDisable(pc.Auth, namespace, repository, d.Id())
103+
_, err := pc.APIClient.EntitlementsApi.EntitlementsDisableExecute(req)
104+
if err != nil {
105+
return err
106+
}
107+
108+
return nil
109+
}
110+
111+
func resourceEntitlementControl() *schema.Resource {
112+
return &schema.Resource{
113+
Create: entitlementControlCreate,
114+
Read: entitlementControlRead,
115+
Update: entitlementControlUpdate,
116+
Delete: entitlementControlDelete,
117+
118+
Importer: &schema.ResourceImporter{
119+
StateContext: entitlementControlImport,
120+
},
121+
122+
Schema: map[string]*schema.Schema{
123+
"namespace": {
124+
Type: schema.TypeString,
125+
Description: "Namespace to which this entitlement belongs.",
126+
Required: true,
127+
ForceNew: true,
128+
ValidateFunc: validation.StringIsNotEmpty,
129+
},
130+
"repository": {
131+
Type: schema.TypeString,
132+
Description: "Repository to which this entitlement belongs.",
133+
Required: true,
134+
ForceNew: true,
135+
ValidateFunc: validation.StringIsNotEmpty,
136+
},
137+
"identifier": {
138+
Type: schema.TypeString,
139+
Description: "The identifier (slug_perm) of the entitlement token.",
140+
Required: true,
141+
ForceNew: true,
142+
ValidateFunc: validation.StringIsNotEmpty,
143+
},
144+
"enabled": {
145+
Type: schema.TypeBool,
146+
Description: "Whether the entitlement token is enabled or disabled.",
147+
Required: true,
148+
},
149+
},
150+
}
151+
}
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
//nolint:testpackage
2+
package cloudsmith
3+
4+
import (
5+
"fmt"
6+
"os"
7+
"testing"
8+
9+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/resource"
10+
"github.com/hashicorp/terraform-plugin-sdk/v2/terraform"
11+
)
12+
13+
// TestAccEntitlementControl_basic spins up a repository and uses its default entitlement token,
14+
// creates an entitlement control with the token disabled, verifies it exists and checks
15+
// the enabled state is set correctly. Then it changes the enabled state to true,
16+
// and verifies it's been set correctly before tearing down the resources and
17+
// verifying deletion.
18+
func TestAccEntitlementControl_basic(t *testing.T) {
19+
t.Parallel()
20+
21+
resource.Test(t, resource.TestCase{
22+
PreCheck: func() { testAccPreCheck(t) },
23+
Providers: testAccProviders,
24+
CheckDestroy: testAccEntitlementControlCheckDestroy("cloudsmith_entitlement_control.test"),
25+
Steps: []resource.TestStep{
26+
{
27+
Config: testAccEntitlementControlConfigBasic,
28+
Check: resource.ComposeTestCheckFunc(
29+
testAccEntitlementControlCheckExists("cloudsmith_entitlement_control.test"),
30+
resource.TestCheckResourceAttr("cloudsmith_entitlement_control.test", "namespace", os.Getenv("CLOUDSMITH_NAMESPACE")),
31+
resource.TestCheckResourceAttr("cloudsmith_entitlement_control.test", "enabled", "false"),
32+
),
33+
},
34+
{
35+
Config: testAccEntitlementControlConfigBasicUpdate,
36+
Check: resource.ComposeTestCheckFunc(
37+
testAccEntitlementControlCheckExists("cloudsmith_entitlement_control.test"),
38+
resource.TestCheckResourceAttr("cloudsmith_entitlement_control.test", "namespace", os.Getenv("CLOUDSMITH_NAMESPACE")),
39+
resource.TestCheckResourceAttr("cloudsmith_entitlement_control.test", "enabled", "true"),
40+
),
41+
},
42+
{
43+
ResourceName: "cloudsmith_entitlement_control.test",
44+
ImportState: true,
45+
ImportStateVerify: true,
46+
ImportStateIdFunc: func(s *terraform.State) (string, error) {
47+
resourceState := s.RootModule().Resources["cloudsmith_entitlement_control.test"]
48+
return fmt.Sprintf(
49+
"%s.%s.%s",
50+
resourceState.Primary.Attributes["namespace"],
51+
resourceState.Primary.Attributes["repository"],
52+
resourceState.Primary.ID,
53+
), nil
54+
},
55+
ImportStateVerifyIgnore: []string{
56+
"identifier", // Ignore identifier as it's used for creation but not returned in read
57+
},
58+
},
59+
},
60+
})
61+
}
62+
63+
//nolint:goerr113
64+
func testAccEntitlementControlCheckDestroy(resourceName string) resource.TestCheckFunc {
65+
return func(s *terraform.State) error {
66+
resourceState, ok := s.RootModule().Resources[resourceName]
67+
if !ok {
68+
return fmt.Errorf("resource not found: %s", resourceName)
69+
}
70+
71+
if resourceState.Primary.ID == "" {
72+
return fmt.Errorf("resource id not set")
73+
}
74+
75+
pc := testAccProvider.Meta().(*providerConfig)
76+
77+
namespace := os.Getenv("CLOUDSMITH_NAMESPACE")
78+
repository := resourceState.Primary.Attributes["repository"]
79+
identifier := resourceState.Primary.ID
80+
81+
req := pc.APIClient.EntitlementsApi.EntitlementsRead(pc.Auth, namespace, repository, identifier)
82+
entitlement, resp, err := pc.APIClient.EntitlementsApi.EntitlementsReadExecute(req)
83+
if err != nil && !is404(resp) {
84+
return fmt.Errorf("unable to verify entitlement control state: %w", err)
85+
} else if is200(resp) && entitlement.GetIsActive() {
86+
return fmt.Errorf("unable to verify entitlement control state: still enabled: %s/%s/%s", namespace, repository, identifier)
87+
}
88+
defer resp.Body.Close()
89+
90+
return nil
91+
}
92+
}
93+
94+
//nolint:goerr113
95+
func testAccEntitlementControlCheckExists(resourceName string) resource.TestCheckFunc {
96+
return func(s *terraform.State) error {
97+
resourceState, ok := s.RootModule().Resources[resourceName]
98+
if !ok {
99+
return fmt.Errorf("resource not found: %s", resourceName)
100+
}
101+
102+
if resourceState.Primary.ID == "" {
103+
return fmt.Errorf("resource id not set")
104+
}
105+
106+
pc := testAccProvider.Meta().(*providerConfig)
107+
108+
namespace := os.Getenv("CLOUDSMITH_NAMESPACE")
109+
repository := resourceState.Primary.Attributes["repository"]
110+
identifier := resourceState.Primary.ID
111+
112+
req := pc.APIClient.EntitlementsApi.EntitlementsRead(pc.Auth, namespace, repository, identifier)
113+
_, resp, err := pc.APIClient.EntitlementsApi.EntitlementsReadExecute(req)
114+
if err != nil {
115+
return fmt.Errorf("unable to verify entitlement control existence: %w", err)
116+
}
117+
defer resp.Body.Close()
118+
119+
return nil
120+
}
121+
}
122+
123+
var testAccEntitlementControlConfigBasic = fmt.Sprintf(`
124+
resource "cloudsmith_repository" "test" {
125+
name = "terraform-acc-test-ent-ctrl"
126+
namespace = "%s"
127+
}
128+
129+
data "cloudsmith_entitlement_list" "test" {
130+
namespace = resource.cloudsmith_repository.test.namespace
131+
repository = resource.cloudsmith_repository.test.slug_perm
132+
query = ["name:Default"]
133+
}
134+
135+
resource "cloudsmith_entitlement_control" "test" {
136+
namespace = resource.cloudsmith_repository.test.namespace
137+
repository = resource.cloudsmith_repository.test.slug_perm
138+
identifier = data.cloudsmith_entitlement_list.test.entitlement_tokens[0].slug_perm
139+
enabled = false
140+
}
141+
`, os.Getenv("CLOUDSMITH_NAMESPACE"))
142+
143+
var testAccEntitlementControlConfigBasicUpdate = fmt.Sprintf(`
144+
resource "cloudsmith_repository" "test" {
145+
name = "terraform-acc-test-ent-ctrl"
146+
namespace = "%s"
147+
}
148+
149+
data "cloudsmith_entitlement_list" "test" {
150+
namespace = resource.cloudsmith_repository.test.namespace
151+
repository = resource.cloudsmith_repository.test.slug_perm
152+
query = ["name:Default"]
153+
}
154+
155+
resource "cloudsmith_entitlement_control" "test" {
156+
namespace = resource.cloudsmith_repository.test.namespace
157+
repository = resource.cloudsmith_repository.test.slug_perm
158+
identifier = data.cloudsmith_entitlement_list.test.entitlement_tokens[0].slug_perm
159+
enabled = true
160+
}
161+
`, os.Getenv("CLOUDSMITH_NAMESPACE"))

0 commit comments

Comments
 (0)