github.com/altoros/juju-vmware@v0.0.0-20150312064031-f19ae857ccca/environs/cloudinit_test.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package environs_test
     5  
     6  import (
     7  	"path"
     8  	"time"
     9  
    10  	"github.com/juju/names"
    11  	jc "github.com/juju/testing/checkers"
    12  	"github.com/juju/utils"
    13  	gc "gopkg.in/check.v1"
    14  	goyaml "gopkg.in/yaml.v1"
    15  
    16  	"github.com/juju/juju/agent"
    17  	"github.com/juju/juju/api"
    18  	"github.com/juju/juju/apiserver/params"
    19  	"github.com/juju/juju/cert"
    20  	coreCloudinit "github.com/juju/juju/cloudinit"
    21  	"github.com/juju/juju/environs"
    22  	"github.com/juju/juju/environs/cloudinit"
    23  	"github.com/juju/juju/environs/config"
    24  	"github.com/juju/juju/juju/osenv"
    25  	"github.com/juju/juju/juju/paths"
    26  	"github.com/juju/juju/mongo"
    27  	"github.com/juju/juju/provider/dummy"
    28  	"github.com/juju/juju/state/multiwatcher"
    29  	"github.com/juju/juju/testing"
    30  	"github.com/juju/juju/tools"
    31  	"github.com/juju/juju/version"
    32  )
    33  
    34  // dummySampleConfig returns the dummy sample config without
    35  // the state server configured.
    36  // will not run a state server.
    37  func dummySampleConfig() testing.Attrs {
    38  	return dummy.SampleConfig().Merge(testing.Attrs{
    39  		"state-server": false,
    40  	})
    41  }
    42  
    43  type CloudInitSuite struct {
    44  	testing.BaseSuite
    45  }
    46  
    47  var _ = gc.Suite(&CloudInitSuite{})
    48  
    49  func must(s string, err error) string {
    50  	if err != nil {
    51  		panic(err)
    52  	}
    53  	return s
    54  }
    55  
    56  var logDir = must(paths.LogDir("precise"))
    57  var cloudInitOutputLog = path.Join(logDir, "cloud-init-output.log")
    58  
    59  func (s *CloudInitSuite) TestFinishInstanceConfig(c *gc.C) {
    60  
    61  	userTag := names.NewLocalUserTag("not-touched")
    62  
    63  	expectedMcfg := &cloudinit.MachineConfig{
    64  		AuthorizedKeys: "we-are-the-keys",
    65  		AgentEnvironment: map[string]string{
    66  			agent.ProviderType:  "dummy",
    67  			agent.ContainerType: "",
    68  		},
    69  		MongoInfo: &mongo.MongoInfo{Tag: userTag},
    70  		APIInfo:   &api.Info{Tag: userTag},
    71  		DisableSSLHostnameVerification: false,
    72  		PreferIPv6:                     true,
    73  		EnableOSRefreshUpdate:          true,
    74  		EnableOSUpgrade:                true,
    75  	}
    76  
    77  	cfg, err := config.New(config.NoDefaults, dummySampleConfig().Merge(testing.Attrs{
    78  		"authorized-keys": "we-are-the-keys",
    79  	}))
    80  	c.Assert(err, jc.ErrorIsNil)
    81  
    82  	mcfg := &cloudinit.MachineConfig{
    83  		MongoInfo: &mongo.MongoInfo{Tag: userTag},
    84  		APIInfo:   &api.Info{Tag: userTag},
    85  	}
    86  	err = environs.FinishMachineConfig(mcfg, cfg)
    87  
    88  	c.Assert(err, jc.ErrorIsNil)
    89  	c.Assert(mcfg, jc.DeepEquals, expectedMcfg)
    90  
    91  	// Test when updates/upgrades are set to false.
    92  	cfg, err = config.New(config.NoDefaults, dummySampleConfig().Merge(testing.Attrs{
    93  		"authorized-keys":          "we-are-the-keys",
    94  		"enable-os-refresh-update": false,
    95  		"enable-os-upgrade":        false,
    96  	}))
    97  	c.Assert(err, jc.ErrorIsNil)
    98  	err = environs.FinishMachineConfig(mcfg, cfg)
    99  	c.Assert(err, jc.ErrorIsNil)
   100  	expectedMcfg.EnableOSRefreshUpdate = false
   101  	expectedMcfg.EnableOSUpgrade = false
   102  	c.Assert(mcfg, jc.DeepEquals, expectedMcfg)
   103  }
   104  
   105  func (s *CloudInitSuite) TestFinishMachineConfigNonDefault(c *gc.C) {
   106  	userTag := names.NewLocalUserTag("not-touched")
   107  	attrs := dummySampleConfig().Merge(testing.Attrs{
   108  		"authorized-keys":           "we-are-the-keys",
   109  		"ssl-hostname-verification": false,
   110  	})
   111  	cfg, err := config.New(config.NoDefaults, attrs)
   112  	c.Assert(err, jc.ErrorIsNil)
   113  	mcfg := &cloudinit.MachineConfig{
   114  		MongoInfo: &mongo.MongoInfo{Tag: userTag},
   115  		APIInfo:   &api.Info{Tag: userTag},
   116  	}
   117  	err = environs.FinishMachineConfig(mcfg, cfg)
   118  	c.Assert(err, jc.ErrorIsNil)
   119  	c.Assert(mcfg, jc.DeepEquals, &cloudinit.MachineConfig{
   120  		AuthorizedKeys: "we-are-the-keys",
   121  		AgentEnvironment: map[string]string{
   122  			agent.ProviderType:  "dummy",
   123  			agent.ContainerType: "",
   124  		},
   125  		MongoInfo: &mongo.MongoInfo{Tag: userTag},
   126  		APIInfo:   &api.Info{Tag: userTag},
   127  		DisableSSLHostnameVerification: true,
   128  		PreferIPv6:                     true,
   129  		EnableOSRefreshUpdate:          true,
   130  		EnableOSUpgrade:                true,
   131  	})
   132  }
   133  
   134  func (s *CloudInitSuite) TestFinishBootstrapConfig(c *gc.C) {
   135  	attrs := dummySampleConfig().Merge(testing.Attrs{
   136  		"authorized-keys": "we-are-the-keys",
   137  		"admin-secret":    "lisboan-pork",
   138  		"agent-version":   "1.2.3",
   139  		"state-server":    false,
   140  	})
   141  	cfg, err := config.New(config.NoDefaults, attrs)
   142  	c.Assert(err, jc.ErrorIsNil)
   143  	oldAttrs := cfg.AllAttrs()
   144  	mcfg := &cloudinit.MachineConfig{
   145  		Bootstrap: true,
   146  	}
   147  	err = environs.FinishMachineConfig(mcfg, cfg)
   148  	c.Assert(err, jc.ErrorIsNil)
   149  	c.Check(mcfg.AuthorizedKeys, gc.Equals, "we-are-the-keys")
   150  	c.Check(mcfg.DisableSSLHostnameVerification, jc.IsFalse)
   151  	password := utils.UserPasswordHash("lisboan-pork", utils.CompatSalt)
   152  	c.Check(mcfg.APIInfo, gc.DeepEquals, &api.Info{
   153  		Password: password, CACert: testing.CACert,
   154  		EnvironTag: testing.EnvironmentTag,
   155  	})
   156  	c.Check(mcfg.MongoInfo, gc.DeepEquals, &mongo.MongoInfo{
   157  		Password: password, Info: mongo.Info{CACert: testing.CACert},
   158  	})
   159  	c.Check(mcfg.StateServingInfo.StatePort, gc.Equals, cfg.StatePort())
   160  	c.Check(mcfg.StateServingInfo.APIPort, gc.Equals, cfg.APIPort())
   161  	c.Check(mcfg.StateServingInfo.CAPrivateKey, gc.Equals, oldAttrs["ca-private-key"])
   162  
   163  	oldAttrs["ca-private-key"] = ""
   164  	oldAttrs["admin-secret"] = ""
   165  	c.Check(mcfg.Config.AllAttrs(), gc.DeepEquals, oldAttrs)
   166  	srvCertPEM := mcfg.StateServingInfo.Cert
   167  	srvKeyPEM := mcfg.StateServingInfo.PrivateKey
   168  	_, _, err = cert.ParseCertAndKey(srvCertPEM, srvKeyPEM)
   169  	c.Check(err, jc.ErrorIsNil)
   170  
   171  	err = cert.Verify(srvCertPEM, testing.CACert, time.Now())
   172  	c.Assert(err, jc.ErrorIsNil)
   173  	err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(9, 0, 0))
   174  	c.Assert(err, jc.ErrorIsNil)
   175  	err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(10, 0, 1))
   176  	c.Assert(err, gc.NotNil)
   177  }
   178  
   179  func (s *CloudInitSuite) TestUserData(c *gc.C) {
   180  	s.testUserData(c, false)
   181  }
   182  
   183  func (s *CloudInitSuite) TestStateServerUserData(c *gc.C) {
   184  	s.testUserData(c, true)
   185  }
   186  
   187  func (*CloudInitSuite) testUserData(c *gc.C, bootstrap bool) {
   188  	testJujuHome := c.MkDir()
   189  	defer osenv.SetJujuHome(osenv.SetJujuHome(testJujuHome))
   190  	// Use actual series paths instead of local defaults
   191  	logDir := must(paths.LogDir("quantal"))
   192  	dataDir := must(paths.DataDir("quantal"))
   193  	tools := &tools.Tools{
   194  		URL:     "http://foo.com/tools/released/juju1.2.3-quantal-amd64.tgz",
   195  		Version: version.MustParseBinary("1.2.3-quantal-amd64"),
   196  	}
   197  	envConfig, err := config.New(config.NoDefaults, dummySampleConfig())
   198  	c.Assert(err, jc.ErrorIsNil)
   199  
   200  	allJobs := []multiwatcher.MachineJob{
   201  		multiwatcher.JobManageEnviron,
   202  		multiwatcher.JobHostUnits,
   203  		multiwatcher.JobManageNetworking,
   204  	}
   205  	cfg := &cloudinit.MachineConfig{
   206  		MachineId:    "10",
   207  		MachineNonce: "5432",
   208  		Tools:        tools,
   209  		Series:       "quantal",
   210  		MongoInfo: &mongo.MongoInfo{
   211  			Info: mongo.Info{
   212  				Addrs:  []string{"127.0.0.1:1234"},
   213  				CACert: "CA CERT\n" + testing.CACert,
   214  			},
   215  			Password: "pw1",
   216  			Tag:      names.NewMachineTag("10"),
   217  		},
   218  		APIInfo: &api.Info{
   219  			Addrs:      []string{"127.0.0.1:1234"},
   220  			Password:   "pw2",
   221  			CACert:     "CA CERT\n" + testing.CACert,
   222  			Tag:        names.NewMachineTag("10"),
   223  			EnvironTag: testing.EnvironmentTag,
   224  		},
   225  		DataDir:                 dataDir,
   226  		LogDir:                  path.Join(logDir, "juju"),
   227  		Jobs:                    allJobs,
   228  		CloudInitOutputLog:      cloudInitOutputLog,
   229  		Config:                  envConfig,
   230  		AgentEnvironment:        map[string]string{agent.ProviderType: "dummy"},
   231  		AuthorizedKeys:          "wheredidileavemykeys",
   232  		MachineAgentServiceName: "jujud-machine-10",
   233  		EnableOSUpgrade:         true,
   234  	}
   235  	if bootstrap {
   236  		cfg.Bootstrap = true
   237  		cfg.StateServingInfo = &params.StateServingInfo{
   238  			StatePort:    envConfig.StatePort(),
   239  			APIPort:      envConfig.APIPort(),
   240  			Cert:         testing.ServerCert,
   241  			PrivateKey:   testing.ServerKey,
   242  			CAPrivateKey: testing.CAKey,
   243  		}
   244  	}
   245  	script1 := "script1"
   246  	script2 := "script2"
   247  	cloudcfg := coreCloudinit.New()
   248  	cloudcfg.AddRunCmd(script1)
   249  	cloudcfg.AddRunCmd(script2)
   250  	result, err := environs.ComposeUserData(cfg, cloudcfg)
   251  	c.Assert(err, jc.ErrorIsNil)
   252  
   253  	unzipped, err := utils.Gunzip(result)
   254  	c.Assert(err, jc.ErrorIsNil)
   255  
   256  	config := make(map[interface{}]interface{})
   257  	err = goyaml.Unmarshal(unzipped, &config)
   258  	c.Assert(err, jc.ErrorIsNil)
   259  
   260  	// The scripts given to userData where added as the first
   261  	// commands to be run.
   262  	runCmd := config["runcmd"].([]interface{})
   263  	c.Check(runCmd[0], gc.Equals, script1)
   264  	c.Check(runCmd[1], gc.Equals, script2)
   265  
   266  	if bootstrap {
   267  		// The cloudinit config should have nothing but the basics:
   268  		// SSH authorized keys, the additional runcmds, and log output.
   269  		//
   270  		// Note: the additional runcmds *do* belong here, at least
   271  		// for MAAS. MAAS needs to configure and then bounce the
   272  		// network interfaces, which would sever the SSH connection
   273  		// in the synchronous bootstrap phase.
   274  		c.Check(config, gc.DeepEquals, map[interface{}]interface{}{
   275  			"output": map[interface{}]interface{}{
   276  				"all": "| tee -a /var/log/cloud-init-output.log",
   277  			},
   278  			"runcmd": []interface{}{
   279  				"script1", "script2",
   280  				"set -xe",
   281  				"install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'",
   282  				"printf '%s\\n' '5432' > '/var/lib/juju/nonce.txt'",
   283  			},
   284  			"ssh_authorized_keys": []interface{}{"wheredidileavemykeys"},
   285  		})
   286  	} else {
   287  		// Just check that the cloudinit config looks good,
   288  		// and that there are more runcmds than the additional
   289  		// ones we passed into ComposeUserData.
   290  		c.Check(config["apt_upgrade"], jc.IsTrue)
   291  		c.Check(len(runCmd) > 2, jc.IsTrue)
   292  	}
   293  }