github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/modelconfig/modelconfig_test.go (about) 1 // Copyright 2016 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package modelconfig_test 5 6 import ( 7 "github.com/juju/errors" 8 gitjujutesting "github.com/juju/testing" 9 jc "github.com/juju/testing/checkers" 10 gc "gopkg.in/check.v1" 11 "gopkg.in/juju/names.v2" 12 13 "github.com/juju/juju/apiserver/facades/client/modelconfig" 14 "github.com/juju/juju/apiserver/params" 15 apiservertesting "github.com/juju/juju/apiserver/testing" 16 "github.com/juju/juju/environs/config" 17 "github.com/juju/juju/provider/dummy" 18 _ "github.com/juju/juju/provider/dummy" 19 "github.com/juju/juju/state" 20 "github.com/juju/juju/testing" 21 ) 22 23 type modelconfigSuite struct { 24 gitjujutesting.IsolationSuite 25 backend *mockBackend 26 authorizer apiservertesting.FakeAuthorizer 27 api *modelconfig.ModelConfigAPIV2 28 } 29 30 var _ = gc.Suite(&modelconfigSuite{}) 31 32 func (s *modelconfigSuite) SetUpTest(c *gc.C) { 33 s.IsolationSuite.SetUpTest(c) 34 s.authorizer = apiservertesting.FakeAuthorizer{ 35 Tag: names.NewUserTag("bruce@local"), 36 AdminTag: names.NewUserTag("bruce@local"), 37 } 38 s.backend = &mockBackend{ 39 cfg: config.ConfigValues{ 40 "type": {"dummy", "model"}, 41 "agent-version": {"1.2.3.4", "model"}, 42 "ftp-proxy": {"http://proxy", "model"}, 43 "authorized-keys": {testing.FakeAuthKeys, "model"}, 44 }, 45 } 46 var err error 47 s.api, err = modelconfig.NewModelConfigAPI(s.backend, &s.authorizer) 48 c.Assert(err, jc.ErrorIsNil) 49 } 50 51 func (s *modelconfigSuite) TestModelGet(c *gc.C) { 52 result, err := s.api.ModelGet() 53 c.Assert(err, jc.ErrorIsNil) 54 c.Assert(result.Config, jc.DeepEquals, map[string]params.ConfigValue{ 55 "type": {"dummy", "model"}, 56 "ftp-proxy": {"http://proxy", "model"}, 57 "agent-version": {Value: "1.2.3.4", Source: "model"}, 58 }) 59 } 60 61 func (s *modelconfigSuite) assertConfigValue(c *gc.C, key string, expected interface{}) { 62 value, found := s.backend.cfg[key] 63 c.Assert(found, jc.IsTrue) 64 c.Assert(value.Value, gc.Equals, expected) 65 } 66 67 func (s *modelconfigSuite) assertConfigValueMissing(c *gc.C, key string) { 68 _, found := s.backend.cfg[key] 69 c.Assert(found, jc.IsFalse) 70 } 71 72 func (s *modelconfigSuite) TestModelSet(c *gc.C) { 73 params := params.ModelSet{ 74 Config: map[string]interface{}{ 75 "some-key": "value", 76 "other-key": "other value"}, 77 } 78 err := s.api.ModelSet(params) 79 c.Assert(err, jc.ErrorIsNil) 80 s.assertConfigValue(c, "some-key", "value") 81 s.assertConfigValue(c, "other-key", "other value") 82 } 83 84 func (s *modelconfigSuite) blockAllChanges(c *gc.C, msg string) { 85 s.backend.msg = msg 86 s.backend.b = state.ChangeBlock 87 } 88 89 func (s *modelconfigSuite) assertBlocked(c *gc.C, err error, msg string) { 90 c.Assert(params.IsCodeOperationBlocked(err), jc.IsTrue, gc.Commentf("error: %#v", err)) 91 c.Assert(errors.Cause(err), jc.DeepEquals, ¶ms.Error{ 92 Message: msg, 93 Code: "operation is blocked", 94 }) 95 } 96 97 func (s *modelconfigSuite) assertModelSetBlocked(c *gc.C, args map[string]interface{}, msg string) { 98 err := s.api.ModelSet(params.ModelSet{args}) 99 s.assertBlocked(c, err, msg) 100 } 101 102 func (s *modelconfigSuite) TestBlockChangesModelSet(c *gc.C) { 103 s.blockAllChanges(c, "TestBlockChangesModelSet") 104 args := map[string]interface{}{"some-key": "value"} 105 s.assertModelSetBlocked(c, args, "TestBlockChangesModelSet") 106 } 107 108 func (s *modelconfigSuite) TestModelSetCannotChangeAgentVersion(c *gc.C) { 109 old, err := config.New(config.UseDefaults, dummy.SampleConfig().Merge(testing.Attrs{ 110 "agent-version": "1.2.3.4", 111 })) 112 c.Assert(err, jc.ErrorIsNil) 113 s.backend.old = old 114 args := params.ModelSet{ 115 map[string]interface{}{"agent-version": "9.9.9"}, 116 } 117 err = s.api.ModelSet(args) 118 c.Assert(err, gc.ErrorMatches, "agent-version cannot be changed") 119 120 // It's okay to pass config back with the same agent-version. 121 result, err := s.api.ModelGet() 122 c.Assert(err, jc.ErrorIsNil) 123 c.Assert(result.Config["agent-version"], gc.NotNil) 124 args.Config["agent-version"] = result.Config["agent-version"].Value 125 err = s.api.ModelSet(args) 126 c.Assert(err, jc.ErrorIsNil) 127 } 128 129 func (s *modelconfigSuite) TestAdminCanSetLogTrace(c *gc.C) { 130 args := params.ModelSet{ 131 map[string]interface{}{"logging-config": "<root>=DEBUG;somepackage=TRACE"}, 132 } 133 err := s.api.ModelSet(args) 134 c.Assert(err, jc.ErrorIsNil) 135 136 result, err := s.api.ModelGet() 137 c.Assert(err, jc.ErrorIsNil) 138 c.Assert(result.Config["logging-config"].Value, gc.Equals, "<root>=DEBUG;somepackage=TRACE") 139 } 140 141 func (s *modelconfigSuite) TestUserCanSetLogNoTrace(c *gc.C) { 142 args := params.ModelSet{ 143 map[string]interface{}{"logging-config": "<root>=DEBUG;somepackage=ERROR"}, 144 } 145 apiUser := names.NewUserTag("fred") 146 s.authorizer.Tag = apiUser 147 s.authorizer.HasWriteTag = apiUser 148 err := s.api.ModelSet(args) 149 c.Assert(err, jc.ErrorIsNil) 150 151 result, err := s.api.ModelGet() 152 c.Assert(err, jc.ErrorIsNil) 153 c.Assert(result.Config["logging-config"].Value, gc.Equals, "<root>=DEBUG;somepackage=ERROR") 154 } 155 156 func (s *modelconfigSuite) TestUserReadAccess(c *gc.C) { 157 apiUser := names.NewUserTag("read") 158 s.authorizer.Tag = apiUser 159 160 _, err := s.api.ModelGet() 161 c.Assert(err, jc.ErrorIsNil) 162 163 err = s.api.ModelSet(params.ModelSet{}) 164 c.Assert(errors.Cause(err), gc.ErrorMatches, "permission denied") 165 } 166 167 func (s *modelconfigSuite) TestUserCannotSetLogTrace(c *gc.C) { 168 args := params.ModelSet{ 169 map[string]interface{}{"logging-config": "<root>=DEBUG;somepackage=TRACE"}, 170 } 171 apiUser := names.NewUserTag("fred") 172 s.authorizer.Tag = apiUser 173 s.authorizer.HasWriteTag = apiUser 174 err := s.api.ModelSet(args) 175 c.Assert(err, gc.ErrorMatches, `only controller admins can set a model's logging level to TRACE`) 176 } 177 178 func (s *modelconfigSuite) TestModelUnset(c *gc.C) { 179 err := s.backend.UpdateModelConfig(map[string]interface{}{"abc": 123}, nil) 180 c.Assert(err, jc.ErrorIsNil) 181 182 args := params.ModelUnset{[]string{"abc"}} 183 err = s.api.ModelUnset(args) 184 c.Assert(err, jc.ErrorIsNil) 185 s.assertConfigValueMissing(c, "abc") 186 } 187 188 func (s *modelconfigSuite) TestBlockModelUnset(c *gc.C) { 189 err := s.backend.UpdateModelConfig(map[string]interface{}{"abc": 123}, nil) 190 c.Assert(err, jc.ErrorIsNil) 191 s.blockAllChanges(c, "TestBlockModelUnset") 192 193 args := params.ModelUnset{[]string{"abc"}} 194 err = s.api.ModelUnset(args) 195 s.assertBlocked(c, err, "TestBlockModelUnset") 196 } 197 198 func (s *modelconfigSuite) TestModelUnsetMissing(c *gc.C) { 199 // It's okay to unset a non-existent attribute. 200 args := params.ModelUnset{[]string{"not_there"}} 201 err := s.api.ModelUnset(args) 202 c.Assert(err, jc.ErrorIsNil) 203 } 204 205 func (s *modelconfigSuite) TestSetSupportCredentals(c *gc.C) { 206 err := s.api.SetSLALevel(params.ModelSLA{params.ModelSLAInfo{"level", "bob"}, []byte("foobar")}) 207 c.Assert(err, jc.ErrorIsNil) 208 } 209 210 type mockBackend struct { 211 cfg config.ConfigValues 212 old *config.Config 213 b state.BlockType 214 msg string 215 } 216 217 func (m *mockBackend) ModelConfigValues() (config.ConfigValues, error) { 218 return m.cfg, nil 219 } 220 221 func (m *mockBackend) Sequences() (map[string]int, error) { 222 return nil, nil 223 } 224 225 func (m *mockBackend) UpdateModelConfig(update map[string]interface{}, remove []string, validate ...state.ValidateConfigFunc) error { 226 for _, validateFunc := range validate { 227 if err := validateFunc(update, remove, m.old); err != nil { 228 return err 229 } 230 } 231 for k, v := range update { 232 m.cfg[k] = config.ConfigValue{v, "model"} 233 } 234 for _, n := range remove { 235 delete(m.cfg, n) 236 } 237 return nil 238 } 239 240 func (m *mockBackend) GetBlockForType(t state.BlockType) (state.Block, bool, error) { 241 if m.b == t { 242 return &mockBlock{t: t, m: m.msg}, true, nil 243 } else { 244 return nil, false, nil 245 } 246 } 247 248 func (m *mockBackend) ModelTag() names.ModelTag { 249 return names.NewModelTag("deadbeef-2f18-4fd2-967d-db9663db7bea") 250 } 251 252 func (m *mockBackend) ControllerTag() names.ControllerTag { 253 return names.NewControllerTag("deadbeef-babe-4fd2-967d-db9663db7bea") 254 } 255 256 func (m *mockBackend) SetSLA(level, owner string, credentials []byte) error { 257 return nil 258 } 259 260 func (m *mockBackend) SLALevel() (string, error) { 261 return "mock-level", nil 262 } 263 264 type mockBlock struct { 265 state.Block 266 t state.BlockType 267 m string 268 } 269 270 func (m mockBlock) Id() string { return "" } 271 272 func (m mockBlock) Tag() (names.Tag, error) { return names.NewModelTag("mocktesting"), nil } 273 274 func (m mockBlock) Type() state.BlockType { return m.t } 275 276 func (m mockBlock) Message() string { return m.m } 277 278 func (m mockBlock) ModelUUID() string { return "" }