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