Skip to content

Commit

Permalink
Merge pull request #8348 from wallyworld/caas-uniter
Browse files Browse the repository at this point in the history
  • Loading branch information
jujubot committed Feb 9, 2018
2 parents f93b43d + 38c1173 commit d7d2acc
Show file tree
Hide file tree
Showing 70 changed files with 1,625 additions and 771 deletions.
111 changes: 77 additions & 34 deletions api/caasoperator/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,9 @@ import (
"gopkg.in/juju/names.v2"

"github.com/juju/juju/api/base"
"github.com/juju/juju/api/common"
apiwatcher "github.com/juju/juju/api/watcher"
"github.com/juju/juju/apiserver/params"
"github.com/juju/juju/core/life"
"github.com/juju/juju/status"
"github.com/juju/juju/watcher"
)
Expand Down Expand Up @@ -103,39 +104,6 @@ func (c *Client) Charm(application string) (_ *charm.URL, sha256 string, _ error
return curl, result.SHA256, nil
}

// WatchCharmConfig returns a watcher that is notified whenever the
// application's charm config changes.
func (c *Client) WatchCharmConfig(application string) (watcher.NotifyWatcher, error) {
tag, err := c.appTag(application)
if err != nil {
return nil, errors.Trace(err)
}
return common.Watch(c.facade, "WatchCharmConfig", tag)
}

// CharmConfig returns the application's charm config settings.
func (c *Client) CharmConfig(application string) (charm.Settings, error) {
tag, err := c.appTag(application)
if err != nil {
return nil, errors.Trace(err)
}
var results params.ConfigSettingsResults
args := params.Entities{
Entities: []params.Entity{{Tag: tag.String()}},
}
if err := c.facade.FacadeCall("CharmConfig", args, &results); err != nil {
return nil, errors.Trace(err)
}
if len(results.Results) != 1 {
return nil, errors.Errorf("expected 1 result, got %d", len(results.Results))
}
result := results.Results[0]
if result.Error != nil {
return nil, errors.Trace(result.Error)
}
return charm.Settings(result.Settings), nil
}

// SetContainerSpec sets the container spec of the specified application or unit.
func (c *Client) SetContainerSpec(entityName string, spec string) error {
var tag names.Tag
Expand Down Expand Up @@ -193,3 +161,78 @@ func proxySettingsParamToProxySettings(cfg params.ProxyConfig) proxy.Settings {
NoProxy: cfg.NoProxy,
}
}

func applicationTag(application string) (names.ApplicationTag, error) {
if !names.IsValidApplication(application) {
return names.ApplicationTag{}, errors.NotValidf("application name %q", application)
}
return names.NewApplicationTag(application), nil
}

func entities(tags ...names.Tag) params.Entities {
entities := params.Entities{
Entities: make([]params.Entity, len(tags)),
}
for i, tag := range tags {
entities.Entities[i].Tag = tag.String()
}
return entities
}

// WatchUnits returns a StringsWatcher that notifies of
// changes to the lifecycles of units of the specified
// CAAS application in the current model.
func (c *Client) WatchUnits(application string) (watcher.StringsWatcher, error) {
applicationTag, err := applicationTag(application)
if err != nil {
return nil, errors.Trace(err)
}
args := entities(applicationTag)

var results params.StringsWatchResults
if err := c.facade.FacadeCall("WatchUnits", args, &results); err != nil {
return nil, err
}
if n := len(results.Results); n != 1 {
return nil, errors.Errorf("expected 1 result, got %d", n)
}
if err := results.Results[0].Error; err != nil {
return nil, errors.Trace(err)
}
w := apiwatcher.NewStringsWatcher(c.facade.RawAPICaller(), results.Results[0])
return w, nil
}

// Life returns the lifecycle state for the specified CAAS application
// or unit in the current model.
func (c *Client) Life(entityName string) (life.Value, error) {
var tag names.Tag
switch {
case names.IsValidApplication(entityName):
tag = names.NewApplicationTag(entityName)
case names.IsValidUnit(entityName):
tag = names.NewUnitTag(entityName)
default:
return "", errors.NotValidf("application or unit name %q", entityName)
}
args := entities(tag)

var results params.LifeResults
if err := c.facade.FacadeCall("Life", args, &results); err != nil {
return "", err
}
if n := len(results.Results); n != 1 {
return "", errors.Errorf("expected 1 result, got %d", n)
}
if err := results.Results[0].Error; err != nil {
return "", maybeNotFound(err)
}
return life.Value(results.Results[0].Life), nil
}

