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 }