Skip to content

Commit 5f4d404

Browse files
feat: support subnet_cidr argument for cloud_environment (#119)
1 parent 24e8472 commit 5f4d404

File tree

7 files changed

+130
-8
lines changed

7 files changed

+130
-8
lines changed

cloud/cloud_environment_network.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,9 @@ func flattenCloudEnvironmentNetwork(in *cloudv1alpha1.Network) []interface{} {
2626
if in.CIDR != "" {
2727
att["cidr"] = in.CIDR
2828
}
29+
if in.SubnetCIDR != "" {
30+
att["subnet_cidr"] = in.SubnetCIDR
31+
}
2932

3033
return []interface{}{att}
3134
}

cloud/data_source_cloud_environment.go

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,10 @@ package cloud
1717
import (
1818
"context"
1919
"fmt"
20-
apierrors "k8s.io/apimachinery/pkg/api/errors"
2120
"strings"
2221

22+
apierrors "k8s.io/apimachinery/pkg/api/errors"
23+
2324
"github.com/hashicorp/terraform-plugin-sdk/v2/diag"
2425
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
2526
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -77,6 +78,10 @@ func dataSourceCloudEnvironment() *schema.Resource {
7778
Type: schema.TypeString,
7879
Optional: true,
7980
},
81+
"subnet_cidr": {
82+
Type: schema.TypeString,
83+
Optional: true,
84+
},
8085
},
8186
},
8287
},

cloud/resource_cloud_environment.go

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ package cloud
1717
import (
1818
"context"
1919
"fmt"
20+
cloudv1alpha1 "github.com/streamnative/cloud-api-server/pkg/apis/cloud/v1alpha1"
21+
cloudclient "github.com/streamnative/cloud-api-server/pkg/client/clientset_generated/clientset"
2022
"strings"
2123
"time"
2224

@@ -25,9 +27,6 @@ import (
2527
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
2628
apierrors "k8s.io/apimachinery/pkg/api/errors"
2729
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
28-
29-
cloudv1alpha1 "github.com/streamnative/cloud-api-server/pkg/apis/cloud/v1alpha1"
30-
cloudclient "github.com/streamnative/cloud-api-server/pkg/client/clientset_generated/clientset"
3130
)
3231

3332
func resourceCloudEnvironment() *schema.Resource {
@@ -56,9 +55,10 @@ func resourceCloudEnvironment() *schema.Resource {
5655
diff.HasChanges("cloud_connection_name") ||
5756
diff.HasChanges("region") ||
5857
diff.HasChanges("network_id") ||
59-
diff.HasChanges("network_cidr") {
58+
diff.HasChanges("network_cidr") ||
59+
diff.HasChanges("network_subnet_cidr") {
6060
return fmt.Errorf("ERROR_UPDATE_CLOUD_ENVIRONMENT: " +
61-
"The cloud environment does not support updates on the attributes: organization, cloud_connection_name, region, network_id, network_cidr. Please recreate it")
61+
"The cloud environment does not support updates on the attributes: organization, cloud_connection_name, region, network_id, network_cidr, network_subnet_cidr. Please recreate it")
6262
}
6363
return nil
6464
},
@@ -120,6 +120,11 @@ func resourceCloudEnvironment() *schema.Resource {
120120
Optional: true,
121121
ValidateFunc: validateCidrRange,
122122
},
123+
"subnet_cidr": {
124+
Type: schema.TypeString,
125+
Optional: true,
126+
ValidateFunc: validateCidrRange,
127+
},
123128
},
124129
},
125130
},
@@ -260,12 +265,27 @@ func resourceCloudEnvironmentCreate(ctx context.Context, d *schema.ResourceData,
260265
networkCidr := networkItemMap["cidr"].(string)
261266
cloudEnvironment.Spec.Network.CIDR = networkCidr
262267
}
268+
if networkItemMap["subnet_cidr"] != nil {
269+
subnetCidr := networkItemMap["subnet_cidr"].(string)
270+
cloudEnvironment.Spec.Network.SubnetCIDR = subnetCidr
271+
}
263272
}
264273
}
265274

266275
if cloudEnvironment.Spec.Network.ID == "" && cloudEnvironment.Spec.Network.CIDR == "" {
267276
return diag.FromErr(fmt.Errorf("ERROR_CREATE_CLOUD_ENVIRONMENT: " + "One of network.id or network.cidr must be set"))
268277
}
278+
if cc.Spec.ConnectionType == cloudv1alpha1.ConnectionTypeAzure {
279+
if cloudEnvironment.Spec.Network.CIDR != "" {
280+
if cloudEnvironment.Spec.Network.SubnetCIDR == "" {
281+
cloudEnvironment.Spec.Network.SubnetCIDR = cloudEnvironment.Spec.Network.CIDR
282+
}
283+
if validate, _ := validateSubnetCIDR(cloudEnvironment.Spec.Network.SubnetCIDR, cloudEnvironment.Spec.Network.CIDR); !validate {
284+
return diag.FromErr(fmt.Errorf("ERROR_CREATE_CLOUD_ENVIRONMENT: " +
285+
"Azure cloud environment requires network.subnet_cidr to be a subnet of network.cidr"))
286+
}
287+
}
288+
}
269289

