github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/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/errors"
    10  	"github.com/juju/version"
    11  	"gopkg.in/juju/charm.v6"
    12  	"gopkg.in/juju/names.v2"
    13  
    14  	apiwatcher "github.com/juju/juju/api/watcher"
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/core/instance"
    17  	"github.com/juju/juju/core/status"
    18  	"github.com/juju/juju/core/watcher"
    19  )
    20  
    21  //go:generate mockgen -package mocks -destination mocks/machine_mock.go github.com/juju/juju/api/provisioner MachineProvisioner
    22  
    23  // MachineProvisioner defines what provisioner needs to provision a machine.
    24  type MachineProvisioner interface {
    25  	// Tag returns the machine's tag.
    26  	Tag() names.Tag
    27  
    28  	// ModelAgentVersion returns the agent version the machine's model is currently
    29  	// running or an error.
    30  	ModelAgentVersion() (*version.Number, error)
    31  
    32  	// MachineTag returns the identifier for the machine as the most specific type.
    33  	MachineTag() names.MachineTag
    34  
    35  	// Id returns the machine id.
    36  	Id() string
    37  
    38  	// String returns the machine as a string.
    39  	String() string
    40  
    41  	// Life returns the machine's lifecycle value.
    42  	Life() params.Life
    43  
    44  	// Refresh updates the cached local copy of the machine's data.
    45  	Refresh() error
    46  
    47  	// ProvisioningInfo returns the information required to provision a machine.
    48  	ProvisioningInfo() (*params.ProvisioningInfo, error)
    49  
    50  	// SetInstanceStatus sets the status for the provider instance.
    51  	SetInstanceStatus(status status.Status, message string, data map[string]interface{}) error
    52  
    53  	// InstanceStatus returns the status of the provider instance.
    54  	InstanceStatus() (status.Status, string, error)
    55  
    56  	// SetStatus sets the status of the machine.
    57  	SetStatus(status status.Status, info string, data map[string]interface{}) error
    58  
    59  	// Status returns the status of the machine.
    60  	Status() (status.Status, string, error)
    61  
    62  	// EnsureDead sets the machine lifecycle to Dead if it is Alive or
    63  	// Dying. It does nothing otherwise.
    64  	EnsureDead() error
    65  
    66  	// Remove removes the machine from state. It will fail if the machine
    67  	// is not Dead.
    68  	Remove() error
    69  
    70  	// MarkForRemoval indicates that the machine is ready to have any
    71  	// provider-level resources cleaned up and be removed.
    72  	MarkForRemoval() error
    73  
    74  	// AvailabilityZone returns an underlying provider's availability zone
    75  	// for a machine.
    76  	AvailabilityZone() (string, error)
    77  
    78  	// DistributionGroup returns a slice of instance.Ids
    79  	// that belong to the same distribution group as this
    80  	// Machine. The provisioner may use this information
    81  	// to distribute instances for high availability.
    82  	DistributionGroup() ([]instance.Id, error)
    83  
    84  	// SetInstanceInfo sets the provider specific instance id, nonce, metadata,
    85  	// network config for this machine. Once set, the instance id cannot be changed.
    86  	SetInstanceInfo(
    87  		id instance.Id, displayName string, nonce string, characteristics *instance.HardwareCharacteristics,
    88  		networkConfig []params.NetworkConfig, volumes []params.Volume,
    89  		volumeAttachments map[string]params.VolumeAttachmentInfo, charmProfiles []string,
    90  	) error
    91  
    92  	// InstanceId returns the provider specific instance id for the
    93  	// machine or an CodeNotProvisioned error, if not set.
    94  	InstanceId() (instance.Id, error)
    95  
    96  	// KeepInstance returns the value of the keep-instance
    97  	// for the machine.
    98  	KeepInstance() (bool, error)
    99  
   100  	// SetPassword sets the machine's password.
   101  	SetPassword(password string) error
   102  
   103  	// WatchContainers returns a StringsWatcher that notifies of changes
   104  	// to the lifecycles of containers of the specified type on the machine.
   105  	WatchContainers(ctype instance.ContainerType) (watcher.StringsWatcher, error)
   106  
   107  	// WatchAllContainers returns a StringsWatcher that notifies of changes
   108  	// to the lifecycles of all containers on the machine.
   109  	WatchAllContainers() (watcher.StringsWatcher, error)
   110  
   111  	// SetSupportedContainers updates the list of containers supported by this machine.
   112  	SetSupportedContainers(containerTypes ...instance.ContainerType) error
   113  
   114  	// SupportsNoContainers records the fact that this machine doesn't support any containers.
   115  	SupportsNoContainers() error
   116  
   117  	// WatchContainers returns a StringsWatcher that notifies of
   118  	// changes to the upgrade charm profile charm url for all
   119  	// containers of the specified type  on the machine.
   120  	WatchContainersCharmProfiles(ctype instance.ContainerType) (watcher.StringsWatcher, error)
   121  
   122  	// CharmProfileChangeInfo retrieves the info necessary to change a charm
   123  	// profile used by a machine.
   124  	CharmProfileChangeInfo() (CharmProfileChangeInfo, error)
   125  
   126  	// SetCharmProfiles records the given slice of charm profile names.
   127  	SetCharmProfiles([]string) error
   128  
   129  	// SetUpgradeCharmProfileComplete recorded that the result of updating
   130  	// the machine's charm profile(s)
   131  	SetUpgradeCharmProfileComplete(string) error
   132  
   133  	// RemoveUpgradeCharmProfileData completely removes the instance charm profile
   134  	// data for a machine, even if the machine is dead.
   135  	RemoveUpgradeCharmProfileData() error
   136  }
   137  
   138  // Machine represents a juju machine as seen by the provisioner worker.
   139  type Machine struct {
   140  	tag  names.MachineTag
   141  	life params.Life
   142  	st   *State
   143  }
   144  
   145  // Tag implements MachineProvisioner.Tag.
   146  func (m *Machine) Tag() names.Tag {
   147  	return m.tag
   148  }
   149  
   150  // ModelAgentVersion implements MachineProvisioner.ModelAgentVersion.
   151  func (m *Machine) ModelAgentVersion() (*version.Number, error) {
   152  	mc, err := m.st.ModelConfig()
   153  	if err != nil {
   154  		return nil, errors.Trace(err)
   155  	}
   156  
   157  	if v, ok := mc.AgentVersion(); ok {
   158  		return &v, nil
   159  	}
   160  
   161  	return nil, errors.New("failed to get model's agent version.")
   162  }
   163  
   164  // MachineTag implements MachineProvisioner.MachineTag.
   165  func (m *Machine) MachineTag() names.MachineTag {
   166  	return m.tag
   167  }
   168  
   169  // Id implements MachineProvisioner.Id.
   170  func (m *Machine) Id() string {
   171  	return m.tag.Id()
   172  }
   173  
   174  // String implements MachineProvisioner.String.
   175  func (m *Machine) String() string {
   176  	return m.Id()
   177  }
   178  
   179  // Life implements MachineProvisioner..
   180  func (m *Machine) Life() params.Life {
   181  	return m.life
   182  }
   183  
   184  // Refresh implements MachineProvisioner.Refresh.
   185  func (m *Machine) Refresh() error {
   186  	life, err := m.st.machineLife(m.tag)
   187  	if err != nil {
   188  		return err
   189  	}
   190  	m.life = life
   191  	return nil
   192  }
   193  
   194  // ProvisioningInfo implements MachineProvisioner.ProvisioningInfo.
   195  func (m *Machine) ProvisioningInfo() (*params.ProvisioningInfo, error) {
   196  	var results params.ProvisioningInfoResults
   197  	args := params.Entities{Entities: []params.Entity{{m.tag.String()}}}
   198  	err := m.st.facade.FacadeCall("ProvisioningInfo", args, &results)
   199  	if err != nil {
   200  		return nil, err
   201  	}
   202  	if len(results.Results) != 1 {
   203  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   204  	}
   205  	result := results.Results[0]
   206  	if result.Error != nil {
   207  		return nil, result.Error
   208  	}
   209  	return result.Result, nil
   210  }
   211  
   212  // SetInstanceStatus implements MachineProvisioner.SetInstanceStatus.
   213  func (m *Machine) SetInstanceStatus(status status.Status, message string, data map[string]interface{}) error {
   214  	var result params.ErrorResults
   215  	args := params.SetStatus{Entities: []params.EntityStatusArgs{
   216  		{Tag: m.tag.String(), Status: status.String(), Info: message, Data: data},
   217  	}}
   218  	err := m.st.facade.FacadeCall("SetInstanceStatus", args, &result)
   219  	if err != nil {
   220  		return err
   221  	}
   222  	return result.OneError()
   223  }
   224  
   225  // InstanceStatus implements MachineProvisioner.InstanceStatus.
   226  func (m *Machine) InstanceStatus() (status.Status, string, error) {
   227  	var results params.StatusResults
   228  	args := params.Entities{Entities: []params.Entity{
   229  		{Tag: m.tag.String()},
   230  	}}
   231  	err := m.st.facade.FacadeCall("InstanceStatus", args, &results)
   232  	if err != nil {
   233  		return "", "", err
   234  	}
   235  	if len(results.Results) != 1 {
   236  		return "", "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   237  	}
   238  	result := results.Results[0]
   239  	if result.Error != nil {
   240  		return "", "", result.Error
   241  	}
   242  	// TODO(perrito666) add status validation.
   243  	return status.Status(result.Status), result.Info, nil
   244  }
   245  
   246  // SetStatus implements MachineProvisioner.SetStatus.
   247  func (m *Machine) SetStatus(status status.Status, info string, data map[string]interface{}) error {
   248  	var result params.ErrorResults
   249  	args := params.SetStatus{
   250  		Entities: []params.EntityStatusArgs{
   251  			{Tag: m.tag.String(), Status: status.String(), Info: info, Data: data},
   252  		},
   253  	}
   254  	err := m.st.facade.FacadeCall("SetStatus", args, &result)
   255  	if err != nil {
   256  		return err
   257  	}
   258  	return result.OneError()
   259  }
   260  
   261  // Status implements MachineProvisioner.Status.
   262  func (m *Machine) Status() (status.Status, string, error) {
   263  	var results params.StatusResults
   264  	args := params.Entities{
   265  		Entities: []params.Entity{{Tag: m.tag.String()}},
   266  	}
   267  	err := m.st.facade.FacadeCall("Status", args, &results)
   268  	if err != nil {
   269  		return "", "", err
   270  	}
   271  	if len(results.Results) != 1 {
   272  		return "", "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   273  	}
   274  	result := results.Results[0]
   275  	if result.Error != nil {
   276  		return "", "", result.Error
   277  	}
   278  	// TODO(perrito666) add status validation.
   279  	return status.Status(result.Status), result.Info, nil
   280  }
   281  
   282  // EnsureDead implements MachineProvisioner.EnsureDead.
   283  func (m *Machine) EnsureDead() error {
   284  	var result params.ErrorResults
   285  	args := params.Entities{
   286  		Entities: []params.Entity{{Tag: m.tag.String()}},
   287  	}
   288  	err := m.st.facade.FacadeCall("EnsureDead", args, &result)
   289  	if err != nil {
   290  		return err
   291  	}
   292  	return result.OneError()
   293  }
   294  
   295  // Remove implements MachineProvisioner.Remove.
   296  func (m *Machine) Remove() error {
   297  	var result params.ErrorResults
   298  	args := params.Entities{
   299  		Entities: []params.Entity{{Tag: m.tag.String()}},
   300  	}
   301  	err := m.st.facade.FacadeCall("Remove", args, &result)
   302  	if err != nil {
   303  		return err
   304  	}
   305  	return result.OneError()
   306  }
   307  
   308  // MarkForRemoval implements MachineProvisioner.MarkForRemoval.
   309  func (m *Machine) MarkForRemoval() error {
   310  	var result params.ErrorResults
   311  	args := params.Entities{
   312  		Entities: []params.Entity{{Tag: m.tag.String()}},
   313  	}
   314  	err := m.st.facade.FacadeCall("MarkMachinesForRemoval", args, &result)
   315  	if err != nil {
   316  		return err
   317  	}
   318  	return result.OneError()
   319  }
   320  
   321  // AvailabilityZone implements MachineProvisioner.AvailabilityZone.
   322  func (m *Machine) AvailabilityZone() (string, error) {
   323  	var results params.StringResults
   324  	args := params.Entities{
   325  		Entities: []params.Entity{{Tag: m.tag.String()}},
   326  	}
   327  	err := m.st.facade.FacadeCall("AvailabilityZone", args, &results)
   328  	if err != nil {
   329  		return "", err
   330  	}
   331  	if len(results.Results) != 1 {
   332  		return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   333  	}
   334  	result := results.Results[0]
   335  	if result.Error != nil {
   336  		return "", result.Error
   337  	}
   338  	return result.Result, nil
   339  }
   340  
   341  // DistributionGroup implements MachineProvisioner.DistributionGroup.
   342  func (m *Machine) DistributionGroup() ([]instance.Id, error) {
   343  	var results params.DistributionGroupResults
   344  	args := params.Entities{
   345  		Entities: []params.Entity{{Tag: m.tag.String()}},
   346  	}
   347  	err := m.st.facade.FacadeCall("DistributionGroup", args, &results)
   348  	if err != nil {
   349  		return nil, err
   350  	}
   351  	if len(results.Results) != 1 {
   352  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   353  	}
   354  	result := results.Results[0]
   355  	if result.Error != nil {
   356  		return nil, result.Error
   357  	}
   358  	return result.Result, nil
   359  }
   360  
   361  // SetInstanceInfo implements MachineProvisioner.SetInstanceInfo.
   362  func (m *Machine) SetInstanceInfo(
   363  	id instance.Id, displayName string, nonce string, characteristics *instance.HardwareCharacteristics,
   364  	networkConfig []params.NetworkConfig, volumes []params.Volume,
   365  	volumeAttachments map[string]params.VolumeAttachmentInfo, charmProfiles []string,
   366  ) error {
   367  	var result params.ErrorResults
   368  	args := params.InstancesInfo{
   369  		Machines: []params.InstanceInfo{{
   370  			Tag:               m.tag.String(),
   371  			InstanceId:        id,
   372  			DisplayName:       displayName,
   373  			Nonce:             nonce,
   374  			Characteristics:   characteristics,
   375  			Volumes:           volumes,
   376  			VolumeAttachments: volumeAttachments,
   377  			NetworkConfig:     networkConfig,
   378  			CharmProfiles:     charmProfiles,
   379  		}},
   380  	}
   381  	err := m.st.facade.FacadeCall("SetInstanceInfo", args, &result)
   382  	if err != nil {
   383  		return err
   384  	}
   385  	return result.OneError()
   386  }
   387  
   388  // InstanceId implements MachineProvisioner.InstanceId.
   389  func (m *Machine) InstanceId() (instance.Id, error) {
   390  	var results params.StringResults
   391  	args := params.Entities{
   392  		Entities: []params.Entity{{Tag: m.tag.String()}},
   393  	}
   394  	err := m.st.facade.FacadeCall("InstanceId", args, &results)
   395  	if err != nil {
   396  		return "", err
   397  	}
   398  	if len(results.Results) != 1 {
   399  		return "", fmt.Errorf("expected 1 result, got %d", len(results.Results))
   400  	}
   401  	result := results.Results[0]
   402  	if result.Error != nil {
   403  		return "", result.Error
   404  	}
   405  	return instance.Id(result.Result), nil
   406  }
   407  
   408  // KeepInstance implements MachineProvisioner.KeepInstance.
   409  func (m *Machine) KeepInstance() (bool, error) {
   410  	var results params.BoolResults
   411  	args := params.Entities{
   412  		Entities: []params.Entity{{Tag: m.tag.String()}},
   413  	}
   414  	err := m.st.facade.FacadeCall("KeepInstance", args, &results)
   415  	if err != nil {
   416  		return false, err
   417  	}
   418  	if len(results.Results) != 1 {
   419  		return false, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   420  	}
   421  	result := results.Results[0]
   422  	if result.Error != nil {
   423  		if params.IsCodeNotSupported(err) {
   424  			return false, errors.NewNotSupported(nil, "KeepInstance")
   425  		}
   426  		return false, result.Error
   427  	}
   428  	return result.Result, nil
   429  }
   430  
   431  // SetPassword implements MachineProvisioner.SetPassword.
   432  func (m *Machine) SetPassword(password string) error {
   433  	var result params.ErrorResults
   434  	args := params.EntityPasswords{
   435  		Changes: []params.EntityPassword{
   436  			{Tag: m.tag.String(), Password: password},
   437  		},
   438  	}
   439  	err := m.st.facade.FacadeCall("SetPasswords", args, &result)
   440  	if err != nil {
   441  		return err
   442  	}
   443  	return result.OneError()
   444  }
   445  
   446  // WatchContainers implements MachineProvisioner.WatchContainers.
   447  func (m *Machine) WatchContainers(ctype instance.ContainerType) (watcher.StringsWatcher, error) {
   448  	if string(ctype) == "" {
   449  		return nil, fmt.Errorf("container type must be specified")
   450  	}
   451  	supported := false
   452  	for _, c := range instance.ContainerTypes {
   453  		if ctype == c {
   454  			supported = true
   455  			break
   456  		}
   457  	}
   458  	if !supported {
   459  		return nil, fmt.Errorf("unsupported container type %q", ctype)
   460  	}
   461  	var results params.StringsWatchResults
   462  	args := params.WatchContainers{
   463  		Params: []params.WatchContainer{
   464  			{MachineTag: m.tag.String(), ContainerType: string(ctype)},
   465  		},
   466  	}
   467  	err := m.st.facade.FacadeCall("WatchContainers", args, &results)
   468  	if err != nil {
   469  		return nil, err
   470  	}
   471  	if len(results.Results) != 1 {
   472  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   473  	}
   474  	result := results.Results[0]
   475  	if result.Error != nil {
   476  		return nil, result.Error
   477  	}
   478  	w := apiwatcher.NewStringsWatcher(m.st.facade.RawAPICaller(), result)
   479  	return w, nil
   480  }
   481  
   482  // WatchAllContainers implements MachineProvisioner.WatchAllContainers.
   483  func (m *Machine) WatchAllContainers() (watcher.StringsWatcher, error) {
   484  	var results params.StringsWatchResults
   485  	args := params.WatchContainers{
   486  		Params: []params.WatchContainer{
   487  			{MachineTag: m.tag.String()},
   488  		},
   489  	}
   490  	err := m.st.facade.FacadeCall("WatchContainers", args, &results)
   491  	if err != nil {
   492  		return nil, err
   493  	}
   494  	if len(results.Results) != 1 {
   495  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   496  	}
   497  	result := results.Results[0]
   498  	if result.Error != nil {
   499  		return nil, result.Error
   500  	}
   501  	w := apiwatcher.NewStringsWatcher(m.st.facade.RawAPICaller(), result)
   502  	return w, nil
   503  }
   504  
   505  // WatchContainers implements MachineProvisioner.WatchContainersCharmProfiles.
   506  func (m *Machine) WatchContainersCharmProfiles(ctype instance.ContainerType) (watcher.StringsWatcher, error) {
   507  	if string(ctype) == "" {
   508  		return nil, fmt.Errorf("container type must be specified")
   509  	}
   510  	supported := false
   511  	for _, c := range instance.ContainerTypes {
   512  		if ctype == c {
   513  			supported = true
   514  			break
   515  		}
   516  	}
   517  	if !supported {
   518  		return nil, fmt.Errorf("unsupported container type %q", ctype)
   519  	}
   520  	var results params.StringsWatchResults
   521  	args := params.WatchContainers{
   522  		Params: []params.WatchContainer{
   523  			{MachineTag: m.tag.String(), ContainerType: string(ctype)},
   524  		},
   525  	}
   526  	err := m.st.facade.FacadeCall("WatchContainersCharmProfiles", args, &results)
   527  	if err != nil {
   528  		return nil, err
   529  	}
   530  	if len(results.Results) != 1 {
   531  		return nil, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   532  	}
   533  	result := results.Results[0]
   534  	if result.Error != nil {
   535  		return nil, result.Error
   536  	}
   537  	w := apiwatcher.NewStringsWatcher(m.st.facade.RawAPICaller(), result)
   538  	return w, nil
   539  }
   540  
   541  // SetSupportedContainers implements MachineProvisioner.SetSupportedContainers.
   542  func (m *Machine) SetSupportedContainers(containerTypes ...instance.ContainerType) error {
   543  	var results params.ErrorResults
   544  	args := params.MachineContainersParams{
   545  		Params: []params.MachineContainers{
   546  			{MachineTag: m.tag.String(), ContainerTypes: containerTypes},
   547  		},
   548  	}
   549  	err := m.st.facade.FacadeCall("SetSupportedContainers", args, &results)
   550  	if err != nil {
   551  		return err
   552  	}
   553  	if len(results.Results) != 1 {
   554  		return fmt.Errorf("expected 1 result, got %d", len(results.Results))
   555  	}
   556  	apiError := results.Results[0].Error
   557  	if apiError != nil {
   558  		return apiError
   559  	}
   560  	return nil
   561  }
   562  
   563  // SupportsNoContainers implements MachineProvisioner.SupportsNoContainers.
   564  func (m *Machine) SupportsNoContainers() error {
   565  	return m.SetSupportedContainers([]instance.ContainerType{}...)
   566  }
   567  
   568  type CharmProfileChangeInfo struct {
   569  	OldProfileName string
   570  	NewProfileName string
   571  	LXDProfile     *charm.LXDProfile
   572  	Subordinate    bool
   573  }
   574  
   575  // CharmProfileChangeInfo implements MachineProvisioner.CharmProfileChangeInfo.
   576  func (m *Machine) CharmProfileChangeInfo() (CharmProfileChangeInfo, error) {
   577  	var results params.ProfileChangeResults
   578  	args := params.Entities{Entities: []params.Entity{
   579  		{Tag: m.tag.String()},
   580  	}}
   581  	err := m.st.facade.FacadeCall("CharmProfileChangeInfo", args, &results)
   582  	if err != nil {
   583  		return CharmProfileChangeInfo{}, err
   584  	}
   585  	if len(results.Results) != 1 {
   586  		return CharmProfileChangeInfo{}, fmt.Errorf("expected 1 result, got %d", len(results.Results))
   587  	}
   588  	result := results.Results[0]
   589  	if result.Error != nil {
   590  		return CharmProfileChangeInfo{}, result.Error
   591  	}
   592  	var profile *charm.LXDProfile
   593  	if result.Profile != nil {
   594  		p := charm.LXDProfile(*result.Profile)
   595  		profile = &p
   596  	}
   597  	return CharmProfileChangeInfo{
   598  		OldProfileName: result.OldProfileName,
   599  		NewProfileName: result.NewProfileName,
   600  		LXDProfile:     profile,
   601  		Subordinate:    result.Subordinate,
   602  	}, nil
   603  }
   604  
   605  // SetCharmProfiles implements MachineProvisioner.SetCharmProfiles.
   606  func (m *Machine) SetCharmProfiles(profiles []string) error {
   607  	var results params.ErrorResults
   608  	args := params.SetProfileArgs{
   609  		Args: []params.SetProfileArg{
   610  			{
   611  				Entity:   params.Entity{Tag: m.tag.String()},
   612  				Profiles: profiles,
   613  			},
   614  		},
   615  	}
   616  	err := m.st.facade.FacadeCall("SetCharmProfiles", args, &results)
   617  	if err != nil {
   618  		return err
   619  	}
   620  	if len(results.Results) != 1 {
   621  		return fmt.Errorf("expected 1 result, got %d", len(results.Results))
   622  	}
   623  	result := results.Results[0]
   624  	if result.Error != nil {
   625  		return result.Error
   626  	}
   627  	return nil
   628  }
   629  
   630  // SetUpgradeCharmProfileComplete implements MachineProvisioner.SetUpgradeCharmProfileComplete.
   631  func (m *Machine) SetUpgradeCharmProfileComplete(message string) error {
   632  	var results params.ErrorResults
   633  	args := params.SetProfileUpgradeCompleteArgs{
   634  		Args: []params.SetProfileUpgradeCompleteArg{
   635  			{
   636  				Entity:  params.Entity{Tag: m.tag.String()},
   637  				Message: message,
   638  			},
   639  		},
   640  	}
   641  	err := m.st.facade.FacadeCall("SetUpgradeCharmProfileComplete", args, &results)
   642  	if err != nil {
   643  		return err
   644  	}
   645  	if len(results.Results) != 1 {
   646  		return fmt.Errorf("expected 1 result, got %d", len(results.Results))
   647  	}
   648  	result := results.Results[0]
   649  	if result.Error != nil {
   650  		return result.Error
   651  	}
   652  	return nil
   653  }
   654  
   655  // RemoveUpgradeCharmProfileData implements MachineProvisioner.RemoveUpgradeCharmProfileData.
   656  func (m *Machine) RemoveUpgradeCharmProfileData() error {
   657  	var results params.ErrorResults
   658  	args := params.Entities{
   659  		Entities: []params.Entity{
   660  			{
   661  				Tag: m.tag.String(),
   662  			},
   663  		},
   664  	}
   665  	err := m.st.facade.FacadeCall("RemoveUpgradeCharmProfileData", args, &results)
   666  	if err != nil {
   667  		return err
   668  	}
   669  	if len(results.Results) != 1 {
   670  		return fmt.Errorf("expected 1 result, got %d", len(results.Results))
   671  	}
   672  	result := results.Results[0]
   673  	if result.Error != nil {
   674  		return result.Error
   675  	}
   676  	return nil
   677  }