Skip to content

Commit

Permalink
Add a datasource for google_compute_instance (#1906)
Browse files Browse the repository at this point in the history
* Add a compute datasource.

* Add provider changes.

* Add self_link support to google_compute_instance datasource.

* Error check complex d.Set calls.
  • Loading branch information
rileykarson authored Sep 18, 2018
1 parent 97399b7 commit d656e91
Show file tree
Hide file tree
Showing 7 changed files with 552 additions and 39 deletions.
150 changes: 150 additions & 0 deletions google/data_source_google_compute_instance.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
package google

import (
"fmt"
"github.com/hashicorp/terraform/helper/schema"
)

func dataSourceGoogleComputeInstance() *schema.Resource {
// Generate datasource schema from resource
dsSchema := datasourceSchemaFromResourceSchema(resourceComputeInstance().Schema)

// Set 'Required' schema elements
addRequiredFieldsToSchema(dsSchema, "name")

// Set 'Optional' schema elements
addOptionalFieldsToSchema(dsSchema, "project", "zone")

return &schema.Resource{
Read: dataSourceGoogleComputeInstanceRead,
Schema: dsSchema,
}
}

func dataSourceGoogleComputeInstanceRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)

project, zone, name, err := GetZonalResourcePropertiesFromSelfLinkOrSchema(d, config)
if err != nil {
return err
}

instance, err := config.clientComputeBeta.Instances.Get(project, zone, name).Do()
if err != nil {
return handleNotFoundError(err, d, fmt.Sprintf("Instance %s", name))
}

md := flattenMetadataBeta(instance.Metadata)
if err = d.Set("metadata", md); err != nil {
return fmt.Errorf("error setting metadata: %s", err)
}

d.Set("can_ip_forward", instance.CanIpForward)
d.Set("machine_type", GetResourceNameFromSelfLink(instance.MachineType))

// Set the networks
// Use the first external IP found for the default connection info.
networkInterfaces, _, internalIP, externalIP, err := flattenNetworkInterfaces(d, config, instance.NetworkInterfaces)
if err != nil {
return err
}
if err := d.Set("network_interface", networkInterfaces); err != nil {
return err
}

// Fall back on internal ip if there is no external ip. This makes sense in the situation where
// terraform is being used on a cloud instance and can therefore access the instances it creates
// via their internal ips.
sshIP := externalIP
if sshIP == "" {
sshIP = internalIP
}

// Initialize the connection info
d.SetConnInfo(map[string]string{
"type": "ssh",
"host": sshIP,
})

// Set the metadata fingerprint if there is one.
if instance.Metadata != nil {
d.Set("metadata_fingerprint", instance.Metadata.Fingerprint)
}

// Set the tags fingerprint if there is one.
if instance.Tags != nil {
d.Set("tags_fingerprint", instance.Tags.Fingerprint)
d.Set("tags", convertStringArrToInterface(instance.Tags.Items))
}

if err := d.Set("labels", instance.Labels); err != nil {
return err
}

if instance.LabelFingerprint != "" {
d.Set("label_fingerprint", instance.LabelFingerprint)
}

attachedDisks := []map[string]interface{}{}
scratchDisks := []map[string]interface{}{}
for _, disk := range instance.Disks {
if disk.Boot {
err = d.Set("boot_disk", flattenBootDisk(d, disk, config))
if err != nil {
return err
}
} else if disk.Type == "SCRATCH" {
scratchDisks = append(scratchDisks, flattenScratchDisk(disk))
} else {
di := map[string]interface{}{
"source": ConvertSelfLinkToV1(disk.Source),
"device_name": disk.DeviceName,
"mode": disk.Mode,
}
if key := disk.DiskEncryptionKey; key != nil {
di["disk_encryption_key_sha256"] = key.Sha256
}
attachedDisks = append(attachedDisks, di)
}
}
// Remove nils from map in case there were disks in the config that were not present on read;
// i.e. a disk was detached out of band
ads := []map[string]interface{}{}
for _, d := range attachedDisks {
if d != nil {
ads = append(ads, d)
}
}

err = d.Set("service_account", flattenServiceAccounts(instance.ServiceAccounts))
if err != nil {
return err
}

err = d.Set("scheduling", flattenScheduling(instance.Scheduling))
if err != nil {
return err
}

err = d.Set("guest_accelerator", flattenGuestAccelerators(instance.GuestAccelerators))
if err != nil {
return err
}

err = d.Set("scratch_disk", scratchDisks)
if err != nil {
return err
}

