├── .dockerignore ├── .editorconfig ├── .gitignore ├── Dockerfile ├── README.md ├── exampleBuild.sh └── stack-update.sh /.dockerignore: -------------------------------------------------------------------------------- 1 | # Git files 2 | .git 3 | .gitignore 4 | 5 | # Local config files 6 | .editorconfig 7 | 8 | #build files 9 | build.sh 10 | run.sh 11 | buildCache.sh 12 | 13 | # Information files 14 | LICENSE 15 | README.md 16 | 17 | #Jetbrains 18 | .idea 19 | *.iml 20 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig is awesome: http://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root = true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | charset = utf-8 11 | trim_trailing_whitespace = true 12 | indent_style = space 13 | indent_size = 2 14 | 15 | # 4 space indentation 16 | [*.php] 17 | indent_style = space 18 | indent_size = 4 19 | 20 | # 2 space indentation 21 | [*.js] 22 | indent_style = space 23 | indent_size = 2 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | #Jetbrains 2 | .idea 3 | *.iml 4 | 5 | # Created by .ignore support plugin (hsz.mobi) 6 | ### macOS template 7 | # General 8 | .DS_Store 9 | .AppleDouble 10 | .LSOverride 11 | 12 | # Icon must end with two \r 13 | Icon 14 | 15 | # Thumbnails 16 | ._* 17 | 18 | # Files that might appear in the root of a volume 19 | .DocumentRevisions-V100 20 | .fseventsd 21 | .Spotlight-V100 22 | .TemporaryItems 23 | .Trashes 24 | .VolumeIcon.icns 25 | .com.apple.timemachine.donotpresent 26 | 27 | # Directories potentially created on remote AFP share 28 | .AppleDB 29 | .AppleDesktop 30 | Network Trash Folder 31 | Temporary Items 32 | .apdisk 33 | ### Windows template 34 | # Windows thumbnail cache files 35 | Thumbs.db 36 | ehthumbs.db 37 | ehthumbs_vista.db 38 | 39 | # Dump file 40 | *.stackdump 41 | 42 | # Folder config file 43 | [Dd]esktop.ini 44 | 45 | # Recycle Bin used on file shares 46 | $RECYCLE.BIN/ 47 | 48 | # Windows Installer files 49 | *.cab 50 | *.msi 51 | *.msix 52 | *.msm 53 | *.msp 54 | 55 | # Windows shortcuts 56 | *.lnk 57 | 58 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM alpine 2 | 3 | ENV LANG="en_US.UTF-8" \ 4 | LC_ALL="C.UTF-8" \ 5 | LANGUAGE="en_US.UTF-8" \ 6 | TERM="xterm" 7 | 8 | RUN apk --update add \ 9 | curl \ 10 | bash \ 11 | ca-certificates \ 12 | jq \ 13 | vim \ 14 | && \ 15 | rm -rf /tmp/src && \ 16 | rm -rf /var/cache/apk/* 17 | 18 | ADD ./stack-update.sh /stack-update.sh 19 | 20 | RUN chmod u+x /stack-update.sh 21 | 22 | EXPOSE 80 23 | 24 | ENV P_USER="root" \ 25 | P_PASS="password" \ 26 | P_URL="http://example.com:9000" \ 27 | P_PRUNE="false" 28 | 29 | ENTRYPOINT /stack-update.sh 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Bash scripts to interact with Portainer 2 | 3 | # [Stack Update {stack-name} {composer-file.yml}](https://github.com/docker-how-to/portainer-bash-scripts/blob/master/stack-update.sh) 4 | ... will update a running stack with the name given as first parameter with the yml content from the file given as the second parameter 5 | 6 | Requires: 7 | * bash (or sh) 8 | * jq 9 | * curl 10 | 11 | Usage: 12 | 13 | * Set the following environmental variables or edit file and set the authentication details 14 | ```bash 15 | P_USER="root" 16 | P_PASS="password" 17 | P_URL="http://example.com:9000" 18 | P_PRUNE="false" 19 | ``` 20 | 21 | * run with 22 | ```bash 23 | export P_USER="root" 24 | export P_PASS="password" 25 | export P_URL="http://example.com:9000" 26 | export P_PRUNE="false" 27 | ./stack-update.sh mqtt mqtt/docker-compose.yml 28 | ``` 29 | -------------------------------------------------------------------------------- /exampleBuild.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | IMAGE_NAME="docker-how-to/portainer-bash-scripts" 3 | docker build --pull --rm -t ${IMAGE_NAME} . 4 | -------------------------------------------------------------------------------- /stack-update.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | P_USER=${P_USER:-"root"} 3 | P_PASS=${P_PASS:-"rootroot"} 4 | P_URL=${P_URL:-"http://10.11.9.200:9000"} 5 | P_PRUNE=${P_PRUNE:-"false"} 6 | 7 | if [ -z ${1+x} ]; then 8 | echo "Parameter #1 missing: stack name " 9 | exit 1 10 | fi 11 | TARGET="$1" 12 | 13 | if [ -z ${2+x} ]; then 14 | echo "Parameter #2 missing: path to yml" 15 | exit 16 | fi 17 | TARGET_YML="$2" 18 | 19 | echo "Updating $TARGET" 20 | 21 | echo "Logging in..." 22 | P_TOKEN=$(curl -s -X POST -H "Content-Type: application/json;charset=UTF-8" -d "{\"username\":\"$P_USER\",\"password\":\"$P_PASS\"}" "$P_URL/api/auth") 23 | if [[ $P_TOKEN = *"jwt"* ]]; then 24 | echo " ... success" 25 | else 26 | echo "Result: failed to login" 27 | exit 1 28 | fi 29 | T=$(echo $P_TOKEN | awk -F '"' '{print $4}') 30 | echo "Token: $T" 31 | 32 | INFO=$(curl -s -H "Authorization: Bearer $T" "$P_URL/api/endpoints/1/docker/info") 33 | CID=$(echo "$INFO" | awk -F '"Cluster":{"ID":"' '{print $2}' | awk -F '"' '{print $1}') 34 | echo "Cluster ID: $CID" 35 | 36 | echo "Getting stacks..." 37 | STACKS=$(curl -s -H "Authorization: Bearer $T" "$P_URL/api/stacks") 38 | 39 | #echo "/---" && echo $STACKS && echo "\\---" 40 | 41 | found=0 42 | stack=$(echo "$STACKS"|jq --arg TARGET "$TARGET" -jc '.[]| select(.Name == $TARGET)') 43 | 44 | if [ -z "$stack" ];then 45 | echo "Result: Stack not found." 46 | exit 1 47 | fi 48 | sid="$(echo "$stack" |jq -j ".Id")" 49 | name=$(echo "$stack" |jq -j ".Name") 50 | 51 | found=1 52 | echo "Identified stack: $sid / $name" 53 | 54 | existing_env_json="$(echo -n "$stack"|jq ".Env" -jc)" 55 | 56 | dcompose=$(cat "$TARGET_YML") 57 | dcompose="${dcompose//$'\r'/''}" 58 | dcompose="${dcompose//$'"'/'\"'}" 59 | echo "/-----READ_YML--------" 60 | 61 | echo "$dcompose" 62 | echo "\---------------------" 63 | dcompose="${dcompose//$'\n'/'\n'}" 64 | data_prefix="{\"Id\":\"$sid\",\"StackFileContent\":\"" 65 | data_suffix="\",\"Env\":"$existing_env_json",\"Prune\":$P_PRUNE}" 66 | sep="'" 67 | echo "/~~~~CONVERTED_JSON~~~~~~" 68 | echo "$data_prefix$dcompose$data_suffix" 69 | echo "\~~~~~~~~~~~~~~~~~~~~~~~~" 70 | echo "$data_prefix$dcompose$data_suffix" > json.tmp 71 | 72 | echo "Updating stack..." 73 | UPDATE=$(curl -s \ 74 | "$P_URL/api/stacks/$sid?endpointId=1" \ 75 | -X PUT \ 76 | -H "Authorization: Bearer $T" \ 77 | -H "Content-Type: application/json;charset=UTF-8" \ 78 | -H 'Cache-Control: no-cache' \ 79 | --data-binary "@json.tmp" 80 | ) 81 | rm json.tmp 82 | echo "Got response: $UPDATE" 83 | if [ -z ${UPDATE+x} ]; then 84 | echo "Result: failure to update" 85 | exit 1 86 | else 87 | echo "Result: successfully updated" 88 | exit 0 89 | fi 90 | 91 | 92 | if [ "$found" == "1" ]; then 93 | echo "Result: found stack but failed to process" 94 | exit 1 95 | else 96 | echo "Result: fail" 97 | exit 1 98 | fi 99 | 100 | --------------------------------------------------------------------------------