Skip to content

Commit

Permalink
OCM-12837 | feat: support shared vpc attributes in cluster resource
Browse files Browse the repository at this point in the history
  • Loading branch information
gdbranco committed Nov 29, 2024
1 parent 66f9087 commit 678396d
Show file tree
Hide file tree
Showing 13 changed files with 841 additions and 38 deletions.
14 changes: 14 additions & 0 deletions docs/data-sources/cluster_rosa_hcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,11 @@ data "rhcs_cluster_rosa_hcp" "cluster" {
- `api_url` (String) URL of the API server.
- `availability_zones` (List of String) Availability zones. This attribute specifically applies to the Worker Machine Pool and becomes irrelevant once the resource is created. Any modifications to the initial Machine Pool should be made through the Terraform imported Machine Pool resource. For more details, refer to [Worker Machine Pool in ROSA Cluster](../guides/worker-machine-pool.md)
- `aws_account_id` (String) Identifier of the AWS account. After the creation of the resource, it is not possible to update the attribute value.
- `aws_additional_allowed_principals` (List of String) AWS additional allowed principals.
- `aws_additional_compute_security_group_ids` (List of String) AWS additional compute security group ids. After the creation of the resource, it is not possible to update the attribute value.
- `aws_billing_account_id` (String) Identifier of the AWS account for billing. After the creation of the resource, it is not possible to update the attribute value.
- `aws_subnet_ids` (List of String) AWS subnet IDs. After the creation of the resource, it is not possible to update the attribute value.
- `base_dns_domain` (String) Base DNS domain name previously reserved, e.g. '1vo8.p3.openshiftapps.com'. After the creation of the resource, it is not possible to update the attribute value.
- `channel_group` (String) This attribute is not supported for cluster data source. Therefore, it will not be displayed as an output of the datasource
- `cloud_region` (String) Cloud region identifier, for example 'us-east-1'.
- `compute_machine_type` (String) This attribute is not supported for cluster data source. Therefore, it will not be displayed as an output of the datasource
Expand All @@ -63,6 +65,7 @@ data "rhcs_cluster_rosa_hcp" "cluster" {
- `proxy` (Attributes) proxy (see [below for nested schema](#nestedatt--proxy))
- `replicas` (Number) This attribute is not supported for cluster data source. Therefore, it will not be displayed as an output of the datasource
- `service_cidr` (String) Block of IP addresses for the cluster service network. After the creation of the resource, it is not possible to update the attribute value.
- `shared_vpc` (Attributes) Shared VPC configuration.After the creation of the resource, it is not possible to update the attribute value. (see [below for nested schema](#nestedatt--shared_vpc))
- `state` (String) State of the cluster.
- `sts` (Attributes) STS configuration. (see [below for nested schema](#nestedatt--sts))
- `tags` (Map of String) Apply user defined tags to all cluster resources created in AWS. After the creation of the resource, it is not possible to update the attribute value.
Expand Down Expand Up @@ -122,6 +125,17 @@ Read-Only:
- `no_proxy` (String) No proxy.


<a id="nestedatt--shared_vpc"></a>
### Nested Schema for `shared_vpc`

Read-Only:

- `ingress_private_hosted_zone_id` (String) ID assigned by AWS to private Route 53 hosted zone associated with intended shared VPC, e.g. 'Z05646003S02O1ENCDCSN'.
- `internal_communication_private_hosted_zone_id` (String) ID assigned by AWS to private Route 53 hosted zone associated with intended shared VPC, e.g. 'Z05646003S02O1ENCDCSN'.
- `route53_role_arn` (String) AWS IAM role ARN with a policy attached, granting permissions necessary to create and manage Route 53 DNS records in private Route 53 hosted zone associated with intended shared VPC.
- `vpce_role_arn` (String) AWS IAM role ARN with a policy attached, granting permissions necessary to create and manage VPC Endpoints associated with intended shared VPC.


<a id="nestedatt--sts"></a>
### Nested Schema for `sts`

Expand Down
18 changes: 18 additions & 0 deletions docs/resources/cluster_rosa_hcp.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,9 @@ resource "rhcs_cluster_rosa_hcp" "rosa_sts_cluster" {
### Optional

- `admin_credentials` (Attributes) Admin user credentials. After the creation of the resource, it is not possible to update the attribute value. (see [below for nested schema](#nestedatt--admin_credentials))
- `aws_additional_allowed_principals` (List of String) AWS additional allowed principals.
- `aws_additional_compute_security_group_ids` (List of String) AWS additional compute security group ids.
- `base_dns_domain` (String) Base DNS domain name previously reserved, e.g. '1vo8.p3.openshiftapps.com'. After the creation of the resource, it is not possible to update the attribute value.
- `channel_group` (String) Name of the channel group where you select the OpenShift cluster version, for example 'stable'. For ROSA, only 'stable' is supported. After the creation of the resource, it is not possible to update the attribute value.
- `compute_machine_type` (String) Identifies the machine type used by the initial worker nodes, for example `m5.xlarge`. Use the `rhcs_machine_types` data source to find the possible values. This attribute specifically applies to the Worker Machine Pool and becomes irrelevant once the resource is created. Any modifications to the initial Machine Pool should be made through the Terraform imported Machine Pool resource. For more details, refer to [Worker Machine Pool in ROSA Cluster](../guides/worker-machine-pool.md)
- `create_admin_user` (Boolean) Indicates if create cluster admin user. Set it true to create cluster admin user with default username `cluster-admin` and generated password. It will be ignored if `admin_credentials` is set.After the creation of the resource, it is not possible to update the attribute value.
Expand All @@ -80,6 +82,7 @@ resource "rhcs_cluster_rosa_hcp" "rosa_sts_cluster" {
- `registry_config` (Attributes) Registry configuration for this cluster. (see [below for nested schema](#nestedatt--registry_config))
- `replicas` (Number) Number of worker/compute nodes to provision. Requires that the number supplied be a multiple of the number of private subnets. This attribute specifically applies to the Worker Machine Pool and becomes irrelevant once the resource is created. Any modifications to the initial Machine Pool should be made through the Terraform imported Machine Pool resource. For more details, refer to [Worker Machine Pool in ROSA Cluster](../guides/worker-machine-pool.md)
- `service_cidr` (String) Block of IP addresses for the cluster service network. After the creation of the resource, it is not possible to update the attribute value.
- `shared_vpc` (Attributes) Shared VPC configuration.After the creation of the resource, it is not possible to update the attribute value. (see [below for nested schema](#nestedatt--shared_vpc))
- `tags` (Map of String) Apply user defined tags to all cluster resources created in AWS. After the creation of the resource, it is not possible to update the attribute value.
- `upgrade_acknowledgements_for` (String) Indicates acknowledgement of agreements required to upgrade the cluster version between minor versions (e.g. a value of "4.12" indicates acknowledgement of any agreements required to upgrade to OpenShift 4.12.z from 4.11 or before).
- `version` (String) Desired version of OpenShift for the cluster, for example '4.11.0'. If version is greater than the currently running version, an upgrade will be scheduled.
Expand Down Expand Up @@ -173,3 +176,18 @@ Optional:
- `allowed_registries` (List of String) allowed_registries: registries for which image pull and push actions are allowed. To specify all subdomains, add the asterisk (*) wildcard character as a prefix to the domain name. For example, *.example.com. You can specify an individual repository within a registry. For example: reg1.io/myrepo/myapp:latest. All other registries are blocked. Mutually exclusive with `BlockedRegistries`
- `blocked_registries` (List of String) blocked_registries: registries for which image pull and push actions are denied. To specify all subdomains, add the asterisk (*) wildcard character as a prefix to the domain name. For example, *.example.com. You can specify an individual repository within a registry. For example: reg1.io/myrepo/myapp:latest. All other registries are allowed. Mutually exclusive with `AllowedRegistries`
- `insecure_registries` (List of String) insecure_registries are registries which do not have a valid TLS certificate or only support HTTP connections. To specify all subdomains, add the asterisk (*) wildcard character as a prefix to the domain name. For example, *.example.com. You can specify an individual repository within a registry. For example: reg1.io/myrepo/myapp:latest.



<a id="nestedatt--shared_vpc"></a>
### Nested Schema for `shared_vpc`

Required:

- `ingress_private_hosted_zone_id` (String) ID assigned by AWS to private Route 53 hosted zone associated with intended shared VPC, e.g. 'Z05646003S02O1ENCDCSN'.
- `route53_role_arn` (String) AWS IAM role ARN with a policy attached, granting permissions necessary to create and manage Route 53 DNS records in private Route 53 hosted zone associated with intended shared VPC.
- `vpce_role_arn` (String) AWS IAM role ARN with a policy attached, granting permissions necessary to create and manage VPC Endpoints associated with intended shared VPC.

Optional:

- `internal_communication_private_hosted_zone_id` (String) ID assigned by AWS to private Route 53 hosted zone associated with intended shared VPC, e.g. 'Z05646003S02O1ENCDCSN'.
30 changes: 24 additions & 6 deletions internal/ocm/resource/cluster.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (
)

var privateHostedZoneRoleArnRE = regexp.MustCompile(
`^arn:aws:iam::\d{12}:role\/[A-Za-z0-9]+(?:-[A-Za-z0-9]+)+$`,
`^arn:aws:iam::\d{12}:role(?:(?:\/?.+\/?)?)(?:\/[0-9A-Za-z\\+\\.@_,-]{1,64})$`,
)

type Cluster struct {
Expand Down Expand Up @@ -142,9 +142,11 @@ func (c *Cluster) CreateAWSBuilder(clusterTopology rosaTypes.ClusterTopology,
isPrivateLink bool, awsAccountID *string, awsBillingAccountId *string,
stsBuilder *cmv1.STSBuilder, awsSubnetIDs []string,
privateHostedZoneID *string, privateHostedZoneRoleARN *string,
hcpInternalCommunicationPrivateHostedZoneId *string, vpceRoleArn *string,
additionalComputeSecurityGroupIds []string,
additionalInfraSecurityGroupIds []string,
additionalControlPlaneSecurityGroupIds []string) error {
additionalControlPlaneSecurityGroupIds []string,
additionalAllowedPrincipals []string) error {

if clusterTopology == rosaTypes.Hcp && awsSubnetIDs == nil {
return errors.New("Hosted Control Plane clusters must have a pre-configure VPC. Make sure to specify the subnet ids.")
Expand Down Expand Up @@ -209,14 +211,30 @@ func (c *Cluster) CreateAWSBuilder(clusterTopology rosaTypes.ClusterTopology,
}

if privateHostedZoneID != nil && privateHostedZoneRoleARN != nil {
if !privateHostedZoneRoleArnRE.MatchString(*privateHostedZoneRoleARN) {
return errors.New(fmt.Sprintf("Expected a valid value for PrivateHostedZoneRoleARN matching %s. Got %s", privateHostedZoneRoleArnRE, *privateHostedZoneRoleARN))
}
if awsSubnetIDs == nil || stsBuilder == nil {
return errors.New("PrivateHostedZone parameters require STS and SubnetIDs configurations.")
return errors.New("Shared VPC parameters require STS and SubnetIDs configurations.")
}
privateRoleArnField := "PrivateHostedZoneRoleARN"
if clusterTopology == rosaTypes.Hcp {
privateRoleArnField = "Route53RoleArn"
}
if !privateHostedZoneRoleArnRE.MatchString(*privateHostedZoneRoleARN) {
return errors.New(fmt.Sprintf("Expected a valid value for %s matching %s. Got %s",
privateRoleArnField, privateHostedZoneRoleArnRE, *privateHostedZoneRoleARN))
}
awsBuilder.PrivateHostedZoneID(*privateHostedZoneID)
awsBuilder.PrivateHostedZoneRoleARN(*privateHostedZoneRoleARN)
if clusterTopology == rosaTypes.Hcp && hcpInternalCommunicationPrivateHostedZoneId != nil && vpceRoleArn != nil {
if !privateHostedZoneRoleArnRE.MatchString(*vpceRoleArn) {
return errors.New(fmt.Sprintf("Expected a valid value for VpcEndpointRoleArn matching %s. Got %s", privateHostedZoneRoleArnRE, *vpceRoleArn))
}
awsBuilder.HcpInternalCommunicationHostedZoneId(*hcpInternalCommunicationPrivateHostedZoneId)
awsBuilder.VpcEndpointRoleArn(*vpceRoleArn)
}
}

if additionalAllowedPrincipals != nil {
awsBuilder.AdditionalAllowedPrincipals(additionalAllowedPrincipals...)
}

c.clusterBuilder.AWS(awsBuilder)
Expand Down
18 changes: 9 additions & 9 deletions internal/ocm/resource/cluster_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -203,17 +203,17 @@ var _ = Describe("Cluster", func() {
})
Context("CreateAWSBuilder validation", func() {
It("PrivateLink true subnets IDs empty - failure", func() {
err := cluster.CreateAWSBuilder(rosaTypes.Classic, nil, nil, nil, nil, true, nil, nil, nil, nil, nil, nil, nil, nil, nil)
err := cluster.CreateAWSBuilder(rosaTypes.Classic, nil, nil, nil, nil, true, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal("Clusters with PrivateLink must have a pre-configured VPC. Make sure to specify the subnet ids."))
})
It("PrivateLink false invalid kmsKeyARN - failure", func() {
err := cluster.CreateAWSBuilder(rosaTypes.Classic, nil, nil, pointer("test"), nil, false, nil, nil, nil, nil, nil, nil, nil, nil, nil)
err := cluster.CreateAWSBuilder(rosaTypes.Classic, nil, nil, pointer("test"), nil, false, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
Expect(err).To(HaveOccurred())
Expect(err.Error()).To(Equal(fmt.Sprintf("expected the kms-key-arn: %s to match %s", "test", kmsArnRegexpValidator.KmsArnRE)))
})
It("PrivateLink false empty kmsKeyARN - success", func() {
err := cluster.CreateAWSBuilder(rosaTypes.Classic, nil, nil, nil, nil, false, nil, nil, nil, nil, nil, nil, nil, nil, nil)
err := cluster.CreateAWSBuilder(rosaTypes.Classic, nil, nil, nil, nil, false, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
Expect(err).NotTo(HaveOccurred())
ocmCluster, err := cluster.Build()
Expect(err).NotTo(HaveOccurred())
Expand All @@ -228,7 +228,7 @@ var _ = Describe("Cluster", func() {
})
It("PrivateLink false invalid Ec2MetadataHttpTokens - success", func() {
// TODO Need to add validation for Ec2MetadataHttpTokens
err := cluster.CreateAWSBuilder(rosaTypes.Classic, nil, pointer("test"), nil, nil, false, nil, nil, nil, nil, nil, nil, nil, nil, nil)
err := cluster.CreateAWSBuilder(rosaTypes.Classic, nil, pointer("test"), nil, nil, false, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, nil)
Expect(err).NotTo(HaveOccurred())
ocmCluster, err := cluster.Build()
Expect(err).NotTo(HaveOccurred())
Expand Down Expand Up @@ -257,7 +257,7 @@ var _ = Describe("Cluster", func() {
err := cluster.CreateAWSBuilder(rosaTypes.Classic, map[string]string{"key1": "val1"},
pointer(string(cmv1.Ec2MetadataHttpTokensRequired)),
pointer(validKmsKey), nil, true, pointer(accountID), nil,
sts, subnets, nil, nil, nil, nil, nil)
sts, subnets, nil, nil, nil, nil, nil, nil, nil, nil)
Expect(err).NotTo(HaveOccurred())
ocmCluster, err := cluster.Build()
Expect(err).NotTo(HaveOccurred())
Expand Down Expand Up @@ -299,7 +299,7 @@ var _ = Describe("Cluster", func() {
err := cluster.CreateAWSBuilder(rosaTypes.Classic, map[string]string{"key1": "val1"},
pointer(string(cmv1.Ec2MetadataHttpTokensRequired)),
pointer(validKmsKey), nil, true, pointer(accountID), nil,
sts, subnets, &privateHZId, &privateHZRoleArn, nil, nil, nil)
sts, subnets, &privateHZId, &privateHZRoleArn, nil, nil, nil, nil, nil, nil)
Expect(err).NotTo(HaveOccurred())
ocmCluster, err := cluster.Build()
Expect(err).NotTo(HaveOccurred())
Expand All @@ -324,7 +324,7 @@ var _ = Describe("Cluster", func() {
err := cluster.CreateAWSBuilder(rosaTypes.Classic, map[string]string{"key1": "val1"},
pointer(string(cmv1.Ec2MetadataHttpTokensRequired)),
pointer(validKmsKey), nil, true, pointer(accountID), nil,
sts, subnets, &privateHZId, &privateHZRoleArn, nil, nil, nil)
sts, subnets, &privateHZId, &privateHZRoleArn, nil, nil, nil, nil, nil, nil)
Expect(err).To(HaveOccurred())
})
It("PrivateHostedZone set missing STS - fail", func() {
Expand All @@ -336,7 +336,7 @@ var _ = Describe("Cluster", func() {
err := cluster.CreateAWSBuilder(rosaTypes.Classic, map[string]string{"key1": "val1"},
pointer(string(cmv1.Ec2MetadataHttpTokensRequired)),
pointer(validKmsKey), nil, true, pointer(accountID), nil,
nil, subnets, &privateHZId, &privateHZRoleArn, nil, nil, nil)
nil, subnets, &privateHZId, &privateHZRoleArn, nil, nil, nil, nil, nil, nil)
Expect(err).To(HaveOccurred())
})
It("PrivateHostedZone set missing subnet ids - fail", func() {
Expand All @@ -355,7 +355,7 @@ var _ = Describe("Cluster", func() {
err := cluster.CreateAWSBuilder(rosaTypes.Classic, map[string]string{"key1": "val1"},
pointer(string(cmv1.Ec2MetadataHttpTokensRequired)),
pointer(validKmsKey), nil, true, pointer(accountID), nil,
sts, nil, &privateHZId, &privateHZRoleArn, nil, nil, nil)
sts, nil, &privateHZId, &privateHZRoleArn, nil, nil, nil, nil, nil, nil)
Expect(err).To(HaveOccurred())
})
})
Expand Down
8 changes: 5 additions & 3 deletions provider/clusterrosa/classic/cluster_rosa_classic_resource.go
Original file line number Diff line number Diff line change
Expand Up @@ -653,9 +653,10 @@ func createClassicClusterObject(ctx context.Context,
}
if err := ocmClusterResource.CreateAWSBuilder(rosaTypes.Classic, awsTags, ec2MetadataHttpTokens,
kmsKeyARN, nil,
isPrivateLink, awsAccountID, nil, stsBuilder, awsSubnetIDs, privateHostedZoneID, privateHostedZoneRoleARN,
isPrivateLink, awsAccountID, nil, stsBuilder, awsSubnetIDs,
privateHostedZoneID, privateHostedZoneRoleARN, nil, nil,
awsAdditionalComputeSecurityGroupIds, awsAdditionalInfraSecurityGroupIds,
awsAdditionalControlPlaneSecurityGroupIds); err != nil {
awsAdditionalControlPlaneSecurityGroupIds, nil); err != nil {
return nil, err
}

Expand Down Expand Up @@ -997,7 +998,8 @@ func validateNoImmutableAttChange(state, plan *ClusterRosaClassicState) diag.Dia
common.ValidateStateAndPlanEquals(state.AWSAdditionalComputeSecurityGroupIds, plan.AWSAdditionalComputeSecurityGroupIds, "aws_additional_compute_security_group_ids", &diags)

if !reflect.DeepEqual(state.PrivateHostedZone, plan.PrivateHostedZone) {
diags.AddError(common.AssertionErrorSummaryMessage, fmt.Sprintf(common.AssertionErrorDetailsMessage, "private_hosted_zone", *state.PrivateHostedZone, *plan.PrivateHostedZone))
diags.AddError(common.AssertionErrorSummaryMessage, fmt.Sprintf(common.AssertionErrorDetailsMessage, "private_hosted_zone",
common.GetJsonStringOrNullString(state.PrivateHostedZone), common.GetJsonStringOrNullString(plan.PrivateHostedZone)))
}

// default machine pool's attributes
Expand Down
2 changes: 1 addition & 1 deletion provider/clusterrosa/common/validators.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ var AvailabilityZoneValidator = attrvalidators.NewStringValidator("AZ should be
}
})

var PrivateHZValidator = attrvalidators.NewObjectValidator("proxy map should not include an hard coded OCM proxy",
var PrivateHZValidator = attrvalidators.NewObjectValidator("Private Hosted Zone attribute must include all attributes",
func(ctx context.Context, req validator.ObjectRequest, resp *validator.ObjectResponse) {
if req.ConfigValue.IsNull() || req.ConfigValue.IsUnknown() {
return
Expand Down
Loading

0 comments on commit 678396d

Please sign in to comment.