Skip to content

Commit d87be6d

Browse files
ichung08vandyliu
andauthored
Add Hybrid Cluster Workspace Authorization Resource (#77)
Co-authored-by: Vandy Liu <vandy.liu@astronomer.io>
1 parent b91c51f commit d87be6d

12 files changed

+635
-48
lines changed

.github/workflows/testacc.yml

+4
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,7 @@ jobs:
7070
"internal/provider/resources/resource_cluster.go"
7171
"internal/provider/resources/resource_cluster_test.go"
7272
"internal/provider/resources/resource_deployment.go"
73+
"internal/provider/resources/common_cluster.go"
7374
)
7475
for file in "${FILES_TO_CHECK[@]}"; do
7576
if git diff --name-only remotes/origin/${{ github.base_ref }} remotes/origin/${{ github.head_ref }} | grep -q "$file"; then
@@ -90,6 +91,7 @@ jobs:
9091
ASTRO_API_HOST: https://api.astronomer-dev.io
9192
SKIP_CLUSTER_RESOURCE_TESTS: ${{ env.SKIP_CLUSTER_RESOURCE_TESTS }}
9293
HOSTED_TEAM_ID: clwbclrc100bl01ozjj5s4jmq
94+
HYBRID_WORKSPACE_IDS: cl70oe7cu445571iynrkthtybl,cl8wpve4993871i37qe1k152c
9395
TESTARGS: "-failfast"
9496
run: make testacc
9597

@@ -131,6 +133,7 @@ jobs:
131133
HYBRID_NODE_POOL_ID: clqqongl40fmu01m94pwp4kct
132134
ASTRO_API_HOST: https://api.astronomer-stage.io
133135
HOSTED_TEAM_ID: clwv0r0x7091n01l0t1fm4vxy
136+
HYBRID_WORKSPACE_IDS: clwv06sva08vg01hovu1j7znw
134137
TESTARGS: "-failfast"
135138
run: make testacc
136139

@@ -172,5 +175,6 @@ jobs:
172175
HYBRID_NODE_POOL_ID: clnp86ly5000301ndzfxz895w
173176
ASTRO_API_HOST: https://api.astronomer-dev.io
174177
HOSTED_TEAM_ID: clwbclrc100bl01ozjj5s4jmq
178+
HYBRID_WORKSPACE_IDS: cl70oe7cu445571iynrkthtybl,cl8wpve4993871i37qe1k152c
175179
TESTARGS: "-failfast"
176180
run: make testacc
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "astro_hybrid_cluster_workspace_authorization Resource - astro"
4+
subcategory: ""
5+
description: |-
6+
Hybrid cluster workspace authorization resource
7+
---
8+
9+
# astro_hybrid_cluster_workspace_authorization (Resource)
10+
11+
Hybrid cluster workspace authorization resource
12+
13+
## Example Usage
14+
15+
```terraform
16+
resource "astro_hybrid_cluster_workspace_authorization" "example" {
17+
cluster_id = "clk8h0fv1006801j8yysfybbt"
18+
workspace_ids = ["cl70oe7cu445571iynrkthtybl", "cl70oe7cu445571iynrkthacsd"]
19+
}
20+
```
21+
22+
<!-- schema generated by tfplugindocs -->
23+
## Schema
24+
25+
### Required
26+
27+
- `cluster_id` (String) The ID of the hybrid cluster to set authorizations for
28+
29+
### Optional
30+
31+
- `workspace_ids` (Set of String) The IDs of the workspaces to authorize for the hybrid cluster
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
resource "astro_hybrid_cluster_workspace_authorization" "example" {
2+
cluster_id = "clk8h0fv1006801j8yysfybbt"
3+
workspace_ids = ["cl70oe7cu445571iynrkthtybl", "cl70oe7cu445571iynrkthacsd"]
4+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package models
2+
3+
import (
4+
"github.com/astronomer/terraform-provider-astro/internal/clients/platform"
5+
"github.com/astronomer/terraform-provider-astro/internal/utils"
6+
"github.com/hashicorp/terraform-plugin-framework/diag"
7+
"github.com/hashicorp/terraform-plugin-framework/types"
8+
)
9+
10+
type HybridClusterWorkspaceAuthorizationResource struct {
11+
ClusterId types.String `tfsdk:"cluster_id"`
12+
WorkspaceIds types.Set `tfsdk:"workspace_ids"`
13+
}
14+
15+
func (data *HybridClusterWorkspaceAuthorizationResource) ReadFromResponse(
16+
cluster *platform.Cluster,
17+
) diag.Diagnostics {
18+
var diags diag.Diagnostics
19+
data.ClusterId = types.StringValue(cluster.Id)
20+
if cluster.WorkspaceIds == nil || len(*cluster.WorkspaceIds) == 0 {
21+
data.WorkspaceIds = types.SetNull(types.StringType)
22+
} else {
23+
data.WorkspaceIds, diags = utils.StringSet(cluster.WorkspaceIds)
24+
if diags.HasError() {
25+
return diags
26+
}
27+
}
28+
29+
return nil
30+
}

internal/provider/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,7 @@ func (p *AstroProvider) Resources(ctx context.Context) []func() resource.Resourc
124124
resources.NewDeploymentResource,
125125
resources.NewClusterResource,
126126
resources.NewTeamRolesResource,
127+
resources.NewHybridClusterWorkspaceAuthorizationResource,
127128
}
128129
}
129130

