launchpad.net/~rogpeppe/juju-core/500-errgo-fix@v0.0.0-20140213181702-000000002356/environs/cloudinit.go (about)

     1  // Copyright 2013 Canonical Ltd.
     2  // Licensed under the AGPLv3, see LICENCE file for details.
     3  
     4  package environs
     5  
     6  import (
     7  	"fmt"
     8  
     9  	"github.com/errgo/errgo"
    10  
    11  	"launchpad.net/juju-core/agent"
    12  	coreCloudinit "launchpad.net/juju-core/cloudinit"
    13  	"launchpad.net/juju-core/constraints"
    14  	"launchpad.net/juju-core/environs/cloudinit"
    15  	"launchpad.net/juju-core/environs/config"
    16  	"launchpad.net/juju-core/juju/osenv"
    17  	"launchpad.net/juju-core/names"
    18  	"launchpad.net/juju-core/state"
    19  	"launchpad.net/juju-core/state/api"
    20  	"launchpad.net/juju-core/utils"
    21  )
    22  
    23  // DataDir is the default data directory.
    24  // Tests can override this where needed, so they don't need to mess with global
    25  // system state.
    26  var DataDir = "/var/lib/juju"
    27  
    28  // LogDir is the default log file path.
    29  const LogDir = "/var/log/juju"
    30  
    31  // CloudInitOutputLog is the default cloud-init-output.log file path.
    32  const CloudInitOutputLog = "/var/log/cloud-init-output.log"
    33  
    34  // RsyslogConfPath is the default rsyslogd conf file path.
    35  const RsyslogConfPath = "/etc/rsyslog.d/25-juju.conf"
    36  
    37  // MongoServiceName is the default Upstart service name for Mongo.
    38  const MongoServiceName = "juju-db"
    39  
    40  // NewMachineConfig sets up a basic machine configuration, for a non-bootstrap
    41  // node.  You'll still need to supply more information, but this takes care of
    42  // the fixed entries and the ones that are always needed.
    43  func NewMachineConfig(machineID, machineNonce string,
    44  	stateInfo *state.Info, apiInfo *api.Info) *cloudinit.MachineConfig {
    45  	return &cloudinit.MachineConfig{
    46  		// Fixed entries.
    47  		DataDir:                 DataDir,
    48  		LogDir:                  LogDir,
    49  		CloudInitOutputLog:      CloudInitOutputLog,
    50  		RsyslogConfPath:         RsyslogConfPath,
    51  		MachineAgentServiceName: "jujud-" + names.MachineTag(machineID),
    52  		MongoServiceName:        MongoServiceName,
    53  
    54  		// Parameter entries.
    55  		MachineId:    machineID,
    56  		MachineNonce: machineNonce,
    57  		StateInfo:    stateInfo,
    58  		APIInfo:      apiInfo,
    59  	}
    60  }
    61  
    62  // NewBootstrapMachineConfig sets up a basic machine configuration for a
    63  // bootstrap node.  You'll still need to supply more information, but this
    64  // takes care of the fixed entries and the ones that are always needed.
    65  // stateInfoURL is the storage URL for the environment's state file.
    66  func NewBootstrapMachineConfig(stateInfoURL string, privateSystemSSHKey string) *cloudinit.MachineConfig {
    67  	// For a bootstrap instance, FinishMachineConfig will provide the
    68  	// state.Info and the api.Info. The machine id must *always* be "0".
    69  	mcfg := NewMachineConfig("0", state.BootstrapNonce, nil, nil)
    70  	mcfg.StateServer = true
    71  	mcfg.StateInfoURL = stateInfoURL
    72  	mcfg.SystemPrivateSSHKey = privateSystemSSHKey
    73  	return mcfg
    74  }
    75  
    76  // PopulateMachineConfig is called both from the FinishMachineConfig below,
    77  // which does have access to the environment config, and from the container
    78  // provisioners, which don't have access to the environment config. Everything
    79  // that is needed to provision a container needs to be returned to the
    80  // provisioner in the ContainerConfig structure. Those values are then used to
    81  // call this function.
    82  func PopulateMachineConfig(mcfg *cloudinit.MachineConfig,
    83  	providerType, authorizedKeys string,
    84  	sslHostnameVerification bool,
    85  	syslogPort int,
    86  	proxy, aptProxy osenv.ProxySettings,
    87  ) error {
    88  	if authorizedKeys == "" {
    89  		return fmt.Errorf("environment configuration has no authorized-keys")
    90  	}
    91  	mcfg.AuthorizedKeys = authorizedKeys
    92  	if mcfg.AgentEnvironment == nil {
    93  		mcfg.AgentEnvironment = make(map[string]string)
    94  	}
    95  	mcfg.AgentEnvironment[agent.ProviderType] = providerType
    96  	mcfg.AgentEnvironment[agent.ContainerType] = string(mcfg.MachineContainerType)
    97  	mcfg.DisableSSLHostnameVerification = !sslHostnameVerification
    98  	mcfg.SyslogPort = syslogPort
    99  	mcfg.ProxySettings = proxy
   100  	mcfg.AptProxySettings = aptProxy
   101  	return nil
   102  }
   103  
   104  // FinishMachineConfig sets fields on a MachineConfig that can be determined by
   105  // inspecting a plain config.Config and the machine constraints at the last
   106  // moment before bootstrapping. It assumes that the supplied Config comes from
   107  // an environment that has passed through all the validation checks in the
   108  // Bootstrap func, and that has set an agent-version (via finding the tools to,
   109  // use for bootstrap, or otherwise).
   110  // TODO(fwereade) This function is not meant to be "good" in any serious way:
   111  // it is better that this functionality be collected in one place here than
   112  // that it be spread out across 3 or 4 providers, but this is its only
   113  // redeeming feature.
   114  func FinishMachineConfig(mcfg *cloudinit.MachineConfig, cfg *config.Config, cons constraints.Value) (err error) {
   115  	defer utils.ErrorContextf(&err, "cannot complete machine configuration")
   116  
   117  	if err := PopulateMachineConfig(
   118  		mcfg,
   119  		cfg.Type(),
   120  		cfg.AuthorizedKeys(),
   121  		cfg.SSLHostnameVerification(),
   122  		cfg.SyslogPort(),
   123  		cfg.ProxySettings(),
   124  		cfg.AptProxySettings(),
   125  	); err != nil {
   126  		return err
   127  	}
   128  
   129  	// The following settings are only appropriate at bootstrap time. At the
   130  	// moment, the only state server is the bootstrap node, but this
   131  	// will probably change.
   132  	if !mcfg.StateServer {
   133  		return nil
   134  	}
   135  	if mcfg.APIInfo != nil || mcfg.StateInfo != nil {
   136  		return fmt.Errorf("machine configuration already has api/state info")
   137  	}
   138  	caCert, hasCACert := cfg.CACert()
   139  	if !hasCACert {
   140  		return fmt.Errorf("environment configuration has no ca-cert")
   141  	}
   142  	password := cfg.AdminSecret()
   143  	if password == "" {
   144  		return fmt.Errorf("environment configuration has no admin-secret")
   145  	}
   146  	passwordHash := utils.UserPasswordHash(password, utils.CompatSalt)
   147  	mcfg.APIInfo = &api.Info{Password: passwordHash, CACert: caCert}
   148  	mcfg.StateInfo = &state.Info{Password: passwordHash, CACert: caCert}
   149  	mcfg.StatePort = cfg.StatePort()
   150  	mcfg.APIPort = cfg.APIPort()
   151  	mcfg.Constraints = cons
   152  	if mcfg.Config, err = BootstrapConfig(cfg); err != nil {
   153  		return err
   154  	}
   155  
   156  	// These really are directly relevant to running a state server.
   157  	cert, key, err := cfg.GenerateStateServerCertAndKey()
   158  	if err != nil {
   159  		return errgo.Annotate(err, "cannot generate state server certificate")
   160  	}
   161  	mcfg.StateServerCert = cert
   162  	mcfg.StateServerKey = key
   163  	return nil
   164  }
   165  
   166  // ComposeUserData puts together a binary (gzipped) blob of user data.
   167  // The additionalScripts are additional command lines that you need cloudinit
   168  // to run on the instance; they are executed before all other cloud-init
   169  // runcmds.  Use with care.
   170  func ComposeUserData(cfg *cloudinit.MachineConfig, additionalScripts ...string) ([]byte, error) {
   171  	cloudcfg := coreCloudinit.New()
   172  	for _, script := range additionalScripts {
   173  		cloudcfg.AddRunCmd(script)
   174  	}
   175  	// When bootstrapping, we only want to apt-get update/upgrade
   176  	// and setup the SSH keys. The rest we leave to cloudinit/sshinit.
   177  	if cfg.StateServer {
   178  		if err := cloudinit.ConfigureBasic(cfg, cloudcfg); err != nil {
   179  			return nil, err
   180  		}
   181  	} else {
   182  		if err := cloudinit.Configure(cfg, cloudcfg); err != nil {
   183  			return nil, err
   184  		}
   185  	}
   186  	data, err := cloudcfg.Render()
   187  	logger.Tracef("Generated cloud init:\n%s", string(data))
   188  	if err != nil {
   189  		return nil, err
   190  	}
   191  	return utils.Gzip(data), nil
   192  }