Skip to content

Commit b7b0449

Browse files
committed
fix: adapt tenant_v4 resource to actual implementation
1 parent c6af469 commit b7b0449

File tree

2 files changed

+116
-95
lines changed

2 files changed

+116
-95
lines changed

internal/provider/tenant_resource.go

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package provider
33
import (
44
"context"
55
"fmt"
6+
"slices"
67
"strings"
78

89
"github.com/meshcloud/terraform-provider-meshstack/client"
@@ -255,14 +256,12 @@ func (r *tenantResource) Delete(ctx context.Context, req resource.DeleteRequest,
255256
func (r *tenantResource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
256257
identifier := strings.Split(req.ID, ".")
257258

258-
for _, s := range identifier {
259-
if s == "" {
260-
resp.Diagnostics.AddError(
261-
"Incomplete Import Identifier",
262-
fmt.Sprintf("Encountered empty import identifier field. Got: %q", req.ID),
263-
)
264-
return
265-
}
259+
if slices.Contains(identifier, "") {
260+
resp.Diagnostics.AddError(
261+
"Incomplete Import Identifier",
262+
fmt.Sprintf("Encountered empty import identifier field. Got: %q", req.ID),
263+
)
264+
return
266265
}
267266

268267
if len(identifier) != 4 {

internal/provider/tenant_v4_resource.go

Lines changed: 109 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,35 @@ var (
2323
_ resource.ResourceWithImportState = &tenantV4Resource{}
2424
)
2525

26+
type tenantV4ResourceModel struct {
27+
ApiVersion types.String `tfsdk:"api_version"`
28+
Kind types.String `tfsdk:"kind"`
29+
Metadata tenantV4ResourceMetadataModel `tfsdk:"metadata"`
30+
Spec tenantV4ResourceSpecModel `tfsdk:"spec"`
31+
Status *client.MeshTenantV4Status `tfsdk:"status"`
32+
}
33+
34+
type tenantV4ResourceMetadataModel struct {
35+
Uuid types.String `tfsdk:"uuid"`
36+
OwnedByWorkspace types.String `tfsdk:"owned_by_workspace"`
37+
OwnedByProject types.String `tfsdk:"owned_by_project"`
38+
CreatedOn types.String `tfsdk:"created_on"`
39+
DeletedOn types.String `tfsdk:"deleted_on"`
40+
MarkedForDeletionOn types.String `tfsdk:"marked_for_deletion_on"`
41+
}
42+
43+
type tenantV4ResourceSpecModel struct {
44+
PlatformIdentifier types.String `tfsdk:"platform_identifier"`
45+
PlatformTenantId types.String `tfsdk:"platform_tenant_id"`
46+
LandingZoneIdentifier types.String `tfsdk:"landing_zone_identifier"`
47+
Quotas []tenantQuotaModel `tfsdk:"quotas"`
48+
}
49+
50+
type tenantQuotaModel struct {
51+
Key types.String `tfsdk:"key"`
52+
Value types.Int64 `tfsdk:"value"`
53+
}
54+
2655
func NewTenantV4Resource() resource.Resource {
2756
return &tenantV4Resource{}
2857
}
@@ -56,17 +85,17 @@ func (r *tenantV4Resource) Configure(_ context.Context, req resource.ConfigureRe
5685

5786
func (r *tenantV4Resource) Schema(_ context.Context, _ resource.SchemaRequest, resp *resource.SchemaResponse) {
5887
resp.Schema = schema.Schema{
59-
MarkdownDescription: "Single tenant by workspace, project, and platform (v4).",
88+
MarkdownDescription: "Manages a `meshTenant` with API version 4.",
6089

6190
Attributes: map[string]schema.Attribute{
6291
"api_version": schema.StringAttribute{
63-
MarkdownDescription: "Tenant datatype version",
92+
MarkdownDescription: "API version of the tenant resource.",
6493
Computed: true,
6594
PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()},
6695
},
6796

6897
"kind": schema.StringAttribute{
69-
MarkdownDescription: "meshObject type, always `meshTenant`.",
98+
MarkdownDescription: "The kind of the meshObject, always `meshTenant`.",
7099
Computed: true,
71100
Validators: []validator.String{
72101
stringvalidator.OneOf([]string{"meshTenant"}...),
@@ -75,28 +104,36 @@ func (r *tenantV4Resource) Schema(_ context.Context, _ resource.SchemaRequest, r
75104
},
76105

77106
"metadata": schema.SingleNestedAttribute{
78-
MarkdownDescription: "Tenant metadata. Workspace, project and platform of the target tenant must be set here.",
107+
MarkdownDescription: "Metadata of the tenant. The `owned_by_workspace` and `owned_by_project` attributes must be set here.",
79108
Required: true,
80109
PlanModifiers: []planmodifier.Object{objectplanmodifier.RequiresReplace()},
81110
Attributes: map[string]schema.Attribute{
82111
"uuid": schema.StringAttribute{
83-
MarkdownDescription: "UUID of the tenant.",
84-
Required: true,
112+
MarkdownDescription: "The unique identifier (UUID) of the tenant.",
113+
Computed: true,
114+
PlanModifiers: []planmodifier.String{
115+
stringplanmodifier.UseStateForUnknown(),
116+
},
85117
},
86118
"owned_by_workspace": schema.StringAttribute{
87-
MarkdownDescription: "Identifier of the workspace the tenant belongs to.",
119+
MarkdownDescription: "The identifier of the workspace that the tenant belongs to.",
88120
Required: true,
89121
},
90122
"owned_by_project": schema.StringAttribute{
91-
MarkdownDescription: "Identifier of the project the tenant belongs to.",
123+
MarkdownDescription: "The identifier of the project that the tenant belongs to.",
92124
Required: true,
93125
},
126+
"created_on": schema.StringAttribute{
127+
MarkdownDescription: "The creation timestamp of the meshTenant (e.g. `2020-12-22T09:37:43Z`).",
128+
Computed: true,
129+
PlanModifiers: []planmodifier.String{stringplanmodifier.UseStateForUnknown()},
130+
},
94131
"deleted_on": schema.StringAttribute{
95-
MarkdownDescription: "If the tenant has been submitted for deletion by a workspace manager, the date is shown here (e.g. 2020-12-22T09:37:43Z).",
132+
MarkdownDescription: "The deletion timestamp of the tenant (e.g. `2020-12-22T09:37:43Z`).",
96133
Computed: true,
97134
},
98-
"created_on": schema.StringAttribute{
99-
MarkdownDescription: "The date the tenant was created (e.g. 2020-12-22T09:37:43Z).",
135+
"marked_for_deletion_on": schema.StringAttribute{
136+
MarkdownDescription: "The timestamp when the tenant was marked for deletion (e.g. `2020-12-22T09:37:43Z`).",
100137
Computed: true,
101138
},
102139
},
@@ -111,24 +148,24 @@ func (r *tenantV4Resource) Schema(_ context.Context, _ resource.SchemaRequest, r
111148
MarkdownDescription: "Identifier of the target platform.",
112149
Required: true,
113150
},
114-
"local_id": schema.StringAttribute{
115-
MarkdownDescription: "Tenant ID local to the platform (e.g. GCP project ID, Azure subscription ID). Setting the local ID means that a tenant with this ID should be imported into meshStack. Not setting a local ID means that a new tenant should be created. Field will be empty until a successful replication has run.",
151+
"platform_tenant_id": schema.StringAttribute{
152+
MarkdownDescription: "The identifier of the tenant on the platform (e.g. GCP project ID or Azure subscription ID). If this is not set, a new tenant will be created. If this is set, an existing tenant will be imported. Otherwise, this field will be empty until a successful replication has run.",
116153
Optional: true,
117154
Computed: true,
118155
},
119156
"landing_zone_identifier": schema.StringAttribute{
120-
MarkdownDescription: "Identifier of landing zone to assign to this tenant.",
157+
MarkdownDescription: "The identifier of the landing zone to assign to this tenant.",
121158
Optional: true,
122159
Computed: true,
123160
},
124161
"quotas": schema.ListNestedAttribute{
125-
MarkdownDescription: "Set of applied tenant quotas. By default the landing zone quotas are applied to new tenants.",
162+
MarkdownDescription: "The set of quotas applied to the tenant. By default, the landing zone quotas are applied to new tenants.",
126163
Optional: true,
127164
Computed: true,
128165
NestedObject: schema.NestedAttributeObject{
129166
Attributes: map[string]schema.Attribute{
130-
"key": schema.StringAttribute{Computed: true},
131-
"value": schema.Int64Attribute{Computed: true},
167+
"key": schema.StringAttribute{Required: true},
168+
"value": schema.Int64Attribute{Required: true},
132169
},
133170
},
134171
},
@@ -139,17 +176,21 @@ func (r *tenantV4Resource) Schema(_ context.Context, _ resource.SchemaRequest, r
139176
MarkdownDescription: "Tenant status.",
140177
Computed: true,
141178
Attributes: map[string]schema.Attribute{
142-
"tags": schema.MapAttribute{
143-
MarkdownDescription: "Tags assigned to this tenant.",
144-
ElementType: types.ListType{ElemType: types.StringType},
179+
"tenant_name": schema.StringAttribute{
180+
MarkdownDescription: "The full tenant name, a concatenation of the workspace identifier, project identifier and platform identifier.",
145181
Computed: true,
146182
},
147-
"last_replicated": schema.StringAttribute{
148-
MarkdownDescription: "The last time the tenant was replicated (e.g. 2020-12-22T09:37:43Z).",
183+
"platform_type_identifier": schema.StringAttribute{
184+
MarkdownDescription: "Identifier of the platform type.",
149185
Computed: true,
150186
},
151-
"current_replication_status": schema.StringAttribute{
152-
MarkdownDescription: "The current replication status of the tenant.",
187+
"platform_workspace_identifier": schema.StringAttribute{
188+
MarkdownDescription: "Some platforms create representations of workspaces, in such cases this will contain the identifier of the workspace on the platform.",
189+
Computed: true,
190+
},
191+
"tags": schema.MapAttribute{
192+
MarkdownDescription: "Tags assigned to this tenant.",
193+
ElementType: types.ListType{ElemType: types.StringType},
153194
Computed: true,
154195
},
155196
},
@@ -159,22 +200,42 @@ func (r *tenantV4Resource) Schema(_ context.Context, _ resource.SchemaRequest, r
159200
}
160201

161202
func (r *tenantV4Resource) Create(ctx context.Context, req resource.CreateRequest, resp *resource.CreateResponse) {
162-
var metadata client.MeshTenantV4CreateMetadata
163-
resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("metadata"), &metadata)...)
164-
165-
var spec client.MeshTenantV4CreateSpec
166-
resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("spec"), &spec)...)
203+
var plan tenantV4ResourceModel
204+
resp.Diagnostics.Append(req.Plan.Get(ctx, &plan)...)
167205

168206
if resp.Diagnostics.HasError() {
169207
return
170208
}
171209

172-
create := client.MeshTenantV4Create{
173-
Metadata: metadata,
174-
Spec: spec,
210+
createRequest := client.MeshTenantV4Create{
211+
Metadata: client.MeshTenantV4CreateMetadata{
212+
OwnedByProject: plan.Metadata.OwnedByProject.ValueString(),
213+
OwnedByWorkspace: plan.Metadata.OwnedByWorkspace.ValueString(),
214+
},
215+
Spec: client.MeshTenantV4CreateSpec{
216+
PlatformIdentifier: plan.Spec.PlatformIdentifier.ValueString(),
217+
},
175218
}
176219

177-
tenant, err := r.client.CreateTenantV4(&create)
220+
if !plan.Spec.PlatformTenantId.IsNull() && !plan.Spec.PlatformTenantId.IsUnknown() {
221+
createRequest.Spec.PlatformTenantId = plan.Spec.PlatformTenantId.ValueStringPointer()
222+
}
223+
if !plan.Spec.LandingZoneIdentifier.IsNull() && !plan.Spec.LandingZoneIdentifier.IsUnknown() {
224+
createRequest.Spec.LandingZoneIdentifier = plan.Spec.LandingZoneIdentifier.ValueStringPointer()
225+
}
226+
227+
if len(plan.Spec.Quotas) > 0 {
228+
quotas := make([]client.MeshTenantQuota, len(plan.Spec.Quotas))
229+
for i, q := range plan.Spec.Quotas {
230+
quotas[i] = client.MeshTenantQuota{
231+
Key: q.Key.ValueString(),
232+
Value: q.Value.ValueInt64(),
233+
}
234+
}
235+
createRequest.Spec.Quotas = &quotas
236+
}
237+
238+
tenant, err := r.client.CreateTenantV4(&createRequest)
178239
if err != nil {
179240
resp.Diagnostics.AddError(
180241
"Error creating tenant",
@@ -186,19 +247,25 @@ func (r *tenantV4Resource) Create(ctx context.Context, req resource.CreateReques
186247
resp.Diagnostics.Append(resp.State.Set(ctx, tenant)...)
187248
}
188249

250+
func (r *tenantV4Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
251+
resp.Diagnostics.AddError("Tenants can't be updated", "Unsupported operation: tenant can't be updated.")
252+
}
253+
189254
func (r *tenantV4Resource) Read(ctx context.Context, req resource.ReadRequest, resp *resource.ReadResponse) {
190-
var uuid string
191-
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("metadata").AtName("uuid"), &uuid)...)
255+
var state tenantV4ResourceModel
256+
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
192257

193258
if resp.Diagnostics.HasError() {
194259
return
195260
}
196261

262+
uuid := state.Metadata.Uuid.ValueString()
263+
197264
tenant, err := r.client.ReadTenantV4(uuid)
198265
if err != nil {
199266
resp.Diagnostics.AddError(
200267
"Error reading tenant",
201-
fmt.Sprintf("Could not read tenant, unexpected error: %s", err.Error()),
268+
fmt.Sprintf("Could not read tenant with uuid %s, unexpected error: %s", uuid, err.Error()),
202269
)
203270
return
204271
}
@@ -211,71 +278,26 @@ func (r *tenantV4Resource) Read(ctx context.Context, req resource.ReadRequest, r
211278
resp.Diagnostics.Append(resp.State.Set(ctx, tenant)...)
212279
}
213280

214-
func (r *tenantV4Resource) Update(ctx context.Context, req resource.UpdateRequest, resp *resource.UpdateResponse) {
215-
var metadata client.MeshTenantV4CreateMetadata
216-
resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("metadata"), &metadata)...)
217-
218-
var spec client.MeshTenantV4CreateSpec
219-
resp.Diagnostics.Append(req.Plan.GetAttribute(ctx, path.Root("spec"), &spec)...)
220-
221-
if resp.Diagnostics.HasError() {
222-
return
223-
}
224-
225-
update := client.MeshTenantV4Create{
226-
Metadata: metadata,
227-
Spec: spec,
228-
}
229-
230-
tenant, err := r.client.CreateTenantV4(&update)
231-
if err != nil {
232-
resp.Diagnostics.AddError(
233-
"Error updating tenant",
234-
fmt.Sprintf("Could not update tenant, unexpected error: %s", err.Error()),
235-
)
236-
return
237-
}
238-
239-
resp.Diagnostics.Append(resp.State.Set(ctx, tenant)...)
240-
}
241-
242281
func (r *tenantV4Resource) Delete(ctx context.Context, req resource.DeleteRequest, resp *resource.DeleteResponse) {
243-
var uuid string
244-
resp.Diagnostics.Append(req.State.GetAttribute(ctx, path.Root("metadata").AtName("uuid"), &uuid)...)
282+
var state tenantV4ResourceModel
283+
resp.Diagnostics.Append(req.State.Get(ctx, &state)...)
245284

246285
if resp.Diagnostics.HasError() {
247286
return
248287
}
249288

289+
uuid := state.Metadata.Uuid.ValueString()
290+
250291
err := r.client.DeleteTenantV4(uuid)
251292
if err != nil {
252293
resp.Diagnostics.AddError(
253294
"Error deleting tenant",
254-
fmt.Sprintf("Could not delete tenant, unexpected error: %s", err.Error()),
295+
fmt.Sprintf("Could not delete tenant with uuid %s, unexpected error: %s", uuid, err.Error()),
255296
)
256297
return
257298
}
258299
}
259300

260301
func (r *tenantV4Resource) ImportState(ctx context.Context, req resource.ImportStateRequest, resp *resource.ImportStateResponse) {
261-
uuid := req.ID
262-
263-
tenant, err := r.client.ReadTenantV4(uuid)
264-
if err != nil {
265-
resp.Diagnostics.AddError(
266-
"Error importing tenant",
267-
fmt.Sprintf("Could not import tenant, unexpected error: %s", err.Error()),
268-
)
269-
return
270-
}
271-
272-
if tenant == nil {
273-
resp.Diagnostics.AddError(
274-
"Error importing tenant",
275-
"Tenant not found",
276-
)
277-
return
278-
}
279-
280-
resp.Diagnostics.Append(resp.State.Set(ctx, tenant)...)
302+
resource.ImportStatePassthroughID(ctx, path.Root("metadata").AtName("uuid"), req, resp)
281303
}

0 commit comments

Comments
 (0)