Skip to content

Commit

Permalink
ENG-14270: add redshiftSettings and new AWS IAM authN flag
Browse files Browse the repository at this point in the history
  • Loading branch information
ricardorey10 committed Aug 7, 2024
1 parent 46010aa commit 14131b3
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 16 deletions.
5 changes: 5 additions & 0 deletions cyral/internal/repository/constants.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,11 @@ const (
RepoMongoDBServerTypeKey = "server_type"
RepoMongoDBSRVRecordName = "srv_record_name"
RepoMongoDBFlavorKey = "flavor"
// Redshift settings keys
RepoRedshiftSettingsKey = "redshift_settings"
RepoRedshiftClusterIdentifier = "cluster_identifier"
RepoRedshiftWorkgroupName = "workgroup_name"
RepoRedshiftAWSRegion = "aws_region"
)

const (
Expand Down
65 changes: 56 additions & 9 deletions cyral/internal/repository/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,15 +11,16 @@ type Labels []string
type RepoNodes []*RepoNode

type RepoInfo struct {
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Host string `json:"repoHost"`
Port uint32 `json:"repoPort"`
ConnParams *ConnParams `json:"connParams"`
Labels Labels `json:"labels"`
RepoNodes RepoNodes `json:"repoNodes,omitempty"`
MongoDBSettings *MongoDBSettings `json:"mongoDbSettings,omitempty"`
ID string `json:"id"`
Name string `json:"name"`
Type string `json:"type"`
Host string `json:"repoHost"`
Port uint32 `json:"repoPort"`
ConnParams *ConnParams `json:"connParams"`
Labels Labels `json:"labels"`
RepoNodes RepoNodes `json:"repoNodes,omitempty"`
MongoDBSettings *MongoDBSettings `json:"mongoDbSettings,omitempty"`
RedshiftSettings *RedshiftSettings `json:"redshiftSettings,omitempty"`
}

type ConnParams struct {
Expand All @@ -38,6 +39,12 @@ type MongoDBSettings struct {
Flavor string `json:"flavor,omitempty"`
}

type RedshiftSettings struct {
ClusterIdentifier string `json:"clusterIdentifier,omitempty"`
WorkgroupName string `json:"workgroupName,omitempty"`
AwsRegion string `json:"awsRegion,omitempty"`
}

type RepoNode struct {
Name string `json:"name"`
Host string `json:"host"`
Expand All @@ -60,6 +67,7 @@ func (res *RepoInfo) WriteToSchema(d *schema.ResourceData) error {
d.Set(RepoConnDrainingKey, res.ConnParams.AsInterface())
d.Set(RepoNodesKey, res.RepoNodes.AsInterface())
d.Set(RepoMongoDBSettingsKey, res.MongoDBSettings.AsInterface())
d.Set(RepoRedshiftSettingsKey, res.RedshiftSettings.AsInterface())
return nil
}

Expand All @@ -77,7 +85,18 @@ func (r *RepoInfo) ReadFromSchema(d *schema.ResourceData) error {
return fmt.Errorf("'%s' block is only allowed when '%s=%s'", RepoMongoDBSettingsKey, utils.TypeKey, MongoDB)
}
m, err := mongoDBSettingsFromInterface(mongoDBSettings)
if err != nil {
return err
}
r.MongoDBSettings = m

var redshiftSettings = d.Get(RepoRedshiftSettingsKey).(*schema.Set).List()
if r.Type != Redshift && len(redshiftSettings) > 0 {
return fmt.Errorf("'%s' block is only allowed when '%s=%s'", RepoRedshiftSettingsKey, utils.TypeKey, Redshift)
}
redshift, err := redshiftSettingsFromInterface(redshiftSettings)
r.RedshiftSettings = redshift

return err
}

Expand Down Expand Up @@ -156,6 +175,18 @@ func repoNodesFromInterface(i []interface{}) RepoNodes {
return repoNodes
}

func (r *RedshiftSettings) AsInterface() []interface{} {
if r == nil {
return nil
}

return []interface{}{map[string]interface{}{
RepoRedshiftClusterIdentifier: r.ClusterIdentifier,
RepoRedshiftWorkgroupName: r.WorkgroupName,
RepoRedshiftAWSRegion: r.AwsRegion,
}}
}

func (m *MongoDBSettings) AsInterface() []interface{} {
if m == nil {
return nil
Expand All @@ -169,6 +200,22 @@ func (m *MongoDBSettings) AsInterface() []interface{} {
}}
}

func redshiftSettingsFromInterface(i []interface{}) (*RedshiftSettings, error) {
if len(i) == 0 {
return nil, nil
}

var clusterIdentifier = i[0].(map[string]interface{})[RepoRedshiftClusterIdentifier].(string)
var workgroupName = i[0].(map[string]interface{})[RepoRedshiftWorkgroupName].(string)
var awsRegion = i[0].(map[string]interface{})[RepoRedshiftAWSRegion].(string)

return &RedshiftSettings{
ClusterIdentifier: clusterIdentifier,
WorkgroupName: workgroupName,
AwsRegion: awsRegion,
}, nil
}

func mongoDBSettingsFromInterface(i []interface{}) (*MongoDBSettings, error) {
if len(i) == 0 {
return nil, nil
Expand Down
25 changes: 25 additions & 0 deletions cyral/internal/repository/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,6 +171,31 @@ func resourceSchema() *schema.Resource {
},
},
},
RepoRedshiftSettingsKey: {
Description: "Parameters related to Redshift repositories.",
Type: schema.TypeSet,
Optional: true,
MaxItems: 1,
Elem: &schema.Resource{
Schema: map[string]*schema.Schema{
RepoRedshiftClusterIdentifier: {
Description: "Name of the provisioned cluster.",
Type: schema.TypeString,
Optional: true,
},
RepoRedshiftWorkgroupName: {
Description: "Workgroup name for serverless cluster.",
Type: schema.TypeString,
Optional: true,
},
RepoRedshiftAWSRegion: {
Description: "AWS region where the Redshift instance is deployed.",
Type: schema.TypeString,
Optional: true,
},
},
},
},
},
Importer: &schema.ResourceImporter{
StateContext: schema.ImportStatePassthroughContext,
Expand Down
66 changes: 65 additions & 1 deletion cyral/internal/repository/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,21 @@ var (
Flavor: "documentdb",
},
}

withRedshiftSettings = repository.RepoInfo{
Name: utils.AccTestName(utils.RepositoryResourceName, "repo-with-redshift-settings"),
Type: "redshift",
RepoNodes: repository.RepoNodes{
{
Host: "redshift.local",
Port: 3333,
},
},
RedshiftSettings: &repository.RedshiftSettings{
ClusterIdentifier: "myCluster",
AwsRegion: "us-east-1",
},
}
)

