Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions cloudstack/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -89,6 +89,7 @@ func Provider() terraform.ResourceProvider {
"cloudstack_firewall": resourceCloudStackFirewall(),
"cloudstack_instance": resourceCloudStackInstance(),
"cloudstack_ipaddress": resourceCloudStackIPAddress(),
"cloudstack_kubernetes_cluster": resourceCloudStackKubernetesCluster(),
"cloudstack_loadbalancer_rule": resourceCloudStackLoadBalancerRule(),
"cloudstack_network": resourceCloudStackNetwork(),
"cloudstack_network_acl": resourceCloudStackNetworkACL(),
Expand Down
343 changes: 343 additions & 0 deletions cloudstack/resource_cloudstack_kubernetes_cluster.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,343 @@
//
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
//

package cloudstack

import (
"fmt"
"log"
"strings"

"github.com/apache/cloudstack-go/v2/cloudstack"
"github.com/hashicorp/terraform/helper/schema"
)

func resourceCloudStackKubernetesCluster() *schema.Resource {
return &schema.Resource{
Create: resourceCloudStackKubernetesClusterCreate,
Read: resourceCloudStackKubernetesClusterRead,
Update: resourceCloudStackKubernetesClusterUpdate,
Delete: resourceCloudStackKubernetesClusterDelete,
Importer: &schema.ResourceImporter{
State: importStatePassthrough,
},

Schema: map[string]*schema.Schema{
"name": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"zone": {
Type: schema.TypeString,
Required: true,
ForceNew: true,
},

"kubernetes_version": {
Type: schema.TypeString,
Required: true,
},

"service_offering": {
Type: schema.TypeString,
Required: true,
},

// Begin optional params
"size": {
Type: schema.TypeInt,
Optional: true,
Default: 1,
},

"autoscaling_enabled": {
Type: schema.TypeBool,
Optional: true,
},

"min_size": {
Type: schema.TypeInt,
Optional: true,
},

"max_size": {
Type: schema.TypeInt,
Optional: true,
},

"control_nodes_size": {
Type: schema.TypeInt,
Optional: true,
Computed: true,
ForceNew: true, // For now
},

"description": {
Type: schema.TypeString,
Optional: true,
Computed: true,
},

"keypair": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},

"network_id": {
Type: schema.TypeString,
Optional: true,
Computed: true,
ForceNew: true,
},

"ip_address": {
Type: schema.TypeString,
Computed: true,
},

"state": {
Type: schema.TypeString,
Optional: true,
Computed: true,
// Default: "Running",
},

"project": {
Type: schema.TypeString,
Optional: true,
ForceNew: true,
},
},
}
}

func resourceCloudStackKubernetesClusterCreate(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

// State is always Running when created
if state, ok := d.GetOk("state"); ok {
if state.(string) != "Running" {
return fmt.Errorf("State must be 'Running' when first creating a cluster")
}
}

name := d.Get("name").(string)
size := int64(d.Get("size").(int))
serviceOfferingID, e := retrieveID(cs, "service_offering", d.Get("service_offering").(string))
if e != nil {
return e.Error()
}
zoneID, e := retrieveID(cs, "zone", d.Get("zone").(string))
if e != nil {
return e.Error()
}
kubernetesVersionID, e := retrieveID(cs, "kubernetes_version", d.Get("kubernetes_version").(string))
if e != nil {
return e.Error()
}

// Create a new parameter struct
p := cs.Kubernetes.NewCreateKubernetesClusterParams(name, kubernetesVersionID, name, serviceOfferingID, size, zoneID)

// Set optional params
if description, ok := d.GetOk("description"); ok {
p.SetDescription(description.(string))
}
if keypair, ok := d.GetOk("keypair"); ok {
p.SetKeypair(keypair.(string))
}
if networkID, ok := d.GetOk("network_id"); ok {
p.SetNetworkid(networkID.(string))
}
if controlNodesSize, ok := d.GetOk("control_nodes_size"); ok {
p.SetControlnodes(int64(controlNodesSize.(int)))
}

// If there is a project supplied, we retrieve and set the project id
if err := setProjectid(p, cs, d); err != nil {
return err
}

log.Printf("[DEBUG] Creating Kubernetes Cluster %s", name)
r, err := cs.Kubernetes.CreateKubernetesCluster(p)
if err != nil {
return err
}

log.Printf("[DEBUG] Kubernetes Cluster %s successfully created", name)
d.SetId(r.Id)

if _, ok := d.GetOk("autoscaling_enabled"); ok {
err = autoscaleKubernetesCluster(d, meta)
if err != nil {
return err
}
}

return resourceCloudStackKubernetesClusterRead(d, meta)
}

func resourceCloudStackKubernetesClusterRead(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

log.Printf("[DEBUG] Retrieving Kubernetes Cluster %s", d.Get("name").(string))

// Get the Kubernetes Cluster details
cluster, count, err := cs.Kubernetes.GetKubernetesClusterByID(
d.Id(),
cloudstack.WithProject(d.Get("project").(string)),
)
if err != nil {
if count == 0 {
log.Printf("[DEBUG] Kubernetes Cluster %s does not longer exist", d.Get("name").(string))
d.SetId("")
return nil
}

return err
}

// Update the config
d.SetId(cluster.Id)
d.Set("name", cluster.Name)
d.Set("description", cluster.Description)
d.Set("control_nodes_size", cluster.Controlnodes)
d.Set("size", cluster.Size)
d.Set("autoscaling_enabled", cluster.Autoscalingenabled)
d.Set("min_size", cluster.Minsize)
d.Set("max_size", cluster.Maxsize)
d.Set("keypair", cluster.Keypair)
d.Set("network_id", cluster.Networkid)
d.Set("ip_address", cluster.Ipaddress)
d.Set("state", cluster.State)

setValueOrID(d, "kubernetes_version", cluster.Kubernetesversionname, cluster.Kubernetesversionid)
setValueOrID(d, "service_offering", cluster.Serviceofferingname, cluster.Serviceofferingid)
setValueOrID(d, "project", cluster.Project, cluster.Projectid)
setValueOrID(d, "zone", cluster.Zonename, cluster.Zoneid)

return nil
}

