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: &params.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: &params.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: &params.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: &params.CharmActions{},
   159  				LXDProfile: &params.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: &params.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: &params.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 := &params.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  }