diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee2b82b..2c16de4 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]
+### Changed
+- **Breaking Change**: [HTTPie](https://httpie.io/) is replaced by [cURL](https://curl.se), for smaller Docker images, faster execution and to be more portable
+
+### Fixed
+- Running concurrently `psu` commands should work now, by creating unique temporary file names
+
## [1.2.0] - 2021-09-14
### Added
- Add tests for `actions`, `containers` and `services` actions
@@ -151,7 +157,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Debug mode
- Strict mode
-[Unreleased]: https://gitlab.com/psuapp/psu/compare/v1.2.0...1-2-stable
+[Unreleased]: https://gitlab.com/psuapp/psu/compare/v1.2.0...1-3-next
[1.2.0]: https://gitlab.com/psuapp/psu/-/tags/v1.2.0
[1.2.0-beta.1]: https://gitlab.com/psuapp/psu/-/tags/v1.2.0-beta.1
[1.2.0-alpha]: https://gitlab.com/psuapp/psu/-/tags/v1.2.0-alpha
diff --git a/Dockerfile b/Dockerfile
index 609d38a..677372c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -2,7 +2,7 @@ FROM alpine:3.14
RUN set -e; \
apk add --no-cache \
- bash ca-certificates docker-compose gettext httpie jq; \
+ bash ca-certificates curl docker-compose gettext jq; \
rm -rf $(find / -regex '.*\.py[co]')
ENV LANG="en_US.UTF-8" \
diff --git a/Dockerfile.core b/Dockerfile.core
index ca61667..704c313 100644
--- a/Dockerfile.core
+++ b/Dockerfile.core
@@ -2,8 +2,7 @@ FROM alpine:3.14
RUN set -e; \
apk add --no-cache \
- bash ca-certificates gettext httpie jq py3-setuptools; \
- rm -rf $(find / -regex '.*\.py[co]')
+ bash ca-certificates curl gettext jq
ENV LANG="en_US.UTF-8" \
LC_ALL="C.UTF-8" \
diff --git a/Dockerfile.debian b/Dockerfile.debian
index aede26e..d292017 100644
--- a/Dockerfile.debian
+++ b/Dockerfile.debian
@@ -5,12 +5,11 @@ ARG DOCKER_COMPOSE_VERSION=1.29.2
RUN set -e; \
apt-get update -yqq; \
apt-get install \
- ca-certificates gettext-base httpie jq curl -yqq; \
+ ca-certificates curl gettext-base jq -yqq; \
\
- curl -fL "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose; \
+ curl --fail --silent --location "https://github.com/docker/compose/releases/download/${DOCKER_COMPOSE_VERSION}/docker-compose-$(uname -s)-$(uname -m)" -o /usr/local/bin/docker-compose; \
chmod +x /usr/local/bin/docker-compose; \
\
- apt-get purge curl -y; \
apt-get clean; \
rm -rf /var/lib/apt/lists/*; \
rm -rf $(find / -regex '.*\.py[co]')
diff --git a/Dockerfile.debian-core b/Dockerfile.debian-core
index 24c80f3..b755a1c 100644
--- a/Dockerfile.debian-core
+++ b/Dockerfile.debian-core
@@ -3,11 +3,10 @@ FROM debian:11-slim
RUN set -e; \
apt-get update -yqq; \
apt-get install \
- ca-certificates gettext-base httpie jq -yqq; \
+ ca-certificates curl gettext-base jq -yqq; \
\
apt-get clean; \
- rm -rf /var/lib/apt/lists/*; \
- rm -rf $(find / -regex '.*\.py[co]')
+ rm -rf /var/lib/apt/lists/*
ENV LANG="en_US.UTF-8" \
LC_ALL="C.UTF-8" \
diff --git a/README.md b/README.md
index e060c5a..9c79cea 100644
--- a/README.md
+++ b/README.md
@@ -49,12 +49,15 @@ For detailed instructions, see [How to use](#how-to-use) section.
You will need these dependencies installed:
-- [bash](https://www.gnu.org/software/bash/)
-- [httpie](https://httpie.org/)
-- [jq](https://stedolan.github.io/jq/)
-- [timeout](https://man7.org/linux/man-pages/man1/timeout.1.html) For macOS run: `brew install coreutils`
+- [bash](https://www.gnu.org/software/bash/)* (>= 5.0.3)
+- [curl](https://curl.se/)* (>= 7.64.0, but >= 7.76.0 is recommended)
+- [jq](https://stedolan.github.io/jq/)* (>= 1.5.1)
+- [timeout](https://man7.org/linux/man-pages/man1/timeout.1.html)* For macOS run: `brew install coreutils`
+- [uuidgen](https://man7.org/linux/man-pages/man1/uuidgen.1.html) only for some OS (Debian and Alpine work fine without it)
-For Debian and similar apt-powered systems: `apt install bash httpie jq`
+* = required
+
+For Debian and similar apt-powered systems: `apt install bash curl jq`
### Docker image and variants
diff --git a/psu b/psu
index f231cae..d6e535b 100755
--- a/psu
+++ b/psu
@@ -115,12 +115,10 @@ main() {
# Get list of all stacks
echo_verbose "Getting stack $PORTAINER_STACK_NAME..."
- STACKS=$(http \
- --check-status \
- --ignore-stdin \
- --verify=$HTTPIE_VERIFY_SSL \
- "$PORTAINER_URL/api/stacks" \
- "Authorization: Bearer $PORTAINER_AUTH_TOKEN")
+ STACKS=$(curl_wrapper \
+ --request GET \
+ --header "Authorization: Bearer ${PORTAINER_AUTH_TOKEN}" \
+ "${PORTAINER_URL}/api/stacks")
check_for_errors $? "$STACKS"
echo_debug_safe_json "Get stacks response -> $(echo $STACKS | jq -C .)" "$STACKS"
@@ -187,8 +185,7 @@ main() {
# or desired state == 'shutdown' and state == 'complete'
# Tasks should not have one of these states:
# 'failed', 'orphaned', 'remove'
-
- timeout $TIMEOUT bash -c "until (export PORTAINER_SERVICE_NAME=$PORTAINER_SERVICE_NAME && export DEBUG_MODE=false && export VERBOSE_MODE=false && psu -a tasks:healthy -q) >/dev/null 2>&1; do echo -n \$(if [ \"\$VERBOSE_MODE\" == \"true\" ]; then echo -n .; fi) && sleep 1; done;"
+ timeout "$TIMEOUT" bash -c "until (export PORTAINER_SERVICE_NAME=$PORTAINER_SERVICE_NAME && psu --action=tasks:healthy --quiet --debug=false --verbose=false) >/dev/null 2>&1; do echo -n \$(if [ \"\$VERBOSE_MODE\" == \"true\" ]; then echo -n .; fi) && sleep 1; done;"
status=$?
if $(exit $status); then
@@ -460,8 +457,10 @@ inputs() {
-C=*|-F=*|--compose-file-base64=*|--file-base64=*|-C|-F|--compose-file-base64|--file-base64)
local docker_compose_file_base64
docker_compose_file_base64=$(input_option "$1" "$2")
- echo "$docker_compose_file_base64" | base64 -d > docker_compose_file_from_base64.yml
- DOCKER_COMPOSE_FILE=docker_compose_file_from_base64.yml
+ local docker_compose_file_from_base64_path
+ docker_compose_file_from_base64_path="$(unique_temp_file_path)"
+ echo "$docker_compose_file_base64" | base64 -d > "$docker_compose_file_from_base64_path"
+ DOCKER_COMPOSE_FILE="$docker_compose_file_from_base64_path"
if [ -n "$2" ] && [[ ! $2 =~ ^-.+$ ]] ; then
# When the second argument is the value of the current option
shift
@@ -484,8 +483,10 @@ inputs() {
-G=*|--env-file-base64=*|-G|--env-file-base64)
local env_file_base64
env_file_base64=$(input_option "$1" "$2")
- echo "$env_file_base64" | base64 -d > env_file_from_base64
- ENVIRONMENT_VARIABLES_FILE=env_file_from_base64
+ local env_file_from_base64_path
+ env_file_from_base64_path="$(unique_temp_file_path)"
+ echo "$env_file_base64" | base64 -d > "$env_file_from_base64_path"
+ ENVIRONMENT_VARIABLES_FILE="$env_file_from_base64_path"
if [ -n "$2" ] && [[ ! $2 =~ ^-.+$ ]] ; then
# When the second argument is the value of the current option
shift
@@ -741,6 +742,7 @@ echo_error() {
local error_message="$@"
local red='\033[0;31m'
local nc='\033[0m'
+
echo -e "${red}[$(date +'%Y-%m-%dT%H:%M:%S%z')]: ${error_message}${nc}" >&2
}
@@ -785,18 +787,15 @@ check_for_errors() {
local response=$2
if [ $exit_code -ne 0 ]; then
case $exit_code in
- 2) echo_error 'Request timed out!' ;;
- 3) echo_error 'Unexpected HTTP 3xx Redirection!' ;;
- 4)
- echo_error 'HTTP 4xx Client Error!'
- echo_error $response
+ 22)
+ echo_error 'HTTP 4xx or 5xx Client Error!'
+ echo_error "$response"
+ ;;
+ 28) echo_error 'Request timed out!' ;;
+ 47) echo_error 'Exceeded --max-redirs redirects!' ;;
+ *)
+ echo_error 'Unholy Error!'
;;
- 5)
- echo_error 'HTTP 5xx Server Error!'
- echo_error $response
- ;;
- 6) echo_error 'Exceeded --max-redirects= redirects!' ;;
- *) echo_error 'Unholy Error!' ;;
esac
exit 1
fi
@@ -819,10 +818,10 @@ echo_safe_json() {
# If the $json variable has an '.Env' entry
# We parse its content to mask sensitive values
if [ "$MASKED_VARIABLES" == "extended" ] && [ -n "$(echo "$json" | jq -j 'if type == "array" then .[] else . end | .Env // ""')" ]; then
- temp_file=".stack_envs-$(echo "$(date +%s%3N)")"
+ temp_file="$(unique_temp_file_path)"
(
- echo "$json" | jq -r 'if type == "array" then .[] else . end | .Env | map(.name = .name + "=" + .value) | map (.name) | .[]' | sed "s/\"/\\\\\"/g" | sed "s/^\([^=]\{1,\}=\)\(.* .*\)$/\1\"\2\"/" > $temp_file && \
- . $temp_file && rm -f $temp_file && \
+ echo "$json" | jq -r 'if type == "array" then .[] else . end | .Env | map(.name = .name + "=" + .value) | map (.name) | .[]' | sed "s/\"/\\\\\"/g" | sed "s/^\([^=]\{1,\}=\)\(.* .*\)$/\1\"\2\"/" > "$temp_file" && \
+ . "$temp_file" && rm -f "$temp_file" && \
if [ "$type" == "debug" ]; then \
echo_debug "$message"; \
elif [ "$type" == "verbose" ]; then \
@@ -906,6 +905,75 @@ mask_variables() {
echo "$message"
}
+unique_temp_file_path() {
+ local file_prefix="psu"
+ local fallback_temp_path
+ local temp_path
+ local temp_file_path
+
+ fallback_temp_path="$(if [ -w "/tmp" ]; then echo "/tmp"; else pwd; fi)"
+ temp_path="${TMPDIR:-${TMP:-${TEMP:-$fallback_temp_path}}}"
+ if [ -w "$temp_path" ]; then
+ local file_uuid
+ # Generate universally unique identifier (UUID)
+ file_uuid=$(if [ -x "$(command -v uuidgen)" ]; then uuidgen; else cat /proc/sys/kernel/random/uuid; fi)
+ if [ -z "$file_uuid" ]; then
+ echo_error "You must install the 'uuidgen' program, to generate universally unique identifier"
+ exit 1
+ fi
+ # Unique temp file path
+ temp_file_path="${temp_path}/${file_prefix}_${file_uuid}.tmp"
+ else
+ echo_error "'${temp_path}' path is NOT WRITABLE!"
+ exit 1
+ fi
+
+ echo "$temp_file_path"
+}
+
+curl_wrapper() {
+ local result
+ # Use --fail-with-body cURL option if available
+ # And use environment variable to cache result
+ CURL_HAS_FAIL_WITH_BODY="${CURL_HAS_FAIL_WITH_BODY:-$(curl --help all | grep -w '\-\-fail\-with\-body' || true)}"
+ export CURL_HAS_FAIL_WITH_BODY
+
+ # Otherwise fallback to a temp result file storing
+ # Borrowed from: https://stackoverflow.com/a/55434980
+ local result_response_file
+ if [ -z "$CURL_HAS_FAIL_WITH_BODY" ]; then
+ result_response_file="$(unique_temp_file_path)"
+ fi
+
+ local curl_fail_params
+ curl_fail_params=$(if [ -n "$CURL_HAS_FAIL_WITH_BODY" ]; then echo --fail-with-body; else echo --output "$result_response_file" --write-out %\{http_code\}; fi)
+
+ result="$(curl \
+ $curl_fail_params \
+ --header "Content-Type: application/json" \
+ --silent \
+ $(if [ "$HTTPIE_VERIFY_SSL" == "no" ]; then echo --insecure; else $(if [ -f "$HTTPIE_VERIFY_SSL" ]; then echo --cacert "$HTTPIE_VERIFY_SSL"; else echo ''; fi); fi) \
+ "$@")"
+
+ result_exit_code="$?"
+ if [ -n "$result_response_file" ]; then
+ local response
+ response="$(cat "$result_response_file")"
+ rm -f "$result_response_file"
+ local response_result
+ response_result="$result"
+ result="$response"
+ echo "$result"
+ if [ "$response_result" -ge 400 ] && [ "$response_result" -le 599 ]; then
+ exit 22
+ fi
+ exit "$result_exit_code"
+ else
+ echo "$result"
+ exit "$result_exit_code"
+ fi
+}
+
###################################################
############### actions section ###################
###################################################
@@ -938,6 +1006,9 @@ deploy() {
echo_debug "DOCKER_COMPOSE_FILE -> $DOCKER_COMPOSE_FILE"
echo_debug "docker_compose_file_content -> $docker_compose_file_content"
+ local json_temp_path
+ json_temp_path="$(unique_temp_file_path)"
+
# If the stack does not exist
if [ -z "$STACK" ]; then
echo_verbose "Stack $PORTAINER_STACK_NAME does not exist."
@@ -967,23 +1038,19 @@ deploy() {
fi
local data_prefix="{\"Name\":\"$PORTAINER_STACK_NAME\","
local data_suffix=",\"Env\":"$stack_envvars"}"
- echo "$data_prefix$docker_compose_file_content$data_suffix" > json.tmp
+ echo "$data_prefix$docker_compose_file_content$data_suffix" > "$json_temp_path"
echo_debug_safe_json "Stack JSON -> $(echo $data_prefix$docker_compose_file_content$data_suffix | jq -C .)" "$data_prefix$docker_compose_file_content$data_suffix"
# Create stack for single Docker instance
echo_verbose "Creating stack $PORTAINER_STACK_NAME..."
local create
- create=$(http \
- --check-status \
- --ignore-stdin \
- --verify=$HTTPIE_VERIFY_SSL \
- --timeout=300 \
- "$PORTAINER_URL/api/stacks" \
- "Authorization: Bearer $PORTAINER_AUTH_TOKEN" \
- type==2 \
- method==string \
- endpointId==$PORTAINER_ENDPOINT \
- @json.tmp)
+ create=$(curl_wrapper \
+ --max-time 300 \
+ --request POST \
+ --header "Authorization: Bearer ${PORTAINER_AUTH_TOKEN}" \
+ --data "@${json_temp_path}" \
+ "${PORTAINER_URL}/api/stacks?type=2&method=string&endpointId=${PORTAINER_ENDPOINT}")
+
check_for_errors $? "$create"
echo_debug "Create action response -> $(echo $create | jq -C .)"
else
@@ -997,28 +1064,23 @@ deploy() {
fi
local data_prefix="{\"Name\":\"$PORTAINER_STACK_NAME\",\"SwarmID\":\"$swarm_id\","
local data_suffix=",\"Env\":"$stack_envvars"}"
- echo "$data_prefix$docker_compose_file_content$data_suffix" > json.tmp
+ echo "$data_prefix$docker_compose_file_content$data_suffix" > "$json_temp_path"
echo_debug_safe_json "Stack JSON -> $(echo $data_prefix$docker_compose_file_content$data_suffix | jq -C .)" "$data_prefix$docker_compose_file_content$data_suffix"
# Create stack for Docker swarm
echo_verbose "Creating stack $PORTAINER_STACK_NAME..."
local create
- create=$(http \
- --check-status \
- --ignore-stdin \
- --verify=$HTTPIE_VERIFY_SSL \
- --timeout=300 \
- "$PORTAINER_URL/api/stacks" \
- "Authorization: Bearer $PORTAINER_AUTH_TOKEN" \
- type==1 \
- method==string \
- endpointId==$PORTAINER_ENDPOINT \
- @json.tmp)
+ create=$(curl_wrapper \
+ --max-time 300 \
+ --request POST \
+ --header "Authorization: Bearer ${PORTAINER_AUTH_TOKEN}" \
+ --data "@${json_temp_path}" \
+ "${PORTAINER_URL}/api/stacks?type=1&method=string&endpointId=${PORTAINER_ENDPOINT}")
check_for_errors $? "$create"
echo_debug "Create action response -> $(echo $create | jq -C .)"
fi
- rm json.tmp
+ rm -f "$json_temp_path"
else
if [ $STRICT_MODE == "true" ]; then
echo_error "Error: Stack $PORTAINER_STACK_NAME already exists."
@@ -1038,25 +1100,22 @@ deploy() {
fi
local data_prefix="{\"Id\":\"$stack_id\","
local data_suffix=",\"Env\":"$stack_envvars",\"Prune\":$PORTAINER_PRUNE}"
- echo "$data_prefix$docker_compose_file_content$data_suffix" > json.tmp
+ echo "$data_prefix$docker_compose_file_content$data_suffix" > "$json_temp_path"
echo_debug_safe_json "Stack JSON -> $(echo $data_prefix$docker_compose_file_content$data_suffix | jq -C .)" "$data_prefix$docker_compose_file_content$data_suffix"
# Update stack
echo_verbose "Updating stack $PORTAINER_STACK_NAME..."
local update
- update=$(http \
- --check-status \
- --ignore-stdin \
- --verify=$HTTPIE_VERIFY_SSL \
- --timeout=300 \
- PUT "$PORTAINER_URL/api/stacks/$stack_id" \
- "Authorization: Bearer $PORTAINER_AUTH_TOKEN" \
- endpointId==$PORTAINER_ENDPOINT \
- @json.tmp)
+ update=$(curl_wrapper \
+ --max-time 300 \
+ --request PUT \
+ --header "Authorization: Bearer ${PORTAINER_AUTH_TOKEN}" \
+ --data "@${json_temp_path}" \
+ "${PORTAINER_URL}/api/stacks/${stack_id}?endpointId=${PORTAINER_ENDPOINT}")
check_for_errors $? "$update"
echo_debug "Update action response -> $(echo $update | jq -C .)"
- rm json.tmp
+ rm -f "$json_temp_path"
fi
}
@@ -1091,13 +1150,10 @@ undeploy() {
echo_verbose "Deleting stack $PORTAINER_STACK_NAME..."
local delete
- delete=$(http \
- --check-status \
- --ignore-stdin \
- --verify=$HTTPIE_VERIFY_SSL \
- DELETE "$PORTAINER_URL/api/stacks/$stack_id" \
- "Authorization: Bearer $PORTAINER_AUTH_TOKEN" \
- endpointId==$PORTAINER_ENDPOINT)
+ delete=$(curl_wrapper \
+ --request DELETE \
+ --header "Authorization: Bearer ${PORTAINER_AUTH_TOKEN}" \
+ "${PORTAINER_URL}/api/stacks/${stack_id}?endpointId=${PORTAINER_ENDPOINT}")
check_for_errors $? "$delete"
echo_debug "Delete action response -> $(echo $delete | jq -C .)"
}
@@ -1106,13 +1162,10 @@ undeploy() {
login() {
echo_verbose "Getting auth token..."
local auth_token_json
- auth_token_json=$(http \
- --check-status \
- --ignore-stdin \
- --verify=$HTTPIE_VERIFY_SSL \
- $PORTAINER_URL/api/auth \
- username=$PORTAINER_USER \
- password=$PORTAINER_PASSWORD)
+ auth_token_json=$(curl_wrapper \
+ --request POST \
+ --data "{\"username\":\"${PORTAINER_USER}\",\"password\":\"${PORTAINER_PASSWORD}\"}" \
+ "${PORTAINER_URL}/api/auth")
check_for_errors $? "$auth_token_json"
PORTAINER_AUTH_TOKEN=$(echo $auth_token_json | jq -r .jwt)
echo_debug "Get auth token response -> $(echo $auth_token_json | jq -C .)"
@@ -1126,12 +1179,10 @@ login() {
# Get Docker info
docker_info() {
local docker_info
- docker_info=$(http \
- --check-status \
- --ignore-stdin \
- --verify=$HTTPIE_VERIFY_SSL \
- "$PORTAINER_URL/api/endpoints/$PORTAINER_ENDPOINT/docker/info" \
- "Authorization: Bearer $PORTAINER_AUTH_TOKEN")
+ docker_info=$(curl_wrapper \
+ --request GET \
+ --header "Authorization: Bearer ${PORTAINER_AUTH_TOKEN}" \
+ "${PORTAINER_URL}/api/endpoints/${PORTAINER_ENDPOINT}/docker/info")
check_for_errors $? "$docker_info"
echo "$docker_info"
@@ -1151,13 +1202,12 @@ tasks() {
local filters
filters="{$filter_service$(if [ -n "$filter_desired_state" ]; then echo ",$filter_desired_state"; fi)}"
local tasks
- tasks=$(http \
- --check-status \
- --ignore-stdin \
- --verify=$HTTPIE_VERIFY_SSL \
- "$PORTAINER_URL/api/endpoints/$PORTAINER_ENDPOINT/docker/tasks" \
- filters=="$filters" \
- "Authorization: Bearer $PORTAINER_AUTH_TOKEN")
+ tasks=$(curl_wrapper \
+ --request GET \
+ --get \
+ --header "Authorization: Bearer ${PORTAINER_AUTH_TOKEN}" \
+ --data-urlencode "filters=${filters}" \
+ "${PORTAINER_URL}/api/endpoints/${PORTAINER_ENDPOINT}/docker/tasks")
check_for_errors $? "$tasks"
if [ -n "$state" ]; then
local filter_status
@@ -1192,14 +1242,15 @@ tasks_healthy() {
services() {
local services
local filter_service
+ local filters
filter_service="\"label\":{\"com.docker.stack.namespace=$PORTAINER_STACK_NAME\":true}"
- services=$(http \
- --check-status \
- --ignore-stdin \
- --verify=$HTTPIE_VERIFY_SSL \
- "$PORTAINER_URL/api/endpoints/$PORTAINER_ENDPOINT/docker/services" \
- filters=="{$filter_service}" \
- "Authorization: Bearer $PORTAINER_AUTH_TOKEN")
+ filters="{${filter_service}}"
+ services=$(curl_wrapper \
+ --request GET \
+ --get \
+ --header "Authorization: Bearer ${PORTAINER_AUTH_TOKEN}" \
+ --data-urlencode "filters=${filters}" \
+ "${PORTAINER_URL}/api/endpoints/${PORTAINER_ENDPOINT}/docker/services")
check_for_errors $? "$services"
local filter_mode
@@ -1223,13 +1274,12 @@ containers() {
filter_service="\"label\":{\"com.docker.swarm.service.name=$service_name\":true}"
fi
filters="{$filter_stack$(if [ -n "$filter_service" ]; then echo ",$filter_service"; fi),$filter_task}"
- containers=$(http \
- --check-status \
- --ignore-stdin \
- --verify=$HTTPIE_VERIFY_SSL \
- "$PORTAINER_URL/api/endpoints/$PORTAINER_ENDPOINT/docker/containers/json" \
- filters=="$filters" \
- "Authorization: Bearer $PORTAINER_AUTH_TOKEN")
+ containers=$(curl_wrapper \
+ --request GET \
+ --get \
+ --header "Authorization: Bearer ${PORTAINER_AUTH_TOKEN}" \
+ --data-urlencode "filters=${filters}" \
+ "${PORTAINER_URL}/api/endpoints/${PORTAINER_ENDPOINT}/docker/containers/json")
check_for_errors $? "$containers"
echo "$containers"
}
@@ -1241,11 +1291,13 @@ lint() {
if [ -x "$(command -v docker-compose)" ]; then
echo_verbose "Linting Docker compose/stack file..."
docker_stack_error="error_docker_stack_is_invalid"
- docker_stack_validation=$(docker-compose -f "$DOCKER_COMPOSE_FILE" config -q > docker_stack_validation_report 2>&1 || echo $docker_stack_error)
+ local docker_stack_validation_report
+ docker_stack_validation_report="$(unique_temp_file_path)"
+ docker_stack_validation=$(docker-compose -f "$DOCKER_COMPOSE_FILE" config -q > "$docker_stack_validation_report" 2>&1 || echo $docker_stack_error)
if [[ $docker_stack_validation =~ $docker_stack_error$ ]]; then
echo_error "Error: The '$DOCKER_COMPOSE_FILE' Docker compose/stack file is invalid:"
- echo_error "$(cat docker_stack_validation_report)"
- rm -f docker_stack_validation_report
+ echo_error "$(cat $docker_stack_validation_report)"
+ rm -f "$docker_stack_validation_report"
exit 1
else
if [ "$ACTION" == "lint" ]; then
diff --git a/tests/run.sh b/tests/run.sh
index f4fb197..e6d4bab 100644
--- a/tests/run.sh
+++ b/tests/run.sh
@@ -118,7 +118,16 @@ PSU_AUTH_TOKEN=$(psu_wrapper login --user $PSU_USER --password $PSU_PASSWORD --u
# Add GitLab Docker registry access to Portainer
envsubst '$CI_REGISTRY_USER,$CI_REGISTRY_PASSWORD' < gitlab-registry.json > gitlab-registry-final.json
-http --check-status --ignore-stdin --verify=no --timeout=10 POST "$PSU_URL/api/registries" "Authorization: Bearer $PSU_AUTH_TOKEN" @gitlab-registry-final.json
+curl \
+ --fail \
+ --silent \
+ --insecure \
+ --max-time 10 \
+ --request POST \
+ --header "Authorization: Bearer ${PSU_AUTH_TOKEN}" \
+ --header "Content-Type: application/json" \
+ --data "@gitlab-registry-final.json" \
+ "${PSU_URL}/api/registries"
# Add local endpoint to the Portainer instance
end_point_type_param=EndpointType
@@ -126,7 +135,15 @@ if [[ "$PORTAINER_VERSION" == "latest" ]] || [ "$(version $PORTAINER_VERSION)" -
# See: https://github.com/portainer/portainer/issues/4602#issuecomment-746819197
end_point_type_param=EndpointCreationType
fi
-http --check-status --ignore-stdin --verify=no --timeout=10 POST "$PSU_URL/api/endpoints" "Authorization: Bearer $PSU_AUTH_TOKEN" Name==local $end_point_type_param==1
+curl \
+ --fail \
+ --silent \
+ --insecure \
+ --max-time 10 \
+ --request POST \
+ --header "Authorization: Bearer ${PSU_AUTH_TOKEN}" \
+ --header "Content-Type: application/json" \
+ "${PSU_URL}/api/endpoints?Name=local&${end_point_type_param}=1"
# Docker system info from Portainer test
docker_info=$(psu_wrapper system:info --user $PSU_USER --password $PSU_PASSWORD --url $PSU_URL --insecure --debug false --verbose false)