Skip to content

Fix terminating containers #102

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 10 commits into from
Sep 21, 2022
2 changes: 1 addition & 1 deletion .ci/openshift_integration.sh
Original file line number Diff line number Diff line change
Expand Up @@ -62,4 +62,4 @@ $(realpath odo) registry delete DefaultDevfileRegistry -f
$(realpath odo) registry add TestDevfileRegistry http://$REGISTRY_HOSTNAME

# Run the devfile validation tests
ENV=openshift REGISTRY=remote tests/test.sh $(realpath odo) $YQ_PATH
ENV=openshift REGISTRY=remote tests/check_odo_happy_path.sh $(realpath odo) $YQ_PATH
12 changes: 7 additions & 5 deletions .github/workflows/validate-devfiles-minikube.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,10 @@ jobs:
- name: Checkout
uses: actions/checkout@v1
- name: Setup Minikube
uses: manusa/actions-setup-minikube@v2.4.2
uses: manusa/actions-setup-minikube@v2.7.0
with:
minikube version: 'v1.21.0'
kubernetes version: 'v1.21.0'
minikube version: 'v1.26.1'
kubernetes version: '1.25.0'
driver: 'docker'
github token: ${{ secrets.GITHUB_TOKEN }}
start args: '--addons=ingress'
Expand All @@ -43,5 +43,7 @@ jobs:
with:
# Installs the latest release of odo
odo: "2.5.1"
- name: Validate the devfile stacks
run: tests/test.sh odo
- name: Check that containers components are non terminating
run: tests/check_non_terminating.sh
- name: Run odo happy path tests
run: tests/check_odo_happy_path.sh odo
1 change: 1 addition & 0 deletions stacks/dotnet50/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ components:
- name: dotnet
container:
image: registry.access.redhat.com/ubi8/dotnet-50:5.0
args: ["tail", "-f", "/dev/null"]
mountSources: true
env:
- name: CONFIGURATION
Expand Down
1 change: 1 addition & 0 deletions stacks/dotnet60/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ components:
- name: dotnet
container:
image: registry.access.redhat.com/ubi8/dotnet-60:6.0
args: ["tail", "-f", "/dev/null"]
mountSources: true
env:
- name: CONFIGURATION
Expand Down
1 change: 1 addition & 0 deletions stacks/dotnetcore31/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ components:
- name: dotnet
container:
image: registry.access.redhat.com/ubi8/dotnet-31:3.1
args: ["tail", "-f", "/dev/null"]
mountSources: true
env:
- name: CONFIGURATION
Expand Down
1 change: 1 addition & 0 deletions stacks/go/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ components:
- name: http-go
targetPort: 8080
image: quay.io/devfile/golang:latest
args: ["tail", "-f", "/dev/null"]
memoryLimit: 1024Mi
mountSources: true
name: runtime
Expand Down
1 change: 1 addition & 0 deletions stacks/java-openliberty-gradle/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ components:
- name: dev
container:
image: icr.io/appcafe/open-liberty-devfile-stack:{{liberty-version}}-gradle
args: ["tail", "-f", "/dev/null"]
memoryLimit: 1280Mi
mountSources: true
endpoints:
Expand Down
1 change: 1 addition & 0 deletions stacks/java-openliberty/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ components:
container:
# In the original upstream of this devfile, the image used is openliberty/devfile-stack:<x.y.z>, which is built from the repository: https://github.com/OpenLiberty/devfile-stack
image: icr.io/appcafe/open-liberty-devfile-stack:{{liberty-version}}
args: ["tail", "-f", "/dev/null"]
memoryLimit: 768Mi
mountSources: true
endpoints:
Expand Down
1 change: 1 addition & 0 deletions stacks/java-quarkus/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ components:
- name: tools
container:
image: registry.access.redhat.com/ubi8/openjdk-11
args: ["tail", "-f", "/dev/null"]
memoryLimit: 512Mi ## default app nowhere needs this but leaving room for expansion.
mountSources: true
volumeMounts:
Expand Down
1 change: 1 addition & 0 deletions stacks/java-websphereliberty-gradle/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ components:
- name: dev
container:
image: icr.io/appcafe/websphere-liberty-devfile-stack:{{liberty-version}}-gradle
args: ["tail", "-f", "/dev/null"]
memoryLimit: 1280Mi
mountSources: true
endpoints:
Expand Down
1 change: 1 addition & 0 deletions stacks/java-websphereliberty/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ components:
container:
# In the original upstream of this devfile, the image used is openliberty/devfile-stack:<x.y.z>, which is built from the repository: https://github.com/OpenLiberty/devfile-stack
image: icr.io/appcafe/websphere-liberty-devfile-stack:{{liberty-version}}
command: ["tail", "-f", "/dev/null"]
memoryLimit: 768Mi
mountSources: true
endpoints:
Expand Down
1 change: 1 addition & 0 deletions stacks/java-wildfly-bootable-jar/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,7 @@ components:
- name: wildfly
container:
image: registry.access.redhat.com/ubi8/openjdk-11
args: ["tail", "-f", "/dev/null"]
memoryLimit: 1512Mi
mountSources: true
volumeMounts:
Expand Down
1 change: 1 addition & 0 deletions stacks/nodejs-angular/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ components:
- name: http-angular
targetPort: 4200
image: registry.access.redhat.com/ubi8/nodejs-16:latest
args: ["tail", "-f", "/dev/null"]
memoryLimit: 1024Mi
name: runtime
commands:
Expand Down
1 change: 1 addition & 0 deletions stacks/nodejs-nextjs/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ components:
- name: http-nextjs
targetPort: 3000
image: registry.access.redhat.com/ubi8/nodejs-16:latest
command: ["tail", "-f", "/dev/null"]
memoryLimit: 1024Mi
name: runtime
commands:
Expand Down
1 change: 1 addition & 0 deletions stacks/nodejs-nuxtjs/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ components:
- name: http-nuxtjs
targetPort: 3000
image: registry.access.redhat.com/ubi8/nodejs-16:latest
args: ["tail", "-f", "/dev/null"]
memoryLimit: 1024Mi
name: runtime
commands:
Expand Down
1 change: 1 addition & 0 deletions stacks/nodejs-react/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ components:
- name: http-react
targetPort: 3000
image: registry.access.redhat.com/ubi8/nodejs-16:latest
args: ["tail", "-f", "/dev/null"]
memoryLimit: 1024Mi
name: runtime
commands:
Expand Down
1 change: 1 addition & 0 deletions stacks/nodejs-svelte/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ components:
- name: http-svelte
targetPort: 3000
image: registry.access.redhat.com/ubi8/nodejs-16:latest
args: ["tail", "-f", "/dev/null"]
memoryLimit: 1024Mi
name: runtime
commands:
Expand Down
1 change: 1 addition & 0 deletions stacks/nodejs-vue/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ components:
- name: http-vue
targetPort: 3000
image: registry.access.redhat.com/ubi8/nodejs-16:latest
args: ["tail", "-f", "/dev/null"]
memoryLimit: 1024Mi
name: runtime
commands:
Expand Down
1 change: 1 addition & 0 deletions stacks/nodejs/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ components:
- name: runtime
container:
image: registry.access.redhat.com/ubi8/nodejs-16:latest
args: ["tail", "-f", "/dev/null"]
memoryLimit: 1024Mi
mountSources: true
endpoints:
Expand Down
1 change: 1 addition & 0 deletions stacks/php-laravel/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ components:
- name: http-laravel
targetPort: 8000
image: quay.io/devfile/composer:2.1.11
args: ["tail", "-f", "/dev/null"]
memoryLimit: 1024Mi
mountSources: true
name: runtime
Expand Down
1 change: 1 addition & 0 deletions stacks/python-django/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ components:
- name: py-web
container:
image: registry.access.redhat.com/ubi9/python-39:latest
args: ["tail", "-f", "/dev/null"]
mountSources: true
endpoints:
- name: http-django
Expand Down
1 change: 1 addition & 0 deletions stacks/python-flask/devfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ components:
- name: py-web
container:
image: registry.access.redhat.com/ubi9/python-39:latest
args: ["tail", "-f", "/dev/null"]
mountSources: true
endpoints:
- name: http-python
Expand Down
10 changes: 9 additions & 1 deletion tests/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,20 @@