func autoscaleKubernetesCluster(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)
p := cs.Kubernetes.NewScaleKubernetesClusterParams(d.Id())
p.SetAutoscalingenabled(d.Get("autoscaling_enabled").(bool))
p.SetMinsize(int64(d.Get("min_size").(int)))
p.SetMaxsize(int64(d.Get("max_size").(int)))
_, err := cs.Kubernetes.ScaleKubernetesCluster(p)
return err
}

func resourceCloudStackKubernetesClusterUpdate(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)
d.Partial(true)

if d.HasChange("service_offering") || d.HasChange("size") {
p := cs.Kubernetes.NewScaleKubernetesClusterParams(d.Id())
serviceOfferingID, e := retrieveID(cs, "service_offering", d.Get("service_offering").(string))
if e != nil {
return e.Error()
}
p.SetServiceofferingid(serviceOfferingID)
p.SetSize(int64(d.Get("size").(int)))
_, err := cs.Kubernetes.ScaleKubernetesCluster(p)
if err != nil {
return fmt.Errorf(
"Error Scaling Kubernetes Cluster %s: %s", d.Id(), err)
}
d.SetPartial("service_offering")
d.SetPartial("size")
}

if d.HasChange("autoscaling_enabled") || d.HasChange("min_size") || d.HasChange("max_size") {
err := autoscaleKubernetesCluster(d, meta)
if err != nil {
return err
}
d.SetPartial("autoscaling_enabled")
d.SetPartial("min_size")
d.SetPartial("max_size")
}

if d.HasChange("kubernetes_version") {
kubernetesVersionID, e := retrieveID(cs, "kubernetes_version", d.Get("kubernetes_version").(string))
if e != nil {
return e.Error()
}
p := cs.Kubernetes.NewUpgradeKubernetesClusterParams(d.Id(), kubernetesVersionID)
_, err := cs.Kubernetes.UpgradeKubernetesCluster(p)
if err != nil {
return fmt.Errorf(
"Error Upgrading Kubernetes Cluster %s: %s", d.Id(), err)
}
d.SetPartial("kubernetes_version")
}

if d.HasChange("state") {
state := d.Get("state").(string)
switch state {
case "Running":
p := cs.Kubernetes.NewStartKubernetesClusterParams(d.Id())
_, err := cs.Kubernetes.StartKubernetesCluster(p)
if err != nil {
return fmt.Errorf(
"Error Starting Kubernetes Cluster %s: %s", d.Id(), err)
}
case "Stopped":
p := cs.Kubernetes.NewStopKubernetesClusterParams(d.Id())
_, err := cs.Kubernetes.StopKubernetesCluster(p)
if err != nil {
return fmt.Errorf(
"Error Stopping Kubernetes Cluster %s: %s", d.Id(), err)
}
default:
return fmt.Errorf("State must either be 'Running' or 'Stopped'")
}
d.SetPartial("state")
}

d.Partial(false)
return resourceCloudStackKubernetesClusterRead(d, meta)
}

func resourceCloudStackKubernetesClusterDelete(d *schema.ResourceData, meta interface{}) error {
cs := meta.(*cloudstack.CloudStackClient)

// Create a new parameter struct
p := cs.Kubernetes.NewDeleteKubernetesClusterParams(d.Id())

// Delete the Kubernetes Cluster
_, err := cs.Kubernetes.DeleteKubernetesCluster(p)
if err != nil {
// This is a very poor way to be told the ID does no longer exist :(
if strings.Contains(err.Error(), fmt.Sprintf(
"Invalid parameter id value=%s due to incorrect long value format, "+
"or entity does not exist", d.Id())) {
return nil
}

return fmt.Errorf("Error deleting Kubernetes Cluster: %s", err)
}

return nil
}
6 changes: 4 additions & 2 deletions cloudstack/resources.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,14 @@ func retrieveID(cs *cloudstack.CloudStackClient, name string, value string, opts
switch name {
case "disk_offering":
id, _, err = cs.DiskOffering.GetDiskOfferingID(value)
case "service_offering":
id, _, err = cs.ServiceOffering.GetServiceOfferingID(value)
case "kubernetes_version":
id, _, err = cs.Kubernetes.GetKubernetesSupportedVersionID(value)
case "network_offering":
id, _, err = cs.NetworkOffering.GetNetworkOfferingID(value)
case "project":
id, _, err = cs.Project.GetProjectID(value)
case "service_offering":
id, _, err = cs.ServiceOffering.GetServiceOfferingID(value)
case "vpc_offering":
id, _, err = cs.VPC.GetVPCOfferingID(value)
case "zone":
Expand Down
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
module github.com/terraform-providers/terraform-provider-cloudstack

require (
github.com/apache/cloudstack-go/v2 v2.11.0
github.com/apache/cloudstack-go/v2 v2.13.1
github.com/go-ini/ini v1.40.0
github.com/gopherjs/gopherjs v0.0.0-20181103185306-d547d1d9531e // indirect
github.com/hashicorp/go-multierror v1.0.0
Expand Down
Loading