2015-02-16 16:06:23 +00:00
package google
import (
"fmt"
"log"
2018-10-16 23:53:48 +00:00
"regexp"
2015-10-23 19:06:46 +00:00
"strings"
2016-08-05 18:30:41 +00:00
"time"
2015-02-16 16:06:23 +00:00
2018-03-20 21:20:43 +00:00
"github.com/hashicorp/terraform/helper/resource"
2015-02-16 16:06:23 +00:00
"github.com/hashicorp/terraform/helper/schema"
2017-08-02 17:12:16 +00:00
"github.com/hashicorp/terraform/helper/validation"
2018-10-24 22:04:58 +00:00
2017-07-26 20:37:59 +00:00
computeBeta "google.golang.org/api/compute/v0.beta"
2016-08-05 18:30:41 +00:00
"google.golang.org/api/compute/v1"
2015-02-16 16:06:23 +00:00
)
2018-10-16 23:53:48 +00:00
var (
instanceGroupManagerIdRegex = regexp . MustCompile ( "^" + ProjectRegex + "/[a-z0-9-]+/[a-z0-9-]+$" )
instanceGroupManagerIdNameRegex = regexp . MustCompile ( "^[a-z0-9-]+$" )
)
2015-07-28 00:47:10 +00:00
func resourceComputeInstanceGroupManager ( ) * schema . Resource {
2015-02-16 16:06:23 +00:00
return & schema . Resource {
2015-07-28 00:47:10 +00:00
Create : resourceComputeInstanceGroupManagerCreate ,
Read : resourceComputeInstanceGroupManagerRead ,
Update : resourceComputeInstanceGroupManagerUpdate ,
Delete : resourceComputeInstanceGroupManagerDelete ,
2016-08-05 18:30:41 +00:00
Importer : & schema . ResourceImporter {
2018-03-21 19:39:12 +00:00
State : resourceInstanceGroupManagerStateImporter ,
2016-08-05 18:30:41 +00:00
} ,
2015-02-16 16:06:23 +00:00
Schema : map [ string ] * schema . Schema {
2016-04-10 21:34:15 +00:00
"base_instance_name" : & schema . Schema {
2015-02-16 16:06:23 +00:00
Type : schema . TypeString ,
Required : true ,
ForceNew : true ,
} ,
2016-04-10 21:34:15 +00:00
"instance_template" : & schema . Schema {
2017-07-26 20:37:59 +00:00
Type : schema . TypeString ,
2018-06-04 22:34:48 +00:00
Optional : true ,
2017-07-26 20:37:59 +00:00
DiffSuppressFunc : compareSelfLinkRelativePaths ,
2016-04-10 21:34:15 +00:00
} ,
2018-06-04 22:34:48 +00:00
"version" : & schema . Schema {
2018-10-04 01:46:20 +00:00
Deprecated : "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details." ,
2018-10-03 00:22:42 +00:00
Type : schema . TypeList ,
Optional : true ,
Computed : true ,
2018-06-04 22:34:48 +00:00
Elem : & schema . Resource {
Schema : map [ string ] * schema . Schema {
"name" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
} ,
"instance_template" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
DiffSuppressFunc : compareSelfLinkRelativePaths ,
} ,
"target_size" : & schema . Schema {
Type : schema . TypeList ,
Optional : true ,
MaxItems : 1 ,
Elem : & schema . Resource {
Schema : map [ string ] * schema . Schema {
"fixed" : & schema . Schema {
Type : schema . TypeInt ,
Optional : true ,
} ,
"percent" : & schema . Schema {
Type : schema . TypeInt ,
Optional : true ,
ValidateFunc : validation . IntBetween ( 0 , 100 ) ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
2016-04-10 21:34:15 +00:00
"name" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
2015-02-16 16:06:23 +00:00
ForceNew : true ,
} ,
2016-04-10 21:34:15 +00:00
"zone" : & schema . Schema {
2015-02-16 16:06:23 +00:00
Type : schema . TypeString ,
2017-12-06 22:30:04 +00:00
Optional : true ,
2018-01-03 21:18:40 +00:00
Computed : true ,
2015-02-16 16:06:23 +00:00
ForceNew : true ,
} ,
2016-04-10 21:34:15 +00:00
"description" : & schema . Schema {
2015-02-16 16:06:23 +00:00
Type : schema . TypeString ,
2016-04-10 21:34:15 +00:00
Optional : true ,
ForceNew : true ,
2015-02-16 16:06:23 +00:00
} ,
2016-04-10 21:34:15 +00:00
"fingerprint" : & schema . Schema {
2015-02-16 16:06:23 +00:00
Type : schema . TypeString ,
Computed : true ,
} ,
2016-04-10 21:34:15 +00:00
"instance_group" : & schema . Schema {
2015-02-16 16:06:23 +00:00
Type : schema . TypeString ,
2016-04-10 21:34:15 +00:00
Computed : true ,
2015-02-16 16:06:23 +00:00
} ,
2016-01-10 14:09:05 +00:00
"named_port" : & schema . Schema {
Type : schema . TypeList ,
Optional : true ,
Elem : & schema . Resource {
Schema : map [ string ] * schema . Schema {
"name" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
} ,
"port" : & schema . Schema {
Type : schema . TypeInt ,
Required : true ,
} ,
} ,
} ,
} ,
2016-04-10 21:34:15 +00:00
"project" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
ForceNew : true ,
2016-08-05 18:30:41 +00:00
Computed : true ,
2016-04-10 21:34:15 +00:00
} ,
"self_link" : & schema . Schema {
Type : schema . TypeString ,
Computed : true ,
} ,
2015-11-12 20:44:31 +00:00
"update_strategy" : & schema . Schema {
2017-09-14 00:06:07 +00:00
Type : schema . TypeString ,
Optional : true ,
2018-10-03 00:22:42 +00:00
Default : "REPLACE" ,
ValidateFunc : validation . StringInSlice ( [ ] string { "RESTART" , "NONE" , "ROLLING_UPDATE" , "REPLACE" } , false ) ,
DiffSuppressFunc : func ( key , old , new string , d * schema . ResourceData ) bool {
if old == "REPLACE" && new == "RESTART" {
return true
}
if old == "RESTART" && new == "REPLACE" {
return true
}
return false
} ,
2015-11-12 20:44:31 +00:00
} ,
2015-02-16 16:06:23 +00:00
"target_pools" : & schema . Schema {
2017-09-14 00:06:07 +00:00
Type : schema . TypeSet ,
Optional : true ,
2017-07-26 20:37:59 +00:00
Elem : & schema . Schema {
Type : schema . TypeString ,
} ,
Set : selfLinkRelativePathHash ,
2015-02-16 16:06:23 +00:00
} ,
"target_size" : & schema . Schema {
Type : schema . TypeInt ,
Computed : true ,
2015-05-17 00:14:38 +00:00
Optional : true ,
2015-02-16 16:06:23 +00:00
} ,
2017-08-02 17:12:16 +00:00
"auto_healing_policies" : & schema . Schema {
2018-10-04 01:46:20 +00:00
Deprecated : "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details." ,
2018-10-03 00:22:42 +00:00
Type : schema . TypeList ,
Optional : true ,
MaxItems : 1 ,
2017-08-02 17:12:16 +00:00
Elem : & schema . Resource {
Schema : map [ string ] * schema . Schema {
"health_check" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
DiffSuppressFunc : compareSelfLinkRelativePaths ,
} ,
"initial_delay_sec" : & schema . Schema {
Type : schema . TypeInt ,
Required : true ,
ValidateFunc : validation . IntBetween ( 0 , 3600 ) ,
} ,
} ,
} ,
} ,
2018-06-04 22:34:48 +00:00
2018-03-15 18:10:09 +00:00
"rolling_update_policy" : & schema . Schema {
2018-10-04 01:46:20 +00:00
Deprecated : "This field is in beta and will be removed from this provider. Use it in the the google-beta provider instead. See https://terraform.io/docs/providers/google/provider_versions.html for more details." ,
2018-10-03 00:22:42 +00:00
Type : schema . TypeList ,
Optional : true ,
MaxItems : 1 ,
2018-03-15 18:10:09 +00:00
Elem : & schema . Resource {
Schema : map [ string ] * schema . Schema {
"minimal_action" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
ValidateFunc : validation . StringInSlice ( [ ] string { "RESTART" , "REPLACE" } , false ) ,
} ,
"type" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
ValidateFunc : validation . StringInSlice ( [ ] string { "OPPORTUNISTIC" , "PROACTIVE" } , false ) ,
} ,
"max_surge_fixed" : & schema . Schema {
Type : schema . TypeInt ,
Optional : true ,
Default : 1 ,
ConflictsWith : [ ] string { "rolling_update_policy.0.max_surge_percent" } ,
} ,
"max_surge_percent" : & schema . Schema {
Type : schema . TypeInt ,
Optional : true ,
ConflictsWith : [ ] string { "rolling_update_policy.0.max_surge_fixed" } ,
ValidateFunc : validation . IntBetween ( 0 , 100 ) ,
} ,
"max_unavailable_fixed" : & schema . Schema {
Type : schema . TypeInt ,
Optional : true ,
Default : 1 ,
ConflictsWith : [ ] string { "rolling_update_policy.0.max_unavailable_percent" } ,
} ,
"max_unavailable_percent" : & schema . Schema {
Type : schema . TypeInt ,
Optional : true ,
ConflictsWith : [ ] string { "rolling_update_policy.0.max_unavailable_fixed" } ,
ValidateFunc : validation . IntBetween ( 0 , 100 ) ,
} ,
"min_ready_sec" : & schema . Schema {
Type : schema . TypeInt ,
Optional : true ,
ValidateFunc : validation . IntBetween ( 0 , 3600 ) ,
} ,
} ,
} ,
} ,
2018-06-04 22:34:48 +00:00
2018-03-20 21:20:43 +00:00
"wait_for_instances" : & schema . Schema {
Type : schema . TypeBool ,
Optional : true ,
2018-03-21 19:39:12 +00:00
Default : false ,
2018-03-20 21:20:43 +00:00
} ,
2015-02-16 16:06:23 +00:00
} ,
}
}
2016-01-10 14:09:05 +00:00
func getNamedPorts ( nps [ ] interface { } ) [ ] * compute . NamedPort {
namedPorts := make ( [ ] * compute . NamedPort , 0 , len ( nps ) )
for _ , v := range nps {
np := v . ( map [ string ] interface { } )
namedPorts = append ( namedPorts , & compute . NamedPort {
Name : np [ "name" ] . ( string ) ,
Port : int64 ( np [ "port" ] . ( int ) ) ,
} )
}
2017-07-26 20:37:59 +00:00
return namedPorts
}
func getNamedPortsBeta ( nps [ ] interface { } ) [ ] * computeBeta . NamedPort {
namedPorts := make ( [ ] * computeBeta . NamedPort , 0 , len ( nps ) )
for _ , v := range nps {
np := v . ( map [ string ] interface { } )
namedPorts = append ( namedPorts , & computeBeta . NamedPort {
Name : np [ "name" ] . ( string ) ,
Port : int64 ( np [ "port" ] . ( int ) ) ,
} )
}
2016-01-10 14:09:05 +00:00
return namedPorts
}
2015-07-28 00:47:10 +00:00
func resourceComputeInstanceGroupManagerCreate ( d * schema . ResourceData , meta interface { } ) error {
2015-02-16 16:06:23 +00:00
config := meta . ( * Config )
2016-04-10 16:59:57 +00:00
project , err := getProject ( d , config )
if err != nil {
return err
}
2017-12-06 22:30:04 +00:00
zone , err := getZone ( d , config )
if err != nil {
return err
}
2018-03-15 18:10:09 +00:00
if _ , ok := d . GetOk ( "rolling_update_policy" ) ; d . Get ( "update_strategy" ) == "ROLLING_UPDATE" && ! ok {
return fmt . Errorf ( "[rolling_update_policy] must be set when 'update_strategy' is set to 'ROLLING_UPDATE'" )
}
2015-02-16 16:06:23 +00:00
// Build the parameter
2017-07-26 20:37:59 +00:00
manager := & computeBeta . InstanceGroupManager {
2017-09-14 00:06:07 +00:00
Name : d . Get ( "name" ) . ( string ) ,
Description : d . Get ( "description" ) . ( string ) ,
BaseInstanceName : d . Get ( "base_instance_name" ) . ( string ) ,
InstanceTemplate : d . Get ( "instance_template" ) . ( string ) ,
TargetSize : int64 ( d . Get ( "target_size" ) . ( int ) ) ,
NamedPorts : getNamedPortsBeta ( d . Get ( "named_port" ) . ( [ ] interface { } ) ) ,
TargetPools : convertStringSet ( d . Get ( "target_pools" ) . ( * schema . Set ) ) ,
AutoHealingPolicies : expandAutoHealingPolicies ( d . Get ( "auto_healing_policies" ) . ( [ ] interface { } ) ) ,
2018-06-04 22:34:48 +00:00
Versions : expandVersions ( d . Get ( "version" ) . ( [ ] interface { } ) ) ,
2017-06-16 22:39:44 +00:00
// Force send TargetSize to allow a value of 0.
2017-06-16 22:41:26 +00:00
ForceSendFields : [ ] string { "TargetSize" } ,
2015-02-16 16:06:23 +00:00
}
log . Printf ( "[DEBUG] InstanceGroupManager insert request: %#v" , manager )
2018-05-09 18:24:40 +00:00
op , err := config . clientComputeBeta . InstanceGroupManagers . Insert (
project , zone , manager ) . Do ( )
2017-07-26 20:37:59 +00:00
2015-02-16 16:06:23 +00:00
if err != nil {
return fmt . Errorf ( "Error creating InstanceGroupManager: %s" , err )
}
// It probably maybe worked, so store the ID now
2018-10-16 23:53:48 +00:00
d . SetId ( instanceGroupManagerId { Project : project , Zone : zone , Name : manager . Name } . terraformId ( ) )
2015-02-16 16:06:23 +00:00
// Wait for the operation to complete
2017-10-13 22:36:03 +00:00
err = computeSharedOperationWait ( config . clientCompute , op , project , "Creating InstanceGroupManager" )
2015-02-16 16:06:23 +00:00
if err != nil {
return err
}
2015-07-28 00:47:10 +00:00
return resourceComputeInstanceGroupManagerRead ( d , meta )
2015-02-16 16:06:23 +00:00
}
2017-07-26 20:37:59 +00:00
func flattenNamedPortsBeta ( namedPorts [ ] * computeBeta . NamedPort ) [ ] map [ string ] interface { } {
2016-08-05 18:30:41 +00:00
result := make ( [ ] map [ string ] interface { } , 0 , len ( namedPorts ) )
for _ , namedPort := range namedPorts {
namedPortMap := make ( map [ string ] interface { } )
namedPortMap [ "name" ] = namedPort . Name
namedPortMap [ "port" ] = namedPort . Port
result = append ( result , namedPortMap )
}
return result
}
2018-06-04 22:34:48 +00:00
func flattenVersions ( versions [ ] * computeBeta . InstanceGroupManagerVersion ) [ ] map [ string ] interface { } {
result := make ( [ ] map [ string ] interface { } , 0 , len ( versions ) )
for _ , version := range versions {
versionMap := make ( map [ string ] interface { } )
versionMap [ "name" ] = version . Name
versionMap [ "instance_template" ] = ConvertSelfLinkToV1 ( version . InstanceTemplate )
versionMap [ "target_size" ] = flattenFixedOrPercent ( version . TargetSize )
result = append ( result , versionMap )
}
return result
}
func flattenFixedOrPercent ( fixedOrPercent * computeBeta . FixedOrPercent ) [ ] map [ string ] interface { } {
result := make ( map [ string ] interface { } )
if value := fixedOrPercent . Percent ; value > 0 {
result [ "percent" ] = value
} else if value := fixedOrPercent . Fixed ; value > 0 {
result [ "fixed" ] = fixedOrPercent . Fixed
} else {
return [ ] map [ string ] interface { } { }
}
return [ ] map [ string ] interface { } { result }
}
2018-03-20 21:20:43 +00:00
func getManager ( d * schema . ResourceData , meta interface { } ) ( * computeBeta . InstanceGroupManager , error ) {
2015-02-16 16:06:23 +00:00
config := meta . ( * Config )
2018-10-16 23:53:48 +00:00
zonalID , err := parseInstanceGroupManagerId ( d . Id ( ) )
2016-04-10 16:59:57 +00:00
if err != nil {
2018-03-20 21:20:43 +00:00
return nil , err
2016-04-10 16:59:57 +00:00
}
2018-10-16 23:53:48 +00:00
if zonalID . Project == "" {
project , err := getProject ( d , config )
if err != nil {
return nil , err
}
zonalID . Project = project
}
if zonalID . Zone == "" {
zonalID . Zone , _ = getZone ( d , config )
2016-08-05 18:30:41 +00:00
}
2015-02-16 16:06:23 +00:00
2018-05-09 18:24:40 +00:00
getInstanceGroupManager := func ( zone string ) ( interface { } , error ) {
2018-10-16 23:53:48 +00:00
return config . clientComputeBeta . InstanceGroupManagers . Get ( zonalID . Project , zone , zonalID . Name ) . Do ( )
2018-05-09 18:24:40 +00:00
}
2017-07-26 20:37:59 +00:00
2018-05-09 18:24:40 +00:00
var manager * computeBeta . InstanceGroupManager
2018-10-16 23:53:48 +00:00
if zonalID . Zone == "" {
2018-05-09 18:24:40 +00:00
// If the resource was imported, the only info we have is the ID. Try to find the resource
// by searching in the region of the project.
2018-10-16 23:53:48 +00:00
region , err := getRegion ( d , config )
if err != nil {
return nil , err
}
resource , err := getZonalBetaResourceFromRegion ( getInstanceGroupManager , region , config . clientComputeBeta , zonalID . Project )
if err != nil {
return nil , err
}
if resource != nil {
manager = resource . ( * computeBeta . InstanceGroupManager )
}
} else {
manager , err = config . clientComputeBeta . InstanceGroupManagers . Get ( zonalID . Project , zonalID . Zone , zonalID . Name ) . Do ( )
if err != nil {
return nil , handleNotFoundError ( err , d , fmt . Sprintf ( "Instance Group Manager %q" , zonalID . Name ) )
2018-05-04 14:51:45 +00:00
}
2018-05-09 18:24:40 +00:00
}
2018-05-03 19:24:21 +00:00
2018-05-09 18:24:40 +00:00
if manager == nil {
log . Printf ( "[WARN] Removing Instance Group Manager %q because it's gone" , d . Get ( "name" ) . ( string ) )
2018-05-04 14:51:45 +00:00
2018-05-09 18:24:40 +00:00
// The resource doesn't exist anymore
d . SetId ( "" )
return nil , nil
2018-05-04 14:51:45 +00:00
}
2018-05-09 18:24:40 +00:00
2018-03-20 21:20:43 +00:00
return manager , nil
}
func resourceComputeInstanceGroupManagerRead ( d * schema . ResourceData , meta interface { } ) error {
config := meta . ( * Config )
project , err := getProject ( d , config )
if err != nil {
return err
}
manager , err := getManager ( d , meta )
2018-10-19 16:36:14 +00:00
if err != nil {
2018-03-20 21:20:43 +00:00
return err
}
2018-10-19 16:36:14 +00:00
if manager == nil {
log . Printf ( "[WARN] Instance Group Manager %q not found, removing from state." , d . Id ( ) )
d . SetId ( "" )
return nil
}
2016-08-05 18:30:41 +00:00
d . Set ( "base_instance_name" , manager . BaseInstanceName )
2018-05-09 18:24:40 +00:00
d . Set ( "instance_template" , ConvertSelfLinkToV1 ( manager . InstanceTemplate ) )
2018-06-04 22:34:48 +00:00
if err := d . Set ( "version" , flattenVersions ( manager . Versions ) ) ; err != nil {
return err
}
2016-08-05 18:30:41 +00:00
d . Set ( "name" , manager . Name )
2017-09-14 00:06:07 +00:00
d . Set ( "zone" , GetResourceNameFromSelfLink ( manager . Zone ) )
2016-08-05 18:30:41 +00:00
d . Set ( "description" , manager . Description )
d . Set ( "project" , project )
d . Set ( "target_size" , manager . TargetSize )
2018-10-19 16:36:14 +00:00
if err = d . Set ( "target_pools" , manager . TargetPools ) ; err != nil {
return fmt . Errorf ( "Error setting target_pools in state: %s" , err . Error ( ) )
}
if err = d . Set ( "named_port" , flattenNamedPortsBeta ( manager . NamedPorts ) ) ; err != nil {
return fmt . Errorf ( "Error setting named_port in state: %s" , err . Error ( ) )
}
2015-02-16 16:06:23 +00:00
d . Set ( "fingerprint" , manager . Fingerprint )
2018-05-09 18:24:40 +00:00
d . Set ( "instance_group" , ConvertSelfLinkToV1 ( manager . InstanceGroup ) )
2017-07-26 20:37:59 +00:00
d . Set ( "self_link" , ConvertSelfLinkToV1 ( manager . SelfLink ) )
2018-10-19 16:36:14 +00:00
2017-01-09 23:15:50 +00:00
update_strategy , ok := d . GetOk ( "update_strategy" )
if ! ok {
2018-10-03 00:22:42 +00:00
update_strategy = "REPLACE"
2017-01-09 23:15:50 +00:00
}
d . Set ( "update_strategy" , update_strategy . ( string ) )
2018-10-19 16:36:14 +00:00
if err = d . Set ( "auto_healing_policies" , flattenAutoHealingPolicies ( manager . AutoHealingPolicies ) ) ; err != nil {
return fmt . Errorf ( "Error setting auto_healing_policies in state: %s" , err . Error ( ) )
}
2015-02-16 16:06:23 +00:00
2018-03-20 21:20:43 +00:00
if d . Get ( "wait_for_instances" ) . ( bool ) {
conf := resource . StateChangeConf {
Pending : [ ] string { "creating" , "error" } ,
Target : [ ] string { "created" } ,
Refresh : waitForInstancesRefreshFunc ( getManager , d , meta ) ,
Timeout : d . Timeout ( schema . TimeoutCreate ) ,
}
_ , err := conf . WaitForState ( )
if err != nil {
return err
}
}
2015-02-16 16:06:23 +00:00
return nil
}
2017-07-26 20:37:59 +00:00
2018-07-25 20:21:20 +00:00
// Updates an instance group manager by applying the update strategy (REPLACE, RESTART)
// and rolling update policy (PROACTIVE, OPPORTUNISTIC). Updates performed by API
// are OPPORTUNISTIC by default.
func performZoneUpdate ( config * Config , id string , updateStrategy string , rollingUpdatePolicy * computeBeta . InstanceGroupManagerUpdatePolicy , versions [ ] * computeBeta . InstanceGroupManagerVersion , project string , zone string ) error {
2018-10-03 00:22:42 +00:00
if updateStrategy == "RESTART" || updateStrategy == "REPLACE" {
2018-06-04 22:34:48 +00:00
managedInstances , err := config . clientComputeBeta . InstanceGroupManagers . ListManagedInstances ( project , zone , id ) . Do ( )
if err != nil {
return fmt . Errorf ( "Error getting instance group managers instances: %s" , err )
}
managedInstanceCount := len ( managedInstances . ManagedInstances )
instances := make ( [ ] string , managedInstanceCount )
for i , v := range managedInstances . ManagedInstances {
instances [ i ] = v . Instance
}
recreateInstances := & computeBeta . InstanceGroupManagersRecreateInstancesRequest {
Instances : instances ,
}
op , err := config . clientComputeBeta . InstanceGroupManagers . RecreateInstances ( project , zone , id , recreateInstances ) . Do ( )
if err != nil {
return fmt . Errorf ( "Error restarting instance group managers instances: %s" , err )
}
// Wait for the operation to complete
err = computeSharedOperationWaitTime ( config . clientCompute , op , project , managedInstanceCount * 4 , "Restarting InstanceGroupManagers instances" )
if err != nil {
return err
}
}
if updateStrategy == "ROLLING_UPDATE" {
2018-07-12 20:09:57 +00:00
// UpdatePolicy is set for InstanceGroupManager on update only, because it is only relevant for `Patch` calls.
2018-06-04 22:34:48 +00:00
// Other tools(gcloud and UI) capable of executing the same `ROLLING UPDATE` call
// expect those values to be provided by user as part of the call
// or provide their own defaults without respecting what was previously set on UpdateManager.
// To follow the same logic, we provide policy values on relevant update change only.
manager := & computeBeta . InstanceGroupManager {
UpdatePolicy : rollingUpdatePolicy ,
Versions : versions ,
}
2018-07-12 20:09:57 +00:00
op , err := config . clientComputeBeta . InstanceGroupManagers . Patch ( project , zone , id , manager ) . Do ( )
2018-06-04 22:34:48 +00:00
if err != nil {
return fmt . Errorf ( "Error updating managed group instances: %s" , err )
}
err = computeSharedOperationWait ( config . clientCompute , op , project , "Updating managed group instances" )
if err != nil {
return err
}
}
return nil
}
2015-07-28 00:47:10 +00:00
func resourceComputeInstanceGroupManagerUpdate ( d * schema . ResourceData , meta interface { } ) error {
2015-02-16 16:06:23 +00:00
config := meta . ( * Config )
2018-10-16 23:53:48 +00:00
zonalID , err := parseInstanceGroupManagerId ( d . Id ( ) )
2016-04-10 16:59:57 +00:00
if err != nil {
return err
}
2018-10-16 23:53:48 +00:00
if zonalID . Project == "" {
zonalID . Project , err = getProject ( d , config )
if err != nil {
return err
}
}
if zonalID . Zone == "" {
zonalID . Zone , err = getZone ( d , config )
if err != nil {
return err
}
2017-12-06 22:30:04 +00:00
}
2015-02-16 16:06:23 +00:00
d . Partial ( true )
2018-03-15 18:10:09 +00:00
if _ , ok := d . GetOk ( "rolling_update_policy" ) ; d . Get ( "update_strategy" ) == "ROLLING_UPDATE" && ! ok {
return fmt . Errorf ( "[rolling_update_policy] must be set when 'update_strategy' is set to 'ROLLING_UPDATE'" )
}
2015-02-16 16:06:23 +00:00
// If target_pools changes then update
if d . HasChange ( "target_pools" ) {
2017-09-14 00:06:07 +00:00
targetPools := convertStringSet ( d . Get ( "target_pools" ) . ( * schema . Set ) )
2015-02-16 16:06:23 +00:00
// Build the parameter
2017-07-26 20:37:59 +00:00
setTargetPools := & computeBeta . InstanceGroupManagersSetTargetPoolsRequest {
2015-02-16 16:06:23 +00:00
Fingerprint : d . Get ( "fingerprint" ) . ( string ) ,
TargetPools : targetPools ,
}
2018-05-09 18:24:40 +00:00
op , err := config . clientComputeBeta . InstanceGroupManagers . SetTargetPools (
2018-10-16 23:53:48 +00:00
zonalID . Project , zonalID . Zone , zonalID . Name , setTargetPools ) . Do ( )
2017-07-26 20:37:59 +00:00
2015-02-16 16:06:23 +00:00
if err != nil {
return fmt . Errorf ( "Error updating InstanceGroupManager: %s" , err )
}
// Wait for the operation to complete
2018-10-16 23:53:48 +00:00
err = computeSharedOperationWait ( config . clientCompute , op , zonalID . Project , "Updating InstanceGroupManager" )
2015-02-16 16:06:23 +00:00
if err != nil {
return err
}
d . SetPartial ( "target_pools" )
}
2016-01-10 14:09:05 +00:00
// If named_port changes then update:
if d . HasChange ( "named_port" ) {
// Build the parameters for a "SetNamedPorts" request:
2017-07-26 20:37:59 +00:00
namedPorts := getNamedPortsBeta ( d . Get ( "named_port" ) . ( [ ] interface { } ) )
setNamedPorts := & computeBeta . InstanceGroupsSetNamedPortsRequest {
2016-01-10 14:09:05 +00:00
NamedPorts : namedPorts ,
}
// Make the request:
2018-05-09 18:24:40 +00:00
op , err := config . clientComputeBeta . InstanceGroups . SetNamedPorts (
2018-10-16 23:53:48 +00:00
zonalID . Project , zonalID . Zone , zonalID . Name , setNamedPorts ) . Do ( )
2017-07-26 20:37:59 +00:00
2016-01-10 14:09:05 +00:00
if err != nil {
return fmt . Errorf ( "Error updating InstanceGroupManager: %s" , err )
}
// Wait for the operation to complete:
2018-10-16 23:53:48 +00:00
err = computeSharedOperationWait ( config . clientCompute , op , zonalID . Project , "Updating InstanceGroupManager" )
2016-01-10 14:09:05 +00:00
if err != nil {
return err
}
d . SetPartial ( "named_port" )
}
2015-05-17 00:14:38 +00:00
if d . HasChange ( "target_size" ) {
2017-06-16 22:39:44 +00:00
targetSize := int64 ( d . Get ( "target_size" ) . ( int ) )
2018-05-09 18:24:40 +00:00
op , err := config . clientComputeBeta . InstanceGroupManagers . Resize (
2018-10-16 23:53:48 +00:00
zonalID . Project , zonalID . Zone , zonalID . Name , targetSize ) . Do ( )
2017-07-26 20:37:59 +00:00
2017-06-13 19:43:18 +00:00
if err != nil {
return fmt . Errorf ( "Error updating InstanceGroupManager: %s" , err )
}
2015-02-16 16:06:23 +00:00
2017-06-13 19:43:18 +00:00
// Wait for the operation to complete
2018-10-16 23:53:48 +00:00
err = computeSharedOperationWait ( config . clientCompute , op , zonalID . Project , "Updating InstanceGroupManager" )
2017-06-13 19:43:18 +00:00
if err != nil {
return err
2015-02-16 16:06:23 +00:00
}
2015-05-17 00:14:38 +00:00
d . SetPartial ( "target_size" )
2015-02-16 16:06:23 +00:00
}
2017-08-02 17:12:16 +00:00
// We will always be in v0beta inside this conditional
if d . HasChange ( "auto_healing_policies" ) {
setAutoHealingPoliciesRequest := & computeBeta . InstanceGroupManagersSetAutoHealingRequest { }
if v , ok := d . GetOk ( "auto_healing_policies" ) ; ok {
setAutoHealingPoliciesRequest . AutoHealingPolicies = expandAutoHealingPolicies ( v . ( [ ] interface { } ) )
}
op , err := config . clientComputeBeta . InstanceGroupManagers . SetAutoHealingPolicies (
2018-10-16 23:53:48 +00:00
zonalID . Project , zonalID . Zone , zonalID . Name , setAutoHealingPoliciesRequest ) . Do ( )
2017-08-02 17:12:16 +00:00
if err != nil {
return fmt . Errorf ( "Error updating AutoHealingPolicies: %s" , err )
}
// Wait for the operation to complete
2018-10-16 23:53:48 +00:00
err = computeSharedOperationWait ( config . clientCompute , op , zonalID . Project , "Updating AutoHealingPolicies" )
2017-08-02 17:12:16 +00:00
if err != nil {
return err
}
d . SetPartial ( "auto_healing_policies" )
}
2018-06-04 22:34:48 +00:00
// If instance_template changes then update
if d . HasChange ( "instance_template" ) {
// Build the parameter
setInstanceTemplate := & computeBeta . InstanceGroupManagersSetInstanceTemplateRequest {
InstanceTemplate : d . Get ( "instance_template" ) . ( string ) ,
}
2018-10-16 23:53:48 +00:00
op , err := config . clientComputeBeta . InstanceGroupManagers . SetInstanceTemplate ( zonalID . Project , zonalID . Zone , zonalID . Name , setInstanceTemplate ) . Do ( )
2018-06-04 22:34:48 +00:00
if err != nil {
return fmt . Errorf ( "Error updating InstanceGroupManager: %s" , err )
}
// Wait for the operation to complete
2018-10-16 23:53:48 +00:00
err = computeSharedOperationWait ( config . clientCompute , op , zonalID . Project , "Updating InstanceGroupManager" )
2018-06-04 22:34:48 +00:00
if err != nil {
return err
}
updateStrategy := d . Get ( "update_strategy" ) . ( string )
rollingUpdatePolicy := expandUpdatePolicy ( d . Get ( "rolling_update_policy" ) . ( [ ] interface { } ) )
2018-10-16 23:53:48 +00:00
err = performZoneUpdate ( config , zonalID . Name , updateStrategy , rollingUpdatePolicy , nil , zonalID . Project , zonalID . Zone )
2018-06-04 22:34:48 +00:00
d . SetPartial ( "instance_template" )
}
// If version changes then update
if d . HasChange ( "version" ) {
updateStrategy := d . Get ( "update_strategy" ) . ( string )
rollingUpdatePolicy := expandUpdatePolicy ( d . Get ( "rolling_update_policy" ) . ( [ ] interface { } ) )
versions := expandVersions ( d . Get ( "version" ) . ( [ ] interface { } ) )
2018-10-16 23:53:48 +00:00
err = performZoneUpdate ( config , zonalID . Name , updateStrategy , rollingUpdatePolicy , versions , zonalID . Project , zonalID . Zone )
2018-06-04 22:34:48 +00:00
if err != nil {
return err
}
d . SetPartial ( "version" )
}
2015-02-16 16:06:23 +00:00
d . Partial ( false )
2015-07-28 00:47:10 +00:00
return resourceComputeInstanceGroupManagerRead ( d , meta )
2015-02-16 16:06:23 +00:00
}
2015-07-28 00:47:10 +00:00
func resourceComputeInstanceGroupManagerDelete ( d * schema . ResourceData , meta interface { } ) error {
2015-02-16 16:06:23 +00:00
config := meta . ( * Config )
2018-10-16 23:53:48 +00:00
zonalID , err := parseInstanceGroupManagerId ( d . Id ( ) )
2016-04-10 16:59:57 +00:00
if err != nil {
return err
}
2018-10-16 23:53:48 +00:00
if zonalID . Project == "" {
zonalID . Project , err = getProject ( d , config )
if err != nil {
return err
}
}
if zonalID . Zone == "" {
zonalID . Zone , err = getZone ( d , config )
if err != nil {
return err
}
2017-12-06 22:30:04 +00:00
}
2017-07-26 20:37:59 +00:00
2018-10-16 23:53:48 +00:00
op , err := config . clientComputeBeta . InstanceGroupManagers . Delete ( zonalID . Project , zonalID . Zone , zonalID . Name ) . Do ( )
2018-05-09 18:24:40 +00:00
attempt := 0
for err != nil && attempt < 20 {
attempt ++
time . Sleep ( 2000 * time . Millisecond )
2018-10-16 23:53:48 +00:00
op , err = config . clientComputeBeta . InstanceGroupManagers . Delete ( zonalID . Project , zonalID . Zone , zonalID . Name ) . Do ( )
2016-08-05 18:30:41 +00:00
}
2017-07-26 20:37:59 +00:00
2015-02-16 16:06:23 +00:00
if err != nil {
return fmt . Errorf ( "Error deleting instance group manager: %s" , err )
}
2015-10-23 19:06:46 +00:00
currentSize := int64 ( d . Get ( "target_size" ) . ( int ) )
2015-02-16 16:06:23 +00:00
// Wait for the operation to complete
2018-10-16 23:53:48 +00:00
err = computeSharedOperationWait ( config . clientCompute , op , zonalID . Project , "Deleting InstanceGroupManager" )
2015-10-23 19:06:46 +00:00
for err != nil && currentSize > 0 {
if ! strings . Contains ( err . Error ( ) , "timeout" ) {
2015-10-29 22:10:44 +00:00
return err
2015-10-23 19:06:46 +00:00
}
2018-05-09 18:24:40 +00:00
instanceGroup , err := config . clientComputeBeta . InstanceGroups . Get (
2018-10-16 23:53:48 +00:00
zonalID . Project , zonalID . Zone , zonalID . Name ) . Do ( )
2018-05-09 18:24:40 +00:00
if err != nil {
return fmt . Errorf ( "Error getting instance group size: %s" , err )
2018-05-04 14:51:45 +00:00
}
2018-05-03 19:24:21 +00:00
2018-05-09 18:24:40 +00:00
instanceGroupSize := instanceGroup . Size
2017-07-26 20:37:59 +00:00
if instanceGroupSize >= currentSize {
2015-10-23 19:06:46 +00:00
return fmt . Errorf ( "Error, instance group isn't shrinking during delete" )
}
2017-07-26 20:37:59 +00:00
log . Printf ( "[INFO] timeout occured, but instance group is shrinking (%d < %d)" , instanceGroupSize , currentSize )
currentSize = instanceGroupSize
2018-10-16 23:53:48 +00:00
err = computeSharedOperationWait ( config . clientCompute , op , zonalID . Project , "Deleting InstanceGroupManager" )
2015-02-16 16:06:23 +00:00
}
d . SetId ( "" )
return nil
}
2017-08-02 17:12:16 +00:00
func expandAutoHealingPolicies ( configured [ ] interface { } ) [ ] * computeBeta . InstanceGroupManagerAutoHealingPolicy {
autoHealingPolicies := make ( [ ] * computeBeta . InstanceGroupManagerAutoHealingPolicy , 0 , len ( configured ) )
for _ , raw := range configured {
data := raw . ( map [ string ] interface { } )
autoHealingPolicy := computeBeta . InstanceGroupManagerAutoHealingPolicy {
HealthCheck : data [ "health_check" ] . ( string ) ,
InitialDelaySec : int64 ( data [ "initial_delay_sec" ] . ( int ) ) ,
}
autoHealingPolicies = append ( autoHealingPolicies , & autoHealingPolicy )
}
return autoHealingPolicies
}
2018-06-04 22:34:48 +00:00
func expandVersions ( configured [ ] interface { } ) [ ] * computeBeta . InstanceGroupManagerVersion {
versions := make ( [ ] * computeBeta . InstanceGroupManagerVersion , 0 , len ( configured ) )
for _ , raw := range configured {
data := raw . ( map [ string ] interface { } )
version := computeBeta . InstanceGroupManagerVersion {
Name : data [ "name" ] . ( string ) ,
InstanceTemplate : data [ "instance_template" ] . ( string ) ,
TargetSize : expandFixedOrPercent ( data [ "target_size" ] . ( [ ] interface { } ) ) ,
}
versions = append ( versions , & version )
}
return versions
}
func expandFixedOrPercent ( configured [ ] interface { } ) * computeBeta . FixedOrPercent {
fixedOrPercent := & computeBeta . FixedOrPercent { }
for _ , raw := range configured {
data := raw . ( map [ string ] interface { } )
if percent := data [ "percent" ] ; percent . ( int ) > 0 {
fixedOrPercent . Percent = int64 ( percent . ( int ) )
} else {
fixedOrPercent . Fixed = int64 ( data [ "fixed" ] . ( int ) )
fixedOrPercent . ForceSendFields = [ ] string { "Fixed" }
}
}
return fixedOrPercent
}
2018-03-15 18:10:09 +00:00
func expandUpdatePolicy ( configured [ ] interface { } ) * computeBeta . InstanceGroupManagerUpdatePolicy {
updatePolicy := & computeBeta . InstanceGroupManagerUpdatePolicy { }
for _ , raw := range configured {
data := raw . ( map [ string ] interface { } )
updatePolicy . MinimalAction = data [ "minimal_action" ] . ( string )
updatePolicy . Type = data [ "type" ] . ( string )
// percent and fixed values are conflicting
// when the percent values are set, the fixed values will be ignored
if v := data [ "max_surge_percent" ] ; v . ( int ) > 0 {
updatePolicy . MaxSurge = & computeBeta . FixedOrPercent {
Percent : int64 ( v . ( int ) ) ,
}
} else {
updatePolicy . MaxSurge = & computeBeta . FixedOrPercent {
Fixed : int64 ( data [ "max_surge_fixed" ] . ( int ) ) ,
2018-03-23 20:51:30 +00:00
// allow setting this value to 0
ForceSendFields : [ ] string { "Fixed" } ,
2018-03-15 18:10:09 +00:00
}
}
if v := data [ "max_unavailable_percent" ] ; v . ( int ) > 0 {
updatePolicy . MaxUnavailable = & computeBeta . FixedOrPercent {
Percent : int64 ( v . ( int ) ) ,
}
} else {
updatePolicy . MaxUnavailable = & computeBeta . FixedOrPercent {
Fixed : int64 ( data [ "max_unavailable_fixed" ] . ( int ) ) ,
2018-03-23 20:51:30 +00:00
// allow setting this value to 0
ForceSendFields : [ ] string { "Fixed" } ,
2018-03-15 18:10:09 +00:00
}
}
if v , ok := data [ "min_ready_sec" ] ; ok {
updatePolicy . MinReadySec = int64 ( v . ( int ) )
}
}
return updatePolicy
}
2017-08-02 17:12:16 +00:00
func flattenAutoHealingPolicies ( autoHealingPolicies [ ] * computeBeta . InstanceGroupManagerAutoHealingPolicy ) [ ] map [ string ] interface { } {
autoHealingPoliciesSchema := make ( [ ] map [ string ] interface { } , 0 , len ( autoHealingPolicies ) )
for _ , autoHealingPolicy := range autoHealingPolicies {
data := map [ string ] interface { } {
"health_check" : autoHealingPolicy . HealthCheck ,
"initial_delay_sec" : autoHealingPolicy . InitialDelaySec ,
}
autoHealingPoliciesSchema = append ( autoHealingPoliciesSchema , data )
}
return autoHealingPoliciesSchema
}
2018-03-21 19:39:12 +00:00
func resourceInstanceGroupManagerStateImporter ( d * schema . ResourceData , meta interface { } ) ( [ ] * schema . ResourceData , error ) {
d . Set ( "wait_for_instances" , false )
2018-10-16 23:53:48 +00:00
zonalID , err := parseInstanceGroupManagerId ( d . Id ( ) )
if err != nil {
return nil , err
}
if zonalID . Zone == "" || zonalID . Project == "" {
return nil , fmt . Errorf ( "Invalid instance group manager import ID. Expecting {projectId}/{zone}/{name}." )
}
d . Set ( "project" , zonalID . Project )
d . Set ( "zone" , zonalID . Zone )
d . Set ( "name" , zonalID . Name )
2018-03-21 19:39:12 +00:00
return [ ] * schema . ResourceData { d } , nil
}
2018-10-16 23:53:48 +00:00
type instanceGroupManagerId struct {
Project string
Zone string
Name string
}
func ( i instanceGroupManagerId ) terraformId ( ) string {
return fmt . Sprintf ( "%s/%s/%s" , i . Project , i . Zone , i . Name )
}
func parseInstanceGroupManagerId ( id string ) ( * instanceGroupManagerId , error ) {
switch {
case instanceGroupManagerIdRegex . MatchString ( id ) :
parts := strings . Split ( id , "/" )
return & instanceGroupManagerId {
Project : parts [ 0 ] ,
Zone : parts [ 1 ] ,
Name : parts [ 2 ] ,
} , nil
case instanceGroupManagerIdNameRegex . MatchString ( id ) :
return & instanceGroupManagerId {
Name : id ,
} , nil
default :
return nil , fmt . Errorf ( "Invalid instance group manager specifier. Expecting either {projectId}/{zone}/{name} or {name}, where {projectId} and {zone} will be derived from the provider." )
}
}