Skip to content

Commit

Permalink
PR #147: fix machine user role and resource role assignment
Browse files Browse the repository at this point in the history
  • Loading branch information
ueisele authored and gregito committed Jul 31, 2024
1 parent ba8c94f commit c58dabc
Show file tree
Hide file tree
Showing 9 changed files with 322 additions and 19 deletions.
2 changes: 1 addition & 1 deletion cdp-sdk-go/cdp/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ type Client struct {
}

func NewClient(config *Config) (*Client, error) {
if err := config.loadConfig(); err != nil {
if err := config.LoadConfig(); err != nil {
return nil, err
}

Expand Down
2 changes: 1 addition & 1 deletion cdp-sdk-go/cdp/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ func NewConfig() *Config {
}
}

func (config *Config) loadConfig() error {
func (config *Config) LoadConfig() error {
if config.BaseApiPath == "" {
config.BaseApiPath = defaultBaseApiPath
}
Expand Down
22 changes: 11 additions & 11 deletions cdp-sdk-go/cdp/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ func TestGetCredentialsNotFound(t *testing.T) {
Profile: profile,
CredentialsFile: path,
}
err := emptyConfig.loadConfig()
err := emptyConfig.LoadConfig()
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -118,7 +118,7 @@ func TestGetCdpCredentials(t *testing.T) {
AccessKeyId: "value-from-config",
PrivateKey: "value-from-config"},
}
err := config.loadConfig()
err := config.LoadConfig()
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -164,9 +164,9 @@ func TestLoadConfigFileNotFound(t *testing.T) {
ConfigFile: "testdata/non-existent-file",
}
// should silently ignore non existent files
err := config.loadConfig()
err := config.LoadConfig()
if err != nil {
t.Errorf("Unexpected error from loadConfig()")
t.Errorf("Unexpected error from LoadConfig()")
}
}

Expand All @@ -176,7 +176,7 @@ func TestLoadConfigFile(t *testing.T) {
config := Config{
ConfigFile: "testdata/test-config",
}
err := config.loadConfig()
err := config.LoadConfig()
if err != nil {
t.Fatal(err)
}
Expand All @@ -193,7 +193,7 @@ func TestLoadConfigFileFromEnv(t *testing.T) {
config := Config{
ConfigFile: "testdata/test-config",
}
err := config.loadConfig()
err := config.LoadConfig()
if err != nil {
t.Fatal(err)
}
Expand All @@ -210,7 +210,7 @@ func TestGetCdpProfileCaseSensitivity(t *testing.T) {
ConfigFile: "testdata/test-config",
Profile: "UPPER_CASE_PROFILE",
}
err := config.loadConfig()
err := config.LoadConfig()
if err != nil {
t.Fatal(err)
}
Expand Down Expand Up @@ -256,7 +256,7 @@ func TestGetCdpRegionFromConfigFile(t *testing.T) {
Profile: "foo",
ConfigFile: "testdata/test-config",
}
err := config.loadConfig()
err := config.LoadConfig()
if err != nil {
t.Fatal(err)
}
Expand All @@ -281,7 +281,7 @@ func TestGetEndpointsWithProfile(t *testing.T) {
Profile: "foo",
ConfigFile: "testdata/test-config",
}
err := config.loadConfig()
err := config.LoadConfig()
if err != nil {
t.Fatal(err)
}
Expand All @@ -307,7 +307,7 @@ func TestGetEndpointsWithRegionInProfile(t *testing.T) {
Profile: "bar",
ConfigFile: "testdata/test-config",
}
err := config.loadConfig()
err := config.LoadConfig()
if err != nil {
t.Fatal(err)
}
Expand All @@ -333,7 +333,7 @@ func TestDefaultEndpoints(t *testing.T) {
ConfigFile: "testdata/test-config",
Profile: "non-existing",
}
err := config.loadConfig()
err := config.LoadConfig()
if err != nil {
t.Fatal(err)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,6 @@ import "github.com/hashicorp/terraform-plugin-framework/types"
type machineUserResourceRoleAssignmentResourceModel struct {
Id types.String `tfsdk:"id"`
MachineUser types.String `tfsdk:"machine_user"`
ResourceCrn types.String `tfsdk:"resource_role_crn"`
ResourceCrn types.String `tfsdk:"resource_crn"`
ResourceRoleCrn types.String `tfsdk:"resource_role_crn"`
}
16 changes: 13 additions & 3 deletions resources/iam/resource_machine_user_resource_role_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package iam

import (
"context"
"github.com/hashicorp/terraform-plugin-framework/types"

"github.com/hashicorp/terraform-plugin-log/tflog"

Expand Down Expand Up @@ -41,6 +42,10 @@ func (r *machineUserResourceRoleAssignmentResource) Metadata(_ context.Context,
resp.TypeName = req.ProviderTypeName + "_iam_machine_user_resource_role_assignment"
}

func (r *machineUserResourceRoleAssignmentResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
r.client = utils.GetCdpClientForResource(req, resp)
}

func (r *machineUserResourceRoleAssignmentResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data machineUserResourceRoleAssignmentResourceModel

Expand All @@ -57,13 +62,18 @@ func (r *machineUserResourceRoleAssignmentResource) Create(ctx context.Context,
ResourceRoleCrn: data.ResourceRoleCrn.ValueStringPointer(),
})

_, err := r.client.Iam.Operations.AssignMachineUserResourceRole(request) // void method, does not have any return value
responseOk, err := r.client.Iam.Operations.AssignMachineUserResourceRole(request)
if err != nil {
utils.AddIamDiagnosticsError(err, &resp.Diagnostics, "assign Machine User Resource Role")
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
if responseOk.Payload != nil {
data.Id = types.StringValue(data.MachineUser.ValueString() + "_" + data.ResourceCrn.ValueString() + "_" + data.ResourceRoleCrn.ValueString())

// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
}

func (r *machineUserResourceRoleAssignmentResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
Expand All @@ -84,7 +94,7 @@ func (r *machineUserResourceRoleAssignmentResource) Read(ctx context.Context, re

hasAssignedResourceRole := false
for _, asgn := range machineUser.Payload.ResourceAssignments {
if asgn.ResourceCrn == data.ResourceCrn.ValueStringPointer() && asgn.ResourceRoleCrn == data.ResourceRoleCrn.ValueStringPointer() {
if *asgn.ResourceCrn == data.ResourceCrn.ValueString() && *asgn.ResourceRoleCrn == data.ResourceRoleCrn.ValueString() {
resp.State.Set(ctx, &data)
hasAssignedResourceRole = true
break
Expand Down
178 changes: 178 additions & 0 deletions resources/iam/resource_machine_user_resource_role_assignment_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// Copyright 2024 Cloudera. All Rights Reserved.
//
// This file is licensed under the Apache License Version 2.0 (the "License").
// You may not use this file except in compliance with the License.
// You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
//
// This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS
// OF ANY KIND, either express or implied. Refer to the License for the specific
// permissions and limitations governing your use of the file.

package iam_test

import (
"context"
"fmt"
"github.com/cloudera/terraform-provider-cdp/resources/iam"
"testing"

"github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/gen/iam/client/operations"
"github.com/cloudera/terraform-provider-cdp/cdp-sdk-go/gen/iam/models"
"github.com/cloudera/terraform-provider-cdp/cdpacctest"
"github.com/cloudera/terraform-provider-cdp/utils"
"github.com/hashicorp/terraform-plugin-testing/helper/acctest"
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
"github.com/hashicorp/terraform-plugin-testing/terraform"
)

func TestAccIamMachineUserResourceRoleAssignment_basic(t *testing.T) {
cdpRegion, err := iam.GetCdpRegionFromConfig()
if err != nil {
t.Fatal(err)
}
muName := acctest.RandomWithPrefix(cdpacctest.ResourcePrefix)
resourceRoleCrn := fmt.Sprintf("crn:altus:iam:%s:altus:resourceRole:IamGroupAdmin", cdpRegion)
resourceName := "cdp_iam_group.test"
resourceUnderTestName := "cdp_iam_machine_user_resource_role_assignment.test"
resource.Test(t, resource.TestCase{
PreCheck: func() { cdpacctest.PreCheck(t) },
ProtoV6ProviderFactories: cdpacctest.TestAccProtoV6ProviderFactories,
Steps: []resource.TestStep{
// Create and Read testing
{
Config: utils.Concat(
cdpacctest.TestAccCdpProviderConfig(),
testAccIamMachineUserResourceRoleAssignmentConfig(muName, resourceRoleCrn)),
Check: resource.ComposeAggregateTestCheckFunc(
resource.TestCheckResourceAttr(resourceUnderTestName, "machine_user", muName),
testAccCheckResourceAttrWith(resourceUnderTestName, "resource_crn", resourceAttrValue(resourceName, "crn")),
resource.TestCheckResourceAttr(resourceUnderTestName, "resource_role_crn", resourceRoleCrn),
testAccCheckResourceAttrWith(resourceUnderTestName, "id", statefValue(muName+"_%s_"+resourceRoleCrn, resourceAttrValue(resourceName, "crn"))),
testAccCheckIamMachineUserResourceRoleAssignmentExists(muName, resourceAttrValue(resourceName, "crn"), resourceRoleCrn),
),
},
// Delete testing automatically occurs in TestCase
},
})
}

func testAccIamMachineUserResourceRoleAssignmentConfig(muName string, resourceRoleCrn string) string {
return fmt.Sprintf(`
resource "cdp_iam_machine_user" "test" {
name = %[1]q
}
resource "cdp_iam_group" "test" {
group_name = %[1]q
}
resource "cdp_iam_machine_user_resource_role_assignment" "test" {
machine_user = %[1]q
resource_crn = cdp_iam_group.test.crn
resource_role_crn = %[2]q
depends_on = [cdp_iam_machine_user.test]
}
`, muName, resourceRoleCrn)
}

// testAccCheckIamMachineUserRoleAssignmentExists queries the API and retrieves the matching IamMachineUserResourceRoleAssignment via the passed in pointer.
func testAccCheckIamMachineUserResourceRoleAssignmentExists(muName string, resourceCrnFn func(s *terraform.State) (string, error), resourceRoleCrn string) resource.TestCheckFunc {
return func(s *terraform.State) error {

cdpClient := cdpacctest.GetCdpClientForAccTest()

params := operations.NewListMachineUserAssignedResourceRolesParamsWithContext(context.TODO())
params.WithInput(&models.ListMachineUserAssignedResourceRolesRequest{
MachineUserName: &muName,
})

responseOk, err := cdpClient.Iam.Operations.ListMachineUserAssignedResourceRoles(params)
if err != nil {
if d, ok := err.(*operations.ListMachineUserAssignedRolesDefault); ok && d.GetPayload() != nil && d.GetPayload().Code == "NOT_FOUND" {
return fmt.Errorf("machine user %s not found", muName)
}
return nil
}

resourceCrn, err := resourceCrnFn(s)
if err != nil {
return err
}

if len(responseOk.Payload.ResourceAssignments) != 1 ||
(*responseOk.Payload.ResourceAssignments[0].ResourceCrn != resourceCrn &&
*responseOk.Payload.ResourceAssignments[0].ResourceRoleCrn != resourceRoleCrn) {
return fmt.Errorf("machine user resource role assignment %s on resource %s not found", resourceRoleCrn, resourceCrn)
}

return nil
}
}

func testAccCheckResourceAttrWith(name, key string, valueFn func(s *terraform.State) (string, error)) resource.TestCheckFunc {
return func(s *terraform.State) error {
expectedValue, err := valueFn(s)
if err != nil {
return err
}

is, err := modulePrimaryInstanceState(s.RootModule(), name)
if err != nil {
return err
}

actualValue, ok := is.Attributes[key]
if !ok {
return fmt.Errorf("%s: Attribute '%s' not found", name, key)
}
if actualValue != expectedValue {
return fmt.Errorf("%s: Attribute '%s' expected %#v, got %#v", name, key, expectedValue, actualValue)
}

return nil
}
}

func modulePrimaryInstanceState(ms *terraform.ModuleState, name string) (*terraform.InstanceState, error) {
rs, ok := ms.Resources[name]
if !ok {
return nil, fmt.Errorf("Not found: %s in %s", name, ms.Path)
}

is := rs.Primary
if is == nil {
return nil, fmt.Errorf("No primary instance: %s in %s", name, ms.Path)
}

return is, nil
}

func resourceAttrValue(name, key string) func(s *terraform.State) (string, error) {
return func(s *terraform.State) (string, error) {
is, err := modulePrimaryInstanceState(s.RootModule(), name)
if err != nil {
return "", err
}

value, ok := is.Attributes[key]
if !ok {
return "", fmt.Errorf("%s: Attribute '%s' not found", name, key)
}

return value, nil
}
}

func statefValue(format string, a ...func(s *terraform.State) (string, error)) func(s *terraform.State) (string, error) {
return func(s *terraform.State) (string, error) {
values := make([]any, len(a))
for i, fn := range a {
var err error
values[i], err = fn(s)
if err != nil {
return "", err
}
}
return fmt.Sprintf(format, values...), nil
}
}
14 changes: 12 additions & 2 deletions resources/iam/resource_machine_user_role_assignment.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ package iam

import (
"context"
"github.com/hashicorp/terraform-plugin-framework/types"

"github.com/hashicorp/terraform-plugin-log/tflog"

Expand Down Expand Up @@ -41,6 +42,10 @@ func (r *machineUserRoleAssignmentResource) Metadata(_ context.Context, req reso
resp.TypeName = req.ProviderTypeName + "_iam_machine_user_role_assignment"
}

func (r *machineUserRoleAssignmentResource) Configure(_ context.Context, req resource.ConfigureRequest, resp *resource.ConfigureResponse) {
r.client = utils.GetCdpClientForResource(req, resp)
}

func (r *machineUserRoleAssignmentResource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
var data machineUserRoleAssignmentResourceModel

Expand All @@ -56,13 +61,18 @@ func (r *machineUserRoleAssignmentResource) Create(ctx context.Context, req reso
Role: data.Role.ValueStringPointer(),
})

_, err := r.client.Iam.Operations.AssignMachineUserRole(request) // void method, does not have any return value
responseOk, err := r.client.Iam.Operations.AssignMachineUserRole(request)
if err != nil {
utils.AddIamDiagnosticsError(err, &resp.Diagnostics, "assign Machine User Role")
return
}

resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
if responseOk.Payload != nil {
data.Id = types.StringValue(data.MachineUser.ValueString() + "_" + data.Role.ValueString())

// Save data into Terraform state
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
}
}

func (r *machineUserRoleAssignmentResource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
Expand Down
Loading

0 comments on commit c58dabc

Please sign in to comment.