internal/provider/provider_test_utils.go

+3
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,9 @@ func TestAccPreCheck(t *testing.T) {
3535
if hybridOrgId := os.Getenv("HYBRID_ORGANIZATION_ID"); len(hybridOrgId) == 0 {
3636
missingEnvVars = append(missingEnvVars, "HYBRID_ORGANIZATION_ID")
3737
}
38+
if hybridWorkspaceIds := os.Getenv("HYBRID_WORKSPACE_IDS"); len(hybridWorkspaceIds) == 0 {
39+
missingEnvVars = append(missingEnvVars, "HYBRID_WORKSPACE_IDS")
40+
}
3841
if host := os.Getenv("ASTRO_API_HOST"); len(host) == 0 {
3942
missingEnvVars = append(missingEnvVars, "ASTRO_API_HOST")
4043
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
package resources
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"net/http"
7+
8+
"github.com/astronomer/terraform-provider-astro/internal/clients"
9+
"github.com/astronomer/terraform-provider-astro/internal/clients/platform"
10+
"github.com/hashicorp/terraform-plugin-log/tflog"
11+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/retry"
12+
)
13+
14+
// ClusterResourceRefreshFunc returns a retry.StateRefreshFunc that polls the platform API for the cluster status
15+
// If the cluster is not found, it returns "DELETED" status
16+
// If the cluster is found, it returns the cluster status
17+
// If there is an error, it returns the error
18+
// WaitForStateContext will keep polling until the target status is reached, the timeout is reached or an err is returned
19+
func ClusterResourceRefreshFunc(ctx context.Context, platformClient *platform.ClientWithResponses, organizationId string, clusterId string) retry.StateRefreshFunc {
20+
return func() (any, string, error) {
21+
cluster, err := platformClient.GetClusterWithResponse(ctx, organizationId, clusterId)
22+
if err != nil {
23+
tflog.Error(ctx, "failed to get cluster while polling for cluster 'CREATED' status", map[string]interface{}{"error": err})
24+
return nil, "", err
25+
}
26+
statusCode, diagnostic := clients.NormalizeAPIError(ctx, cluster.HTTPResponse, cluster.Body)
27+
if statusCode == http.StatusNotFound {
28+
return &platform.Cluster{}, "DELETED", nil
29+
}
30+
if diagnostic != nil {
31+
return nil, "", fmt.Errorf("error getting cluster %s", diagnostic.Detail())
32+
}
33+
if cluster != nil && cluster.JSON200 != nil {
34+
switch cluster.JSON200.Status {
35+
case platform.ClusterStatusCREATED:
36+
return cluster.JSON200, string(cluster.JSON200.Status), nil
37+
case platform.ClusterStatusUPDATEFAILED, platform.ClusterStatusCREATEFAILED:
38+
return cluster.JSON200, string(cluster.JSON200.Status), fmt.Errorf("cluster mutation failed for cluster '%v'", cluster.JSON200.Id)
39+
case platform.ClusterStatusCREATING, platform.ClusterStatusUPDATING:
40+
return cluster.JSON200, string(cluster.JSON200.Status), nil
41+
default:
42+
return cluster.JSON200, string(cluster.JSON200.Status), fmt.Errorf("unexpected cluster status '%v' for cluster '%v'", cluster.JSON200.Status, cluster.JSON200.Id)
43+
}
44+
}
45+
return nil, "", fmt.Errorf("error getting cluster %s", clusterId)
46+
}
47+
}

internal/provider/resources/resource_cluster.go

+3-38
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,7 @@ func (r *ClusterResource) Create(
217217
stateConf := &retry.StateChangeConf{
218218
Pending: []string{string(platform.ClusterStatusCREATING), string(platform.ClusterStatusUPDATING)},
219219
Target: []string{string(platform.ClusterStatusCREATED), string(platform.ClusterStatusUPDATEFAILED), string(platform.ClusterStatusCREATEFAILED)},
220-
Refresh: r.resourceRefreshFunc(ctx, cluster.JSON200.Id),
220+
Refresh: ClusterResourceRefreshFunc(ctx, r.platformClient, r.organizationId, cluster.JSON200.Id),
221221
Timeout: 3 * time.Hour,
222222
MinTimeout: 1 * time.Minute,
223223
}
@@ -370,7 +370,7 @@ func (r *ClusterResource) Update(
370370
stateConf := &retry.StateChangeConf{
371371
Pending: []string{string(platform.ClusterStatusCREATING), string(platform.ClusterStatusUPDATING)},
372372
Target: []string{string(platform.ClusterStatusCREATED), string(platform.ClusterStatusUPDATEFAILED), string(platform.ClusterStatusCREATEFAILED)},
373-
Refresh: r.resourceRefreshFunc(ctx, cluster.JSON200.Id),
373+
Refresh: ClusterResourceRefreshFunc(ctx, r.platformClient, r.organizationId, cluster.JSON200.Id),
374374
Timeout: 3 * time.Hour,
375375
MinTimeout: 1 * time.Minute,
376376
}
@@ -441,7 +441,7 @@ func (r *ClusterResource) Delete(
441441
stateConf := &retry.StateChangeConf{
442442
Pending: []string{string(platform.ClusterStatusCREATING), string(platform.ClusterStatusUPDATING), string(platform.ClusterStatusCREATED), string(platform.ClusterStatusUPDATEFAILED), string(platform.ClusterStatusCREATEFAILED)},
443443
Target: []string{"DELETED"},
444-
Refresh: r.resourceRefreshFunc(ctx, data.Id.ValueString()),
444+
Refresh: ClusterResourceRefreshFunc(ctx, r.platformClient, r.organizationId, data.Id.ValueString()),
445445
Timeout: 1 * time.Hour,
446446
MinTimeout: 30 * time.Second,
447447
}
@@ -576,38 +576,3 @@ func validateGcpConfig(ctx context.Context, data *models.ClusterResource) diag.D
576576
}
577577
return diags
578578
}
579-
580-
// resourceRefreshFunc returns a retry.StateRefreshFunc that polls the platform API for the cluster status
581-
// If the cluster is not found, it returns "DELETED" status
582-
// If the cluster is found, it returns the cluster status
583-
// If there is an error, it returns the error
584-
// WaitForStateContext will keep polling until the target status is reached, the timeout is reached or an err is returned
585-
func (r *ClusterResource) resourceRefreshFunc(ctx context.Context, clusterId string) retry.StateRefreshFunc {
586-
return func() (any, string, error) {
587-
cluster, err := r.platformClient.GetClusterWithResponse(ctx, r.organizationId, clusterId)
588-
if err != nil {
589-
tflog.Error(ctx, "failed to get cluster while polling for cluster 'CREATED' status", map[string]interface{}{"error": err})
590-
return nil, "", err
591-
}
592-
statusCode, diagnostic := clients.NormalizeAPIError(ctx, cluster.HTTPResponse, cluster.Body)
593-
if statusCode == http.StatusNotFound {
594-
return &platform.Cluster{}, "DELETED", nil
595-
}
596-
if diagnostic != nil {
597-
return nil, "", fmt.Errorf("error getting cluster %s", diagnostic.Detail())
598-
}
599-
if cluster != nil && cluster.JSON200 != nil {
600-
switch cluster.JSON200.Status {
601-
case platform.ClusterStatusCREATED:
602-
return cluster.JSON200, string(cluster.JSON200.Status), nil
603-
case platform.ClusterStatusUPDATEFAILED, platform.ClusterStatusCREATEFAILED:
604-
return cluster.JSON200, string(cluster.JSON200.Status), fmt.Errorf("cluster mutation failed for cluster '%v'", cluster.JSON200.Id)
605-
case platform.ClusterStatusCREATING, platform.ClusterStatusUPDATING:
606-
return cluster.JSON200, string(cluster.JSON200.Status), nil
607-
default:
608-
return cluster.JSON200, string(cluster.JSON200.Status), fmt.Errorf("unexpected cluster status '%v' for cluster '%v'", cluster.JSON200.Status, cluster.JSON200.Id)
609-
}
610-
}
611-
return nil, "", fmt.Errorf("error getting cluster %s", clusterId)
612-
}
613-
}

internal/provider/resources/resource_deployment_test.go

+4-10
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,6 @@ import (
2020
"github.com/stretchr/testify/assert"
2121
)
2222

23-
// We will test dedicated deployment resources once dedicated_cluster_resource is implemented
24-
2523
func TestAcc_ResourceDeploymentHybrid(t *testing.T) {
2624
namePrefix := utils.GenerateTestResourceName(10)
2725

@@ -576,13 +574,9 @@ func hybridDeployment(input hybridDeploymentInput) string {
576574
} else {
577575
taskPodNodePoolIdStr = fmt.Sprintf(`task_pod_node_pool_id = "%v"`, input.NodePoolId)
578576
}
579-
return fmt.Sprintf(`
580-
resource "astro_workspace" "%v_workspace" {
581-
name = "%s"
582-
description = "%s"
583-
cicd_enforced_default = true
584-
}
585577

578+
workspaceId := strings.Split(os.Getenv("HYBRID_WORKSPACE_IDS"), ",")[0]
579+
return fmt.Sprintf(`
586580
resource "astro_deployment" "%v" {
587581
name = "%s"
588582
description = "%s"
@@ -594,13 +588,13 @@ resource "astro_deployment" "%v" {
594588
is_dag_deploy_enabled = true
595589
scheduler_au = %v
596590
scheduler_replicas = 1
597-
workspace_id = astro_workspace.%v_workspace.id
591+
workspace_id = "%v"
598592
%v
599593
%v
600594
%v
601595
}
602596
`,
603-
input.Name, input.Name, utils.TestResourceDescription, input.Name, input.Name, input.Description, input.ClusterId, input.Executor, input.SchedulerAu, input.Name,
597+
input.Name, input.Name, utils.TestResourceDescription, input.ClusterId, input.Executor, input.SchedulerAu, workspaceId,
604598
envVarsStr(input.IncludeEnvironmentVariables), wqStr, taskPodNodePoolIdStr)
605599
}
606600

0 commit comments

Comments
 (0)