func maybeNotFound(err *params.Error) error {
if !params.IsCodeNotFound(err) {
return err
}
return errors.NewNotFound(err, "")
}
138 changes: 84 additions & 54 deletions api/caasoperator/client_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,12 +9,12 @@ import (
jc "github.com/juju/testing/checkers"
"github.com/juju/utils/proxy"
gc "gopkg.in/check.v1"
"gopkg.in/juju/charm.v6"
names "gopkg.in/juju/names.v2"
"gopkg.in/juju/names.v2"

basetesting "github.com/juju/juju/api/base/testing"
"github.com/juju/juju/api/caasoperator"
"github.com/juju/juju/apiserver/params"
"github.com/juju/juju/core/life"
)

type operatorSuite struct {
Expand Down Expand Up @@ -113,58 +113,6 @@ func (s *operatorSuite) TestCharmInvalidApplicationName(c *gc.C) {
c.Assert(err, gc.ErrorMatches, `application name "" not valid`)
}

func (s *operatorSuite) TestCharmConfig(c *gc.C) {
apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
c.Check(objType, gc.Equals, "CAASOperator")
c.Check(version, gc.Equals, 0)
c.Check(id, gc.Equals, "")
c.Check(request, gc.Equals, "CharmConfig")
c.Check(arg, jc.DeepEquals, params.Entities{
Entities: []params.Entity{{
Tag: "application-gitlab",
}},
})
c.Assert(result, gc.FitsTypeOf, &params.ConfigSettingsResults{})
*(result.(*params.ConfigSettingsResults)) = params.ConfigSettingsResults{
Results: []params.ConfigSettingsResult{{
Settings: params.ConfigSettings{"k": 123},
}},
}
return nil
})

client := caasoperator.NewClient(apiCaller)
settings, err := client.CharmConfig("gitlab")
c.Assert(err, jc.ErrorIsNil)
c.Assert(settings, jc.DeepEquals, charm.Settings{"k": 123})
}

func (s *operatorSuite) TestWatchCharmConfig(c *gc.C) {
apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
c.Check(objType, gc.Equals, "CAASOperator")
c.Check(version, gc.Equals, 0)
c.Check(id, gc.Equals, "")
c.Check(request, gc.Equals, "WatchCharmConfig")
c.Check(arg, jc.DeepEquals, params.Entities{
Entities: []params.Entity{{
Tag: "application-gitlab",
}},
})
c.Assert(result, gc.FitsTypeOf, &params.NotifyWatchResults{})
*(result.(*params.NotifyWatchResults)) = params.NotifyWatchResults{
Results: []params.NotifyWatchResult{{
Error: &params.Error{Message: "FAIL"},
}},
}
return nil
})

client := caasoperator.NewClient(apiCaller)
watcher, err := client.WatchCharmConfig("gitlab")
c.Assert(watcher, gc.IsNil)
c.Assert(err, gc.ErrorMatches, "FAIL")
}

func (s *operatorSuite) TestSetContainerSpec(c *gc.C) {
s.testSetContainerSpec(c, names.NewApplicationTag("gitlab"))
s.testSetContainerSpec(c, names.NewUnitTag("gitlab/0"))
Expand Down Expand Up @@ -269,3 +217,85 @@ func (s *operatorSuite) TestProxySettings(c *gc.C) {
NoProxy: "no.proxy",
})
}

func (s *operatorSuite) TestWatchUnits(c *gc.C) {
apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
c.Check(objType, gc.Equals, "CAASOperator")
c.Check(version, gc.Equals, 0)
c.Check(id, gc.Equals, "")
c.Check(request, gc.Equals, "WatchUnits")
c.Assert(arg, jc.DeepEquals, params.Entities{
Entities: []params.Entity{{
Tag: "application-gitlab",
}},
})
c.Assert(result, gc.FitsTypeOf, &params.StringsWatchResults{})
*(result.(*params.StringsWatchResults)) = params.StringsWatchResults{
Results: []params.StringsWatchResult{{
Error: &params.Error{Message: "FAIL"},
}},
}
return nil
})

client := caasoperator.NewClient(apiCaller)
watcher, err := client.WatchUnits("gitlab")
c.Assert(watcher, gc.IsNil)
c.Assert(err, gc.ErrorMatches, "FAIL")
}

func (s *operatorSuite) TestLife(c *gc.C) {
s.testLife(c, names.NewApplicationTag("gitlab"))
s.testLife(c, names.NewUnitTag("gitlab/0"))
}

