// ConfigureScript generates the bash script that applies // the specified cloud-config. func ConfigureScript(cloudcfg *cloudinit.Config) (string, error) { // TODO(axw): 2013-08-23 bug 1215777 // Carry out configuration for ssh-keys-per-user, // machine-updates-authkeys, using cloud-init config. // // We should work with smoser to get a supported // command in (or next to) cloud-init for manually // invoking cloud-config. This would address the // above comment by removing the need to generate a // script "by hand". // Bootcmds must be run before anything else, // as they may affect package installation. bootcmds, err := cmdlist(cloudcfg.BootCmds()) if err != nil { return "", err } // Add package sources and packages. pkgcmds, err := addPackageCommands(cloudcfg) if err != nil { return "", err } // Runcmds come last. runcmds, err := cmdlist(cloudcfg.RunCmds()) if err != nil { return "", err } // We prepend "set -xe". This is already in runcmds, // but added here to avoid relying on that to be // invariant. script := []string{"#!/bin/bash", "set -e"} // We must initialise progress reporting before entering // the subshell and redirecting stderr. script = append(script, cloudinit.InitProgressCmd()) stdout, stderr := cloudcfg.Output(cloudinit.OutAll) script = append(script, "(") if stderr != "" { script = append(script, "(") } script = append(script, bootcmds...) script = append(script, pkgcmds...) script = append(script, runcmds...) if stderr != "" { script = append(script, ") "+stdout) script = append(script, ") "+stderr) } else { script = append(script, ") "+stdout+" 2>&1") } return strings.Join(script, "\n"), nil }
// addPackageCommands returns a slice of commands that, when run, // will add the required apt repositories and packages. func addPackageCommands(cfg *cloudinit.Config) ([]string, error) { var cmds []string if len(cfg.AptSources()) > 0 { // Ensure add-apt-repository is available. cmds = append(cmds, cloudinit.LogProgressCmd("Installing add-apt-repository")) cmds = append(cmds, aptget+"install python-software-properties") } for _, src := range cfg.AptSources() { // PPA keys are obtained by add-apt-repository, from launchpad. if !strings.HasPrefix(src.Source, "ppa:") { if src.Key != "" { key := utils.ShQuote(src.Key) cmd := fmt.Sprintf("printf '%%s\\n' %s | apt-key add -", key) cmds = append(cmds, cmd) } } cmds = append(cmds, cloudinit.LogProgressCmd("Adding apt repository: %s", src.Source)) cmds = append(cmds, "add-apt-repository -y "+utils.ShQuote(src.Source)) if src.Prefs != nil { path := utils.ShQuote(src.Prefs.Path) contents := utils.ShQuote(src.Prefs.FileContents()) cmds = append(cmds, "install -D -m 644 /dev/null "+path) cmds = append(cmds, `printf '%s\n' `+contents+` > `+path) } } if len(cfg.AptSources()) > 0 || cfg.AptUpdate() { cmds = append(cmds, cloudinit.LogProgressCmd("Running apt-get update")) cmds = append(cmds, aptget+"update") } if cfg.AptUpgrade() { cmds = append(cmds, cloudinit.LogProgressCmd("Running apt-get upgrade")) cmds = append(cmds, aptget+"upgrade") } for _, pkg := range cfg.Packages() { cmds = append(cmds, cloudinit.LogProgressCmd("Installing package: %s", pkg)) if !strings.Contains(pkg, "--target-release") { // We only need to shquote the package name if it does not // contain additional arguments. pkg = utils.ShQuote(pkg) } cmd := fmt.Sprintf(aptget+"install %s", pkg) cmds = append(cmds, cmd) } if len(cmds) > 0 { // setting DEBIAN_FRONTEND=noninteractive prevents debconf // from prompting, always taking default values instead. cmds = append([]string{"export DEBIAN_FRONTEND=noninteractive"}, cmds...) } return cmds, nil }