d.Set("attached_disk", ads)
d.Set("cpu_platform", instance.CpuPlatform)
d.Set("min_cpu_platform", instance.MinCpuPlatform)
d.Set("deletion_protection", instance.DeletionProtection)
d.Set("self_link", ConvertSelfLinkToV1(instance.SelfLink))
d.Set("instance_id", fmt.Sprintf("%d", instance.Id))
d.Set("project", project)
d.Set("zone", GetResourceNameFromSelfLink(instance.Zone))
d.Set("name", instance.Name)
d.SetId(ConvertSelfLinkToV1(instance.SelfLink))
return nil
}
135 changes: 135 additions & 0 deletions google/data_source_google_compute_instance_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
package google

import (
"fmt"
"testing"

"github.com/hashicorp/terraform/helper/acctest"
"github.com/hashicorp/terraform/helper/resource"
"github.com/hashicorp/terraform/terraform"
)

func TestAccDataSourceComputeInstance_basic(t *testing.T) {
t.Parallel()

instanceName := fmt.Sprintf("data-instance-test-%s", acctest.RandString(10))

resource.Test(t, resource.TestCase{
PreCheck: func() { testAccPreCheck(t) },
Providers: testAccProviders,
CheckDestroy: testAccCheckComputeInstanceDestroy,
Steps: []resource.TestStep{
resource.TestStep{
Config: testAccDataSourceComputeInstanceConfig(instanceName),
Check: resource.ComposeTestCheckFunc(
testAccDataSourceComputeInstanceCheck("data.google_compute_instance.bar", "google_compute_instance.foo"),
resource.TestCheckResourceAttr("data.google_compute_instance.bar", "network_interface.#", "1"),
resource.TestCheckResourceAttr("data.google_compute_instance.bar", "boot_disk.0.initialize_params.0.size", "10"),
resource.TestCheckResourceAttr("data.google_compute_instance.bar", "boot_disk.0.initialize_params.0.type", "pd-standard"),
resource.TestCheckResourceAttr("data.google_compute_instance.bar", "scratch_disk.0.interface", "SCSI"),
resource.TestCheckResourceAttr("data.google_compute_instance.bar", "network_interface.0.access_config.0.network_tier", "PREMIUM"),
),
},
},
})
}

func testAccDataSourceComputeInstanceCheck(datasourceName string, resourceName string) resource.TestCheckFunc {
return func(s *terraform.State) error {
ds, ok := s.RootModule().Resources[datasourceName]
if !ok {
return fmt.Errorf("root module has no resource called %s", datasourceName)
}

rs, ok := s.RootModule().Resources[resourceName]
if !ok {
return fmt.Errorf("can't find %s in state", resourceName)
}

datasourceAttributes := ds.Primary.Attributes
resourceAttributes := rs.Primary.Attributes

instanceAttrsToTest := []string{
"name",
"machine_type",
"can_ip_forward",
"description",
"deletion_protection",
"labels",
"metadata",
"min_cpu_platform",
"project",
"tags",
"zone",
"cpu_platform",
"instance_id",
"label_fingerprint",
"metadata_fingerprint",
"self_link",
"tags_fingerprint",
}

for _, attrToCheck := range instanceAttrsToTest {
if datasourceAttributes[attrToCheck] != resourceAttributes[attrToCheck] {
return fmt.Errorf(
"%s is %s; want %s",
attrToCheck,
datasourceAttributes[attrToCheck],
resourceAttributes[attrToCheck],
)
}
}

return nil
}
}

func testAccDataSourceComputeInstanceConfig(instanceName string) string {
return fmt.Sprintf(`
resource "google_compute_instance" "foo" {
name = "%s"
machine_type = "n1-standard-1"
zone = "us-central1-a"
can_ip_forward = false
tags = ["foo", "bar"]
boot_disk {
initialize_params{
image = "debian-8-jessie-v20160803"
}
}
scratch_disk {
}
network_interface {
network = "default"
access_config {
// Ephemeral IP
}
}
metadata {
foo = "bar"
baz = "qux"
}
create_timeout = 5
metadata {
startup-script = "echo Hello"
}
labels {
my_key = "my_value"
my_other_key = "my_other_value"
}
}
data "google_compute_instance" "bar" {
name = "${google_compute_instance.foo.name}"
zone = "us-central1-a"
}
`, instanceName)
}
35 changes: 5 additions & 30 deletions google/data_source_google_compute_region_instance_group.go
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
package google