func TestAccRepositoryResource(t *testing.T) {
Expand All @@ -153,11 +168,13 @@ func TestAccRepositoryResource(t *testing.T) {
connDrainingConfig, "conn_draining_test")
allDynamic := setupRepositoryTest(
allRepoNodesAreDynamic, "all_repo_nodes_are_dynamic")
redshift := setupRepositoryTest(
withRedshiftSettings, "with_redshift_settings")

multiNode := setupRepositoryTest(
mixedMultipleNodesConfig, "multi_node_test")

// Should use name of the last resource created.
// Must use name of the last resource created.
importTest := resource.TestStep{
ImportState: true,
ImportStateVerify: true,
Expand All @@ -171,6 +188,7 @@ func TestAccRepositoryResource(t *testing.T) {
update,
connDrainingEmpty,
connDraining,
redshift,
allDynamic,
multiNode,
importTest,
Expand Down Expand Up @@ -256,6 +274,23 @@ func repoCheckFuctions(repo repository.RepoInfo, resName string) resource.TestCh
}...)
}

if repo.RedshiftSettings != nil {
checkFuncs = append(checkFuncs, []resource.TestCheckFunc{
resource.TestCheckResourceAttr(resourceFullName,
"redshift_settings.0.cluster_identifier",
repo.RedshiftSettings.ClusterIdentifier,
),
resource.TestCheckResourceAttr(resourceFullName,
"redshift_settings.0.workgroup_name",
repo.RedshiftSettings.WorkgroupName,
),
resource.TestCheckResourceAttr(resourceFullName,
"redshift_settings.0.aws_region",
repo.RedshiftSettings.AwsRegion,
),
}...)
}

return resource.ComposeTestCheckFunc(checkFuncs...)
}

Expand Down Expand Up @@ -307,6 +342,35 @@ func repoAsConfig(repo repository.RepoInfo, resName string) string {
)
}