1) Ensure minikube is running and `minikube ip` reports a valid ip address

2) From the root of this repository, run `tests/test.sh`.
2) From the root of this repository, run `tests/check_odo_happy_path.sh`.

- The test script will validate each devfile stack under `stacks/` with odo, verifying that the stack can be used to build a starter project and that the application is properly built and exposed.
- The test script checks for an HTTP 200 status code to determine "properly exposed".
- Each devfile stack **must** have at least one starter project specified in the devfile.yaml

3) From the root of this repository, run `tests/check_non_terminating.sh`.

- The test script will validate each devfile stack under `stacks/`, verifying that the components of type container are terminating.
- The test script retrieves the `image`, `command` and `args` of a container component and uses them to run a pod and wait until it reaches the `Running` state:
```bash
kubectl run test-terminating -n default --attach=false --restart=Never --image="<image>" --command=true -- "<command>" "<args>"
```
- Each container component **must** be non-terminating. If the default `image` entrypoint is terminating an `args` (preferred) or `command` should be specified in the defile (e.g. `["tail", "-f", "/dev/null"]`).

## Limitations

Expand Down
166 changes: 166 additions & 0 deletions tests/check_non_terminating.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#!/bin/bash
set -o nounset
set -o errexit

DEVFILES_DIR="$(pwd)/stacks"