func (s *operatorSuite) testLife(c *gc.C, tag names.Tag) {
apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
c.Check(objType, gc.Equals, "CAASOperator")
c.Check(version, gc.Equals, 0)
c.Check(id, gc.Equals, "")
c.Check(request, gc.Equals, "Life")
c.Check(arg, jc.DeepEquals, params.Entities{
Entities: []params.Entity{{
Tag: tag.String(),
}},
})
c.Assert(result, gc.FitsTypeOf, &params.LifeResults{})
*(result.(*params.LifeResults)) = params.LifeResults{
Results: []params.LifeResult{{
Life: params.Alive,
}},
}
return nil
})

client := caasoperator.NewClient(apiCaller)
lifeValue, err := client.Life(tag.Id())
c.Assert(err, jc.ErrorIsNil)
c.Assert(lifeValue, gc.Equals, life.Alive)
}

func (s *operatorSuite) TestLifeError(c *gc.C) {
apiCaller := basetesting.APICallerFunc(func(objType string, version int, id, request string, arg, result interface{}) error {
*(result.(*params.LifeResults)) = params.LifeResults{
Results: []params.LifeResult{{Error: &params.Error{
Code: params.CodeNotFound,
Message: "bletch",
}}},
}
return nil
})

client := caasoperator.NewClient(apiCaller)
_, err := client.Life("gitlab/0")
c.Assert(err, gc.ErrorMatches, "bletch")
c.Assert(err, jc.Satisfies, errors.IsNotFound)
}

func (s *operatorSuite) TestLifeInvalidEntityame(c *gc.C) {
client := caasoperator.NewClient(basetesting.APICallerFunc(func(_ string, _ int, _, _ string, _, _ interface{}) error {
return errors.New("should not be called")
}))
_, err := client.Life("")
c.Assert(err, gc.ErrorMatches, `application or unit name "" not valid`)
}
20 changes: 14 additions & 6 deletions api/uniter/environ.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,29 @@

package uniter

import "github.com/juju/juju/core/model"

// This module implements a subset of the interface provided by
// state.Model, as needed by the uniter API.

// Model represents the state of a model.
type Model struct {
name string
uuid string
name string
uuid string
modelType model.ModelType
}

// UUID returns the universally unique identifier of the model.
func (e Model) UUID() string {
return e.uuid
func (m Model) UUID() string {
return m.uuid
}

// Name returns the human friendly name of the model.
func (e Model) Name() string {
return e.name
func (m Model) Name() string {
return m.name
}

// Type returns the model type.
func (m Model) Type() model.ModelType {
return m.modelType
}
4 changes: 4 additions & 0 deletions api/uniter/environ_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,7 @@ func (s *environSuite) TestUUID(c *gc.C) {
func (s *environSuite) TestName(c *gc.C) {
c.Assert(s.apiEnviron.Name(), gc.Equals, s.stateEnviron.Name())
}

func (s *environSuite) TestType(c *gc.C) {
c.Assert(string(s.apiEnviron.Type().String()), gc.Equals, string(s.stateEnviron.Type()))
}
12 changes: 6 additions & 6 deletions api/uniter/leadership.go
Original file line number Diff line number Diff line change
Expand Up @@ -33,15 +33,14 @@ type LeadershipSettingsAccessor struct {
}

// Merge merges the provided settings into the leadership settings for
// the given service ID. Only leaders of a given service may perform
// the given application and unit. Only leaders of a given service may perform
// this operation.
func (lsa *LeadershipSettingsAccessor) Merge(serviceId string, settings map[string]string) error {
func (lsa *LeadershipSettingsAccessor) Merge(appId, unitId string, settings map[string]string) error {

if err := lsa.checkAPIVersion("Merge"); err != nil {
return errors.Annotatef(err, "cannot access leadership api")
}

results, err := lsa.bulkMerge(lsa.prepareMerge(serviceId, settings))
results, err := lsa.bulkMerge(lsa.prepareMerge(appId, unitId, settings))
if err != nil {
return errors.Annotatef(err, "failed to call leadership api")
}
Expand Down Expand Up @@ -103,9 +102,10 @@ func (lsa *LeadershipSettingsAccessor) WatchLeadershipSettings(serviceId string)
// Prepare functions for building bulk-calls.
//

func (lsa *LeadershipSettingsAccessor) prepareMerge(serviceId string, settings map[string]string) params.MergeLeadershipSettingsParam {
func (lsa *LeadershipSettingsAccessor) prepareMerge(appId, unitId string, settings map[string]string) params.MergeLeadershipSettingsParam {
return params.MergeLeadershipSettingsParam{
ApplicationTag: names.NewApplicationTag(serviceId).String(),
ApplicationTag: names.NewApplicationTag(appId).String(),
UnitTag: names.NewUnitTag(unitId).String(),
Settings: settings,
}
}
Expand Down

0 comments on commit d7d2acc

Please sign in to comment.