github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/modelgeneration/modelgeneration.go (about) 1 // Copyright 2019 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package modelgeneration 5 6 import ( 7 "github.com/juju/errors" 8 "github.com/juju/loggo" 9 "gopkg.in/juju/names.v2" 10 11 "github.com/juju/juju/apiserver/common" 12 "github.com/juju/juju/apiserver/facade" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/permission" 15 ) 16 17 var logger = loggo.GetLogger("juju.apiserver.modelgeneration") 18 19 // ModelGenerationAPI implements the ModelGeneration interface and is the concrete implementation 20 // of the API endpoint. 21 type ModelGenerationAPI struct { 22 check *common.BlockChecker 23 authorizer facade.Authorizer 24 apiUser names.UserTag 25 isControllerAdmin bool 26 model GenerationModel 27 } 28 29 // NewModelGenerationFacade provides the signature required for facade registration. 30 func NewModelGenerationFacade(ctx facade.Context) (*ModelGenerationAPI, error) { 31 authorizer := ctx.Auth() 32 st := &modelGenerationStateShim{State: ctx.State()} 33 model, err := st.Model() 34 if err != nil { 35 return nil, errors.Trace(err) 36 } 37 return NewModelGenerationAPI(st, authorizer, model) 38 } 39 40 // NewModelGenerationAPI creates a new API endpoint for dealing with model generations. 41 func NewModelGenerationAPI( 42 st ModelGenerationState, 43 authorizer facade.Authorizer, 44 m GenerationModel, 45 ) (*ModelGenerationAPI, error) { 46 if !authorizer.AuthClient() { 47 return nil, common.ErrPerm 48 } 49 // Since we know this is a user tag (because AuthClient is true), 50 // we just do the type assertion to the UserTag. 51 apiUser, _ := authorizer.GetAuthTag().(names.UserTag) 52 // Pretty much all of the user manager methods have special casing for admin 53 // users, so look once when we start and remember if the user is an admin. 54 isAdmin, err := authorizer.HasPermission(permission.SuperuserAccess, st.ControllerTag()) 55 if err != nil { 56 return nil, errors.Trace(err) 57 } 58 59 return &ModelGenerationAPI{ 60 authorizer: authorizer, 61 isControllerAdmin: isAdmin, 62 apiUser: apiUser, 63 model: m, 64 }, nil 65 } 66 67 func (m *ModelGenerationAPI) hasAdminAccess(modelTag names.ModelTag) (bool, error) { 68 canWrite, err := m.authorizer.HasPermission(permission.AdminAccess, modelTag) 69 if errors.IsNotFound(err) { 70 return false, nil 71 } 72 return canWrite, err 73 } 74 75 // AddGeneration adds a 'next' generation to the given model. 76 func (m *ModelGenerationAPI) AddGeneration(arg params.Entity) (params.ErrorResult, error) { 77 result := params.ErrorResult{} 78 modelTag, err := names.ParseModelTag(arg.Tag) 79 if err != nil { 80 return result, errors.Trace(err) 81 } 82 isModelAdmin, err := m.hasAdminAccess(modelTag) 83 if !isModelAdmin && !m.isControllerAdmin { 84 return result, common.ErrPerm 85 } 86 87 result.Error = common.ServerError(m.model.AddGeneration()) 88 return result, nil 89 } 90 91 // HasNextGeneration returns a true result if the input model has a "next" 92 // generation that has not yet been completed. 93 func (m *ModelGenerationAPI) HasNextGeneration(arg params.Entity) (params.BoolResult, error) { 94 result := params.BoolResult{} 95 modelTag, err := names.ParseModelTag(arg.Tag) 96 if err != nil { 97 return result, errors.Trace(err) 98 } 99 isModelAdmin, err := m.hasAdminAccess(modelTag) 100 if !isModelAdmin && !m.isControllerAdmin { 101 return result, common.ErrPerm 102 } 103 104 if has, err := m.model.HasNextGeneration(); err != nil { 105 result.Error = common.ServerError(err) 106 } else { 107 result.Result = has 108 } 109 return result, nil 110 } 111 112 // AdvanceGeneration, adds the provided unit(s) and/or application(s) to 113 // the "next" generation. If the generation can auto complete, it is 114 // made the "current" generation. 115 func (m *ModelGenerationAPI) AdvanceGeneration(arg params.AdvanceGenerationArg) (params.AdvanceGenerationResult, error) { 116 modelTag, err := names.ParseModelTag(arg.Model.Tag) 117 if err != nil { 118 return params.AdvanceGenerationResult{}, errors.Trace(err) 119 } 120 isModelAdmin, err := m.hasAdminAccess(modelTag) 121 if !isModelAdmin && !m.isControllerAdmin { 122 return params.AdvanceGenerationResult{}, common.ErrPerm 123 } 124 125 generation, err := m.model.NextGeneration() 126 if err != nil { 127 return params.AdvanceGenerationResult{}, errors.Trace(err) 128 } 129 130 results := params.ErrorResults{ 131 Results: make([]params.ErrorResult, len(arg.Entities)), 132 } 133 for i, entity := range arg.Entities { 134 tag, err := names.ParseTag(entity.Tag) 135 if err != nil { 136 results.Results[i].Error = common.ServerError(err) 137 continue 138 } 139 switch tag.Kind() { 140 case names.ApplicationTagKind: 141 results.Results[i].Error = common.ServerError(generation.AssignAllUnits(tag.Id())) 142 case names.UnitTagKind: 143 results.Results[i].Error = common.ServerError(generation.AssignUnit(tag.Id())) 144 default: 145 results.Results[i].Error = common.ServerError( 146 errors.Errorf("expected names.UnitTag or names.ApplicationTag, got %T", tag)) 147 } 148 if err := generation.Refresh(); err != nil { 149 return params.AdvanceGenerationResult{AdvanceResults: results}, errors.Trace(err) 150 } 151 } 152 result := params.AdvanceGenerationResult{AdvanceResults: results} 153 154 // Complete the generation if possible. 155 completed, err := generation.AutoComplete() 156 result.CompleteResult = params.BoolResult{ 157 Result: completed, 158 Error: common.ServerError(err), 159 } 160 return result, nil 161 } 162 163 // CancelGeneration cancels the 'next' generation if cancel 164 // criteria are met. 165 func (m *ModelGenerationAPI) CancelGeneration(arg params.Entity) (params.ErrorResult, error) { 166 result := params.ErrorResult{} 167 modelTag, err := names.ParseModelTag(arg.Tag) 168 if err != nil { 169 return result, errors.Trace(err) 170 } 171 isModelAdmin, err := m.hasAdminAccess(modelTag) 172 if !isModelAdmin && !m.isControllerAdmin { 173 return result, common.ErrPerm 174 } 175 176 generation, err := m.model.NextGeneration() 177 if err != nil { 178 return result, errors.Trace(err) 179 } 180 result.Error = common.ServerError(generation.MakeCurrent()) 181 return result, nil 182 }