Skip to content

Commit cbe06a8

Browse files
hanxiaopscarlson
andauthored
feat: add workspace config to kargo instance and kargo agent (#325)
This implements improvements based on the commits in #323, enabling more fine-grained workspace config for the resources. Otherwise, different resources in the terraform file would need to be in the same workspace to function properly. --------- Co-authored-by: steve <[email protected]>
1 parent ab66f7a commit cbe06a8

18 files changed

+86
-37
lines changed

akp/data_source_akp_instance_test.go

+2-2
Original file line numberDiff line numberDiff line change
@@ -35,8 +35,8 @@ func TestAccInstanceDataSource(t *testing.T) {
3535
resource.TestCheckResourceAttr("data.akp_instance.test", "argocd.spec.instance_spec.host_aliases.0.hostnames.0", "test-1"),
3636
resource.TestCheckResourceAttr("data.akp_instance.test", "argocd.spec.instance_spec.host_aliases.0.hostnames.1", "test-2"),
3737

38-
// argocd_cm
39-
resource.TestCheckResourceAttr("data.akp_instance.test", "argocd_cm.%", "9"),
38+
// argocd_cm, all fields should be computed.
39+
resource.TestCheckResourceAttr("data.akp_instance.test", "argocd_cm.%", "0"),
4040
),
4141
},
4242
},

akp/data_source_akp_kargo_schema.go

+4
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,10 @@ func getAKPKargoDataSourceAttributes() map[string]schema.Attribute {
4040
ElementType: types.StringType,
4141
Computed: true,
4242
},
43+
"workspace": schema.StringAttribute{
44+
MarkdownDescription: "Workspace name for the Kargo instance",
45+
Computed: true,
46+
},
4347
}
4448
}
4549

akp/data_source_akp_kargoagent_schema.go

