github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/application/get_test.go (about) 1 // Copyright 2012, 2013 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package application_test 5 6 import ( 7 "fmt" 8 9 jc "github.com/juju/testing/checkers" 10 gc "gopkg.in/check.v1" 11 "gopkg.in/juju/charm.v6" 12 "gopkg.in/juju/environschema.v1" 13 "gopkg.in/juju/names.v2" 14 15 apiapplication "github.com/juju/juju/api/application" 16 "github.com/juju/juju/apiserver/common" 17 "github.com/juju/juju/apiserver/facades/client/application" 18 "github.com/juju/juju/apiserver/params" 19 apiservertesting "github.com/juju/juju/apiserver/testing" 20 "github.com/juju/juju/caas" 21 k8s "github.com/juju/juju/caas/kubernetes/provider" 22 coreapplication "github.com/juju/juju/core/application" 23 "github.com/juju/juju/core/constraints" 24 "github.com/juju/juju/core/model" 25 jujutesting "github.com/juju/juju/juju/testing" 26 "github.com/juju/juju/state" 27 "github.com/juju/juju/testing/factory" 28 ) 29 30 type getSuite struct { 31 jujutesting.JujuConnSuite 32 33 applicationAPI *application.APIv9 34 authorizer apiservertesting.FakeAuthorizer 35 } 36 37 var _ = gc.Suite(&getSuite{}) 38 39 func (s *getSuite) SetUpTest(c *gc.C) { 40 s.JujuConnSuite.SetUpTest(c) 41 42 s.authorizer = apiservertesting.FakeAuthorizer{ 43 Tag: s.AdminUserTag(c), 44 } 45 storageAccess, err := application.GetStorageState(s.State) 46 c.Assert(err, jc.ErrorIsNil) 47 blockChecker := common.NewBlockChecker(s.State) 48 model, err := s.State.Model() 49 c.Assert(err, jc.ErrorIsNil) 50 api, err := application.NewAPIBase( 51 application.GetState(s.State), 52 storageAccess, 53 s.authorizer, 54 blockChecker, 55 model.ModelTag(), 56 model.Type(), 57 model.Name(), 58 application.CharmToStateCharm, 59 application.DeployApplication, 60 &mockStoragePoolManager{}, 61 common.NewResources(), 62 nil, // CAAS Broker not used in this suite. 63 ) 64 c.Assert(err, jc.ErrorIsNil) 65 s.applicationAPI = &application.APIv9{api} 66 } 67 68 func (s *getSuite) TestClientApplicationGetSmoketestV4(c *gc.C) { 69 s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 70 v4 := &application.APIv4{&application.APIv5{&application.APIv6{&application.APIv7{&application.APIv8{s.applicationAPI}}}}} 71 results, err := v4.Get(params.ApplicationGet{ApplicationName: "wordpress"}) 72 c.Assert(err, jc.ErrorIsNil) 73 c.Assert(results, gc.DeepEquals, params.ApplicationGetResults{ 74 Application: "wordpress", 75 Charm: "wordpress", 76 CharmConfig: map[string]interface{}{ 77 "blog-title": map[string]interface{}{ 78 "default": true, 79 "description": "A descriptive title used for the blog.", 80 "type": "string", 81 "value": "My Title", 82 }, 83 }, 84 Series: "quantal", 85 }) 86 } 87 88 func (s *getSuite) TestClientApplicationGetSmoketestV5(c *gc.C) { 89 s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 90 v5 := &application.APIv5{&application.APIv6{&application.APIv7{&application.APIv8{s.applicationAPI}}}} 91 results, err := v5.Get(params.ApplicationGet{ApplicationName: "wordpress"}) 92 c.Assert(err, jc.ErrorIsNil) 93 c.Assert(results, gc.DeepEquals, params.ApplicationGetResults{ 94 Application: "wordpress", 95 Charm: "wordpress", 96 CharmConfig: map[string]interface{}{ 97 "blog-title": map[string]interface{}{ 98 "default": "My Title", 99 "description": "A descriptive title used for the blog.", 100 "source": "default", 101 "type": "string", 102 "value": "My Title", 103 }, 104 }, 105 Series: "quantal", 106 }) 107 } 108 109 func (s *getSuite) TestClientApplicationGetIAASModelSmoketest(c *gc.C) { 110 s.AddTestingApplication(c, "wordpress", s.AddTestingCharm(c, "wordpress")) 111 112 results, err := s.applicationAPI.Get(params.ApplicationGet{ApplicationName: "wordpress"}) 113 c.Assert(err, jc.ErrorIsNil) 114 c.Assert(results, jc.DeepEquals, params.ApplicationGetResults{ 115 Application: "wordpress", 116 Charm: "wordpress", 117 CharmConfig: map[string]interface{}{ 118 "blog-title": map[string]interface{}{ 119 "default": "My Title", 120 "description": "A descriptive title used for the blog.", 121 "source": "default", 122 "type": "string", 123 "value": "My Title", 124 }, 125 }, 126 ApplicationConfig: map[string]interface{}{ 127 "trust": map[string]interface{}{ 128 "default": false, 129 "description": "Does this application have access to trusted credentials", 130 "source": "default", 131 "type": environschema.Tbool, 132 "value": false, 133 }}, 134 Series: "quantal", 135 }) 136 } 137 138 func (s *getSuite) TestClientApplicationGetCAASModelSmoketest(c *gc.C) { 139 st := s.Factory.MakeCAASModel(c, nil) 140 defer st.Close() 141 f := factory.NewFactory(st, s.StatePool) 142 ch := f.MakeCharm(c, &factory.CharmParams{Name: "dashboard4miner", Series: "kubernetes"}) 143 app := f.MakeApplication(c, &factory.ApplicationParams{Name: "dashboard4miner", Charm: ch}) 144 145 schemaFields, err := caas.ConfigSchema(k8s.ConfigSchema()) 146 c.Assert(err, jc.ErrorIsNil) 147 defaults := caas.ConfigDefaults(k8s.ConfigDefaults()) 148 149 schemaFields, defaults, err = application.AddTrustSchemaAndDefaults(schemaFields, defaults) 150 c.Assert(err, jc.ErrorIsNil) 151 152 appConfig, err := coreapplication.NewConfig(map[string]interface{}{"juju-external-hostname": "ext"}, schemaFields, defaults) 153 c.Assert(err, jc.ErrorIsNil) 154 err = app.UpdateApplicationConfig(appConfig.Attributes(), nil, schemaFields, defaults) 155 c.Assert(err, jc.ErrorIsNil) 156 157 expectedAppConfig := make(map[string]interface{}) 158 for name, field := range schemaFields { 159 info := map[string]interface{}{ 160 "description": field.Description, 161 "source": "unset", 162 "type": field.Type, 163 } 164 expectedAppConfig[name] = info 165 } 166 167 for name, val := range appConfig.Attributes() { 168 field := schemaFields[name] 169 info := map[string]interface{}{ 170 "description": field.Description, 171 "source": "unset", 172 "type": field.Type, 173 } 174 if val != nil { 175 info["source"] = "user" 176 info["value"] = val 177 } 178 if defaultVal := defaults[name]; defaultVal != nil { 179 info["default"] = defaultVal 180 info["source"] = "default" 181 if val != defaultVal { 182 info["source"] = "user" 183 } 184 } 185 expectedAppConfig[name] = info 186 } 187 188 storageAccess, err := application.GetStorageState(st) 189 c.Assert(err, jc.ErrorIsNil) 190 blockChecker := common.NewBlockChecker(st) 191 api, err := application.NewAPIBase( 192 application.GetState(st), 193 storageAccess, 194 s.authorizer, 195 blockChecker, 196 names.NewModelTag(st.ModelUUID()), 197 state.ModelTypeCAAS, 198 "caasmodel", 199 application.CharmToStateCharm, 200 application.DeployApplication, 201 &mockStoragePoolManager{}, 202 common.NewResources(), 203 nil, // CAAS Broker not used in this suite. 204 ) 205 c.Assert(err, jc.ErrorIsNil) 206 apiV8 := &application.APIv8{&application.APIv9{api}} 207 208 results, err := apiV8.Get(params.ApplicationGet{ApplicationName: "dashboard4miner"}) 209 c.Assert(err, jc.ErrorIsNil) 210 c.Assert(results, jc.DeepEquals, params.ApplicationGetResults{ 211 Application: "dashboard4miner", 212 Charm: "dashboard4miner", 213 CharmConfig: map[string]interface{}{ 214 "port": map[string]interface{}{ 215 "default": int64(443), 216 "description": "https port", 217 "source": "default", 218 "type": "int", 219 "value": int64(443), 220 }, 221 }, 222 ApplicationConfig: expectedAppConfig, 223 Series: "kubernetes", 224 }) 225 } 226 227 func (s *getSuite) TestApplicationGetUnknownApplication(c *gc.C) { 228 _, err := s.applicationAPI.Get(params.ApplicationGet{ApplicationName: "unknown"}) 229 c.Assert(err, gc.ErrorMatches, `application "unknown" not found`) 230 } 231 232 var getTests = []struct { 233 about string 234 charm string 235 constraints string 236 config charm.Settings 237 expect params.ApplicationGetResults 238 }{{ 239 about: "deployed application", 240 charm: "dummy", 241 constraints: "mem=2G cpu-power=400", 242 config: charm.Settings{ 243 // Different from default. 244 "title": "Look To Windward", 245 // Same as default. 246 "username": "admin001", 247 // Use default (but there's no charm default) 248 "skill-level": nil, 249 // Outlook is left unset. 250 }, 251 expect: params.ApplicationGetResults{ 252 CharmConfig: map[string]interface{}{ 253 "title": map[string]interface{}{ 254 "default": "My Title", 255 "description": "A descriptive title used for the application.", 256 "source": "user", 257 "type": "string", 258 "value": "Look To Windward", 259 }, 260 "outlook": map[string]interface{}{ 261 "description": "No default outlook.", 262 "source": "unset", 263 "type": "string", 264 }, 265 "username": map[string]interface{}{ 266 "default": "admin001", 267 "description": "The name of the initial account (given admin permissions).", 268 "source": "default", 269 "type": "string", 270 "value": "admin001", 271 }, 272 "skill-level": map[string]interface{}{ 273 "description": "A number indicating skill.", 274 "source": "unset", 275 "type": "int", 276 }, 277 }, 278 ApplicationConfig: map[string]interface{}{ 279 "trust": map[string]interface{}{ 280 "value": false, 281 "default": false, 282 "description": "Does this application have access to trusted credentials", 283 "source": "default", 284 "type": "bool", 285 }, 286 }, 287 Series: "quantal", 288 }, 289 }, { 290 about: "deployed application #2", 291 charm: "dummy", 292 config: charm.Settings{ 293 // Set title to default. 294 "title": nil, 295 // Value when there's a default. 296 "username": "foobie", 297 // Numeric value. 298 "skill-level": 0, 299 // String value. 300 "outlook": "phlegmatic", 301 }, 302 expect: params.ApplicationGetResults{ 303 CharmConfig: map[string]interface{}{ 304 "title": map[string]interface{}{ 305 "default": "My Title", 306 "description": "A descriptive title used for the application.", 307 "source": "default", 308 "type": "string", 309 "value": "My Title", 310 }, 311 "outlook": map[string]interface{}{ 312 "description": "No default outlook.", 313 "type": "string", 314 "source": "user", 315 "value": "phlegmatic", 316 }, 317 "username": map[string]interface{}{ 318 "default": "admin001", 319 "description": "The name of the initial account (given admin permissions).", 320 "source": "user", 321 "type": "string", 322 "value": "foobie", 323 }, 324 "skill-level": map[string]interface{}{ 325 "description": "A number indicating skill.", 326 "source": "user", 327 "type": "int", 328 // TODO(jam): 2013-08-28 bug #1217742 329 // we have to use float64() here, because the 330 // API does not preserve int types. This used 331 // to be int64() but we end up with a type 332 // mismatch when comparing the content 333 "value": float64(0), 334 }, 335 }, 336 ApplicationConfig: map[string]interface{}{ 337 "trust": map[string]interface{}{ 338 "value": false, 339 "default": false, 340 "description": "Does this application have access to trusted credentials", 341 "source": "default", 342 "type": "bool", 343 }, 344 }, 345 Series: "quantal", 346 }, 347 }, { 348 about: "subordinate application", 349 charm: "logging", 350 expect: params.ApplicationGetResults{ 351 CharmConfig: map[string]interface{}{}, 352 Series: "quantal", 353 ApplicationConfig: map[string]interface{}{ 354 "trust": map[string]interface{}{ 355 "value": false, 356 "default": false, 357 "description": "Does this application have access to trusted credentials", 358 "source": "default", 359 "type": "bool", 360 }, 361 }, 362 }, 363 }} 364 365 func (s *getSuite) TestApplicationGet(c *gc.C) { 366 for i, t := range getTests { 367 c.Logf("test %d. %s", i, t.about) 368 ch := s.AddTestingCharm(c, t.charm) 369 app := s.AddTestingApplication(c, fmt.Sprintf("test%d", i), ch) 370 371 var constraintsv constraints.Value 372 if t.constraints != "" { 373 constraintsv = constraints.MustParse(t.constraints) 374 err := app.SetConstraints(constraintsv) 375 c.Assert(err, jc.ErrorIsNil) 376 } 377 if t.config != nil { 378 err := app.UpdateCharmConfig(t.config) 379 c.Assert(err, jc.ErrorIsNil) 380 } 381 expect := t.expect 382 expect.Constraints = constraintsv 383 expect.Application = app.Name() 384 expect.Charm = ch.Meta().Name 385 client := apiapplication.NewClient(s.APIState) 386 got, err := client.Get(model.GenerationCurrent, app.Name()) 387 c.Assert(err, jc.ErrorIsNil) 388 c.Assert(*got, jc.DeepEquals, expect) 389 } 390 } 391 392 func (s *getSuite) TestGetMaxResolutionInt(c *gc.C) { 393 // See the bug http://pad.lv/1217742 394 // Get ends up pushing a map[string]interface{} which contains 395 // an int64 through a JSON Marshal & Unmarshal which ends up changing 396 // the int64 into a float64. We will fix it if we find it is actually a 397 // problem. 398 const nonFloatInt = (int64(1) << 54) + 1 399 const asFloat = float64(nonFloatInt) 400 c.Assert(int64(asFloat), gc.Not(gc.Equals), nonFloatInt) 401 c.Assert(int64(asFloat)+1, gc.Equals, nonFloatInt) 402 403 ch := s.AddTestingCharm(c, "dummy") 404 app := s.AddTestingApplication(c, "test-application", ch) 405 406 err := app.UpdateCharmConfig(map[string]interface{}{"skill-level": nonFloatInt}) 407 c.Assert(err, jc.ErrorIsNil) 408 client := apiapplication.NewClient(s.APIState) 409 got, err := client.Get(model.GenerationCurrent, app.Name()) 410 c.Assert(err, jc.ErrorIsNil) 411 c.Assert(got.CharmConfig["skill-level"], jc.DeepEquals, map[string]interface{}{ 412 "description": "A number indicating skill.", 413 "source": "user", 414 "type": "int", 415 "value": asFloat, 416 }) 417 }