Skip to content

Commit 34ccefa

Browse files
committed
feat(kafka_instance): support multiple security groups
1 parent 88f4bb9 commit 34ccefa

File tree

8 files changed

+121
-2
lines changed

8 files changed

+121
-2
lines changed

client/model_kafka_instance.go

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ type SpecificationParam struct {
1919
NodeConfig *NodeConfigParam `json:"nodeConfig,omitempty"`
2020
Networks []InstanceNetworkParam `json:"networks,omitempty"`
2121
KubernetesNodeGroups []KubernetesNodeGroupParam `json:"kubernetesNodeGroups,omitempty"`
22-
SecurityGroup *string `json:"securityGroup,omitempty"`
22+
SecurityGroups []string `json:"securityGroups,omitempty"`
2323
Template *string `json:"template,omitempty"`
2424
FileSystem *FileSystemParam `json:"fileSystemForFsWal,omitempty"`
2525
DeployType *string `json:"deployType,omitempty"`
@@ -223,6 +223,7 @@ type SpecificationVO struct {
223223
BucketProfiles []BucketProfileSummaryVO `json:"bucketProfiles,omitempty"`
224224
DataBuckets []BucketProfileVO `json:"dataBuckets,omitempty"`
225225
SecurityGroupId *string `json:"securityGroupId,omitempty"`
226+
SecurityGroups []string `json:"securityGroups,omitempty"`
226227
FileSystem *FileSystemVO `json:"fileSystemForFsWal,omitempty"`
227228
Provider *string `json:"provider,omitempty"`
228229
Region *string `json:"region,omitempty"`
@@ -365,7 +366,7 @@ type InstanceConfigParam struct {
365366
type SpecificationUpdateParam struct {
366367
ReservedAku *int32 `json:"reservedAku,omitempty"`
367368
NodeConfig *NodeConfigParam `json:"nodeConfig,omitempty"`
368-
SecurityGroup *string `json:"securityGroup,omitempty"`
369+
SecurityGroups []string `json:"securityGroups,omitempty"`
369370
Template *string `json:"template,omitempty"`
370371
Networks []InstanceNetworkParam `json:"networks,omitempty"`
371372
KubernetesNodeGroups []KubernetesNodeGroupParam `json:"kubernetesNodeGroups,omitempty"`

docs/data-sources/kafka_instance.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -71,6 +71,7 @@ Read-Only:
7171
- `kubernetes_service_account` (String)
7272
- `networks` (Attributes List) To configure the network settings for an instance, you need to specify the availability zone(s) and subnet information. Currently, you can set either one availability zone or three availability zones. (see [below for nested schema](#nestedatt--compute_specs--networks))
7373
- `reserved_aku` (Number) AutoMQ defines AKU (AutoMQ Kafka Unit) to measure the scale of the cluster. Each AKU provides 20 MiB/s of read/write throughput. For more details on AKU, please refer to the [documentation](https://docs.automq.com/automq-cloud/subscriptions-and-billings/byoc-env-billings/billing-instructions-for-byoc#indicator-constraints). The currently supported AKU specifications are 6, 8, 10, 12, 14, 16, 18, 20, 22, and 24. If an invalid AKU value is set, the instance cannot be created.
74+
- `security_groups` (List of String) Security groups for the instance
7475

7576
<a id="nestedatt--compute_specs--data_buckets"></a>
7677
### Nested Schema for `compute_specs.data_buckets`

docs/resources/kafka_instance.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,8 @@ resource "automq_kafka_instance" "example" {
4040
bucket_name = "automq-data-bucket"
4141
}
4242
]
43+
44+
# security_groups = ["sg-example123"] # Optional. Omit this field entirely to let backend auto-generate. If specified, must contain at least one security group.
4345
}
4446
4547
features = {
@@ -155,6 +157,7 @@ Optional:
155157
- `kubernetes_namespace` (String)
156158
- `kubernetes_node_groups` (Attributes List) Node groups (or node pools) are units for unified configuration management of physical nodes in Kubernetes. Different Kubernetes providers may use different terms for node groups. Select target node groups that must be created in advance and configured for either single-AZ or three-AZ deployment. The instance node type must meet the requirements specified in the documentation. If you select a single-AZ node group, the AutoMQ instance will be deployed in a single availability zone; if you select a three-AZ node group, the instance will be deployed across three availability zones. (see [below for nested schema](#nestedatt--compute_specs--kubernetes_node_groups))
157159
- `kubernetes_service_account` (String)
160+
- `security_groups` (List of String) Security groups for the instance. Omit this field entirely to let backend auto-generate. If specified, must contain at least one security group.
158161

159162
<a id="nestedatt--compute_specs--networks"></a>
160163
### Nested Schema for `compute_specs.networks`

examples/resources/automq_kafka_instance/resource.tf

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ resource "automq_kafka_instance" "example" {
2020
bucket_name = "automq-data-bucket"
2121
}
2222
]
23+
24+
# security_groups = ["sg-example123"] # Optional. Omit this field entirely to let backend auto-generate. If specified, must contain at least one security group.
2325
}
2426

2527
features = {

internal/models/kafka_instance.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,7 @@ type ComputeSpecsModel struct {
7777
KubernetesServiceAcct types.String `tfsdk:"kubernetes_service_account"`
7878
InstanceRole types.String `tfsdk:"instance_role"`
7979
DataBuckets types.List `tfsdk:"data_buckets"`
80+
SecurityGroups types.List `tfsdk:"security_groups"`
8081
FileSystemParam *FileSystemParamModel `tfsdk:"file_system_param"`
8182
}
8283

@@ -316,6 +317,16 @@ func ExpandKafkaInstanceResource(ctx context.Context, instance KafkaInstanceReso
316317
request.Spec.DataBuckets = dataBuckets
317318
}
318319

320+
// Security Groups for compute specs
321+
if !instance.ComputeSpecs.SecurityGroups.IsNull() &&
322+
!instance.ComputeSpecs.SecurityGroups.IsUnknown() {
323+
var securityGroups []string
324+
diags := instance.ComputeSpecs.SecurityGroups.ElementsAs(ctx, &securityGroups, false)
325+
if !diags.HasError() && len(securityGroups) > 0 {
326+
request.Spec.SecurityGroups = securityGroups
327+
}
328+
}
329+
319330
// File System Parameters for FSWAL
320331
if instance.ComputeSpecs.FileSystemParam != nil {
321332
fileSystemParam := &client.FileSystemParam{
@@ -654,6 +665,7 @@ func FlattenKafkaInstanceModel(ctx context.Context, instance *client.InstanceVO,
654665
Networks: []NetworkModel{},
655666
KubernetesNodeGroups: []NodeGroupModel{},
656667
DataBuckets: types.ListNull(DataBucketObjectType),
668+
SecurityGroups: types.ListNull(types.StringType),
657669
DeployType: types.StringNull(),
658670
DnsZone: types.StringNull(),
659671
KubernetesClusterID: types.StringNull(),
@@ -742,6 +754,18 @@ func FlattenKafkaInstanceModel(ctx context.Context, instance *client.InstanceVO,
742754
resource.ComputeSpecs.DataBuckets = types.ListNull(DataBucketObjectType)
743755
}
744756

757+
// Security Groups for compute specs
758+
if len(instance.Spec.SecurityGroups) > 0 {
759+
securityGroupsList, sgDiags := types.ListValueFrom(ctx, types.StringType, instance.Spec.SecurityGroups)
760+
if !sgDiags.HasError() {
761+
resource.ComputeSpecs.SecurityGroups = securityGroupsList
762+
}
763+
} else if previousSpecs != nil && !previousSpecs.SecurityGroups.IsNull() {
764+
resource.ComputeSpecs.SecurityGroups = previousSpecs.SecurityGroups
765+
} else {
766+
resource.ComputeSpecs.SecurityGroups = types.ListNull(types.StringType)
767+
}
768+
745769
// File System Parameters for FSWAL
746770
var previousFileSystemParam *FileSystemParamModel
747771
if previousSpecs != nil {

internal/provider/datasource_instance.go

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,11 @@ func (r *KafkaInstanceDataSource) Schema(_ context.Context, _ datasource.SchemaR
112112
},
113113
},
114114
},
115+
"security_groups": schema.ListAttribute{
116+
ElementType: types.StringType,
117+
Computed: true,
118+
Description: "Security groups for the instance",
119+
},
115120
"file_system_param": schema.SingleNestedAttribute{
116121
Computed: true,
117122
Description: "File system configuration for FSWAL mode",

internal/provider/resource_instance.go

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,19 @@ func (r *KafkaInstanceResource) Schema(ctx context.Context, req resource.SchemaR
255255
stringplanmodifier.UseStateForUnknown(),
256256
},
257257
},
258+
"security_groups": schema.ListAttribute{
259+
ElementType: types.StringType,
260+
Optional: true,
261+
Computed: true,
262+
Description: "Security groups for the instance. Omit this field entirely to let backend auto-generate. If specified, must contain at least one security group.",
263+
PlanModifiers: []planmodifier.List{
264+
listplanmodifier.RequiresReplace(),
265+
listplanmodifier.UseStateForUnknown(),
266+
},
267+
Validators: []validator.List{
268+
listvalidator.SizeAtLeast(1),
269+
},
270+
},
258271
"file_system_param": schema.SingleNestedAttribute{
259272
Optional: true,
260273
Description: "File system configuration for FSWAL mode",

internal/provider/resource_instance_validation_test.go

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -757,3 +757,73 @@ func TestValidateKafkaInstanceConfiguration_FSWALWithUnknownSecurityGroups(t *te
757757
t.Fatalf("unexpected diagnostics for FSWAL with unknown security_groups: %v", diags)
758758
}
759759
}
760+
761+
// TestComputeSpecsSecurityGroupsValidator tests the compute_specs.security_groups schema validator
762+
func TestComputeSpecsSecurityGroupsValidator(t *testing.T) {
763+
s := getKafkaInstanceResourceSchema(t)
764+
computeAttrRaw, ok := s.Attributes["compute_specs"].(schema.SingleNestedAttribute)
765+
if !ok {
766+
t.Fatalf("compute_specs attribute has unexpected type %T", s.Attributes["compute_specs"])
767+
}
768+
computeAttr := computeAttrRaw
769+
770+
// Test security_groups attribute properties
771+
securityGroupsAttrRaw, ok := computeAttr.Attributes["security_groups"].(schema.ListAttribute)
772+
if !ok {
773+
t.Fatalf("security_groups attribute has unexpected type %T", computeAttr.Attributes["security_groups"])
774+
}
775+
securityGroupsAttr := securityGroupsAttrRaw
776+
if !securityGroupsAttr.Optional {
777+
t.Fatalf("security_groups should be optional")
778+
}
779+
if !securityGroupsAttr.Computed {
780+
t.Fatalf("security_groups should be computed")
781+
}
782+
if len(securityGroupsAttr.PlanModifiers) == 0 {
783+
t.Fatalf("security_groups plan modifiers missing")
784+
}
785+
if !hasListRequiresReplace(securityGroupsAttr.PlanModifiers) {
786+
t.Fatalf("expected security_groups to require replacement, modifiers: %v", securityGroupsAttr.PlanModifiers)
787+
}
788+
if len(securityGroupsAttr.Validators) == 0 {
789+
t.Fatalf("security_groups validators missing")
790+
}
791+
792+
// Test that empty list is rejected
793+
req := validator.ListRequest{
794+
ConfigValue: types.ListValueMust(types.StringType, []attr.Value{}),
795+
Path: path.Root("compute_specs").AtName("security_groups"),
796+
}
797+
resp := validator.ListResponse{}
798+
securityGroupsAttr.Validators[0].ValidateList(context.Background(), req, &resp)
799+
if !resp.Diagnostics.HasError() {
800+
t.Fatalf("expected validator error for empty security_groups list")
801+
}
802+
803+
// Test that list with one element is accepted
804+
reqValid := validator.ListRequest{
805+
ConfigValue: types.ListValueMust(types.StringType, []attr.Value{
806+
types.StringValue("sg-12345"),
807+
}),
808+
Path: path.Root("compute_specs").AtName("security_groups"),
809+
}
810+
respValid := validator.ListResponse{}
811+
securityGroupsAttr.Validators[0].ValidateList(context.Background(), reqValid, &respValid)
812+
if respValid.Diagnostics.HasError() {
813+
t.Fatalf("validator should accept non-empty security_groups list: %v", respValid.Diagnostics)
814+
}
815+
816+
// Test that list with multiple elements is accepted
817+
reqMultiple := validator.ListRequest{
818+
ConfigValue: types.ListValueMust(types.StringType, []attr.Value{
819+
types.StringValue("sg-12345"),
820+
types.StringValue("sg-67890"),
821+
}),
822+
Path: path.Root("compute_specs").AtName("security_groups"),
823+
}
824+
respMultiple := validator.ListResponse{}
825+
securityGroupsAttr.Validators[0].ValidateList(context.Background(), reqMultiple, &respMultiple)
826+
if respMultiple.Diagnostics.HasError() {
827+
t.Fatalf("validator should accept multiple security_groups: %v", respMultiple.Diagnostics)
828+
}
829+
}

0 commit comments

Comments
 (0)