github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/provider/gce/google/instance.go (about)

     1  // Copyright 2014 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package google
     5  
     6  import (
     7  	"fmt"
     8  	"path"
     9  
    10  	"google.golang.org/api/compute/v1"
    11  
    12  	"github.com/juju/juju/network"
    13  )
    14  
    15  // InstanceSpec holds all the information needed to create a new GCE
    16  // instance within some zone.
    17  // TODO(ericsnow) Validate the invariants?
    18  type InstanceSpec struct {
    19  	// ID is the "name" of the instance.
    20  	ID string
    21  	// Type is the name of the GCE instance type. The value is resolved
    22  	// relative to an availability zone when the API request is sent.
    23  	// The type must match one of the GCE-recognized types.
    24  	Type string
    25  	// Disks holds the information needed to request each of the disks
    26  	// that should be attached to a new instance. This must include a
    27  	// single root disk.
    28  	Disks []DiskSpec
    29  	// Network identifies the information for the network that a new
    30  	// instance should use. If the network does not exist then it will
    31  	// be added when the instance is. At least the network's name must
    32  	// be set.
    33  	Network NetworkSpec
    34  	// NetworkInterfaces is the names of the network interfaces to
    35  	// associate with the instance. They will be connected to the the
    36  	// network identified by the instance spec. At least one name must
    37  	// be provided.
    38  	NetworkInterfaces []string
    39  	// Metadata is the GCE instance "user-specified" metadata that will
    40  	// be initialized on the new instance.
    41  	Metadata map[string]string
    42  	// Tags are the labels to associate with the instance. This is
    43  	// useful when making bulk calls or in relation to some API methods
    44  	// (e.g. related to firewalls access rules).
    45  	Tags []string
    46  }
    47  
    48  func (is InstanceSpec) raw() *compute.Instance {
    49  	return &compute.Instance{
    50  		Name:              is.ID,
    51  		Disks:             is.disks(),
    52  		NetworkInterfaces: is.networkInterfaces(),
    53  		Metadata:          packMetadata(is.Metadata),
    54  		Tags:              &compute.Tags{Items: is.Tags},
    55  		// MachineType is set in the addInstance call.
    56  	}
    57  }
    58  
    59  // Summary builds an InstanceSummary based on the spec and returns it.
    60  func (is InstanceSpec) Summary() InstanceSummary {
    61  	raw := is.raw()
    62  	return newInstanceSummary(raw)
    63  }
    64  
    65  func (is InstanceSpec) disks() []*compute.AttachedDisk {
    66  	var result []*compute.AttachedDisk
    67  	for _, spec := range is.Disks {
    68  		result = append(result, spec.newAttached())
    69  	}
    70  	return result
    71  }
    72  
    73  func (is InstanceSpec) networkInterfaces() []*compute.NetworkInterface {
    74  	var result []*compute.NetworkInterface
    75  	for _, name := range is.NetworkInterfaces {
    76  		result = append(result, is.Network.newInterface(name))
    77  	}
    78  	return result
    79  }
    80  
    81  // RootDisk identifies the root disk for a given instance (or instance
    82  // spec) and returns it. If the root disk could not be determined then
    83  // nil is returned.
    84  // TODO(ericsnow) Return an error?
    85  func (is InstanceSpec) RootDisk() *compute.AttachedDisk {
    86  	return is.Disks[0].newAttached()
    87  }
    88  
    89  // InstanceSummary captures all the data needed by Instance.
    90  type InstanceSummary struct {
    91  	// ID is the "name" of the instance.
    92  	ID string
    93  	// ZoneName is the unqualified name of the zone in which the
    94  	// instance was provisioned.
    95  	ZoneName string
    96  	// Status holds the status of the instance at a certain point in time.
    97  	Status string
    98  	// Metadata is the instance metadata.
    99  	Metadata map[string]string
   100  	// Addresses are the IP Addresses associated with the instance.
   101  	Addresses []network.Address
   102  }
   103  
   104  func newInstanceSummary(raw *compute.Instance) InstanceSummary {
   105  	return InstanceSummary{
   106  		ID:        raw.Name,
   107  		ZoneName:  path.Base(raw.Zone),
   108  		Status:    raw.Status,
   109  		Metadata:  unpackMetadata(raw.Metadata),
   110  		Addresses: extractAddresses(raw.NetworkInterfaces...),
   111  	}
   112  }
   113  
   114  // Instance represents a single realized GCE compute instance.
   115  type Instance struct {
   116  	InstanceSummary
   117  
   118  	// spec is the InstanceSpec used to create this instance.
   119  	spec *InstanceSpec
   120  }
   121  
   122  func newInstance(raw *compute.Instance, spec *InstanceSpec) *Instance {
   123  	summary := newInstanceSummary(raw)
   124  	return NewInstance(summary, spec)
   125  }
   126  
   127  // NewInstance builds an instance from the provided summary and spec
   128  // and returns it.
   129  func NewInstance(summary InstanceSummary, spec *InstanceSpec) *Instance {
   130  	if spec != nil {
   131  		// Make a copy.
   132  		val := *spec
   133  		spec = &val
   134  	}
   135  	return &Instance{
   136  		InstanceSummary: summary,
   137  		spec:            spec,
   138  	}
   139  }
   140  
   141  // RootDisk returns an AttachedDisk
   142  func (gi Instance) RootDisk() *compute.AttachedDisk {
   143  	if gi.spec == nil {
   144  		return nil
   145  	}
   146  	return gi.spec.RootDisk()
   147  }
   148  
   149  // RootDiskGB returns the size of the instance's root disk. If it
   150  // cannot be determined then 0 is returned.
   151  func (gi Instance) RootDiskGB() uint64 {
   152  	if gi.spec == nil {
   153  		return 0
   154  	}
   155  	attached := gi.RootDisk()
   156  	return uint64(attached.InitializeParams.DiskSizeGb)
   157  }
   158  
   159  // Status returns a string identifying the status of the instance. The
   160  // value will match one of the Status* constants in the package.
   161  func (gi Instance) Status() string {
   162  	return gi.InstanceSummary.Status
   163  }
   164  
   165  // Addresses identifies information about the network addresses
   166  // associated with the instance and returns it.
   167  func (gi Instance) Addresses() []network.Address {
   168  	// TODO*ericsnow) return a copy?
   169  	return gi.InstanceSummary.Addresses
   170  }
   171  
   172  // Metadata returns the user-specified metadata for the instance.
   173  func (gi Instance) Metadata() map[string]string {
   174  	// TODO*ericsnow) return a copy?
   175  	return gi.InstanceSummary.Metadata
   176  }
   177  
   178  // packMetadata composes the provided data into the format required
   179  // by the GCE API.
   180  func packMetadata(data map[string]string) *compute.Metadata {
   181  	var items []*compute.MetadataItems
   182  	for key, value := range data {
   183  		item := compute.MetadataItems{
   184  			Key:   key,
   185  			Value: value,
   186  		}
   187  		items = append(items, &item)
   188  	}
   189  	return &compute.Metadata{Items: items}
   190  }
   191  
   192  // unpackMetadata decomposes the provided data from the format used
   193  // in the GCE API.
   194  func unpackMetadata(data *compute.Metadata) map[string]string {
   195  	if data == nil {
   196  		return nil
   197  	}
   198  
   199  	result := make(map[string]string)
   200  	for _, item := range data.Items {
   201  		result[item.Key] = item.Value
   202  	}
   203  	return result
   204  }
   205  
   206  func formatMachineType(zone, name string) string {
   207  	return fmt.Sprintf("zones/%s/machineTypes/%s", zone, name)
   208  }