270290
expandDns := func() error {
271291
for _, l := range dns {
@@ -393,9 +413,10 @@ func resourceCloudEnvironmentUpdate(ctx context.Context, d *schema.ResourceData,
393413
d.HasChanges("cloud_connection_name") ||
394414
d.HasChanges("region") ||
395415
d.HasChanges("network_id") ||
396-
d.HasChanges("network_cidr") {
416+
d.HasChanges("network_cidr") ||
417+
d.HasChanges("network_subnet_cidr") {
397418
return diag.FromErr(fmt.Errorf("ERROR_UPDATE_CLOUD_ENVIRONMENT: " +
398-
"The cloud environment does not support updates on the attributes: organization, cloud_connection_name, region, network_id, network_cidr. Please recreate it"))
419+
"The cloud environment does not support updates on the attributes: organization, cloud_connection_name, region, network_id, network_cidr, network_subnet_cidr. Please recreate it"))
399420
}
400421

401422
cloudEnvironment, err := clientSet.CloudV1alpha1().CloudEnvironments(namespace).Get(ctx, name, metav1.GetOptions{})

cloud/validate_helpers.go

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ package cloud
1616

1717
import (
1818
"fmt"
19+
"net"
1920
"strconv"
2021
"strings"
2122

@@ -127,6 +128,31 @@ func validateCidrRange(val interface{}, key string) (warns []string, errs []erro
127128
return
128129
}
129130

131+
func validateSubnetCIDR(subnetStr, parentStr string) (bool, error) {
132+
subnetIP, subnetNet, err := net.ParseCIDR(subnetStr)
133+
if err != nil {
134+
return false, fmt.Errorf("invalid subnet CIDR: %v", err)
135+
}
136+
_, parentNet, err := net.ParseCIDR(parentStr)
137+
if err != nil {
138+
return false, fmt.Errorf("invalid parent CIDR: %v", err)
139+
}
140+
141+
// Check if the subnet IP is within the parent network
142+
if !parentNet.Contains(subnetIP) {
143+
return false, nil
144+
}
145+
146+
// Ensure the subnet is not broader than the parent (i.e., subnet mask >= parent mask)
147+
subnetMaskSize, _ := subnetNet.Mask.Size()
148+
parentMaskSize, _ := parentNet.Mask.Size()
149+
if subnetMaskSize < parentMaskSize {
150+
return false, nil
151+
}
152+
153+
return true, nil
154+
}
155+
130156
func validateAnnotations(value interface{}, key string) (ws []string, es []error) {
131157
m := value.(map[string]interface{})
132158
for k := range m {

cloud/validate_helpers_test.go

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright 2024 StreamNative, Inc.
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package cloud
16+
17+
import (
18+
"testing"
19+
)
20+
21+
func Test_validateSubnetCIDR(t *testing.T) {
22+
tests := []struct {
23+
subnet string
24+
parent string
25+
expect bool
26+
expectErr bool
27+
}{
28+
// Valid subnets
29+
{"10.0.1.0/24", "10.0.0.0/16", true, false},
30+
{"192.168.1.0/25", "192.168.1.0/24", true, false},
31+
{"172.16.5.128/26", "172.16.0.0/16", true, false},
32+
33+
// Subnet not contained in parent
34+
{"192.168.2.0/24", "192.168.1.0/24", false, false},
35+
{"10.1.0.0/16", "10.0.0.0/16", false, false},
36+
37+
// Subnet broader than parent (mask too small)
38+
{"10.0.0.0/8", "10.0.0.0/16", false, false},
39+
{"172.16.0.0/12", "172.16.0.0/16", false, false},
40+
41+
// Equal subnet and parent
42+
{"192.168.1.0/24", "192.168.1.0/24", true, false},
43+
44+
// Invalid input
45+
{"invalid", "192.168.1.0/24", false, true},
46+
{"192.168.1.0/24", "invalid", false, true},
47+
}
48+
49+
for _, tt := range tests {
50+
ok, err := validateSubnetCIDR(tt.subnet, tt.parent)
51+
if tt.expectErr {
52+
if err == nil {
53+
t.Errorf("Expected error for input (%s, %s), got none", tt.subnet, tt.parent)
54+
}
55+
continue
56+
}
57+
if err != nil {
58+
t.Errorf("Unexpected error for input (%s, %s): %v", tt.subnet, tt.parent, err)
59+
continue
60+
}
61+
if ok != tt.expect {
62+
t.Errorf("For (%s, %s), expected %v, got %v", tt.subnet, tt.parent, tt.expect, ok)
63+
}
64+
}
65+
}

docs/data-sources/cloud_environment.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,3 +53,4 @@ Read-Only:
5353

5454
- `cidr` (String)
5555
- `id` (String)
56+
- `subnet_cidr` (String)

docs/resources/cloud_environment.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,7 @@ Optional:
4343

4444
- `cidr` (String)
4545
- `id` (String)
46+
- `subnet_cidr` (String)
4647

4748

4849
<a id="nestedblock--default_gateway"></a>

0 commit comments

Comments
 (0)