github.com/wallyworld/juju@v0.0.0-20161013125918-6cf1bc9d917a/api/provisioner/machine.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package provisioner
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"gopkg.in/juju/names.v2"
    10  
    11  	apiwatcher "github.com/juju/juju/api/watcher"
    12  	"github.com/juju/juju/apiserver/params"
    13  	"github.com/juju/juju/instance"
    14  	"github.com/juju/juju/status"
    15  	"github.com/juju/juju/watcher"
    16  )
    17  
    18  // Machine represents a juju machine as seen by the provisioner worker.
    19  type Machine struct {
    20  	tag  names.MachineTag
    21  	life params.Life
    22  	st   *State
    23  }
    24  
    25  // Tag returns the machine's tag.
    26  func (m *Machine) Tag() names.Tag {
    27  	return m.tag
    28  }
    29  
    30  // Id returns the machine id.
    31  func (m *Machine) Id() string {
    32  	return m.tag.Id()
    33  }
    34  
    35  // String returns the machine as a string.
    36  func (m *Machine) String() string {
    37  	return m.Id()
    38  }
    39  
    40  // Life returns the machine's lifecycle value.
    41  func (m *Machine) Life() params.Life {
    42  	return m.life
    43  }
    44  
    45  // Refresh updates the cached local copy of the machine's data.
    46  func (m *Machine) Refresh() error {
    47  	life, err := m.st.machineLife(m.tag)
    48  	if err != nil {
    49  		return err
    50  	}
    51  	m.life = life
    52  	return nil
    53  }
    54  
    55  // ProvisioningInfo returns the information required to provision a machine.
    56  func (m *Machine) ProvisioningInfo() (*params.ProvisioningInfo, error) {
    57  	var results params.ProvisioningInfoResults
    58  	args := params.Entities{Entities: []params.Entity{{m.tag.String()}}}
    59  	err := m.st.facade.FacadeCall("ProvisioningInfo", args, &results)
    60  	if err != nil {
    61  		return nil, err
    62  	}
    63  	if len(results.Results) != 1 {
    64  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
    65  	}
    66  	result := results.Results[0]
    67  	if result.Error != nil {
    68  		return nil, result.Error
    69  	}
    70  	return result.Result, nil
    71  }
    72  
    73  // SetInstanceStatus sets the status for the provider instance.
    74  func (m *Machine) SetInstanceStatus(status status.Status, message string, data map[string]interface{}) error {
    75  	var result params.ErrorResults
    76  	args := params.SetStatus{Entities: []params.EntityStatusArgs{
    77  		{Tag: m.tag.String(), Status: status.String(), Info: message, Data: data},
    78  	}}
    79  	err := m.st.facade.FacadeCall("SetInstanceStatus", args, &result)
    80  	if err != nil {
    81  		return err
    82  	}
    83  	return result.OneError()
    84  }
    85  
    86  // InstanceStatus returns the status of the provider instance.
    87  func (m *Machine) InstanceStatus() (status.Status, string, error) {
    88  	var results params.StatusResults
    89  	args := params.Entities{Entities: []params.Entity{
    90  		{Tag: m.tag.String()},
    91  	}}
    92  	err := m.st.facade.FacadeCall("InstanceStatus", args, &results)
    93  	if err != nil {
    94  		return "", "", err
    95  	}
    96  	if len(results.Results) != 1 {
    97  		return "", "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
    98  	}
    99  	result := results.Results[0]
   100  	if result.Error != nil {
   101  		return "", "", result.Error
   102  	}
   103  	// TODO(perrito666) add status validation.
   104  	return status.Status(result.Status), result.Info, nil
   105  }
   106  
   107  // SetStatus sets the status of the machine.
   108  func (m *Machine) SetStatus(status status.Status, info string, data map[string]interface{}) error {
   109  	var result params.ErrorResults
   110  	args := params.SetStatus{
   111  		Entities: []params.EntityStatusArgs{
   112  			{Tag: m.tag.String(), Status: status.String(), Info: info, Data: data},
   113  		},
   114  	}
   115  	err := m.st.facade.FacadeCall("SetStatus", args, &result)
   116  	if err != nil {
   117  		return err
   118  	}
   119  	return result.OneError()
   120  }
   121  
   122  // Status returns the status of the machine.
   123  func (m *Machine) Status() (status.Status, string, error) {
   124  	var results params.StatusResults
   125  	args := params.Entities{
   126  		Entities: []params.Entity{{Tag: m.tag.String()}},
   127  	}
   128  	err := m.st.facade.FacadeCall("Status", args, &results)
   129  	if err != nil {
   130  		return "", "", err
   131  	}
   132  	if len(results.Results) != 1 {
   133  		return "", "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   134  	}
   135  	result := results.Results[0]
   136  	if result.Error != nil {
   137  		return "", "", result.Error
   138  	}
   139  	// TODO(perrito666) add status validation.
   140  	return status.Status(result.Status), result.Info, nil
   141  }
   142  
   143  // EnsureDead sets the machine lifecycle to Dead if it is Alive or
   144  // Dying. It does nothing otherwise.
   145  func (m *Machine) EnsureDead() error {
   146  	var result params.ErrorResults
   147  	args := params.Entities{
   148  		Entities: []params.Entity{{Tag: m.tag.String()}},
   149  	}
   150  	err := m.st.facade.FacadeCall("EnsureDead", args, &result)
   151  	if err != nil {
   152  		return err
   153  	}
   154  	return result.OneError()
   155  }
   156  
   157  // Remove removes the machine from state. It will fail if the machine
   158  // is not Dead.
   159  func (m *Machine) Remove() error {
   160  	var result params.ErrorResults
   161  	args := params.Entities{
   162  		Entities: []params.Entity{{Tag: m.tag.String()}},
   163  	}
   164  	err := m.st.facade.FacadeCall("Remove", args, &result)
   165  	if err != nil {
   166  		return err
   167  	}
   168  	return result.OneError()
   169  }
   170  
   171  // MarkForRemoval indicates that the machine is ready to have any
   172  // provider-level resources cleaned up and be removed.
   173  func (m *Machine) MarkForRemoval() error {
   174  	var result params.ErrorResults
   175  	args := params.Entities{
   176  		Entities: []params.Entity{{Tag: m.tag.String()}},
   177  	}
   178  	err := m.st.facade.FacadeCall("MarkMachinesForRemoval", args, &result)
   179  	if err != nil {
   180  		return err
   181  	}
   182  	return result.OneError()
   183  }
   184  
   185  // Series returns the operating system series running on the machine.
   186  //
   187  // NOTE: Unlike state.Machine.Series(), this method returns an error
   188  // as well, because it needs to do an API call.
   189  func (m *Machine) Series() (string, error) {
   190  	var results params.StringResults
   191  	args := params.Entities{
   192  		Entities: []params.Entity{{Tag: m.tag.String()}},
   193  	}
   194  	err := m.st.facade.FacadeCall("Series", args, &results)
   195  	if err != nil {
   196  		return "", err
   197  	}
   198  	if len(results.Results) != 1 {
   199  		return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   200  	}
   201  	result := results.Results[0]
   202  	if result.Error != nil {
   203  		return "", result.Error
   204  	}
   205  	return result.Result, nil
   206  }
   207  
   208  // DistributionGroup returns a slice of instance.Ids
   209  // that belong to the same distribution group as this
   210  // Machine. The provisioner may use this information
   211  // to distribute instances for high availability.
   212  func (m *Machine) DistributionGroup() ([]instance.Id, error) {
   213  	var results params.DistributionGroupResults
   214  	args := params.Entities{
   215  		Entities: []params.Entity{{Tag: m.tag.String()}},
   216  	}
   217  	err := m.st.facade.FacadeCall("DistributionGroup", args, &results)
   218  	if err != nil {
   219  		return nil, err
   220  	}
   221  	if len(results.Results) != 1 {
   222  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   223  	}
   224  	result := results.Results[0]
   225  	if result.Error != nil {
   226  		return nil, result.Error
   227  	}
   228  	return result.Result, nil
   229  }
   230  
   231  // SetInstanceInfo sets the provider specific instance id, nonce, metadata,
   232  // network config for this machine. Once set, the instance id cannot be changed.
   233  func (m *Machine) SetInstanceInfo(
   234  	id instance.Id, nonce string, characteristics *instance.HardwareCharacteristics,
   235  	networkConfig []params.NetworkConfig, volumes []params.Volume,
   236  	volumeAttachments map[string]params.VolumeAttachmentInfo,
   237  ) error {
   238  	var result params.ErrorResults
   239  	args := params.InstancesInfo{
   240  		Machines: []params.InstanceInfo{{
   241  			Tag:               m.tag.String(),
   242  			InstanceId:        id,
   243  			Nonce:             nonce,
   244  			Characteristics:   characteristics,
   245  			Volumes:           volumes,
   246  			VolumeAttachments: volumeAttachments,
   247  			NetworkConfig:     networkConfig,
   248  		}},
   249  	}
   250  	err := m.st.facade.FacadeCall("SetInstanceInfo", args, &result)
   251  	if err != nil {
   252  		return err
   253  	}
   254  	return result.OneError()
   255  }
   256  
   257  // InstanceId returns the provider specific instance id for the
   258  // machine or an CodeNotProvisioned error, if not set.
   259  func (m *Machine) InstanceId() (instance.Id, error) {
   260  	var results params.StringResults
   261  	args := params.Entities{
   262  		Entities: []params.Entity{{Tag: m.tag.String()}},
   263  	}
   264  	err := m.st.facade.FacadeCall("InstanceId", args, &results)
   265  	if err != nil {
   266  		return "", err
   267  	}
   268  	if len(results.Results) != 1 {
   269  		return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   270  	}
   271  	result := results.Results[0]
   272  	if result.Error != nil {
   273  		return "", result.Error
   274  	}
   275  	return instance.Id(result.Result), nil
   276  }
   277  
   278  // SetPassword sets the machine's password.
   279  func (m *Machine) SetPassword(password string) error {
   280  	var result params.ErrorResults
   281  	args := params.EntityPasswords{
   282  		Changes: []params.EntityPassword{
   283  			{Tag: m.tag.String(), Password: password},
   284  		},
   285  	}
   286  	err := m.st.facade.FacadeCall("SetPasswords", args, &result)
   287  	if err != nil {
   288  		return err
   289  	}
   290  	return result.OneError()
   291  }
   292  
   293  // WatchContainers returns a StringsWatcher that notifies of changes
   294  // to the lifecycles of containers of the specified type on the machine.
   295  func (m *Machine) WatchContainers(ctype instance.ContainerType) (watcher.StringsWatcher, error) {
   296  	if string(ctype) == "" {
   297  		return nil, fmt.Errorf("container type must be specified")
   298  	}
   299  	supported := false
   300  	for _, c := range instance.ContainerTypes {
   301  		if ctype == c {
   302  			supported = true
   303  			break
   304  		}
   305  	}
   306  	if !supported {
   307  		return nil, fmt.Errorf("unsupported container type %q", ctype)
   308  	}
   309  	var results params.StringsWatchResults
   310  	args := params.WatchContainers{
   311  		Params: []params.WatchContainer{
   312  			{MachineTag: m.tag.String(), ContainerType: string(ctype)},
   313  		},
   314  	}
   315  	err := m.st.facade.FacadeCall("WatchContainers", args, &results)
   316  	if err != nil {
   317  		return nil, err
   318  	}
   319  	if len(results.Results) != 1 {
   320  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   321  	}
   322  	result := results.Results[0]
   323  	if result.Error != nil {
   324  		return nil, result.Error
   325  	}
   326  	w := apiwatcher.NewStringsWatcher(m.st.facade.RawAPICaller(), result)
   327  	return w, nil
   328  }
   329  
   330  // WatchAllContainers returns a StringsWatcher that notifies of changes
   331  // to the lifecycles of all containers on the machine.
   332  func (m *Machine) WatchAllContainers() (watcher.StringsWatcher, error) {
   333  	var results params.StringsWatchResults
   334  	args := params.WatchContainers{
   335  		Params: []params.WatchContainer{
   336  			{MachineTag: m.tag.String()},
   337  		},
   338  	}
   339  	err := m.st.facade.FacadeCall("WatchContainers", args, &results)
   340  	if err != nil {
   341  		return nil, err
   342  	}
   343  	if len(results.Results) != 1 {
   344  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   345  	}
   346  	result := results.Results[0]
   347  	if result.Error != nil {
   348  		return nil, result.Error
   349  	}
   350  	w := apiwatcher.NewStringsWatcher(m.st.facade.RawAPICaller(), result)
   351  	return w, nil
   352  }
   353  
   354  // SetSupportedContainers updates the list of containers supported by this machine.
   355  func (m *Machine) SetSupportedContainers(containerTypes ...instance.ContainerType) error {
   356  	var results params.ErrorResults
   357  	args := params.MachineContainersParams{
   358  		Params: []params.MachineContainers{
   359  			{MachineTag: m.tag.String(), ContainerTypes: containerTypes},
   360  		},
   361  	}
   362  	err := m.st.facade.FacadeCall("SetSupportedContainers", args, &results)
   363  	if err != nil {
   364  		return err
   365  	}
   366  	if len(results.Results) != 1 {
   367  		return fmt.Errorf("expected 1 result, got %d", len(results.Results))
   368  	}
   369  	apiError := results.Results[0].Error
   370  	if apiError != nil {
   371  		return apiError
   372  	}
   373  	return nil
   374  }
   375  
   376  // SupportsNoContainers records the fact that this machine doesn't support any containers.
   377  func (m *Machine) SupportsNoContainers() error {
   378  	return m.SetSupportedContainers([]instance.ContainerType{}...)
   379  }