if repo.RedshiftSettings != nil {
clusterIdentifier := "null"
workgroupName := "null"
awsRegion := "null"

if repo.RedshiftSettings.ClusterIdentifier != "" {
clusterIdentifier = fmt.Sprintf(`"%s"`, repo.RedshiftSettings.ClusterIdentifier)
}

if repo.RedshiftSettings.WorkgroupName != "" {
workgroupName = fmt.Sprintf(`"%s"`, repo.RedshiftSettings.WorkgroupName)
}

if repo.RedshiftSettings.AwsRegion != "" {
awsRegion = fmt.Sprintf(`"%s"`, repo.RedshiftSettings.AwsRegion)
}

config += fmt.Sprintf(`
redshift_settings {
cluster_identifier = %s
workgroup_name = %s
aws_region = %s
}`,
clusterIdentifier,
workgroupName,
awsRegion,
)
}

for _, node := range repo.RepoNodes {
name, host := "null", "null"
if node.Name != "" {
Expand Down
9 changes: 6 additions & 3 deletions cyral/internal/repository/useraccount/model.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ type AuthScheme struct {
}

type AuthSchemeAWSIAM struct {
RoleARN string `json:"roleARN,omitempty"`
RoleARN string `json:"roleARN,omitempty"`
AuthenticateAsIAMUser bool `json:"authenticateAsIAMUser,omitempty"`
}

type AuthSchemeAWSSecretsManager struct {
Expand Down Expand Up @@ -118,7 +119,8 @@ func (resource *UserAccountResource) WriteToSchema(d *schema.ResourceData) error
map[string]interface{}{
"aws_iam": []interface{}{
map[string]interface{}{
"role_arn": resource.AuthScheme.AWSIAM.RoleARN,
"role_arn": resource.AuthScheme.AWSIAM.RoleARN,
"authenticate_as_iam_user": resource.AuthScheme.AWSIAM.AuthenticateAsIAMUser,
},
},
},
Expand Down Expand Up @@ -259,7 +261,8 @@ func (userAccount *UserAccountResource) ReadFromSchema(d *schema.ResourceData) e
case "aws_iam":
userAccount.AuthScheme = &AuthScheme{
AWSIAM: &AuthSchemeAWSIAM{
RoleARN: m["role_arn"].(string),
RoleARN: m["role_arn"].(string),
AuthenticateAsIAMUser: m["authenticate_as_iam_user"].(bool),
},
}
case "aws_secrets_manager":
Expand Down
7 changes: 7 additions & 0 deletions cyral/internal/repository/useraccount/resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,13 @@ func resourceSchema() *schema.Resource {
Type: schema.TypeString,
Required: true,
},
"authenticate_as_iam_user": {
Description: "Boolean flag which indicates whether to access as an IAM " +
"user or IAM role on the Redshift cluster. By default, this is false, " +
"which means this governs access for a user.",
Type: schema.TypeBool,
Optional: true,
},
},
},
},
Expand Down
14 changes: 11 additions & 3 deletions cyral/internal/repository/useraccount/resource_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,8 @@ func TestAccRepositoryUserAccountResource(t *testing.T) {
Name: "aws-iam-useracc",
AuthScheme: &useraccount.AuthScheme{
AWSIAM: &useraccount.AuthSchemeAWSIAM{
RoleARN: "role-arn-1",
RoleARN: "role-arn-1",
AuthenticateAsIAMUser: true,
},
},
}
Expand Down Expand Up @@ -286,7 +287,11 @@ func setupRepositoryUserAccountCheck(resName string, userAccount useraccount.Use
checkFuncs = append(checkFuncs,
resource.TestCheckResourceAttr(resFullName,
authSchemeScope+"aws_iam.0.role_arn",
authScheme.AWSIAM.RoleARN))
authScheme.AWSIAM.RoleARN),
resource.TestCheckResourceAttr(resFullName,
authSchemeScope+"aws_iam.0.authenticate_as_iam_user",
strconv.FormatBool(authScheme.AWSIAM.AuthenticateAsIAMUser)),
)
case authScheme.AWSSecretsManager != nil:
checkFuncs = append(checkFuncs,
resource.TestCheckResourceAttr(resFullName,
Expand Down Expand Up @@ -348,7 +353,10 @@ func setupRepositoryUserAccountConfig(resName string, userAccount useraccount.Us
authSchemeStr = fmt.Sprintf(`
aws_iam {
role_arn = "%s"
}`, authScheme.AWSIAM.RoleARN)
authenticate_as_iam_user = %t
}`,
authScheme.AWSIAM.RoleARN,
authScheme.AWSIAM.AuthenticateAsIAMUser)
case authScheme.AWSSecretsManager != nil:
authSchemeStr = fmt.Sprintf(`
aws_secrets_manager {
Expand Down

0 comments on commit 14131b3

Please sign in to comment.