Exemple #1
0
// newConf returns the init system config for the mongo state service.
func newConf(dataDir, dbDir, mongoPath string, port, oplogSizeMB int, wantNumaCtl bool) common.Conf {
	mongoCmd := mongoPath +
		" --auth" +
		" --dbpath " + utils.ShQuote(dbDir) +
		" --sslOnNormalPorts" +
		" --sslPEMKeyFile " + utils.ShQuote(sslKeyPath(dataDir)) +
		" --sslPEMKeyPassword ignored" +
		" --port " + fmt.Sprint(port) +
		" --noprealloc" +
		" --syslog" +
		" --smallfiles" +
		" --journal" +
		" --keyFile " + utils.ShQuote(sharedSecretPath(dataDir)) +
		" --replSet " + ReplicaSetName +
		" --ipv6" +
		" --oplogSize " + strconv.Itoa(oplogSizeMB)
	extraScript := ""
	if wantNumaCtl {
		extraScript = fmt.Sprintf(detectMultiNodeScript, multinodeVarName, multinodeVarName)
		mongoCmd = fmt.Sprintf(numaCtlWrap, multinodeVarName) + mongoCmd
	}
	conf := common.Conf{
		Desc: "juju state database",
		Limit: map[string]int{
			"nofile": maxFiles,
			"nproc":  maxProcs,
		},
		Timeout:     serviceTimeout,
		ExtraScript: extraScript,
		ExecStart:   mongoCmd,
	}
	return conf
}
Exemple #2
0
// NewSSHStorage creates a new SSHStorage, connected to the
// specified host, managing state under the specified remote path.
func NewSSHStorage(params NewSSHStorageParams) (*SSHStorage, error) {
	if params.StorageDir == "" {
		return nil, errors.New("storagedir must be specified and non-empty")
	}
	if params.TmpDir == "" {
		return nil, errors.New("tmpdir must be specified and non-empty")
	}

	script := fmt.Sprintf(
		"install -d -g $SUDO_GID -o $SUDO_UID %s %s",
		utils.ShQuote(params.StorageDir),
		utils.ShQuote(params.TmpDir),
	)

	cmd := sshCommand(params.Host, "sudo", "-n", "/bin/bash")
	var stderr bytes.Buffer
	cmd.Stderr = &stderr
	cmd.Stdin = strings.NewReader(script)
	if err := cmd.Run(); err != nil {
		err = fmt.Errorf("failed to create storage dir: %v (%v)", err, strings.TrimSpace(stderr.String()))
		return nil, err
	}

	// We could use sftp, but then we'd be at the mercy of
	// sftp's output messages for checking errors. Instead,
	// we execute an interactive bash shell.
	cmd = sshCommand(params.Host, "bash")
	stdin, err := cmd.StdinPipe()
	if err != nil {
		return nil, err
	}
	stdout, err := cmd.StdoutPipe()
	if err != nil {
		stdin.Close()
		return nil, err
	}
	// Combine stdout and stderr, so we can easily
	// get at catastrophic failure messages.
	cmd.Stderr = cmd.Stdout
	stor := &SSHStorage{
		host:       params.Host,
		remotepath: params.StorageDir,
		tmpdir:     params.TmpDir,
		cmd:        cmd,
		stdin:      stdin,
		stdout:     stdout,
		scanner:    bufio.NewScanner(stdout),
	}
	cmd.Start()

	// Verify we have write permissions.
	_, err = stor.runf(flockExclusive, "touch %s", utils.ShQuote(params.StorageDir))
	if err != nil {
		stdin.Close()
		stdout.Close()
		cmd.Wait()
		return nil, err
	}
	return stor, nil
}
Exemple #3
0
func (e *manualEnviron) Destroy() error {
	script := `
set -x
touch %s
pkill -%d jujud && exit
stop %s
rm -f /etc/init/juju*
rm -fr %s %s
exit 0
`
	script = fmt.Sprintf(
		script,
		// WARNING: this is linked with the use of uninstallFile in
		// the agent package. Don't change it without extreme care,
		// and handling for mismatches with already-deployed agents.
		utils.ShQuote(path.Join(
			agent.DefaultPaths.DataDir,
			agent.UninstallFile,
		)),
		terminationworker.TerminationSignal,
		mongo.ServiceName,
		utils.ShQuote(agent.DefaultPaths.DataDir),
		utils.ShQuote(agent.DefaultPaths.LogDir),
	)
	_, err := runSSHCommand(
		"ubuntu@"+e.envConfig().bootstrapHost(),
		[]string{"sudo", "/bin/bash"}, script,
	)
	return err
}
Exemple #4
0
// upstartService returns the upstart config for the mongo state service.
// It also returns the path to the mongod executable that the upstart config
// will be using.
func upstartService(namespace, dataDir, dbDir string, port, oplogSizeMB int) (*upstart.Conf, string, error) {
	svc := upstart.NewService(ServiceName(namespace))

	mongoPath, err := Path()
	if err != nil {
		return nil, "", err
	}

	mongoCmd := mongoPath + " --auth" +
		" --dbpath=" + utils.ShQuote(dbDir) +
		" --sslOnNormalPorts" +
		" --sslPEMKeyFile " + utils.ShQuote(sslKeyPath(dataDir)) +
		" --sslPEMKeyPassword ignored" +
		" --port " + fmt.Sprint(port) +
		" --noprealloc" +
		" --syslog" +
		" --smallfiles" +
		" --journal" +
		" --keyFile " + utils.ShQuote(sharedSecretPath(dataDir)) +
		" --replSet " + ReplicaSetName +
		" --ipv6 " +
		" --oplogSize " + strconv.Itoa(oplogSizeMB)
	conf := &upstart.Conf{
		Service: *svc,
		Desc:    "juju state database",
		Limit: map[string]string{
			"nofile": fmt.Sprintf("%d %d", maxFiles, maxFiles),
			"nproc":  fmt.Sprintf("%d %d", maxProcs, maxProcs),
		},
		Cmd: mongoCmd,
	}
	return conf, mongoPath, nil
}
Exemple #5
0
func (e *manualEnviron) Destroy() error {
	script := `
set -x
touch %s
pkill -%d jujud && exit
stop %s
rm -f /etc/init/juju*
rm -f /etc/rsyslog.d/*juju*
rm -fr %s %s
exit 0
`
	script = fmt.Sprintf(
		script,
		utils.ShQuote(path.Join(
			agent.DefaultPaths.DataDir,
			agent.UninstallAgentFile,
		)),
		terminationworker.TerminationSignal,
		mongo.ServiceName(""),
		utils.ShQuote(agent.DefaultPaths.DataDir),
		utils.ShQuote(agent.DefaultPaths.LogDir),
	)
	_, err := runSSHCommand(
		"ubuntu@"+e.envConfig().bootstrapHost(),
		[]string{"sudo", "/bin/bash"}, script,
	)
	return err
}
Exemple #6
0
// DestroyController implements the Environ interface.
func (e *manualEnviron) DestroyController(controllerUUID string) error {
	script := `
set -x
touch %s
# If jujud is running, we then wait for a while for it to stop.
stopped=0
if pkill -%d jujud; then
    for i in ` + "`seq 1 30`" + `; do
        if pgrep jujud > /dev/null ; then
            sleep 1
        else
            echo "jujud stopped"
            stopped=1
            break
        fi
    done
fi
if [ $stopped -ne 1 ]; then
    # If jujud didn't stop nicely, we kill it hard here.
    %spkill -9 jujud
    service %s stop
fi
rm -f /etc/init/juju*
rm -f /etc/systemd/system/juju*
rm -fr %s %s
exit 0
`
	var diagnostics string
	if featureflag.Enabled(feature.DeveloperMode) {
		diagnostics = `
    echo "Dump engine report and goroutines for stuck jujud"
    source /etc/profile.d/juju-introspection.sh
    juju-engine-report
    juju-goroutines
`
	}
	script = fmt.Sprintf(
		script,
		// WARNING: this is linked with the use of uninstallFile in
		// the agent package. Don't change it without extreme care,
		// and handling for mismatches with already-deployed agents.
		utils.ShQuote(path.Join(
			agent.DefaultPaths.DataDir,
			agent.UninstallFile,
		)),
		terminationworker.TerminationSignal,
		diagnostics,
		mongo.ServiceName,
		utils.ShQuote(agent.DefaultPaths.DataDir),
		utils.ShQuote(agent.DefaultPaths.LogDir),
	)
	logger.Tracef("destroy controller script: %s", script)
	stdout, stderr, err := runSSHCommand(
		"ubuntu@"+e.host,
		[]string{"sudo", "/bin/bash"}, script,
	)
	logger.Debugf("script stdout: \n%s", stdout)
	logger.Debugf("script stderr: \n%s", stderr)
	return err
}
Exemple #7
0
func writeFileCommands(filename string, contents []byte, permission int) []string {
	quotedFilename := utils.ShQuote(filename)
	quotedContents := utils.ShQuote(string(contents))
	return []string{
		fmt.Sprintf("install -m %o /dev/null %s", permission, quotedFilename),
		fmt.Sprintf(`printf '%%s\n' %s > %s`, quotedContents, quotedFilename),
	}
}
Exemple #8
0
func (w *UbuntuRenderer) WriteFile(filename string, contents string, permission int) []string {
	quotedFilename := utils.ShQuote(filename)
	quotedContents := utils.ShQuote(contents)
	return []string{
		fmt.Sprintf("install -m %o /dev/null %s", permission, quotedFilename),
		fmt.Sprintf(`printf '%%s\n' %s > %s`, quotedContents, quotedFilename),
	}
}
Exemple #9
0
// newConf returns the init system config for the mongo state service.
func newConf(args ConfigArgs) common.Conf {
	mongoCmd := args.MongoPath +

		" --dbpath " + utils.ShQuote(args.DBDir) +
		" --sslOnNormalPorts" +
		" --sslPEMKeyFile " + utils.ShQuote(sslKeyPath(args.DataDir)) +
		// --sslPEMKeyPassword has to have its argument passed with = thanks to
		// https://bugs.launchpad.net/juju-core/+bug/1581284.
		" --sslPEMKeyPassword=ignored" +
		" --port " + fmt.Sprint(args.Port) +
		" --syslog" +
		" --journal" +

		" --replSet " + ReplicaSetName +
		" --quiet" +
		" --oplogSize " + strconv.Itoa(args.OplogSizeMB)

	if args.IPv6 {
		mongoCmd = mongoCmd +
			" --ipv6"
	}

	if args.Auth {
		mongoCmd = mongoCmd +
			" --auth" +
			" --keyFile " + utils.ShQuote(sharedSecretPath(args.DataDir))
	} else {
		mongoCmd = mongoCmd +
			" --noauth"
	}
	if args.Version.StorageEngine != WiredTiger {
		mongoCmd = mongoCmd +
			" --noprealloc" +
			" --smallfiles"
	} else {
		mongoCmd = mongoCmd +
			" --storageEngine wiredTiger"
	}
	extraScript := ""
	if args.WantNumaCtl {
		extraScript = fmt.Sprintf(detectMultiNodeScript, multinodeVarName, multinodeVarName)
		mongoCmd = fmt.Sprintf(numaCtlWrap, multinodeVarName) + mongoCmd
	}
	conf := common.Conf{
		Desc: "juju state database",
		Limit: map[string]int{
			"nofile": maxFiles,
			"nproc":  maxProcs,
		},
		Timeout:     serviceTimeout,
		ExtraScript: extraScript,
		ExecStart:   mongoCmd,
	}
	return conf
}
Exemple #10
0
func (s *SSHStorage) run(flockmode flockmode, command string, input io.Reader, inputlen int64) (string, error) {
	const rcPrefix = "JUJU-RC: "
	command = fmt.Sprintf(
		"SHELL=/bin/bash flock %s %s -c %s",
		flockmode,
		utils.ShQuote(s.remotepath),
		utils.ShQuote(command),
	)
	stdin := bufio.NewWriter(s.stdin)
	if input != nil {
		command = fmt.Sprintf("base64 -d << '@EOF' | (%s)", command)
	}
	command = fmt.Sprintf("(%s) 2>&1; echo %s$?", command, rcPrefix)
	if _, err := stdin.WriteString(command + "\n"); err != nil {
		return "", fmt.Errorf("failed to write command: %v", err)
	}
	if input != nil {
		if err := copyAsBase64(stdin, input); err != nil {
			return "", s.terminate(fmt.Errorf("failed to write input: %v", err))
		}
	}
	if err := stdin.Flush(); err != nil {
		return "", s.terminate(fmt.Errorf("failed to write input: %v", err))
	}
	var output []string
	for s.scanner.Scan() {
		line := s.scanner.Text()
		if strings.HasPrefix(line, rcPrefix) {
			line := line[len(rcPrefix):]
			rc, err := strconv.Atoi(line)
			if err != nil {
				return "", fmt.Errorf("failed to parse exit code %q: %v", line, err)
			}
			outputJoined := strings.Join(output, "\n")
			if rc == 0 {
				return outputJoined, nil
			}
			return "", SSHStorageError{outputJoined, rc}
		} else {
			output = append(output, line)
		}
	}

	err := fmt.Errorf("failed to locate %q", rcPrefix)
	if len(output) > 0 {
		err = fmt.Errorf("%v (output: %q)", err, strings.Join(output, "\n"))
	}
	if scannerErr := s.scanner.Err(); scannerErr != nil {
		err = fmt.Errorf("%v (scanner error: %v)", err, scannerErr)
	}
	return "", err
}
Exemple #11
0
// 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
}
Exemple #12
0
func (w *proxyWorker) writeEnvironmentFile() error {
	// Writing the environment file is handled by executing the script:
	//
	// On cloud-instance ubuntu images, the ubuntu user is uid 1000, but in
	// the situation where the ubuntu user has been created as a part of the
	// manual provisioning process, the user will exist, and will not have the
	// same uid/gid as the default cloud image.
	//
	// It is easier to shell out to check, and is also the same way that the file
	// is written in the cloud-init process, so consistency FTW.
	filePath := path.Join(w.config.Directory, w.config.Filename)
	result, err := exec.RunCommands(exec.RunParams{
		Commands: fmt.Sprintf(
			`[ -e %s ] && (printf '%%s\n' %s > %s && chown ubuntu:ubuntu %s)`,
			w.config.Directory,
			utils.ShQuote(w.proxy.AsScriptEnvironment()),
			filePath, filePath),
		WorkingDir: w.config.Directory,
	})

	if err != nil {
		return err
	}
	if result.Code != 0 {
		logger.Errorf("failed writing new proxy values: \n%s\n%s", result.Stdout, result.Stderr)
	}
	return nil
}
Exemple #13
0
// Run the commands specified on the machines identified through the
// list of machines, units and services.
func (c *Client) Run(run params.RunParams) (results params.RunResults, err error) {
	units, err := getAllUnitNames(c.api.state, run.Units, run.Services)
	if err != nil {
		return results, err
	}
	// We want to create a RemoteExec for each unit and each machine.
	// If we have both a unit and a machine request, we run it twice,
	// once for the unit inside the exec context using juju-run, and
	// the other outside the context just using bash.
	var params []*RemoteExec
	var quotedCommands = utils.ShQuote(run.Commands)
	for _, unit := range units {
		// We know that the unit is both a principal unit, and that it has an
		// assigned machine.
		machineId, _ := unit.AssignedMachineId()
		machine, err := c.api.state.Machine(machineId)
		if err != nil {
			return results, err
		}
		command := fmt.Sprintf("juju-run %s %s", unit.Name(), quotedCommands)
		execParam := remoteParamsForMachine(machine, command, run.Timeout)
		execParam.UnitId = unit.Name()
		params = append(params, execParam)
	}
	for _, machineId := range run.Machines {
		machine, err := c.api.state.Machine(machineId)
		if err != nil {
			return results, err
		}
		command := fmt.Sprintf("juju-run --no-context %s", quotedCommands)
		execParam := remoteParamsForMachine(machine, command, run.Timeout)
		params = append(params, execParam)
	}
	return ParallelExecute(c.getDataDir(), params), nil
}
Exemple #14
0
// ProvisioningScript generates a bash script that can be
// executed on a remote host to carry out the cloud-init
// configuration.
func ProvisioningScript(mcfg *cloudinit.MachineConfig) (string, error) {

	cloudcfg := coreCloudinit.New()
	cloudcfg.SetAptUpdate(mcfg.EnableOSRefreshUpdate)
	cloudcfg.SetAptUpgrade(mcfg.EnableOSUpgrade)

	udata, err := cloudinit.NewUserdataConfig(mcfg, cloudcfg)
	if err != nil {
		return "", errors.Annotate(err, "error generating cloud-config")
	}
	if err := udata.ConfigureJuju(); err != nil {
		return "", errors.Annotate(err, "error generating cloud-config")
	}

	configScript, err := sshinit.ConfigureScript(cloudcfg)
	if err != nil {
		return "", errors.Annotate(err, "error converting cloud-config to script")
	}

	var buf bytes.Buffer
	// Always remove the cloud-init-output.log file first, if it exists.
	fmt.Fprintf(&buf, "rm -f %s\n", utils.ShQuote(mcfg.CloudInitOutputLog))
	// If something goes wrong, dump cloud-init-output.log to stderr.
	buf.WriteString(shell.DumpFileOnErrorScript(mcfg.CloudInitOutputLog))
	buf.WriteString(configScript)
	return buf.String(), nil
}
Exemple #15
0
// mockShellCommand creates a new command with the given
// name and contents, and patches $PATH so that it will be
// executed by preference. It returns the name of a file
// that is written by each call to the command - mockShellCalls
// can be used to retrieve the calls.
func mockShellCommand(c *gc.C, s *testing.CleanupSuite, name string) string {
	dir := c.MkDir()
	s.PatchEnvPathPrepend(dir)

	// Note the shell script produces output of the form:
	// +arg1+\n
	// +arg2+\n
	// ...
	// +argn+\n
	// -
	//
	// It would be nice if there was a simple way of unambiguously
	// quoting shell arguments, but this will do as long
	// as no argument contains a newline character.
	outputFile := filepath.Join(dir, name+".out")
	contents := `#!/bin/sh
{
	for i in "$@"; do
		echo +"$i"+
	done
	echo -
} >> ` + utils.ShQuote(outputFile) + `
`
	err := ioutil.WriteFile(filepath.Join(dir, name), []byte(contents), 0755)
	c.Assert(err, gc.IsNil)
	return outputFile
}
func (w *MachineEnvironmentWorker) writeEnvironmentFile() error {
	// Writing the environment file is handled by executing the script for two
	// primary reasons:
	//
	// 1: In order to have the local provider specify the environment settings
	// for the machine agent running on the host, this worker needs to run,
	// but it shouldn't be touching any files on the disk.  If however there is
	// an ubuntu user, it will. This shouldn't be a problem.
	//
	// 2: On cloud-instance ubuntu images, the ubuntu user is uid 1000, but in
	// the situation where the ubuntu user has been created as a part of the
	// manual provisioning process, the user will exist, and will not have the
	// same uid/gid as the default cloud image.
	//
	// It is easier to shell out to check both these things, and is also the
	// same way that the file is written in the cloud-init process, so
	// consistency FTW.
	filePath := path.Join(ProxyDirectory, ProxyFile)
	result, err := exec.RunCommands(exec.RunParams{
		Commands: fmt.Sprintf(
			`[ -e %s ] && (printf '%%s\n' %s > %s && chown ubuntu:ubuntu %s)`,
			ProxyDirectory,
			utils.ShQuote(w.proxy.AsScriptEnvironment()),
			filePath, filePath),
		WorkingDir: ProxyDirectory,
	})
	if err != nil {
		return err
	}
	if result.Code != 0 {
		logger.Errorf("failed writing new proxy values: \n%s\n%s", result.Stdout, result.Stderr)
	}
	return nil
}
Exemple #17
0
func (e *manualEnviron) verifyBootstrapHost() error {
	// First verify that the environment is bootstrapped by checking
	// if the agents directory exists. Note that we cannot test the
	// root data directory, as that is created in the process of
	// initialising sshstorage.
	agentsDir := path.Join(agent.DefaultPaths.DataDir, "agents")
	const noAgentDir = "no-agent-dir"
	stdin := fmt.Sprintf(
		"test -d %s || echo %s",
		utils.ShQuote(agentsDir),
		noAgentDir,
	)
	out, _, err := runSSHCommand(
		"ubuntu@"+e.host,
		[]string{"/bin/bash"},
		stdin,
	)
	if err != nil {
		return err
	}
	if out = strings.TrimSpace(out); len(out) > 0 {
		if out == noAgentDir {
			return environs.ErrNotBootstrapped
		}
		err := errors.Errorf("unexpected output: %q", out)
		logger.Infof(err.Error())
		return err
	}
	return nil
}
Exemple #18
0
// templateUserData returns a minimal user data necessary for the template.
// This should have the authorized keys, base packages, the cloud archive if
// necessary,  initial apt proxy config, and it should do the apt-get
// update/upgrade initially.
func templateUserData(
	series string,
	authorizedKeys string,
	aptProxy proxy.Settings,
	enablePackageUpdates bool,
	enableOSUpgrades bool,
) ([]byte, error) {
	config := corecloudinit.New()
	config.AddScripts(
		"set -xe", // ensure we run all the scripts or abort.
	)
	config.AddSSHAuthorizedKeys(authorizedKeys)
	if enablePackageUpdates {
		cloudinit.MaybeAddCloudArchiveCloudTools(config, series)
	}
	cloudinit.AddAptCommands(aptProxy, config, enablePackageUpdates, enableOSUpgrades)
	config.AddScripts(
		fmt.Sprintf(
			"printf '%%s\n' %s > %s",
			utils.ShQuote(templateShutdownUpstartScript),
			templateShutdownUpstartFilename,
		))

	renderer, err := corecloudinit.NewRenderer(series)
	if err != nil {
		return nil, err
	}
	data, err := renderer.Render(config)
	if err != nil {
		return nil, err
	}
	return data, nil
}
Exemple #19
0
// RunConfigureScript connects to the specified host over
// SSH, and executes the provided script which is expected
// to have been returned by cloudinit ConfigureScript.
func RunConfigureScript(script string, params ConfigureParams) error {
	logger.Tracef("Running script on %s: %s", params.Host, script)

	encoded := base64.StdEncoding.EncodeToString([]byte(`
set -e
tmpfile=$(mktemp)
trap "rm -f $tmpfile" EXIT
cat > $tmpfile
/bin/bash $tmpfile
`))

	// bash will read a byte at a time when consuming commands
	// from stdin. We avoid sending the entire script -- which
	// will be very large when uploading tools -- directly to
	// bash for this reason. Instead, run cat which will write
	// the script to disk, and then execute it from there.
	cmd := ssh.Command(params.Host, []string{
		"sudo", "/bin/bash", "-c",
		// The outer bash interprets the $(...), and executes
		// the decoded script in the nested bash. This avoids
		// linebreaks in the commandline, which the go.crypto-
		// based client has trouble with.
		fmt.Sprintf(
			`/bin/bash -c "$(echo %s | base64 -d)"`,
			utils.ShQuote(encoded),
		),
	}, nil)

	cmd.Stdin = strings.NewReader(script)
	cmd.Stderr = params.ProgressWriter
	return cmd.Run()
}
Exemple #20
0
// cloudinitRunCmd returns the shell command that, when run, will create the
// "machine info" file containing the hostname of a machine.
// That command is destined to be used by cloudinit.
func (info *machineInfo) cloudinitRunCmd() (string, error) {
	yaml, err := goyaml.Marshal(info)
	if err != nil {
		return "", err
	}
	script := fmt.Sprintf(`mkdir -p %s; echo -n %s > %s`, utils.ShQuote(environs.DataDir), utils.ShQuote(string(yaml)), utils.ShQuote(_MAASInstanceFilename))
	return script, nil
}
Exemple #21
0
// Remove implements storage.StorageWriter.Remove
func (s *SSHStorage) Remove(name string) error {
	path, err := s.path(name)
	if err != nil {
		return err
	}
	path = utils.ShQuote(path)
	_, err = s.runf(flockExclusive, "rm -f %s", path)
	return err
}
Exemple #22
0
func (c *configInternal) WriteCommands() ([]string, error) {
	data, err := c.fileContents()
	if err != nil {
		return nil, err
	}
	commands := []string{"mkdir -p " + utils.ShQuote(c.Dir())}
	commands = append(commands, writeFileCommands(c.File(agentConfigFilename), data, 0600)...)
	return commands, nil
}
Exemple #23
0
// Put implements storage.StorageWriter.Put
func (s *SSHStorage) Put(name string, r io.Reader, length int64) error {
	logger.Debugf("putting %q (len %d) to storage", name, length)
	path, err := s.path(name)
	if err != nil {
		return err
	}
	path = utils.ShQuote(path)
	tmpdir := utils.ShQuote(s.tmpdir)

	// Write to a temporary file ($TMPFILE), then mv atomically.
	command := fmt.Sprintf("mkdir -p `dirname %s` && cat >| $TMPFILE", path)
	command = fmt.Sprintf(
		"TMPFILE=`mktemp --tmpdir=%s` && ((%s && mv $TMPFILE %s) || rm -f $TMPFILE)",
		tmpdir, command, path,
	)

	_, err = s.run(flockExclusive, command+"\n", r, length)
	return err
}
Exemple #24
0
// Updates proxy settings used when rendering the conf as a script
func (cfg *ubuntuCloudConfig) updateProxySettings(proxySettings proxy.Settings) {
	// Write out the apt proxy settings
	if (proxySettings != proxy.Settings{}) {
		filename := config.AptProxyConfigFile
		cfg.AddBootCmd(fmt.Sprintf(
			`printf '%%s\n' %s > %s`,
			utils.ShQuote(cfg.paccmder.ProxyConfigContents(proxySettings)),
			filename))
	}
}
Exemple #25
0
func GetCheckNonceCommand(instanceConfig *instancecfg.InstanceConfig) string {
	// Each attempt to connect to an address must verify the machine is the
	// bootstrap machine by checking its nonce file exists and contains the
	// nonce in the InstanceConfig. This also blocks sshinit from proceeding
	// until cloud-init has completed, which is necessary to ensure apt
	// invocations don't trample each other.
	nonceFile := utils.ShQuote(path.Join(instanceConfig.DataDir, cloudconfig.NonceFile))
	checkNonceCommand := fmt.Sprintf(`
	noncefile=%s
	if [ ! -e "$noncefile" ]; then
		echo "$noncefile does not exist" >&2
		exit 1
	fi
	content=$(cat $noncefile)
	if [ "$content" != %s ]; then
		echo "$noncefile contents do not match machine nonce" >&2
		exit 1
	fi
	`, nonceFile, utils.ShQuote(instanceConfig.MachineNonce))
	return checkNonceCommand
}
Exemple #26
0
// addFile is a helper function returns all the required shell commands to write
// a file (be it text or binary) with regards to the given parameters
// NOTE: if the file already exists, it will be overwritten.
func addFileCmds(filename string, data []byte, mode uint, binary bool) []string {
	// Note: recent versions of cloud-init have the "write_files"
	// module, which can write arbitrary files. We currently support
	// 12.04 LTS, which uses an older version of cloud-init without
	// this module.
	// TODO (aznashwan): eagerly await 2017 and to do the right thing here
	p := utils.ShQuote(filename)

	cmds := []string{fmt.Sprintf("install -D -m %o /dev/null %s", mode, p)}
	// Don't use the shell's echo builtin here; the interpretation
	// of escape sequences differs between shells, namely bash and
	// dash. Instead, we use printf (or we could use /bin/echo).
	if binary {
		encoded := base64.StdEncoding.EncodeToString(data)
		cmds = append(cmds, fmt.Sprintf(`printf %%s %s | base64 -d > %s`, encoded, p))
	} else {
		cmds = append(cmds, fmt.Sprintf(`printf '%%s\n' %s > %s`, utils.ShQuote(string(data)), p))
	}

	return cmds
}
Exemple #27
0
// runViaSSH runs script in the remote machine with address addr.
func runViaSSH(addr string, script string) error {
	// This is taken from cmd/juju/ssh.go there is no other clear way to set user
	userAddr := "ubuntu@" + addr
	sshOptions := ssh.Options{}
	sshOptions.SetIdentities("/var/lib/juju/system-identity")
	userCmd := sshCommand(userAddr, []string{"sudo", "-n", "bash", "-c " + utils.ShQuote(script)}, &sshOptions)
	var stderrBuf bytes.Buffer
	userCmd.Stderr = &stderrBuf
	if err := userCmd.Run(); err != nil {
		return errors.Annotatef(err, "ssh command failed: %q", stderrBuf.String())
	}
	return nil
}
Exemple #28
0
// RunOnAllMachines attempts to run the specified command on all the machines.
func (c *Client) RunOnAllMachines(run params.RunParams) (params.RunResults, error) {
	machines, err := c.api.state.AllMachines()
	if err != nil {
		return params.RunResults{}, err
	}
	var params []*RemoteExec
	quotedCommands := utils.ShQuote(run.Commands)
	command := fmt.Sprintf("juju-run --no-context %s", quotedCommands)
	for _, machine := range machines {
		params = append(params, remoteParamsForMachine(machine, command, run.Timeout))
	}
	return ParallelExecute(c.getDataDir(), params), nil
}
Exemple #29
0
// InitUbuntuUser adds the ubuntu user if it doesn't
// already exist, updates its ~/.ssh/authorized_keys,
// and enables passwordless sudo for it.
//
// InitUbuntuUser will initially attempt to login as
// the ubuntu user, and verify that passwordless sudo
// is enabled; only if this is false will there be an
// attempt with the specified login.
//
// authorizedKeys may be empty, in which case the file
// will be created and left empty.
//
// stdin and stdout will be used for remote sudo prompts,
// if the ubuntu user must be created/updated.
func InitUbuntuUser(host, login, authorizedKeys string, stdin io.Reader, stdout io.Writer) error {
	logger.Infof("initialising %q, user %q", host, login)

	// To avoid unnecessary prompting for the specified login,
	// initUbuntuUser will first attempt to ssh to the machine
	// as "ubuntu" with password authentication disabled, and
	// ensure that it can use sudo without a password.
	//
	// Note that we explicitly do not allocate a PTY, so we
	// get a failure if sudo prompts.
	cmd := ssh.Command("ubuntu@"+host, []string{"sudo", "-n", "true"}, nil)
	if cmd.Run() == nil {
		logger.Infof("ubuntu user is already initialised")
		return nil
	}

	// Failed to login as ubuntu (or passwordless sudo is not enabled).
	// Use specified login, and execute the initUbuntuScript below.
	if login != "" {
		host = login + "@" + host
	}
	script := fmt.Sprintf(initUbuntuScript, utils.ShQuote(authorizedKeys))
	var options ssh.Options
	options.AllowPasswordAuthentication()
	options.EnablePTY()
	cmd = ssh.Command(host, []string{"sudo", "/bin/bash -c " + utils.ShQuote(script)}, &options)
	var stderr bytes.Buffer
	cmd.Stdin = stdin
	cmd.Stdout = stdout // for sudo prompt
	cmd.Stderr = &stderr
	if err := cmd.Run(); err != nil {
		if stderr.Len() != 0 {
			err = fmt.Errorf("%v (%v)", err, strings.TrimSpace(stderr.String()))
		}
		return err
	}
	return nil
}
Exemple #30
0
// DumpFileOnErrorScript returns a bash script that
// may be used to dump the contents of the specified
// file to stderr when the shell exits with an error.
func DumpFileOnErrorScript(filename string) string {
	script := `
dump_file() {
    code=$?
    if [ $code -ne 0 -a -e %s ]; then
        cat %s >&2
    fi
    exit $code
}
trap dump_file EXIT
`[1:]
	filename = utils.ShQuote(filename)
	return fmt.Sprintf(script, filename, filename)
}