Skip to content

Commit 7432e29

Browse files
committed
Adding support for Kubernetes Clusters
1 parent 9496d6a commit 7432e29

5 files changed

Lines changed: 326 additions & 3 deletions

File tree

cloudstack/provider.go

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -89,6 +89,7 @@ func Provider() terraform.ResourceProvider {
8989
"cloudstack_firewall": resourceCloudStackFirewall(),
9090
"cloudstack_instance": resourceCloudStackInstance(),
9191
"cloudstack_ipaddress": resourceCloudStackIPAddress(),
92+
"cloudstack_kubernetes_cluster": resourceCloudStackKubernetesCluster(),
9293
"cloudstack_loadbalancer_rule": resourceCloudStackLoadBalancerRule(),
9394
"cloudstack_network": resourceCloudStackNetwork(),
9495
"cloudstack_network_acl": resourceCloudStackNetworkACL(),
Lines changed: 304 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,304 @@
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/helper/schema"
29+
)
30+
31+
func resourceCloudStackKubernetesCluster() *schema.Resource {
32+
return &schema.Resource{
33+
Create: resourceCloudStackKubernetesClusterCreate,
34+
Read: resourceCloudStackKubernetesClusterRead,
35+
Update: resourceCloudStackKubernetesClusterUpdate,
36+
Delete: resourceCloudStackKubernetesClusterDelete,
37+
Importer: &schema.ResourceImporter{
38+
State: importStatePassthrough,
39+
},
40+
41+
Schema: map[string]*schema.Schema{
42+
"name": {
43+
Type: schema.TypeString,
44+
Required: true,
45+
ForceNew: true,
46+
},
47+
48+
"zone": {
49+
Type: schema.TypeString,
50+
Required: true,
51+
ForceNew: true,
52+
},
53+
54+
"kubernetes_version": {
55+
Type: schema.TypeString,
56+
Required: true,
57+
},
58+
59+
"service_offering": {
60+
Type: schema.TypeString,
61+
Required: true,
62+
},
63+
64+
// Begin optional params
65+
"size": {
66+
Type: schema.TypeInt,
67+
Optional: true,
68+
Default: 1,
69+
},
70+
71+
"autoscaling_enabled": {
72+
Type: schema.TypeBool,
73+
Optional: true,
74+
},
75+
76+
"min_size": {
77+
Type: schema.TypeInt,
78+
Optional: true,
79+
},
80+
81+
"max_size": {
82+
Type: schema.TypeInt,
83+
Optional: true,
84+
},
85+
86+
"control_nodes_size": {
87+
Type: schema.TypeInt,
88+
Optional: true,
89+
Computed: true,
90+
ForceNew: true, // For now
91+
},
92+
93+
"description": {
94+
Type: schema.TypeString,
95+
Optional: true,
96+
Computed: true,
97+
},
98+
99+
"keypair": {
100+
Type: schema.TypeString,
101+
Optional: true,
102+
ForceNew: true,
103+
},
104+
105+
"network_id": {
106+
Type: schema.TypeString,
107+
Optional: true,
108+
Computed: true,
109+
ForceNew: true,
110+
},
111+
112+
"ip_address": {
113+
Type: schema.TypeString,
114+
Computed: true,
115+
},
116+
117+
"project": {
118+
Type: schema.TypeString,
119+
Optional: true,
120+
ForceNew: true,
121+
},
122+
},
123+
}
124+
}
125+
126+
func resourceCloudStackKubernetesClusterCreate(d *schema.ResourceData, meta interface{}) error {
127+
cs := meta.(*cloudstack.CloudStackClient)
128+
129+
name := d.Get("name").(string)
130+
size := int64(d.Get("size").(int))
131+
serviceOfferingID, e := retrieveID(cs, "service_offering", d.Get("service_offering").(string))
132+
if e != nil {
133+
return e.Error()
134+
}
135+
zoneID, e := retrieveID(cs, "zone", d.Get("zone").(string))
136+
if e != nil {
137+
return e.Error()
138+
}
139+
kubernetesVersionID, e := retrieveID(cs, "kubernetes_version", d.Get("kubernetes_version").(string))
140+
if e != nil {
141+
return e.Error()
142+
}
143+
144+
// Create a new parameter struct
145+
p := cs.Kubernetes.NewCreateKubernetesClusterParams(name, kubernetesVersionID, name, serviceOfferingID, size, zoneID)
146+
147+
// Set optional params
148+
if description, ok := d.GetOk("description"); ok {
149+
p.SetDescription(description.(string))
150+
}
151+
if keypair, ok := d.GetOk("keypair"); ok {
152+
p.SetKeypair(keypair.(string))
153+
}
154+
if networkID, ok := d.GetOk("network_id"); ok {
155+
p.SetNetworkid(networkID.(string))
156+
}
157+
if controlNodesSize, ok := d.GetOk("control_nodes_size"); ok {
158+
p.SetControlnodes(int64(controlNodesSize.(int)))
159+
}
160+
161+
// If there is a project supplied, we retrieve and set the project id
162+
if err := setProjectid(p, cs, d); err != nil {
163+
return err
164+
}
165+
166+
log.Printf("[DEBUG] Creating Kubernetes Cluster %s", name)
167+
r, err := cs.Kubernetes.CreateKubernetesCluster(p)
168+
if err != nil {
169+
return err
170+
}
171+
172+
log.Printf("[DEBUG] Kubernetes Cluster %s successfully created", name)
173+
d.SetId(r.Id)
174+
175+
if _, ok := d.GetOk("autoscaling_enabled"); ok {
176+
err = autoscaleKubernetesCluster(d, meta)
177+
if err != nil {
178+
return err
179+
}
180+
}
181+
182+
return resourceCloudStackKubernetesClusterRead(d, meta)
183+
}
184+
185+
func resourceCloudStackKubernetesClusterRead(d *schema.ResourceData, meta interface{}) error {
186+
cs := meta.(*cloudstack.CloudStackClient)
187+
188+
log.Printf("[DEBUG] Retrieving Kubernetes Cluster %s", d.Get("name").(string))
189+
190+
// Get the Kubernetes Cluster details
191+
cluster, count, err := cs.Kubernetes.GetKubernetesClusterByID(
192+
d.Id(),
193+
cloudstack.WithProject(d.Get("project").(string)),
194+
)
195+
if err != nil {
196+
if count == 0 {
197+
log.Printf("[DEBUG] Kubernetes Cluster %s does not longer exist", d.Get("name").(string))
198+
d.SetId("")
199+
return nil
200+
}
201+
202+
return err
203+
}
204+
205+
// Update the config
206+
d.SetId(cluster.Id)
207+
d.Set("name", cluster.Name)
208+
d.Set("description", cluster.Description)
209+
d.Set("control_nodes_size", cluster.Controlnodes)
210+
d.Set("size", cluster.Size)
211+
d.Set("autoscaling_enabled", cluster.Autoscalingenabled)
212+
d.Set("min_size", cluster.Minsize)
213+
d.Set("max_size", cluster.Maxsize)
214+
d.Set("keypair", cluster.Keypair)
215+
d.Set("network_id", cluster.Networkid)
216+
d.Set("ip_address", cluster.Ipaddress)
217+
218+
setValueOrID(d, "kubernetes_version", cluster.Kubernetesversionname, cluster.Kubernetesversionid)
219+
setValueOrID(d, "service_offering", cluster.Serviceofferingname, cluster.Serviceofferingid)
220+
setValueOrID(d, "project", cluster.Project, cluster.Projectid)
221+
setValueOrID(d, "zone", cluster.Zonename, cluster.Zoneid)
222+
223+
return nil
224+
}
225+
226+
func autoscaleKubernetesCluster(d *schema.ResourceData, meta interface{}) error {
227+
cs := meta.(*cloudstack.CloudStackClient)
228+
p := cs.Kubernetes.NewScaleKubernetesClusterParams(d.Id())
229+
p.SetAutoscalingenabled(d.Get("autoscaling_enabled").(bool))
230+
p.SetMinsize(int64(d.Get("min_size").(int)))
231+
p.SetMaxsize(int64(d.Get("max_size").(int)))
232+
_, err := cs.Kubernetes.ScaleKubernetesCluster(p)
233+
return err
234+
}
235+
236+
func resourceCloudStackKubernetesClusterUpdate(d *schema.ResourceData, meta interface{}) error {
237+
cs := meta.(*cloudstack.CloudStackClient)
238+
d.Partial(true)
239+
240+
if d.HasChange("service_offering") || d.HasChange("size") {
241+
p := cs.Kubernetes.NewScaleKubernetesClusterParams(d.Id())
242+
serviceOfferingID, e := retrieveID(cs, "service_offering", d.Get("service_offering").(string))
243+
if e != nil {
244+
return e.Error()
245+
}
246+
p.SetServiceofferingid(serviceOfferingID)
247+
p.SetSize(int64(d.Get("size").(int)))
248+
_, err := cs.Kubernetes.ScaleKubernetesCluster(p)
249+
if err != nil {
250+
return fmt.Errorf(
251+
"Error Scaling Kubernetes Cluster %s: %s", d.Id(), err)
252+
}
253+
d.SetPartial("service_offering")
254+
d.SetPartial("size")
255+
}
256+
257+
if d.HasChange("autoscaling_enabled") || d.HasChange("min_size") || d.HasChange("max_size") {
258+
err := autoscaleKubernetesCluster(d, meta)
259+
if err != nil {
260+
return err
261+
}
262+
d.SetPartial("autoscaling_enabled")
263+
d.SetPartial("min_size")
264+
d.SetPartial("max_size")
265+
}
266+
267+
if d.HasChange("kubernetes_version") {
268+
kubernetesVersionID, e := retrieveID(cs, "kubernetes_version", d.Get("kubernetes_version").(string))
269+
if e != nil {
270+
return e.Error()
271+
}
272+
p := cs.Kubernetes.NewUpgradeKubernetesClusterParams(d.Id(), kubernetesVersionID)
273+
_, err := cs.Kubernetes.UpgradeKubernetesCluster(p)
274+
if err != nil {
275+
return fmt.Errorf(
276+
"Error Upgrading Kubernetes Cluster %s: %s", d.Id(), err)
277+
}
278+
d.SetPartial("kubernetes_version")
279+
}
280+
281+
return resourceCloudStackKubernetesClusterRead(d, meta)
282+
}
283+
284+
func resourceCloudStackKubernetesClusterDelete(d *schema.ResourceData, meta interface{}) error {
285+
cs := meta.(*cloudstack.CloudStackClient)
286+
287+
// Create a new parameter struct
288+
p := cs.Kubernetes.NewDeleteKubernetesClusterParams(d.Id())
289+
290+
// Delete the Kubernetes Cluster
291+
_, err := cs.Kubernetes.DeleteKubernetesCluster(p)
292+
if err != nil {
293+
// This is a very poor way to be told the ID does no longer exist :(
294+
if strings.Contains(err.Error(), fmt.Sprintf(
295+
"Invalid parameter id value=%s due to incorrect long value format, "+
296+
"or entity does not exist", d.Id())) {
297+
return nil
298+
}
299+
300+
return fmt.Errorf("Error deleting Kubernetes Cluster: %s", err)
301+
}
302+
303+
return nil
304+
}