+4
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,10 @@ func getAKPKargoAgentDataSourceAttributes() map[string]schema.Attribute {
2525
MarkdownDescription: "The ID of the Kargo instance",
2626
Required: true,
2727
},
28+
"workspace": schema.StringAttribute{
29+
MarkdownDescription: "Workspace name for the Kargo agent",
30+
Computed: true,
31+
},
2832
"name": schema.StringAttribute{
2933
MarkdownDescription: "The name of the Kargo agent",
3034
Required: true,

akp/provider.go

+2
Original file line numberDiff line numberDiff line change
@@ -105,6 +105,7 @@ func (p *AkpProvider) Configure(ctx context.Context, req provider.ConfigureReque
105105
if ServerUrl == "" {
106106
ServerUrl = "https://akuity.cloud"
107107
}
108+
108109
if apiKeyID == "" {
109110
resp.Diagnostics.AddAttributeError(
110111
path.Root("api_key_id"),
@@ -159,6 +160,7 @@ func (p *AkpProvider) Configure(ctx context.Context, req provider.ConfigureReque
159160
argoc := argocdv1.NewArgoCDServiceGatewayClient(gwc)
160161
kargoc := kargov1.NewKargoServiceGatewayClient(gwc)
161162
orgc = orgcv1.NewOrganizationServiceGatewayClient(gwc)
163+
162164
akpCli := &AkpCli{
163165
Cli: argoc,
164166
KargoCli: kargoc,

akp/resource_akp_kargo.go

+29-15
Original file line numberDiff line numberDiff line change
@@ -139,32 +139,26 @@ func (r *AkpKargoInstanceResource) ImportState(ctx context.Context, req resource
139139

140140
func (r *AkpKargoInstanceResource) upsert(ctx context.Context, diagnostics *diag.Diagnostics, plan *types.KargoInstance) error {
141141
ctx = httpctx.SetAuthorizationHeader(ctx, r.akpCli.Cred.Scheme(), r.akpCli.Cred.Credential())
142-
workspaces, err := r.akpCli.OrgCli.ListWorkspaces(ctx, &orgcv1.ListWorkspacesRequest{
143-
OrganizationId: r.akpCli.OrgId,
144-
})
142+
143+
workspace, err := getWorkspace(ctx, r.akpCli.OrgCli, r.akpCli.OrgId, plan.Workspace.ValueString())
145144
if err != nil {
146-
return errors.Wrap(err, "Unable to read workspaces")
147-
}
148-
var workspaceId string
149-
for _, w := range workspaces.GetWorkspaces() {
150-
if w.GetIsDefault() {
151-
workspaceId = w.GetId()
152-
break
153-
}
154-
}
155-
if workspaceId == "" {
156-
return errors.New("Default workspace not found")
145+
diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get workspace. %s", err))
146+
return errors.New("Unable to get workspace")
157147
}
158-
apiReq := buildKargoApplyRequest(ctx, diagnostics, plan, r.akpCli.OrgId, workspaceId)
148+
apiReq := buildKargoApplyRequest(ctx, diagnostics, plan, r.akpCli.OrgId, workspace.GetId())
159149
if diagnostics.HasError() {
160150
return errors.New("Unable to build Kargo instance request")
161151
}
162152
tflog.Debug(ctx, fmt.Sprintf("Apply instance request: %s", apiReq))
153+
163154
_, err = r.akpCli.KargoCli.ApplyKargoInstance(ctx, apiReq)
164155
if err != nil {
165156
return errors.Wrap(err, "Unable to upsert Kargo instance")
166157
}
167158

159+
if plan.Workspace.ValueString() == "" {
160+
plan.Workspace = tftypes.StringValue(workspace.GetName())
161+
}
168162
return refreshKargoState(ctx, diagnostics, r.akpCli.KargoCli, plan, r.akpCli.OrgId)
169163
}
170164

@@ -245,3 +239,23 @@ func refreshKargoState(ctx context.Context, diagnostics *diag.Diagnostics, clien
245239
tflog.Debug(ctx, fmt.Sprintf("Export Kargo instance response: %s", exportResp))
246240
return kargo.Update(ctx, diagnostics, exportResp)
247241
}
242+
243+
func getWorkspace(ctx context.Context, orgc orgcv1.OrganizationServiceGatewayClient, orgid, name string) (*orgcv1.Workspace, error) {
244+
workspaces, err := orgc.ListWorkspaces(ctx, &orgcv1.ListWorkspacesRequest{
245+
OrganizationId: orgid,
246+
})
247+
if err != nil {
248+
return nil, errors.Wrap(err, "unable to read org workspaces")
249+
}
250+
for _, w := range workspaces.GetWorkspaces() {
251+
if name == "" && w.IsDefault {
252+
// if no workspace name is provided, return the default workspace
253+
return w, nil
254+
}
255+
if w.Name == name {
256+
return w, nil
257+
}
258+
}
259+
260+
return nil, fmt.Errorf("workspace %s not found", name)
261+
}

akp/resource_akp_kargo_schema.go

+8
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,14 @@ func getAKPKargoInstanceAttributes() map[string]schema.Attribute {
5959
Optional: true,
6060
Sensitive: true,
6161
},
62+
"workspace": schema.StringAttribute{
63+
Optional: true,
64+
Computed: true,
65+
MarkdownDescription: "Workspace name for the Kargo instance",
66+
PlanModifiers: []planmodifier.String{
67+
stringplanmodifier.UseStateForUnknown(),
68+
},
69+
},
6270
}
6371
}
6472

akp/resource_akp_kargoagent.go

+10-16
Original file line numberDiff line numberDiff line change
@@ -10,13 +10,13 @@ import (
1010
"github.com/hashicorp/terraform-plugin-framework/path"
1111
"github.com/hashicorp/terraform-plugin-framework/resource"
1212
"github.com/hashicorp/terraform-plugin-framework/tfsdk"
13+
tftypes "github.com/hashicorp/terraform-plugin-framework/types"
1314
"github.com/hashicorp/terraform-plugin-log/tflog"
1415
"github.com/pkg/errors"
1516
"golang.org/x/exp/slices"
1617
"google.golang.org/protobuf/types/known/structpb"
1718

1819
kargov1 "github.com/akuity/api-client-go/pkg/api/gen/kargo/v1"
19-
orgcv1 "github.com/akuity/api-client-go/pkg/api/gen/organization/v1"
2020
healthv1 "github.com/akuity/api-client-go/pkg/api/gen/types/status/health/v1"
2121
reconv1 "github.com/akuity/api-client-go/pkg/api/gen/types/status/reconciliation/v1"
2222
httpctx "github.com/akuity/grpc-gateway-client/pkg/http/context"
@@ -179,30 +179,24 @@ func (r *AkpKargoAgentResource) ImportState(ctx context.Context, req resource.Im
179179

180180
func (r *AkpKargoAgentResource) upsert(ctx context.Context, diagnostics *diag.Diagnostics, plan *types.KargoAgent, isCreate bool) (*types.KargoAgent, error) {
181181
ctx = httpctx.SetAuthorizationHeader(ctx, r.akpCli.Cred.Scheme(), r.akpCli.Cred.Credential())
182-
workspaces, err := r.akpCli.OrgCli.ListWorkspaces(ctx, &orgcv1.ListWorkspacesRequest{
183-
OrganizationId: r.akpCli.OrgId,
184-
})
182+
183+
workspace, err := getWorkspace(ctx, r.akpCli.OrgCli, r.akpCli.OrgId, plan.Workspace.ValueString())
185184
if err != nil {
186-
return nil, errors.Wrap(err, "Unable to read workspaces")
185+
diagnostics.AddError("Client Error", fmt.Sprintf("Unable to get workspace. %s", err))
186+
return nil, errors.New("Unable to get workspace")
187187
}
188-
var workspaceId string
189-
for _, w := range workspaces.GetWorkspaces() {
190-
if w.GetIsDefault() {
191-
workspaceId = w.GetId()
192-
break
193-
}
194-
}
195-
if workspaceId == "" {
196-
return nil, errors.New("Default workspace not found")
197-
}
198-
apiReq := buildKargoAgentApplyRequest(ctx, diagnostics, plan, r.akpCli.OrgId, workspaceId)
188+
apiReq := buildKargoAgentApplyRequest(ctx, diagnostics, plan, r.akpCli.OrgId, workspace.Id)
199189
if diagnostics.HasError() {
200190
return nil, nil
201191
}
202192
result, err := r.applyKargoInstance(ctx, plan, apiReq, isCreate, r.akpCli.KargoCli.ApplyKargoInstance, r.upsertKubeConfig)
203193
if err != nil {
204194
return result, err
205195
}
196+
197+
if plan.Workspace.ValueString() == "" {
198+
plan.Workspace = tftypes.StringValue(workspace.GetName())
199+
}
206200
return result, refreshKargoAgentState(ctx, diagnostics, r.akpCli.KargoCli, result, r.akpCli.OrgId, nil, plan)
207201
}
208202

akp/resource_akp_kargoagent_schema.go

+12-2
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,14 @@ func getAKPKargoAgentResourceAttributes() map[string]schema.Attribute {
3838
stringplanmodifier.RequiresReplace(),
3939
},
4040
},
41+
"workspace": schema.StringAttribute{
42+
Optional: true,
43+
Computed: true,
44+
MarkdownDescription: "Workspace name for the Kargo agent",
45+
PlanModifiers: []planmodifier.String{
46+
stringplanmodifier.UseStateForUnknown(),
47+
},
48+
},
4149
"name": schema.StringAttribute{
4250
MarkdownDescription: "The name of the Kargo agent",
4351
Required: true,
@@ -150,15 +158,17 @@ func getAKPKargoAgentDataAttributes() map[string]schema.Attribute {
150158
Optional: true,
151159
Computed: true,
152160
PlanModifiers: []planmodifier.String{
153-
stringplanmodifier.RequiresReplace(),
161+
stringplanmodifier.UseStateForUnknown(),
162+
stringplanmodifier.RequiresReplaceIfConfigured(),
154163
},
155164
},
156165
"akuity_managed": schema.BoolAttribute{
157166
MarkdownDescription: "This means the agent is managed by Akuity",
158167
Optional: true,
159168
Computed: true,
160169
PlanModifiers: []planmodifier.Bool{
161-
boolplanmodifier.RequiresReplace(),
170+
boolplanmodifier.UseStateForUnknown(),
171+
boolplanmodifier.RequiresReplaceIfConfigured(),
162172
},
163173
},
164174
"argocd_namespace": schema.StringAttribute{

akp/types/kargo_instance.go

+1
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ type KargoInstance struct {
2020
Kargo *Kargo `tfsdk:"kargo"`
2121
KargoConfigMap types.Map `tfsdk:"kargo_cm"`
2222
KargoSecret types.Map `tfsdk:"kargo_secret"`
23+
Workspace types.String `tfsdk:"workspace"`
2324
}
2425

2526
func (k *KargoInstance) Update(ctx context.Context, diagnostics *diag.Diagnostics, exportResp *kargov1.ExportKargoInstanceResponse) error {

akp/types/kargoagent.go

+1
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import (
1212
type KargoAgent struct {
1313
ID types.String `tfsdk:"id"`
1414
InstanceID types.String `tfsdk:"instance_id"`
15+
Workspace types.String `tfsdk:"workspace"`
1516
Name types.String `tfsdk:"name"`
1617
Namespace types.String `tfsdk:"namespace"`
1718
Labels types.Map `tfsdk:"labels"`

docs/data-sources/kargo_agent.md

+1
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ data "akp_kargo_agent" "example" {
3535
- `namespace` (String) The namespace of the Kargo agent
3636
- `remove_agent_resources_on_destroy` (Boolean) Whether to remove agent resources on destroy
3737
- `spec` (Attributes) The spec of the Kargo agent (see [below for nested schema](#nestedatt--spec))
38+
- `workspace` (String) Workspace name for the Kargo agent
3839

3940
<a id="nestedatt--kube_config"></a>
4041
### Nested Schema for `kube_config`

docs/data-sources/kargo_agents.md

+1
Original file line numberDiff line numberDiff line change
@@ -51,6 +51,7 @@ Read-Only:
5151
- `namespace` (String) The namespace of the Kargo agent
5252
- `remove_agent_resources_on_destroy` (Boolean) Whether to remove agent resources on destroy
5353
- `spec` (Attributes) The spec of the Kargo agent (see [below for nested schema](#nestedatt--agents--spec))
54+
- `workspace` (String) Workspace name for the Kargo agent
5455

5556
<a id="nestedatt--agents--kube_config"></a>
5657
### Nested Schema for `agents.kube_config`

docs/data-sources/kargo_instance.md

+1
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@ data "akp_kargo_instance" "example" {
3131
- `kargo` (Attributes) Specification of the Kargo instance (see [below for nested schema](#nestedatt--kargo))
3232
- `kargo_cm` (Map of String) ConfigMap to configure system account accesses. The usage can be found in the examples/resources/akp_kargo_instance/resource.tf
3333
- `kargo_secret` (Map of String) Secret to configure system account accesses. The usage can be found in the examples/resources/akp_kargo_instance/resource.tf
34+
- `workspace` (String) Workspace name for the Kargo instance
3435

3536
<a id="nestedatt--kargo"></a>
3637
### Nested Schema for `kargo`

docs/resources/kargo_agent.md

+3
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ resource "akp_kargo_agent" "example-agent" {
2929
instance_id = akp_kargo_instance.example.id
3030
name = "test-agent"
3131
namespace = "test-namespace"
32+
workspace = "kargo-workspace"
3233
labels = {
3334
"app" = "kargo"
3435
}
@@ -54,6 +55,7 @@ resource "akp_kargo_agent" "example-agent" {
5455
instance_id = akp_kargo_instance.example.id
5556
name = "test-agent"
5657
namespace = "test-namespace"
58+
workspace = "kargo-workspace"
5759
labels = {
5860
"app" = "kargo"
5961
}
@@ -105,6 +107,7 @@ EOT
105107
- `labels` (Map of String) Labels
106108
- `namespace` (String) The namespace of the Kargo agent
107109
- `remove_agent_resources_on_destroy` (Boolean) Remove agent Kubernetes resources from the managed cluster when destroying cluster, default to `true`
110+
- `workspace` (String) Workspace name for the Kargo agent
108111

109112
### Read-Only
110113

docs/resources/kargo_instance.md

+3-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ resource "akp_kargo_instance" "example" {
2626
## Example Usage (Exhaustive)
2727
```terraform
2828
resource "akp_kargo_instance" "example" {
29-
name = "test"
29+
name = "test"
30+
workspace = "kargo-workspace"
3031
kargo_cm = {
3132
adminAccountEnabled = "true"
3233
adminAccountTokenTtl = "24h"
@@ -128,6 +129,7 @@ EOT
128129

129130
- `kargo_cm` (Map of String) ConfigMap to configure system account accesses. The usage can be found in the examples/resources/akp_kargo_instance/resource.tf
130131
- `kargo_secret` (Map of String, Sensitive) Secret to configure system account accesses. The usage can be found in the examples/resources/akp_kargo_instance/resource.tf
132+
- `workspace` (String) Workspace name for the Kargo instance
131133

132134
### Read-Only
133135

examples/resources/akp_kargo_agent/akuity_managed.tf

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ resource "akp_kargo_agent" "example-agent" {
22
instance_id = akp_kargo_instance.example.id
33
name = "test-agent"
44
namespace = "test-namespace"
5+
workspace = "kargo-workspace"
56
labels = {
67
"app" = "kargo"
78
}

examples/resources/akp_kargo_agent/self_hosted.tf

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ resource "akp_kargo_agent" "example-agent" {
22
instance_id = akp_kargo_instance.example.id
33
name = "test-agent"
44
namespace = "test-namespace"
5+
workspace = "kargo-workspace"
56
labels = {
67
"app" = "kargo"
78
}

examples/resources/akp_kargo_instance/resource.tf

+2-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
resource "akp_kargo_instance" "example" {
2-
name = "test"
2+
name = "test"
3+
workspace = "kargo-workspace"
34
kargo_cm = {
45
adminAccountEnabled = "true"
56
adminAccountTokenTtl = "24h"

0 commit comments

Comments
 (0)