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

     1  // Copyright 2016 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package migrationmaster
     5  
     6  import (
     7  	"encoding/json"
     8  
     9  	"github.com/juju/errors"
    10  	"github.com/juju/version"
    11  	"gopkg.in/juju/names.v2"
    12  	"gopkg.in/macaroon.v1"
    13  
    14  	"github.com/juju/juju/api/base"
    15  	"github.com/juju/juju/apiserver/params"
    16  	"github.com/juju/juju/core/migration"
    17  	"github.com/juju/juju/watcher"
    18  )
    19  
    20  // NewWatcherFunc exists to let us unit test Facade without patching.
    21  type NewWatcherFunc func(base.APICaller, params.NotifyWatchResult) watcher.NotifyWatcher
    22  
    23  // NewClient returns a new Client based on an existing API connection.
    24  func NewClient(caller base.APICaller, newWatcher NewWatcherFunc) *Client {
    25  	return &Client{
    26  		caller:     base.NewFacadeCaller(caller, "MigrationMaster"),
    27  		newWatcher: newWatcher,
    28  	}
    29  }
    30  
    31  // Client describes the client side API for the MigrationMaster facade
    32  // (used by the migrationmaster worker).
    33  type Client struct {
    34  	caller     base.FacadeCaller
    35  	newWatcher NewWatcherFunc
    36  }
    37  
    38  // Watch returns a watcher which reports when a migration is active
    39  // for the model associated with the API connection.
    40  func (c *Client) Watch() (watcher.NotifyWatcher, error) {
    41  	var result params.NotifyWatchResult
    42  	err := c.caller.FacadeCall("Watch", nil, &result)
    43  	if err != nil {
    44  		return nil, errors.Trace(err)
    45  	}
    46  	if result.Error != nil {
    47  		return nil, result.Error
    48  	}
    49  	return c.newWatcher(c.caller.RawAPICaller(), result), nil
    50  }
    51  
    52  // MigrationStatus returns the details and progress of the latest
    53  // model migration.
    54  func (c *Client) MigrationStatus() (migration.MigrationStatus, error) {
    55  	var empty migration.MigrationStatus
    56  	var status params.MasterMigrationStatus
    57  	err := c.caller.FacadeCall("MigrationStatus", nil, &status)
    58  	if err != nil {
    59  		return empty, errors.Trace(err)
    60  	}
    61  
    62  	modelTag, err := names.ParseModelTag(status.Spec.ModelTag)
    63  	if err != nil {
    64  		return empty, errors.Annotatef(err, "parsing model tag")
    65  	}
    66  
    67  	phase, ok := migration.ParsePhase(status.Phase)
    68  	if !ok {
    69  		return empty, errors.New("unable to parse phase")
    70  	}
    71  
    72  	target := status.Spec.TargetInfo
    73  	controllerTag, err := names.ParseControllerTag(target.ControllerTag)
    74  	if err != nil {
    75  		return empty, errors.Annotatef(err, "parsing controller tag")
    76  	}
    77  
    78  	authTag, err := names.ParseUserTag(target.AuthTag)
    79  	if err != nil {
    80  		return empty, errors.Annotatef(err, "unable to parse auth tag")
    81  	}
    82  
    83  	var macs []macaroon.Slice
    84  	if target.Macaroons != "" {
    85  		if err := json.Unmarshal([]byte(target.Macaroons), &macs); err != nil {
    86  			return empty, errors.Annotatef(err, "unmarshalling macaroon")
    87  		}
    88  	}
    89  
    90  	return migration.MigrationStatus{
    91  		MigrationId:      status.MigrationId,
    92  		ModelUUID:        modelTag.Id(),
    93  		ExternalControl:  status.Spec.ExternalControl,
    94  		Phase:            phase,
    95  		PhaseChangedTime: status.PhaseChangedTime,
    96  		TargetInfo: migration.TargetInfo{
    97  			ControllerTag: controllerTag,
    98  			Addrs:         target.Addrs,
    99  			CACert:        target.CACert,
   100  			AuthTag:       authTag,
   101  			Password:      target.Password,
   102  			Macaroons:     macs,
   103  		},
   104  	}, nil
   105  }
   106  
   107  // SetPhase updates the phase of the currently active model migration.
   108  func (c *Client) SetPhase(phase migration.Phase) error {
   109  	args := params.SetMigrationPhaseArgs{
   110  		Phase: phase.String(),
   111  	}
   112  	return c.caller.FacadeCall("SetPhase", args, nil)
   113  }
   114  
   115  // SetStatusMessage sets a human readable message regarding the
   116  // progress of a migration.
   117  func (c *Client) SetStatusMessage(message string) error {
   118  	args := params.SetMigrationStatusMessageArgs{
   119  		Message: message,
   120  	}
   121  	return c.caller.FacadeCall("SetStatusMessage", args, nil)
   122  }
   123  
   124  // ModelInfo return basic information about the model to migrated.
   125  func (c *Client) ModelInfo() (migration.ModelInfo, error) {
   126  	var info params.MigrationModelInfo
   127  	err := c.caller.FacadeCall("ModelInfo", nil, &info)
   128  	if err != nil {
   129  		return migration.ModelInfo{}, errors.Trace(err)
   130  	}
   131  	owner, err := names.ParseUserTag(info.OwnerTag)
   132  	if err != nil {
   133  		return migration.ModelInfo{}, errors.Trace(err)
   134  	}
   135  	return migration.ModelInfo{
   136  		UUID:         info.UUID,
   137  		Name:         info.Name,
   138  		Owner:        owner,
   139  		AgentVersion: info.AgentVersion,
   140  	}, nil
   141  }
   142  
   143  // Prechecks verifies that the source controller and model are healthy
   144  // and able to participate in a migration.
   145  func (c *Client) Prechecks() error {
   146  	return c.caller.FacadeCall("Prechecks", nil, nil)
   147  }
   148  
   149  // Export returns a serialized representation of the model associated
   150  // with the API connection. The charms used by the model are also
   151  // returned.
   152  func (c *Client) Export() (migration.SerializedModel, error) {
   153  	var serialized params.SerializedModel
   154  	err := c.caller.FacadeCall("Export", nil, &serialized)
   155  	if err != nil {
   156  		return migration.SerializedModel{}, err
   157  	}
   158  
   159  	// Convert tools info to output map.
   160  	tools := make(map[version.Binary]string)
   161  	for _, toolsInfo := range serialized.Tools {
   162  		v, err := version.ParseBinary(toolsInfo.Version)
   163  		if err != nil {
   164  			return migration.SerializedModel{}, errors.Annotate(err, "error parsing tools version")
   165  		}
   166  		tools[v] = toolsInfo.URI
   167  	}
   168  
   169  	return migration.SerializedModel{
   170  		Bytes:  serialized.Bytes,
   171  		Charms: serialized.Charms,
   172  		Tools:  tools,
   173  	}, nil
   174  }
   175  
   176  // Reap removes the documents for the model associated with the API
   177  // connection.
   178  func (c *Client) Reap() error {
   179  	return c.caller.FacadeCall("Reap", nil, nil)
   180  }
   181  
   182  // WatchMinionReports returns a watcher which reports when a migration
   183  // minion has made a report for the current migration phase.
   184  func (c *Client) WatchMinionReports() (watcher.NotifyWatcher, error) {
   185  	var result params.NotifyWatchResult
   186  	err := c.caller.FacadeCall("WatchMinionReports", nil, &result)
   187  	if err != nil {
   188  		return nil, errors.Trace(err)
   189  	}
   190  	if result.Error != nil {
   191  		return nil, result.Error
   192  	}
   193  	return c.newWatcher(c.caller.RawAPICaller(), result), nil
   194  }
   195  
   196  // MinionReports returns details of the reports made by migration
   197  // minions to the controller for the current migration phase.
   198  func (c *Client) MinionReports() (migration.MinionReports, error) {
   199  	var in params.MinionReports
   200  	var out migration.MinionReports
   201  
   202  	err := c.caller.FacadeCall("MinionReports", nil, &in)
   203  	if err != nil {
   204  		return out, errors.Trace(err)
   205  	}
   206  
   207  	out.MigrationId = in.MigrationId
   208  
   209  	phase, ok := migration.ParsePhase(in.Phase)
   210  	if !ok {
   211  		return out, errors.Errorf("invalid phase: %q", in.Phase)
   212  	}
   213  	out.Phase = phase
   214  
   215  	out.SuccessCount = in.SuccessCount
   216  	out.UnknownCount = in.UnknownCount
   217  
   218  	out.SomeUnknownMachines, out.SomeUnknownUnits, err = groupTagIds(in.UnknownSample)
   219  	if err != nil {
   220  		return out, errors.Annotate(err, "processing unknown agents")
   221  	}
   222  
   223  	out.FailedMachines, out.FailedUnits, err = groupTagIds(in.Failed)
   224  	if err != nil {
   225  		return out, errors.Annotate(err, "processing failed agents")
   226  	}
   227  
   228  	return out, nil
   229  }
   230  
   231  func groupTagIds(tagStrs []string) ([]string, []string, error) {
   232  	var machines []string
   233  	var units []string
   234  
   235  	for i := 0; i < len(tagStrs); i++ {
   236  		tag, err := names.ParseTag(tagStrs[i])
   237  		if err != nil {
   238  			return nil, nil, errors.Trace(err)
   239  		}
   240  		switch t := tag.(type) {
   241  		case names.MachineTag:
   242  			machines = append(machines, t.Id())
   243  		case names.UnitTag:
   244  			units = append(units, t.Id())
   245  		default:
   246  			return nil, nil, errors.Errorf("unsupported tag: %q", tag)
   247  		}
   248  	}
   249  	return machines, units, nil
   250  }