github.com/Pankov404/juju@v0.0.0-20150703034450-be266991dceb/agent/bootstrap_test.go (about)

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