github.com/mattyw/juju@v0.0.0-20140610034352-732aecd63861/state/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  	"github.com/juju/names"
    10  
    11  	"github.com/juju/juju/instance"
    12  	"github.com/juju/juju/state/api/params"
    13  	"github.com/juju/juju/state/api/watcher"
    14  )
    15  
    16  // Machine represents a juju machine as seen by the provisioner worker.
    17  type Machine struct {
    18  	tag  string
    19  	life params.Life
    20  	st   *State
    21  }
    22  
    23  // Tag returns the machine's tag.
    24  func (m *Machine) Tag() string {
    25  	return m.tag
    26  }
    27  
    28  // Id returns the machine id.
    29  func (m *Machine) Id() string {
    30  	_, machineId, err := names.ParseTag(m.tag, names.MachineTagKind)
    31  	if err != nil {
    32  		panic(fmt.Sprintf("%q is not a valid machine tag", m.tag))
    33  	}
    34  	return machineId
    35  }
    36  
    37  // String returns the machine as a string.
    38  func (m *Machine) String() string {
    39  	return m.Id()
    40  }
    41  
    42  // Life returns the machine's lifecycle value.
    43  func (m *Machine) Life() params.Life {
    44  	return m.life
    45  }
    46  
    47  // Refresh updates the cached local copy of the machine's data.
    48  func (m *Machine) Refresh() error {
    49  	life, err := m.st.machineLife(m.tag)
    50  	if err != nil {
    51  		return err
    52  	}
    53  	m.life = life
    54  	return nil
    55  }
    56  
    57  // ProvisioningInfo returns the information required to provisiong a machine.
    58  func (m *Machine) ProvisioningInfo() (*params.ProvisioningInfo, error) {
    59  	var results params.ProvisioningInfoResults
    60  	args := params.Entities{Entities: []params.Entity{{m.tag}}}
    61  	err := m.st.call("ProvisioningInfo", args, &results)
    62  	if err != nil {
    63  		return nil, err
    64  	}
    65  	if len(results.Results) != 1 {
    66  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
    67  	}
    68  	result := results.Results[0]
    69  	if result.Error != nil {
    70  		return nil, result.Error
    71  	}
    72  	return result.Result, nil
    73  }
    74  
    75  // SetStatus sets the status of the machine.
    76  func (m *Machine) SetStatus(status params.Status, info string, data params.StatusData) error {
    77  	var result params.ErrorResults
    78  	args := params.SetStatus{
    79  		Entities: []params.EntityStatus{
    80  			{Tag: m.tag, Status: status, Info: info, Data: data},
    81  		},
    82  	}
    83  	err := m.st.call("SetStatus", args, &result)
    84  	if err != nil {
    85  		return err
    86  	}
    87  	return result.OneError()
    88  }
    89  
    90  // Status returns the status of the machine.
    91  func (m *Machine) Status() (params.Status, string, error) {
    92  	var results params.StatusResults
    93  	args := params.Entities{
    94  		Entities: []params.Entity{{Tag: m.tag}},
    95  	}
    96  	err := m.st.call("Status", args, &results)
    97  	if err != nil {
    98  		return "", "", err
    99  	}
   100  	if len(results.Results) != 1 {
   101  		return "", "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   102  	}
   103  	result := results.Results[0]
   104  	if result.Error != nil {
   105  		return "", "", result.Error
   106  	}
   107  	return result.Status, result.Info, nil
   108  }
   109  
   110  // EnsureDead sets the machine lifecycle to Dead if it is Alive or
   111  // Dying. It does nothing otherwise.
   112  func (m *Machine) EnsureDead() error {
   113  	var result params.ErrorResults
   114  	args := params.Entities{
   115  		Entities: []params.Entity{{Tag: m.tag}},
   116  	}
   117  	err := m.st.call("EnsureDead", args, &result)
   118  	if err != nil {
   119  		return err
   120  	}
   121  	return result.OneError()
   122  }
   123  
   124  // Remove removes the machine from state. It will fail if the machine
   125  // is not Dead.
   126  func (m *Machine) Remove() error {
   127  	var result params.ErrorResults
   128  	args := params.Entities{
   129  		Entities: []params.Entity{{Tag: m.tag}},
   130  	}
   131  	err := m.st.call("Remove", args, &result)
   132  	if err != nil {
   133  		return err
   134  	}
   135  	return result.OneError()
   136  }
   137  
   138  // Series returns the operating system series running on the machine.
   139  //
   140  // NOTE: Unlike state.Machine.Series(), this method returns an error
   141  // as well, because it needs to do an API call.
   142  func (m *Machine) Series() (string, error) {
   143  	var results params.StringResults
   144  	args := params.Entities{
   145  		Entities: []params.Entity{{Tag: m.tag}},
   146  	}
   147  	err := m.st.call("Series", args, &results)
   148  	if err != nil {
   149  		return "", err
   150  	}
   151  	if len(results.Results) != 1 {
   152  		return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   153  	}
   154  	result := results.Results[0]
   155  	if result.Error != nil {
   156  		return "", result.Error
   157  	}
   158  	return result.Result, nil
   159  }
   160  
   161  // DistributionGroup returns a slice of instance.Ids
   162  // that belong to the same distribution group as this
   163  // Machine. The provisioner may use this information
   164  // to distribute instances for high availability.
   165  func (m *Machine) DistributionGroup() ([]instance.Id, error) {
   166  	var results params.DistributionGroupResults
   167  	args := params.Entities{
   168  		Entities: []params.Entity{{Tag: m.tag}},
   169  	}
   170  	err := m.st.caller.Call("Provisioner", "", "DistributionGroup", args, &results)
   171  	if err != nil {
   172  		return nil, err
   173  	}
   174  	if len(results.Results) != 1 {
   175  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   176  	}
   177  	result := results.Results[0]
   178  	if result.Error != nil {
   179  		return nil, result.Error
   180  	}
   181  	return result.Result, nil
   182  }
   183  
   184  // SetInstanceInfo sets the provider specific instance id, nonce,
   185  // metadata, networks and interfaces for this machine. Once set, the
   186  // instance id cannot be changed.
   187  func (m *Machine) SetInstanceInfo(
   188  	id instance.Id, nonce string, characteristics *instance.HardwareCharacteristics,
   189  	networks []params.Network, interfaces []params.NetworkInterface,
   190  ) error {
   191  	var result params.ErrorResults
   192  	args := params.InstancesInfo{
   193  		Machines: []params.InstanceInfo{{
   194  			Tag:             m.tag,
   195  			InstanceId:      id,
   196  			Nonce:           nonce,
   197  			Characteristics: characteristics,
   198  			Networks:        networks,
   199  			Interfaces:      interfaces,
   200  		}},
   201  	}
   202  	err := m.st.call("SetInstanceInfo", args, &result)
   203  	if err != nil {
   204  		return err
   205  	}
   206  	return result.OneError()
   207  }
   208  
   209  // InstanceId returns the provider specific instance id for the
   210  // machine or an CodeNotProvisioned error, if not set.
   211  func (m *Machine) InstanceId() (instance.Id, error) {
   212  	var results params.StringResults
   213  	args := params.Entities{
   214  		Entities: []params.Entity{{Tag: m.tag}},
   215  	}
   216  	err := m.st.call("InstanceId", args, &results)
   217  	if err != nil {
   218  		return "", err
   219  	}
   220  	if len(results.Results) != 1 {
   221  		return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   222  	}
   223  	result := results.Results[0]
   224  	if result.Error != nil {
   225  		return "", result.Error
   226  	}
   227  	return instance.Id(result.Result), nil
   228  }
   229  
   230  // SetPassword sets the machine's password.
   231  func (m *Machine) SetPassword(password string) error {
   232  	var result params.ErrorResults
   233  	args := params.EntityPasswords{
   234  		Changes: []params.EntityPassword{
   235  			{Tag: m.tag, Password: password},
   236  		},
   237  	}
   238  	err := m.st.call("SetPasswords", args, &result)
   239  	if err != nil {
   240  		return err
   241  	}
   242  	return result.OneError()
   243  }
   244  
   245  // WatchContainers returns a StringsWatcher that notifies of changes
   246  // to the lifecycles of containers of the specified type on the machine.
   247  func (m *Machine) WatchContainers(ctype instance.ContainerType) (watcher.StringsWatcher, error) {
   248  	if string(ctype) == "" {
   249  		return nil, fmt.Errorf("container type must be specified")
   250  	}
   251  	supported := false
   252  	for _, c := range instance.ContainerTypes {
   253  		if ctype == c {
   254  			supported = true
   255  			break
   256  		}
   257  	}
   258  	if !supported {
   259  		return nil, fmt.Errorf("unsupported container type %q", ctype)
   260  	}
   261  	var results params.StringsWatchResults
   262  	args := params.WatchContainers{
   263  		Params: []params.WatchContainer{
   264  			{MachineTag: m.tag, ContainerType: string(ctype)},
   265  		},
   266  	}
   267  	err := m.st.call("WatchContainers", args, &results)
   268  	if err != nil {
   269  		return nil, err
   270  	}
   271  	if len(results.Results) != 1 {
   272  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   273  	}
   274  	result := results.Results[0]
   275  	if result.Error != nil {
   276  		return nil, result.Error
   277  	}
   278  	w := watcher.NewStringsWatcher(m.st.caller, result)
   279  	return w, nil
   280  }
   281  
   282  // WatchAllContainers returns a StringsWatcher that notifies of changes
   283  // to the lifecycles of all containers on the machine.
   284  func (m *Machine) WatchAllContainers() (watcher.StringsWatcher, error) {
   285  	var results params.StringsWatchResults
   286  	args := params.WatchContainers{
   287  		Params: []params.WatchContainer{
   288  			{MachineTag: m.tag},
   289  		},
   290  	}
   291  	err := m.st.call("WatchContainers", args, &results)
   292  	if err != nil {
   293  		return nil, err
   294  	}
   295  	if len(results.Results) != 1 {
   296  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   297  	}
   298  	result := results.Results[0]
   299  	if result.Error != nil {
   300  		return nil, result.Error
   301  	}
   302  	w := watcher.NewStringsWatcher(m.st.caller, result)
   303  	return w, nil
   304  }
   305  
   306  // SetSupportedContainers updates the list of containers supported by this machine.
   307  func (m *Machine) SetSupportedContainers(containerTypes ...instance.ContainerType) error {
   308  	var results params.ErrorResults
   309  	args := params.MachineContainersParams{
   310  		Params: []params.MachineContainers{
   311  			{MachineTag: m.tag, ContainerTypes: containerTypes},
   312  		},
   313  	}
   314  	err := m.st.call("SetSupportedContainers", args, &results)
   315  	if err != nil {
   316  		return err
   317  	}
   318  	if len(results.Results) != 1 {
   319  		return fmt.Errorf("expected 1 result, got %d", len(results.Results))
   320  	}
   321  	apiError := results.Results[0].Error
   322  	if apiError != nil {
   323  		return apiError
   324  	}
   325  	return nil
   326  }
   327  
   328  // SupportsNoContainers records the fact that this machine doesn't support any containers.
   329  func (m *Machine) SupportsNoContainers() error {
   330  	return m.SetSupportedContainers([]instance.ContainerType{}...)
   331  }