Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add optional flag to disable api key return #63

Merged
merged 3 commits into from
Oct 24, 2023
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 31 additions & 21 deletions cloudsmith/resource_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,9 @@ func resourceServiceCreate(ctx context.Context, d *schema.ResourceData, m interf
// Unfortunately this means that if the user needs to rotate the service's
// API key then the only way to do it and have it reflected properly in
// Terraform is to taint the whole resource and let Terraform recreate it.
d.Set("key", service.GetKey())

if requiredBool(d, "return_api_key") {
d.Set("key", service.GetKey())
}
checkerFunc := func() error {
req := pc.APIClient.OrgsApi.OrgsServicesRead(pc.Auth, org, d.Id())
if _, resp, err := pc.APIClient.OrgsApi.OrgsServicesReadExecute(req); err != nil {
Expand Down Expand Up @@ -152,29 +153,32 @@ func resourceServiceRead(ctx context.Context, d *schema.ResourceData, m interfac
// they'll need to recreate the resource if they want to pull the new key
// into Terraform. This can be accomplished by tainting.
var diags diag.Diagnostics
existingKey := requiredString(d, "key")
if existingKey == importSentinel {
diags = append(diags, diag.Diagnostic{
Severity: diag.Warning,
Summary: "API key unavailable for imported services",
Detail: "API keys are only available via the Cloudsmith API at the time a service " +
"is created, and therefore it is not possible to retrieve the current API key for " +
"a service which has been imported. If the API key value is needed within Terraform" +
"then the resource can be tainted post-import to recreate it and store the key.",
AttributePath: cty.Path{cty.GetAttrStep{Name: "key"}},
})
} else {
existingLastFour := existingKey[len(existingKey)-4:]
newLastFour := service.GetKey()[len(service.GetKey())-4:]
if existingLastFour != newLastFour {
if requiredBool(d, "return_api_key") {
BartoszBlizniak marked this conversation as resolved.
Show resolved Hide resolved
existingKey := requiredString(d, "key")
if existingKey == importSentinel {
diags = append(diags, diag.Diagnostic{
Severity: diag.Warning,
Summary: "API key has changed",
Detail: "API key for this service has changed outside of Terraform. If this " +
"key is used within Terraform the resource must be tainted or otherwise " +
"recreated to retrieve the new value.",
Summary: "API key unavailable for imported services",
Detail: "API keys are only available via the Cloudsmith API at the time a service " +
"is created, and therefore it is not possible to retrieve the current API key for " +
"a service which has been imported. If the API key value is needed within Terraform" +
"then the resource can be tainted post-import to recreate it and store the key.",
AttributePath: cty.Path{cty.GetAttrStep{Name: "key"}},
})
} else {
d.Set("key", "disabled")
BartoszBlizniak marked this conversation as resolved.
Show resolved Hide resolved
existingLastFour := existingKey[len(existingKey)-4:]
BartoszBlizniak marked this conversation as resolved.
Show resolved Hide resolved
newLastFour := service.GetKey()[len(service.GetKey())-4:]
if existingLastFour != newLastFour {
diags = append(diags, diag.Diagnostic{
Severity: diag.Warning,
Summary: "API key has changed",
Detail: "API key for this service has changed outside of Terraform. If this " +
"key is used within Terraform, the resource must be tainted or otherwise " +
"recreated to retrieve the new value.",
AttributePath: cty.Path{cty.GetAttrStep{Name: "key"}},
})
}
}
}

Expand Down Expand Up @@ -318,6 +322,12 @@ func resourceService() *schema.Resource {
},
Optional: true,
},
"return_api_key": {
Type: schema.TypeBool,
Description: "Whether to include the service's API key in Terraform state.",
Optional: true,
Default: true,
},
},
}
}
19 changes: 18 additions & 1 deletion cloudsmith/resource_service_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import (
// NOTE: It is not necessary to check properties that have been explicitly set
// as Terraform performs a drift/plan check after every step anyway. Only
// computed properties need explicitly checked.
// TestAccService_basic runs a series of tests for the cloudsmith_service resource.
func TestAccService_basic(t *testing.T) {
t.Parallel()

Expand Down Expand Up @@ -69,6 +70,14 @@ func TestAccService_basic(t *testing.T) {
}),
),
},
{
Config: testAccServiceConfigNoAPIKey,
Check: resource.ComposeTestCheckFunc(
testAccServiceCheckExists("cloudsmith_service.test"),
// check that the key attribute is explicitly an empty string
resource.TestCheckResourceAttr("cloudsmith_service.test", "key", "disabled"),
),
},
{
ResourceName: "cloudsmith_service.test",
ImportState: true,
Expand All @@ -81,7 +90,7 @@ func TestAccService_basic(t *testing.T) {
), nil
},
ImportStateVerify: true,
ImportStateVerifyIgnore: []string{"key"},
ImportStateVerifyIgnore: []string{"key", "return_api_key"},
},
},
})
Expand Down Expand Up @@ -139,6 +148,14 @@ func testAccServiceCheckExists(resourceName string) resource.TestCheckFunc {
}
}

var testAccServiceConfigNoAPIKey = fmt.Sprintf(`
resource "cloudsmith_service" "test" {
name = "TF Test Service No API Key"
organization = "%s"
return_api_key = false
}
`, os.Getenv("CLOUDSMITH_NAMESPACE"))

var testAccServiceConfigBasic = fmt.Sprintf(`
resource "cloudsmith_service" "test" {
name = "TF Test Service"
Expand Down
4 changes: 2 additions & 2 deletions docs/resources/service.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,12 +41,12 @@ The following arguments are supported:
* `team` - (Optional) Variable number of blocks containing team assignments for this service.
* `role` - (Optional) The service's role in the team. If defined, must be one of `Member` or `Manager`.
* `slug` - (Required) The team the service should be added to.

* `return_api_key` - (Optional) The service's API key to be returned in state. Defaults to `true`. If se to `false`, the "key" value is replaced with `disabled`
BartoszBlizniak marked this conversation as resolved.
Show resolved Hide resolved
BartoszBlizniak marked this conversation as resolved.
Show resolved Hide resolved
## Attribute Reference

In addition to all arguments above, the following attributes are exported:

* `key` - The service's API key.
* `key` - The service's API key. If `return_api_key` is set to false, the value returned will equal to `disabled`
* `slug` - The slug identifies the service in URIs or where a username is required.

## Import
Expand Down