github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/agent/caasoperator/operator.go (about) 1 // Copyright 2017 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package caasoperator 5 6 import ( 7 "github.com/juju/errors" 8 "gopkg.in/juju/names.v2" 9 10 "github.com/juju/juju/apiserver/common" 11 "github.com/juju/juju/apiserver/facade" 12 "github.com/juju/juju/apiserver/params" 13 "github.com/juju/juju/caas" 14 "github.com/juju/juju/core/status" 15 "github.com/juju/juju/environs" 16 "github.com/juju/juju/state/watcher" 17 ) 18 19 type Facade struct { 20 auth facade.Authorizer 21 resources facade.Resources 22 state CAASOperatorState 23 *common.LifeGetter 24 *common.AgentEntityWatcher 25 *common.Remover 26 *common.ToolsSetter 27 *common.APIAddresser 28 29 model Model 30 } 31 32 // NewStateFacade provides the signature required for facade registration. 33 func NewStateFacade(ctx facade.Context) (*Facade, error) { 34 authorizer := ctx.Auth() 35 resources := ctx.Resources() 36 return NewFacade(resources, authorizer, stateShim{ctx.State()}) 37 } 38 39 // NewFacade returns a new CAASOperator facade. 40 func NewFacade( 41 resources facade.Resources, 42 authorizer facade.Authorizer, 43 st CAASOperatorState, 44 ) (*Facade, error) { 45 if !authorizer.AuthApplicationAgent() { 46 return nil, common.ErrPerm 47 } 48 model, err := st.Model() 49 if err != nil { 50 return nil, errors.Trace(err) 51 } 52 canRead := common.AuthAny( 53 common.AuthFuncForTagKind(names.ApplicationTagKind), 54 common.AuthFuncForTagKind(names.UnitTagKind), 55 ) 56 accessUnit := func() (common.AuthFunc, error) { 57 switch tag := authorizer.GetAuthTag().(type) { 58 case names.ApplicationTag: 59 // Any of the units belonging to 60 // the application can be accessed. 61 app, err := st.Application(tag.Name) 62 if err != nil { 63 return nil, errors.Trace(err) 64 } 65 allUnits, err := app.AllUnits() 66 if err != nil { 67 return nil, errors.Trace(err) 68 } 69 return func(tag names.Tag) bool { 70 for _, u := range allUnits { 71 if u.Tag() == tag { 72 return true 73 } 74 } 75 return false 76 }, nil 77 default: 78 return nil, errors.Errorf("expected names.ApplicationTag, got %T", tag) 79 } 80 } 81 return &Facade{ 82 LifeGetter: common.NewLifeGetter(st, canRead), 83 APIAddresser: common.NewAPIAddresser(st, resources), 84 AgentEntityWatcher: common.NewAgentEntityWatcher(st, resources, canRead), 85 Remover: common.NewRemover(st, true, accessUnit), 86 ToolsSetter: common.NewToolsSetter(st, common.AuthFuncForTag(authorizer.GetAuthTag())), 87 auth: authorizer, 88 resources: resources, 89 state: st, 90 model: model, 91 }, nil 92 } 93 94 // CurrentModel returns the name and UUID for the current juju model. 95 func (f *Facade) CurrentModel() (params.ModelResult, error) { 96 return params.ModelResult{ 97 Name: f.model.Name(), 98 UUID: f.model.UUID(), 99 Type: string(f.model.Type()), 100 }, nil 101 } 102 103 // SetStatus sets the status of each given entity. 104 func (f *Facade) SetStatus(args params.SetStatus) (params.ErrorResults, error) { 105 results := params.ErrorResults{ 106 Results: make([]params.ErrorResult, len(args.Entities)), 107 } 108 authTag := f.auth.GetAuthTag() 109 for i, arg := range args.Entities { 110 tag, err := names.ParseApplicationTag(arg.Tag) 111 if err != nil { 112 results.Results[i].Error = common.ServerError(err) 113 continue 114 } 115 if tag != authTag { 116 results.Results[i].Error = common.ServerError(common.ErrPerm) 117 continue 118 } 119 info := status.StatusInfo{ 120 Status: status.Status(arg.Status), 121 Message: arg.Info, 122 Data: arg.Data, 123 } 124 results.Results[i].Error = common.ServerError(f.setStatus(tag, info)) 125 } 126 return results, nil 127 } 128 129 func (f *Facade) setStatus(tag names.ApplicationTag, info status.StatusInfo) error { 130 app, err := f.state.Application(tag.Id()) 131 if err != nil { 132 return errors.Trace(err) 133 } 134 return app.SetOperatorStatus(info) 135 } 136 137 // Charm returns the charm info for all given applications. 138 func (f *Facade) Charm(args params.Entities) (params.ApplicationCharmResults, error) { 139 results := params.ApplicationCharmResults{ 140 Results: make([]params.ApplicationCharmResult, len(args.Entities)), 141 } 142 authTag := f.auth.GetAuthTag() 143 for i, entity := range args.Entities { 144 tag, err := names.ParseApplicationTag(entity.Tag) 145 if err != nil { 146 results.Results[i].Error = common.ServerError(err) 147 continue 148 } 149 if tag != authTag { 150 results.Results[i].Error = common.ServerError(common.ErrPerm) 151 continue 152 } 153 application, err := f.state.Application(tag.Id()) 154 if err != nil { 155 results.Results[i].Error = common.ServerError(err) 156 continue 157 } 158 charm, force, err := application.Charm() 159 if err != nil { 160 results.Results[i].Error = common.ServerError(err) 161 continue 162 } 163 results.Results[i].Result = ¶ms.ApplicationCharm{ 164 URL: charm.URL().String(), 165 ForceUpgrade: force, 166 SHA256: charm.BundleSha256(), 167 CharmModifiedVersion: application.CharmModifiedVersion(), 168 } 169 } 170 return results, nil 171 } 172 173 // SetPodSpec sets the container specs for a set of applications. 174 func (f *Facade) SetPodSpec(args params.SetPodSpecParams) (params.ErrorResults, error) { 175 results := params.ErrorResults{ 176 Results: make([]params.ErrorResult, len(args.Specs)), 177 } 178 179 cfg, err := f.model.ModelConfig() 180 if err != nil { 181 return params.ErrorResults{}, errors.Trace(err) 182 } 183 provider, err := environs.Provider(cfg.Type()) 184 if err != nil { 185 return params.ErrorResults{}, errors.Trace(err) 186 } 187 caasProvider, ok := provider.(caas.ContainerEnvironProvider) 188 if !ok { 189 return params.ErrorResults{}, errors.NotValidf("container environ provider %T", provider) 190 } 191 192 for i, arg := range args.Specs { 193 tag, err := names.ParseApplicationTag(arg.Tag) 194 if err != nil { 195 results.Results[i].Error = common.ServerError(common.ErrPerm) 196 continue 197 } 198 if !f.auth.AuthOwner(tag) { 199 results.Results[i].Error = common.ServerError(common.ErrPerm) 200 continue 201 } 202 if _, err := caasProvider.ParsePodSpec(arg.Value); err != nil { 203 results.Results[i].Error = common.ServerError(errors.New("invalid pod spec")) 204 continue 205 } 206 results.Results[i].Error = common.ServerError( 207 f.model.SetPodSpec(tag, arg.Value), 208 ) 209 } 210 return results, nil 211 } 212 213 // WatchUnits starts a StringsWatcher to watch changes to the 214 // lifecycle states of units for the specified applications in 215 // this model. 216 func (f *Facade) WatchUnits(args params.Entities) (params.StringsWatchResults, error) { 217 results := params.StringsWatchResults{ 218 Results: make([]params.StringsWatchResult, len(args.Entities)), 219 } 220 for i, arg := range args.Entities { 221 id, changes, err := f.watchUnits(arg.Tag) 222 if err != nil { 223 results.Results[i].Error = common.ServerError(err) 224 continue 225 } 226 results.Results[i].StringsWatcherId = id 227 results.Results[i].Changes = changes 228 } 229 return results, nil 230 } 231 232 func (f *Facade) watchUnits(tagString string) (string, []string, error) { 233 tag, err := names.ParseApplicationTag(tagString) 234 if err != nil { 235 return "", nil, errors.Trace(err) 236 } 237 app, err := f.state.Application(tag.Id()) 238 if err != nil { 239 return "", nil, errors.Trace(err) 240 } 241 w := app.WatchUnits() 242 if changes, ok := <-w.Changes(); ok { 243 return f.resources.Register(w), changes, nil 244 } 245 return "", nil, watcher.EnsureErr(w) 246 }