2015-07-05 16:39:01 +00:00
|
|
|
package google
|
|
|
|
|
|
|
|
import (
|
|
|
|
"fmt"
|
|
|
|
"log"
|
|
|
|
"regexp"
|
2017-09-07 17:31:58 +00:00
|
|
|
"strings"
|
2017-06-28 08:22:31 +00:00
|
|
|
"time"
|
2015-07-05 16:39:01 +00:00
|
|
|
|
2018-03-23 00:22:44 +00:00
|
|
|
"github.com/hashicorp/errwrap"
|
2017-11-21 17:34:32 +00:00
|
|
|
"github.com/hashicorp/go-version"
|
2017-10-12 18:21:33 +00:00
|
|
|
"github.com/hashicorp/terraform/helper/resource"
|
2015-07-05 16:39:01 +00:00
|
|
|
"github.com/hashicorp/terraform/helper/schema"
|
2017-08-18 00:51:58 +00:00
|
|
|
"github.com/hashicorp/terraform/helper/validation"
|
2015-07-05 16:39:01 +00:00
|
|
|
"google.golang.org/api/container/v1"
|
2018-03-07 01:44:05 +00:00
|
|
|
containerBeta "google.golang.org/api/container/v1beta1"
|
2015-07-05 16:39:01 +00:00
|
|
|
)
|
|
|
|
|
2017-03-07 05:14:32 +00:00
|
|
|
var (
|
2018-03-07 01:44:05 +00:00
|
|
|
instanceGroupManagerURL = regexp.MustCompile("^https://www.googleapis.com/compute/v1/projects/([a-z][a-z0-9-]{5}(?:[-a-z0-9]{0,23}[a-z0-9])?)/zones/([a-z0-9-]*)/instanceGroupManagers/([^/]*)")
|
|
|
|
ContainerClusterBaseApiVersion = v1
|
2018-03-15 20:28:30 +00:00
|
|
|
ContainerClusterVersionedFeatures = []Feature{
|
|
|
|
{Version: v1beta1, Item: "pod_security_policy_config"},
|
2018-03-27 21:55:42 +00:00
|
|
|
{Version: v1beta1, Item: "node_config.*.taint"},
|
2018-03-15 20:28:30 +00:00
|
|
|
{Version: v1beta1, Item: "node_config.*.workload_metadata_config"},
|
2018-03-30 17:10:25 +00:00
|
|
|
{Version: v1beta1, Item: "private_cluster"},
|
|
|
|
{Version: v1beta1, Item: "master_ipv4_cidr_block"},
|
2018-04-05 21:51:35 +00:00
|
|
|
{Version: v1beta1, Item: "region"},
|
2018-03-15 20:28:30 +00:00
|
|
|
}
|
2018-03-07 01:44:05 +00:00
|
|
|
|
|
|
|
networkConfig = &schema.Resource{
|
2018-03-01 21:19:18 +00:00
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"cidr_blocks": {
|
|
|
|
Type: schema.TypeSet,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
MaxItems: 10,
|
|
|
|
Elem: cidrBlockConfig,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
cidrBlockConfig = &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"cidr_block": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
ValidateFunc: validation.CIDRNetwork(0, 32),
|
|
|
|
},
|
|
|
|
"display_name": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
2017-03-07 05:14:32 +00:00
|
|
|
)
|
|
|
|
|
2015-07-05 16:39:01 +00:00
|
|
|
func resourceContainerCluster() *schema.Resource {
|
|
|
|
return &schema.Resource{
|
|
|
|
Create: resourceContainerClusterCreate,
|
|
|
|
Read: resourceContainerClusterRead,
|
|
|
|
Update: resourceContainerClusterUpdate,
|
|
|
|
Delete: resourceContainerClusterDelete,
|
|
|
|
|
2017-06-28 08:22:31 +00:00
|
|
|
Timeouts: &schema.ResourceTimeout{
|
|
|
|
Create: schema.DefaultTimeout(30 * time.Minute),
|
|
|
|
Update: schema.DefaultTimeout(10 * time.Minute),
|
|
|
|
Delete: schema.DefaultTimeout(10 * time.Minute),
|
|
|
|
},
|
|
|
|
|
2017-07-05 23:00:49 +00:00
|
|
|
SchemaVersion: 1,
|
|
|
|
MigrateState: resourceContainerClusterMigrateState,
|
|
|
|
|
2017-09-07 17:31:58 +00:00
|
|
|
Importer: &schema.ResourceImporter{
|
|
|
|
State: resourceContainerClusterStateImporter,
|
|
|
|
},
|
|
|
|
|
2015-07-05 16:39:01 +00:00
|
|
|
Schema: map[string]*schema.Schema{
|
2017-06-28 08:34:20 +00:00
|
|
|
"name": {
|
2015-07-05 16:39:01 +00:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
ForceNew: true,
|
|
|
|
ValidateFunc: func(v interface{}, k string) (ws []string, errors []error) {
|
|
|
|
value := v.(string)
|
|
|
|
|
|
|
|
if len(value) > 40 {
|
|
|
|
errors = append(errors, fmt.Errorf(
|
|
|
|
"%q cannot be longer than 40 characters", k))
|
|
|
|
}
|
|
|
|
if !regexp.MustCompile("^[a-z0-9-]+$").MatchString(value) {
|
|
|
|
errors = append(errors, fmt.Errorf(
|
|
|
|
"%q can only contain lowercase letters, numbers and hyphens", k))
|
|
|
|
}
|
|
|
|
if !regexp.MustCompile("^[a-z]").MatchString(value) {
|
|
|
|
errors = append(errors, fmt.Errorf(
|
|
|
|
"%q must start with a letter", k))
|
|
|
|
}
|
|
|
|
if !regexp.MustCompile("[a-z0-9]$").MatchString(value) {
|
|
|
|
errors = append(errors, fmt.Errorf(
|
|
|
|
"%q must end with a number or a letter", k))
|
|
|
|
}
|
|
|
|
return
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
"region": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ConflictsWith: []string{"zone"},
|
|
|
|
},
|
|
|
|
|
2017-06-28 08:34:20 +00:00
|
|
|
"zone": {
|
2018-04-05 21:51:35 +00:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
ConflictsWith: []string{"region"},
|
2016-04-10 21:34:15 +00:00
|
|
|
},
|
|
|
|
|
2017-06-28 08:34:20 +00:00
|
|
|
"additional_zones": {
|
2017-07-05 23:00:49 +00:00
|
|
|
Type: schema.TypeSet,
|
2017-01-04 06:14:39 +00:00
|
|
|
Optional: true,
|
2017-02-07 01:21:34 +00:00
|
|
|
Computed: true,
|
2017-01-04 06:14:39 +00:00
|
|
|
Elem: &schema.Schema{Type: schema.TypeString},
|
|
|
|
},
|
|
|
|
|
2017-06-28 08:34:20 +00:00
|
|
|
"addons_config": {
|
2016-03-25 23:29:56 +00:00
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
2017-10-20 16:47:07 +00:00
|
|
|
Computed: true,
|
2016-03-25 23:29:56 +00:00
|
|
|
MaxItems: 1,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
2017-06-28 08:34:20 +00:00
|
|
|
"http_load_balancing": {
|
2016-03-25 23:29:56 +00:00
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
2017-10-20 16:47:07 +00:00
|
|
|
Computed: true,
|
2016-03-25 23:29:56 +00:00
|
|
|
MaxItems: 1,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
2017-06-28 08:34:20 +00:00
|
|
|
"disabled": {
|
2016-03-25 23:29:56 +00:00
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2017-06-28 08:34:20 +00:00
|
|
|
"horizontal_pod_autoscaling": {
|
2016-03-25 23:29:56 +00:00
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
2017-10-20 16:47:07 +00:00
|
|
|
Computed: true,
|
2016-03-25 23:29:56 +00:00
|
|
|
MaxItems: 1,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
2017-06-28 08:34:20 +00:00
|
|
|
"disabled": {
|
2016-03-25 23:29:56 +00:00
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2017-10-03 16:29:27 +00:00
|
|
|
"kubernetes_dashboard": {
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
2017-10-20 16:47:07 +00:00
|
|
|
Computed: true,
|
2017-10-03 16:29:27 +00:00
|
|
|
MaxItems: 1,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"disabled": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-03-15 21:50:24 +00:00
|
|
|
"network_policy_config": {
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
MaxItems: 1,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"disabled": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2016-03-25 23:29:56 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2015-07-05 16:39:01 +00:00
|
|
|
|
2017-10-27 22:18:34 +00:00
|
|
|
"cluster_ipv4_cidr": {
|
2017-11-02 17:38:20 +00:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
ValidateFunc: validateRFC1918Network(8, 32),
|
2017-10-27 22:18:34 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
"description": {
|
2017-10-06 22:48:01 +00:00
|
|
|
Type: schema.TypeString,
|
2017-10-27 22:18:34 +00:00
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
2017-10-31 23:38:18 +00:00
|
|
|
"enable_kubernetes_alpha": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
Default: false,
|
|
|
|
},
|
|
|
|
|
2017-10-27 22:18:34 +00:00
|
|
|
"enable_legacy_abac": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
2018-04-04 17:58:08 +00:00
|
|
|
Default: false,
|
2017-10-27 22:18:34 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
"initial_node_count": {
|
|
|
|
Type: schema.TypeInt,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"logging_service": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ValidateFunc: validation.StringInSlice([]string{"logging.googleapis.com", "none"}, false),
|
|
|
|
},
|
|
|
|
|
2017-11-07 23:42:11 +00:00
|
|
|
"maintenance_policy": {
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
|
|
|
MaxItems: 1,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"daily_maintenance_window": {
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Required: true,
|
|
|
|
MaxItems: 1,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"start_time": {
|
2017-11-13 19:30:26 +00:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
ValidateFunc: validateRFC3339Time,
|
|
|
|
DiffSuppressFunc: rfc3339TimeDiffSuppress,
|
2017-11-07 23:42:11 +00:00
|
|
|
},
|
|
|
|
"duration": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2017-10-27 22:18:34 +00:00
|
|
|
"master_auth": {
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
MaxItems: 1,
|
2017-10-06 22:48:01 +00:00
|
|
|
Computed: true,
|
2017-10-27 22:18:34 +00:00
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"password": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
ForceNew: true,
|
|
|
|
Sensitive: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"username": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Required: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"client_certificate": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"client_key": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
Sensitive: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"cluster_ca_certificate": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2017-10-06 22:48:01 +00:00
|
|
|
},
|
|
|
|
|
2017-11-02 17:38:20 +00:00
|
|
|
"master_authorized_networks_config": {
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
|
|
|
MaxItems: 1,
|
2018-03-01 21:19:18 +00:00
|
|
|
Elem: networkConfig,
|
2017-11-02 17:38:20 +00:00
|
|
|
},
|
|
|
|
|
2017-10-12 18:21:33 +00:00
|
|
|
"min_master_version": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
},
|
|
|
|
|
2017-10-27 22:18:34 +00:00
|
|
|
"monitoring_service": {
|
2016-04-10 21:34:15 +00:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
2015-07-05 16:39:01 +00:00
|
|
|
Computed: true,
|
|
|
|
},
|
2016-04-10 16:59:57 +00:00
|
|
|
|
2017-10-27 22:18:34 +00:00
|
|
|
"network": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Default: "default",
|
|
|
|
ForceNew: true,
|
|
|
|
StateFunc: StoreResourceName,
|
|
|
|
},
|
|
|
|
|
2017-11-27 20:40:07 +00:00
|
|
|
"network_policy": {
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
MaxItems: 1,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"enabled": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
Default: false,
|
|
|
|
},
|
|
|
|
"provider": {
|
2018-02-01 18:25:12 +00:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Default: "PROVIDER_UNSPECIFIED",
|
|
|
|
Optional: true,
|
|
|
|
ValidateFunc: validation.StringInSlice([]string{"PROVIDER_UNSPECIFIED", "CALICO"}, false),
|
|
|
|
DiffSuppressFunc: emptyOrDefaultStringSuppress("PROVIDER_UNSPECIFIED"),
|
2017-11-27 20:40:07 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2017-10-27 22:18:34 +00:00
|
|
|
"node_config": schemaNodeConfig,
|
|
|
|
|
2017-06-28 08:34:20 +00:00
|
|
|
"node_pool": {
|
2017-04-12 19:57:53 +00:00
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true, // TODO(danawillow): Add ability to add/remove nodePools
|
|
|
|
Elem: &schema.Resource{
|
2017-10-04 00:09:34 +00:00
|
|
|
Schema: schemaNodePool,
|
2017-04-12 19:57:53 +00:00
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2017-10-27 22:18:34 +00:00
|
|
|
"node_version": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
|
2018-03-14 21:00:31 +00:00
|
|
|
"pod_security_policy_config": {
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
|
|
|
MaxItems: 1,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"enabled": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Required: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
|
2017-06-28 08:34:20 +00:00
|
|
|
"project": {
|
2016-04-10 16:59:57 +00:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
2017-11-28 00:32:20 +00:00
|
|
|
Computed: true,
|
2016-04-10 16:59:57 +00:00
|
|
|
ForceNew: true,
|
|
|
|
},
|
2017-10-27 22:18:34 +00:00
|
|
|
|
|
|
|
"subnetwork": {
|
2018-02-08 18:04:16 +00:00
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
Computed: true,
|
|
|
|
ForceNew: true,
|
|
|
|
DiffSuppressFunc: compareSelfLinkOrResourceName,
|
2017-10-27 22:18:34 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
"endpoint": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
},
|
|
|
|
|
|
|
|
"instance_group_urls": {
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Computed: true,
|
|
|
|
Elem: &schema.Schema{Type: schema.TypeString},
|
|
|
|
},
|
|
|
|
|
|
|
|
"master_version": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Computed: true,
|
|
|
|
},
|
2017-11-27 23:15:03 +00:00
|
|
|
|
|
|
|
"ip_allocation_policy": {
|
|
|
|
Type: schema.TypeList,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
MaxItems: 1,
|
|
|
|
Elem: &schema.Resource{
|
|
|
|
Schema: map[string]*schema.Schema{
|
|
|
|
"cluster_secondary_range_name": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
"services_secondary_range_name": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
2018-03-23 00:22:44 +00:00
|
|
|
|
|
|
|
"remove_default_node_pool": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
},
|
2018-03-30 17:10:25 +00:00
|
|
|
|
|
|
|
"private_cluster": {
|
|
|
|
Type: schema.TypeBool,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
Default: false,
|
|
|
|
},
|
|
|
|
|
|
|
|
"master_ipv4_cidr_block": {
|
|
|
|
Type: schema.TypeString,
|
|
|
|
Optional: true,
|
|
|
|
ForceNew: true,
|
|
|
|
ValidateFunc: validation.CIDRNetwork(28, 28),
|
|
|
|
},
|
2015-07-05 16:39:01 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceContainerClusterCreate(d *schema.ResourceData, meta interface{}) error {
|
2018-04-05 21:51:35 +00:00
|
|
|
containerAPIVersion := getContainerApiVersion(d, ContainerClusterBaseApiVersion, ContainerClusterVersionedFeatures)
|
2015-07-05 16:39:01 +00:00
|
|
|
config := meta.(*Config)
|
|
|
|
|
2016-04-10 16:59:57 +00:00
|
|
|
project, err := getProject(d, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
location, err := getLocation(d, config)
|
2017-12-06 22:30:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-05 21:51:35 +00:00
|
|
|
|
2015-07-05 16:39:01 +00:00
|
|
|
clusterName := d.Get("name").(string)
|
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
cluster := &containerBeta.Cluster{
|
2017-05-31 19:43:31 +00:00
|
|
|
Name: clusterName,
|
|
|
|
InitialNodeCount: int64(d.Get("initial_node_count").(int)),
|
2015-07-05 16:39:01 +00:00
|
|
|
}
|
|
|
|
|
2017-06-28 08:22:31 +00:00
|
|
|
timeoutInMinutes := int(d.Timeout(schema.TimeoutCreate).Minutes())
|
|
|
|
|
2017-11-07 23:42:11 +00:00
|
|
|
if v, ok := d.GetOk("maintenance_policy"); ok {
|
2017-12-27 20:29:14 +00:00
|
|
|
cluster.MaintenancePolicy = expandMaintenancePolicy(v)
|
2017-11-07 23:42:11 +00:00
|
|
|
}
|
|
|
|
|
2017-05-31 19:43:31 +00:00
|
|
|
if v, ok := d.GetOk("master_auth"); ok {
|
|
|
|
masterAuths := v.([]interface{})
|
|
|
|
masterAuth := masterAuths[0].(map[string]interface{})
|
2018-03-07 01:44:05 +00:00
|
|
|
cluster.MasterAuth = &containerBeta.MasterAuth{
|
2015-07-05 16:39:01 +00:00
|
|
|
Password: masterAuth["password"].(string),
|
|
|
|
Username: masterAuth["username"].(string),
|
2017-05-31 19:43:31 +00:00
|
|
|
}
|
2015-07-05 16:39:01 +00:00
|
|
|
}
|
|
|
|
|
2017-11-02 17:38:20 +00:00
|
|
|
if v, ok := d.GetOk("master_authorized_networks_config"); ok {
|
|
|
|
cluster.MasterAuthorizedNetworksConfig = expandMasterAuthorizedNetworksConfig(v)
|
|
|
|
}
|
|
|
|
|
2017-10-12 18:21:33 +00:00
|
|
|
if v, ok := d.GetOk("min_master_version"); ok {
|
2016-12-18 13:50:46 +00:00
|
|
|
cluster.InitialClusterVersion = v.(string)
|
|
|
|
}
|
|
|
|
|
2017-10-12 18:21:33 +00:00
|
|
|
// Only allow setting node_version on create if it's set to the equivalent master version,
|
|
|
|
// since `InitialClusterVersion` only accepts valid master-style versions.
|
|
|
|
if v, ok := d.GetOk("node_version"); ok {
|
|
|
|
// ignore -gke.X suffix for now. if it becomes a problem later, we can fix it.
|
|
|
|
mv := strings.Split(cluster.InitialClusterVersion, "-")[0]
|
|
|
|
nv := strings.Split(v.(string), "-")[0]
|
|
|
|
if mv != nv {
|
|
|
|
return fmt.Errorf("node_version and min_master_version must be set to equivalent values on create")
|
|
|
|
}
|
2017-10-06 22:48:01 +00:00
|
|
|
}
|
|
|
|
|
2017-01-04 06:14:39 +00:00
|
|
|
if v, ok := d.GetOk("additional_zones"); ok {
|
2018-04-05 21:51:35 +00:00
|
|
|
locationsSet := v.(*schema.Set)
|
|
|
|
if locationsSet.Contains(location) {
|
|
|
|
return fmt.Errorf("additional_zones should not contain the original 'zone'")
|
|
|
|
}
|
|
|
|
if isZone(location) {
|
|
|
|
// GKE requires a full list of locations (including the original zone),
|
|
|
|
// but our schema only asks for additional zones, so append the original.
|
|
|
|
locationsSet.Add(location)
|
2017-01-04 06:14:39 +00:00
|
|
|
}
|
2018-04-05 21:51:35 +00:00
|
|
|
cluster.Locations = convertStringSet(locationsSet)
|
2017-01-04 06:14:39 +00:00
|
|
|
}
|
|
|
|
|
2015-07-05 16:39:01 +00:00
|
|
|
if v, ok := d.GetOk("cluster_ipv4_cidr"); ok {
|
|
|
|
cluster.ClusterIpv4Cidr = v.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := d.GetOk("description"); ok {
|
|
|
|
cluster.Description = v.(string)
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
cluster.LegacyAbac = &containerBeta.LegacyAbac{
|
2017-07-31 18:09:05 +00:00
|
|
|
Enabled: d.Get("enable_legacy_abac").(bool),
|
|
|
|
ForceSendFields: []string{"Enabled"},
|
|
|
|
}
|
|
|
|
|
2015-07-05 16:39:01 +00:00
|
|
|
if v, ok := d.GetOk("logging_service"); ok {
|
|
|
|
cluster.LoggingService = v.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := d.GetOk("monitoring_service"); ok {
|
|
|
|
cluster.MonitoringService = v.(string)
|
|
|
|
}
|
|
|
|
|
2017-11-29 18:54:10 +00:00
|
|
|
if v, ok := d.GetOk("network"); ok {
|
|
|
|
network, err := ParseNetworkFieldValue(v.(string), d, config)
|
2016-09-03 09:51:20 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-11-29 18:54:10 +00:00
|
|
|
cluster.Network = network.Name
|
2015-07-05 16:39:01 +00:00
|
|
|
}
|
|
|
|
|
2017-11-27 20:40:07 +00:00
|
|
|
if v, ok := d.GetOk("network_policy"); ok && len(v.([]interface{})) > 0 {
|
|
|
|
cluster.NetworkPolicy = expandNetworkPolicy(v)
|
|
|
|
}
|
|
|
|
|
2016-03-25 23:29:56 +00:00
|
|
|
if v, ok := d.GetOk("subnetwork"); ok {
|
|
|
|
cluster.Subnetwork = v.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := d.GetOk("addons_config"); ok {
|
2017-10-20 16:47:07 +00:00
|
|
|
cluster.AddonsConfig = expandClusterAddonsConfig(v)
|
2016-03-25 23:29:56 +00:00
|
|
|
}
|
2017-10-20 16:47:07 +00:00
|
|
|
|
2017-10-31 23:38:18 +00:00
|
|
|
if v, ok := d.GetOk("enable_kubernetes_alpha"); ok {
|
|
|
|
cluster.EnableKubernetesAlpha = v.(bool)
|
|
|
|
}
|
|
|
|
|
2017-04-12 19:57:53 +00:00
|
|
|
nodePoolsCount := d.Get("node_pool.#").(int)
|
|
|
|
if nodePoolsCount > 0 {
|
2018-03-07 01:44:05 +00:00
|
|
|
nodePools := make([]*containerBeta.NodePool, 0, nodePoolsCount)
|
2017-04-12 19:57:53 +00:00
|
|
|
for i := 0; i < nodePoolsCount; i++ {
|
2017-10-04 00:09:34 +00:00
|
|
|
prefix := fmt.Sprintf("node_pool.%d.", i)
|
|
|
|
nodePool, err := expandNodePool(d, prefix)
|
2017-08-04 22:34:02 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
2017-04-12 19:57:53 +00:00
|
|
|
}
|
|
|
|
nodePools = append(nodePools, nodePool)
|
|
|
|
}
|
|
|
|
cluster.NodePools = nodePools
|
2018-01-08 23:54:45 +00:00
|
|
|
} else {
|
|
|
|
// Node Configs have default values that are set in the expand function,
|
|
|
|
// but can only be set if node pools are unspecified.
|
|
|
|
cluster.NodeConfig = expandNodeConfig([]interface{}{})
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := d.GetOk("node_config"); ok {
|
|
|
|
cluster.NodeConfig = expandNodeConfig(v)
|
2017-04-12 19:57:53 +00:00
|
|
|
}
|
|
|
|
|
2017-11-27 23:15:03 +00:00
|
|
|
if v, ok := d.GetOk("ip_allocation_policy"); ok {
|
|
|
|
cluster.IpAllocationPolicy, err = expandIPAllocationPolicy(v)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-14 21:00:31 +00:00
|
|
|
if v, ok := d.GetOk("pod_security_policy_config"); ok {
|
|
|
|
cluster.PodSecurityPolicyConfig = expandPodSecurityPolicyConfig(v)
|
|
|
|
}
|
|
|
|
|
2018-03-30 17:10:25 +00:00
|
|
|
if v, ok := d.GetOk("master_ipv4_cidr_block"); ok {
|
|
|
|
cluster.MasterIpv4CidrBlock = v.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := d.GetOk("private_cluster"); ok {
|
|
|
|
if cluster.PrivateCluster = v.(bool); cluster.PrivateCluster {
|
|
|
|
if cluster.MasterIpv4CidrBlock == "" {
|
|
|
|
return fmt.Errorf("master_ipv4_cidr_block is mandatory when private_cluster=true")
|
|
|
|
}
|
|
|
|
if cluster.IpAllocationPolicy == nil {
|
|
|
|
return fmt.Errorf("ip_allocation_policy is mandatory when private_cluster=true")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
req := &containerBeta.CreateClusterRequest{
|
2015-07-05 16:39:01 +00:00
|
|
|
Cluster: cluster,
|
|
|
|
}
|
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
mutexKV.Lock(containerClusterMutexKey(project, location, clusterName))
|
|
|
|
defer mutexKV.Unlock(containerClusterMutexKey(project, location, clusterName))
|
2018-03-07 01:44:05 +00:00
|
|
|
|
|
|
|
var op interface{}
|
2018-04-05 21:51:35 +00:00
|
|
|
switch containerAPIVersion {
|
2018-03-07 01:44:05 +00:00
|
|
|
case v1:
|
|
|
|
reqV1 := &container.CreateClusterRequest{}
|
|
|
|
err = Convert(req, reqV1)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-05 21:51:35 +00:00
|
|
|
op, err = config.clientContainer.Projects.Zones.Clusters.Create(project, location, reqV1).Do()
|
2018-03-07 01:44:05 +00:00
|
|
|
case v1beta1:
|
|
|
|
reqV1Beta := &containerBeta.CreateClusterRequest{}
|
|
|
|
err = Convert(req, reqV1Beta)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-05 21:51:35 +00:00
|
|
|
|
|
|
|
parent := fmt.Sprintf("projects/%s/locations/%s", project, location)
|
|
|
|
op, err = config.clientContainerBeta.Projects.Locations.Clusters.Create(parent, reqV1Beta).Do()
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2015-07-05 16:39:01 +00:00
|
|
|
}
|
|
|
|
|
2018-03-07 19:06:00 +00:00
|
|
|
d.SetId(clusterName)
|
|
|
|
|
2015-07-05 16:39:01 +00:00
|
|
|
// Wait until it's created
|
2018-04-05 21:51:35 +00:00
|
|
|
waitErr := containerSharedOperationWait(config, op, project, location, "creating GKE cluster", timeoutInMinutes, 3)
|
2017-03-06 22:59:24 +00:00
|
|
|
if waitErr != nil {
|
|
|
|
// The resource didn't actually create
|
|
|
|
d.SetId("")
|
|
|
|
return waitErr
|
2015-07-05 16:39:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[INFO] GKE cluster %s has been created", clusterName)
|
|
|
|
|
2018-03-23 00:22:44 +00:00
|
|
|
if d.Get("remove_default_node_pool").(bool) {
|
2018-04-05 21:51:35 +00:00
|
|
|
var op interface{}
|
|
|
|
switch containerAPIVersion {
|
|
|
|
case v1:
|
|
|
|
op, err = config.clientContainer.Projects.Zones.Clusters.NodePools.Delete(
|
|
|
|
project, location, clusterName, "default-pool").Do()
|
|
|
|
case v1beta1:
|
|
|
|
parent := fmt.Sprintf("%s/nodePools/%s", containerClusterFullName(project, location, clusterName), "default-pool")
|
|
|
|
op, err = config.clientContainerBeta.Projects.Locations.Clusters.NodePools.Delete(parent).Do()
|
|
|
|
}
|
2018-03-23 00:22:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return errwrap.Wrapf("Error deleting default node pool: {{err}}", err)
|
|
|
|
}
|
2018-04-05 21:51:35 +00:00
|
|
|
err = containerSharedOperationWait(config, op, project, location, "removing default node pool", timeoutInMinutes, 3)
|
2018-03-23 00:22:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return errwrap.Wrapf("Error deleting default node pool: {{err}}", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2015-07-05 16:39:01 +00:00
|
|
|
return resourceContainerClusterRead(d, meta)
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceContainerClusterRead(d *schema.ResourceData, meta interface{}) error {
|
2018-04-05 21:51:35 +00:00
|
|
|
containerAPIVersion := getContainerApiVersion(d, ContainerClusterBaseApiVersion, ContainerClusterVersionedFeatures)
|
2015-07-05 16:39:01 +00:00
|
|
|
config := meta.(*Config)
|
|
|
|
|
2016-04-10 16:59:57 +00:00
|
|
|
project, err := getProject(d, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
location, err := getLocation(d, config)
|
2017-12-06 22:30:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2015-07-05 16:39:01 +00:00
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
cluster := &containerBeta.Cluster{}
|
2018-04-05 21:51:35 +00:00
|
|
|
var clust interface{}
|
2017-10-12 18:21:33 +00:00
|
|
|
err = resource.Retry(2*time.Minute, func() *resource.RetryError {
|
2018-04-05 21:51:35 +00:00
|
|
|
switch containerAPIVersion {
|
2018-03-07 01:44:05 +00:00
|
|
|
case v1:
|
2018-04-05 21:51:35 +00:00
|
|
|
clust, err = config.clientContainer.Projects.Zones.Clusters.Get(
|
|
|
|
project, location, d.Get("name").(string)).Do()
|
2018-03-07 01:44:05 +00:00
|
|
|
case v1beta1:
|
2018-04-05 21:51:35 +00:00
|
|
|
name := containerClusterFullName(project, location, d.Get("name").(string))
|
|
|
|
clust, err = config.clientContainerBeta.Projects.Locations.Clusters.Get(name).Do()
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return resource.NonRetryableError(err)
|
|
|
|
}
|
|
|
|
err = Convert(clust, cluster)
|
|
|
|
if err != nil {
|
|
|
|
return resource.NonRetryableError(err)
|
2017-10-12 18:21:33 +00:00
|
|
|
}
|
|
|
|
if cluster.Status != "RUNNING" {
|
|
|
|
return resource.RetryableError(fmt.Errorf("Cluster %q has status %q with message %q", d.Get("name"), cluster.Status, cluster.StatusMessage))
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
})
|
2015-07-05 16:39:01 +00:00
|
|
|
if err != nil {
|
2017-05-09 23:00:47 +00:00
|
|
|
return handleNotFoundError(err, d, fmt.Sprintf("Container Cluster %q", d.Get("name").(string)))
|
2015-07-05 16:39:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
d.Set("name", cluster.Name)
|
2017-11-27 20:40:07 +00:00
|
|
|
|
|
|
|
d.Set("network_policy", flattenNetworkPolicy(cluster.NetworkPolicy))
|
|
|
|
|
2015-07-05 16:39:01 +00:00
|
|
|
d.Set("zone", cluster.Zone)
|
2017-02-03 01:37:03 +00:00
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
locations := schema.NewSet(schema.HashString, convertStringArrToInterface(cluster.Locations))
|
|
|
|
locations.Remove(cluster.Zone) // Remove the original zone since we only store additional zones
|
2017-02-08 03:21:00 +00:00
|
|
|
d.Set("additional_zones", locations)
|
2017-02-07 01:21:34 +00:00
|
|
|
|
2015-07-05 16:39:01 +00:00
|
|
|
d.Set("endpoint", cluster.Endpoint)
|
|
|
|
|
2017-12-27 20:29:14 +00:00
|
|
|
if cluster.MaintenancePolicy != nil {
|
|
|
|
d.Set("maintenance_policy", flattenMaintenancePolicy(cluster.MaintenancePolicy))
|
2017-11-07 23:42:11 +00:00
|
|
|
}
|
|
|
|
|
2015-07-05 16:39:01 +00:00
|
|
|
masterAuth := []map[string]interface{}{
|
2017-06-28 08:34:20 +00:00
|
|
|
{
|
2015-07-05 16:39:01 +00:00
|
|
|
"username": cluster.MasterAuth.Username,
|
|
|
|
"password": cluster.MasterAuth.Password,
|
|
|
|
"client_certificate": cluster.MasterAuth.ClientCertificate,
|
|
|
|
"client_key": cluster.MasterAuth.ClientKey,
|
|
|
|
"cluster_ca_certificate": cluster.MasterAuth.ClusterCaCertificate,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
d.Set("master_auth", masterAuth)
|
|
|
|
|
2017-11-02 17:38:20 +00:00
|
|
|
if cluster.MasterAuthorizedNetworksConfig != nil {
|
|
|
|
d.Set("master_authorized_networks_config", flattenMasterAuthorizedNetworksConfig(cluster.MasterAuthorizedNetworksConfig))
|
|
|
|
}
|
|
|
|
|
2015-07-05 16:39:01 +00:00
|
|
|
d.Set("initial_node_count", cluster.InitialNodeCount)
|
2017-10-06 22:48:01 +00:00
|
|
|
d.Set("master_version", cluster.CurrentMasterVersion)
|
2015-07-05 16:39:01 +00:00
|
|
|
d.Set("node_version", cluster.CurrentNodeVersion)
|
|
|
|
d.Set("cluster_ipv4_cidr", cluster.ClusterIpv4Cidr)
|
|
|
|
d.Set("description", cluster.Description)
|
2017-10-31 23:38:18 +00:00
|
|
|
d.Set("enable_kubernetes_alpha", cluster.EnableKubernetesAlpha)
|
2017-07-31 18:09:05 +00:00
|
|
|
d.Set("enable_legacy_abac", cluster.LegacyAbac.Enabled)
|
2015-07-05 16:39:01 +00:00
|
|
|
d.Set("logging_service", cluster.LoggingService)
|
|
|
|
d.Set("monitoring_service", cluster.MonitoringService)
|
2017-09-07 17:31:58 +00:00
|
|
|
d.Set("network", cluster.Network)
|
2016-03-25 23:29:56 +00:00
|
|
|
d.Set("subnetwork", cluster.Subnetwork)
|
2018-03-15 20:28:30 +00:00
|
|
|
if err := d.Set("node_config", flattenNodeConfig(cluster.NodeConfig)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-11-28 00:32:20 +00:00
|
|
|
d.Set("project", project)
|
2017-10-20 16:47:07 +00:00
|
|
|
if cluster.AddonsConfig != nil {
|
|
|
|
d.Set("addons_config", flattenClusterAddonsConfig(cluster.AddonsConfig))
|
|
|
|
}
|
2017-08-18 00:51:58 +00:00
|
|
|
nps, err := flattenClusterNodePools(d, config, cluster.NodePools)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
d.Set("node_pool", nps)
|
2017-03-07 05:14:32 +00:00
|
|
|
|
2017-11-27 23:15:03 +00:00
|
|
|
if cluster.IpAllocationPolicy != nil {
|
|
|
|
if err := d.Set("ip_allocation_policy", flattenIPAllocationPolicy(cluster.IpAllocationPolicy)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-03-22 23:33:11 +00:00
|
|
|
if igUrls, err := getInstanceGroupUrlsFromManagerUrls(config, cluster.InstanceGroupUrls); err != nil {
|
|
|
|
return err
|
|
|
|
} else {
|
|
|
|
d.Set("instance_group_urls", igUrls)
|
2017-03-07 05:14:32 +00:00
|
|
|
}
|
2015-07-05 16:39:01 +00:00
|
|
|
|
2018-03-14 21:00:31 +00:00
|
|
|
if cluster.PodSecurityPolicyConfig != nil {
|
|
|
|
if err := d.Set("pod_security_policy_config", flattenPodSecurityPolicyConfig(cluster.PodSecurityPolicyConfig)); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-30 17:10:25 +00:00
|
|
|
d.Set("private_cluster", cluster.PrivateCluster)
|
|
|
|
d.Set("master_ipv4_cidr_block", cluster.MasterIpv4CidrBlock)
|
|
|
|
|
2015-07-05 16:39:01 +00:00
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceContainerClusterUpdate(d *schema.ResourceData, meta interface{}) error {
|
2018-04-05 21:51:35 +00:00
|
|
|
containerAPIVersion := getContainerApiVersion(d, ContainerClusterBaseApiVersion, ContainerClusterVersionedFeatures)
|
2015-07-05 16:39:01 +00:00
|
|
|
config := meta.(*Config)
|
|
|
|
|
2016-04-10 16:59:57 +00:00
|
|
|
project, err := getProject(d, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
location, err := getLocation(d, config)
|
2017-12-06 22:30:04 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2018-04-05 21:51:35 +00:00
|
|
|
|
2015-07-05 16:39:01 +00:00
|
|
|
clusterName := d.Get("name").(string)
|
2017-06-28 08:22:31 +00:00
|
|
|
timeoutInMinutes := int(d.Timeout(schema.TimeoutUpdate).Minutes())
|
2015-07-05 16:39:01 +00:00
|
|
|
|
2017-07-05 23:00:49 +00:00
|
|
|
d.Partial(true)
|
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
lockKey := containerClusterMutexKey(project, location, clusterName)
|
|
|
|
|
|
|
|
updateFunc := func(req *container.UpdateClusterRequest, updateDescription string) func() error {
|
|
|
|
return func() error {
|
|
|
|
var err error
|
|
|
|
var op interface{}
|
|
|
|
switch containerAPIVersion {
|
|
|
|
case v1:
|
|
|
|
op, err = config.clientContainer.Projects.Zones.Clusters.Update(project, location, clusterName, req).Do()
|
|
|
|
case v1beta1:
|
|
|
|
reqV1Beta := &containerBeta.UpdateClusterRequest{}
|
|
|
|
err = Convert(req, reqV1Beta)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
name := containerClusterFullName(project, location, clusterName)
|
|
|
|
op, err = config.clientContainerBeta.Projects.Locations.Clusters.Update(name, reqV1Beta).Do()
|
|
|
|
}
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Wait until it's updated
|
|
|
|
return containerSharedOperationWait(config, op, project, location, updateDescription, timeoutInMinutes, 2)
|
|
|
|
}
|
|
|
|
}
|
2018-02-05 17:45:53 +00:00
|
|
|
|
2018-03-23 18:27:37 +00:00
|
|
|
// The ClusterUpdate object that we use for most of these updates only allows updating one field at a time,
|
|
|
|
// so we have to make separate calls for each field that we want to update. The order here is fairly arbitrary-
|
|
|
|
// if the order of updating fields does matter, it is called out explicitly.
|
2017-11-02 17:38:20 +00:00
|
|
|
if d.HasChange("master_authorized_networks_config") {
|
|
|
|
c := d.Get("master_authorized_networks_config")
|
2018-03-07 01:44:05 +00:00
|
|
|
conf := &container.MasterAuthorizedNetworksConfig{}
|
|
|
|
err := Convert(expandMasterAuthorizedNetworksConfig(c), conf)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-11-02 17:38:20 +00:00
|
|
|
req := &container.UpdateClusterRequest{
|
|
|
|
Update: &container.ClusterUpdate{
|
2018-03-07 01:44:05 +00:00
|
|
|
DesiredMasterAuthorizedNetworksConfig: conf,
|
2017-11-02 17:38:20 +00:00
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
updateF := updateFunc(req, "updating GKE cluster master authorized networks")
|
2018-02-05 17:45:53 +00:00
|
|
|
if err := lockedCall(lockKey, updateF); err != nil {
|
|
|
|
return err
|
2017-11-02 17:38:20 +00:00
|
|
|
}
|
|
|
|
log.Printf("[INFO] GKE cluster %s master authorized networks config has been updated", d.Id())
|
|
|
|
|
|
|
|
d.SetPartial("master_authorized_networks_config")
|
|
|
|
}
|
|
|
|
|
2017-10-06 22:48:01 +00:00
|
|
|
// The master must be updated before the nodes
|
2017-10-12 18:21:33 +00:00
|
|
|
if d.HasChange("min_master_version") {
|
|
|
|
desiredMasterVersion := d.Get("min_master_version").(string)
|
|
|
|
currentMasterVersion := d.Get("master_version").(string)
|
|
|
|
des, err := version.NewVersion(desiredMasterVersion)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
2017-07-05 23:00:49 +00:00
|
|
|
}
|
2017-10-12 18:21:33 +00:00
|
|
|
cur, err := version.NewVersion(currentMasterVersion)
|
2017-07-05 23:00:49 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2017-10-12 18:21:33 +00:00
|
|
|
// Only upgrade the master if the current version is lower than the desired version
|
|
|
|
if cur.LessThan(des) {
|
|
|
|
req := &container.UpdateClusterRequest{
|
|
|
|
Update: &container.ClusterUpdate{
|
|
|
|
DesiredMasterVersion: desiredMasterVersion,
|
|
|
|
},
|
|
|
|
}
|
2017-08-08 18:31:12 +00:00
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
updateF := updateFunc(req, "updating GKE master version")
|
2018-02-05 17:45:53 +00:00
|
|
|
// Call update serially.
|
|
|
|
if err := lockedCall(lockKey, updateF); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
log.Printf("[INFO] GKE cluster %s: master has been updated to %s", d.Id(), desiredMasterVersion)
|
2017-10-12 18:21:33 +00:00
|
|
|
}
|
|
|
|
d.SetPartial("min_master_version")
|
2017-10-06 22:48:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if d.HasChange("node_version") {
|
|
|
|
desiredNodeVersion := d.Get("node_version").(string)
|
|
|
|
req := &container.UpdateClusterRequest{
|
2017-08-08 18:31:12 +00:00
|
|
|
Update: &container.ClusterUpdate{
|
|
|
|
DesiredNodeVersion: desiredNodeVersion,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
updateF := updateFunc(req, "updating GKE node version")
|
2018-02-05 17:45:53 +00:00
|
|
|
// Call update serially.
|
|
|
|
if err := lockedCall(lockKey, updateF); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-08-08 18:31:12 +00:00
|
|
|
log.Printf("[INFO] GKE cluster %s: nodes have been updated to %s", d.Id(),
|
2017-07-05 23:00:49 +00:00
|
|
|
desiredNodeVersion)
|
|
|
|
|
|
|
|
d.SetPartial("node_version")
|
2015-07-05 16:39:01 +00:00
|
|
|
}
|
|
|
|
|
2017-10-20 16:47:07 +00:00
|
|
|
if d.HasChange("addons_config") {
|
|
|
|
if ac, ok := d.GetOk("addons_config"); ok {
|
2018-03-07 01:44:05 +00:00
|
|
|
conf := &container.AddonsConfig{}
|
|
|
|
err := Convert(expandClusterAddonsConfig(ac), conf)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-10-20 16:47:07 +00:00
|
|
|
req := &container.UpdateClusterRequest{
|
|
|
|
Update: &container.ClusterUpdate{
|
2018-03-07 01:44:05 +00:00
|
|
|
DesiredAddonsConfig: conf,
|
2017-10-20 16:47:07 +00:00
|
|
|
},
|
|
|
|
}
|
2018-02-05 17:45:53 +00:00
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
updateF := updateFunc(req, "updating GKE cluster addons")
|
2018-02-05 17:45:53 +00:00
|
|
|
// Call update serially.
|
|
|
|
if err := lockedCall(lockKey, updateF); err != nil {
|
|
|
|
return err
|
2017-10-20 16:47:07 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[INFO] GKE cluster %s addons have been updated", d.Id())
|
|
|
|
|
|
|
|
d.SetPartial("addons_config")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-12-27 20:29:14 +00:00
|
|
|
if d.HasChange("maintenance_policy") {
|
|
|
|
var req *container.SetMaintenancePolicyRequest
|
|
|
|
if mp, ok := d.GetOk("maintenance_policy"); ok {
|
2018-03-07 01:44:05 +00:00
|
|
|
pol := &container.MaintenancePolicy{}
|
|
|
|
err := Convert(expandMaintenancePolicy(mp), pol)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-12-27 20:29:14 +00:00
|
|
|
req = &container.SetMaintenancePolicyRequest{
|
2018-03-07 01:44:05 +00:00
|
|
|
MaintenancePolicy: pol,
|
2017-12-27 20:29:14 +00:00
|
|
|
}
|
|
|
|
} else {
|
|
|
|
req = &container.SetMaintenancePolicyRequest{
|
|
|
|
NullFields: []string{"MaintenancePolicy"},
|
|
|
|
}
|
|
|
|
}
|
2018-02-05 17:45:53 +00:00
|
|
|
|
|
|
|
updateF := func() error {
|
2018-04-05 21:51:35 +00:00
|
|
|
var op interface{}
|
|
|
|
switch containerAPIVersion {
|
|
|
|
case v1:
|
|
|
|
op, err = config.clientContainer.Projects.Zones.Clusters.SetMaintenancePolicy(
|
|
|
|
project, location, clusterName, req).Do()
|
|
|
|
case v1beta1:
|
|
|
|
reqV1Beta := &containerBeta.SetMaintenancePolicyRequest{}
|
|
|
|
err = Convert(req, reqV1Beta)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
name := containerClusterFullName(project, location, clusterName)
|
|
|
|
op, err = config.clientContainerBeta.Projects.Locations.Clusters.SetMaintenancePolicy(name, reqV1Beta).Do()
|
|
|
|
}
|
|
|
|
|
2018-02-05 17:45:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait until it's updated
|
2018-04-05 21:51:35 +00:00
|
|
|
return containerSharedOperationWait(config, op, project, location, "updating GKE cluster maintenance policy", timeoutInMinutes, 2)
|
2017-12-27 20:29:14 +00:00
|
|
|
}
|
|
|
|
|
2018-02-05 17:45:53 +00:00
|
|
|
// Call update serially.
|
|
|
|
if err := lockedCall(lockKey, updateF); err != nil {
|
|
|
|
return err
|
2017-12-27 20:29:14 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[INFO] GKE cluster %s maintenance policy has been updated", d.Id())
|
|
|
|
|
|
|
|
d.SetPartial("maintenance_policy")
|
|
|
|
}
|
|
|
|
|
2017-07-05 23:00:49 +00:00
|
|
|
if d.HasChange("additional_zones") {
|
|
|
|
azSet := d.Get("additional_zones").(*schema.Set)
|
2018-04-05 21:51:35 +00:00
|
|
|
if azSet.Contains(location) {
|
|
|
|
return fmt.Errorf("additional_zones should not contain the original 'zone'")
|
2017-07-05 23:00:49 +00:00
|
|
|
}
|
2018-04-05 21:51:35 +00:00
|
|
|
azSet.Add(location)
|
2017-07-05 23:00:49 +00:00
|
|
|
req := &container.UpdateClusterRequest{
|
|
|
|
Update: &container.ClusterUpdate{
|
2018-04-05 21:51:35 +00:00
|
|
|
DesiredLocations: convertStringSet(azSet),
|
2017-07-05 23:00:49 +00:00
|
|
|
},
|
|
|
|
}
|
2018-02-05 17:45:53 +00:00
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
updateF := updateFunc(req, "updating GKE cluster locations")
|
2018-02-05 17:45:53 +00:00
|
|
|
// Call update serially.
|
|
|
|
if err := lockedCall(lockKey, updateF); err != nil {
|
|
|
|
return err
|
2017-07-05 23:00:49 +00:00
|
|
|
}
|
2017-06-28 08:22:31 +00:00
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
log.Printf("[INFO] GKE cluster %s locations have been updated to %v", d.Id(), azSet.List())
|
2017-07-31 18:09:05 +00:00
|
|
|
|
|
|
|
d.SetPartial("additional_zones")
|
|
|
|
}
|
|
|
|
|
|
|
|
if d.HasChange("enable_legacy_abac") {
|
|
|
|
enabled := d.Get("enable_legacy_abac").(bool)
|
|
|
|
req := &container.SetLegacyAbacRequest{
|
|
|
|
Enabled: enabled,
|
|
|
|
ForceSendFields: []string{"Enabled"},
|
|
|
|
}
|
2018-02-05 17:45:53 +00:00
|
|
|
|
|
|
|
updateF := func() error {
|
2018-04-05 21:51:35 +00:00
|
|
|
log.Println("[DEBUG] updating enable_legacy_abac")
|
|
|
|
var op interface{}
|
|
|
|
switch containerAPIVersion {
|
|
|
|
case v1:
|
|
|
|
op, err = config.clientContainer.Projects.Zones.Clusters.LegacyAbac(project, location, clusterName, req).Do()
|
|
|
|
case v1beta1:
|
|
|
|
reqV1Beta := &containerBeta.SetLegacyAbacRequest{}
|
|
|
|
err = Convert(req, reqV1Beta)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
name := containerClusterFullName(project, location, clusterName)
|
|
|
|
op, err = config.clientContainerBeta.Projects.Locations.Clusters.SetLegacyAbac(name, reqV1Beta).Do()
|
|
|
|
}
|
2018-02-05 17:45:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait until it's updated
|
2018-04-05 21:51:35 +00:00
|
|
|
err = containerSharedOperationWait(config, op, project, location, "updating GKE legacy ABAC", timeoutInMinutes, 2)
|
|
|
|
log.Println("[DEBUG] done updating enable_legacy_abac")
|
2017-07-31 18:09:05 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-02-05 17:45:53 +00:00
|
|
|
// Call update serially.
|
|
|
|
if err := lockedCall(lockKey, updateF); err != nil {
|
|
|
|
return err
|
2017-07-31 18:09:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[INFO] GKE cluster %s legacy ABAC has been updated to %v", d.Id(), enabled)
|
|
|
|
|
|
|
|
d.SetPartial("enable_legacy_abac")
|
2015-07-05 16:39:01 +00:00
|
|
|
}
|
|
|
|
|
2017-10-20 16:46:21 +00:00
|
|
|
if d.HasChange("monitoring_service") {
|
|
|
|
desiredMonitoringService := d.Get("monitoring_service").(string)
|
|
|
|
|
|
|
|
req := &container.UpdateClusterRequest{
|
|
|
|
Update: &container.ClusterUpdate{
|
|
|
|
DesiredMonitoringService: desiredMonitoringService,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
updateF := updateFunc(req, "updating GKE cluster monitoring service")
|
2018-02-05 17:45:53 +00:00
|
|
|
// Call update serially.
|
|
|
|
if err := lockedCall(lockKey, updateF); err != nil {
|
|
|
|
return err
|
2017-10-20 16:46:21 +00:00
|
|
|
}
|
|
|
|
log.Printf("[INFO] Monitoring service for GKE cluster %s has been updated to %s", d.Id(),
|
|
|
|
desiredMonitoringService)
|
|
|
|
|
|
|
|
d.SetPartial("monitoring_service")
|
|
|
|
}
|
|
|
|
|
2017-11-27 20:40:07 +00:00
|
|
|
if d.HasChange("network_policy") {
|
2018-03-07 01:44:05 +00:00
|
|
|
np := d.Get("network_policy")
|
2017-11-27 20:40:07 +00:00
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
pol := &container.NetworkPolicy{}
|
|
|
|
err := Convert(expandNetworkPolicy(np), pol)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
2017-11-27 20:40:07 +00:00
|
|
|
req := &container.SetNetworkPolicyRequest{
|
2018-03-07 01:44:05 +00:00
|
|
|
NetworkPolicy: pol,
|
2017-11-27 20:40:07 +00:00
|
|
|
}
|
2018-02-05 17:45:53 +00:00
|
|
|
|
|
|
|
updateF := func() error {
|
2018-04-05 21:51:35 +00:00
|
|
|
log.Println("[DEBUG] updating network_policy")
|
|
|
|
var op interface{}
|
|
|
|
switch containerAPIVersion {
|
|
|
|
case v1:
|
|
|
|
op, err = config.clientContainer.Projects.Zones.Clusters.SetNetworkPolicy(
|
|
|
|
project, location, clusterName, req).Do()
|
|
|
|
case v1beta1:
|
|
|
|
reqV1Beta := &containerBeta.SetNetworkPolicyRequest{}
|
|
|
|
err = Convert(req, reqV1Beta)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
name := containerClusterFullName(project, location, clusterName)
|
|
|
|
op, err = config.clientContainerBeta.Projects.Locations.Clusters.SetNetworkPolicy(name, reqV1Beta).Do()
|
|
|
|
}
|
2018-02-05 17:45:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait until it's updated
|
2018-04-05 21:51:35 +00:00
|
|
|
err = containerSharedOperationWait(config, op, project, location, "updating GKE cluster network policy", timeoutInMinutes, 2)
|
|
|
|
log.Println("[DEBUG] done updating network_policy")
|
2017-11-27 20:40:07 +00:00
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-02-05 17:45:53 +00:00
|
|
|
// Call update serially.
|
|
|
|
if err := lockedCall(lockKey, updateF); err != nil {
|
|
|
|
return err
|
2017-11-27 20:40:07 +00:00
|
|
|
}
|
2018-02-05 17:45:53 +00:00
|
|
|
|
2017-11-27 20:40:07 +00:00
|
|
|
log.Printf("[INFO] Network policy for GKE cluster %s has been updated", d.Id())
|
|
|
|
|
|
|
|
d.SetPartial("network_policy")
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2017-08-18 00:51:58 +00:00
|
|
|
if n, ok := d.GetOk("node_pool.#"); ok {
|
|
|
|
for i := 0; i < n.(int); i++ {
|
2017-10-04 00:09:34 +00:00
|
|
|
if err := nodePoolUpdate(d, meta, clusterName, fmt.Sprintf("node_pool.%d.", i), timeoutInMinutes); err != nil {
|
|
|
|
return err
|
2017-08-18 00:51:58 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
d.SetPartial("node_pool")
|
|
|
|
}
|
|
|
|
|
2017-08-18 22:29:51 +00:00
|
|
|
if d.HasChange("logging_service") {
|
|
|
|
logging := d.Get("logging_service").(string)
|
|
|
|
|
|
|
|
req := &container.SetLoggingServiceRequest{
|
|
|
|
LoggingService: logging,
|
|
|
|
}
|
2018-02-05 17:45:53 +00:00
|
|
|
updateF := func() error {
|
2018-04-05 21:51:35 +00:00
|
|
|
var op interface{}
|
|
|
|
switch containerAPIVersion {
|
|
|
|
case v1:
|
|
|
|
op, err = config.clientContainer.Projects.Zones.Clusters.Logging(
|
|
|
|
project, location, clusterName, req).Do()
|
|
|
|
case v1beta1:
|
|
|
|
reqV1Beta := &containerBeta.SetLoggingServiceRequest{}
|
|
|
|
err = Convert(req, reqV1Beta)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
name := containerClusterFullName(project, location, clusterName)
|
|
|
|
op, err = config.clientContainerBeta.Projects.Locations.Clusters.SetLogging(name, reqV1Beta).Do()
|
|
|
|
}
|
2018-02-05 17:45:53 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait until it's updated
|
2018-04-05 21:51:35 +00:00
|
|
|
return containerSharedOperationWait(config, op, project, location, "updating GKE logging service", timeoutInMinutes, 2)
|
2017-08-18 22:29:51 +00:00
|
|
|
}
|
|
|
|
|
2018-02-05 17:45:53 +00:00
|
|
|
// Call update serially.
|
|
|
|
if err := lockedCall(lockKey, updateF); err != nil {
|
|
|
|
return err
|
2017-08-18 22:29:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[INFO] GKE cluster %s: logging service has been updated to %s", d.Id(),
|
|
|
|
logging)
|
|
|
|
d.SetPartial("logging_service")
|
|
|
|
}
|
|
|
|
|
2018-03-23 18:27:37 +00:00
|
|
|
if d.HasChange("pod_security_policy_config") {
|
|
|
|
c := d.Get("pod_security_policy_config")
|
|
|
|
req := &containerBeta.UpdateClusterRequest{
|
|
|
|
Update: &containerBeta.ClusterUpdate{
|
|
|
|
DesiredPodSecurityPolicyConfig: expandPodSecurityPolicyConfig(c),
|
|
|
|
},
|
|
|
|
}
|
|
|
|
|
|
|
|
updateF := func() error {
|
2018-04-05 21:51:35 +00:00
|
|
|
op, err := config.clientContainerBeta.Projects.Zones.Clusters.Update(project, location, clusterName, req).Do()
|
2018-03-23 18:27:37 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
// Wait until it's updated
|
2018-04-05 21:51:35 +00:00
|
|
|
return containerSharedOperationWait(config, op, project, location, "updating GKE cluster pod security policy config", timeoutInMinutes, 2)
|
2018-03-23 18:27:37 +00:00
|
|
|
}
|
|
|
|
if err := lockedCall(lockKey, updateF); err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
log.Printf("[INFO] GKE cluster %s pod security policy config has been updated", d.Id())
|
|
|
|
|
|
|
|
d.SetPartial("pod_security_policy_config")
|
|
|
|
}
|
|
|
|
|
2018-03-23 00:22:44 +00:00
|
|
|
if d.HasChange("remove_default_node_pool") && d.Get("remove_default_node_pool").(bool) {
|
2018-04-05 21:51:35 +00:00
|
|
|
var op interface{}
|
|
|
|
switch containerAPIVersion {
|
|
|
|
case v1:
|
|
|
|
op, err = config.clientContainer.Projects.Zones.Clusters.NodePools.Delete(
|
|
|
|
project, location, clusterName, "default-pool").Do()
|
|
|
|
case v1beta1:
|
|
|
|
name := fmt.Sprintf("%s/nodePools/%s", containerClusterFullName(project, location, clusterName), "default-pool")
|
|
|
|
op, err = config.clientContainerBeta.Projects.Locations.Clusters.NodePools.Delete(name).Do()
|
|
|
|
}
|
2018-03-23 00:22:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return errwrap.Wrapf("Error deleting default node pool: {{err}}", err)
|
|
|
|
}
|
2018-04-05 21:51:35 +00:00
|
|
|
err = containerSharedOperationWait(config, op, project, location, "removing default node pool", timeoutInMinutes, 3)
|
2018-03-23 00:22:44 +00:00
|
|
|
if err != nil {
|
|
|
|
return errwrap.Wrapf("Error deleting default node pool: {{err}}", err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-07-05 23:00:49 +00:00
|
|
|
d.Partial(false)
|
2015-07-05 16:39:01 +00:00
|
|
|
|
|
|
|
return resourceContainerClusterRead(d, meta)
|
|
|
|
}
|
|
|
|
|
|
|
|
func resourceContainerClusterDelete(d *schema.ResourceData, meta interface{}) error {
|
2018-04-05 21:51:35 +00:00
|
|
|
containerAPIVersion := getContainerApiVersion(d, ContainerClusterBaseApiVersion, ContainerClusterVersionedFeatures)
|
2015-07-05 16:39:01 +00:00
|
|
|
config := meta.(*Config)
|
|
|
|
|
2016-04-10 16:59:57 +00:00
|
|
|
project, err := getProject(d, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
var location string
|
|
|
|
locations := []string{}
|
|
|
|
if regionName, isRegionalCluster := d.GetOk("region"); !isRegionalCluster {
|
|
|
|
location, err = getZone(d, config)
|
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
locations = append(locations, location)
|
|
|
|
} else {
|
|
|
|
location = regionName.(string)
|
2017-12-06 22:30:04 +00:00
|
|
|
}
|
2018-04-05 21:51:35 +00:00
|
|
|
|
2015-07-05 16:39:01 +00:00
|
|
|
clusterName := d.Get("name").(string)
|
2017-06-28 08:22:31 +00:00
|
|
|
timeoutInMinutes := int(d.Timeout(schema.TimeoutDelete).Minutes())
|
2015-07-05 16:39:01 +00:00
|
|
|
|
|
|
|
log.Printf("[DEBUG] Deleting GKE cluster %s", d.Get("name").(string))
|
2018-04-05 21:51:35 +00:00
|
|
|
mutexKV.Lock(containerClusterMutexKey(project, location, clusterName))
|
|
|
|
defer mutexKV.Unlock(containerClusterMutexKey(project, location, clusterName))
|
|
|
|
|
|
|
|
var op interface{}
|
|
|
|
switch containerAPIVersion {
|
|
|
|
case v1:
|
|
|
|
op, err = config.clientContainer.Projects.Zones.Clusters.Delete(project, location, clusterName).Do()
|
|
|
|
case v1beta1:
|
|
|
|
name := containerClusterFullName(project, location, clusterName)
|
|
|
|
op, err = config.clientContainerBeta.Projects.Locations.Clusters.Delete(name).Do()
|
|
|
|
}
|
2015-07-05 16:39:01 +00:00
|
|
|
if err != nil {
|
|
|
|
return err
|
|
|
|
}
|
|
|
|
|
|
|
|
// Wait until it's deleted
|
2018-04-05 21:51:35 +00:00
|
|
|
waitErr := containerSharedOperationWait(config, op, project, location, "deleting GKE cluster", timeoutInMinutes, 3)
|
2017-03-06 22:59:24 +00:00
|
|
|
if waitErr != nil {
|
|
|
|
return waitErr
|
2015-07-05 16:39:01 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
log.Printf("[INFO] GKE cluster %s has been deleted", d.Id())
|
|
|
|
|
|
|
|
d.SetId("")
|
|
|
|
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2017-03-22 23:33:11 +00:00
|
|
|
// container engine's API currently mistakenly returns the instance group manager's
|
|
|
|
// URL instead of the instance group's URL in its responses. This shim detects that
|
|
|
|
// error, and corrects it, by fetching the instance group manager URL and retrieving
|
|
|
|
// the instance group manager, then using that to look up the instance group URL, which
|
|
|
|
// is then substituted.
|
|
|
|
//
|
|
|
|
// This should be removed when the API response is fixed.
|
|
|
|
func getInstanceGroupUrlsFromManagerUrls(config *Config, igmUrls []string) ([]string, error) {
|
|
|
|
instanceGroupURLs := make([]string, 0, len(igmUrls))
|
|
|
|
for _, u := range igmUrls {
|
|
|
|
if !instanceGroupManagerURL.MatchString(u) {
|
|
|
|
instanceGroupURLs = append(instanceGroupURLs, u)
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
matches := instanceGroupManagerURL.FindStringSubmatch(u)
|
|
|
|
instanceGroupManager, err := config.clientCompute.InstanceGroupManagers.Get(matches[1], matches[2], matches[3]).Do()
|
|
|
|
if err != nil {
|
|
|
|
return nil, fmt.Errorf("Error reading instance group manager returned as an instance group URL: %s", err)
|
|
|
|
}
|
|
|
|
instanceGroupURLs = append(instanceGroupURLs, instanceGroupManager.InstanceGroup)
|
|
|
|
}
|
|
|
|
return instanceGroupURLs, nil
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
func expandClusterAddonsConfig(configured interface{}) *containerBeta.AddonsConfig {
|
2017-10-20 16:47:07 +00:00
|
|
|
config := configured.([]interface{})[0].(map[string]interface{})
|
2018-03-07 01:44:05 +00:00
|
|
|
ac := &containerBeta.AddonsConfig{}
|
2017-10-20 16:47:07 +00:00
|
|
|
|
|
|
|
if v, ok := config["http_load_balancing"]; ok && len(v.([]interface{})) > 0 {
|
|
|
|
addon := v.([]interface{})[0].(map[string]interface{})
|
2018-03-07 01:44:05 +00:00
|
|
|
ac.HttpLoadBalancing = &containerBeta.HttpLoadBalancing{
|
2017-10-20 16:47:07 +00:00
|
|
|
Disabled: addon["disabled"].(bool),
|
|
|
|
ForceSendFields: []string{"Disabled"},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := config["horizontal_pod_autoscaling"]; ok && len(v.([]interface{})) > 0 {
|
|
|
|
addon := v.([]interface{})[0].(map[string]interface{})
|
2018-03-07 01:44:05 +00:00
|
|
|
ac.HorizontalPodAutoscaling = &containerBeta.HorizontalPodAutoscaling{
|
2017-10-20 16:47:07 +00:00
|
|
|
Disabled: addon["disabled"].(bool),
|
|
|
|
ForceSendFields: []string{"Disabled"},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := config["kubernetes_dashboard"]; ok && len(v.([]interface{})) > 0 {
|
|
|
|
addon := v.([]interface{})[0].(map[string]interface{})
|
2018-03-07 01:44:05 +00:00
|
|
|
ac.KubernetesDashboard = &containerBeta.KubernetesDashboard{
|
2017-10-20 16:47:07 +00:00
|
|
|
Disabled: addon["disabled"].(bool),
|
|
|
|
ForceSendFields: []string{"Disabled"},
|
|
|
|
}
|
|
|
|
}
|
2018-03-15 21:50:24 +00:00
|
|
|
|
|
|
|
if v, ok := config["network_policy_config"]; ok && len(v.([]interface{})) > 0 {
|
|
|
|
addon := v.([]interface{})[0].(map[string]interface{})
|
|
|
|
ac.NetworkPolicyConfig = &containerBeta.NetworkPolicyConfig{
|
|
|
|
Disabled: addon["disabled"].(bool),
|
|
|
|
ForceSendFields: []string{"Disabled"},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-20 16:47:07 +00:00
|
|
|
return ac
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
func expandIPAllocationPolicy(configured interface{}) (*containerBeta.IPAllocationPolicy, error) {
|
|
|
|
ap := &containerBeta.IPAllocationPolicy{}
|
2018-02-09 21:55:38 +00:00
|
|
|
l := configured.([]interface{})
|
|
|
|
if len(l) > 0 {
|
|
|
|
if config, ok := l[0].(map[string]interface{}); ok {
|
2017-11-27 23:15:03 +00:00
|
|
|
ap.UseIpAliases = true
|
|
|
|
if v, ok := config["cluster_secondary_range_name"]; ok {
|
|
|
|
ap.ClusterSecondaryRangeName = v.(string)
|
|
|
|
}
|
|
|
|
|
|
|
|
if v, ok := config["services_secondary_range_name"]; ok {
|
|
|
|
ap.ServicesSecondaryRangeName = v.(string)
|
|
|
|
}
|
2018-02-09 21:55:38 +00:00
|
|
|
} else {
|
2018-04-05 21:51:35 +00:00
|
|
|
return nil, fmt.Errorf("clusters using IP aliases must specify secondary ranges")
|
2017-11-27 23:15:03 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return ap, nil
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
func expandMaintenancePolicy(configured interface{}) *containerBeta.MaintenancePolicy {
|
|
|
|
result := &containerBeta.MaintenancePolicy{}
|
2017-12-27 20:29:14 +00:00
|
|
|
if len(configured.([]interface{})) > 0 {
|
|
|
|
maintenancePolicy := configured.([]interface{})[0].(map[string]interface{})
|
|
|
|
dailyMaintenanceWindow := maintenancePolicy["daily_maintenance_window"].([]interface{})[0].(map[string]interface{})
|
|
|
|
startTime := dailyMaintenanceWindow["start_time"].(string)
|
2018-03-07 01:44:05 +00:00
|
|
|
result.Window = &containerBeta.MaintenanceWindow{
|
|
|
|
DailyMaintenanceWindow: &containerBeta.DailyMaintenanceWindow{
|
2017-12-27 20:29:14 +00:00
|
|
|
StartTime: startTime,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
func expandMasterAuthorizedNetworksConfig(configured interface{}) *containerBeta.MasterAuthorizedNetworksConfig {
|
|
|
|
result := &containerBeta.MasterAuthorizedNetworksConfig{}
|
2017-11-02 17:38:20 +00:00
|
|
|
if len(configured.([]interface{})) > 0 {
|
|
|
|
result.Enabled = true
|
|
|
|
config := configured.([]interface{})[0].(map[string]interface{})
|
|
|
|
if _, ok := config["cidr_blocks"]; ok {
|
|
|
|
cidrBlocks := config["cidr_blocks"].(*schema.Set).List()
|
2018-03-07 01:44:05 +00:00
|
|
|
result.CidrBlocks = make([]*containerBeta.CidrBlock, 0)
|
2017-11-02 17:38:20 +00:00
|
|
|
for _, v := range cidrBlocks {
|
|
|
|
cidrBlock := v.(map[string]interface{})
|
2018-03-07 01:44:05 +00:00
|
|
|
result.CidrBlocks = append(result.CidrBlocks, &containerBeta.CidrBlock{
|
2017-11-02 17:38:20 +00:00
|
|
|
CidrBlock: cidrBlock["cidr_block"].(string),
|
|
|
|
DisplayName: cidrBlock["display_name"].(string),
|
|
|
|
})
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
func expandNetworkPolicy(configured interface{}) *containerBeta.NetworkPolicy {
|
|
|
|
result := &containerBeta.NetworkPolicy{}
|
2017-11-27 20:40:07 +00:00
|
|
|
if configured != nil && len(configured.([]interface{})) > 0 {
|
|
|
|
config := configured.([]interface{})[0].(map[string]interface{})
|
|
|
|
if enabled, ok := config["enabled"]; ok && enabled.(bool) {
|
|
|
|
result.Enabled = true
|
|
|
|
if provider, ok := config["provider"]; ok {
|
|
|
|
result.Provider = provider.(string)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2018-03-14 21:00:31 +00:00
|
|
|
func expandPodSecurityPolicyConfig(configured interface{}) *containerBeta.PodSecurityPolicyConfig {
|
|
|
|
result := &containerBeta.PodSecurityPolicyConfig{}
|
|
|
|
if len(configured.([]interface{})) > 0 {
|
|
|
|
config := configured.([]interface{})[0].(map[string]interface{})
|
|
|
|
result.Enabled = config["enabled"].(bool)
|
|
|
|
result.ForceSendFields = []string{"Enabled"}
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
func flattenNetworkPolicy(c *containerBeta.NetworkPolicy) []map[string]interface{} {
|
2017-11-27 20:40:07 +00:00
|
|
|
result := []map[string]interface{}{}
|
|
|
|
if c != nil {
|
|
|
|
result = append(result, map[string]interface{}{
|
|
|
|
"enabled": c.Enabled,
|
|
|
|
"provider": c.Provider,
|
|
|
|
})
|
|
|
|
}
|
|
|
|
return result
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
func flattenClusterAddonsConfig(c *containerBeta.AddonsConfig) []map[string]interface{} {
|
2017-10-20 16:47:07 +00:00
|
|
|
result := make(map[string]interface{})
|
|
|
|
if c.HorizontalPodAutoscaling != nil {
|
|
|
|
result["horizontal_pod_autoscaling"] = []map[string]interface{}{
|
|
|
|
{
|
|
|
|
"disabled": c.HorizontalPodAutoscaling.Disabled,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if c.HttpLoadBalancing != nil {
|
|
|
|
result["http_load_balancing"] = []map[string]interface{}{
|
|
|
|
{
|
|
|
|
"disabled": c.HttpLoadBalancing.Disabled,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if c.KubernetesDashboard != nil {
|
|
|
|
result["kubernetes_dashboard"] = []map[string]interface{}{
|
|
|
|
{
|
|
|
|
"disabled": c.KubernetesDashboard.Disabled,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
2018-03-15 21:50:24 +00:00
|
|
|
if c.NetworkPolicyConfig != nil {
|
|
|
|
result["network_policy_config"] = []map[string]interface{}{
|
|
|
|
{
|
|
|
|
"disabled": c.NetworkPolicyConfig.Disabled,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-20 16:47:07 +00:00
|
|
|
return []map[string]interface{}{result}
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
func flattenClusterNodePools(d *schema.ResourceData, config *Config, c []*containerBeta.NodePool) ([]map[string]interface{}, error) {
|
2017-08-18 00:51:58 +00:00
|
|
|
nodePools := make([]map[string]interface{}, 0, len(c))
|
2017-04-12 19:57:53 +00:00
|
|
|
|
|
|
|
for i, np := range c {
|
2017-10-04 00:09:34 +00:00
|
|
|
nodePool, err := flattenNodePool(d, config, np, fmt.Sprintf("node_pool.%d.", i))
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
2017-04-12 19:57:53 +00:00
|
|
|
}
|
|
|
|
nodePools = append(nodePools, nodePool)
|
|
|
|
}
|
|
|
|
|
2017-08-18 00:51:58 +00:00
|
|
|
return nodePools, nil
|
2017-04-12 19:57:53 +00:00
|
|
|
}
|
2017-08-04 22:34:02 +00:00
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
func flattenIPAllocationPolicy(c *containerBeta.IPAllocationPolicy) []map[string]interface{} {
|
2017-11-27 23:15:03 +00:00
|
|
|
return []map[string]interface{}{
|
|
|
|
{
|
|
|
|
"cluster_secondary_range_name": c.ClusterSecondaryRangeName,
|
|
|
|
"services_secondary_range_name": c.ServicesSecondaryRangeName,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
func flattenMaintenancePolicy(mp *containerBeta.MaintenancePolicy) []map[string]interface{} {
|
2017-12-27 20:29:14 +00:00
|
|
|
return []map[string]interface{}{
|
|
|
|
{
|
|
|
|
"daily_maintenance_window": []map[string]interface{}{
|
|
|
|
{
|
|
|
|
"start_time": mp.Window.DailyMaintenanceWindow.StartTime,
|
|
|
|
"duration": mp.Window.DailyMaintenanceWindow.Duration,
|
|
|
|
},
|
|
|
|
},
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-03-07 01:44:05 +00:00
|
|
|
func flattenMasterAuthorizedNetworksConfig(c *containerBeta.MasterAuthorizedNetworksConfig) []map[string]interface{} {
|
2018-03-30 17:10:25 +00:00
|
|
|
if len(c.CidrBlocks) == 0 {
|
|
|
|
return nil
|
|
|
|
}
|
2017-11-02 17:38:20 +00:00
|
|
|
result := make(map[string]interface{})
|
2018-03-30 17:10:25 +00:00
|
|
|
if c.Enabled {
|
2018-03-01 21:19:18 +00:00
|
|
|
cidrBlocks := make([]interface{}, 0, len(c.CidrBlocks))
|
2017-11-02 17:38:20 +00:00
|
|
|
for _, v := range c.CidrBlocks {
|
|
|
|
cidrBlocks = append(cidrBlocks, map[string]interface{}{
|
|
|
|
"cidr_block": v.CidrBlock,
|
|
|
|
"display_name": v.DisplayName,
|
|
|
|
})
|
|
|
|
}
|
2018-03-01 21:19:18 +00:00
|
|
|
result["cidr_blocks"] = schema.NewSet(schema.HashResource(cidrBlockConfig), cidrBlocks)
|
2017-11-02 17:38:20 +00:00
|
|
|
}
|
|
|
|
return []map[string]interface{}{result}
|
|
|
|
}
|
|
|
|
|
2018-03-14 21:00:31 +00:00
|
|
|
func flattenPodSecurityPolicyConfig(c *containerBeta.PodSecurityPolicyConfig) []map[string]interface{} {
|
|
|
|
return []map[string]interface{}{
|
|
|
|
{
|
|
|
|
"enabled": c.Enabled,
|
|
|
|
},
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-09-07 17:31:58 +00:00
|
|
|
func resourceContainerClusterStateImporter(d *schema.ResourceData, meta interface{}) ([]*schema.ResourceData, error) {
|
|
|
|
parts := strings.Split(d.Id(), "/")
|
|
|
|
|
2018-04-03 20:39:28 +00:00
|
|
|
switch len(parts) {
|
|
|
|
case 2:
|
2018-04-05 21:51:35 +00:00
|
|
|
if loc := parts[0]; isZone(loc) {
|
|
|
|
d.Set("zone", loc)
|
|
|
|
} else {
|
|
|
|
d.Set("region", loc)
|
|
|
|
}
|
2018-04-03 20:39:28 +00:00
|
|
|
d.Set("name", parts[1])
|
|
|
|
case 3:
|
|
|
|
d.Set("project", parts[0])
|
2018-04-05 21:51:35 +00:00
|
|
|
if loc := parts[1]; isZone(loc) {
|
|
|
|
d.Set("zone", loc)
|
|
|
|
} else {
|
|
|
|
d.Set("region", loc)
|
|
|
|
}
|
2018-04-03 20:39:28 +00:00
|
|
|
d.Set("name", parts[2])
|
|
|
|
default:
|
|
|
|
return nil, fmt.Errorf("Invalid container cluster specifier. Expecting {zone}/{name} or {project}/{zone}/{name}")
|
|
|
|
}
|
2017-09-07 17:31:58 +00:00
|
|
|
|
2018-04-03 20:39:28 +00:00
|
|
|
d.SetId(parts[len(parts)-1])
|
2017-09-07 17:31:58 +00:00
|
|
|
return []*schema.ResourceData{d}, nil
|
|
|
|
}
|
2018-01-09 17:39:04 +00:00
|
|
|
|
2018-04-05 21:51:35 +00:00
|
|
|
func containerClusterMutexKey(project, location, clusterName string) string {
|
|
|
|
return fmt.Sprintf("google-container-cluster/%s/%s/%s", project, location, clusterName)
|
|
|
|
}
|
|
|
|
|
|
|
|
func containerClusterFullName(project, location, cluster string) string {
|
|
|
|
return fmt.Sprintf("projects/%s/locations/%s/clusters/%s", project, location, cluster)
|
|
|
|
}
|
|
|
|
|
|
|
|
func getLocation(d *schema.ResourceData, config *Config) (string, error) {
|
|
|
|
if v, isRegionalCluster := d.GetOk("region"); isRegionalCluster {
|
|
|
|
return v.(string), nil
|
|
|
|
} else {
|
|
|
|
// If region is not explicitly set, use "zone" (or fall back to the provider-level zone).
|
|
|
|
// For now, to avoid confusion, we require region to be set in the config to create a regional
|
|
|
|
// cluster rather than falling back to the provider-level region.
|
|
|
|
return getZone(d, config)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func isZone(location string) bool {
|
|
|
|
return len(strings.Split(location, "-")) == 3
|
2018-01-09 17:39:04 +00:00
|
|
|
}
|