github.com/juju/juju@v0.0.0-20240430160146-1752b71fcf00/api/agent/upgradeseries/upgradeseries.go (about) 1 // Copyright 2018 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package upgradeseries 5 6 import ( 7 "strings" 8 9 "github.com/juju/errors" 10 "github.com/juju/names/v5" 11 12 "github.com/juju/juju/api/base" 13 "github.com/juju/juju/api/common" 14 corebase "github.com/juju/juju/core/base" 15 "github.com/juju/juju/core/model" 16 "github.com/juju/juju/core/status" 17 "github.com/juju/juju/rpc/params" 18 ) 19 20 const upgradeSeriesFacade = "UpgradeSeries" 21 22 // Client provides access to the UpgradeSeries API facade. 23 type Client struct { 24 *common.UpgradeSeriesAPI 25 *common.LeadershipPinningAPI 26 27 facade base.FacadeCaller 28 // authTag contains the authenticated unit/machine tag. 29 authTag names.Tag 30 } 31 32 // NewClient Constructs an API caller. 33 func NewClient(caller base.APICaller, authTag names.Tag) *Client { 34 facadeCaller := base.NewFacadeCaller( 35 caller, 36 upgradeSeriesFacade, 37 ) 38 return &Client{ 39 facade: facadeCaller, 40 authTag: authTag, 41 UpgradeSeriesAPI: common.NewUpgradeSeriesAPI(facadeCaller, authTag), 42 LeadershipPinningAPI: common.NewLeadershipPinningAPIFromFacade(facadeCaller), 43 } 44 } 45 46 // MachineStatus status retrieves the machine status from remote state. 47 func (s *Client) MachineStatus() (model.UpgradeSeriesStatus, error) { 48 var results params.UpgradeSeriesStatusResults 49 args := params.Entities{ 50 Entities: []params.Entity{{Tag: s.authTag.String()}}, 51 } 52 53 err := s.facade.FacadeCall("MachineStatus", args, &results) 54 if err != nil { 55 return "", errors.Trace(err) 56 } 57 if len(results.Results) != 1 { 58 return "", errors.Errorf("expected 1 result, got %d", len(results.Results)) 59 } 60 61 r := results.Results[0] 62 if r.Error == nil { 63 return r.Status, nil 64 } 65 66 if params.IsCodeNotFound(r.Error) { 67 return "", errors.NewNotFound(r.Error, "") 68 } 69 return "", errors.Trace(r.Error) 70 } 71 72 // UnitsPrepared returns the units running on this machine that have 73 // completed their upgrade-machine preparation, and are ready to be stopped and 74 // have their unit agent services converted for the target series. 75 func (s *Client) UnitsPrepared() ([]names.UnitTag, error) { 76 units, err := s.unitsInState("UnitsPrepared") 77 return units, errors.Trace(err) 78 } 79 80 // UnitsCompleted returns the units running on this machine that have completed 81 // the upgrade-machine workflow and are in their normal running state. 82 func (s *Client) UnitsCompleted() ([]names.UnitTag, error) { 83 units, err := s.unitsInState("UnitsCompleted") 84 return units, errors.Trace(err) 85 } 86 87 func (s *Client) unitsInState(facadeMethod string) ([]names.UnitTag, error) { 88 var results params.EntitiesResults 89 args := params.Entities{ 90 Entities: []params.Entity{{Tag: s.authTag.String()}}, 91 } 92 93 err := s.facade.FacadeCall(facadeMethod, args, &results) 94 if err != nil { 95 return nil, errors.Trace(err) 96 } 97 if len(results.Results) != 1 { 98 return nil, errors.Errorf("expected 1 result, got %d", len(results.Results)) 99 } 100 101 r := results.Results[0] 102 if r.Error == nil { 103 tags := make([]names.UnitTag, len(r.Entities)) 104 for i, e := range r.Entities { 105 tag, err := names.ParseUnitTag(e.Tag) 106 if err != nil { 107 return nil, errors.Trace(err) 108 } 109 tags[i] = tag 110 } 111 return tags, nil 112 } 113 114 if params.IsCodeNotFound(r.Error) { 115 return nil, errors.NewNotFound(r.Error, "") 116 } 117 return nil, errors.Trace(r.Error) 118 } 119 120 // SetMachineStatus sets the series upgrade status in remote state. 121 func (s *Client) SetMachineStatus(status model.UpgradeSeriesStatus, reason string) error { 122 var results params.ErrorResults 123 args := params.UpgradeSeriesStatusParams{ 124 Params: []params.UpgradeSeriesStatusParam{{ 125 Entity: params.Entity{Tag: s.authTag.String()}, 126 Status: status, 127 Message: reason, 128 }}, 129 } 130 131 err := s.facade.FacadeCall("SetMachineStatus", args, &results) 132 if err != nil { 133 return err 134 } 135 if len(results.Results) != 1 { 136 return errors.Errorf("expected 1 result, got %d", len(results.Results)) 137 } 138 139 result := results.Results[0] 140 if result.Error != nil { 141 return result.Error 142 } 143 return nil 144 } 145 146 // StartUnitCompletion starts the complete phase for all subordinate units. 147 func (s *Client) StartUnitCompletion(reason string) error { 148 var results params.ErrorResults 149 args := params.UpgradeSeriesStartUnitCompletionParam{ 150 Entities: []params.Entity{{Tag: s.authTag.String()}}, 151 Message: reason, 152 } 153 154 err := s.facade.FacadeCall("StartUnitCompletion", args, &results) 155 if err != nil { 156 return err 157 } 158 if len(results.Results) != 1 { 159 return errors.Errorf("expected 1 result, got %d", len(results.Results)) 160 } 161 162 result := results.Results[0] 163 if result.Error != nil { 164 return result.Error 165 } 166 return nil 167 } 168 169 // FinishUpgradeSeries notifies the controller that the upgrade process is 170 // completely finished, passing the current host OS series. 171 // We use the name "Finish" to distinguish this method from the various 172 // "Complete" phases. 173 func (s *Client) FinishUpgradeSeries(hostBase corebase.Base) error { 174 var results params.ErrorResults 175 args := params.UpdateChannelArgs{Args: []params.UpdateChannelArg{{ 176 Entity: params.Entity{Tag: s.authTag.String()}, 177 Channel: hostBase.Channel.Track, 178 }}} 179 180 err := s.facade.FacadeCall("FinishUpgradeSeries", args, &results) 181 if err != nil { 182 return err 183 } 184 if len(results.Results) != 1 { 185 return errors.Errorf("expected 1 result, got %d", len(results.Results)) 186 } 187 188 result := results.Results[0] 189 if result.Error != nil { 190 return result.Error 191 } 192 return nil 193 } 194 195 // SetInstanceStatus sets the machine status in remote state. 196 func (s *Client) SetInstanceStatus(sts model.UpgradeSeriesStatus, msg string) error { 197 var results params.ErrorResults 198 args := params.SetStatus{ 199 Entities: []params.EntityStatusArgs{{ 200 Tag: s.authTag.String(), 201 Status: string(status.Running), 202 Info: strings.Join([]string{"series upgrade ", string(sts), ": ", msg}, ""), 203 }}, 204 } 205 206 err := s.facade.FacadeCall("SetInstanceStatus", args, &results) 207 if err != nil { 208 return err 209 } 210 if len(results.Results) != 1 { 211 return errors.Errorf("expected 1 result, got %d", len(results.Results)) 212 } 213 214 result := results.Results[0] 215 if result.Error != nil { 216 return result.Error 217 } 218 return nil 219 }