-
Notifications
You must be signed in to change notification settings - Fork 112
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Implement the consul_acl_role_policy_attachment resource (#362)
Closes #354
- Loading branch information
1 parent
f55cfd9
commit 4154012
Showing
6 changed files
with
367 additions
and
8 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,141 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package consul | ||
|
||
import ( | ||
"fmt" | ||
"strings" | ||
|
||
consulapi "github.com/hashicorp/consul/api" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/schema" | ||
) | ||
|
||
func resourceConsulACLRolePolicyAttachment() *schema.Resource { | ||
return &schema.Resource{ | ||
Create: resourceConsulACLRolePolicyAttachmentCreate, | ||
Read: resourceConsulACLRolePolicyAttachmentRead, | ||
Delete: resourceConsulACLRolePolicyAttachmentDelete, | ||
Importer: &schema.ResourceImporter{ | ||
State: schema.ImportStatePassthrough, | ||
}, | ||
|
||
Description: "The `consul_acl_role_policy_attachment` resource links a Consul ACL role and an ACL policy. The link is implemented through an update to the Consul ACL role.\n\n~> **NOTE:** This resource is only useful to attach policies to an ACL role that has been created outside the current Terraform configuration. If the ACL role you need to attach a policy to has been created in the current Terraform configuration and will only be used in it, you should use the `policies` attribute of [`consul_acl_role`](/docs/providers/consul/r/acl_role.html).", | ||
|
||
Schema: map[string]*schema.Schema{ | ||
"role_id": { | ||
Type: schema.TypeString, | ||
ForceNew: true, | ||
Required: true, | ||
Description: "The id of the role.", | ||
}, | ||
"policy": { | ||
Type: schema.TypeString, | ||
ForceNew: true, | ||
Required: true, | ||
Description: "The policy name.", | ||
}, | ||
}, | ||
} | ||
} | ||
|
||
func resourceConsulACLRolePolicyAttachmentCreate(d *schema.ResourceData, meta interface{}) error { | ||
client, qOpts, wOpts := getClient(d, meta) | ||
|
||
roleID := d.Get("role_id").(string) | ||
|
||
role, _, err := client.ACL().RoleRead(roleID, qOpts) | ||
if err != nil { | ||
return fmt.Errorf("role '%s' not found", roleID) | ||
} | ||
|
||
newPolicyName := d.Get("policy").(string) | ||
for _, iPolicy := range role.Policies { | ||
if iPolicy.Name == newPolicyName { | ||
return fmt.Errorf("policy '%s' already attached to role", newPolicyName) | ||
} | ||
} | ||
|
||
role.Policies = append(role.Policies, &consulapi.ACLRolePolicyLink{ | ||
Name: newPolicyName, | ||
}) | ||
|
||
_, _, err = client.ACL().RoleUpdate(role, wOpts) | ||
if err != nil { | ||
return fmt.Errorf("error updating role '%q' to set new policy attachment: '%s'", roleID, err) | ||
} | ||
|
||
id := fmt.Sprintf("%s:%s", roleID, newPolicyName) | ||
|
||
d.SetId(id) | ||
|
||
return resourceConsulACLRolePolicyAttachmentRead(d, meta) | ||
} | ||
|
||
func resourceConsulACLRolePolicyAttachmentRead(d *schema.ResourceData, meta interface{}) error { | ||
client, qOpts, _ := getClient(d, meta) | ||
|
||
id := d.Id() | ||
|
||
roleID, policyName, err := parseTwoPartID(id, "role", "policy") | ||
if err != nil { | ||
return fmt.Errorf("invalid role policy attachment id '%q'", id) | ||
} | ||
|
||
role, _, err := client.ACL().RoleRead(roleID, qOpts) | ||
if err != nil { | ||
if strings.Contains(err.Error(), "role not found") { | ||
d.SetId("") | ||
return nil | ||
} | ||
return fmt.Errorf("failed to read token '%s': %v", id, err) | ||
} | ||
|
||
policyFound := false | ||
for _, iPolicy := range role.Policies { | ||
if iPolicy.Name == policyName { | ||
policyFound = true | ||
break | ||
} | ||
} | ||
if !policyFound { | ||
d.SetId("") | ||
return nil | ||
} | ||
|
||
sw := newStateWriter(d) | ||
sw.set("role_id", roleID) | ||
sw.set("policy", policyName) | ||
|
||
return sw.error() | ||
} | ||
|
||
func resourceConsulACLRolePolicyAttachmentDelete(d *schema.ResourceData, meta interface{}) error { | ||
client, qOpts, wOpts := getClient(d, meta) | ||
|
||
id := d.Id() | ||
|
||
roleID, policyName, err := parseTwoPartID(id, "role", "policy") | ||
if err != nil { | ||
return fmt.Errorf("invalid role policy attachment id '%q'", id) | ||
} | ||
|
||
role, _, err := client.ACL().RoleRead(roleID, qOpts) | ||
if err != nil { | ||
return fmt.Errorf("role '%s' not found", roleID) | ||
} | ||
|
||
for i, iPolicy := range role.Policies { | ||
if iPolicy.Name == policyName { | ||
role.Policies = append(role.Policies[:i], role.Policies[i+1:]...) | ||
break | ||
} | ||
} | ||
|
||
_, _, err = client.ACL().RoleUpdate(role, wOpts) | ||
if err != nil { | ||
return fmt.Errorf("error updating role '%q' to remove policy attachment: '%s'", roleID, err) | ||
} | ||
|
||
return nil | ||
} |
149 changes: 149 additions & 0 deletions
149
consul/resource_consul_acl_role_policy_attachment_test.go
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,149 @@ | ||
// Copyright (c) HashiCorp, Inc. | ||
// SPDX-License-Identifier: MPL-2.0 | ||
|
||
package consul | ||
|
||
import ( | ||
"fmt" | ||
"testing" | ||
|
||
consulapi "github.com/hashicorp/consul/api" | ||
"github.com/hashicorp/terraform-plugin-sdk/helper/resource" | ||
"github.com/hashicorp/terraform-plugin-sdk/terraform" | ||
) | ||
|
||
func testAccCheckConsulACLRolePolicyAttachmentDestroy(client *consulapi.Client) func(s *terraform.State) error { | ||
return func(s *terraform.State) error { | ||
for _, rs := range s.RootModule().Resources { | ||
if rs.Type != "consul_acl_role_policy_attachment" { | ||
continue | ||
} | ||
roleID, policyName, err := parseTwoPartID(rs.Primary.ID, "role", "policy") | ||
if err != nil { | ||
return fmt.Errorf("Invalid role policy attachment id '%q'", rs.Primary.ID) | ||
} | ||
role, _, _ := client.ACL().RoleRead(roleID, nil) | ||
if role != nil { | ||
for _, iPolicy := range role.Policies { | ||
if iPolicy.Name == policyName { | ||
return fmt.Errorf("role policy attachment %q still exists", rs.Primary.ID) | ||
} | ||
} | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
} | ||
|
||
func testAccCheckRolePolicyID(client *consulapi.Client) func(s *terraform.State) error { | ||
return func(s *terraform.State) error { | ||
rs, ok := s.RootModule().Resources["consul_acl_role.test_role"] | ||
if !ok { | ||
return fmt.Errorf("Not Found: consul_acl_role.test_role") | ||
} | ||
|
||
roleID := rs.Primary.Attributes["id"] | ||
if roleID == "" { | ||
return fmt.Errorf("No token ID is set") | ||
} | ||
|
||
_, _, err := client.ACL().RoleRead(roleID, nil) | ||
if err != nil { | ||
return fmt.Errorf("Unable to retrieve role %q", roleID) | ||
} | ||
|
||
// Make sure the policy has then same role_id | ||
rs, ok = s.RootModule().Resources["consul_acl_role_policy_attachment.test"] | ||
if !ok { | ||
return fmt.Errorf("Not Found: consul_acl_role_policy_attachment.test") | ||
} | ||
|
||
policyTokenID := rs.Primary.Attributes["role_id"] | ||
if policyTokenID == "" { | ||
return fmt.Errorf("No policy role_id is set") | ||
} | ||
|
||
if policyTokenID != roleID { | ||
return fmt.Errorf("%s != %s", policyTokenID, roleID) | ||
} | ||
|
||
return nil | ||
} | ||
} | ||
|
||
func TestAccConsulACLRolePolicyAttachment_basic(t *testing.T) { | ||
providers, client := startTestServer(t) | ||
|
||
resource.Test(t, resource.TestCase{ | ||
Providers: providers, | ||
CheckDestroy: testAccCheckConsulACLRolePolicyAttachmentDestroy(client), | ||
Steps: []resource.TestStep{ | ||
{ | ||
Config: testResourceACLRolePolicyAttachmentConfigBasic, | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckRolePolicyID(client), | ||
resource.TestCheckResourceAttr("consul_acl_role_policy_attachment.test", "policy", "test-attachment"), | ||
), | ||
}, | ||
{ | ||
Config: testResourceACLRolePolicyAttachmentConfigUpdate, | ||
Check: resource.ComposeTestCheckFunc( | ||
testAccCheckRolePolicyID(client), | ||
resource.TestCheckResourceAttr("consul_acl_role_policy_attachment.test", "policy", "test2"), | ||
), | ||
}, | ||
{ | ||
Config: testResourceACLRolePolicyAttachmentConfigUpdate, | ||
}, | ||
{ | ||
Config: testResourceACLRolePolicyAttachmentConfigUpdate, | ||
ResourceName: "consul_acl_role_policy_attachment.test", | ||
ImportState: true, | ||
ImportStateVerify: true, | ||
}, | ||
}, | ||
}) | ||
} | ||
|
||
const testResourceACLRolePolicyAttachmentConfigBasic = ` | ||
resource "consul_acl_policy" "test_policy" { | ||
name = "test-attachment" | ||
rules = "node \"\" { policy = \"read\" }" | ||
datacenters = [ "dc1" ] | ||
} | ||
resource "consul_acl_role" "test_role" { | ||
name = "test" | ||
lifecycle { | ||
ignore_changes = ["policies"] | ||
} | ||
} | ||
resource "consul_acl_role_policy_attachment" "test" { | ||
role_id = consul_acl_role.test_role.id | ||
policy = consul_acl_policy.test_policy.name | ||
} | ||
` | ||
|
||
const testResourceACLRolePolicyAttachmentConfigUpdate = ` | ||
// Using another resource to force the update of consul_acl_role | ||
resource "consul_acl_policy" "test2" { | ||
name = "test2" | ||
rules = "node \"\" { policy = \"read\" }" | ||
datacenters = [ "dc1" ] | ||
} | ||
resource "consul_acl_role" "test_role" { | ||
name = "test" | ||
lifecycle { | ||
ignore_changes = ["policies"] | ||
} | ||
} | ||
resource "consul_acl_role_policy_attachment" "test" { | ||
role_id = consul_acl_role.test_role.id | ||
policy = consul_acl_policy.test2.name | ||
}` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
--- | ||
# generated by https://github.com/hashicorp/terraform-plugin-docs | ||
page_title: "consul_acl_role_policy_attachment Resource - terraform-provider-consul" | ||
subcategory: "" | ||
description: |- | ||
The consul_acl_role_policy_attachment resource links a Consul ACL role and an ACL policy. The link is implemented through an update to the Consul ACL role. | ||
~> NOTE: This resource is only useful to attach policies to an ACL role that has been created outside the current Terraform configuration. If the ACL role you need to attach a policy to has been created in the current Terraform configuration and will only be used in it, you should use the policies attribute of consul_acl_role. | ||
--- | ||
|
||
# consul_acl_role_policy_attachment (Resource) | ||
|
||
The `consul_acl_role_policy_attachment` resource links a Consul ACL role and an ACL policy. The link is implemented through an update to the Consul ACL role. | ||
|
||
~> **NOTE:** This resource is only useful to attach policies to an ACL role that has been created outside the current Terraform configuration. If the ACL role you need to attach a policy to has been created in the current Terraform configuration and will only be used in it, you should use the `policies` attribute of [`consul_acl_role`](/docs/providers/consul/r/acl_role.html). | ||
|
||
## Example Usage | ||
|
||
```terraform | ||
data "consul_acl_role" "my_role" { | ||
name = "my_role" | ||
} | ||
resource "consul_acl_policy" "read_policy" { | ||
name = "read-policy" | ||
rules = "node \"\" { policy = \"read\" }" | ||
datacenters = ["dc1"] | ||
} | ||
resource "consul_acl_role_policy_attachment" "my_role_read_policy" { | ||
role_id = data.consul_acl_role.test.id | ||
policy = consul_acl_policy.read_policy.name | ||
} | ||
``` | ||
|
||
<!-- schema generated by tfplugindocs --> | ||
## Schema | ||
|
||
### Required | ||
|
||
- `policy` (String) The policy name. | ||
- `role_id` (String) The id of the role. | ||
|
||
### Read-Only | ||
|
||
- `id` (String) The ID of this resource. | ||
|
||
## Import | ||
|
||
Import is supported using the following syntax: | ||
|
||
```shell | ||
terraform import consul_acl_role_policy_attachment.my_role_read_policy 624d94ca-bc5c-f960-4e83-0a609cf588be:policy_name | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
terraform import consul_acl_role_policy_attachment.my_role_read_policy 624d94ca-bc5c-f960-4e83-0a609cf588be:policy_name |
Oops, something went wrong.