github.com/mwhudson/juju@v0.0.0-20160512215208-90ff01f3497f/agent/agentbootstrap/bootstrap_test.go (about)

     1  // Copyright 2012, 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package agentbootstrap_test
     5  
     6  import (
     7  	"io/ioutil"
     8  	"net"
     9  	"path/filepath"
    10  
    11  	"github.com/juju/names"
    12  	gitjujutesting "github.com/juju/testing"
    13  	jc "github.com/juju/testing/checkers"
    14  	"github.com/juju/utils"
    15  	"github.com/juju/utils/series"
    16  	gc "gopkg.in/check.v1"
    17  
    18  	"github.com/juju/juju/agent"
    19  	"github.com/juju/juju/agent/agentbootstrap"
    20  	"github.com/juju/juju/apiserver/params"
    21  	"github.com/juju/juju/constraints"
    22  	"github.com/juju/juju/environs"
    23  	"github.com/juju/juju/environs/config"
    24  	"github.com/juju/juju/instance"
    25  	"github.com/juju/juju/mongo"
    26  	"github.com/juju/juju/mongo/mongotest"
    27  	"github.com/juju/juju/network"
    28  	"github.com/juju/juju/provider/dummy"
    29  	"github.com/juju/juju/state"
    30  	"github.com/juju/juju/state/multiwatcher"
    31  	"github.com/juju/juju/testing"
    32  	jujuversion "github.com/juju/juju/version"
    33  )
    34  
    35  type bootstrapSuite struct {
    36  	testing.BaseSuite
    37  	mgoInst gitjujutesting.MgoInstance
    38  }
    39  
    40  var _ = gc.Suite(&bootstrapSuite{})
    41  
    42  func (s *bootstrapSuite) SetUpTest(c *gc.C) {
    43  	s.BaseSuite.SetUpTest(c)
    44  	// Don't use MgoSuite, because we need to ensure
    45  	// we have a fresh mongo for each test case.
    46  	s.mgoInst.EnableAuth = true
    47  	err := s.mgoInst.Start(testing.Certs)
    48  	c.Assert(err, jc.ErrorIsNil)
    49  }
    50  
    51  func (s *bootstrapSuite) TearDownTest(c *gc.C) {
    52  	s.mgoInst.Destroy()
    53  	s.BaseSuite.TearDownTest(c)
    54  }
    55  
    56  func (s *bootstrapSuite) TestInitializeState(c *gc.C) {
    57  	dataDir := c.MkDir()
    58  
    59  	lxcFakeNetConfig := filepath.Join(c.MkDir(), "lxc-net")
    60  	netConf := []byte(`
    61    # comments ignored
    62  LXC_BR= ignored
    63  LXC_ADDR = "fooo"
    64  LXC_BRIDGE="foobar" # detected
    65  anything else ignored
    66  LXC_BRIDGE="ignored"`[1:])
    67  	err := ioutil.WriteFile(lxcFakeNetConfig, netConf, 0644)
    68  	c.Assert(err, jc.ErrorIsNil)
    69  	s.PatchValue(&network.InterfaceByNameAddrs, func(name string) ([]net.Addr, error) {
    70  		c.Assert(name, gc.Equals, "foobar")
    71  		return []net.Addr{
    72  			&net.IPAddr{IP: net.IPv4(10, 0, 3, 1)},
    73  			&net.IPAddr{IP: net.IPv4(10, 0, 3, 4)},
    74  		}, nil
    75  	})
    76  	s.PatchValue(&network.LXCNetDefaultConfig, lxcFakeNetConfig)
    77  
    78  	configParams := agent.AgentConfigParams{
    79  		Paths:             agent.Paths{DataDir: dataDir},
    80  		Tag:               names.NewMachineTag("0"),
    81  		UpgradedToVersion: jujuversion.Current,
    82  		StateAddresses:    []string{s.mgoInst.Addr()},
    83  		CACert:            testing.CACert,
    84  		Password:          testing.DefaultMongoPassword,
    85  		Model:             testing.ModelTag,
    86  	}
    87  	servingInfo := params.StateServingInfo{
    88  		Cert:           testing.ServerCert,
    89  		PrivateKey:     testing.ServerKey,
    90  		CAPrivateKey:   testing.CAKey,
    91  		APIPort:        1234,
    92  		StatePort:      s.mgoInst.Port(),
    93  		SystemIdentity: "def456",
    94  	}
    95  
    96  	cfg, err := agent.NewStateMachineConfig(configParams, servingInfo)
    97  	c.Assert(err, jc.ErrorIsNil)
    98  
    99  	_, available := cfg.StateServingInfo()
   100  	c.Assert(available, jc.IsTrue)
   101  	expectBootstrapConstraints := constraints.MustParse("mem=1024M")
   102  	expectModelConstraints := constraints.MustParse("mem=512M")
   103  	expectHW := instance.MustParseHardware("mem=2048M")
   104  	initialAddrs := network.NewAddresses(
   105  		"zeroonetwothree",
   106  		"0.1.2.3",
   107  		"10.0.3.1", // lxc bridge address filtered.
   108  		"10.0.3.4", // lxc bridge address filtered (-"-).
   109  		"10.0.3.3", // not a lxc bridge address
   110  	)
   111  	mcfg := agentbootstrap.BootstrapMachineConfig{
   112  		Addresses:            initialAddrs,
   113  		BootstrapConstraints: expectBootstrapConstraints,
   114  		ModelConstraints:     expectModelConstraints,
   115  		Jobs:                 []multiwatcher.MachineJob{multiwatcher.JobManageModel},
   116  		InstanceId:           "i-bootstrap",
   117  		Characteristics:      expectHW,
   118  		SharedSecret:         "abc123",
   119  	}
   120  	filteredAddrs := network.NewAddresses(
   121  		"zeroonetwothree",
   122  		"0.1.2.3",
   123  		"10.0.3.3",
   124  	)
   125  
   126  	// Prepare bootstrap config, so we can use it in the state policy.
   127  	provider, err := environs.Provider("dummy")
   128  	c.Assert(err, jc.ErrorIsNil)
   129  	envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{
   130  		"agent-version":  jujuversion.Current.String(),
   131  		"not-for-hosted": "foo",
   132  	})
   133  	envCfg, err := config.New(config.NoDefaults, envAttrs)
   134  	c.Assert(err, jc.ErrorIsNil)
   135  	envCfg, err = provider.BootstrapConfig(environs.BootstrapConfigParams{Config: envCfg})
   136  	c.Assert(err, jc.ErrorIsNil)
   137  	defer dummy.Reset(c)
   138  
   139  	hostedModelUUID := utils.MustNewUUID().String()
   140  	hostedModelConfigAttrs := map[string]interface{}{
   141  		"name": "hosted",
   142  		"uuid": hostedModelUUID,
   143  	}
   144  
   145  	adminUser := names.NewLocalUserTag("agent-admin")
   146  	st, m, err := agentbootstrap.InitializeState(
   147  		adminUser, cfg, envCfg, hostedModelConfigAttrs, mcfg,
   148  		mongotest.DialOpts(), environs.NewStatePolicy(),
   149  	)
   150  	c.Assert(err, jc.ErrorIsNil)
   151  	defer st.Close()
   152  
   153  	err = cfg.Write()
   154  	c.Assert(err, jc.ErrorIsNil)
   155  
   156  	// Check that the environment has been set up.
   157  	env, err := st.Model()
   158  	c.Assert(err, jc.ErrorIsNil)
   159  	c.Assert(env.UUID(), gc.Equals, envCfg.UUID())
   160  
   161  	// Check that initial admin user has been set up correctly.
   162  	modelTag := env.Tag().(names.ModelTag)
   163  	s.assertCanLogInAsAdmin(c, modelTag, testing.DefaultMongoPassword)
   164  	user, err := st.User(env.Owner())
   165  	c.Assert(err, jc.ErrorIsNil)
   166  	c.Assert(user.PasswordValid(testing.DefaultMongoPassword), jc.IsTrue)
   167  
   168  	// Check that controller model configuration has been added, and
   169  	// model constraints set.
   170  	newEnvCfg, err := st.ModelConfig()
   171  	c.Assert(err, jc.ErrorIsNil)
   172  	c.Assert(newEnvCfg.AllAttrs(), gc.DeepEquals, envCfg.AllAttrs())
   173  	gotModelConstraints, err := st.ModelConstraints()
   174  	c.Assert(err, jc.ErrorIsNil)
   175  	c.Assert(gotModelConstraints, gc.DeepEquals, expectModelConstraints)
   176  
   177  	// Check that the hosted model has been added, and model constraints
   178  	// set.
   179  	hostedModelSt, err := st.ForModel(names.NewModelTag(hostedModelUUID))
   180  	c.Assert(err, jc.ErrorIsNil)
   181  	defer hostedModelSt.Close()
   182  	gotModelConstraints, err = hostedModelSt.ModelConstraints()
   183  	c.Assert(err, jc.ErrorIsNil)
   184  	c.Assert(gotModelConstraints, gc.DeepEquals, expectModelConstraints)
   185  	hostedModel, err := hostedModelSt.Model()
   186  	c.Assert(err, jc.ErrorIsNil)
   187  	c.Assert(hostedModel.Name(), gc.Equals, "hosted")
   188  	hostedCfg, err := hostedModelSt.ModelConfig()
   189  	c.Assert(err, jc.ErrorIsNil)
   190  	_, hasUnexpected := hostedCfg.AllAttrs()["not-for-hosted"]
   191  	c.Assert(hasUnexpected, jc.IsFalse)
   192  
   193  	// Check that the bootstrap machine looks correct.
   194  	c.Assert(m.Id(), gc.Equals, "0")
   195  	c.Assert(m.Jobs(), gc.DeepEquals, []state.MachineJob{state.JobManageModel})
   196  	c.Assert(m.Series(), gc.Equals, series.HostSeries())
   197  	c.Assert(m.CheckProvisioned(agent.BootstrapNonce), jc.IsTrue)
   198  	c.Assert(m.Addresses(), jc.DeepEquals, filteredAddrs)
   199  	gotBootstrapConstraints, err := m.Constraints()
   200  	c.Assert(err, jc.ErrorIsNil)
   201  	c.Assert(gotBootstrapConstraints, gc.DeepEquals, expectBootstrapConstraints)
   202  	c.Assert(err, jc.ErrorIsNil)
   203  	gotHW, err := m.HardwareCharacteristics()
   204  	c.Assert(err, jc.ErrorIsNil)
   205  	c.Assert(*gotHW, gc.DeepEquals, expectHW)
   206  
   207  	// Check that the API host ports are initialised correctly.
   208  	apiHostPorts, err := st.APIHostPorts()
   209  	c.Assert(err, jc.ErrorIsNil)
   210  	c.Assert(apiHostPorts, jc.DeepEquals, [][]network.HostPort{
   211  		network.AddressesWithPort(filteredAddrs, 1234),
   212  	})
   213  
   214  	// Check that the state serving info is initialised correctly.
   215  	stateServingInfo, err := st.StateServingInfo()
   216  	c.Assert(err, jc.ErrorIsNil)
   217  	c.Assert(stateServingInfo, jc.DeepEquals, state.StateServingInfo{
   218  		APIPort:        1234,
   219  		StatePort:      s.mgoInst.Port(),
   220  		Cert:           testing.ServerCert,
   221  		PrivateKey:     testing.ServerKey,
   222  		CAPrivateKey:   testing.CAKey,
   223  		SharedSecret:   "abc123",
   224  		SystemIdentity: "def456",
   225  	})
   226  
   227  	// Check that the machine agent's config has been written
   228  	// and that we can use it to connect to the state.
   229  	machine0 := names.NewMachineTag("0")
   230  	newCfg, err := agent.ReadConfig(agent.ConfigPath(dataDir, machine0))
   231  	c.Assert(err, jc.ErrorIsNil)
   232  	c.Assert(newCfg.Tag(), gc.Equals, machine0)
   233  	info, ok := cfg.MongoInfo()
   234  	c.Assert(ok, jc.IsTrue)
   235  	c.Assert(info.Password, gc.Not(gc.Equals), testing.DefaultMongoPassword)
   236  	st1, err := state.Open(newCfg.Model(), info, mongotest.DialOpts(), environs.NewStatePolicy())
   237  	c.Assert(err, jc.ErrorIsNil)
   238  	defer st1.Close()
   239  }
   240  
   241  func (s *bootstrapSuite) TestInitializeStateWithStateServingInfoNotAvailable(c *gc.C) {
   242  	configParams := agent.AgentConfigParams{
   243  		Paths:             agent.Paths{DataDir: c.MkDir()},
   244  		Tag:               names.NewMachineTag("0"),
   245  		UpgradedToVersion: jujuversion.Current,
   246  		StateAddresses:    []string{s.mgoInst.Addr()},
   247  		CACert:            testing.CACert,
   248  		Password:          "fake",
   249  		Model:             testing.ModelTag,
   250  	}
   251  	cfg, err := agent.NewAgentConfig(configParams)
   252  	c.Assert(err, jc.ErrorIsNil)
   253  
   254  	_, available := cfg.StateServingInfo()
   255  	c.Assert(available, jc.IsFalse)
   256  
   257  	adminUser := names.NewLocalUserTag("agent-admin")
   258  	_, _, err = agentbootstrap.InitializeState(adminUser, cfg, nil, nil, agentbootstrap.BootstrapMachineConfig{}, mongotest.DialOpts(), environs.NewStatePolicy())
   259  	// InitializeState will fail attempting to get the api port information
   260  	c.Assert(err, gc.ErrorMatches, "state serving information not available")
   261  }
   262  
   263  func (s *bootstrapSuite) TestInitializeStateFailsSecondTime(c *gc.C) {
   264  	dataDir := c.MkDir()
   265  
   266  	configParams := agent.AgentConfigParams{
   267  		Paths:             agent.Paths{DataDir: dataDir},
   268  		Tag:               names.NewMachineTag("0"),
   269  		UpgradedToVersion: jujuversion.Current,
   270  		StateAddresses:    []string{s.mgoInst.Addr()},
   271  		CACert:            testing.CACert,
   272  		Password:          testing.DefaultMongoPassword,
   273  		Model:             testing.ModelTag,
   274  	}
   275  	cfg, err := agent.NewAgentConfig(configParams)
   276  	c.Assert(err, jc.ErrorIsNil)
   277  	cfg.SetStateServingInfo(params.StateServingInfo{
   278  		APIPort:        5555,
   279  		StatePort:      s.mgoInst.Port(),
   280  		Cert:           "foo",
   281  		PrivateKey:     "bar",
   282  		SharedSecret:   "baz",
   283  		SystemIdentity: "qux",
   284  	})
   285  	expectHW := instance.MustParseHardware("mem=2048M")
   286  	mcfg := agentbootstrap.BootstrapMachineConfig{
   287  		BootstrapConstraints: constraints.MustParse("mem=1024M"),
   288  		Jobs:                 []multiwatcher.MachineJob{multiwatcher.JobManageModel},
   289  		InstanceId:           "i-bootstrap",
   290  		Characteristics:      expectHW,
   291  	}
   292  	envAttrs := dummy.SampleConfig().Delete("admin-secret").Merge(testing.Attrs{
   293  		"agent-version": jujuversion.Current.String(),
   294  	})
   295  	envCfg, err := config.New(config.NoDefaults, envAttrs)
   296  	c.Assert(err, jc.ErrorIsNil)
   297  
   298  	hostedModelConfigAttrs := map[string]interface{}{
   299  		"name": "hosted",
   300  		"uuid": utils.MustNewUUID().String(),
   301  	}
   302  
   303  	adminUser := names.NewLocalUserTag("agent-admin")
   304  	st, _, err := agentbootstrap.InitializeState(
   305  		adminUser, cfg, envCfg, hostedModelConfigAttrs, mcfg,
   306  		mongotest.DialOpts(), state.Policy(nil),
   307  	)
   308  	c.Assert(err, jc.ErrorIsNil)
   309  	st.Close()
   310  
   311  	st, _, err = agentbootstrap.InitializeState(adminUser, cfg, envCfg, nil, mcfg, mongotest.DialOpts(), environs.NewStatePolicy())
   312  	if err == nil {
   313  		st.Close()
   314  	}
   315  	c.Assert(err, gc.ErrorMatches, "failed to initialize mongo admin user: cannot set admin password: not authorized .*")
   316  }
   317  
   318  func (s *bootstrapSuite) TestMachineJobFromParams(c *gc.C) {
   319  	var tests = []struct {
   320  		name multiwatcher.MachineJob
   321  		want state.MachineJob
   322  		err  string
   323  	}{{
   324  		name: multiwatcher.JobHostUnits,
   325  		want: state.JobHostUnits,
   326  	}, {
   327  		name: multiwatcher.JobManageModel,
   328  		want: state.JobManageModel,
   329  	}, {
   330  		name: multiwatcher.JobManageNetworking,
   331  		want: state.JobManageNetworking,
   332  	}, {
   333  		name: "invalid",
   334  		want: -1,
   335  		err:  `invalid machine job "invalid"`,
   336  	}}
   337  	for _, test := range tests {
   338  		got, err := agentbootstrap.MachineJobFromParams(test.name)
   339  		if err != nil {
   340  			c.Check(err, gc.ErrorMatches, test.err)
   341  		}
   342  		c.Check(got, gc.Equals, test.want)
   343  	}
   344  }
   345  
   346  func (s *bootstrapSuite) assertCanLogInAsAdmin(c *gc.C, modelTag names.ModelTag, password string) {
   347  	info := &mongo.MongoInfo{
   348  		Info: mongo.Info{
   349  			Addrs:  []string{s.mgoInst.Addr()},
   350  			CACert: testing.CACert,
   351  		},
   352  		Tag:      nil, // admin user
   353  		Password: password,
   354  	}
   355  	st, err := state.Open(modelTag, info, mongotest.DialOpts(), environs.NewStatePolicy())
   356  	c.Assert(err, jc.ErrorIsNil)
   357  	defer st.Close()
   358  	_, err = st.Machine("0")
   359  	c.Assert(err, jc.ErrorIsNil)
   360  }