github.com/axw/juju@v0.0.0-20161005053422-4bd6544d08d4/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 }