Skip to content

Commit 2c8451e

Browse files
committed
add workspaces data source
1 parent db752d5 commit 2c8451e

16 files changed

+420
-73
lines changed

Makefile

+2-10
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
CORE_IAM_OPENAPI_SPEC=../astro-monorepo/apps/core/docs/iam/v1beta1/iam_v1beta1.yaml
2-
CORE_PLATFORM_OPENAPI_SPEC=../astro-monorepo/apps/core/docs/platform/v1beta1/platform_v1beta1.yaml
1+
CORE_IAM_OPENAPI_SPEC=../astro/apps/core/docs/iam/v1beta1/iam_v1beta1.yaml
2+
CORE_PLATFORM_OPENAPI_SPEC=../astro/apps/core/docs/platform/v1beta1/platform_v1beta1.yaml
33

44
DESIRED_OAPI_CODEGEN_VERSION=v2.1.0
55
DESIRED_MOCKERY_VERSION=v2.40.2
@@ -8,7 +8,6 @@ DESIRED_MOCKERY_VERSION=v2.40.2
88
ENVTEST_ASSETS_DIR=$(shell pwd)/bin
99
$(ENVTEST_ASSETS_DIR):
1010
mkdir -p $(ENVTEST_ASSETS_DIR)
11-
MOCKERY ?= $(ENVTEST_ASSETS_DIR)/mockery
1211
OAPI_CODEGEN ?= $(ENVTEST_ASSETS_DIR)/oapi-codegen
1312

1413
# Run acceptance tests
@@ -47,13 +46,6 @@ dep:
4746
build:
4847
go build -o ${ENVTEST_ASSETS_DIR}/terraform-provider-astronomer
4948
go generate ./...
50-
#
51-
#.PHONY: mock
52-
#mock: $(ENVTEST_ASSETS_DIR)
53-
# # Install correct mockery version if not installed
54-
# (test -s $(MOCKERY) && $(MOCKERY) --version | grep -i $(DESIRED_MOCKERY_VERSION)) || GOBIN=$(ENVTEST_ASSETS_DIR) go install github.com/vektra/mockery/v2@$(DESIRED_MOCKERY_VERSION)
55-
# rm -rf internal/mocks
56-
# $(MOCKERY) --config .mockery.yaml
5749

5850
.PHONY: api_client_gen
5951
api_client_gen: $(ENVTEST_ASSETS_DIR)

docs/data-sources/workspaces.md

