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  }