// 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 finding the tools to, // use for bootstrap, 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 errors.Maskf(&err, "cannot complete machine configuration") if err := PopulateMachineConfig( mcfg, cfg.Type(), cfg.AuthorizedKeys(), cfg.SSLHostnameVerification(), cfg.ProxySettings(), cfg.AptProxySettings(), ); err != nil { return err } // The following 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.Bootstrap { return nil } 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.UserPasswordHash(password, utils.CompatSalt) mcfg.APIInfo = &api.Info{Password: passwordHash, CACert: caCert} mcfg.StateInfo = &state.Info{Password: passwordHash, CACert: caCert} // These really are directly relevant to running a state server. cert, key, err := cfg.GenerateStateServerCertAndKey() if err != nil { return errgo.Annotate(err, "cannot generate state server certificate") } srvInfo := params.StateServingInfo{ StatePort: cfg.StatePort(), APIPort: cfg.APIPort(), Cert: string(cert), PrivateKey: string(key), SystemIdentity: mcfg.SystemPrivateSSHKey, } mcfg.StateServingInfo = &srvInfo mcfg.Constraints = cons if mcfg.Config, err = BootstrapConfig(cfg); err != nil { return err } return nil }
// ensureCertificate generates a new CA certificate and // attaches it to the given environment configuration, // unless the configuration already has one. func ensureCertificate(cfg *config.Config) (*config.Config, error) { _, hasCACert := cfg.CACert() _, hasCAKey := cfg.CAPrivateKey() if hasCACert && hasCAKey { return cfg, nil } if hasCACert && !hasCAKey { return nil, fmt.Errorf("environment configuration with a certificate but no CA private key") } caCert, caKey, err := cert.NewCA(cfg.Name(), time.Now().UTC().AddDate(10, 0, 0)) if err != nil { return nil, err } return cfg.Apply(map[string]interface{}{ "ca-cert": string(caCert), "ca-private-key": string(caKey), }) }
// getStateInfo puts together the state.Info and api.Info for the given // config, with the given state-server host names. // The given config absolutely must have a CACert. func getStateInfo(config *config.Config, hostnames []string) (*state.Info, *api.Info) { cert, hasCert := config.CACert() if !hasCert { panic(errors.New("getStateInfo: config has no CACert")) } return &state.Info{ Addrs: composeAddresses(hostnames, config.StatePort()), CACert: cert, }, &api.Info{ Addrs: composeAddresses(hostnames, config.APIPort()), CACert: cert, } }