Skip to content

Commit b92d802

Browse files
committed
feat: add cmr update
1 parent 4d5575b commit b92d802

File tree

2 files changed

+272
-3
lines changed

2 files changed

+272
-3
lines changed

redpanda/resources/cluster/model_to_proto.go

+39-3
Original file line numberDiff line numberDiff line change
@@ -387,9 +387,45 @@ func getMtlsSpec(ctx context.Context, mtls types.Object, diags diag.Diagnostics)
387387
}, diags
388388
}
389389

390-
func generateClusterCMRUpdate(_ context.Context, _ models.Cluster, diags diag.Diagnostics) (*controlplanev1beta2.CustomerManagedResourcesUpdate, diag.Diagnostics) {
391-
// TODO implement for GCP BYOVPC
392-
return nil, diags
390+
func generateClusterCMRUpdate(ctx context.Context, cluster models.Cluster, diags diag.Diagnostics) (*controlplanev1beta2.CustomerManagedResourcesUpdate, diag.Diagnostics) {
391+
// Early returns if not applicable
392+
if cluster.CustomerManagedResources.IsNull() {
393+
return nil, diags
394+
}
395+
396+
// Only supports GCP in the API so no point going past here if not gcp
397+
if cluster.CloudProvider.ValueString() != utils.CloudProviderStringGcp {
398+
return nil, diags
399+
}
400+
401+
// Get the CMR object
402+
var cmrObj types.Object
403+
if d := cluster.CustomerManagedResources.As(ctx, &cmrObj, basetypes.ObjectAsOptions{
404+
UnhandledNullAsEmpty: true,
405+
UnhandledUnknownAsEmpty: true,
406+
}); d.HasError() {
407+
return nil, d
408+
}
409+
410+
// Get the GCP object from CustomerManagedResources
411+
gcp, d := getObjectFromAttributes(ctx, "gcp", gcpType, cmrObj.Attributes(), diags)
412+
if d.HasError() {
413+
if !utils.IsNotFoundSpec(d) {
414+
return nil, d
415+
}
416+
}
417+
418+
gcpUpdate := &controlplanev1beta2.CustomerManagedResourcesUpdate_GCP{}
419+
if pscNatSubnetName, ok := gcp.Attributes()["psc_nat_subnet_name"].(types.String); ok && !pscNatSubnetName.IsNull() {
420+
gcpUpdate.PscNatSubnetName = pscNatSubnetName.ValueString()
421+
}
422+
423+
// Create and return the update object
424+
return &controlplanev1beta2.CustomerManagedResourcesUpdate{
425+
CloudProvider: &controlplanev1beta2.CustomerManagedResourcesUpdate_Gcp{
426+
Gcp: gcpUpdate,
427+
},
428+
}, diags
393429
}
394430

395431
func getAzurePrivateLinkSpec(_ context.Context, azure types.Object, diags diag.Diagnostics) (*controlplanev1beta2.AzurePrivateLinkSpec, diag.Diagnostics) {

redpanda/resources/cluster/model_to_proto_test.go

+233
Original file line numberDiff line numberDiff line change
@@ -8,9 +8,242 @@ import (
88
"github.com/hashicorp/terraform-plugin-framework/attr"
99
"github.com/hashicorp/terraform-plugin-framework/diag"
1010
"github.com/hashicorp/terraform-plugin-framework/types"
11+
"github.com/redpanda-data/terraform-provider-redpanda/redpanda/models"
12+
"github.com/redpanda-data/terraform-provider-redpanda/redpanda/utils"
1113
"github.com/stretchr/testify/assert"
1214
)
1315

16+
func TestGenerateClusterCMRUpdate(t *testing.T) {
17+
// Create a basic GCP cluster proto object
18+
gcpCluster := &controlplanev1beta2.Cluster{
19+
CloudProvider: mustCloudProvider("gcp"),
20+
Type: controlplanev1beta2.Cluster_TYPE_BYOC,
21+
CustomerManagedResources: &controlplanev1beta2.CustomerManagedResources{
22+
CloudProvider: &controlplanev1beta2.CustomerManagedResources_Gcp{
23+
Gcp: &controlplanev1beta2.CustomerManagedResources_GCP{
24+
Subnet: &controlplanev1beta2.CustomerManagedResources_GCP_Subnet{
25+
Name: "subnet-name",
26+
SecondaryIpv4RangePods: &controlplanev1beta2.CustomerManagedResources_GCP_Subnet_SecondaryIPv4Range{
27+
Name: "pods-range",
28+
},
29+
SecondaryIpv4RangeServices: &controlplanev1beta2.CustomerManagedResources_GCP_Subnet_SecondaryIPv4Range{
30+
Name: "services-range",
31+
},
32+
K8SMasterIpv4Range: "10.0.0.0/24",
33+
},
34+
AgentServiceAccount: &controlplanev1beta2.CustomerManagedResources_GCP_ServiceAccount{
35+
36+
},
37+
ConsoleServiceAccount: &controlplanev1beta2.CustomerManagedResources_GCP_ServiceAccount{
38+
39+
},
40+
ConnectorServiceAccount: &controlplanev1beta2.CustomerManagedResources_GCP_ServiceAccount{
41+
42+
},
43+
RedpandaClusterServiceAccount: &controlplanev1beta2.CustomerManagedResources_GCP_ServiceAccount{
44+
45+
},
46+
GkeServiceAccount: &controlplanev1beta2.CustomerManagedResources_GCP_ServiceAccount{
47+
48+
},
49+
TieredStorageBucket: &controlplanev1beta2.CustomerManagedGoogleCloudStorageBucket{
50+
Name: "test-bucket",
51+
},
52+
},
53+
},
54+
},
55+
}
56+
57+
// Create a GCP cluster with PSC NAT subnet name
58+
gcpClusterWithSubnet := &controlplanev1beta2.Cluster{
59+
CloudProvider: mustCloudProvider("gcp"),
60+
Type: controlplanev1beta2.Cluster_TYPE_BYOC,
61+
CustomerManagedResources: &controlplanev1beta2.CustomerManagedResources{
62+
CloudProvider: &controlplanev1beta2.CustomerManagedResources_Gcp{
63+
Gcp: &controlplanev1beta2.CustomerManagedResources_GCP{
64+
Subnet: &controlplanev1beta2.CustomerManagedResources_GCP_Subnet{
65+
Name: "subnet-name",
66+
SecondaryIpv4RangePods: &controlplanev1beta2.CustomerManagedResources_GCP_Subnet_SecondaryIPv4Range{
67+
Name: "pods-range",
68+
},
69+
SecondaryIpv4RangeServices: &controlplanev1beta2.CustomerManagedResources_GCP_Subnet_SecondaryIPv4Range{
70+
Name: "services-range",
71+
},
72+
K8SMasterIpv4Range: "10.0.0.0/24",
73+
},
74+
AgentServiceAccount: &controlplanev1beta2.CustomerManagedResources_GCP_ServiceAccount{
75+
76+
},
77+
ConsoleServiceAccount: &controlplanev1beta2.CustomerManagedResources_GCP_ServiceAccount{
78+
79+
},
80+
ConnectorServiceAccount: &controlplanev1beta2.CustomerManagedResources_GCP_ServiceAccount{
81+
82+
},
83+
RedpandaClusterServiceAccount: &controlplanev1beta2.CustomerManagedResources_GCP_ServiceAccount{
84+
85+
},
86+
GkeServiceAccount: &controlplanev1beta2.CustomerManagedResources_GCP_ServiceAccount{
87+
88+
},
89+
TieredStorageBucket: &controlplanev1beta2.CustomerManagedGoogleCloudStorageBucket{
90+
Name: "test-bucket",
91+
},
92+
PscNatSubnetName: "test-subnet",
93+
},
94+
},
95+
},
96+
}
97+
98+
// Create an AWS cluster
99+
awsCluster := &controlplanev1beta2.Cluster{
100+
CloudProvider: mustCloudProvider("aws"),
101+
Type: controlplanev1beta2.Cluster_TYPE_BYOC,
102+
CustomerManagedResources: &controlplanev1beta2.CustomerManagedResources{
103+
CloudProvider: &controlplanev1beta2.CustomerManagedResources_Aws{
104+
Aws: &controlplanev1beta2.CustomerManagedResources_AWS{
105+
AgentInstanceProfile: &controlplanev1beta2.CustomerManagedResources_AWS_InstanceProfile{
106+
Arn: "arn:aws:iam::123456789012:instance-profile/agent",
107+
},
108+
ConnectorsNodeGroupInstanceProfile: &controlplanev1beta2.CustomerManagedResources_AWS_InstanceProfile{
109+
Arn: "arn:aws:iam::123456789012:instance-profile/connectors",
110+
},
111+
UtilityNodeGroupInstanceProfile: &controlplanev1beta2.CustomerManagedResources_AWS_InstanceProfile{
112+
Arn: "arn:aws:iam::123456789012:instance-profile/utility",
113+
},
114+
RedpandaNodeGroupInstanceProfile: &controlplanev1beta2.CustomerManagedResources_AWS_InstanceProfile{
115+
Arn: "arn:aws:iam::123456789012:instance-profile/redpanda",
116+
},
117+
K8SClusterRole: &controlplanev1beta2.CustomerManagedResources_AWS_Role{
118+
Arn: "arn:aws:iam::123456789012:role/k8s",
119+
},
120+
RedpandaAgentSecurityGroup: &controlplanev1beta2.CustomerManagedResources_AWS_SecurityGroup{
121+
Arn: "arn:aws:ec2:region:123456789012:security-group/agent",
122+
},
123+
ConnectorsSecurityGroup: &controlplanev1beta2.CustomerManagedResources_AWS_SecurityGroup{
124+
Arn: "arn:aws:ec2:region:123456789012:security-group/connectors",
125+
},
126+
RedpandaNodeGroupSecurityGroup: &controlplanev1beta2.CustomerManagedResources_AWS_SecurityGroup{
127+
Arn: "arn:aws:ec2:region:123456789012:security-group/redpanda",
128+
},
129+
UtilitySecurityGroup: &controlplanev1beta2.CustomerManagedResources_AWS_SecurityGroup{
130+
Arn: "arn:aws:ec2:region:123456789012:security-group/utility",
131+
},
132+
ClusterSecurityGroup: &controlplanev1beta2.CustomerManagedResources_AWS_SecurityGroup{
133+
Arn: "arn:aws:ec2:region:123456789012:security-group/cluster",
134+
},
135+
NodeSecurityGroup: &controlplanev1beta2.CustomerManagedResources_AWS_SecurityGroup{
136+
Arn: "arn:aws:ec2:region:123456789012:security-group/node",
137+
},
138+
CloudStorageBucket: &controlplanev1beta2.CustomerManagedAWSCloudStorageBucket{
139+
Arn: "arn:aws:s3:::my-bucket",
140+
},
141+
PermissionsBoundaryPolicy: &controlplanev1beta2.CustomerManagedResources_AWS_Policy{
142+
Arn: "arn:aws:iam::123456789012:policy/boundary",
143+
},
144+
},
145+
},
146+
},
147+
}
148+
149+
// Generate model objects using generateModelCMR function
150+
diagContext := diag.Diagnostics{}
151+
gcpCMR, diagGcp := generateModelCMR(gcpCluster, diagContext)
152+
gcpCMRWithSubnet, diagGcpWithSubnet := generateModelCMR(gcpClusterWithSubnet, diagContext)
153+
awsCMR, diagAws := generateModelCMR(awsCluster, diagContext)
154+
155+
// Make sure all the generations worked correctly
156+
if diagGcp.HasError() || diagGcpWithSubnet.HasError() || diagAws.HasError() {
157+
t.Fatalf("Error generating CMR models for test setup: %v, %v, %v", diagGcp, diagGcpWithSubnet, diagAws)
158+
}
159+
160+
tests := []struct {
161+
name string
162+
cluster models.Cluster
163+
expectNil bool
164+
expectedUpdate *controlplanev1beta2.CustomerManagedResourcesUpdate
165+
expectError bool
166+
}{
167+
{
168+
name: "null customer managed resources",
169+
cluster: models.Cluster{
170+
CustomerManagedResources: types.ObjectNull(cmrType),
171+
CloudProvider: types.StringValue(utils.CloudProviderStringGcp),
172+
},
173+
expectNil: true,
174+
},
175+
{
176+
name: "non-GCP cloud provider",
177+
cluster: models.Cluster{
178+
CustomerManagedResources: awsCMR,
179+
CloudProvider: types.StringValue(utils.CloudProviderStringAws),
180+
},
181+
expectNil: true,
182+
},
183+
{
184+
name: "GCP with no PSC NAT subnet name",
185+
cluster: models.Cluster{
186+
CustomerManagedResources: gcpCMR,
187+
CloudProvider: types.StringValue(utils.CloudProviderStringGcp),
188+
},
189+
expectedUpdate: &controlplanev1beta2.CustomerManagedResourcesUpdate{
190+
CloudProvider: &controlplanev1beta2.CustomerManagedResourcesUpdate_Gcp{
191+
Gcp: &controlplanev1beta2.CustomerManagedResourcesUpdate_GCP{},
192+
},
193+
},
194+
},
195+
{
196+
name: "GCP with PSC NAT subnet name",
197+
cluster: models.Cluster{
198+
CustomerManagedResources: gcpCMRWithSubnet,
199+
CloudProvider: types.StringValue(utils.CloudProviderStringGcp),
200+
},
201+
expectedUpdate: &controlplanev1beta2.CustomerManagedResourcesUpdate{
202+
CloudProvider: &controlplanev1beta2.CustomerManagedResourcesUpdate_Gcp{
203+
Gcp: &controlplanev1beta2.CustomerManagedResourcesUpdate_GCP{
204+
PscNatSubnetName: "test-subnet",
205+
},
206+
},
207+
},
208+
},
209+
}
210+
211+
for _, tt := range tests {
212+
t.Run(tt.name, func(t *testing.T) {
213+
ctxTest := context.Background()
214+
diags := diag.Diagnostics{}
215+
216+
result, resultDiags := generateClusterCMRUpdate(ctxTest, tt.cluster, diags)
217+
218+
// Check for expected errors
219+
if tt.expectError {
220+
assert.True(t, resultDiags.HasError())
221+
} else {
222+
assert.False(t, resultDiags.HasError())
223+
}
224+
225+
// Check if nil result expected
226+
if tt.expectNil {
227+
assert.Nil(t, result)
228+
return
229+
}
230+
231+
// Verify the result
232+
assert.NotNil(t, result)
233+
234+
// Check the GCP provider type
235+
assert.NotNil(t, result.GetGcp())
236+
237+
// Check PSC NAT subnet name if provided
238+
if tt.expectedUpdate != nil && tt.expectedUpdate.GetGcp() != nil {
239+
expectedSubnet := tt.expectedUpdate.GetGcp().PscNatSubnetName
240+
actualSubnet := result.GetGcp().PscNatSubnetName
241+
assert.Equal(t, expectedSubnet, actualSubnet)
242+
}
243+
})
244+
}
245+
}
246+
14247
func TestGetGcpPrivateServiceConnect(t *testing.T) {
15248
tests := []struct {
16249
name string

0 commit comments

Comments
 (0)