replaceVariables() {
image=$1
VAR_KEYS=(liberty-version)
VAR_VALUES=(22.0.0.1)

for i in "${!VAR_KEYS[@]}"; do
key='{{'
key+=${VAR_KEYS[i]}
key+='}}'
value=${VAR_VALUES[i]}
image=${image/${key}/${value}}
done
echo "$image"
}

getContainerComponentsNum() {
devfilePath=$1
component_num=$($YQ_PATH eval '[ .components[] | select(has("container")) ] | length' "$devfilePath" -r)
echo "${component_num}"
}

getName() {
devfilePath=$1
name=$($YQ_PATH eval '.metadata.name' "$devfilePath" -r)
echo "${name}"
}

getFirstContainerComponentImage() {
devfilePath=$1

image_original=$($YQ_PATH eval '[ .components[] | select(has("container")) ] | .[0].container.image' "$devfilePath" -r)
image_processed=$(replaceVariables "${image_original}")
echo "${image_processed}"
}

getFirstContainerComponentCommand() {
devfilePath=$1
local _gfccc_command=()
local _gfccc_command_string=()

IFS=" " read -r -a _gfccc_command_string <<< "$($YQ_PATH eval '[ .components[] | select(has("container")) ] | .[0].container.command[]? + " "' "$devfilePath" -r | paste -s -d '\0' -)"
if (( ${#_gfccc_command_string[@]} == 0 )); then
echo ""
else
for command_word in "${_gfccc_command_string[@]}"; do
_gfccc_command+=("${command_word}")
done
echo "${_gfccc_command[@]}"
fi
}

getFirstContainerComponentArgs() {
devfilePath=$1
local _gfcca_args=()
local _gfcca_args_string=()

IFS=" " read -r -a _gfcca_args_string <<< "$($YQ_PATH eval '[ .components[] | select(has("container")) ] | .[0].container.args[]? + " "' "$devfilePath" -r | paste -s -d '\0' -)"
if (( ${#_gfcca_args_string[@]} == 0 )); then
echo ""
else
for arg in "${_gfcca_args_string[@]}"; do
_gfcca_args+=("${arg}")
done
echo "${_gfcca_args[@]}"
fi
}

isNonTerminating() {
_int_image=$1
_int_command=("$2")
_int_command_args=("$3")

timeout_in_sec=60 # <== includes image pulling

echo " PARAMS: image --> $_int_image, command --> ${_int_command[*]}, args --> ${_int_command_args[*]}"

if [ "${_int_command[*]}" == "null" ] && [ "${_int_command_args[*]}" == "null" ]; then
echo " COMMAND: \"kubectl run test-terminating -n ${TEST_NAMESPACE} --attach=false --restart=Never --image=$_int_image\""
kubectl run test-terminating -n "${TEST_NAMESPACE}" --attach=false --restart=Never --image="$_int_image" >/dev/null 2>&1
elif [ "${_int_command[*]}" == "null" ]; then
echo " COMMAND: \"kubectl run test-terminating -n ${TEST_NAMESPACE} --attach=false --restart=Never --image=$_int_image -- ${_int_command_args[*]}\""
kubectl run test-terminating -n "${TEST_NAMESPACE}" --attach=false --restart=Never --image="$_int_image" -- ${_int_command_args[*]} >/dev/null 2>&1
elif [ "${_int_command_args[*]}" == "null" ]; then
echo " COMMAND: \"kubectl run test-terminating -n ${TEST_NAMESPACE} --attach=false --restart=Never --image=$_int_image --command -- ${_int_command[*]}\""
kubectl run test-terminating -n "${TEST_NAMESPACE}" --attach=false --restart=Never --image="$_int_image" --command=true -- ${_int_command[*]} >/dev/null 2>&1
else
echo " COMMAND: \"kubectl run test-terminating -n ${TEST_NAMESPACE} --attach=false --restart=Never --image=$_int_image --command -- ${_int_command[*]} ${_int_command_args[*]}\""
kubectl run test-terminating -n "${TEST_NAMESPACE}" --attach=false --restart=Never --image="$_int_image" --command=true -- ${_int_command[*]} ${_int_command_args[*]} >/dev/null 2>&1
fi

if kubectl wait pods -n "${TEST_NAMESPACE}" test-terminating --for condition=Ready --timeout=${timeout_in_sec}s >/dev/null 2>&1; then
echo " SUCCESS: The container started successfully and didn't terminate"
kubectl delete pod test-terminating -n "${TEST_NAMESPACE}" >/dev/null 2>&1
return 0
else
echo " ERROR: Failed to reach \"Ready\" condition after $timeout_in_sec seconds"
echo " ↓↓↓↓↓↓↓↓↓ Pod description ↓↓↓↓↓↓↓↓"
echo ""
kubectl describe pod -n "${TEST_NAMESPACE}" test-terminating
echo ""
echo " ↑↑↑↑↑↑↑↑↑ Pod description ↑↑↑↑↑↑↑↑"
kubectl delete pod test-terminating -n "${TEST_NAMESPACE}" >/dev/null 2>&1
return 1
fi
}

YQ_PATH=${YQ_PATH:-yq}
TEST_NAMESPACE=${TEST_NAMESPACE:-default}

find "$DEVFILES_DIR" -maxdepth 1 -type d ! -path "$DEVFILES_DIR" -print0 | while IFS= read -r -d '' devfile_dir; do

devfile_path=$devfile_dir/devfile.yaml

echo "======================="
echo "Testing ${devfile_path}"

IFS=" " read -r -a components_num <<< "$(getContainerComponentsNum "$devfile_path")"
# components_num=($(getContainerComponentsNum "$devfile_path"))

# if there are zero components of type container skip
if (( components_num = 0 )); then
echo "WARNING: Devfile with no container component found (""$devfile_path""). Skipping."
echo "======================="
continue
fi

# if there is more than one component of type container skip (we may want to cover this case in the future)
if (( components_num > 1 )); then
echo "WARNING: Devfile with more than one container component found (""$devfile_path""). Skipping."
echo "======================="
continue
fi

name=$(getName "$devfile_path")
image=$(getFirstContainerComponentImage "$devfile_path")

declare -a command=()
IFS=" " read -r -a command <<< "$(getFirstContainerComponentCommand "$devfile_path")"

declare -a command_args=()
IFS=" " read -r -a command_args <<< "$(getFirstContainerComponentArgs "$devfile_path")"

if (( ${#command[@]} > 0 )); then
command_string="${command[*]}"
else
command_string="null"
fi

if (( ${#command_args[@]} > 0 )); then
command_args_string="${command_args[*]}"
else
command_args_string="null"
fi

isNonTerminating "${image}" "${command_string}" "${command_args_string}";

echo "======================="
done

exit 0
File renamed without changes.