github.com/koding/terraform@v0.6.4-0.20170608090606-5d7e0339779d/builtin/providers/google/provider.go (about)

     1  package google
     2  
     3  import (
     4  	"encoding/json"
     5  	"fmt"
     6  	"log"
     7  	"strings"
     8  
     9  	"github.com/hashicorp/terraform/helper/mutexkv"
    10  	"github.com/hashicorp/terraform/helper/schema"
    11  	"github.com/hashicorp/terraform/terraform"
    12  	"google.golang.org/api/compute/v1"
    13  	"google.golang.org/api/googleapi"
    14  )
    15  
    16  // Global MutexKV
    17  var mutexKV = mutexkv.NewMutexKV()
    18  
    19  // Provider returns a terraform.ResourceProvider.
    20  func Provider() terraform.ResourceProvider {
    21  	return &schema.Provider{
    22  		Schema: map[string]*schema.Schema{
    23  			"credentials": &schema.Schema{
    24  				Type:     schema.TypeString,
    25  				Optional: true,
    26  				DefaultFunc: schema.MultiEnvDefaultFunc([]string{
    27  					"GOOGLE_CREDENTIALS",
    28  					"GOOGLE_CLOUD_KEYFILE_JSON",
    29  					"GCLOUD_KEYFILE_JSON",
    30  				}, nil),
    31  				ValidateFunc: validateCredentials,
    32  			},
    33  
    34  			"project": &schema.Schema{
    35  				Type:     schema.TypeString,
    36  				Optional: true,
    37  				DefaultFunc: schema.MultiEnvDefaultFunc([]string{
    38  					"GOOGLE_PROJECT",
    39  					"GCLOUD_PROJECT",
    40  					"CLOUDSDK_CORE_PROJECT",
    41  				}, nil),
    42  			},
    43  
    44  			"region": &schema.Schema{
    45  				Type:     schema.TypeString,
    46  				Required: true,
    47  				DefaultFunc: schema.MultiEnvDefaultFunc([]string{
    48  					"GOOGLE_REGION",
    49  					"GCLOUD_REGION",
    50  					"CLOUDSDK_COMPUTE_REGION",
    51  				}, nil),
    52  			},
    53  		},
    54  
    55  		DataSourcesMap: map[string]*schema.Resource{
    56  			"google_compute_network":           dataSourceGoogleComputeNetwork(),
    57  			"google_compute_subnetwork":        dataSourceGoogleComputeSubnetwork(),
    58  			"google_compute_zones":             dataSourceGoogleComputeZones(),
    59  			"google_container_engine_versions": dataSourceGoogleContainerEngineVersions(),
    60  			"google_iam_policy":                dataSourceGoogleIamPolicy(),
    61  			"google_storage_object_signed_url": dataSourceGoogleSignedUrl(),
    62  		},
    63  
    64  		ResourcesMap: map[string]*schema.Resource{
    65  			"google_bigquery_dataset":               resourceBigQueryDataset(),
    66  			"google_bigquery_table":                 resourceBigQueryTable(),
    67  			"google_compute_autoscaler":             resourceComputeAutoscaler(),
    68  			"google_compute_address":                resourceComputeAddress(),
    69  			"google_compute_backend_bucket":         resourceComputeBackendBucket(),
    70  			"google_compute_backend_service":        resourceComputeBackendService(),
    71  			"google_compute_disk":                   resourceComputeDisk(),
    72  			"google_compute_snapshot":               resourceComputeSnapshot(),
    73  			"google_compute_firewall":               resourceComputeFirewall(),
    74  			"google_compute_forwarding_rule":        resourceComputeForwardingRule(),
    75  			"google_compute_global_address":         resourceComputeGlobalAddress(),
    76  			"google_compute_global_forwarding_rule": resourceComputeGlobalForwardingRule(),
    77  			"google_compute_health_check":           resourceComputeHealthCheck(),
    78  			"google_compute_http_health_check":      resourceComputeHttpHealthCheck(),
    79  			"google_compute_https_health_check":     resourceComputeHttpsHealthCheck(),
    80  			"google_compute_image":                  resourceComputeImage(),
    81  			"google_compute_instance":               resourceComputeInstance(),
    82  			"google_compute_instance_group":         resourceComputeInstanceGroup(),
    83  			"google_compute_instance_group_manager": resourceComputeInstanceGroupManager(),
    84  			"google_compute_instance_template":      resourceComputeInstanceTemplate(),
    85  			"google_compute_network":                resourceComputeNetwork(),
    86  			"google_compute_project_metadata":       resourceComputeProjectMetadata(),
    87  			"google_compute_region_backend_service": resourceComputeRegionBackendService(),
    88  			"google_compute_route":                  resourceComputeRoute(),
    89  			"google_compute_router":                 resourceComputeRouter(),
    90  			"google_compute_router_interface":       resourceComputeRouterInterface(),
    91  			"google_compute_router_peer":            resourceComputeRouterPeer(),
    92  			"google_compute_ssl_certificate":        resourceComputeSslCertificate(),
    93  			"google_compute_subnetwork":             resourceComputeSubnetwork(),
    94  			"google_compute_target_http_proxy":      resourceComputeTargetHttpProxy(),
    95  			"google_compute_target_https_proxy":     resourceComputeTargetHttpsProxy(),
    96  			"google_compute_target_pool":            resourceComputeTargetPool(),
    97  			"google_compute_url_map":                resourceComputeUrlMap(),
    98  			"google_compute_vpn_gateway":            resourceComputeVpnGateway(),
    99  			"google_compute_vpn_tunnel":             resourceComputeVpnTunnel(),
   100  			"google_container_cluster":              resourceContainerCluster(),
   101  			"google_container_node_pool":            resourceContainerNodePool(),
   102  			"google_dns_managed_zone":               resourceDnsManagedZone(),
   103  			"google_dns_record_set":                 resourceDnsRecordSet(),
   104  			"google_sql_database":                   resourceSqlDatabase(),
   105  			"google_sql_database_instance":          resourceSqlDatabaseInstance(),
   106  			"google_sql_user":                       resourceSqlUser(),
   107  			"google_project":                        resourceGoogleProject(),
   108  			"google_project_iam_policy":             resourceGoogleProjectIamPolicy(),
   109  			"google_project_services":               resourceGoogleProjectServices(),
   110  			"google_pubsub_topic":                   resourcePubsubTopic(),
   111  			"google_pubsub_subscription":            resourcePubsubSubscription(),
   112  			"google_service_account":                resourceGoogleServiceAccount(),
   113  			"google_storage_bucket":                 resourceStorageBucket(),
   114  			"google_storage_bucket_acl":             resourceStorageBucketAcl(),
   115  			"google_storage_bucket_object":          resourceStorageBucketObject(),
   116  			"google_storage_object_acl":             resourceStorageObjectAcl(),
   117  		},
   118  
   119  		ConfigureFunc: providerConfigure,
   120  	}
   121  }
   122  
   123  func providerConfigure(d *schema.ResourceData) (interface{}, error) {
   124  	credentials := d.Get("credentials").(string)
   125  	config := Config{
   126  		Credentials: credentials,
   127  		Project:     d.Get("project").(string),
   128  		Region:      d.Get("region").(string),
   129  	}
   130  
   131  	if err := config.loadAndValidate(); err != nil {
   132  		return nil, err
   133  	}
   134  
   135  	return &config, nil
   136  }
   137  
   138  func validateCredentials(v interface{}, k string) (warnings []string, errors []error) {
   139  	if v == nil || v.(string) == "" {
   140  		return
   141  	}
   142  	creds := v.(string)
   143  	var account accountFile
   144  	if err := json.Unmarshal([]byte(creds), &account); err != nil {
   145  		errors = append(errors,
   146  			fmt.Errorf("credentials are not valid JSON '%s': %s", creds, err))
   147  	}
   148  
   149  	return
   150  }
   151  
   152  // getRegionFromZone returns the region from a zone for Google cloud.
   153  func getRegionFromZone(zone string) string {
   154  	if zone != "" && len(zone) > 2 {
   155  		region := zone[:len(zone)-2]
   156  		return region
   157  	}
   158  	return ""
   159  }
   160  
   161  // getRegion reads the "region" field from the given resource data and falls
   162  // back to the provider's value if not given. If the provider's value is not
   163  // given, an error is returned.
   164  func getRegion(d *schema.ResourceData, config *Config) (string, error) {
   165  	res, ok := d.GetOk("region")
   166  	if !ok {
   167  		if config.Region != "" {
   168  			return config.Region, nil
   169  		}
   170  		return "", fmt.Errorf("%q: required field is not set", "region")
   171  	}
   172  	return res.(string), nil
   173  }
   174  
   175  // getProject reads the "project" field from the given resource data and falls
   176  // back to the provider's value if not given. If the provider's value is not
   177  // given, an error is returned.
   178  func getProject(d *schema.ResourceData, config *Config) (string, error) {
   179  	res, ok := d.GetOk("project")
   180  	if !ok {
   181  		if config.Project != "" {
   182  			return config.Project, nil
   183  		}
   184  		return "", fmt.Errorf("%q: required field is not set", "project")
   185  	}
   186  	return res.(string), nil
   187  }
   188  
   189  func getZonalResourceFromRegion(getResource func(string) (interface{}, error), region string, compute *compute.Service, project string) (interface{}, error) {
   190  	zoneList, err := compute.Zones.List(project).Do()
   191  	if err != nil {
   192  		return nil, err
   193  	}
   194  	var resource interface{}
   195  	for _, zone := range zoneList.Items {
   196  		if strings.Contains(zone.Name, region) {
   197  			resource, err = getResource(zone.Name)
   198  			if err != nil {
   199  				if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   200  					// Resource was not found in this zone
   201  					continue
   202  				}
   203  				return nil, fmt.Errorf("Error reading Resource: %s", err)
   204  			}
   205  			// Resource was found
   206  			return resource, nil
   207  		}
   208  	}
   209  	// Resource does not exist in this region
   210  	return nil, nil
   211  }
   212  
   213  // getNetworkLink reads the "network" field from the given resource data and if the value:
   214  // - is a resource URL, returns the string unchanged
   215  // - is the network name only, then looks up the resource URL using the google client
   216  func getNetworkLink(d *schema.ResourceData, config *Config, field string) (string, error) {
   217  	if v, ok := d.GetOk(field); ok {
   218  		network := v.(string)
   219  
   220  		project, err := getProject(d, config)
   221  		if err != nil {
   222  			return "", err
   223  		}
   224  
   225  		if !strings.HasPrefix(network, "https://www.googleapis.com/compute/") {
   226  			// Network value provided is just the name, lookup the network SelfLink
   227  			networkData, err := config.clientCompute.Networks.Get(
   228  				project, network).Do()
   229  			if err != nil {
   230  				return "", fmt.Errorf("Error reading network: %s", err)
   231  			}
   232  			network = networkData.SelfLink
   233  		}
   234  
   235  		return network, nil
   236  
   237  	} else {
   238  		return "", nil
   239  	}
   240  }
   241  
   242  // getNetworkName reads the "network" field from the given resource data and if the value:
   243  // - is a resource URL, extracts the network name from the URL and returns it
   244  // - is the network name only (i.e not prefixed with http://www.googleapis.com/compute/...), is returned unchanged
   245  func getNetworkName(d *schema.ResourceData, field string) (string, error) {
   246  	if v, ok := d.GetOk(field); ok {
   247  		network := v.(string)
   248  		return getNetworkNameFromSelfLink(network)
   249  	}
   250  	return "", nil
   251  }
   252  
   253  func getNetworkNameFromSelfLink(network string) (string, error) {
   254  	if strings.HasPrefix(network, "https://www.googleapis.com/compute/") {
   255  		// extract the network name from SelfLink URL
   256  		networkName := network[strings.LastIndex(network, "/")+1:]
   257  		if networkName == "" {
   258  			return "", fmt.Errorf("network url not valid")
   259  		}
   260  		return networkName, nil
   261  	}
   262  
   263  	return network, nil
   264  }
   265  
   266  func getRouterLockName(region string, router string) string {
   267  	return fmt.Sprintf("router/%s/%s", region, router)
   268  }
   269  
   270  func handleNotFoundError(err error, d *schema.ResourceData, resource string) error {
   271  	if gerr, ok := err.(*googleapi.Error); ok && gerr.Code == 404 {
   272  		log.Printf("[WARN] Removing %s because it's gone", resource)
   273  		// The resource doesn't exist anymore
   274  		d.SetId("")
   275  
   276  		return nil
   277  	}
   278  
   279  	return fmt.Errorf("Error reading %s: %s", resource, err)
   280  }
   281  
   282  func linkDiffSuppress(k, old, new string, d *schema.ResourceData) bool {
   283  	parts := strings.Split(old, "/")
   284  	if parts[len(parts)-1] == new {
   285  		return true
   286  	}
   287  	return false
   288  }