github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/apiserver/client/api_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package client_test
     5  
     6  import (
     7  	"fmt"
     8  	stdtesting "testing"
     9  	"time"
    10  
    11  	"github.com/juju/errors"
    12  	"github.com/juju/names"
    13  	jc "github.com/juju/testing/checkers"
    14  	gc "gopkg.in/check.v1"
    15  
    16  	"github.com/juju/juju/api"
    17  	"github.com/juju/juju/constraints"
    18  	"github.com/juju/juju/environs"
    19  	"github.com/juju/juju/environs/config"
    20  	"github.com/juju/juju/instance"
    21  	"github.com/juju/juju/juju/testing"
    22  	"github.com/juju/juju/mongo"
    23  	"github.com/juju/juju/state"
    24  	"github.com/juju/juju/state/multiwatcher"
    25  	coretesting "github.com/juju/juju/testing"
    26  	"github.com/juju/juju/testing/factory"
    27  )
    28  
    29  func TestAll(t *stdtesting.T) {
    30  	coretesting.MgoTestPackage(t)
    31  }
    32  
    33  type baseSuite struct {
    34  	testing.JujuConnSuite
    35  }
    36  
    37  var _ = gc.Suite(&baseSuite{})
    38  
    39  func chanReadEmpty(c *gc.C, ch <-chan struct{}, what string) bool {
    40  	select {
    41  	case _, ok := <-ch:
    42  		return ok
    43  	case <-time.After(10 * time.Second):
    44  		c.Fatalf("timed out reading from %s", what)
    45  	}
    46  	panic("unreachable")
    47  }
    48  
    49  func chanReadStrings(c *gc.C, ch <-chan []string, what string) ([]string, bool) {
    50  	select {
    51  	case changes, ok := <-ch:
    52  		return changes, ok
    53  	case <-time.After(10 * time.Second):
    54  		c.Fatalf("timed out reading from %s", what)
    55  	}
    56  	panic("unreachable")
    57  }
    58  
    59  func chanReadConfig(c *gc.C, ch <-chan *config.Config, what string) (*config.Config, bool) {
    60  	select {
    61  	case envConfig, ok := <-ch:
    62  		return envConfig, ok
    63  	case <-time.After(10 * time.Second):
    64  		c.Fatalf("timed out reading from %s", what)
    65  	}
    66  	panic("unreachable")
    67  }
    68  
    69  func removeServiceAndUnits(c *gc.C, service *state.Service) {
    70  	// Destroy all units for the service.
    71  	units, err := service.AllUnits()
    72  	c.Assert(err, jc.ErrorIsNil)
    73  	for _, unit := range units {
    74  		err = unit.EnsureDead()
    75  		c.Assert(err, jc.ErrorIsNil)
    76  		err = unit.Remove()
    77  		c.Assert(err, jc.ErrorIsNil)
    78  	}
    79  	err = service.Destroy()
    80  	c.Assert(err, jc.ErrorIsNil)
    81  
    82  	err = service.Refresh()
    83  	c.Assert(err, jc.Satisfies, errors.IsNotFound)
    84  }
    85  
    86  // apiAuthenticator represents a simple authenticator object with only the
    87  // SetPassword and Tag methods.  This will fit types from both the state
    88  // and api packages, as those in the api package do not have PasswordValid().
    89  type apiAuthenticator interface {
    90  	state.Entity
    91  	SetPassword(string) error
    92  }
    93  
    94  func setDefaultPassword(c *gc.C, e apiAuthenticator) {
    95  	err := e.SetPassword(defaultPassword(e))
    96  	c.Assert(err, jc.ErrorIsNil)
    97  }
    98  
    99  func defaultPassword(e apiAuthenticator) string {
   100  	return e.Tag().String() + " password-1234567890"
   101  }
   102  
   103  type setStatuser interface {
   104  	SetStatus(status state.Status, info string, data map[string]interface{}) error
   105  }
   106  
   107  func setDefaultStatus(c *gc.C, entity setStatuser) {
   108  	err := entity.SetStatus(state.StatusStarted, "", nil)
   109  	c.Assert(err, jc.ErrorIsNil)
   110  }
   111  
   112  func (s *baseSuite) tryOpenState(c *gc.C, e apiAuthenticator, password string) error {
   113  	stateInfo := s.MongoInfo(c)
   114  	stateInfo.Tag = e.Tag()
   115  	stateInfo.Password = password
   116  	st, err := state.Open(stateInfo, mongo.DialOpts{
   117  		Timeout: 25 * time.Millisecond,
   118  	}, environs.NewStatePolicy())
   119  	if err == nil {
   120  		st.Close()
   121  	}
   122  	return err
   123  }
   124  
   125  // openAs connects to the API state as the given entity
   126  // with the default password for that entity.
   127  func (s *baseSuite) openAs(c *gc.C, tag names.Tag) *api.State {
   128  	info := s.APIInfo(c)
   129  	info.Tag = tag
   130  	// Must match defaultPassword()
   131  	info.Password = fmt.Sprintf("%s password-1234567890", tag)
   132  	// Set this always, so that the login attempts as a machine will
   133  	// not fail with ErrNotProvisioned; it's not used otherwise.
   134  	info.Nonce = "fake_nonce"
   135  	c.Logf("opening state; entity %q; password %q", info.Tag, info.Password)
   136  	st, err := api.Open(info, api.DialOpts{})
   137  	c.Assert(err, jc.ErrorIsNil)
   138  	c.Assert(st, gc.NotNil)
   139  	return st
   140  }
   141  
   142  // scenarioStatus describes the expected state
   143  // of the juju environment set up by setUpScenario.
   144  //
   145  // NOTE: AgentState: "down", AgentStateInfo: "(started)" here is due
   146  // to the scenario not calling SetAgentPresence on the respective entities,
   147  // but this behavior is already tested in cmd/juju/status_test.go and
   148  // also tested live and it works.
   149  var scenarioStatus = &api.Status{
   150  	EnvironmentName: "dummyenv",
   151  	Machines: map[string]api.MachineStatus{
   152  		"0": {
   153  			Id:         "0",
   154  			InstanceId: instance.Id("i-machine-0"),
   155  			Agent: api.AgentStatus{
   156  				Status: "started",
   157  				Data:   make(map[string]interface{}),
   158  			},
   159  			AgentState:     "down",
   160  			AgentStateInfo: "(started)",
   161  			Series:         "quantal",
   162  			Containers:     map[string]api.MachineStatus{},
   163  			Jobs:           []multiwatcher.MachineJob{multiwatcher.JobManageEnviron},
   164  			HasVote:        false,
   165  			WantsVote:      true,
   166  		},
   167  		"1": {
   168  			Id:         "1",
   169  			InstanceId: instance.Id("i-machine-1"),
   170  			Agent: api.AgentStatus{
   171  				Status: "started",
   172  				Data:   make(map[string]interface{}),
   173  			},
   174  			AgentState:     "down",
   175  			AgentStateInfo: "(started)",
   176  			Series:         "quantal",
   177  			Containers:     map[string]api.MachineStatus{},
   178  			Jobs:           []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   179  			HasVote:        false,
   180  			WantsVote:      false,
   181  		},
   182  		"2": {
   183  			Id:         "2",
   184  			InstanceId: instance.Id("i-machine-2"),
   185  			Agent: api.AgentStatus{
   186  				Status: "started",
   187  				Data:   make(map[string]interface{}),
   188  			},
   189  			AgentState:     "down",
   190  			AgentStateInfo: "(started)",
   191  			Series:         "quantal",
   192  			Containers:     map[string]api.MachineStatus{},
   193  			Jobs:           []multiwatcher.MachineJob{multiwatcher.JobHostUnits},
   194  			HasVote:        false,
   195  			WantsVote:      false,
   196  		},
   197  	},
   198  	Services: map[string]api.ServiceStatus{
   199  		"logging": {
   200  			Charm: "local:quantal/logging-1",
   201  			Relations: map[string][]string{
   202  				"logging-directory": {"wordpress"},
   203  			},
   204  			SubordinateTo: []string{"wordpress"},
   205  		},
   206  		"mysql": {
   207  			Charm:         "local:quantal/mysql-1",
   208  			Relations:     map[string][]string{},
   209  			SubordinateTo: []string{},
   210  			Units:         map[string]api.UnitStatus{},
   211  		},
   212  		"wordpress": {
   213  			Charm: "local:quantal/wordpress-3",
   214  			Relations: map[string][]string{
   215  				"logging-dir": {"logging"},
   216  			},
   217  			SubordinateTo: []string{},
   218  			Units: map[string]api.UnitStatus{
   219  				"wordpress/0": {
   220  					Agent: api.AgentStatus{
   221  						Status: "error",
   222  						Info:   "blam",
   223  						Data:   map[string]interface{}{"relation-id": "0"},
   224  					},
   225  					AgentState:     "down",
   226  					AgentStateInfo: "(error: blam)",
   227  					Machine:        "1",
   228  					Subordinates: map[string]api.UnitStatus{
   229  						"logging/0": {
   230  							Agent: api.AgentStatus{
   231  								Status: "allocating",
   232  								Data:   make(map[string]interface{}),
   233  							},
   234  							AgentState: "allocating",
   235  						},
   236  					},
   237  				},
   238  				"wordpress/1": {
   239  					Agent: api.AgentStatus{
   240  						Status: "allocating",
   241  						Data:   make(map[string]interface{}),
   242  					},
   243  					AgentState: "allocating",
   244  					Machine:    "2",
   245  					Subordinates: map[string]api.UnitStatus{
   246  						"logging/1": {
   247  							Agent: api.AgentStatus{
   248  								Status: "allocating",
   249  								Data:   make(map[string]interface{}),
   250  							},
   251  							AgentState: "allocating",
   252  						},
   253  					},
   254  				},
   255  			},
   256  		},
   257  	},
   258  	Relations: []api.RelationStatus{
   259  		{
   260  			Id:  0,
   261  			Key: "logging:logging-directory wordpress:logging-dir",
   262  			Endpoints: []api.EndpointStatus{
   263  				{
   264  					ServiceName: "logging",
   265  					Name:        "logging-directory",
   266  					Role:        "requirer",
   267  					Subordinate: true,
   268  				},
   269  				{
   270  					ServiceName: "wordpress",
   271  					Name:        "logging-dir",
   272  					Role:        "provider",
   273  					Subordinate: false,
   274  				},
   275  			},
   276  			Interface: "logging",
   277  			Scope:     "container",
   278  		},
   279  	},
   280  	Networks: map[string]api.NetworkStatus{},
   281  }
   282  
   283  // setUpScenario makes an environment scenario suitable for
   284  // testing most kinds of access scenario. It returns
   285  // a list of all the entities in the scenario.
   286  //
   287  // When the scenario is initialized, we have:
   288  // user-admin
   289  // user-other
   290  // machine-0
   291  //  instance-id="i-machine-0"
   292  //  nonce="fake_nonce"
   293  //  jobs=manage-environ
   294  //  status=started, info=""
   295  // machine-1
   296  //  instance-id="i-machine-1"
   297  //  nonce="fake_nonce"
   298  //  jobs=host-units
   299  //  status=started, info=""
   300  //  constraints=mem=1G
   301  // machine-2
   302  //  instance-id="i-machine-2"
   303  //  nonce="fake_nonce"
   304  //  jobs=host-units
   305  //  status=started, info=""
   306  // service-wordpress
   307  // service-logging
   308  // unit-wordpress-0
   309  //  deployer-name=machine-1
   310  //  status=down with error and status data attached
   311  // unit-logging-0
   312  //  deployer-name=unit-wordpress-0
   313  // unit-wordpress-1
   314  //     deployer-name=machine-2
   315  // unit-logging-1
   316  //  deployer-name=unit-wordpress-1
   317  //
   318  // The passwords for all returned entities are
   319  // set to the entity name with a " password" suffix.
   320  //
   321  // Note that there is nothing special about machine-0
   322  // here - it's the environment manager in this scenario
   323  // just because machine 0 has traditionally been the
   324  // environment manager (bootstrap machine), so is
   325  // hopefully easier to remember as such.
   326  func (s *baseSuite) setUpScenario(c *gc.C) (entities []names.Tag) {
   327  	add := func(e state.Entity) {
   328  		entities = append(entities, e.Tag())
   329  	}
   330  	u, err := s.State.User(s.AdminUserTag(c))
   331  	c.Assert(err, jc.ErrorIsNil)
   332  	setDefaultPassword(c, u)
   333  	add(u)
   334  
   335  	u = s.Factory.MakeUser(c, &factory.UserParams{Name: "other"})
   336  	setDefaultPassword(c, u)
   337  	add(u)
   338  
   339  	m, err := s.State.AddMachine("quantal", state.JobManageEnviron)
   340  	c.Assert(err, jc.ErrorIsNil)
   341  	c.Assert(m.Tag(), gc.Equals, names.NewMachineTag("0"))
   342  	err = m.SetProvisioned(instance.Id("i-"+m.Tag().String()), "fake_nonce", nil)
   343  	c.Assert(err, jc.ErrorIsNil)
   344  	setDefaultPassword(c, m)
   345  	setDefaultStatus(c, m)
   346  	add(m)
   347  	s.AddTestingService(c, "mysql", s.AddTestingCharm(c, "mysql"))
   348  	wordpress := s.AddTestingService(c, "wordpress", s.AddTestingCharm(c, "wordpress"))
   349  	s.AddTestingService(c, "logging", s.AddTestingCharm(c, "logging"))
   350  	eps, err := s.State.InferEndpoints("logging", "wordpress")
   351  	c.Assert(err, jc.ErrorIsNil)
   352  	rel, err := s.State.AddRelation(eps...)
   353  	c.Assert(err, jc.ErrorIsNil)
   354  
   355  	for i := 0; i < 2; i++ {
   356  		wu, err := wordpress.AddUnit()
   357  		c.Assert(err, jc.ErrorIsNil)
   358  		c.Assert(wu.Tag(), gc.Equals, names.NewUnitTag(fmt.Sprintf("wordpress/%d", i)))
   359  		setDefaultPassword(c, wu)
   360  		add(wu)
   361  
   362  		m, err := s.State.AddMachine("quantal", state.JobHostUnits)
   363  		c.Assert(err, jc.ErrorIsNil)
   364  		c.Assert(m.Tag(), gc.Equals, names.NewMachineTag(fmt.Sprintf("%d", i+1)))
   365  		if i == 1 {
   366  			err = m.SetConstraints(constraints.MustParse("mem=1G"))
   367  			c.Assert(err, jc.ErrorIsNil)
   368  		}
   369  		err = m.SetProvisioned(instance.Id("i-"+m.Tag().String()), "fake_nonce", nil)
   370  		c.Assert(err, jc.ErrorIsNil)
   371  		setDefaultPassword(c, m)
   372  		setDefaultStatus(c, m)
   373  		add(m)
   374  
   375  		err = wu.AssignToMachine(m)
   376  		c.Assert(err, jc.ErrorIsNil)
   377  
   378  		deployer, ok := wu.DeployerTag()
   379  		c.Assert(ok, jc.IsTrue)
   380  		c.Assert(deployer, gc.Equals, names.NewMachineTag(fmt.Sprintf("%d", i+1)))
   381  
   382  		wru, err := rel.Unit(wu)
   383  		c.Assert(err, jc.ErrorIsNil)
   384  
   385  		// Put wordpress/0 in error state (with extra status data set)
   386  		if i == 0 {
   387  			sd := map[string]interface{}{
   388  				"relation-id": "0",
   389  				// these this should get filtered out
   390  				// (not in StatusData whitelist)
   391  				"remote-unit": "logging/0",
   392  				"foo":         "bar",
   393  			}
   394  			wu.SetStatus(state.StatusError, "blam", sd)
   395  		}
   396  
   397  		// Create the subordinate unit as a side-effect of entering
   398  		// scope in the principal's relation-unit.
   399  		err = wru.EnterScope(nil)
   400  		c.Assert(err, jc.ErrorIsNil)
   401  
   402  		lu, err := s.State.Unit(fmt.Sprintf("logging/%d", i))
   403  		c.Assert(err, jc.ErrorIsNil)
   404  		c.Assert(lu.IsPrincipal(), jc.IsFalse)
   405  		deployer, ok = lu.DeployerTag()
   406  		c.Assert(ok, jc.IsTrue)
   407  		c.Assert(deployer, gc.Equals, names.NewUnitTag(fmt.Sprintf("wordpress/%d", i)))
   408  		setDefaultPassword(c, lu)
   409  		add(lu)
   410  	}
   411  	return
   412  }