cloudstack/resources.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,12 +70,14 @@ func retrieveID(cs *cloudstack.CloudStackClient, name string, value string, opts
7070
switch name {
7171
case "disk_offering":
7272
id, _, err = cs.DiskOffering.GetDiskOfferingID(value)
73-
case "service_offering":
74-
id, _, err = cs.ServiceOffering.GetServiceOfferingID(value)
73+
case "kubernetes_version":
74+
id, _, err = cs.Kubernetes.GetKubernetesSupportedVersionID(value)
7575
case "network_offering":
7676
id, _, err = cs.NetworkOffering.GetNetworkOfferingID(value)
7777
case "project":
7878
id, _, err = cs.Project.GetProjectID(value)
79+
case "service_offering":
80+
id, _, err = cs.ServiceOffering.GetServiceOfferingID(value)
7981
case "vpc_offering":
8082
id, _, err = cs.VPC.GetVPCOfferingID(value)
8183
case "zone":

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module github.com/terraform-providers/terraform-provider-cloudstack
22

33
require (
4-
github.com/apache/cloudstack-go/v2 v2.11.0
4+
github.com/apache/cloudstack-go/v2 v2.13.1
55
github.com/go-ini/ini v1.40.0
66
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
77
github.com/hashicorp/go-multierror v1.0.0

0 commit comments

Comments
 (0)