commit
355d82c611
@ -0,0 +1,86 @@ |
|||||||
|
Use the Rsync plugin to synchronize files to remote hosts, and execute arbitrary commands on those hosts. |
||||||
|
|
||||||
|
## Config |
||||||
|
The following parameters are used to configure the plugin: |
||||||
|
- **user** - user to log in as on the remote machines, defaults to `root` |
||||||
|
- **key** - private SSH key for the remote machines |
||||||
|
- **hosts** - hostnames or ip-addresses of the remote machines |
||||||
|
- **port** - port to connect to on the remote machines, defaults to `22` |
||||||
|
- **source** - source folder to synchronize from, defaults to `./` |
||||||
|
- **target** - target folder on remote machines to synchronize to |
||||||
|
- **include** - rsync include filter |
||||||
|
- **exclude** - rsync exclude filter |
||||||
|
- **recursive** - recursively synchronize, defaults to `false` |
||||||
|
- **delete** - delete target folder contents, defaults to `false` |
||||||
|
- **args** - instruct plugin to use these additional rsync CLI arguments, example: `"--blocking-io"` |
||||||
|
- **prescript** - list of commands to execute on remote machines before rsync occurs |
||||||
|
- **script** - list of commands to execute on remote machines after rsync occurs |
||||||
|
- **log_level** - ssh log level, defaults to quiet |
||||||
|
|
||||||
|
It is highly recommended to put your private key into a secret (`rsync_key`) so it is not exposed to users. This can be done using the drone-cli: |
||||||
|
|
||||||
|
```sh |
||||||
|
drone secret add \ |
||||||
|
--repository your/repo \ |
||||||
|
--name rsync_key \ |
||||||
|
--data @./id_rsa \ |
||||||
|
``` |
||||||
|
|
||||||
|
Add the secret to your `.drone.yml`: |
||||||
|
```yaml |
||||||
|
kind: pipeline |
||||||
|
|
||||||
|
steps: |
||||||
|
- name: rsync |
||||||
|
image: drillster/drone-rsync |
||||||
|
settings: |
||||||
|
user: some-user |
||||||
|
key: |
||||||
|
from_secret: rsync_key |
||||||
|
hosts: |
||||||
|
- remote1 |
||||||
|
source: ./dist |
||||||
|
target: ~/packages |
||||||
|
secrets: [ rsync_key ] |
||||||
|
``` |
||||||
|
|
||||||
|
See the [secret guides](https://docs.drone.io/user-guide/secrets/pre-repository/) for additional information on secrets. |
||||||
|
|
||||||
|
## Examples |
||||||
|
```yaml |
||||||
|
kind: pipeline |
||||||
|
name: default |
||||||
|
|
||||||
|
steps: |
||||||
|
- name: rsync |
||||||
|
image: drillster/drone-rsync |
||||||
|
settings: |
||||||
|
hosts: |
||||||
|
- remote1 |
||||||
|
- remote2 |
||||||
|
user: |
||||||
|
from_secret: rsync_user |
||||||
|
key: |
||||||
|
from_secret: rsync_key |
||||||
|
source: ./dist |
||||||
|
target: ~/packages |
||||||
|
include: |
||||||
|
- "app.tar.gz" |
||||||
|
- "app.tar.gz.md5" |
||||||
|
exclude: |
||||||
|
- "**.*" |
||||||
|
prescript: |
||||||
|
- cd ~/packages |
||||||
|
- md5sum -c app.tar.gz.md5 |
||||||
|
- tar -xf app.tar.gz -C ~/app |
||||||
|
script: |
||||||
|
- cd ~/packages |
||||||
|
- md5sum -c app.tar.gz.md5 |
||||||
|
- tar -xf app.tar.gz -C ~/app |
||||||
|
``` |
||||||
|
|
||||||
|
The example above illustrates a situation where an app package (`app.tar.gz`) will be deployed to 2 remote hosts (`remote1` and `remote2`). An md5 checksum will be deployed as well. After deploying, the md5 checksum is used to check the deployed package. If successful the package is extracted. |
||||||
|
|
||||||
|
## Important |
||||||
|
The script passed to **script** will be executed on remote machines directly after rsync completes to deploy the files. It will be executed step by step until a command returns a non-zero exit-code. If this happens, the entire plugin will exit and fail the build. |
||||||
|
|
@ -0,0 +1,6 @@ |
|||||||
|
FROM alpine:latest |
||||||
|
|
||||||
|
RUN apk add --no-cache --update ca-certificates bash openssh-client rsync |
||||||
|
COPY upload.sh /usr/local/ |
||||||
|
|
||||||
|
ENTRYPOINT ["/usr/local/upload.sh"] |
@ -0,0 +1,21 @@ |
|||||||
|
MIT License |
||||||
|
|
||||||
|
Copyright (c) 2023 Drillster |
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a copy |
||||||
|
of this software and associated documentation files (the "Software"), to deal |
||||||
|
in the Software without restriction, including without limitation the rights |
||||||
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell |
||||||
|
copies of the Software, and to permit persons to whom the Software is |
||||||
|
furnished to do so, subject to the following conditions: |
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in all |
||||||
|
copies or substantial portions of the Software. |
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR |
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, |
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE |
||||||
|
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER |
||||||
|
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, |
||||||
|
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE |
||||||
|
SOFTWARE. |
@ -0,0 +1,30 @@ |
|||||||
|
# drone-rsync |
||||||
|
[![drone-rsync on Docker Hub](https://img.shields.io/docker/automated/drillster/drone-rsync.svg)](https://hub.docker.com/r/drillster/drone-rsync/) |
||||||
|
|
||||||
|
This is a pure Bash [Drone](https://github.com/drone/drone) >= 0.5 plugin to sync files to remote hosts. |
||||||
|
|
||||||
|
For more information on how to use the plugin, please take a look at [the docs](https://github.com/Drillster/drone-rsync/blob/master/DOCS.md). |
||||||
|
|
||||||
|
## Docker |
||||||
|
Build the docker image by running: |
||||||
|
|
||||||
|
```bash |
||||||
|
docker build --rm=true -t drillster/drone-rsync . |
||||||
|
``` |
||||||
|
|
||||||
|
## Usage |
||||||
|
Execute from the working directory (assuming you have an SSH server running on 127.0.0.1:22): |
||||||
|
|
||||||
|
```bash |
||||||
|
docker run --rm \ |
||||||
|
-e PLUGIN_KEY=$(cat some-private-key) \ |
||||||
|
-e PLUGIN_HOSTS="127.0.0.1, 127.0.0.2, 127.0.0.3" \ |
||||||
|
-e PLUGIN_PORTS="22, 23, 24" \ |
||||||
|
-e PLUGIN_TARGET="./" \ |
||||||
|
-e PLUGIN_PRESCRIPT="echo \"Prescript Done!\"" \ |
||||||
|
-e PLUGIN_SCRIPT="echo \"Postscript Done!\"" \ |
||||||
|
-e PLUGIN_ARGS="--blocking-io" \ |
||||||
|
-v $(pwd):$(pwd) \ |
||||||
|
-w $(pwd) \ |
||||||
|
drillster/drone-rsync |
||||||
|
``` |
@ -0,0 +1,153 @@ |
|||||||
|
#!/bin/bash |
||||||
|
if [ -z "$PLUGIN_HOSTS" ]; then |
||||||
|
echo "Specify at least one host!" |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$PLUGIN_TARGET" ]; then |
||||||
|
echo "Specify a target!" |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
|
||||||
|
DEFAULT_PORT=$PLUGIN_PORT |
||||||
|
if [ -z "$PLUGIN_PORT" ]; then |
||||||
|
echo "Port not specified, using default port 22!" |
||||||
|
DEFAULT_PORT="22" |
||||||
|
fi |
||||||
|
|
||||||
|
SOURCE=$PLUGIN_SOURCE |
||||||
|
if [ -z "$PLUGIN_SOURCE" ]; then |
||||||
|
echo "No source folder specified, using default './'" |
||||||
|
SOURCE="./" |
||||||
|
fi |
||||||
|
|
||||||
|
USER=$RSYNC_USER |
||||||
|
if [ -z "$RSYNC_USER" ]; then |
||||||
|
if [ -z "$PLUGIN_USER" ]; then |
||||||
|
echo "No user specified, using root!" |
||||||
|
USER="root" |
||||||
|
else |
||||||
|
USER=$PLUGIN_USER |
||||||
|
fi |
||||||
|
fi |
||||||
|
|
||||||
|
SSH_KEY=$RSYNC_KEY |
||||||
|
if [ -z "$RSYNC_KEY" ]; then |
||||||
|
if [ -z "$PLUGIN_KEY" ]; then |
||||||
|
echo "No private key specified!" |
||||||
|
exit 1 |
||||||
|
fi |
||||||
|
SSH_KEY=$PLUGIN_KEY |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$PLUGIN_ARGS" ]; then |
||||||
|
ARGS= |
||||||
|
else |
||||||
|
ARGS=$PLUGIN_ARGS |
||||||
|
fi |
||||||
|
|
||||||
|
if [ -z "$PLUGIN_LOG_LEVEL" ]; then |
||||||
|
LOG_LEVEL=quiet |
||||||
|
else |
||||||
|
LOG_LEVEL=$PLUGIN_LOG_LEVEL |
||||||
|
fi |
||||||
|
|
||||||
|
# Building rsync command |
||||||
|
expr="rsync -az $ARGS" |
||||||
|
|
||||||
|
if [[ -n "$PLUGIN_RECURSIVE" && "$PLUGIN_RECURSIVE" == "true" ]]; then |
||||||
|
expr="$expr -r" |
||||||
|
fi |
||||||
|
|
||||||
|
if [[ -n "$PLUGIN_DELETE" && "$PLUGIN_DELETE" == "true" ]]; then |
||||||
|
expr="$expr --del" |
||||||
|
fi |
||||||
|
|
||||||
|
expr="$expr -e 'ssh -p %s -o UserKnownHostsFile=/dev/null -o LogLevel=$LOG_LEVEL -o StrictHostKeyChecking=no'" |
||||||
|
|
||||||
|
# Include |
||||||
|
IFS=','; read -ra INCLUDE <<< "$PLUGIN_INCLUDE" |
||||||
|
for include in "${INCLUDE[@]}"; do |
||||||
|
expr="$expr --include=$include" |
||||||
|
done |
||||||
|
|
||||||
|
# Exclude |
||||||
|
IFS=','; read -ra EXCLUDE <<< "$PLUGIN_EXCLUDE" |
||||||
|
for exclude in "${EXCLUDE[@]}"; do |
||||||
|
expr="$expr --exclude=$exclude" |
||||||
|
done |
||||||
|
|
||||||
|
# Filter |
||||||
|
IFS=','; read -ra FILTER <<< "$PLUGIN_FILTER" |
||||||
|
for filter in "${FILTER[@]}"; do |
||||||
|
expr="$expr --filter=$filter" |
||||||
|
done |
||||||
|
|
||||||
|
expr="$expr $SOURCE" |
||||||
|
|
||||||
|
# Prepare SSH |
||||||
|
home="/root" |
||||||
|
|
||||||
|
mkdir -p "$home/.ssh" |
||||||
|
|
||||||
|
printf "StrictHostKeyChecking no\n" > "$home/.ssh/config" |
||||||
|
chmod 0700 "$home/.ssh/config" |
||||||
|
|
||||||
|
keyfile="$home/.ssh/id_rsa" |
||||||
|
echo "$SSH_KEY" | grep -q "ssh-ed25519" |
||||||
|
if [ $? -eq 0 ]; then |
||||||
|
printf "Using ed25519 based key\n" |
||||||
|
keyfile="$home/.ssh/id_ed25519" |
||||||
|
fi |
||||||
|
echo "$SSH_KEY" | grep -q "ecdsa-" |
||||||
|
if [ $? -eq 0 ]; then |
||||||
|
printf "Using ecdsa based key\n" |
||||||
|
keyfile="$home/.ssh/id_ecdsa" |
||||||
|
fi |
||||||
|
echo "$SSH_KEY" > $keyfile |
||||||
|
chmod 0600 $keyfile |
||||||
|
|
||||||
|
function join_with { local d=$1; shift; echo -n "$1"; shift; printf "%s" "${@/#/$d}"; } |
||||||
|
|
||||||
|
# Parse SSH precommands |
||||||
|
IFS=','; read -ra COMMANDS <<< "$PLUGIN_PRESCRIPT" |
||||||
|
prescript=$(join_with ' && ' "${COMMANDS[@]}") |
||||||
|
# Parse SSH postcommands |
||||||
|
IFS=','; read -ra COMMANDS <<< "$PLUGIN_SCRIPT" |
||||||
|
postscript=$(join_with ' && ' "${COMMANDS[@]}") |
||||||
|
|
||||||
|
# Run rsync |
||||||
|
IFS=','; read -ra HOSTS <<< "$PLUGIN_HOSTS" |
||||||
|
IFS=','; read -ra PORTS <<< "$PLUGIN_PORTS" |
||||||
|
result=0 |
||||||
|
for ((i=0; i < ${#HOSTS[@]}; i++)) |
||||||
|
do |
||||||
|
HOST=${HOSTS[$i]} |
||||||
|
PORT=${PORTS[$i]} |
||||||
|
if [ -z $PORT ] |
||||||
|
then |
||||||
|
# Default Port 22 |
||||||
|
PORT=$DEFAULT_PORT |
||||||
|
fi |
||||||
|
echo $(printf "%s" "$ $(printf "$expr" "$PORT") $USER@$HOST:$PLUGIN_TARGET ...") |
||||||
|
if [ -n "$PLUGIN_PRESCRIPT" ]; then |
||||||
|
echo $(printf "%s" "$ ssh -p $PORT $USER@$HOST ...") |
||||||
|
echo $(printf "%s" " > $prescript ...") |
||||||
|
eval "ssh -p $PORT $USER@$HOST '$prescript'" |
||||||
|
result=$(($result+$?)) |
||||||
|
echo $(printf "%s" "$ ssh -p $PORT $USER@$HOST result: $?") |
||||||
|
if [ "$result" -gt "0" ]; then exit $result; fi |
||||||
|
fi |
||||||
|
eval "$(printf "$expr" "$PORT") $USER@$HOST:$PLUGIN_TARGET" |
||||||
|
result=$(($result+$?)) |
||||||
|
if [ "$result" -gt "0" ]; then exit $result; fi |
||||||
|
if [ -n "$PLUGIN_SCRIPT" ]; then |
||||||
|
echo $(printf "%s" "$ ssh -p $PORT $USER@$HOST ...") |
||||||
|
echo $(printf "%s" " > $postscript ...") |
||||||
|
eval "ssh -p $PORT $USER@$HOST '$postscript'" |
||||||
|
result=$(($result+$?)) |
||||||
|
echo $(printf "%s" "$ ssh -p $PORT $USER@$HOST result: $?") |
||||||
|
if [ "$result" -gt "0" ]; then exit $result; fi |
||||||
|
fi |
||||||
|
done |
||||||
|
exit $result |
Loading…
Reference in new issue