Skip to content

Commit d90eb42

Browse files
authored
Merge pull request #8 from tortuetorche/patch-1
Add custom stack environment variables
2 parents e446fb6 + a692212 commit d90eb42

File tree

3 files changed

+97
-43
lines changed

3 files changed

+97
-43
lines changed

Dockerfile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ ENV LANG="en_US.UTF-8" \
1010
PORTAINER_URL="" \
1111
PORTAINER_STACK_NAME="" \
1212
DOCKER_COMPOSE_FILE="" \
13+
ENVIRONMENT_VARIABLES_FILE="" \
1314
PORTAINER_PRUNE="false" \
1415
PORTAINER_ENDPOINT="1" \
1516
HTTPIE_VERIFY_SSL="yes" \

README.md

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ This is particularly useful for CI/CD pipelines.
4444
- `PORTAINER_URL` (string, required): URL to Portainer
4545
- `PORTAINER_STACK_NAME` (string, required): Stack name
4646
- `DOCKER_COMPOSE_FILE` (string, required if action=deploy): Path to doker-compose file
47+
- `ENVIRONMENT_VARIABLES_FILE` (string, optional, only used when action=deploy or action=update): Path to file with environment variables to be used by the stack. See [stack environment variables](#stack-environment-variables) below.
4748
- `PORTAINER_PRUNE` ("true" or "false", optional): Whether to prune unused containers or not. Defaults to `"false"`.
4849
- `PORTAINER_ENDPOINT` (int, optional): Which endpoint to use. Defaults to `1`.
4950
- `HTTPIE_VERIFY_SSL` ("yes" or "no", optional): Whether to verify SSL certificate or not. Defaults to `"yes"`.
@@ -60,6 +61,7 @@ export PORTAINER_PASSWORD="password"
6061
export PORTAINER_URL="http://portainer.local"
6162
export PORTAINER_STACK_NAME="mystack"
6263
export DOCKER_COMPOSE_FILE="/path/to/docker-compose.yml"
64+
export ENVIRONMENT_VARIABLES_FILE="/path/to/env_vars_file"
6365

6466
./psu
6567
```
@@ -84,6 +86,7 @@ This is more suitable for standalone script usage.
8486
- `-l` (string, required): URL to Portainer
8587
- `-n` (string, required): Stack name
8688
- `-c` (string, required if action=deploy): Path to doker-compose file
89+
- `-g` (string, optional, only used when action=deploy or action=update): Path to file with environment variables to be used by the stack. See [stack environment variables](#stack-environment-variables) below.
8790
- `-r` ("true" or "false", optional): Whether to prune unused containers or not. Defaults to `"false"`.
8891
- `-e` (int, optional): Which endpoint to use. Defaults to `1`.
8992
- `-s` ("yes" or "no", optional): Whether to verify SSL certificate or not. Defaults to `"yes"`.
@@ -94,13 +97,26 @@ This is more suitable for standalone script usage.
9497
#### Examples
9598

9699
```bash
97-
./psu -a deploy -u admin -p password -l http://portainer.local -n mystack -c /path/to/docker-compose.yml
100+
./psu -a deploy -u admin -p password -l http://portainer.local -n mystack -c /path/to/docker-compose.yml -g /path/to/env_vars_file
98101
```
99102

100103
```bash
101104
./psu -a undeploy -u admin -p password -l http://portainer.local -n mystack
102105
```
103106

107+
### Stack environment variables
108+
109+
There can be set environment variables for each stack, be it a new deployment or an update. For example:
110+
111+
```bash
112+
touch .env
113+
echo "MYSQL_ROOT_PASSWORD=agoodpassword" >> .env
114+
echo "ALLOWED_HOSTS=*" >> .env
115+
./psu -a deploy -u admin -p password -l http://portainer.local -n django-stack -c /path/to/docker-compose.yml -g env_vars
116+
```
117+
118+
Stack environment variables can be enabled through [ENVIRONMENT_VARIABLES_FILE envvar](#with-envvars) or [-g flag](#with-flags).
119+
104120
### Verbose mode
105121

106122
In verbose mode the script prints execution steps.

psu

Lines changed: 79 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -66,26 +66,27 @@ main() {
6666
exit 1
6767
}
6868

69-
##########################
70-
# Set globals #
71-
# Globals: #
72-
# ACTION #
73-
# PORTAINER_USER #
74-
# PORTAINER_PASSWORD #
75-
# PORTAINER_URL #
76-
# PORTAINER_STACK_NAME #
77-
# DOCKER_COMPOSE_FILE #
78-
# PORTAINER_ENDPOINT #
79-
# PORTAINER_PRUNE #
80-
# HTTPIE_VERIFY_SSL #
81-
# VERBOSE_MODE #
82-
# DEBUG_MODE #
83-
# STRICT_MODE #
84-
# Arguments: #
85-
# None #
86-
# Returns: #
87-
# None #
88-
##########################
69+
################################
70+
# Set globals #
71+
# Globals: #
72+
# ACTION #
73+
# PORTAINER_USER #
74+
# PORTAINER_PASSWORD #
75+
# PORTAINER_URL #
76+
# PORTAINER_STACK_NAME #
77+
# DOCKER_COMPOSE_FILE #
78+
# ENVIRONMENT_VARIABLES_FILE #
79+
# PORTAINER_ENDPOINT #
80+
# PORTAINER_PRUNE #
81+
# HTTPIE_VERIFY_SSL #
82+
# VERBOSE_MODE #
83+
# DEBUG_MODE #
84+
# STRICT_MODE #
85+
# Arguments: #
86+
# None #
87+
# Returns: #
88+
# None #
89+
################################
8990
set_globals() {
9091
# Set arguments through envvars
9192
ACTION=${ACTION}
@@ -94,6 +95,7 @@ set_globals() {
9495
PORTAINER_URL=${PORTAINER_URL}
9596
PORTAINER_STACK_NAME=${PORTAINER_STACK_NAME}
9697
DOCKER_COMPOSE_FILE=${DOCKER_COMPOSE_FILE}
98+
ENVIRONMENT_VARIABLES_FILE=${ENVIRONMENT_VARIABLES_FILE}
9799
PORTAINER_ENDPOINT=${PORTAINER_ENDPOINT:-"1"}
98100
PORTAINER_PRUNE=${PORTAINER_PRUNE:-"false"}
99101
HTTPIE_VERIFY_SSL=${HTTPIE_VERIFY_SSL:-"yes"}
@@ -102,7 +104,7 @@ set_globals() {
102104
STRICT_MODE=${STRICT_MODE:-"false"}
103105

104106
# Set arguments through flags (overwrite envvars)
105-
while getopts a:u:p:l:n:c:e:rsvdt option; do
107+
while getopts a:u:p:l:n:c:e:g:rsvdt option; do
106108
case "${option}" in
107109
a) ACTION=${OPTARG} ;;
108110
u) PORTAINER_USER=${OPTARG} ;;
@@ -111,6 +113,7 @@ set_globals() {
111113
n) PORTAINER_STACK_NAME=${OPTARG} ;;
112114
c) DOCKER_COMPOSE_FILE=${OPTARG} ;;
113115
e) PORTAINER_ENDPOINT=${OPTARG} ;;
116+
g) ENVIRONMENT_VARIABLES_FILE=${OPTARG} ;;
114117
r) PORTAINER_PRUNE="true" ;;
115118
s) HTTPIE_VERIFY_SSL="no" ;;
116119
v) VERBOSE_MODE="true" ;;
@@ -130,6 +133,7 @@ set_globals() {
130133
echo_debug "PORTAINER_URL -> $PORTAINER_URL"
131134
echo_debug "PORTAINER_STACK_NAME -> $PORTAINER_STACK_NAME"
132135
echo_debug "DOCKER_COMPOSE_FILE -> $DOCKER_COMPOSE_FILE"
136+
echo_debug "ENVIRONMENT_VARIABLES_FILE -> $ENVIRONMENT_VARIABLES_FILE"
133137
echo_debug "PORTAINER_ENDPOINT -> $PORTAINER_ENDPOINT"
134138
echo_debug "PORTAINER_PRUNE -> $PORTAINER_PRUNE"
135139
echo_debug "HTTPIE_VERIFY_SSL -> $HTTPIE_VERIFY_SSL"
@@ -145,6 +149,10 @@ set_globals() {
145149
check_argument "$PORTAINER_STACK_NAME" "portainer stack name" "PORTAINER_STACK_NAME" "n"
146150
if [ $ACTION == "deploy" ]; then
147151
check_argument "$DOCKER_COMPOSE_FILE" "docker compose file" "DOCKER_COMPOSE_FILE" "c"
152+
if [ -n "$ENVIRONMENT_VARIABLES_FILE" ] && [[ ! -f "$ENVIRONMENT_VARIABLES_FILE" ]]; then
153+
echo_error "Error: File path \"$ENVIRONMENT_VARIABLES_FILE\" not found for \"ENVIRONMENT_VARIABLES_FILE\" environment variable or the \"-g\" flag."
154+
exit 1
155+
fi
148156
fi
149157
}
150158

@@ -254,21 +262,22 @@ echo_debug() {
254262
fi
255263
}
256264

257-
##########################
258-
# Create/update a stack #
259-
# Globals: #
260-
# STACK #
261-
# DOCKER_COMPOSE_FILE #
262-
# PORTAINER_STACK_NAME #
263-
# PORTAINER_URL #
264-
# HTTPIE_VERIFY_SSL #
265-
# PORTAINER_ENDPOINT #
266-
# AUTH_TOKEN #
267-
# Arguments: #
268-
# None #
269-
# Returns: #
270-
# None #
271-
##########################
265+
################################
266+
# Create/update a stack #
267+
# Globals: #
268+
# STACK #
269+
# DOCKER_COMPOSE_FILE #
270+
# PORTAINER_STACK_NAME #
271+
# PORTAINER_URL #
272+
# ENVIRONMENT_VARIABLES_FILE #
273+
# HTTPIE_VERIFY_SSL #
274+
# PORTAINER_ENDPOINT #
275+
# AUTH_TOKEN #
276+
# Arguments: #
277+
# None #
278+
# Returns: #
279+
# None #
280+
################################
272281
deploy() {
273282
# Read docker-compose file content
274283
local docker_compose_file_content
@@ -304,14 +313,19 @@ deploy() {
304313
local swarm_id
305314
swarm_id=$(echo $docker_info | jq -r ".Swarm.Cluster.ID // empty")
306315
echo_debug "Swarm ID -> $swarm_id"
307-
316+
308317
# If there is no swarm ID
309318
if [ -z "$swarm_id" ];then
310319
echo_verbose "Swarm cluster not found."
311320

312321
echo_verbose "Preparing stack JSON..."
322+
local stack_envvars
323+
stack_envvars="[]"
324+
if [ -n "$ENVIRONMENT_VARIABLES_FILE" ]; then
325+
stack_envvars=$(env_file_to_json)
326+
fi
313327
local data_prefix="{\"Name\":\"$PORTAINER_STACK_NAME\",\"StackFileContent\":\""
314-
local data_suffix="\"}"
328+
local data_suffix="\",\"Env\":"$stack_envvars"}"
315329
echo "$data_prefix$docker_compose_file_content$data_suffix" > json.tmp
316330
echo_debug "Stack JSON -> $(echo $data_prefix$docker_compose_file_content$data_suffix | jq -C .)"
317331

@@ -335,8 +349,13 @@ deploy() {
335349
echo_verbose "Swarm cluster found."
336350

337351
echo_verbose "Preparing stack JSON..."
352+
local stack_envvars
353+
stack_envvars="[]"
354+
if [ -n "$ENVIRONMENT_VARIABLES_FILE" ]; then
355+
stack_envvars=$(env_file_to_json)
356+
fi
338357
local data_prefix="{\"Name\":\"$PORTAINER_STACK_NAME\",\"SwarmID\":\"$swarm_id\",\"StackFileContent\":\""
339-
local data_suffix="\"}"
358+
local data_suffix="\",\"Env\":"$stack_envvars"}"
340359
echo "$data_prefix$docker_compose_file_content$data_suffix" > json.tmp
341360
echo_debug "Stack JSON -> $(echo $data_prefix$docker_compose_file_content$data_suffix | jq -C .)"
342361

@@ -370,12 +389,17 @@ deploy() {
370389
local stack_id
371390
stack_id="$(echo "$STACK" | jq -j ".Id")"
372391
local stack_envvars
373-
stack_envvars="$(echo -n "$STACK"| jq ".Env" -jc)"
392+
stack_envvars="$(echo -n "$STACK" | jq ".Env" -jc)"
393+
if [ -n "$ENVIRONMENT_VARIABLES_FILE" ]; then
394+
local new_stack_envvars
395+
new_stack_envvars=$(env_file_to_json)
396+
stack_envvars="$(echo -n "${new_stack_envvars}${stack_envvars}" | jq -sjc 'add | unique_by(.name)')"
397+
fi
374398
local data_prefix="{\"Id\":\"$stack_id\",\"StackFileContent\":\""
375399
local data_suffix="\",\"Env\":"$stack_envvars",\"Prune\":$PORTAINER_PRUNE}"
376400
echo "$data_prefix$docker_compose_file_content$data_suffix" > json.tmp
377401
echo_debug "Stack JSON -> $(echo $data_prefix$docker_compose_file_content$data_suffix | jq -C .)"
378-
402+
379403
# Update stack
380404
echo_verbose "Updating stack $PORTAINER_STACK_NAME..."
381405
local update
@@ -390,7 +414,7 @@ deploy() {
390414
@json.tmp)
391415
check_for_errors $? "$update"
392416
echo_debug "Update action response -> $(echo $update | jq -C .)"
393-
417+
394418
rm json.tmp
395419
fi
396420
}
@@ -436,4 +460,17 @@ undeploy() {
436460
echo_debug "Delete action response -> $(echo $delete | jq -C .)"
437461
}
438462

463+
###################################################
464+
# Convert environment variables from file to JSON #
465+
# Globals: #
466+
# ENVIRONMENT_VARIABLES_FILE #
467+
# Arguments: #
468+
# None #
469+
# Returns: #
470+
# JSON string #
471+
###################################################
472+
env_file_to_json() {
473+
echo "$(env -i $(cat $ENVIRONMENT_VARIABLES_FILE) jq -n 'env | to_entries | map({name: .key, value: .value})')"
474+
}
475+
439476
main "$@"

0 commit comments

Comments
 (0)