Merge pull request #3065 from lwander/gce-project-metadata
GCE project metadata
This commit is contained in:
commit
06c7ff4864
|
@ -43,6 +43,7 @@ func Provider() terraform.ResourceProvider {
|
||||||
"google_compute_instance": resourceComputeInstance(),
|
"google_compute_instance": resourceComputeInstance(),
|
||||||
"google_compute_instance_template": resourceComputeInstanceTemplate(),
|
"google_compute_instance_template": resourceComputeInstanceTemplate(),
|
||||||
"google_compute_network": resourceComputeNetwork(),
|
"google_compute_network": resourceComputeNetwork(),
|
||||||
|
"google_compute_project_metadata": resourceComputeProjectMetadata(),
|
||||||
"google_compute_route": resourceComputeRoute(),
|
"google_compute_route": resourceComputeRoute(),
|
||||||
"google_compute_target_pool": resourceComputeTargetPool(),
|
"google_compute_target_pool": resourceComputeTargetPool(),
|
||||||
"google_container_cluster": resourceContainerCluster(),
|
"google_container_cluster": resourceContainerCluster(),
|
||||||
|
|
|
@ -0,0 +1,241 @@
|
||||||
|
package google
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"log"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
// "github.com/hashicorp/terraform/helper/hashcode"
|
||||||
|
"github.com/hashicorp/terraform/helper/schema"
|
||||||
|
"google.golang.org/api/compute/v1"
|
||||||
|
// "google.golang.org/api/googleapi"
|
||||||
|
)
|
||||||
|
|
||||||
|
func resourceComputeProjectMetadata() *schema.Resource {
|
||||||
|
return &schema.Resource{
|
||||||
|
Create: resourceComputeProjectMetadataCreate,
|
||||||
|
Read: resourceComputeProjectMetadataRead,
|
||||||
|
Update: resourceComputeProjectMetadataUpdate,
|
||||||
|
Delete: resourceComputeProjectMetadataDelete,
|
||||||
|
|
||||||
|
SchemaVersion: 0,
|
||||||
|
|
||||||
|
Schema: map[string]*schema.Schema{
|
||||||
|
"metadata": &schema.Schema {
|
||||||
|
Elem: schema.TypeString,
|
||||||
|
Type: schema.TypeMap,
|
||||||
|
Required: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const FINGERPRINT_RETRIES = 10
|
||||||
|
const FINGERPRINT_FAIL = "Invalid fingerprint."
|
||||||
|
|
||||||
|
func resourceOperationWaitGlobal(config *Config, op *compute.Operation, activity string) error {
|
||||||
|
w := &OperationWaiter{
|
||||||
|
Service: config.clientCompute,
|
||||||
|
Op: op,
|
||||||
|
Project: config.Project,
|
||||||
|
Type: OperationWaitGlobal,
|
||||||
|
}
|
||||||
|
|
||||||
|
state := w.Conf()
|
||||||
|
state.Timeout = 2 * time.Minute
|
||||||
|
state.MinTimeout = 1 * time.Second
|
||||||
|
opRaw, err := state.WaitForState()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error waiting for %s: %s", activity, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
op = opRaw.(*compute.Operation)
|
||||||
|
if op.Error != nil {
|
||||||
|
return OperationError(*op.Error)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceComputeProjectMetadataCreate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
attempt := 0
|
||||||
|
|
||||||
|
config := meta.(*Config)
|
||||||
|
|
||||||
|
for attempt < FINGERPRINT_RETRIES {
|
||||||
|
// Load project service
|
||||||
|
log.Printf("[DEBUG] Loading project service: %s", config.Project)
|
||||||
|
project, err := config.clientCompute.Projects.Get(config.Project).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error loading project '%s': %s", config.Project, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
md := project.CommonInstanceMetadata
|
||||||
|
|
||||||
|
newMDMap := d.Get("metadata").(map[string]interface{})
|
||||||
|
// Ensure that we aren't overwriting entries that already exist
|
||||||
|
for _, kv := range(md.Items) {
|
||||||
|
if _, ok := newMDMap[kv.Key]; ok {
|
||||||
|
return fmt.Errorf("Error, key '%s' already exists in project '%s'", kv.Key, config.Project)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Append new metadata to existing metadata
|
||||||
|
for key, val := range(newMDMap) {
|
||||||
|
md.Items = append(md.Items, &compute.MetadataItems {
|
||||||
|
Key: key,
|
||||||
|
Value: val.(string),
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(config.Project, md).Do()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
|
||||||
|
|
||||||
|
// Optimistic locking requires the fingerprint recieved to match
|
||||||
|
// the fingerprint we send the server, if there is a mismatch then we
|
||||||
|
// are working on old data, and must retry
|
||||||
|
err = resourceOperationWaitGlobal(config, op, "SetCommonMetadata")
|
||||||
|
if err == nil {
|
||||||
|
return resourceComputeProjectMetadataRead(d, meta)
|
||||||
|
} else if err.Error() == FINGERPRINT_FAIL {
|
||||||
|
attempt++
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Error, unable to set metadata resource after %d attempts", attempt)
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceComputeProjectMetadataRead(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
|
||||||
|
// Load project service
|
||||||
|
log.Printf("[DEBUG] Loading project service: %s", config.Project)
|
||||||
|
project, err := config.clientCompute.Projects.Get(config.Project).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error loading project '%s': %s", config.Project, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
md := project.CommonInstanceMetadata
|
||||||
|
|
||||||
|
newMD := make(map[string]interface{})
|
||||||
|
|
||||||
|
for _, kv := range(md.Items) {
|
||||||
|
newMD[kv.Key] = kv.Value
|
||||||
|
}
|
||||||
|
|
||||||
|
if err = d.Set("metadata", newMD); err != nil {
|
||||||
|
return fmt.Errorf("Error setting metadata: %s", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
d.SetId("common_metadata")
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceComputeProjectMetadataUpdate(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
attempt := 0
|
||||||
|
|
||||||
|
config := meta.(*Config)
|
||||||
|
|
||||||
|
if d.HasChange("metadata") {
|
||||||
|
o, n := d.GetChange("metadata")
|
||||||
|
oMDMap, nMDMap := o.(map[string]interface{}), n.(map[string]interface{})
|
||||||
|
|
||||||
|
for attempt < FINGERPRINT_RETRIES {
|
||||||
|
// Load project service
|
||||||
|
log.Printf("[DEBUG] Loading project service: %s", config.Project)
|
||||||
|
project, err := config.clientCompute.Projects.Get(config.Project).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error loading project '%s': %s", config.Project, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
md := project.CommonInstanceMetadata
|
||||||
|
|
||||||
|
curMDMap := make(map[string]string)
|
||||||
|
// Load metadata on server into map
|
||||||
|
for _, kv := range(md.Items) {
|
||||||
|
// If the server state has a key that we had in our old
|
||||||
|
// state, but not in our new state, we should delete it
|
||||||
|
_, okOld := oMDMap[kv.Key]
|
||||||
|
_, okNew := nMDMap[kv.Key]
|
||||||
|
if okOld && !okNew {
|
||||||
|
continue
|
||||||
|
} else {
|
||||||
|
curMDMap[kv.Key] = kv.Value
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Insert new metadata into existing metadata (overwriting when needed)
|
||||||
|
for key, val := range(nMDMap) {
|
||||||
|
curMDMap[key] = val.(string)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Reformat old metadata into a list
|
||||||
|
md.Items = nil
|
||||||
|
for key, val := range(curMDMap) {
|
||||||
|
md.Items = append(md.Items, &compute.MetadataItems {
|
||||||
|
Key: key,
|
||||||
|
Value: val,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(config.Project, md).Do()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("SetCommonInstanceMetadata failed: %s", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
|
||||||
|
|
||||||
|
// Optimistic locking requires the fingerprint recieved to match
|
||||||
|
// the fingerprint we send the server, if there is a mismatch then we
|
||||||
|
// are working on old data, and must retry
|
||||||
|
err = resourceOperationWaitGlobal(config, op, "SetCommonMetadata")
|
||||||
|
if err == nil {
|
||||||
|
return resourceComputeProjectMetadataRead(d, meta)
|
||||||
|
} else if err.Error() == FINGERPRINT_FAIL {
|
||||||
|
attempt++
|
||||||
|
} else {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Error, unable to set metadata resource after %d attempts", attempt)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func resourceComputeProjectMetadataDelete(d *schema.ResourceData, meta interface{}) error {
|
||||||
|
config := meta.(*Config)
|
||||||
|
|
||||||
|
// Load project service
|
||||||
|
log.Printf("[DEBUG] Loading project service: %s", config.Project)
|
||||||
|
project, err := config.clientCompute.Projects.Get(config.Project).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error loading project '%s': %s", config.Project, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
md := project.CommonInstanceMetadata
|
||||||
|
|
||||||
|
// Remove all items
|
||||||
|
md.Items = nil
|
||||||
|
|
||||||
|
op, err := config.clientCompute.Projects.SetCommonInstanceMetadata(config.Project, md).Do()
|
||||||
|
|
||||||
|
log.Printf("[DEBUG] SetCommonMetadata: %d (%s)", op.Id, op.SelfLink)
|
||||||
|
|
||||||
|
err = resourceOperationWaitGlobal(config, op, "SetCommonMetadata")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
return resourceComputeProjectMetadataRead(d, meta)
|
||||||
|
}
|
|
@ -0,0 +1,217 @@
|
||||||
|
package google
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"testing"
|
||||||
|
|
||||||
|
"github.com/hashicorp/terraform/helper/resource"
|
||||||
|
"github.com/hashicorp/terraform/terraform"
|
||||||
|
"google.golang.org/api/compute/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Add two key value pairs
|
||||||
|
func TestAccComputeProjectMetadata_basic(t *testing.T) {
|
||||||
|
var project compute.Project
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckComputeProjectMetadataDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeProject_basic0_metadata,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeProjectExists(
|
||||||
|
"google_compute_project_metadata.fizzbuzz", &project),
|
||||||
|
testAccCheckComputeProjectMetadataContains(&project, "banana", "orange"),
|
||||||
|
testAccCheckComputeProjectMetadataContains(&project, "sofa", "darwinism"),
|
||||||
|
testAccCheckComputeProjectMetadataSize(&project, 2),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add three key value pairs, then replace one and modify a second
|
||||||
|
func TestAccComputeProjectMetadata_modify_1(t *testing.T) {
|
||||||
|
var project compute.Project
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckComputeProjectMetadataDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeProject_modify0_metadata,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeProjectExists(
|
||||||
|
"google_compute_project_metadata.fizzbuzz", &project),
|
||||||
|
testAccCheckComputeProjectMetadataContains(&project, "paper", "pen"),
|
||||||
|
testAccCheckComputeProjectMetadataContains(&project, "genghis_khan", "french bread"),
|
||||||
|
testAccCheckComputeProjectMetadataContains(&project, "happy", "smiling"),
|
||||||
|
testAccCheckComputeProjectMetadataSize(&project, 3),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeProject_modify1_metadata,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeProjectExists(
|
||||||
|
"google_compute_project_metadata.fizzbuzz", &project),
|
||||||
|
testAccCheckComputeProjectMetadataContains(&project, "paper", "pen"),
|
||||||
|
testAccCheckComputeProjectMetadataContains(&project, "paris", "french bread"),
|
||||||
|
testAccCheckComputeProjectMetadataContains(&project, "happy", "laughing"),
|
||||||
|
testAccCheckComputeProjectMetadataSize(&project, 3),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add two key value pairs, and replace both
|
||||||
|
func TestAccComputeProjectMetadata_modify_2(t *testing.T) {
|
||||||
|
var project compute.Project
|
||||||
|
|
||||||
|
resource.Test(t, resource.TestCase{
|
||||||
|
PreCheck: func() { testAccPreCheck(t) },
|
||||||
|
Providers: testAccProviders,
|
||||||
|
CheckDestroy: testAccCheckComputeProjectMetadataDestroy,
|
||||||
|
Steps: []resource.TestStep{
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeProject_basic0_metadata,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeProjectExists(
|
||||||
|
"google_compute_project_metadata.fizzbuzz", &project),
|
||||||
|
testAccCheckComputeProjectMetadataContains(&project, "banana", "orange"),
|
||||||
|
testAccCheckComputeProjectMetadataContains(&project, "sofa", "darwinism"),
|
||||||
|
testAccCheckComputeProjectMetadataSize(&project, 2),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
|
||||||
|
resource.TestStep{
|
||||||
|
Config: testAccComputeProject_basic1_metadata,
|
||||||
|
Check: resource.ComposeTestCheckFunc(
|
||||||
|
testAccCheckComputeProjectExists(
|
||||||
|
"google_compute_project_metadata.fizzbuzz", &project),
|
||||||
|
testAccCheckComputeProjectMetadataContains(&project, "kiwi", "papaya"),
|
||||||
|
testAccCheckComputeProjectMetadataContains(&project, "finches", "darwinism"),
|
||||||
|
testAccCheckComputeProjectMetadataSize(&project, 2),
|
||||||
|
),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeProjectMetadataDestroy(s *terraform.State) error {
|
||||||
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
|
||||||
|
project, err := config.clientCompute.Projects.Get(config.Project).Do()
|
||||||
|
if err == nil && len(project.CommonInstanceMetadata.Items) > 0 {
|
||||||
|
return fmt.Errorf("Error, metadata items still exist")
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeProjectExists(n string, project *compute.Project) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
rs, ok := s.RootModule().Resources[n]
|
||||||
|
if !ok {
|
||||||
|
return fmt.Errorf("Not found: %s", n)
|
||||||
|
}
|
||||||
|
|
||||||
|
if rs.Primary.ID == "" {
|
||||||
|
return fmt.Errorf("No ID is set")
|
||||||
|
}
|
||||||
|
|
||||||
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
|
||||||
|
found, err := config.clientCompute.Projects.Get(
|
||||||
|
config.Project).Do()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
if "common_metadata" != rs.Primary.ID {
|
||||||
|
return fmt.Errorf("Common metadata not found, found %s", rs.Primary.ID)
|
||||||
|
}
|
||||||
|
|
||||||
|
*project = *found
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeProjectMetadataContains(project *compute.Project, key string, value string) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
project, err := config.clientCompute.Projects.Get(config.Project).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error, failed to load project service for %s: %s", config.Project, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, kv := range(project.CommonInstanceMetadata.Items) {
|
||||||
|
if kv.Key == key {
|
||||||
|
if (kv.Value == value) {
|
||||||
|
return nil
|
||||||
|
} else {
|
||||||
|
return fmt.Errorf("Error, key value mismatch, wanted (%s, %s), got (%s, %s)",
|
||||||
|
key, value, kv.Key, kv.Value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return fmt.Errorf("Error, key %s not present", key)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func testAccCheckComputeProjectMetadataSize(project *compute.Project, size int) resource.TestCheckFunc {
|
||||||
|
return func(s *terraform.State) error {
|
||||||
|
config := testAccProvider.Meta().(*Config)
|
||||||
|
project, err := config.clientCompute.Projects.Get(config.Project).Do()
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("Error, failed to load project service for %s: %s", config.Project, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
if size > len(project.CommonInstanceMetadata.Items) {
|
||||||
|
return fmt.Errorf("Error, expected at least %d metadata items, got %d", size,
|
||||||
|
len(project.CommonInstanceMetadata.Items))
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const testAccComputeProject_basic0_metadata = `
|
||||||
|
resource "google_compute_project_metadata" "fizzbuzz" {
|
||||||
|
metadata {
|
||||||
|
banana = "orange"
|
||||||
|
sofa = "darwinism"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
const testAccComputeProject_basic1_metadata = `
|
||||||
|
resource "google_compute_project_metadata" "fizzbuzz" {
|
||||||
|
metadata {
|
||||||
|
kiwi = "papaya"
|
||||||
|
finches = "darwinism"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
const testAccComputeProject_modify0_metadata = `
|
||||||
|
resource "google_compute_project_metadata" "fizzbuzz" {
|
||||||
|
metadata {
|
||||||
|
paper = "pen"
|
||||||
|
genghis_khan = "french bread"
|
||||||
|
happy = "smiling"
|
||||||
|
}
|
||||||
|
}`
|
||||||
|
|
||||||
|
const testAccComputeProject_modify1_metadata = `
|
||||||
|
resource "google_compute_project_metadata" "fizzbuzz" {
|
||||||
|
metadata {
|
||||||
|
paper = "pen"
|
||||||
|
paris = "french bread"
|
||||||
|
happy = "laughing"
|
||||||
|
}
|
||||||
|
}`
|
|
@ -0,0 +1,36 @@
|
||||||
|
---
|
||||||
|
layout: "google"
|
||||||
|
page_title: "Google: google_compute_project_metadata"
|
||||||
|
sidebar_current: "docs-google-resource-project-metadata"
|
||||||
|
description: |-
|
||||||
|
Manages common instance metadata
|
||||||
|
---
|
||||||
|
|
||||||
|
# google\_compute\_project\_metadata
|
||||||
|
|
||||||
|
Manages metadata common to all instances for a project in GCE.
|
||||||
|
|
||||||
|
## Example Usage
|
||||||
|
|
||||||
|
```
|
||||||
|
resource "google_compute_project_metadata" "default" {
|
||||||
|
metadata {
|
||||||
|
foo = "bar"
|
||||||
|
fizz = "buzz"
|
||||||
|
13 = "42"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Argument Reference
|
||||||
|
|
||||||
|
The following arguments are supported:
|
||||||
|
|
||||||
|
* `metadata` - (Required) A series of key value pairs. Changing this resource updates
|
||||||
|
the GCE state.
|
||||||
|
|
||||||
|
## Attributes Reference
|
||||||
|
|
||||||
|
The following attributes are exported:
|
||||||
|
|
||||||
|
* `metadata` - Common instance metadata.
|
|
@ -53,6 +53,10 @@
|
||||||
<a href="/docs/providers/google/r/compute_network.html">google_compute_network</a>
|
<a href="/docs/providers/google/r/compute_network.html">google_compute_network</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
||||||
|
<li<%= sidebar_current("docs-google-resource-project-metadata") %>>
|
||||||
|
<a href="/docs/providers/google/r/compute_project_metadata.html">google_compute_project_metadata</a>
|
||||||
|
</li>
|
||||||
|
|
||||||
<li<%= sidebar_current("docs-google-resource-route") %>>
|
<li<%= sidebar_current("docs-google-resource-route") %>>
|
||||||
<a href="/docs/providers/google/r/compute_route.html">google_compute_route</a>
|
<a href="/docs/providers/google/r/compute_route.html">google_compute_route</a>
|
||||||
</li>
|
</li>
|
||||||
|
|
Loading…
Reference in New Issue