Merge pull request #4395 from dokku/cnb-env-vars

Add environment variable support to CNB-based containers
This commit is contained in:
Jose Diaz-Gonzalez
2021-02-11 05:21:31 -05:00
committed by GitHub
14 changed files with 123 additions and 40 deletions

View File

@@ -47,14 +47,14 @@ trigger-builder-herokuish-builder-build() {
plugn trigger pre-build-buildpack "$APP" "$SOURCECODE_WORK_DIR"
local DOCKER_ARGS=$(: | plugn trigger docker-args-build "$APP" "$BUILDER_TYPE")
[[ "$DOKKU_TRACE" ]] && DOCKER_ARGS+=" -e TRACE=true "
[[ "$DOKKU_TRACE" ]] && DOCKER_ARGS+=" --env=TRACE=true "
DOCKER_ARGS+=$(: | plugn trigger docker-args-process-build "$APP" "$BUILDER_TYPE")
declare -a ARG_ARRAY
eval "ARG_ARRAY=($DOCKER_ARGS)"
local DOKKU_CONTAINER_EXIT_CODE=0
local CID=$("$DOCKER_BIN" container create "${DOCKER_RUN_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS -v $DOKKU_APP_HOST_CACHE_DIR:/cache -e CACHE_PATH=/cache "${ARG_ARRAY[@]}" $IMAGE /build)
local CID=$("$DOCKER_BIN" container create "${DOCKER_RUN_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS -v $DOKKU_APP_HOST_CACHE_DIR:/cache --env=CACHE_PATH=/cache "${ARG_ARRAY[@]}" $IMAGE /build)
plugn trigger post-container-create "app" "$CID" "$APP" "build"
"$DOCKER_BIN" container start "$CID" >/dev/null || DOKKU_CONTAINER_EXIT_CODE=$?
"$DOCKER_BIN" container attach "$CID"

View File

@@ -14,6 +14,12 @@ trigger-config-docker-args() {
STDIN=$(cat)
trigger="$0 config_docker_args"
if is_image_cnb_based "$IMAGE"; then
ENV_ARGS="$(config_export app "$APP" --format docker-args-keys --merged)"
echo -n "$STDIN $ENV_ARGS"
return
fi
if ! is_image_herokuish_based "$IMAGE" "$APP"; then
ENV_ARGS="$(config_export app "$APP" --format docker-args --merged)"
echo -n "$STDIN $ENV_ARGS"

View File

@@ -24,8 +24,10 @@ const (
ExportFormatExports ExportFormat = iota
//ExportFormatEnvfile format: dotenv file
ExportFormatEnvfile
//ExportFormatDockerArgs format: --env args for docker
//ExportFormatDockerArgs format: --env KEY=VALUE args for docker
ExportFormatDockerArgs
//ExportFormatDockerArgsKeys format: --env KEY args for docker
ExportFormatDockerArgsKeys
//ExportFormatShell format: env arguments for shell
ExportFormatShell
//ExportFormatPretty format: pretty-printed in columns
@@ -171,6 +173,8 @@ func (e *Env) Export(format ExportFormat) string {
return e.EnvfileString()
case ExportFormatDockerArgs:
return e.DockerArgsString()
case ExportFormatDockerArgsKeys:
return e.DockerArgsKeysString()
case ExportFormatShell:
return e.ShellString()
case ExportFormatPretty:
@@ -201,6 +205,16 @@ func (e *Env) DockerArgsString() string {
return e.stringWithPrefixAndSeparator("--env=", " ")
}
//DockerArgsKeysString gets the contents of this Env in the form -env=KEY --env...
func (e *Env) DockerArgsKeysString() string {
keys := e.Keys()
entries := make([]string, len(keys))
for i, k := range keys {
entries[i] = fmt.Sprintf("%s%s", "--env=", k)
}
return strings.Join(entries, " ")
}
//JSONString returns the contents of this Env as a key/value json object
func (e *Env) JSONString() string {
data, err := json.Marshal(e.Map())

View File

@@ -54,6 +54,7 @@ func TestExport(t *testing.T) {
e, _ := newEnvFromString("BAR='BAZ'\nFOO='b'ar '\nBAZ='a\\nb'")
Expect(e.Export(ExportFormatEnvfile)).To(Equal("BAR=\"BAZ\"\nBAZ=\"a\\nb\"\nFOO=\"b'ar \""))
Expect(e.Export(ExportFormatDockerArgs)).To(Equal("--env=BAR='BAZ' --env=BAZ='a\nb' --env=FOO='b'\\''ar '"))
Expect(e.Export(ExportFormatDockerArgsKeys)).To(Equal("--env=BAR --env=BAZ --env=FOO"))
Expect(e.Export(ExportFormatShell)).To(Equal("BAR='BAZ' BAZ='a\nb' FOO='b'\\''ar '"))
Expect(e.Export(ExportFormatExports)).To(Equal("export BAR='BAZ'\nexport BAZ='a\nb'\nexport FOO='b'\\''ar '"))
Expect(e.Export(ExportFormatPretty)).To(Equal("BAR: BAZ\nBAZ: a\nb\nFOO: b'ar"))

View File

@@ -49,7 +49,7 @@ func main() {
args := flag.NewFlagSet("config:export", flag.ExitOnError)
global := args.Bool("global", false, "--global: use the global environment")
merged := args.Bool("merged", false, "--merged: merge app environment and global environment")
format := args.String("format", "exports", "--format: [ exports | envfile | docker-args | shell | pretty | json | json-list ] which format to export as)")
format := args.String("format", "exports", "--format: [ exports | envfile | docker-args | docker-args-keys | shell | pretty | json | json-list ] which format to export as)")
args.Parse(os.Args[2:])
if !*global {
appName = args.Arg(0)

View File

@@ -43,13 +43,14 @@ func CommandExport(appName string, global bool, merged bool, format string) erro
suffix := "\n"
exportTypes := map[string]ExportFormat{
"exports": ExportFormatExports,
"envfile": ExportFormatEnvfile,
"docker-args": ExportFormatDockerArgs,
"shell": ExportFormatShell,
"pretty": ExportFormatPretty,
"json": ExportFormatJSON,
"json-list": ExportFormatJSONList,
"exports": ExportFormatExports,
"envfile": ExportFormatEnvfile,
"docker-args": ExportFormatDockerArgs,
"docker-args-keys": ExportFormatDockerArgsKeys,
"shell": ExportFormatShell,
"pretty": ExportFormatPretty,
"json": ExportFormatJSON,
"json-list": ExportFormatJSONList,
}
exportType, ok := exportTypes[format]

View File

@@ -49,7 +49,7 @@ func TriggerDockerArgsProcessDeploy(appName string) error {
}
if maxSize != "unlimited" {
fmt.Printf(" --log-opt max-size=%s ", maxSize)
fmt.Printf(" --log-opt=max-size=%s ", maxSize)
}
}

View File

@@ -17,7 +17,7 @@ trigger-scheduler-docker-local-scheduler-deploy() {
rm -f "${DOKKU_LIB_ROOT}/data/scheduler-docker-local/$APP/failed-containers"
local DOCKER_RUN_LABEL_ARGS="--label=com.dokku.app-name=$APP"
local DOCKER_RUN_LABEL_ARGS="--label=com.dokku.app-name=$APP --label=com.dokku.container-type=deploy"
local DOKKU_DOCKER_STOP_TIMEOUT DOKKU_HEROKUISH DOKKU_NETWORK_BIND_ALL IMAGE
DOKKU_HEROKUISH=false
IMAGE=$(get_deploying_app_image_name "$APP" "$IMAGE_TAG")
@@ -72,43 +72,40 @@ trigger-scheduler-docker-local-scheduler-deploy() {
# start the app
local DOCKER_ARGS
DOCKER_ARGS=$(: | plugn trigger docker-args-deploy "$APP" "$IMAGE_TAG" "$PROC_TYPE" "$CONTAINER_INDEX")
DOCKER_ARGS+=" -e DYNO=$DYNO "
DOCKER_ARGS+=" --label=com.dokku.process-type=$PROC_TYPE --label=com.dokku.dyno=$DYNO "
DOCKER_ARGS+=" --env=DYNO=$DYNO "
DOCKER_ARGS+=" --init "
DOCKER_ARGS+=" $DOCKER_RUN_LABEL_ARGS $DOKKU_GLOBAL_RUN_ARGS "
DOCKER_ARGS+=$(: | plugn trigger docker-args-process-deploy "$APP" "$IMAGE_SOURCE_TYPE" "$IMAGE_TAG" "$PROC_TYPE" "$CONTAINER_INDEX")
[[ "$DOKKU_TRACE" ]] && DOCKER_ARGS+=" -e TRACE=true "
declare -a ARG_ARRAY
eval "ARG_ARRAY=($DOCKER_ARGS)"
[[ "$DOKKU_TRACE" ]] && DOCKER_ARGS+=" --env=TRACE=true "
local START_CMD
[[ "$DOKKU_HEROKUISH" == "true" ]] && START_CMD="/start $PROC_TYPE"
[[ -n "$DOKKU_START_CMD" ]] && START_CMD="$DOKKU_START_CMD"
local DOKKU_PORT=""
if [[ "$PROC_TYPE" == "web" ]]; then
ports=($(plugn trigger network-compute-ports "$APP" "$PROC_TYPE" "$DOKKU_HEROKUISH" "$CONTAINER_INDEX"))
local DOKKU_DOCKER_PORT_ARGS=""
local DOKKU_PORT=""
for p in "${ports[@]}"; do
if [[ ! "$p" =~ .*udp.* ]]; then
DOKKU_PORT=${DOKKU_PORT:="$p"}
fi
DOKKU_DOCKER_PORT_ARGS+=" -p $p "
if [[ "$DOKKU_NETWORK_BIND_ALL" == "true" ]]; then
DOCKER_ARGS+=" -p $p "
fi
done
START_CMD=$(fn-scheduler-docker-local-extract-start-cmd "$APP" "$PROC_TYPE" "$START_CMD" "$DOKKU_HEROKUISH" "$DOKKU_PORT")
if [[ "$DOKKU_NETWORK_BIND_ALL" == "false" ]]; then
# shellcheck disable=SC2086
cid=$("$DOCKER_BIN" container create --label=com.dokku.container-type=deploy --label=com.dokku.process-type=$PROC_TYPE --label=com.dokku.dyno=$DYNO "${DOCKER_RUN_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS --init --env PORT=$DOKKU_PORT "${ARG_ARRAY[@]}" $IMAGE $START_CMD)
else
# shellcheck disable=SC2086
cid=$("$DOCKER_BIN" container create --label=com.dokku.container-type=deploy --label=com.dokku.process-type=$PROC_TYPE --label=com.dokku.dyno=$DYNO "${DOCKER_RUN_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS --init $DOKKU_DOCKER_PORT_ARGS --env PORT=$DOKKU_PORT "${ARG_ARRAY[@]}" $IMAGE $START_CMD)
fi
else
START_CMD=$(fn-scheduler-docker-local-extract-start-cmd "$APP" "$PROC_TYPE" "$START_CMD" "$DOKKU_HEROKUISH")
# shellcheck disable=SC2086
cid=$("$DOCKER_BIN" container create --label=com.dokku.container-type=deploy --label=com.dokku.process-type=$PROC_TYPE --label=com.dokku.dyno=$DYNO "${DOCKER_RUN_LABEL_ARGS[@]}" $DOKKU_GLOBAL_RUN_ARGS --init --env PORT=$DOKKU_PORT "${ARG_ARRAY[@]}" $IMAGE $START_CMD)
fi
DOCKER_ARGS+=" --env=PORT=$DOKKU_PORT "
START_CMD=$(fn-scheduler-docker-local-extract-start-cmd "$APP" "$PROC_TYPE" "$START_CMD" "$DOKKU_HEROKUISH" "$DOKKU_PORT")
DOCKER_ARGS+=" $IMAGE "
DOCKER_ARGS+=" $START_CMD "
cid=$(fn-start-app-container "$DOCKER_ARGS")
plugn trigger post-container-create "app" "$cid" "$APP" "deploy" "$PROC_TYPE"
"$DOCKER_BIN" container start "$cid" >/dev/null || true
@@ -219,4 +216,14 @@ fn-scheduler-docker-local-extract-start-cmd() {
echo "$START_CMD"
}
fn-start-app-container() {
declare desc="starts a single app container"
declare DOCKER_ARGS="$1"
declare -a __DOKKU_DOCKER_ARG_ARRAY
eval "__DOKKU_DOCKER_ARG_ARRAY=($DOCKER_ARGS)"
eval "$(config_export app "$APP")"
"$DOCKER_BIN" container create "${__DOKKU_DOCKER_ARG_ARRAY[@]}"
}
trigger-scheduler-docker-local-scheduler-deploy "$@"

View File

@@ -1,4 +1,5 @@
import http.server
import json
import os
@@ -7,7 +8,12 @@ class GetHandler(http.server.BaseHTTPRequestHandler):
self.send_response(200)
self.send_header("Content-Type", "text/plain; charset=utf-8")
self.end_headers()
self.wfile.write("python/http.server".encode("utf-8"))
if self.path == '/':
self.wfile.write("python/http.server".encode("utf-8"))
else:
data = json.dumps(dict(os.environ), sort_keys=True, indent=4)
self.wfile.write(data.encode("utf-8"))
if __name__ == "__main__":

View File

@@ -303,9 +303,14 @@ teardown() {
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "dokku docker-options:add $TEST_APP build \"--link postgres\""
echo "output: $output"
echo "status: $status"
assert_success
deploy_app dockerfile
run deploy_app dockerfile
echo "output: $output"
echo "status: $status"
assert_success
}

View File

@@ -385,7 +385,7 @@ teardown() {
echo "output: $output"
echo "status: $status"
assert_success
assert_output "--log-opt max-size=20m"
assert_output "--log-opt=max-size=20m"
run /bin/bash -c "dokku docker-options:add $TEST_APP deploy --log-driver=local" 2>&1
echo "output: $output"
@@ -396,7 +396,7 @@ teardown() {
echo "output: $output"
echo "status: $status"
assert_success
assert_output "--log-opt max-size=20m"
assert_output "--log-opt=max-size=20m"
run /bin/bash -c "dokku docker-options:add $TEST_APP deploy --log-driver=json-file" 2>&1
echo "output: $output"
@@ -407,7 +407,7 @@ teardown() {
echo "output: $output"
echo "status: $status"
assert_success
assert_output "--log-opt max-size=20m"
assert_output "--log-opt=max-size=20m"
run /bin/bash -c "dokku docker-options:add $TEST_APP deploy --log-driver=journald" 2>&1
echo "output: $output"

37
tests/unit/ps-cnb.bats Normal file
View File

@@ -0,0 +1,37 @@
#!/usr/bin/env bats
load test_helper
setup_file() {
add-apt-repository --yes ppa:cncf-buildpacks/pack-cli
apt-get update
apt-get --yes install pack-cli
}
setup() {
global_setup
create_app
}
teardown() {
destroy_app
global_teardown
}
@test "(ps) cnb env vars" {
run /bin/bash -c "dokku config:set $TEST_APP DOKKU_CNB_EXPERIMENTAL=1"
echo "output: $output"
echo "status: $status"
assert_success
run deploy_app python dokku@dokku.me:$TEST_APP add_requirements_txt
echo "output: $output"
echo "status: $status"
assert_success
run /bin/bash -c "curl $(dokku url $TEST_APP)/env"
echo "output: $output"
echo "status: $status"
assert_success
assert_output_contains '"DOKKU_CNB_EXPERIMENTAL": "1"'
}

View File

@@ -9,7 +9,6 @@ setup() {
teardown() {
destroy_app
destroy_app 0 "$MYAPP" || true
global_teardown
}

View File

@@ -513,6 +513,13 @@ add_release_command() {
echo "release: touch /app/release.test" >> "$APP_REPO_DIR/Procfile"
}
add_requirements_txt() {
local APP="$1"
local APP_REPO_DIR="$2"
[[ -z "$APP" ]] && local APP="$TEST_APP"
echo "flask" >> "$APP_REPO_DIR/requirements.txt"
}
build_nginx_config() {
# simulate nginx post-deploy
dokku domains:setup "$TEST_APP"