Skip to content

Commit e4ecc98

Browse files
Pearl1594vishesh92
andauthored
Add support for the new parameters added to CKS cluster deployement (#230)
Co-authored-by: vishesh92 <[email protected]>
1 parent c4602b5 commit e4ecc98

12 files changed

+952
-64
lines changed

cloudstack/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ func Provider() *schema.Provider {
109109
"cloudstack_autoscale_policy": resourceCloudStackAutoScalePolicy(),
110110
"cloudstack_autoscale_vm_group": resourceCloudStackAutoScaleVMGroup(),
111111
"cloudstack_autoscale_vm_profile": resourceCloudStackAutoScaleVMProfile(),
112+
"cloudstack_cni_configuration": resourceCloudStackCniConfiguration(),
112113
"cloudstack_condition": resourceCloudStackCondition(),
113114
"cloudstack_configuration": resourceCloudStackConfiguration(),
114115
"cloudstack_counter": resourceCloudStackCounter(),
Lines changed: 222 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,222 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package cloudstack
21+
22+
import (
23+
"fmt"
24+
"log"
25+
"strings"
26+
27+
"github.com/apache/cloudstack-go/v2/cloudstack"
28+
"github.com/hashicorp/terraform-plugin-sdk/v2/helper/schema"
29+
)
30+
31+
func resourceCloudStackCniConfiguration() *schema.Resource {
32+
return &schema.Resource{
33+
Create: resourceCloudStackCniConfigurationCreate,
34+
Read: resourceCloudStackCniConfigurationRead,
35+
Delete: resourceCloudStackCniConfigurationDelete,
36+
Importer: &schema.ResourceImporter{
37+
State: importStatePassthrough,
38+
},
39+
40+
Schema: map[string]*schema.Schema{
41+
"name": {
42+
Type: schema.TypeString,
43+
Required: true,
44+
ForceNew: true,
45+
Description: "Name of the CNI configuration",
46+
},
47+
48+
"cni_config": {
49+
Type: schema.TypeString,
50+
Required: true,
51+
ForceNew: true,
52+
Description: "CNI Configuration content to be registered",
53+
},
54+
55+
"account": {
56+
Type: schema.TypeString,
57+
Optional: true,
58+
ForceNew: true,
59+
Description: "An optional account for the CNI configuration. Must be used with domain_id.",
60+
},
61+
62+
"domain_id": {
63+
Type: schema.TypeString,
64+
Optional: true,
65+
ForceNew: true,
66+
Description: "An optional domain ID for the CNI configuration. If the account parameter is used, domain_id must also be used.",
67+
},
68+
69+
"project_id": {
70+
Type: schema.TypeString,
71+
Optional: true,
72+
ForceNew: true,
73+
Description: "An optional project for the CNI configuration",
74+
},
75+
76+
"params": {
77+
Type: schema.TypeSet,
78+
Optional: true,
79+
ForceNew: true,
80+
Description: "List of variables declared in CNI configuration content",
81+
Elem: &schema.Schema{
82+
Type: schema.TypeString,
83+
},
84+
},
85+
},
86+
}
87+
}
88+
89+
func resourceCloudStackCniConfigurationCreate(d *schema.ResourceData, meta interface{}) error {
90+
cs := meta.(*cloudstack.CloudStackClient)
91+
92+
name := d.Get("name").(string)
93+
log.Printf("[DEBUG] Creating CNI configuration: %s", name)
94+
95+
p := cs.Configuration.NewRegisterCniConfigurationParams(name)
96+
97+
if v, ok := d.GetOk("cni_config"); ok {
98+
cniConfig := v.(string)
99+
log.Printf("[DEBUG] CNI config data length: %d bytes", len(cniConfig))
100+
p.SetCniconfig(cniConfig)
101+
} else {
102+
return fmt.Errorf("CNI configuration content is required but not provided")
103+
}
104+
105+
if account := d.Get("account").(string); account != "" {
106+
log.Printf("[DEBUG] Setting account: %s", account)
107+
p.SetAccount(account)
108+
}
109+
110+
if domainID := d.Get("domain_id").(string); domainID != "" {
111+
log.Printf("[DEBUG] Setting domain ID: %s", domainID)
112+
p.SetDomainid(domainID)
113+
}
114+
115+
if projectID := d.Get("project_id").(string); projectID != "" {
116+
log.Printf("[DEBUG] Setting project ID: %s", projectID)
117+
p.SetProjectid(projectID)
118+
}
119+
120+
if params, ok := d.GetOk("params"); ok {
121+
paramsList := []string{}
122+
for _, param := range params.(*schema.Set).List() {
123+
paramsList = append(paramsList, param.(string))
124+
}
125+
if len(paramsList) > 0 {
126+
paramsStr := strings.Join(paramsList, ",")
127+
log.Printf("[DEBUG] Setting params: %s", paramsStr)
128+
p.SetParams(paramsStr)
129+
}
130+
}
131+
132+
resp, err := cs.Configuration.RegisterCniConfiguration(p)
133+
if err != nil {
134+
return fmt.Errorf("Error creating CNI configuration %s: %s", name, err)
135+
}
136+
137+
log.Printf("[DEBUG] CNI configuration creation response: %+v", resp)
138+
139+
// List configurations to find the created one by name since direct ID access is not available
140+
listParams := cs.Configuration.NewListCniConfigurationParams()
141+
listParams.SetName(name)
142+
143+
// Add context parameters if available
144+
if account := d.Get("account").(string); account != "" {
145+
listParams.SetAccount(account)
146+
}
147+
if domainID := d.Get("domain_id").(string); domainID != "" {
148+
listParams.SetDomainid(domainID)
149+
}
150+
if projectID := d.Get("project_id").(string); projectID != "" {
151+
listParams.SetProjectid(projectID)
152+
}
153+
154+
listResp, err := cs.Configuration.ListCniConfiguration(listParams)
155+
if err != nil {
156+
return fmt.Errorf("Error listing CNI configurations after creation: %s", err)
157+
}
158+
159+
if listResp.Count == 0 {
160+
return fmt.Errorf("CNI configuration %s was created but could not be found", name)
161+
}
162+
163+
// Use the first (and should be only) result
164+
config := listResp.CniConfiguration[0]
165+
d.SetId(config.Id)
166+
log.Printf("[DEBUG] CNI configuration %s successfully created with ID: %s", name, d.Id())
167+
168+
return resourceCloudStackCniConfigurationRead(d, meta)
169+
}
170+
171+
func resourceCloudStackCniConfigurationRead(d *schema.ResourceData, meta interface{}) error {
172+
cs := meta.(*cloudstack.CloudStackClient)
173+
174+
log.Printf("[DEBUG] Reading CNI configuration: %s", d.Id())
175+
176+
p := cs.Configuration.NewListCniConfigurationParams()
177+
p.SetId(d.Id())
178+
179+
config, err := cs.Configuration.ListCniConfiguration(p)
180+
if err != nil {
181+
return fmt.Errorf("Error listing CNI configuration: %s", err)
182+
}
183+
if config.Count == 0 {
184+
log.Printf("[DEBUG] CNI configuration %s no longer exists", d.Id())
185+
d.SetId("")
186+
return nil
187+
}
188+
189+
d.Set("name", config.CniConfiguration[0].Name)
190+
d.Set("cni_config", config.CniConfiguration[0].Userdata)
191+
d.Set("account", config.CniConfiguration[0].Account)
192+
d.Set("domain_id", config.CniConfiguration[0].Domainid)
193+
d.Set("project_id", config.CniConfiguration[0].Projectid)
194+
195+
if config.CniConfiguration[0].Params != "" {
196+
paramsList := strings.Split(config.CniConfiguration[0].Params, ",")
197+
d.Set("params", paramsList)
198+
}
199+
200+
return nil
201+
}
202+
203+
func resourceCloudStackCniConfigurationDelete(d *schema.ResourceData, meta interface{}) error {
204+
cs := meta.(*cloudstack.CloudStackClient)
205+
206+
log.Printf("[DEBUG] Deleting CNI configuration: %s", d.Id())
207+
208+
p := cs.Configuration.NewDeleteCniConfigurationParams(d.Id())
209+
210+
_, err := cs.Configuration.DeleteCniConfiguration(p)
211+
if err != nil {
212+
if strings.Contains(err.Error(), "does not exist") ||
213+
strings.Contains(err.Error(), "not found") {
214+
log.Printf("[DEBUG] CNI configuration %s already deleted", d.Id())
215+
return nil
216+
}
217+
return fmt.Errorf("Error deleting CNI configuration %s: %s", d.Id(), err)
218+
}
219+
220+
log.Printf("[DEBUG] CNI configuration %s deleted", d.Id())
221+
return nil
222+
}
Lines changed: 153 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,153 @@
1+
//
2+
// Licensed to the Apache Software Foundation (ASF) under one
3+
// or more contributor license agreements. See the NOTICE file
4+
// distributed with this work for additional information
5+
// regarding copyright ownership. The ASF licenses this file
6+
// to you under the Apache License, Version 2.0 (the
7+
// "License"); you may not use this file except in compliance
8+
// with the License. You may obtain a copy of the License at
9+
//
10+
// http://www.apache.org/licenses/LICENSE-2.0
11+
//
12+
// Unless required by applicable law or agreed to in writing,
13+
// software distributed under the License is distributed on an
14+
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15+
// KIND, either express or implied. See the License for the
16+
// specific language governing permissions and limitations
17+
// under the License.
18+
//
19+
20+
package cloudstack
21+
22+
import (
23+
"fmt"
24+
"testing"
25+
26+
"github.com/apache/cloudstack-go/v2/cloudstack"
27+
"github.com/hashicorp/terraform-plugin-testing/helper/resource"
28+
"github.com/hashicorp/terraform-plugin-testing/terraform"
29+
)
30+
31+
func TestAccCloudStackCniConfiguration_basic(t *testing.T) {
32+
var cniConfig cloudstack.UserData
33+
34+
resource.Test(t, resource.TestCase{
35+
PreCheck: func() { testAccPreCheck(t); testAccPreCheckCniSupport(t) },
36+
Providers: testAccProviders,
37+
CheckDestroy: testAccCheckCloudStackCniConfigurationDestroy,
38+
Steps: []resource.TestStep{
39+
{
40+
Config: testAccCloudStackCniConfiguration_basic,
41+
Check: resource.ComposeTestCheckFunc(
42+
testAccCheckCloudStackCniConfigurationExists("cloudstack_cni_configuration.foo", &cniConfig),
43+
resource.TestCheckResourceAttr("cloudstack_cni_configuration.foo", "name", "test-cni-config"),
44+
resource.TestCheckResourceAttr("cloudstack_cni_configuration.foo", "params.#", "2"),
45+
),
46+
},
47+
},
48+
})
49+
}
50+
51+
func testAccCheckCloudStackCniConfigurationExists(n string, cniConfig *cloudstack.UserData) resource.TestCheckFunc {
52+
return func(s *terraform.State) error {
53+
rs, ok := s.RootModule().Resources[n]
54+
if !ok {
55+
return fmt.Errorf("Not found: %s", n)
56+
}
57+
58+
if rs.Primary.ID == "" {
59+
return fmt.Errorf("No CNI configuration ID is set")
60+
}
61+
62+
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
63+
p := cs.Configuration.NewListCniConfigurationParams()
64+
p.SetId(rs.Primary.ID)
65+
66+
resp, err := cs.Configuration.ListCniConfiguration(p)
67+
if err != nil {
68+
return err
69+
}
70+
71+
if resp.Count != 1 {
72+
return fmt.Errorf("CNI configuration not found")
73+
}
74+
75+
config := resp.CniConfiguration[0]
76+
if config.Id != rs.Primary.ID {
77+
return fmt.Errorf("CNI configuration not found")
78+
}
79+
80+
*cniConfig = *config
81+
return nil
82+
}
83+
}
84+
85+
func testAccCheckCloudStackCniConfigurationDestroy(s *terraform.State) error {
86+
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
87+
88+
for _, rs := range s.RootModule().Resources {
89+
if rs.Type != "cloudstack_cni_configuration" {
90+
continue
91+
}
92+
93+
if rs.Primary.ID == "" {
94+
return fmt.Errorf("No CNI configuration ID is set")
95+
}
96+
97+
p := cs.Configuration.NewListCniConfigurationParams()
98+
p.SetId(rs.Primary.ID)
99+
100+
resp, err := cs.Configuration.ListCniConfiguration(p)
101+
if err == nil && resp.Count > 0 {
102+
return fmt.Errorf("CNI configuration %s still exists", rs.Primary.ID)
103+
}
104+
}
105+
106+
return nil
107+
}
108+
109+
const testAccCloudStackCniConfiguration_basic = `
110+
resource "cloudstack_cni_configuration" "foo" {
111+
name = "test-cni-config"
112+
cni_config = base64encode(jsonencode({
113+
"name": "test-network",
114+
"cniVersion": "0.4.0",
115+
"plugins": [
116+
{
117+
"type": "calico",
118+
"log_level": "info",
119+
"datastore_type": "kubernetes",
120+
"nodename": "KUBERNETES_NODE_NAME",
121+
"mtu": "CNI_MTU",
122+
"ipam": {
123+
"type": "calico-ipam"
124+
},
125+
"policy": {
126+
"type": "k8s"
127+
},
128+
"kubernetes": {
129+
"kubeconfig": "KUBECONFIG_FILEPATH"
130+
}
131+
},
132+
{
133+
"type": "portmap",
134+
"snat": true,
135+
"capabilities": {"portMappings": true}
136+
}
137+
]
138+
}))
139+
140+
params = ["KUBERNETES_NODE_NAME", "CNI_MTU"]
141+
}
142+
`
143+
144+
func testAccPreCheckCniSupport(t *testing.T) {
145+
cs := testAccProvider.Meta().(*cloudstack.CloudStackClient)
146+
147+
// Try to list CNI configurations to check if the feature is available
148+
p := cs.Configuration.NewListCniConfigurationParams()
149+
_, err := cs.Configuration.ListCniConfiguration(p)
150+
if err != nil {
151+
t.Skipf("CNI configuration not supported in this CloudStack version (requires 4.21.0+): %v", err)
152+
}
153+
}

0 commit comments

Comments
 (0)