2015-10-30 22:27:12 +01:00
package google
import (
"fmt"
2015-11-13 21:36:03 +01:00
"log"
2015-10-30 22:27:12 +01:00
"strconv"
"github.com/hashicorp/terraform/helper/schema"
"google.golang.org/api/compute/v1"
2015-11-13 21:36:03 +01:00
"google.golang.org/api/googleapi"
2015-10-30 22:27:12 +01:00
)
func resourceComputeUrlMap ( ) * schema . Resource {
return & schema . Resource {
Create : resourceComputeUrlMapCreate ,
Read : resourceComputeUrlMapRead ,
Update : resourceComputeUrlMapUpdate ,
Delete : resourceComputeUrlMapDelete ,
Schema : map [ string ] * schema . Schema {
2016-04-10 23:34:15 +02:00
"default_service" : & schema . Schema {
2015-10-30 22:27:12 +01:00
Type : schema . TypeString ,
Required : true ,
} ,
2016-04-10 23:34:15 +02:00
"name" : & schema . Schema {
2015-10-30 22:27:12 +01:00
Type : schema . TypeString ,
Required : true ,
2016-04-10 23:34:15 +02:00
ForceNew : true ,
2015-10-30 22:27:12 +01:00
} ,
"description" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"fingerprint" : & schema . Schema {
Type : schema . TypeString ,
Computed : true ,
} ,
"host_rule" : & schema . Schema {
2016-08-08 02:47:05 +02:00
Type : schema . TypeSet ,
2015-10-30 22:27:12 +01:00
Optional : true ,
2016-08-08 02:47:05 +02:00
// TODO(evandbrown): Enable when lists support validation
//ValidateFunc: validateHostRules,
2015-10-30 22:27:12 +01:00
Elem : & schema . Resource {
Schema : map [ string ] * schema . Schema {
"description" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"hosts" : & schema . Schema {
Type : schema . TypeList ,
Required : true ,
Elem : & schema . Schema { Type : schema . TypeString } ,
} ,
"path_matcher" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
} ,
} ,
} ,
} ,
2016-04-10 23:34:15 +02:00
"id" : & schema . Schema {
Type : schema . TypeString ,
Computed : true ,
} ,
2015-10-30 22:27:12 +01:00
"path_matcher" : & schema . Schema {
Type : schema . TypeList ,
Optional : true ,
Elem : & schema . Resource {
Schema : map [ string ] * schema . Schema {
"default_service" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
} ,
"description" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"name" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
} ,
"path_rule" : & schema . Schema {
Type : schema . TypeList ,
Required : true ,
Elem : & schema . Resource {
Schema : map [ string ] * schema . Schema {
"paths" : & schema . Schema {
Type : schema . TypeList ,
Required : true ,
Elem : & schema . Schema { Type : schema . TypeString } ,
} ,
"service" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
} ,
} ,
} ,
} ,
} ,
} ,
} ,
2016-04-10 23:34:15 +02:00
"project" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
ForceNew : true ,
} ,
2015-10-30 22:27:12 +01:00
"self_link" : & schema . Schema {
Type : schema . TypeString ,
Computed : true ,
} ,
"test" : & schema . Schema {
Type : schema . TypeList ,
Optional : true ,
Elem : & schema . Resource {
Schema : map [ string ] * schema . Schema {
"description" : & schema . Schema {
Type : schema . TypeString ,
Optional : true ,
} ,
"host" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
} ,
"path" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
} ,
"service" : & schema . Schema {
Type : schema . TypeString ,
Required : true ,
} ,
} ,
} ,
} ,
} ,
}
}
func createHostRule ( v interface { } ) * compute . HostRule {
_hostRule := v . ( map [ string ] interface { } )
_hosts := _hostRule [ "hosts" ] . ( [ ] interface { } )
hosts := make ( [ ] string , len ( _hosts ) )
for i , v := range _hosts {
hosts [ i ] = v . ( string )
}
pathMatcher := _hostRule [ "path_matcher" ] . ( string )
hostRule := & compute . HostRule {
Hosts : hosts ,
PathMatcher : pathMatcher ,
}
if v , ok := _hostRule [ "description" ] ; ok {
hostRule . Description = v . ( string )
}
return hostRule
}
func createPathMatcher ( v interface { } ) * compute . PathMatcher {
_pathMatcher := v . ( map [ string ] interface { } )
_pathRules := _pathMatcher [ "path_rule" ] . ( [ ] interface { } )
pathRules := make ( [ ] * compute . PathRule , len ( _pathRules ) )
for ip , vp := range _pathRules {
_pathRule := vp . ( map [ string ] interface { } )
_paths := _pathRule [ "paths" ] . ( [ ] interface { } )
paths := make ( [ ] string , len ( _paths ) )
for ipp , vpp := range _paths {
paths [ ipp ] = vpp . ( string )
}
service := _pathRule [ "service" ] . ( string )
pathRule := & compute . PathRule {
Paths : paths ,
Service : service ,
}
pathRules [ ip ] = pathRule
}
name := _pathMatcher [ "name" ] . ( string )
defaultService := _pathMatcher [ "default_service" ] . ( string )
pathMatcher := & compute . PathMatcher {
PathRules : pathRules ,
Name : name ,
DefaultService : defaultService ,
}
if vp , okp := _pathMatcher [ "description" ] ; okp {
pathMatcher . Description = vp . ( string )
}
return pathMatcher
}
func createUrlMapTest ( v interface { } ) * compute . UrlMapTest {
_test := v . ( map [ string ] interface { } )
host := _test [ "host" ] . ( string )
path := _test [ "path" ] . ( string )
service := _test [ "service" ] . ( string )
test := & compute . UrlMapTest {
Host : host ,
Path : path ,
Service : service ,
}
if vp , okp := _test [ "description" ] ; okp {
test . Description = vp . ( string )
}
return test
}
func resourceComputeUrlMapCreate ( d * schema . ResourceData , meta interface { } ) error {
config := meta . ( * Config )
2016-04-10 18:59:57 +02:00
project , err := getProject ( d , config )
if err != nil {
return err
}
2015-10-30 22:27:12 +01:00
name := d . Get ( "name" ) . ( string )
defaultService := d . Get ( "default_service" ) . ( string )
urlMap := & compute . UrlMap {
Name : name ,
DefaultService : defaultService ,
}
if v , ok := d . GetOk ( "description" ) ; ok {
urlMap . Description = v . ( string )
}
2016-08-08 02:47:05 +02:00
_hostRules := d . Get ( "host_rule" ) . ( * schema . Set )
urlMap . HostRules = make ( [ ] * compute . HostRule , _hostRules . Len ( ) )
2015-10-30 22:27:12 +01:00
2016-08-08 02:47:05 +02:00
for i , v := range _hostRules . List ( ) {
2015-10-30 22:27:12 +01:00
urlMap . HostRules [ i ] = createHostRule ( v )
}
_pathMatchers := d . Get ( "path_matcher" ) . ( [ ] interface { } )
urlMap . PathMatchers = make ( [ ] * compute . PathMatcher , len ( _pathMatchers ) )
for i , v := range _pathMatchers {
urlMap . PathMatchers [ i ] = createPathMatcher ( v )
}
_tests := make ( [ ] interface { } , 0 )
if v , ok := d . GetOk ( "test" ) ; ok {
_tests = v . ( [ ] interface { } )
}
urlMap . Tests = make ( [ ] * compute . UrlMapTest , len ( _tests ) )
for i , v := range _tests {
urlMap . Tests [ i ] = createUrlMapTest ( v )
}
2016-04-10 18:59:57 +02:00
op , err := config . clientCompute . UrlMaps . Insert ( project , urlMap ) . Do ( )
2015-10-30 22:27:12 +01:00
if err != nil {
return fmt . Errorf ( "Error, failed to insert Url Map %s: %s" , name , err )
}
2016-06-06 19:35:13 +02:00
err = computeOperationWaitGlobal ( config , op , project , "Insert Url Map" )
2015-10-30 22:27:12 +01:00
if err != nil {
return fmt . Errorf ( "Error, failed waitng to insert Url Map %s: %s" , name , err )
}
return resourceComputeUrlMapRead ( d , meta )
}
func resourceComputeUrlMapRead ( d * schema . ResourceData , meta interface { } ) error {
config := meta . ( * Config )
2016-04-10 18:59:57 +02:00
project , err := getProject ( d , config )
if err != nil {
return err
}
2015-10-30 22:27:12 +01:00
name := d . Get ( "name" ) . ( string )
2016-04-10 18:59:57 +02:00
urlMap , err := config . clientCompute . UrlMaps . Get ( project , name ) . Do ( )
2015-10-30 22:27:12 +01:00
if err != nil {
2015-11-13 21:36:03 +01:00
if gerr , ok := err . ( * googleapi . Error ) ; ok && gerr . Code == 404 {
log . Printf ( "[WARN] Removing URL Map %q because it's gone" , d . Get ( "name" ) . ( string ) )
// The resource doesn't exist anymore
d . SetId ( "" )
return nil
}
2015-10-30 22:27:12 +01:00
return fmt . Errorf ( "Error, failed to get Url Map %s: %s" , name , err )
}
d . SetId ( name )
d . Set ( "self_link" , urlMap . SelfLink )
d . Set ( "id" , strconv . FormatUint ( urlMap . Id , 10 ) )
d . Set ( "fingerprint" , urlMap . Fingerprint )
hostRuleMap := make ( map [ string ] * compute . HostRule )
for _ , v := range urlMap . HostRules {
hostRuleMap [ v . PathMatcher ] = v
}
/* Only read host rules into our TF state that we have defined */
2016-08-08 02:47:05 +02:00
_hostRules := d . Get ( "host_rule" ) . ( * schema . Set ) . List ( )
2015-10-30 22:27:12 +01:00
_newHostRules := make ( [ ] interface { } , 0 )
for _ , v := range _hostRules {
_hostRule := v . ( map [ string ] interface { } )
_pathMatcher := _hostRule [ "path_matcher" ] . ( string )
/* Delete local entries that are no longer found on the GCE server */
if hostRule , ok := hostRuleMap [ _pathMatcher ] ; ok {
_newHostRule := make ( map [ string ] interface { } )
_newHostRule [ "path_matcher" ] = _pathMatcher
hostsSet := make ( map [ string ] bool )
for _ , host := range hostRule . Hosts {
hostsSet [ host ] = true
}
/* Only store hosts we are keeping track of */
_newHosts := make ( [ ] interface { } , 0 )
for _ , vp := range _hostRule [ "hosts" ] . ( [ ] interface { } ) {
if _ , okp := hostsSet [ vp . ( string ) ] ; okp {
_newHosts = append ( _newHosts , vp )
}
}
_newHostRule [ "hosts" ] = _newHosts
_newHostRule [ "description" ] = hostRule . Description
_newHostRules = append ( _newHostRules , _newHostRule )
}
}
d . Set ( "host_rule" , _newHostRules )
pathMatcherMap := make ( map [ string ] * compute . PathMatcher )
for _ , v := range urlMap . PathMatchers {
pathMatcherMap [ v . Name ] = v
}
/* Only read path matchers into our TF state that we have defined */
_pathMatchers := d . Get ( "path_matcher" ) . ( [ ] interface { } )
_newPathMatchers := make ( [ ] interface { } , 0 )
for _ , v := range _pathMatchers {
_pathMatcher := v . ( map [ string ] interface { } )
_name := _pathMatcher [ "name" ] . ( string )
if pathMatcher , ok := pathMatcherMap [ _name ] ; ok {
_newPathMatcher := make ( map [ string ] interface { } )
_newPathMatcher [ "name" ] = _name
_newPathMatcher [ "default_service" ] = pathMatcher . DefaultService
_newPathMatcher [ "description" ] = pathMatcher . Description
_newPathRules := make ( [ ] interface { } , len ( pathMatcher . PathRules ) )
for ip , pathRule := range pathMatcher . PathRules {
_newPathRule := make ( map [ string ] interface { } )
_newPathRule [ "service" ] = pathRule . Service
_paths := make ( [ ] interface { } , len ( pathRule . Paths ) )
for ipp , vpp := range pathRule . Paths {
_paths [ ipp ] = vpp
}
_newPathRule [ "paths" ] = _paths
_newPathRules [ ip ] = _newPathRule
}
_newPathMatcher [ "path_rule" ] = _newPathRules
_newPathMatchers = append ( _newPathMatchers , _newPathMatcher )
}
}
d . Set ( "path_matcher" , _newPathMatchers )
testMap := make ( map [ string ] * compute . UrlMapTest )
for _ , v := range urlMap . Tests {
testMap [ fmt . Sprintf ( "%s/%s" , v . Host , v . Path ) ] = v
}
_tests := make ( [ ] interface { } , 0 )
/* Only read tests into our TF state that we have defined */
if v , ok := d . GetOk ( "test" ) ; ok {
_tests = v . ( [ ] interface { } )
}
_newTests := make ( [ ] interface { } , 0 )
for _ , v := range _tests {
_test := v . ( map [ string ] interface { } )
_host := _test [ "host" ] . ( string )
_path := _test [ "path" ] . ( string )
/* Delete local entries that are no longer found on the GCE server */
if test , ok := testMap [ fmt . Sprintf ( "%s/%s" , _host , _path ) ] ; ok {
_newTest := make ( map [ string ] interface { } )
_newTest [ "host" ] = _host
_newTest [ "path" ] = _path
_newTest [ "description" ] = test . Description
_newTest [ "service" ] = test . Service
_newTests = append ( _newTests , _newTest )
}
}
d . Set ( "test" , _newTests )
return nil
}
func resourceComputeUrlMapUpdate ( d * schema . ResourceData , meta interface { } ) error {
config := meta . ( * Config )
2016-04-10 18:59:57 +02:00
project , err := getProject ( d , config )
if err != nil {
return err
}
2015-10-30 22:27:12 +01:00
name := d . Get ( "name" ) . ( string )
2016-04-10 18:59:57 +02:00
urlMap , err := config . clientCompute . UrlMaps . Get ( project , name ) . Do ( )
2015-10-30 22:27:12 +01:00
if err != nil {
return fmt . Errorf ( "Error, failed to get Url Map %s: %s" , name , err )
}
urlMap . DefaultService = d . Get ( "default_service" ) . ( string )
if v , ok := d . GetOk ( "description" ) ; ok {
urlMap . Description = v . ( string )
}
if d . HasChange ( "host_rule" ) {
_oldHostRules , _newHostRules := d . GetChange ( "host_rule" )
_oldHostRulesMap := make ( map [ string ] interface { } )
_newHostRulesMap := make ( map [ string ] interface { } )
2016-08-08 02:47:05 +02:00
for _ , v := range _oldHostRules . ( * schema . Set ) . List ( ) {
2015-10-30 22:27:12 +01:00
_hostRule := v . ( map [ string ] interface { } )
_oldHostRulesMap [ _hostRule [ "path_matcher" ] . ( string ) ] = v
}
2016-08-08 02:47:05 +02:00
for _ , v := range _newHostRules . ( * schema . Set ) . List ( ) {
2015-10-30 22:27:12 +01:00
_hostRule := v . ( map [ string ] interface { } )
_newHostRulesMap [ _hostRule [ "path_matcher" ] . ( string ) ] = v
}
newHostRules := make ( [ ] * compute . HostRule , 0 )
/* Decide which host rules to keep */
for _ , v := range urlMap . HostRules {
/* If it's in the old state, we have ownership over the host rule */
if vOld , ok := _oldHostRulesMap [ v . PathMatcher ] ; ok {
if vNew , ok := _newHostRulesMap [ v . PathMatcher ] ; ok {
/* Adjust for any changes made to this rule */
_newHostRule := vNew . ( map [ string ] interface { } )
_oldHostRule := vOld . ( map [ string ] interface { } )
_newHostsSet := make ( map [ string ] bool )
_oldHostsSet := make ( map [ string ] bool )
hostRule := & compute . HostRule {
PathMatcher : v . PathMatcher ,
}
for _ , v := range _newHostRule [ "hosts" ] . ( [ ] interface { } ) {
_newHostsSet [ v . ( string ) ] = true
}
for _ , v := range _oldHostRule [ "hosts" ] . ( [ ] interface { } ) {
_oldHostsSet [ v . ( string ) ] = true
}
/ * Only add hosts that have been added locally or are new ,
* not touching those from the GCE server state * /
for _ , host := range v . Hosts {
_ , okNew := _newHostsSet [ host ]
_ , okOld := _oldHostsSet [ host ]
/* Drop deleted hosts */
if okOld && ! okNew {
continue
}
hostRule . Hosts = append ( hostRule . Hosts , host )
/* Kep track of the fact that this host was added */
delete ( _newHostsSet , host )
}
/* Now add in the brand new entries */
2016-08-08 02:47:05 +02:00
for host , _ := range _newHostsSet {
2015-10-30 22:27:12 +01:00
hostRule . Hosts = append ( hostRule . Hosts , host )
}
if v , ok := _newHostRule [ "description" ] ; ok {
hostRule . Description = v . ( string )
}
newHostRules = append ( newHostRules , hostRule )
/* Record that we've include this host rule */
delete ( _newHostRulesMap , v . PathMatcher )
} else {
/* It's been deleted */
continue
}
} else {
if vNew , ok := _newHostRulesMap [ v . PathMatcher ] ; ok {
newHostRules = append ( newHostRules , createHostRule ( vNew ) )
/* Record that we've include this host rule */
delete ( _newHostRulesMap , v . PathMatcher )
} else {
/* It wasn't created or modified locally */
newHostRules = append ( newHostRules , v )
}
}
}
/* Record brand new host rules (ones not deleted above) */
for _ , v := range _newHostRulesMap {
newHostRules = append ( newHostRules , createHostRule ( v ) )
}
urlMap . HostRules = newHostRules
}
if d . HasChange ( "path_matcher" ) {
_oldPathMatchers , _newPathMatchers := d . GetChange ( "path_matcher" )
_oldPathMatchersMap := make ( map [ string ] interface { } )
_newPathMatchersMap := make ( map [ string ] interface { } )
for _ , v := range _oldPathMatchers . ( [ ] interface { } ) {
_pathMatcher := v . ( map [ string ] interface { } )
_oldPathMatchersMap [ _pathMatcher [ "name" ] . ( string ) ] = v
}
for _ , v := range _newPathMatchers . ( [ ] interface { } ) {
_pathMatcher := v . ( map [ string ] interface { } )
_newPathMatchersMap [ _pathMatcher [ "name" ] . ( string ) ] = v
}
newPathMatchers := make ( [ ] * compute . PathMatcher , 0 )
/* Decide which path matchers to keep */
for _ , v := range urlMap . PathMatchers {
/* If it's in the old state, we have ownership over the host rule */
_ , okOld := _oldPathMatchersMap [ v . Name ]
vNew , okNew := _newPathMatchersMap [ v . Name ]
/* Drop deleted entries */
if okOld && ! okNew {
continue
}
/* Don't change entries that don't belong to us */
if ! okNew {
newPathMatchers = append ( newPathMatchers , v )
} else {
newPathMatchers = append ( newPathMatchers , createPathMatcher ( vNew ) )
delete ( _newPathMatchersMap , v . Name )
}
}
/* Record brand new host rules */
for _ , v := range _newPathMatchersMap {
newPathMatchers = append ( newPathMatchers , createPathMatcher ( v ) )
}
urlMap . PathMatchers = newPathMatchers
}
if d . HasChange ( "tests" ) {
_oldTests , _newTests := d . GetChange ( "path_matcher" )
_oldTestsMap := make ( map [ string ] interface { } )
_newTestsMap := make ( map [ string ] interface { } )
for _ , v := range _oldTests . ( [ ] interface { } ) {
_test := v . ( map [ string ] interface { } )
ident := fmt . Sprintf ( "%s/%s" , _test [ "host" ] . ( string ) , _test [ "path" ] . ( string ) )
_oldTestsMap [ ident ] = v
}
for _ , v := range _newTests . ( [ ] interface { } ) {
_test := v . ( map [ string ] interface { } )
ident := fmt . Sprintf ( "%s/%s" , _test [ "host" ] . ( string ) , _test [ "path" ] . ( string ) )
_newTestsMap [ ident ] = v
}
newTests := make ( [ ] * compute . UrlMapTest , 0 )
/* Decide which path matchers to keep */
for _ , v := range urlMap . Tests {
ident := fmt . Sprintf ( "%s/%s" , v . Host , v . Path )
/* If it's in the old state, we have ownership over the host rule */
_ , okOld := _oldTestsMap [ ident ]
vNew , okNew := _newTestsMap [ ident ]
/* Drop deleted entries */
if okOld && ! okNew {
continue
}
/* Don't change entries that don't belong to us */
if ! okNew {
newTests = append ( newTests , v )
} else {
newTests = append ( newTests , createUrlMapTest ( vNew ) )
delete ( _newTestsMap , ident )
}
}
/* Record brand new host rules */
for _ , v := range _newTestsMap {
newTests = append ( newTests , createUrlMapTest ( v ) )
}
urlMap . Tests = newTests
}
2016-04-10 18:59:57 +02:00
op , err := config . clientCompute . UrlMaps . Update ( project , urlMap . Name , urlMap ) . Do ( )
2015-10-30 22:27:12 +01:00
if err != nil {
return fmt . Errorf ( "Error, failed to update Url Map %s: %s" , name , err )
}
2016-06-06 19:35:13 +02:00
err = computeOperationWaitGlobal ( config , op , project , "Update Url Map" )
2015-10-30 22:27:12 +01:00
if err != nil {
return fmt . Errorf ( "Error, failed waitng to update Url Map %s: %s" , name , err )
}
return resourceComputeUrlMapRead ( d , meta )
}
func resourceComputeUrlMapDelete ( d * schema . ResourceData , meta interface { } ) error {
config := meta . ( * Config )
2016-04-10 18:59:57 +02:00
project , err := getProject ( d , config )
if err != nil {
return err
}
2015-10-30 22:27:12 +01:00
name := d . Get ( "name" ) . ( string )
2016-04-10 18:59:57 +02:00
op , err := config . clientCompute . UrlMaps . Delete ( project , name ) . Do ( )
2015-10-30 22:27:12 +01:00
if err != nil {
return fmt . Errorf ( "Error, failed to delete Url Map %s: %s" , name , err )
}
2016-06-06 19:35:13 +02:00
err = computeOperationWaitGlobal ( config , op , project , "Delete Url Map" )
2015-10-30 22:27:12 +01:00
if err != nil {
return fmt . Errorf ( "Error, failed waitng to delete Url Map %s: %s" , name , err )
}
return nil
}
2016-08-08 02:47:05 +02:00
func validateHostRules ( v interface { } , k string ) ( ws [ ] string , es [ ] error ) {
pathMatchers := make ( map [ string ] bool )
hostRules := v . ( [ ] interface { } )
for _ , hri := range hostRules {
hr := hri . ( map [ string ] interface { } )
pm := hr [ "path_matcher" ] . ( string )
if pathMatchers [ pm ] {
es = append ( es , fmt . Errorf ( "Multiple host_rule entries with the same path_matcher are not allowed. Please collapse all hosts with the same path_matcher into one host_rule" ) )
return
}
pathMatchers [ pm ] = true
}
return
}