29. Remote exec and copy commands — cdist 6.9.8 documentation

# 29. Remote exec and copy commands

Cdist interacts with the target host in two ways:

- it executes code (__remote_exec)

- and it copies files (__remote_copy)

By default this is accomplished with ssh and scp respectively. The default implementations used by cdist are:

    __remote_exec: ssh -o User=root
    __remote_copy: scp -o User=root -q

The user can override these defaults by providing custom implementations and passing them to cdist with the --remote-exec and/or --remote-copy arguments.

For __remote_exec, the custom implementation must behave as if it where ssh. For __remote_copy, it must behave like scp. Please notice, custom implementations should work like ssh/scp so __remote_copy must support IPv6 addresses enclosed in square brackets. For __remote_exec you must take into account that for some options (like -L) IPv6 addresses can be specified by enclosed in square brackets (see ssh(1) and scp(1)).

With this simple interface the user can take total control of how cdist interacts with the target when required, while the default implementation remains as simple as possible.

## 29.1. Examples

Here are examples of using alternative __remote_copy and __remote_exec scripts.

All scripts from below are present in cdist sources in other/examples/remote directory.

### 29.1.1. ssh

Same as cdist default.

copy

Usage: cdist config --remote-copy "/path/to/this/script" target_host

    #echo "$@" | logger -t "cdist-ssh-copy"
    scp -o User=root -q $@

exec

Usage: cdist config --remote-exec "/path/to/this/script" target_host

    #echo "$@" | logger -t "cdist-ssh-exec"
    ssh -o User=root $@

### 29.1.2. local

This effectively turns remote calling into local calling. Probably most useful for the unit testing.

copy

    code="$(echo "$@" | sed "s|\([[:space:]]\)$__target_host:|\1|g")"
    cp -L $code

exec

    target_host=$1; shift
    echo "$@" | /bin/sh

### 29.1.3. chroot

copy

Usage: cdist config --remote-copy "/path/to/this/script /path/to/your/chroot" target-id

    log() {
       #echo "$@" | logger -t "cdist-chroot-copy"
       :
    }

    chroot="$1"; shift
    target_host="$__target_host"

    # replace target_host with chroot location
    code="$(echo "$@" | sed "s|$target_host:|$chroot|g")"

    log "target_host: $target_host"
    log "chroot: $chroot"
    log "$@"
    log "$code"

    # copy files into chroot
    cp $code

    log "-----"

exec

Usage: cdist config --remote-exec "/path/to/this/script /path/to/your/chroot" target-id

    log() {
       #echo "$@" | logger -t "cdist-chroot-exec"
       :
    }

    chroot="$1"; shift
    target_host="$1"; shift

    script=$(mktemp "${chroot}/tmp/chroot-${0##*/}.XXXXXXXXXX")
    trap cleanup INT TERM EXIT
    cleanup() {
       [ $__cdist_debug ] || rm "$script"
    }

    log "target_host: $target_host"
    log "script: $script"
    log "@: $@"
    echo "#!/bin/sh -l" > "$script"
    echo "$@" >> "$script"
    chmod +x "$script"

    relative_script="${script#$chroot}"
    log "relative_script: $relative_script"

    # run in chroot
    chroot "$chroot" "$relative_script"

    log "-----"

### 29.1.4. rsync

copy