+68
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
---
2+
# generated by https://github.com/hashicorp/terraform-plugin-docs
3+
page_title: "astronomer_workspaces Data Source - astronomer"
4+
subcategory: ""
5+
description: |-
6+
Workspaces data source
7+
---
8+
9+
# astronomer_workspaces (Data Source)
10+
11+
Workspaces data source
12+
13+
14+
15+
<!-- schema generated by tfplugindocs -->
16+
## Schema
17+
18+
### Optional
19+
20+
- `names` (List of String)
21+
- `workspace_ids` (List of String)
22+
23+
### Read-Only
24+
25+
- `workspaces` (Attributes List) (see [below for nested schema](#nestedatt--workspaces))
26+
27+
<a id="nestedatt--workspaces"></a>
28+
### Nested Schema for `workspaces`
29+
30+
Required:
31+
32+
- `id` (String) Workspace identifier
33+
34+
Read-Only:
35+
36+
- `cicd_enforced_default` (Boolean) Whether new Deployments enforce CI/CD deploys by default
37+
- `created_at` (String) Workspace creation timestamp
38+
- `created_by` (Attributes) Workspace creator (see [below for nested schema](#nestedatt--workspaces--created_by))
39+
- `description` (String) Workspace description
40+
- `name` (String) Workspace name
41+
- `organization_name` (String) Workspace organization name
42+
- `updated_at` (String) Workspace last updated timestamp
43+
- `updated_by` (Attributes) Workspace updater (see [below for nested schema](#nestedatt--workspaces--updated_by))
44+
45+
<a id="nestedatt--workspaces--created_by"></a>
46+
### Nested Schema for `workspaces.created_by`
47+
48+
Read-Only:
49+
50+
- `api_token_name` (String)
51+
- `avatar_url` (String)
52+
- `full_name` (String)
53+
- `id` (String)
54+
- `subject_type` (String)
55+
- `username` (String)
56+
57+
58+
<a id="nestedatt--workspaces--updated_by"></a>
59+
### Nested Schema for `workspaces.updated_by`
60+
61+
Read-Only:
62+
63+
- `api_token_name` (String)
64+
- `avatar_url` (String)
65+
- `full_name` (String)
66+
- `id` (String)
67+
- `subject_type` (String)
68+
- `username` (String)

examples/main.tf

+9
Original file line numberDiff line numberDiff line change
@@ -48,3 +48,12 @@ resource "astronomer_workspace" "imported_workspace" {
4848
output "imported_workspace" {
4949
value = astronomer_workspace.imported_workspace
5050
}
51+
52+
// list workspaces
53+
data "astronomer_workspaces" "list_workspaces" {
54+
names = ["imported_workspace"]
55+
}
56+
57+
output "list_workspaces" {
58+
value = data.astronomer_workspaces.list_workspaces
59+
}

internal/provider/datasources/data_source_workspace.go

+5-1
Original file line numberDiff line numberDiff line change
@@ -107,7 +107,11 @@ func (d *workspaceDataSource) Read(
107107
}
108108

109109
// Populate the model with the response data
110-
data.ReadFromResponse(ctx, workspace.JSON200)
110+
diags := data.ReadFromResponse(ctx, workspace.JSON200)
111+
if diags.HasError() {
112+
resp.Diagnostics.Append(diags...)
113+
return
114+
}
111115

112116
// Save data into Terraform state
113117
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,154 @@
1+
package datasources
2+
3+
import (
4+
"context"
5+
"fmt"
6+
"github.com/hashicorp/terraform-plugin-framework/attr"
7+
"github.com/samber/lo"
8+
"strings"
9+
10+
"github.com/astronomer/astronomer-terraform-provider/internal/clients"
11+
"github.com/astronomer/astronomer-terraform-provider/internal/clients/platform"
12+
"github.com/astronomer/astronomer-terraform-provider/internal/provider/models"
13+
"github.com/astronomer/astronomer-terraform-provider/internal/provider/schemas"
14+
"github.com/astronomer/astronomer-terraform-provider/internal/utils"
15+
"github.com/hashicorp/terraform-plugin-framework/datasource"
16+
"github.com/hashicorp/terraform-plugin-framework/datasource/schema"
17+
"github.com/hashicorp/terraform-plugin-log/tflog"
18+
)
19+
20+
// Ensure provider defined types fully satisfy framework interfaces.
21+
var _ datasource.DataSource = &workspacesDataSource{}
22+
var _ datasource.DataSourceWithConfigure = &workspacesDataSource{}
23+
24+
func NewWorkspacesDataSource() datasource.DataSource {
25+
return &workspacesDataSource{}
26+
}
27+
28+
// workspacesDataSource defines the data source implementation.
29+
type workspacesDataSource struct {
30+
PlatformClient platform.ClientWithResponsesInterface
31+
OrganizationId string
32+
}
33+
34+
func (d *workspacesDataSource) Metadata(
35+
ctx context.Context,
36+
req datasource.MetadataRequest,
37+
resp *datasource.MetadataResponse,
38+
) {
39+
resp.TypeName = req.ProviderTypeName + "_workspaces"
40+
}
41+
42+
func (d *workspacesDataSource) Schema(
43+
ctx context.Context,
44+
req datasource.SchemaRequest,
45+
resp *datasource.SchemaResponse,
46+
) {
47+
resp.Schema = schema.Schema{
48+
// This description is used by the documentation generator and the language server.
49+
MarkdownDescription: "Workspaces data source",
50+
Attributes: schemas.WorkspacesDataSourceSchemaAttributes(),
51+
}
52+
}
53+
54+
func (d *workspacesDataSource) Configure(
55+
ctx context.Context,
56+
req datasource.ConfigureRequest,
57+
resp *datasource.ConfigureResponse,
58+
) {
59+
// Prevent panic if the provider has not been configured.
60+
if req.ProviderData == nil {
61+
return
62+
}
63+
64+
apiClients, ok := req.ProviderData.(models.ApiClientsModel)
65+
if !ok {
66+
utils.DataSourceApiClientConfigureError(ctx, req, resp)
67+
return
68+
}
69+
70+
d.PlatformClient = apiClients.PlatformClient
71+
d.OrganizationId = apiClients.OrganizationId
72+
}
73+
74+
func (d *workspacesDataSource) Read(
75+
ctx context.Context,
76+
req datasource.ReadRequest,
77+
resp *datasource.ReadResponse,
78+
) {
79+
var data models.WorkspacesDataSource
80+
81+
// Read Terraform configuration data into the model
82+
resp.Diagnostics.Append(req.Config.Get(ctx, &data)...)
83+
84+
params := &platform.ListWorkspacesParams{
85+
Limit: lo.ToPtr(1000),
86+
}
87+
workspaceIds := data.WorkspaceIds.Elements()
88+
if len(workspaceIds) > 0 {
89+
workspaceIdsParam := lo.Map(workspaceIds, func(id attr.Value, _ int) string {
90+
// Terraform includes quotes around the string, so we need to remove them
91+
return strings.ReplaceAll(id.String(), `"`, "")
92+
})
93+
params.WorkspaceIds = &workspaceIdsParam
94+
}
95+
names := data.Names.Elements()
96+
if len(names) > 0 {
97+
namesParam := lo.Map(names, func(name attr.Value, _ int) string {
98+
// Terraform includes quotes around the string, so we need to remove them
99+
return strings.ReplaceAll(name.String(), `"`, "")
100+
})
101+
params.Names = &namesParam
102+
}
103+
104+
if resp.Diagnostics.HasError() {
105+
return
106+
}
107+
108+
var workspaces []platform.Workspace
109+
offset := 0
110+
for {
111+
params.Offset = &offset
112+
workspacesResp, err := d.PlatformClient.ListWorkspacesWithResponse(
113+
ctx,
114+
d.OrganizationId,
115+
params,
116+
)
117+
if err != nil {
118+
tflog.Error(ctx, "failed to get workspace", map[string]interface{}{"error": err})
119+
resp.Diagnostics.AddError(
120+
"Client Error",
121+
fmt.Sprintf("Unable to read workspace, got error: %s", err),
122+
)
123+
return
124+
}
125+
_, diagnostic := clients.NormalizeAPIError(ctx, workspacesResp.HTTPResponse, workspacesResp.Body)
126+
if diagnostic != nil {
127+
resp.Diagnostics.Append(diagnostic)
128+
return
129+
}
130+
if workspacesResp.JSON200 == nil {
131+
tflog.Error(ctx, "failed to get workspace", map[string]interface{}{"error": "nil response"})
132+
resp.Diagnostics.AddError("Client Error", "Unable to read workspace, got nil response")
133+
return
134+
}
135+
136+
workspaces = append(workspaces, workspacesResp.JSON200.Workspaces...)
137+
138+
if workspacesResp.JSON200.TotalCount <= offset {
139+
break
140+
}
141+
142+
offset += 1000
143+
}
144+
145+
// Populate the model with the response data
146+
diags := data.ReadFromResponse(ctx, workspaces)
147+
if diags.HasError() {
148+
resp.Diagnostics.Append(diags...)
149+
return
150+
}
151+
152+
// Save data into Terraform state
153+
resp.Diagnostics.Append(resp.State.Set(ctx, &data)...)
154+
}

internal/provider/models/subject_profile.go

+2-12
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,7 @@ package models
22

33
import (
44
"context"
5-
6-
"github.com/hashicorp/terraform-plugin-framework/attr"
5+
"github.com/astronomer/astronomer-terraform-provider/internal/provider/schemas"
76

87
"github.com/astronomer/astronomer-terraform-provider/internal/clients/iam"
98
"github.com/astronomer/astronomer-terraform-provider/internal/clients/platform"
@@ -21,15 +20,6 @@ type SubjectProfile struct {
2120
ApiTokenName types.String `tfsdk:"api_token_name"`
2221
}
2322

24-
var SubjectProfileTF map[string]attr.Type = map[string]attr.Type{
25-
"id": types.StringType,
26-
"subject_type": types.StringType,
27-
"username": types.StringType,
28-
"full_name": types.StringType,
29-
"avatar_url": types.StringType,
30-
"api_token_name": types.StringType,
31-
}
32-
3323
func SubjectProfileTypesObject(
3424
ctx context.Context,
3525
basicSubjectProfile any,
@@ -92,5 +82,5 @@ func SubjectProfileTypesObject(
9282
}
9383
}
9484
}
95-
return types.ObjectValueFrom(ctx, SubjectProfileTF, subjectProfile)
85+
return types.ObjectValueFrom(ctx, schemas.SubjectProfileTF, subjectProfile)
9686
}
+73
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
package models
2+
3+
import (
4+
"context"
5+
"github.com/astronomer/astronomer-terraform-provider/internal/clients/platform"
6+
"github.com/astronomer/astronomer-terraform-provider/internal/provider/schemas"
7+
"github.com/hashicorp/terraform-plugin-framework/attr"
8+
"github.com/hashicorp/terraform-plugin-framework/diag"
9+
"github.com/hashicorp/terraform-plugin-framework/types"
10+
)
11+
12+
// WorkspacesDataSource describes the data source data model.
13+
type WorkspacesDataSource struct {
14+
Workspaces types.List `tfsdk:"workspaces"`
15+
WorkspaceIds types.List `tfsdk:"workspace_ids"`
16+
Names types.List `tfsdk:"names"`
17+
}
18+
19+
func (data *WorkspacesDataSource) ReadFromResponse(
20+
ctx context.Context,
21+
workspaces []platform.Workspace,
22+
) diag.Diagnostics {
23+
if len(workspaces) == 0 {
24+
types.ListNull(types.ObjectType{AttrTypes: schemas.WorkspacesElementAttributeTypes()})
25+
}
26+
27+
values := make([]attr.Value, len(workspaces))
28+
for i, workspace := range workspaces {
29+
v := map[string]attr.Value{}
30+
v["id"] = types.StringValue(workspace.Id)
31+
v["name"] = types.StringValue(workspace.Name)
32+
if workspace.Description != nil {
33+
v["description"] = types.StringValue(*workspace.Description)
34+
} else {
35+
v["description"] = types.StringNull()
36+
}
37+
if workspace.OrganizationName != nil {
38+
v["organization_name"] = types.StringValue(*workspace.OrganizationName)
39+
} else {
40+
v["organization_name"] = types.StringNull()
41+
}
42+
v["cicd_enforced_default"] = types.BoolValue(workspace.CicdEnforcedDefault)
43+
v["created_at"] = types.StringValue(workspace.CreatedAt.String())
44+
v["updated_at"] = types.StringValue(workspace.UpdatedAt.String())
45+
if workspace.CreatedBy != nil {
46+
createdBy, diags := SubjectProfileTypesObject(ctx, workspace.CreatedBy)
47+
if diags.HasError() {
48+
return diags
49+
}
50+
v["created_by"] = createdBy
51+
}
52+
if workspace.UpdatedBy != nil {
53+
updatedBy, diags := SubjectProfileTypesObject(ctx, workspace.UpdatedBy)
54+
if diags.HasError() {
55+
return diags
56+
}
57+
v["updated_by"] = updatedBy
58+
}
59+
60+
objectValue, diags := types.ObjectValue(schemas.WorkspacesElementAttributeTypes(), v)
61+
if diags.HasError() {
62+
return diags
63+
}
64+
values[i] = objectValue
65+
}
66+
var diags diag.Diagnostics
67+
data.Workspaces, diags = types.ListValue(types.ObjectType{AttrTypes: schemas.WorkspacesElementAttributeTypes()}, values)
68+
if diags.HasError() {
69+
return diags
70+
}
71+
72+
return nil
73+
}

internal/provider/provider.go

+1
Original file line numberDiff line numberDiff line change
@@ -130,6 +130,7 @@ func (p *AstronomerProvider) Resources(ctx context.Context) []func() resource.Re
130130
func (p *AstronomerProvider) DataSources(ctx context.Context) []func() datasource.DataSource {
131131
return []func() datasource.DataSource{
132132
datasources.NewWorkspaceDataSource,
133+
datasources.NewWorkspacesDataSource,
133134
}
134135
}
135136

0 commit comments

Comments
 (0)