github.com/niedbalski/juju@v0.0.0-20190215020005-8ff100488e47/apiserver/facades/client/charms/client_test.go (about) 1 // Copyright 2012-2014 Canonical Ltd. 2 // Licensed under the AGPLv3, see LICENCE file for details. 3 4 package charms_test 5 6 import ( 7 jc "github.com/juju/testing/checkers" 8 gc "gopkg.in/check.v1" 9 10 "github.com/juju/juju/apiserver/common" 11 "github.com/juju/juju/apiserver/facade" 12 "github.com/juju/juju/apiserver/facades/client/charms" 13 "github.com/juju/juju/apiserver/params" 14 "github.com/juju/juju/apiserver/testing" 15 "github.com/juju/juju/core/cache" 16 "github.com/juju/juju/core/leadership" 17 "github.com/juju/juju/core/lease" 18 jujutesting "github.com/juju/juju/juju/testing" 19 "github.com/juju/juju/state" 20 "github.com/juju/juju/testing/factory" 21 ) 22 23 type charmsSuite struct { 24 // TODO(anastasiamac) mock to remove JujuConnSuite 25 jujutesting.JujuConnSuite 26 api *charms.API 27 auth facade.Authorizer 28 } 29 30 var _ = gc.Suite(&charmsSuite{}) 31 32 // charmsSuiteContext implements the facade.Context interface. 33 type charmsSuiteContext struct{ cs *charmsSuite } 34 35 func (ctx *charmsSuiteContext) Abort() <-chan struct{} { return nil } 36 func (ctx *charmsSuiteContext) Auth() facade.Authorizer { return ctx.cs.auth } 37 func (ctx *charmsSuiteContext) Dispose() {} 38 func (ctx *charmsSuiteContext) Resources() facade.Resources { return common.NewResources() } 39 func (ctx *charmsSuiteContext) State() *state.State { return ctx.cs.State } 40 func (ctx *charmsSuiteContext) StatePool() *state.StatePool { return nil } 41 func (ctx *charmsSuiteContext) ID() string { return "" } 42 func (ctx *charmsSuiteContext) Presence() facade.Presence { return nil } 43 func (ctx *charmsSuiteContext) Hub() facade.Hub { return nil } 44 func (ctx *charmsSuiteContext) Controller() *cache.Controller { return nil } 45 46 func (ctx *charmsSuiteContext) LeadershipClaimer(string) (leadership.Claimer, error) { return nil, nil } 47 func (ctx *charmsSuiteContext) LeadershipChecker() (leadership.Checker, error) { return nil, nil } 48 func (ctx *charmsSuiteContext) LeadershipPinner(string) (leadership.Pinner, error) { return nil, nil } 49 func (ctx *charmsSuiteContext) SingularClaimer() (lease.Claimer, error) { return nil, nil } 50 51 func (s *charmsSuite) SetUpTest(c *gc.C) { 52 s.JujuConnSuite.SetUpTest(c) 53 54 s.auth = testing.FakeAuthorizer{ 55 Tag: s.AdminUserTag(c), 56 Controller: true, 57 } 58 59 var err error 60 s.api, err = charms.NewFacade(&charmsSuiteContext{cs: s}) 61 c.Assert(err, jc.ErrorIsNil) 62 } 63 64 func (s *charmsSuite) TestClientCharmInfo(c *gc.C) { 65 var clientCharmInfoTests = []struct { 66 about string 67 charm string 68 url string 69 expected params.CharmInfo 70 err string 71 }{ 72 { 73 about: "dummy charm which contains an expectedActions spec", 74 charm: "dummy", 75 url: "local:quantal/dummy-1", 76 expected: params.CharmInfo{ 77 Revision: 1, 78 URL: "local:quantal/dummy-1", 79 Config: map[string]params.CharmOption{ 80 "skill-level": { 81 Type: "int", 82 Description: "A number indicating skill."}, 83 "title": { 84 Type: "string", 85 Description: "A descriptive title used for the application.", 86 Default: "My Title"}, 87 "outlook": { 88 Type: "string", 89 Description: "No default outlook."}, 90 "username": { 91 Type: "string", 92 Description: "The name of the initial account (given admin permissions).", 93 Default: "admin001"}, 94 }, 95 Meta: ¶ms.CharmMeta{ 96 Name: "dummy", 97 Summary: "That's a dummy charm.", 98 Description: "This is a longer description which\npotentially contains multiple lines.\n", 99 Subordinate: false, 100 MinJujuVersion: "0.0.0", 101 }, 102 Actions: ¶ms.CharmActions{ 103 ActionSpecs: map[string]params.CharmActionSpec{ 104 "snapshot": { 105 Description: "Take a snapshot of the database.", 106 Params: map[string]interface{}{ 107 "title": "snapshot", 108 "description": "Take a snapshot of the database.", 109 "type": "object", 110 "properties": map[string]interface{}{ 111 "outfile": map[string]interface{}{ 112 "type": "string", 113 "description": "The file to write out to.", 114 "default": "foo.bz2", 115 }, 116 }, 117 }, 118 }, 119 }, 120 }, 121 }, 122 }, 123 { 124 about: "dummy charm which contains lxd profile spec", 125 charm: "lxd-profile", 126 url: "local:quantal/lxd-profile-0", 127 expected: params.CharmInfo{ 128 Revision: 0, 129 URL: "local:quantal/lxd-profile-0", 130 Config: map[string]params.CharmOption{}, 131 Meta: ¶ms.CharmMeta{ 132 Name: "lxd-profile", 133 Summary: "start a juju machine with a lxd profile", 134 Description: "Run an Ubuntu system, with the given lxd-profile\n", 135 Subordinate: false, 136 MinJujuVersion: "0.0.0", 137 Provides: map[string]params.CharmRelation{ 138 "ubuntu": { 139 Name: "ubuntu", 140 Interface: "ubuntu", 141 Role: "provider", 142 Scope: "global", 143 }, 144 }, 145 ExtraBindings: map[string]string{ 146 "another": "another", 147 }, 148 Tags: []string{ 149 "misc", 150 "application_development", 151 }, 152 Series: []string{ 153 "bionic", 154 "xenial", 155 "quantal", 156 }, 157 }, 158 Actions: ¶ms.CharmActions{}, 159 LXDProfile: ¶ms.CharmLXDProfile{ 160 Description: "lxd profile for testing, will pass validation", 161 Config: map[string]string{ 162 "security.nesting": "true", 163 "security.privileged": "true", 164 "linux.kernel_modules": "openvswitch,nbd,ip_tables,ip6_tables", 165 "environment.http_proxy": "", 166 }, 167 Devices: map[string]map[string]string{ 168 "tun": { 169 "path": "/dev/net/tun", 170 "type": "unix-char", 171 }, 172 "sony": { 173 "type": "usb", 174 "vendorid": "0fce", 175 "productid": "51da", 176 }, 177 "bdisk": { 178 "source": "/dev/loop0", 179 "type": "unix-block", 180 }, 181 "gpu": { 182 "type": "gpu", 183 }, 184 }, 185 }, 186 }, 187 }, 188 { 189 about: "retrieves charm info", 190 // Use wordpress for tests so that we can compare Provides and Requires. 191 charm: "wordpress", 192 url: "local:quantal/wordpress-3", 193 expected: params.CharmInfo{ 194 Revision: 3, 195 URL: "local:quantal/wordpress-3", 196 Config: map[string]params.CharmOption{ 197 "blog-title": {Type: "string", Description: "A descriptive title used for the blog.", Default: "My Title"}}, 198 Meta: ¶ms.CharmMeta{ 199 Name: "wordpress", 200 Summary: "Blog engine", 201 Description: "A pretty popular blog engine", 202 Subordinate: false, 203 Provides: map[string]params.CharmRelation{ 204 "logging-dir": { 205 Name: "logging-dir", 206 Role: "provider", 207 Interface: "logging", 208 Scope: "container", 209 }, 210 "monitoring-port": { 211 Name: "monitoring-port", 212 Role: "provider", 213 Interface: "monitoring", 214 Scope: "container", 215 }, 216 "url": { 217 Name: "url", 218 Role: "provider", 219 Interface: "http", 220 Scope: "global", 221 }, 222 }, 223 Requires: map[string]params.CharmRelation{ 224 "cache": { 225 Name: "cache", 226 Role: "requirer", 227 Interface: "varnish", 228 Optional: true, 229 Limit: 2, 230 Scope: "global", 231 }, 232 "db": { 233 Name: "db", 234 Role: "requirer", 235 Interface: "mysql", 236 Limit: 1, 237 Scope: "global", 238 }, 239 }, 240 ExtraBindings: map[string]string{ 241 "admin-api": "admin-api", 242 "foo-bar": "foo-bar", 243 "db-client": "db-client", 244 }, 245 MinJujuVersion: "0.0.0", 246 }, 247 Actions: ¶ms.CharmActions{ 248 ActionSpecs: map[string]params.CharmActionSpec{ 249 "fakeaction": { 250 Description: "No description", 251 Params: map[string]interface{}{ 252 "properties": map[string]interface{}{}, 253 "description": "No description", 254 "type": "object", 255 "title": "fakeaction"}, 256 }, 257 }, 258 }, 259 }, 260 }, 261 { 262 about: "invalid URL", 263 charm: "wordpress", 264 url: "not-valid!", 265 err: `cannot parse URL "not-valid!": name "not-valid!" not valid`, 266 }, 267 { 268 about: "invalid schema", 269 charm: "wordpress", 270 url: "not-valid:your-arguments", 271 err: `cannot parse URL "not-valid:your-arguments": schema "not-valid" not valid`, 272 }, 273 { 274 about: "unknown charm", 275 charm: "wordpress", 276 url: "cs:missing/one-1", 277 err: `charm "cs:missing/one-1" not found`, 278 }, 279 } 280 281 for i, t := range clientCharmInfoTests { 282 c.Logf("test %d. %s", i, t.about) 283 s.AddTestingCharm(c, t.charm) 284 info, err := s.api.CharmInfo(params.CharmURL{URL: t.url}) 285 if t.err != "" { 286 c.Check(err, gc.ErrorMatches, t.err) 287 continue 288 } 289 if c.Check(err, jc.ErrorIsNil) == false { 290 continue 291 } 292 c.Check(info, jc.DeepEquals, t.expected) 293 } 294 } 295 296 func (s *charmsSuite) TestMeteredCharmInfo(c *gc.C) { 297 meteredCharm := s.Factory.MakeCharm( 298 c, &factory.CharmParams{Name: "metered", URL: "cs:xenial/metered"}) 299 info, err := s.api.CharmInfo(params.CharmURL{ 300 URL: meteredCharm.URL().String(), 301 }) 302 c.Assert(err, jc.ErrorIsNil) 303 expected := ¶ms.CharmMetrics{ 304 Plan: params.CharmPlan{ 305 Required: true, 306 }, 307 Metrics: map[string]params.CharmMetric{ 308 "pings": { 309 Type: "gauge", 310 Description: "Description of the metric."}, 311 "pongs": { 312 Type: "gauge", 313 Description: "Description of the metric."}, 314 "juju-units": { 315 Type: "", 316 Description: ""}}} 317 c.Assert(info.Metrics, jc.DeepEquals, expected) 318 } 319 320 func (s *charmsSuite) TestListCharmsNoFilter(c *gc.C) { 321 s.assertListCharms(c, []string{"dummy"}, []string{}, []string{"local:quantal/dummy-1"}) 322 } 323 324 func (s *charmsSuite) TestListCharmsWithFilterMatchingNone(c *gc.C) { 325 s.assertListCharms(c, []string{"dummy"}, []string{"notdummy"}, []string{}) 326 } 327 328 func (s *charmsSuite) TestListCharmsFilteredOnly(c *gc.C) { 329 s.assertListCharms(c, []string{"dummy", "wordpress"}, []string{"dummy"}, []string{"local:quantal/dummy-1"}) 330 } 331 332 func (s *charmsSuite) assertListCharms(c *gc.C, someCharms, args, expected []string) { 333 for _, aCharm := range someCharms { 334 s.AddTestingCharm(c, aCharm) 335 } 336 found, err := s.api.List(params.CharmsList{Names: args}) 337 c.Assert(err, jc.ErrorIsNil) 338 c.Check(found.CharmURLs, gc.HasLen, len(expected)) 339 c.Check(found.CharmURLs, jc.DeepEquals, expected) 340 } 341 342 func (s *charmsSuite) TestIsMeteredFalse(c *gc.C) { 343 charm := s.Factory.MakeCharm(c, &factory.CharmParams{Name: "wordpress"}) 344 metered, err := s.api.IsMetered(params.CharmURL{ 345 URL: charm.URL().String(), 346 }) 347 c.Assert(err, jc.ErrorIsNil) 348 c.Assert(metered.Metered, jc.IsFalse) 349 } 350 351 func (s *charmsSuite) TestIsMeteredTrue(c *gc.C) { 352 meteredCharm := s.Factory.MakeCharm(c, &factory.CharmParams{Name: "metered", URL: "cs:quantal/metered"}) 353 metered, err := s.api.IsMetered(params.CharmURL{ 354 URL: meteredCharm.URL().String(), 355 }) 356 c.Assert(err, jc.ErrorIsNil) 357 c.Assert(metered.Metered, jc.IsTrue) 358 }