This repository has been archived by the owner on Jun 15, 2019. It is now read-only.
/
cloudinit.go
132 lines (120 loc) · 4.68 KB
/
cloudinit.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
// Copyright 2013 Canonical Ltd.
// Licensed under the AGPLv3, see LICENCE file for details.
package environs
import (
"fmt"
coreCloudinit "launchpad.net/juju-core/cloudinit"
"launchpad.net/juju-core/constraints"
"launchpad.net/juju-core/environs/cloudinit"
"launchpad.net/juju-core/environs/config"
"launchpad.net/juju-core/juju/osenv"
"launchpad.net/juju-core/state"
"launchpad.net/juju-core/state/api"
"launchpad.net/juju-core/utils"
)
// Default data directory.
// Tests can override this where needed, so they don't need to mess with global
// system state.
var DataDir = "/var/lib/juju"
// NewMachineConfig sets up a basic machine configuration, for a non-bootstrap
// node. You'll still need to supply more information, but this takes care of
// the fixed entries and the ones that are always needed.
func NewMachineConfig(machineID, machineNonce string,
stateInfo *state.Info, apiInfo *api.Info) *cloudinit.MachineConfig {
return &cloudinit.MachineConfig{
// Fixed entries.
DataDir: DataDir,
// Parameter entries.
MachineId: machineID,
MachineNonce: machineNonce,
StateInfo: stateInfo,
APIInfo: apiInfo,
}
}
// NewBootstrapMachineConfig sets up a basic machine configuration for a
// bootstrap node. You'll still need to supply more information, but this
// takes care of the fixed entries and the ones that are always needed.
// stateInfoURL is the storage URL for the environment's state file.
func NewBootstrapMachineConfig(machineID, stateInfoURL string) *cloudinit.MachineConfig {
// For a bootstrap instance, FinishMachineConfig will provide the
// state.Info and the api.Info.
mcfg := NewMachineConfig(machineID, state.BootstrapNonce, nil, nil)
mcfg.StateServer = true
mcfg.StateInfoURL = stateInfoURL
return mcfg
}
// FinishMachineConfig sets fields on a MachineConfig that can be determined by
// inspecting a plain config.Config and the machine constraints at the last
// moment before bootstrapping. It assumes that the supplied Config comes from
// an environment that has passed through all the validation checks in the
// Bootstrap func, and that has set an agent-version (via FindBootstrapTools,
// or otherwise).
// TODO(fwereade) This function is not meant to be "good" in any serious way:
// it is better that this functionality be collected in one place here than
// that it be spread out across 3 or 4 providers, but this is its only
// redeeming feature.
func FinishMachineConfig(mcfg *cloudinit.MachineConfig, cfg *config.Config, cons constraints.Value) (err error) {
defer utils.ErrorContextf(&err, "cannot complete machine configuration")
// Everything needs the environment's authorized keys.
authKeys := cfg.AuthorizedKeys()
if authKeys == "" {
return fmt.Errorf("environment configuration has no authorized-keys")
}
mcfg.AuthorizedKeys = authKeys
if mcfg.MachineEnvironment == nil {
mcfg.MachineEnvironment = make(map[string]string)
}
mcfg.MachineEnvironment[osenv.JujuProviderType] = cfg.Type()
if !mcfg.StateServer {
return nil
}
// These settings are only appropriate at bootstrap time. At the
// moment, the only state server is the bootstrap node, but this
// will probably change.
if mcfg.APIInfo != nil || mcfg.StateInfo != nil {
return fmt.Errorf("machine configuration already has api/state info")
}
caCert, hasCACert := cfg.CACert()
if !hasCACert {
return fmt.Errorf("environment configuration has no ca-cert")
}
password := cfg.AdminSecret()
if password == "" {
return fmt.Errorf("environment configuration has no admin-secret")
}
passwordHash := utils.PasswordHash(password)
mcfg.APIInfo = &api.Info{Password: passwordHash, CACert: caCert}
mcfg.StateInfo = &state.Info{Password: passwordHash, CACert: caCert}
mcfg.StatePort = cfg.StatePort()
mcfg.APIPort = cfg.APIPort()
mcfg.Constraints = cons
if mcfg.Config, err = BootstrapConfig(cfg); err != nil {
return err
}
// These really are directly relevant to running a state server.
cert, key, err := cfg.GenerateStateServerCertAndKey()
if err != nil {
return fmt.Errorf("cannot generate state server certificate: %v", err)
}
mcfg.StateServerCert = cert
mcfg.StateServerKey = key
return nil
}
// ComposeUserData puts together a binary (gzipped) blob of user data.
// The additionalScripts are additional command lines that you need cloudinit
// to run on the instance. Use with care.
func ComposeUserData(cfg *cloudinit.MachineConfig, additionalScripts ...string) ([]byte, error) {
cloudcfg := coreCloudinit.New()
for _, script := range additionalScripts {
cloudcfg.AddRunCmd(script)
}
cloudcfg, err := cloudinit.Configure(cfg, cloudcfg)
if err != nil {
return nil, err
}
data, err := cloudcfg.Render()
if err != nil {
return nil, err
}
return utils.Gzip(data), nil
}