github.com/rogpeppe/juju@v0.0.0-20140613142852-6337964b789e/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  	"time"
     8  
     9  	jc "github.com/juju/testing/checkers"
    10  	"github.com/juju/utils"
    11  	gc "launchpad.net/gocheck"
    12  	"launchpad.net/goyaml"
    13  
    14  	"github.com/juju/juju/agent"
    15  	"github.com/juju/juju/cert"
    16  	coreCloudinit "github.com/juju/juju/cloudinit"
    17  	"github.com/juju/juju/constraints"
    18  	"github.com/juju/juju/environs"
    19  	"github.com/juju/juju/environs/cloudinit"
    20  	"github.com/juju/juju/environs/config"
    21  	"github.com/juju/juju/juju/osenv"
    22  	"github.com/juju/juju/mongo"
    23  	"github.com/juju/juju/provider/dummy"
    24  	"github.com/juju/juju/state"
    25  	"github.com/juju/juju/state/api"
    26  	"github.com/juju/juju/state/api/params"
    27  	"github.com/juju/juju/testing"
    28  	"github.com/juju/juju/tools"
    29  	"github.com/juju/juju/version"
    30  )
    31  
    32  // dummySampleConfig returns the dummy sample config without
    33  // the state server configured.
    34  // will not run a state server.
    35  func dummySampleConfig() testing.Attrs {
    36  	return dummy.SampleConfig().Merge(testing.Attrs{
    37  		"state-server": false,
    38  	})
    39  }
    40  
    41  type CloudInitSuite struct {
    42  	testing.BaseSuite
    43  }
    44  
    45  var _ = gc.Suite(&CloudInitSuite{})
    46  
    47  func (s *CloudInitSuite) TestFinishInstanceConfig(c *gc.C) {
    48  	attrs := dummySampleConfig().Merge(testing.Attrs{
    49  		"authorized-keys": "we-are-the-keys",
    50  	})
    51  	cfg, err := config.New(config.NoDefaults, attrs)
    52  	c.Assert(err, gc.IsNil)
    53  	mcfg := &cloudinit.MachineConfig{
    54  		StateInfo: &state.Info{Tag: "not touched"},
    55  		APIInfo:   &api.Info{Tag: "not touched"},
    56  	}
    57  	err = environs.FinishMachineConfig(mcfg, cfg, constraints.Value{})
    58  	c.Assert(err, gc.IsNil)
    59  	c.Assert(mcfg, gc.DeepEquals, &cloudinit.MachineConfig{
    60  		AuthorizedKeys: "we-are-the-keys",
    61  		AgentEnvironment: map[string]string{
    62  			agent.ProviderType:  "dummy",
    63  			agent.ContainerType: "",
    64  		},
    65  		StateInfo: &state.Info{Tag: "not touched"},
    66  		APIInfo:   &api.Info{Tag: "not touched"},
    67  		DisableSSLHostnameVerification: false,
    68  	})
    69  }
    70  
    71  func (s *CloudInitSuite) TestFinishMachineConfigNonDefault(c *gc.C) {
    72  	attrs := dummySampleConfig().Merge(testing.Attrs{
    73  		"authorized-keys":           "we-are-the-keys",
    74  		"ssl-hostname-verification": false,
    75  	})
    76  	cfg, err := config.New(config.NoDefaults, attrs)
    77  	c.Assert(err, gc.IsNil)
    78  	mcfg := &cloudinit.MachineConfig{
    79  		StateInfo: &state.Info{Tag: "not touched"},
    80  		APIInfo:   &api.Info{Tag: "not touched"},
    81  	}
    82  	err = environs.FinishMachineConfig(mcfg, cfg, constraints.Value{})
    83  	c.Assert(err, gc.IsNil)
    84  	c.Assert(mcfg, gc.DeepEquals, &cloudinit.MachineConfig{
    85  		AuthorizedKeys: "we-are-the-keys",
    86  		AgentEnvironment: map[string]string{
    87  			agent.ProviderType:  "dummy",
    88  			agent.ContainerType: "",
    89  		},
    90  		StateInfo: &state.Info{Tag: "not touched"},
    91  		APIInfo:   &api.Info{Tag: "not touched"},
    92  		DisableSSLHostnameVerification: true,
    93  	})
    94  }
    95  
    96  func (s *CloudInitSuite) TestFinishBootstrapConfig(c *gc.C) {
    97  	attrs := dummySampleConfig().Merge(testing.Attrs{
    98  		"authorized-keys": "we-are-the-keys",
    99  		"admin-secret":    "lisboan-pork",
   100  		"agent-version":   "1.2.3",
   101  		"state-server":    false,
   102  	})
   103  	cfg, err := config.New(config.NoDefaults, attrs)
   104  	c.Assert(err, gc.IsNil)
   105  	oldAttrs := cfg.AllAttrs()
   106  	mcfg := &cloudinit.MachineConfig{
   107  		Bootstrap: true,
   108  	}
   109  	cons := constraints.MustParse("mem=1T cpu-power=999999999")
   110  	err = environs.FinishMachineConfig(mcfg, cfg, cons)
   111  	c.Assert(err, gc.IsNil)
   112  	c.Check(mcfg.AuthorizedKeys, gc.Equals, "we-are-the-keys")
   113  	c.Check(mcfg.DisableSSLHostnameVerification, jc.IsFalse)
   114  	password := utils.UserPasswordHash("lisboan-pork", utils.CompatSalt)
   115  	c.Check(mcfg.APIInfo, gc.DeepEquals, &api.Info{
   116  		Password: password, CACert: testing.CACert,
   117  	})
   118  	c.Check(mcfg.StateInfo, gc.DeepEquals, &state.Info{
   119  		Password: password, Info: mongo.Info{CACert: testing.CACert},
   120  	})
   121  	c.Check(mcfg.StateServingInfo.StatePort, gc.Equals, cfg.StatePort())
   122  	c.Check(mcfg.StateServingInfo.APIPort, gc.Equals, cfg.APIPort())
   123  	c.Check(mcfg.Constraints, gc.DeepEquals, cons)
   124  
   125  	oldAttrs["ca-private-key"] = ""
   126  	oldAttrs["admin-secret"] = ""
   127  	c.Check(mcfg.Config.AllAttrs(), gc.DeepEquals, oldAttrs)
   128  	srvCertPEM := mcfg.StateServingInfo.Cert
   129  	srvKeyPEM := mcfg.StateServingInfo.PrivateKey
   130  	_, _, err = cert.ParseCertAndKey(srvCertPEM, srvKeyPEM)
   131  	c.Check(err, gc.IsNil)
   132  
   133  	err = cert.Verify(srvCertPEM, testing.CACert, time.Now())
   134  	c.Assert(err, gc.IsNil)
   135  	err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(9, 0, 0))
   136  	c.Assert(err, gc.IsNil)
   137  	err = cert.Verify(srvCertPEM, testing.CACert, time.Now().AddDate(10, 0, 1))
   138  	c.Assert(err, gc.NotNil)
   139  }
   140  
   141  func (s *CloudInitSuite) TestUserData(c *gc.C) {
   142  	s.testUserData(c, false)
   143  }
   144  
   145  func (s *CloudInitSuite) TestStateServerUserData(c *gc.C) {
   146  	s.testUserData(c, true)
   147  }
   148  
   149  func (*CloudInitSuite) testUserData(c *gc.C, bootstrap bool) {
   150  	testJujuHome := c.MkDir()
   151  	defer osenv.SetJujuHome(osenv.SetJujuHome(testJujuHome))
   152  	tools := &tools.Tools{
   153  		URL:     "http://foo.com/tools/releases/juju1.2.3-linux-amd64.tgz",
   154  		Version: version.MustParseBinary("1.2.3-linux-amd64"),
   155  	}
   156  	envConfig, err := config.New(config.NoDefaults, dummySampleConfig())
   157  	c.Assert(err, gc.IsNil)
   158  
   159  	allJobs := []params.MachineJob{
   160  		params.JobManageEnviron,
   161  		params.JobHostUnits,
   162  	}
   163  	cfg := &cloudinit.MachineConfig{
   164  		MachineId:    "10",
   165  		MachineNonce: "5432",
   166  		Tools:        tools,
   167  		StateInfo: &state.Info{
   168  			Info: mongo.Info{
   169  				Addrs:  []string{"127.0.0.1:1234"},
   170  				CACert: "CA CERT\n" + testing.CACert,
   171  			},
   172  			Password: "pw1",
   173  			Tag:      "machine-10",
   174  		},
   175  		APIInfo: &api.Info{
   176  			Addrs:    []string{"127.0.0.1:1234"},
   177  			Password: "pw2",
   178  			CACert:   "CA CERT\n" + testing.CACert,
   179  			Tag:      "machine-10",
   180  		},
   181  		DataDir:                 environs.DataDir,
   182  		LogDir:                  agent.DefaultLogDir,
   183  		Jobs:                    allJobs,
   184  		CloudInitOutputLog:      environs.CloudInitOutputLog,
   185  		Config:                  envConfig,
   186  		AgentEnvironment:        map[string]string{agent.ProviderType: "dummy"},
   187  		AuthorizedKeys:          "wheredidileavemykeys",
   188  		MachineAgentServiceName: "jujud-machine-10",
   189  	}
   190  	if bootstrap {
   191  		cfg.Bootstrap = true
   192  		cfg.StateServingInfo = &params.StateServingInfo{
   193  			StatePort:  envConfig.StatePort(),
   194  			APIPort:    envConfig.APIPort(),
   195  			Cert:       testing.ServerCert,
   196  			PrivateKey: testing.ServerKey,
   197  		}
   198  	}
   199  	script1 := "script1"
   200  	script2 := "script2"
   201  	cloudcfg := coreCloudinit.New()
   202  	cloudcfg.AddRunCmd(script1)
   203  	cloudcfg.AddRunCmd(script2)
   204  	result, err := environs.ComposeUserData(cfg, cloudcfg)
   205  	c.Assert(err, gc.IsNil)
   206  
   207  	unzipped, err := utils.Gunzip(result)
   208  	c.Assert(err, gc.IsNil)
   209  
   210  	config := make(map[interface{}]interface{})
   211  	err = goyaml.Unmarshal(unzipped, &config)
   212  	c.Assert(err, gc.IsNil)
   213  
   214  	// The scripts given to userData where added as the first
   215  	// commands to be run.
   216  	runCmd := config["runcmd"].([]interface{})
   217  	c.Check(runCmd[0], gc.Equals, script1)
   218  	c.Check(runCmd[1], gc.Equals, script2)
   219  
   220  	if bootstrap {
   221  		// The cloudinit config should have nothing but the basics:
   222  		// SSH authorized keys, the additional runcmds, and log output.
   223  		//
   224  		// Note: the additional runcmds *do* belong here, at least
   225  		// for MAAS. MAAS needs to configure and then bounce the
   226  		// network interfaces, which would sever the SSH connection
   227  		// in the synchronous bootstrap phase.
   228  		c.Check(config, gc.DeepEquals, map[interface{}]interface{}{
   229  			"output": map[interface{}]interface{}{
   230  				"all": "| tee -a /var/log/cloud-init-output.log",
   231  			},
   232  			"runcmd": []interface{}{
   233  				"script1", "script2",
   234  				"set -xe",
   235  				"install -D -m 644 /dev/null '/var/lib/juju/nonce.txt'",
   236  				"printf '%s\\n' '5432' > '/var/lib/juju/nonce.txt'",
   237  			},
   238  			"ssh_authorized_keys": []interface{}{"wheredidileavemykeys"},
   239  		})
   240  	} else {
   241  		// Just check that the cloudinit config looks good,
   242  		// and that there are more runcmds than the additional
   243  		// ones we passed into ComposeUserData.
   244  		c.Check(config["apt_upgrade"], gc.Equals, true)
   245  		c.Check(len(runCmd) > 2, jc.IsTrue)
   246  	}
   247  }