import (
"errors"
"fmt"
"log"
"net/url"
"strconv"
"strings"

"github.com/hashicorp/terraform/helper/schema"
compute "google.golang.org/api/compute/v1"

"google.golang.org/api/compute/v1"
"google.golang.org/api/googleapi"
)

Expand Down Expand Up @@ -90,32 +88,9 @@ func dataSourceGoogleComputeRegionInstanceGroup() *schema.Resource {

func dataSourceComputeRegionInstanceGroupRead(d *schema.ResourceData, meta interface{}) error {
config := meta.(*Config)
var project, region, name string
if self_link, ok := d.GetOk("self_link"); ok {
parsed, err := url.Parse(self_link.(string))
if err != nil {
return err
}
s := strings.Split(parsed.Path, "/")
project, region, name = s[4], s[6], s[8]
// e.g. https://www.googleapis.com/compute/beta/projects/project_name/regions/region_name/instanceGroups/foobarbaz

} else {
var err error
project, err = getProject(d, config)
if err != nil {
return err
}

region, err = getRegion(d, config)
if err != nil {
return err
}
n, ok := d.GetOk("name")
name = n.(string)
if !ok {
return errors.New("Must provide either `self_link` or `name`.")
}
project, region, name, err := GetRegionalResourcePropertiesFromSelfLinkOrSchema(d, config)
if err != nil {
return err
}

instanceGroup, err := config.clientCompute.RegionInstanceGroups.Get(
Expand Down
15 changes: 8 additions & 7 deletions google/provider.go
Original file line number Diff line number Diff line change
Expand Up @@ -67,19 +67,20 @@ func Provider() terraform.ResourceProvider {
"google_client_config": dataSourceGoogleClientConfig(),
"google_cloudfunctions_function": dataSourceGoogleCloudFunctionsFunction(),
"google_compute_address": dataSourceGoogleComputeAddress(),
"google_compute_backend_service": dataSourceGoogleComputeBackendService(),
"google_compute_default_service_account": dataSourceGoogleComputeDefaultServiceAccount(),
"google_compute_forwarding_rule": dataSourceGoogleComputeForwardingRule(),
"google_compute_image": dataSourceGoogleComputeImage(),
"google_compute_instance": dataSourceGoogleComputeInstance(),
"google_compute_instance_group": dataSourceGoogleComputeInstanceGroup(),
"google_compute_global_address": dataSourceGoogleComputeGlobalAddress(),
"google_compute_lb_ip_ranges": dataSourceGoogleComputeLbIpRanges(),
"google_compute_network": dataSourceGoogleComputeNetwork(),
"google_project": dataSourceGoogleProject(),
"google_project_services": dataSourceGoogleProjectServices(),
"google_compute_regions": dataSourceGoogleComputeRegions(),
"google_compute_region_instance_group": dataSourceGoogleComputeRegionInstanceGroup(),
"google_compute_subnetwork": dataSourceGoogleComputeSubnetwork(),
"google_compute_zones": dataSourceGoogleComputeZones(),
"google_compute_instance_group": dataSourceGoogleComputeInstanceGroup(),
"google_compute_region_instance_group": dataSourceGoogleComputeRegionInstanceGroup(),
"google_compute_vpn_gateway": dataSourceGoogleComputeVpnGateway(),
"google_compute_forwarding_rule": dataSourceGoogleComputeForwardingRule(),
"google_compute_ssl_policy": dataSourceGoogleComputeSslPolicy(),
"google_container_cluster": dataSourceGoogleContainerCluster(),
"google_container_engine_versions": dataSourceGoogleContainerEngineVersions(),
Expand All @@ -90,12 +91,12 @@ func Provider() terraform.ResourceProvider {
"google_folder": dataSourceGoogleFolder(),
"google_netblock_ip_ranges": dataSourceGoogleNetblockIpRanges(),
"google_organization": dataSourceGoogleOrganization(),
"google_project": dataSourceGoogleProject(),
"google_project_services": dataSourceGoogleProjectServices(),
"google_service_account": dataSourceGoogleServiceAccount(),
"google_service_account_key": dataSourceGoogleServiceAccountKey(),
"google_storage_object_signed_url": dataSourceGoogleSignedUrl(),
"google_storage_project_service_account": dataSourceGoogleStorageProjectServiceAccount(),
"google_compute_backend_service": dataSourceGoogleComputeBackendService(),
"google_compute_regions": dataSourceGoogleComputeRegions(),
},

ResourcesMap: mergeResourceMaps(
Expand Down
Loading

0 comments on commit d656e91

Please sign in to comment.