Usage: cdist config --remote-copy /path/to/this/script target_host

    # For rsync to do the right thing, the source has to end with "/" if it is
    # a directory. The below preprocessor loop takes care of that.

    # second last argument is the source
    source_index=$(($#-1))
    index=0
    for arg in $@; do
       if [ $index -eq 0 ]; then
          # reset $@
          set --
       fi
       index=$((index+=1))
       if [ $index -eq $source_index -a -d "$arg" ]; then
          arg="${arg%/}/"
       fi
       set -- "$@" "$arg"
    done

    rsync --backup --suffix=~cdist -e 'ssh -o User=root' $@

### 29.1.5. schroot

__remote_copy and __remote_exec scripts to run cdist against a chroot on the target host over ssh.

copy

Usage: cdist config --remote-copy "/path/to/this/script schroot-chroot-name" target_host

    log() {
       #echo "$@" | logger -t "cdist-schroot-copy"
       :
    }

    chroot_name="$1"; shift
    target_host="$__target_host"

    # get directory for given chroot_name
    chroot="$(ssh -o User=root -q $target_host schroot -c $chroot_name --config | awk -F = '/directory=/ {print $2}')"

    # prefix destination with chroot
    code="$(echo "$@" | sed "s|$target_host:|$target_host:$chroot|g")"

    log "target_host: $target_host"
    log "chroot_name: $chroot_name"
    log "chroot: $chroot"
    log "@: $@"
    log "code: $code"

    # copy files into remote chroot
    scp -o User=root -q $code

    log "-----"

exec

Usage: cdist config --remote-exec "/path/to/this/script schroot-chroot-name" target_host

    log() {
       #echo "$@" | logger -t "cdist-schroot-exec"
       :
    }

    chroot_name="$1"; shift
    target_host="$1"; shift

    code="ssh -o User=root -q $target_host schroot -c $chroot_name -- $@"

    log "target_host: $target_host"
    log "chroot_name: $chroot_name"
    log "@: $@"
    log "code: $code"

    # run in remote chroot
    $code

    log "-----"

### 29.1.6. schroot-uri

__remote_exec/__remote_copy script to run cdist against a schroot target URI.

Usage:

    cdist config \
        --remote-exec "/path/to/this/script exec" \
        --remote-copy "/path/to/this/script copy" \
        target_uri

    # target_uri examples:
    schroot:///chroot-name
    schroot://foo.ethz.ch/chroot-name
    schroot://user-name@foo.ethz.ch/chroot-name

    # and how to match them in .../manifest/init
    case "$target_host" in
    schroot://*)
        # any schroot
    ;;
    schroot://foo.ethz.ch/*)
        # any schroot on specific host
    ;;
    schroot://foo.ethz.ch/chroot-name)
        # specific schroot on specific host
    ;;
    schroot:///chroot-name)
        # specific schroot on localhost
    ;;
    esac

copy/exec

    my_name="${0##*/}"
    mode="$1"; shift

    log() {
       # uncomment me for debugging
       #echo "$@" | logger -t "cdist-$my_name-$mode"
       :
    }

    die() {
       echo "$@" >&2
       exit 1
    }

    uri="$__target_host"

    scheme="${uri%%:*}"; rest="${uri#$scheme:}"; rest="${rest#//}"
    authority="${rest%%/*}"; rest="${rest#$authority}"
    path="${rest%\?*}"; rest="${rest#$path}"
    schroot_name="${path#/}"

    [ "$scheme" = "schroot" ] || die "Failed to parse scheme from __target_host ($__target_host). Expected 'schroot', got '$scheme'"
    [ -n "$schroot_name" ] || die "Failed to parse schroot name from __target_host: $__target_host"

    case "$authority" in
       '')
          # authority is empty, neither user nor host given
          user=""
          host=""
       ;;
       *@*)
          # authority contains @, take user from authority
          user="${authority%@*}"
          host="${authority#*@}"
       ;;
       *)
          # no user in authority, default to root
          user="root"
          host="$authority"
       ;;
    esac

    log "mode: $mode"
    log "@: $@"
    log "uri: $uri"
    log "scheme: $scheme"
    log "authority: $authority"
    log "user: $user"
    log "host: $host"
    log "path: $path"
    log "schroot_name: $schroot_name"

    exec_prefix=""
    copy_prefix=""
    if [ -n "$host" ]; then
       # we are working on a remote host
       exec_prefix="ssh -o User=$user -q $host"
       copy_prefix="scp -o User=$user -q"
       copy_destination_prefix="$host:"
    else
       # working on local machine
       copy_prefix="cp"
       copy_destination_prefix=""
    fi
    log "exec_prefix: $exec_prefix"
    log "copy_prefix: $copy_prefix"
    log "copy_destination_prefix: $copy_destination_prefix"

    case "$mode" in
       exec)
          # In exec mode the first argument is the __target_host which we already got from env. Get rid of it.
          shift
          code="$exec_prefix schroot -c $schroot_name -- sh -c '$@'"
       ;;
       copy)
          # get directory for given chroot_name
          schroot_directory="$($exec_prefix schroot -c $schroot_name --config | awk -F = '/directory=/ {print $2}')"
          [ -n "$schroot_directory" ] || die "Failed to retreive schroot directory for schroot: $schroot_name"
          log "schroot_directory: $schroot_directory"
          # prefix destination with chroot
          code="$copy_prefix $(echo "$@" | sed "s|$uri:|${copy_destination_prefix}${schroot_directory}|g")"
       ;;
       *) die "Unknown mode: $mode";;
    esac

    log "code: $code"

    # Run the code
    $code

    log "-----"

### 29.1.7. sudo

copy

Use rsync over ssh to copy files. Uses the "--rsync-path" option to run the remote rsync instance with sudo.

This command assumes your ssh configuration is already set up in ~/.ssh/config.

Usage: cdist config --remote-copy /path/to/this/script target_host

    # For rsync to do the right thing, the source has to end with "/" if it is
    # a directory. The below preprocessor loop takes care of that.

    # second last argument is the source
    source_index=$(($#-1))
    index=0
    for arg in $@; do
       if [ $index -eq 0 ]; then
          # reset $@
          set --
       fi
       index=$((index+=1))
       if [ $index -eq $source_index -a -d "$arg" ]; then
          arg="${arg%/}/"
       fi
       set -- "$@" "$arg"
    done

    rsync --copy-links --rsync-path="sudo rsync" -e 'ssh' "$@"

exec

Prefixes all remote commands with sudo.

This command assumes your ssh configuration is already set up in ~/.ssh/config.

Usage: cdist config --remote-exec "/path/to/this/script" target_host

    host="$1"; shift
    ssh -q "$host" sudo sh -c \""$@"\"