From 9abb876c7657b78f2e44189d1ab5084b4ff0b2da Mon Sep 17 00:00:00 2001 From: David Simansky Date: Tue, 12 Jul 2022 19:27:13 +0200 Subject: [PATCH] Add --probe-liveness and --probe-readiness flags (#1697) * Add --probe-liveness and --probe-readiness flags * Add more unit test * Add even more unit tests * Fix spelling * Add error coverage * Update pkg/kn/flags/podspec.go Co-authored-by: Gunjan Vyas * Regen docs * Fix error message * Split probe opts into standalone flag * Fix docs Co-authored-by: Gunjan Vyas --- docs/cmd/kn_container_add.md | 38 +-- docs/cmd/kn_service_apply.md | 4 + docs/cmd/kn_service_create.md | 4 + docs/cmd/kn_service_update.md | 4 + docs/cmd/kn_source_container_create.md | 44 +-- docs/cmd/kn_source_container_update.md | 44 +-- pkg/kn/flags/podspec.go | 47 ++++ pkg/kn/flags/podspec_helper.go | 165 +++++++++++ pkg/kn/flags/podspec_helper_test.go | 372 +++++++++++++++++++++++++ pkg/kn/flags/podspec_test.go | 15 +- 10 files changed, 679 insertions(+), 58 deletions(-) diff --git a/docs/cmd/kn_container_add.md b/docs/cmd/kn_container_add.md index a78b1392f8..814e7f98b3 100644 --- a/docs/cmd/kn_container_add.md +++ b/docs/cmd/kn_container_add.md @@ -28,23 +28,27 @@ kn container add NAME ### Options ``` - --arg stringArray Add argument to the container command. Example: --arg myArg1 --arg --myArg2 --arg myArg3=3. You can use this flag multiple times. - --cmd stringArray Specify command to be used as entrypoint instead of default one. Example: --cmd /app/start or --cmd sh --cmd /app/start.sh or --cmd /app/start --arg myArg to pass additional arguments. - --containers string Specify path to file including definition for additional containers, alternatively use '-' to read from stdin. Example: --containers ./containers.yaml or --containers -. - -e, --env stringArray Environment variable to set. NAME=value; you may provide this flag any number of times to set multiple environment variables. To unset, specify the environment variable name followed by a "-" (e.g., NAME-). - --env-file string Path to a file containing environment variables (e.g. --env-file=/home/knative/service1/env). - --env-from stringArray Add environment variables from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret:). Example: --env-from cm:myconfigmap or --env-from secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --env-from cm:myconfigmap-. - --env-value-from stringArray Add environment variable from a value of key in ConfigMap (prefix cm: or config-map:) or a Secret (prefix sc: or secret:). Example: --env-value-from NAME=cm:myconfigmap:key or --env-value-from NAME=secret:mysecret:key. You can use this flag multiple times. To unset a value from a ConfigMap/Secret key reference, append "-" to the key, e.g. --env-value-from ENV-. - -h, --help help for add - --image string Image to run. - --limit strings The resource requirement limits for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource limit, append "-" to the resource name, e.g. '--limit memory-'. - --mount stringArray Mount a ConfigMap (prefix cm: or config-map:), a Secret (prefix secret: or sc:), an EmptyDir (prefix ed: or emptyDir:), a PersistentVolumeClaim (prefix pvc: or persistentVolumeClaim) or an existing Volume (without any prefix) on the specified directory. Example: --mount /mydir=cm:myconfigmap, --mount /mydir=secret:mysecret, --mount /mydir=emptyDir:myvol or --mount /mydir=myvolume. When a configmap or a secret is specified, a corresponding volume is automatically generated. You can specify a volume subpath by following the volume name with slash separated path. Example: --mount /mydir=cm:myconfigmap/subpath/to/be/mounted. You can use this flag multiple times. For unmounting a directory, append "-", e.g. --mount /mydir-, which also removes any auto-generated volume. - -p, --port string The port where application listens on, in the format 'NAME:PORT', where 'NAME' is optional. Examples: '--port h2c:8080' , '--port 8080'. - --pull-policy string Image pull policy. Valid values (case insensitive): Always | Never | IfNotPresent - --pull-secret string Image pull secret to set. An empty argument ("") clears the pull secret. The referenced secret must exist in the service's namespace. - --request strings The resource requirement requests for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource request, append "-" to the resource name, e.g. '--request cpu-'. - --service-account string Service account name to set. An empty argument ("") clears the service account. The referenced service account must exist in the service's namespace. - --user int The user ID to run the container (e.g., 1001). + --arg stringArray Add argument to the container command. Example: --arg myArg1 --arg --myArg2 --arg myArg3=3. You can use this flag multiple times. + --cmd stringArray Specify command to be used as entrypoint instead of default one. Example: --cmd /app/start or --cmd sh --cmd /app/start.sh or --cmd /app/start --arg myArg to pass additional arguments. + --containers string Specify path to file including definition for additional containers, alternatively use '-' to read from stdin. Example: --containers ./containers.yaml or --containers -. + -e, --env stringArray Environment variable to set. NAME=value; you may provide this flag any number of times to set multiple environment variables. To unset, specify the environment variable name followed by a "-" (e.g., NAME-). + --env-file string Path to a file containing environment variables (e.g. --env-file=/home/knative/service1/env). + --env-from stringArray Add environment variables from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret:). Example: --env-from cm:myconfigmap or --env-from secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --env-from cm:myconfigmap-. + --env-value-from stringArray Add environment variable from a value of key in ConfigMap (prefix cm: or config-map:) or a Secret (prefix sc: or secret:). Example: --env-value-from NAME=cm:myconfigmap:key or --env-value-from NAME=secret:mysecret:key. You can use this flag multiple times. To unset a value from a ConfigMap/Secret key reference, append "-" to the key, e.g. --env-value-from ENV-. + -h, --help help for add + --image string Image to run. + --limit strings The resource requirement limits for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource limit, append "-" to the resource name, e.g. '--limit memory-'. + --mount stringArray Mount a ConfigMap (prefix cm: or config-map:), a Secret (prefix secret: or sc:), an EmptyDir (prefix ed: or emptyDir:), a PersistentVolumeClaim (prefix pvc: or persistentVolumeClaim) or an existing Volume (without any prefix) on the specified directory. Example: --mount /mydir=cm:myconfigmap, --mount /mydir=secret:mysecret, --mount /mydir=emptyDir:myvol or --mount /mydir=myvolume. When a configmap or a secret is specified, a corresponding volume is automatically generated. You can specify a volume subpath by following the volume name with slash separated path. Example: --mount /mydir=cm:myconfigmap/subpath/to/be/mounted. You can use this flag multiple times. For unmounting a directory, append "-", e.g. --mount /mydir-, which also removes any auto-generated volume. + -p, --port string The port where application listens on, in the format 'NAME:PORT', where 'NAME' is optional. Examples: '--port h2c:8080' , '--port 8080'. + --probe-liveness string Add liveness probe to Service deployment. Supported probe types are HTTGet, Exec and TCPSocket. Format: [http,https]:host:port:path, exec:cmd[,cmd,...], tcp:host:port. + --probe-liveness-opts string Add common options to liveness probe. Common opts (comma separated, case insensitive): InitialDelaySeconds=, FailureThreshold=, SuccessThreshold=, PeriodSeconds=, TimeoutSeconds= + --probe-readiness string Add readiness probe to Service deployment. Supported probe types are HTTGet, Exec and TCPSocket. Format: [http,https]:host:port:path, exec:cmd[,cmd,...], tcp:host:port. + --probe-readiness-opts string Add common options to readiness probe. Common opts (comma separated, case insensitive): InitialDelaySeconds=, FailureThreshold=, SuccessThreshold=, PeriodSeconds=, TimeoutSeconds= + --pull-policy string Image pull policy. Valid values (case insensitive): Always | Never | IfNotPresent + --pull-secret string Image pull secret to set. An empty argument ("") clears the pull secret. The referenced secret must exist in the service's namespace. + --request strings The resource requirement requests for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource request, append "-" to the resource name, e.g. '--request cpu-'. + --service-account string Service account name to set. An empty argument ("") clears the service account. The referenced service account must exist in the service's namespace. + --user int The user ID to run the container (e.g., 1001). ``` ### Options inherited from parent commands diff --git a/docs/cmd/kn_service_apply.md b/docs/cmd/kn_service_apply.md index 526d713db3..04448c46fb 100644 --- a/docs/cmd/kn_service_apply.md +++ b/docs/cmd/kn_service_apply.md @@ -56,6 +56,10 @@ kn service apply s0 --filename my-svc.yml --no-lock-to-digest Do not keep the running image for the service constant when not explicitly specifying the image. (--no-lock-to-digest pulls the image tag afresh with each new revision) --no-wait Do not wait for 'service apply' operation to be completed. -p, --port string The port where application listens on, in the format 'NAME:PORT', where 'NAME' is optional. Examples: '--port h2c:8080' , '--port 8080'. + --probe-liveness string Add liveness probe to Service deployment. Supported probe types are HTTGet, Exec and TCPSocket. Format: [http,https]:host:port:path, exec:cmd[,cmd,...], tcp:host:port. + --probe-liveness-opts string Add common options to liveness probe. Common opts (comma separated, case insensitive): InitialDelaySeconds=, FailureThreshold=, SuccessThreshold=, PeriodSeconds=, TimeoutSeconds= + --probe-readiness string Add readiness probe to Service deployment. Supported probe types are HTTGet, Exec and TCPSocket. Format: [http,https]:host:port:path, exec:cmd[,cmd,...], tcp:host:port. + --probe-readiness-opts string Add common options to readiness probe. Common opts (comma separated, case insensitive): InitialDelaySeconds=, FailureThreshold=, SuccessThreshold=, PeriodSeconds=, TimeoutSeconds= --pull-policy string Image pull policy. Valid values (case insensitive): Always | Never | IfNotPresent --pull-secret string Image pull secret to set. An empty argument ("") clears the pull secret. The referenced secret must exist in the service's namespace. --request strings The resource requirement requests for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource request, append "-" to the resource name, e.g. '--request cpu-'. diff --git a/docs/cmd/kn_service_create.md b/docs/cmd/kn_service_create.md index cd6eef9a88..3133ec407e 100644 --- a/docs/cmd/kn_service_create.md +++ b/docs/cmd/kn_service_create.md @@ -81,6 +81,10 @@ kn service create NAME --image IMAGE --no-lock-to-digest Do not keep the running image for the service constant when not explicitly specifying the image. (--no-lock-to-digest pulls the image tag afresh with each new revision) --no-wait Do not wait for 'service create' operation to be completed. -p, --port string The port where application listens on, in the format 'NAME:PORT', where 'NAME' is optional. Examples: '--port h2c:8080' , '--port 8080'. + --probe-liveness string Add liveness probe to Service deployment. Supported probe types are HTTGet, Exec and TCPSocket. Format: [http,https]:host:port:path, exec:cmd[,cmd,...], tcp:host:port. + --probe-liveness-opts string Add common options to liveness probe. Common opts (comma separated, case insensitive): InitialDelaySeconds=, FailureThreshold=, SuccessThreshold=, PeriodSeconds=, TimeoutSeconds= + --probe-readiness string Add readiness probe to Service deployment. Supported probe types are HTTGet, Exec and TCPSocket. Format: [http,https]:host:port:path, exec:cmd[,cmd,...], tcp:host:port. + --probe-readiness-opts string Add common options to readiness probe. Common opts (comma separated, case insensitive): InitialDelaySeconds=, FailureThreshold=, SuccessThreshold=, PeriodSeconds=, TimeoutSeconds= --pull-policy string Image pull policy. Valid values (case insensitive): Always | Never | IfNotPresent --pull-secret string Image pull secret to set. An empty argument ("") clears the pull secret. The referenced secret must exist in the service's namespace. --request strings The resource requirement requests for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource request, append "-" to the resource name, e.g. '--request cpu-'. diff --git a/docs/cmd/kn_service_update.md b/docs/cmd/kn_service_update.md index 9105e41efc..42295defe0 100644 --- a/docs/cmd/kn_service_update.md +++ b/docs/cmd/kn_service_update.md @@ -68,6 +68,10 @@ kn service update NAME --no-lock-to-digest Do not keep the running image for the service constant when not explicitly specifying the image. (--no-lock-to-digest pulls the image tag afresh with each new revision) --no-wait Do not wait for 'service update' operation to be completed. -p, --port string The port where application listens on, in the format 'NAME:PORT', where 'NAME' is optional. Examples: '--port h2c:8080' , '--port 8080'. + --probe-liveness string Add liveness probe to Service deployment. Supported probe types are HTTGet, Exec and TCPSocket. Format: [http,https]:host:port:path, exec:cmd[,cmd,...], tcp:host:port. + --probe-liveness-opts string Add common options to liveness probe. Common opts (comma separated, case insensitive): InitialDelaySeconds=, FailureThreshold=, SuccessThreshold=, PeriodSeconds=, TimeoutSeconds= + --probe-readiness string Add readiness probe to Service deployment. Supported probe types are HTTGet, Exec and TCPSocket. Format: [http,https]:host:port:path, exec:cmd[,cmd,...], tcp:host:port. + --probe-readiness-opts string Add common options to readiness probe. Common opts (comma separated, case insensitive): InitialDelaySeconds=, FailureThreshold=, SuccessThreshold=, PeriodSeconds=, TimeoutSeconds= --pull-policy string Image pull policy. Valid values (case insensitive): Always | Never | IfNotPresent --pull-secret string Image pull secret to set. An empty argument ("") clears the pull secret. The referenced secret must exist in the service's namespace. --request strings The resource requirement requests for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource request, append "-" to the resource name, e.g. '--request cpu-'. diff --git a/docs/cmd/kn_source_container_create.md b/docs/cmd/kn_source_container_create.md index c1130f8e64..89eac8a443 100644 --- a/docs/cmd/kn_source_container_create.md +++ b/docs/cmd/kn_source_container_create.md @@ -17,26 +17,30 @@ kn source container create NAME --image IMAGE --sink SINK ### Options ``` - --arg stringArray Add argument to the container command. Example: --arg myArg1 --arg --myArg2 --arg myArg3=3. You can use this flag multiple times. - --cmd stringArray Specify command to be used as entrypoint instead of default one. Example: --cmd /app/start or --cmd sh --cmd /app/start.sh or --cmd /app/start --arg myArg to pass additional arguments. - --containers string Specify path to file including definition for additional containers, alternatively use '-' to read from stdin. Example: --containers ./containers.yaml or --containers -. - -e, --env stringArray Environment variable to set. NAME=value; you may provide this flag any number of times to set multiple environment variables. To unset, specify the environment variable name followed by a "-" (e.g., NAME-). - --env-file string Path to a file containing environment variables (e.g. --env-file=/home/knative/service1/env). - --env-from stringArray Add environment variables from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret:). Example: --env-from cm:myconfigmap or --env-from secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --env-from cm:myconfigmap-. - --env-value-from stringArray Add environment variable from a value of key in ConfigMap (prefix cm: or config-map:) or a Secret (prefix sc: or secret:). Example: --env-value-from NAME=cm:myconfigmap:key or --env-value-from NAME=secret:mysecret:key. You can use this flag multiple times. To unset a value from a ConfigMap/Secret key reference, append "-" to the key, e.g. --env-value-from ENV-. - -h, --help help for create - --image string Image to run. - --limit strings The resource requirement limits for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource limit, append "-" to the resource name, e.g. '--limit memory-'. - --mount stringArray Mount a ConfigMap (prefix cm: or config-map:), a Secret (prefix secret: or sc:), an EmptyDir (prefix ed: or emptyDir:), a PersistentVolumeClaim (prefix pvc: or persistentVolumeClaim) or an existing Volume (without any prefix) on the specified directory. Example: --mount /mydir=cm:myconfigmap, --mount /mydir=secret:mysecret, --mount /mydir=emptyDir:myvol or --mount /mydir=myvolume. When a configmap or a secret is specified, a corresponding volume is automatically generated. You can specify a volume subpath by following the volume name with slash separated path. Example: --mount /mydir=cm:myconfigmap/subpath/to/be/mounted. You can use this flag multiple times. For unmounting a directory, append "-", e.g. --mount /mydir-, which also removes any auto-generated volume. - -n, --namespace string Specify the namespace to operate in. - -p, --port string The port where application listens on, in the format 'NAME:PORT', where 'NAME' is optional. Examples: '--port h2c:8080' , '--port 8080'. - --pull-policy string Image pull policy. Valid values (case insensitive): Always | Never | IfNotPresent - --pull-secret string Image pull secret to set. An empty argument ("") clears the pull secret. The referenced secret must exist in the service's namespace. - --request strings The resource requirement requests for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource request, append "-" to the resource name, e.g. '--request cpu-'. - --service-account string Service account name to set. An empty argument ("") clears the service account. The referenced service account must exist in the service's namespace. - -s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. If a prefix is not provided, it is considered as a Knative service in the current namespace. If referring to a Knative service in another namespace, 'ksvc:name:namespace' combination must be provided explicitly. - --user int The user ID to run the container (e.g., 1001). - --volume stringArray Add a volume from a ConfigMap (prefix cm: or config-map:) a Secret (prefix secret: or sc:), an EmptyDir (prefix ed: or emptyDir:) or a PersistentVolumeClaim (prefix pvc: or persistentVolumeClaim). Example: --volume myvolume=cm:myconfigmap, --volume myvolume=secret:mysecret or --volume emptyDir:myvol:size=1Gi,type=Memory. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --volume myvolume-. + --arg stringArray Add argument to the container command. Example: --arg myArg1 --arg --myArg2 --arg myArg3=3. You can use this flag multiple times. + --cmd stringArray Specify command to be used as entrypoint instead of default one. Example: --cmd /app/start or --cmd sh --cmd /app/start.sh or --cmd /app/start --arg myArg to pass additional arguments. + --containers string Specify path to file including definition for additional containers, alternatively use '-' to read from stdin. Example: --containers ./containers.yaml or --containers -. + -e, --env stringArray Environment variable to set. NAME=value; you may provide this flag any number of times to set multiple environment variables. To unset, specify the environment variable name followed by a "-" (e.g., NAME-). + --env-file string Path to a file containing environment variables (e.g. --env-file=/home/knative/service1/env). + --env-from stringArray Add environment variables from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret:). Example: --env-from cm:myconfigmap or --env-from secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --env-from cm:myconfigmap-. + --env-value-from stringArray Add environment variable from a value of key in ConfigMap (prefix cm: or config-map:) or a Secret (prefix sc: or secret:). Example: --env-value-from NAME=cm:myconfigmap:key or --env-value-from NAME=secret:mysecret:key. You can use this flag multiple times. To unset a value from a ConfigMap/Secret key reference, append "-" to the key, e.g. --env-value-from ENV-. + -h, --help help for create + --image string Image to run. + --limit strings The resource requirement limits for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource limit, append "-" to the resource name, e.g. '--limit memory-'. + --mount stringArray Mount a ConfigMap (prefix cm: or config-map:), a Secret (prefix secret: or sc:), an EmptyDir (prefix ed: or emptyDir:), a PersistentVolumeClaim (prefix pvc: or persistentVolumeClaim) or an existing Volume (without any prefix) on the specified directory. Example: --mount /mydir=cm:myconfigmap, --mount /mydir=secret:mysecret, --mount /mydir=emptyDir:myvol or --mount /mydir=myvolume. When a configmap or a secret is specified, a corresponding volume is automatically generated. You can specify a volume subpath by following the volume name with slash separated path. Example: --mount /mydir=cm:myconfigmap/subpath/to/be/mounted. You can use this flag multiple times. For unmounting a directory, append "-", e.g. --mount /mydir-, which also removes any auto-generated volume. + -n, --namespace string Specify the namespace to operate in. + -p, --port string The port where application listens on, in the format 'NAME:PORT', where 'NAME' is optional. Examples: '--port h2c:8080' , '--port 8080'. + --probe-liveness string Add liveness probe to Service deployment. Supported probe types are HTTGet, Exec and TCPSocket. Format: [http,https]:host:port:path, exec:cmd[,cmd,...], tcp:host:port. + --probe-liveness-opts string Add common options to liveness probe. Common opts (comma separated, case insensitive): InitialDelaySeconds=, FailureThreshold=, SuccessThreshold=, PeriodSeconds=, TimeoutSeconds= + --probe-readiness string Add readiness probe to Service deployment. Supported probe types are HTTGet, Exec and TCPSocket. Format: [http,https]:host:port:path, exec:cmd[,cmd,...], tcp:host:port. + --probe-readiness-opts string Add common options to readiness probe. Common opts (comma separated, case insensitive): InitialDelaySeconds=, FailureThreshold=, SuccessThreshold=, PeriodSeconds=, TimeoutSeconds= + --pull-policy string Image pull policy. Valid values (case insensitive): Always | Never | IfNotPresent + --pull-secret string Image pull secret to set. An empty argument ("") clears the pull secret. The referenced secret must exist in the service's namespace. + --request strings The resource requirement requests for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource request, append "-" to the resource name, e.g. '--request cpu-'. + --service-account string Service account name to set. An empty argument ("") clears the service account. The referenced service account must exist in the service's namespace. + -s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. If a prefix is not provided, it is considered as a Knative service in the current namespace. If referring to a Knative service in another namespace, 'ksvc:name:namespace' combination must be provided explicitly. + --user int The user ID to run the container (e.g., 1001). + --volume stringArray Add a volume from a ConfigMap (prefix cm: or config-map:) a Secret (prefix secret: or sc:), an EmptyDir (prefix ed: or emptyDir:) or a PersistentVolumeClaim (prefix pvc: or persistentVolumeClaim). Example: --volume myvolume=cm:myconfigmap, --volume myvolume=secret:mysecret or --volume emptyDir:myvol:size=1Gi,type=Memory. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --volume myvolume-. ``` ### Options inherited from parent commands diff --git a/docs/cmd/kn_source_container_update.md b/docs/cmd/kn_source_container_update.md index 2dad84f0f8..8711382815 100644 --- a/docs/cmd/kn_source_container_update.md +++ b/docs/cmd/kn_source_container_update.md @@ -17,26 +17,30 @@ kn source container update NAME --image IMAGE ### Options ``` - --arg stringArray Add argument to the container command. Example: --arg myArg1 --arg --myArg2 --arg myArg3=3. You can use this flag multiple times. - --cmd stringArray Specify command to be used as entrypoint instead of default one. Example: --cmd /app/start or --cmd sh --cmd /app/start.sh or --cmd /app/start --arg myArg to pass additional arguments. - --containers string Specify path to file including definition for additional containers, alternatively use '-' to read from stdin. Example: --containers ./containers.yaml or --containers -. - -e, --env stringArray Environment variable to set. NAME=value; you may provide this flag any number of times to set multiple environment variables. To unset, specify the environment variable name followed by a "-" (e.g., NAME-). - --env-file string Path to a file containing environment variables (e.g. --env-file=/home/knative/service1/env). - --env-from stringArray Add environment variables from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret:). Example: --env-from cm:myconfigmap or --env-from secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --env-from cm:myconfigmap-. - --env-value-from stringArray Add environment variable from a value of key in ConfigMap (prefix cm: or config-map:) or a Secret (prefix sc: or secret:). Example: --env-value-from NAME=cm:myconfigmap:key or --env-value-from NAME=secret:mysecret:key. You can use this flag multiple times. To unset a value from a ConfigMap/Secret key reference, append "-" to the key, e.g. --env-value-from ENV-. - -h, --help help for update - --image string Image to run. - --limit strings The resource requirement limits for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource limit, append "-" to the resource name, e.g. '--limit memory-'. - --mount stringArray Mount a ConfigMap (prefix cm: or config-map:), a Secret (prefix secret: or sc:), an EmptyDir (prefix ed: or emptyDir:), a PersistentVolumeClaim (prefix pvc: or persistentVolumeClaim) or an existing Volume (without any prefix) on the specified directory. Example: --mount /mydir=cm:myconfigmap, --mount /mydir=secret:mysecret, --mount /mydir=emptyDir:myvol or --mount /mydir=myvolume. When a configmap or a secret is specified, a corresponding volume is automatically generated. You can specify a volume subpath by following the volume name with slash separated path. Example: --mount /mydir=cm:myconfigmap/subpath/to/be/mounted. You can use this flag multiple times. For unmounting a directory, append "-", e.g. --mount /mydir-, which also removes any auto-generated volume. - -n, --namespace string Specify the namespace to operate in. - -p, --port string The port where application listens on, in the format 'NAME:PORT', where 'NAME' is optional. Examples: '--port h2c:8080' , '--port 8080'. - --pull-policy string Image pull policy. Valid values (case insensitive): Always | Never | IfNotPresent - --pull-secret string Image pull secret to set. An empty argument ("") clears the pull secret. The referenced secret must exist in the service's namespace. - --request strings The resource requirement requests for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource request, append "-" to the resource name, e.g. '--request cpu-'. - --service-account string Service account name to set. An empty argument ("") clears the service account. The referenced service account must exist in the service's namespace. - -s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. If a prefix is not provided, it is considered as a Knative service in the current namespace. If referring to a Knative service in another namespace, 'ksvc:name:namespace' combination must be provided explicitly. - --user int The user ID to run the container (e.g., 1001). - --volume stringArray Add a volume from a ConfigMap (prefix cm: or config-map:) a Secret (prefix secret: or sc:), an EmptyDir (prefix ed: or emptyDir:) or a PersistentVolumeClaim (prefix pvc: or persistentVolumeClaim). Example: --volume myvolume=cm:myconfigmap, --volume myvolume=secret:mysecret or --volume emptyDir:myvol:size=1Gi,type=Memory. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --volume myvolume-. + --arg stringArray Add argument to the container command. Example: --arg myArg1 --arg --myArg2 --arg myArg3=3. You can use this flag multiple times. + --cmd stringArray Specify command to be used as entrypoint instead of default one. Example: --cmd /app/start or --cmd sh --cmd /app/start.sh or --cmd /app/start --arg myArg to pass additional arguments. + --containers string Specify path to file including definition for additional containers, alternatively use '-' to read from stdin. Example: --containers ./containers.yaml or --containers -. + -e, --env stringArray Environment variable to set. NAME=value; you may provide this flag any number of times to set multiple environment variables. To unset, specify the environment variable name followed by a "-" (e.g., NAME-). + --env-file string Path to a file containing environment variables (e.g. --env-file=/home/knative/service1/env). + --env-from stringArray Add environment variables from a ConfigMap (prefix cm: or config-map:) or a Secret (prefix secret:). Example: --env-from cm:myconfigmap or --env-from secret:mysecret. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --env-from cm:myconfigmap-. + --env-value-from stringArray Add environment variable from a value of key in ConfigMap (prefix cm: or config-map:) or a Secret (prefix sc: or secret:). Example: --env-value-from NAME=cm:myconfigmap:key or --env-value-from NAME=secret:mysecret:key. You can use this flag multiple times. To unset a value from a ConfigMap/Secret key reference, append "-" to the key, e.g. --env-value-from ENV-. + -h, --help help for update + --image string Image to run. + --limit strings The resource requirement limits for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource limit, append "-" to the resource name, e.g. '--limit memory-'. + --mount stringArray Mount a ConfigMap (prefix cm: or config-map:), a Secret (prefix secret: or sc:), an EmptyDir (prefix ed: or emptyDir:), a PersistentVolumeClaim (prefix pvc: or persistentVolumeClaim) or an existing Volume (without any prefix) on the specified directory. Example: --mount /mydir=cm:myconfigmap, --mount /mydir=secret:mysecret, --mount /mydir=emptyDir:myvol or --mount /mydir=myvolume. When a configmap or a secret is specified, a corresponding volume is automatically generated. You can specify a volume subpath by following the volume name with slash separated path. Example: --mount /mydir=cm:myconfigmap/subpath/to/be/mounted. You can use this flag multiple times. For unmounting a directory, append "-", e.g. --mount /mydir-, which also removes any auto-generated volume. + -n, --namespace string Specify the namespace to operate in. + -p, --port string The port where application listens on, in the format 'NAME:PORT', where 'NAME' is optional. Examples: '--port h2c:8080' , '--port 8080'. + --probe-liveness string Add liveness probe to Service deployment. Supported probe types are HTTGet, Exec and TCPSocket. Format: [http,https]:host:port:path, exec:cmd[,cmd,...], tcp:host:port. + --probe-liveness-opts string Add common options to liveness probe. Common opts (comma separated, case insensitive): InitialDelaySeconds=, FailureThreshold=, SuccessThreshold=, PeriodSeconds=, TimeoutSeconds= + --probe-readiness string Add readiness probe to Service deployment. Supported probe types are HTTGet, Exec and TCPSocket. Format: [http,https]:host:port:path, exec:cmd[,cmd,...], tcp:host:port. + --probe-readiness-opts string Add common options to readiness probe. Common opts (comma separated, case insensitive): InitialDelaySeconds=, FailureThreshold=, SuccessThreshold=, PeriodSeconds=, TimeoutSeconds= + --pull-policy string Image pull policy. Valid values (case insensitive): Always | Never | IfNotPresent + --pull-secret string Image pull secret to set. An empty argument ("") clears the pull secret. The referenced secret must exist in the service's namespace. + --request strings The resource requirement requests for this Service. For example, 'cpu=100m,memory=256Mi'. You can use this flag multiple times. To unset a resource request, append "-" to the resource name, e.g. '--request cpu-'. + --service-account string Service account name to set. An empty argument ("") clears the service account. The referenced service account must exist in the service's namespace. + -s, --sink string Addressable sink for events. You can specify a broker, channel, Knative service or URI. Examples: '--sink broker:nest' for a broker 'nest', '--sink channel:pipe' for a channel 'pipe', '--sink ksvc:mysvc:mynamespace' for a Knative service 'mysvc' in another namespace 'mynamespace', '--sink https://event.receiver.uri' for an URI with an 'http://' or 'https://' schema, '--sink ksvc:receiver' or simply '--sink receiver' for a Knative service 'receiver' in the current namespace. If a prefix is not provided, it is considered as a Knative service in the current namespace. If referring to a Knative service in another namespace, 'ksvc:name:namespace' combination must be provided explicitly. + --user int The user ID to run the container (e.g., 1001). + --volume stringArray Add a volume from a ConfigMap (prefix cm: or config-map:) a Secret (prefix secret: or sc:), an EmptyDir (prefix ed: or emptyDir:) or a PersistentVolumeClaim (prefix pvc: or persistentVolumeClaim). Example: --volume myvolume=cm:myconfigmap, --volume myvolume=secret:mysecret or --volume emptyDir:myvol:size=1Gi,type=Memory. You can use this flag multiple times. To unset a ConfigMap/Secret reference, append "-" to the name, e.g. --volume myvolume-. ``` ### Options inherited from parent commands diff --git a/pkg/kn/flags/podspec.go b/pkg/kn/flags/podspec.go index 584c7d4db7..760941486b 100644 --- a/pkg/kn/flags/podspec.go +++ b/pkg/kn/flags/podspec.go @@ -40,6 +40,11 @@ type PodSpecFlags struct { Command []string Arg []string + LivenessProbe string + LivenessProbeOpts string + ReadinessProbe string + ReadinessProbeOpts string + ExtraContainers string Resources ResourceOptions @@ -177,6 +182,24 @@ func (p *PodSpecFlags) AddFlags(flagset *pflag.FlagSet) []string { "Example: --containers ./containers.yaml or --containers -.") flagNames = append(flagNames, "containers") + // Probes + commonProbeDescription := "Supported probe types are HTTGet, Exec and TCPSocket. " + + "Format: [http,https]:host:port:path, exec:cmd[,cmd,...], tcp:host:port." + commonProbeOptsDesc := "Common opts (comma separated, case insensitive): InitialDelaySeconds=, FailureThreshold=, " + + "SuccessThreshold=, PeriodSeconds=, TimeoutSeconds=" + flagset.StringVarP(&p.LivenessProbe, "probe-liveness", "", "", "Add liveness probe to Service deployment. "+ + commonProbeDescription) + flagNames = append(flagNames, "probe-liveness") + flagset.StringVarP(&p.LivenessProbeOpts, "probe-liveness-opts", "", "", "Add common options to liveness probe. "+ + commonProbeOptsDesc) + flagNames = append(flagNames, "probe-liveness-opts") + flagset.StringVarP(&p.ReadinessProbe, "probe-readiness", "", "", "Add readiness probe to Service deployment. "+ + commonProbeDescription) + flagNames = append(flagNames, "probe-readiness") + flagset.StringVarP(&p.ReadinessProbeOpts, "probe-readiness-opts", "", "", "Add common options to readiness probe. "+ + commonProbeOptsDesc) + flagNames = append(flagNames, "probe-liveness-opts") + flagset.StringSliceVar(&p.Resources.Limits, "limit", nil, @@ -356,5 +379,29 @@ func (p *PodSpecFlags) ResolvePodSpec(podSpec *corev1.PodSpec, flags *pflag.Flag UpdateContainers(podSpec, fromFile.Containers) } + if flags.Changed("probe-liveness") { + if err := UpdateLivenessProbe(podSpec, p.LivenessProbe); err != nil { + return err + } + } + + if flags.Changed("probe-liveness-opts") { + if err := UpdateLivenessProbeOpts(podSpec, p.LivenessProbeOpts); err != nil { + return err + } + } + + if flags.Changed("probe-readiness") { + if err := UpdateReadinessProbe(podSpec, p.ReadinessProbe); err != nil { + return err + } + } + + if flags.Changed("probe-readiness-opts") { + if err := UpdateReadinessProbeOpts(podSpec, p.ReadinessProbeOpts); err != nil { + return err + } + } + return nil } diff --git a/pkg/kn/flags/podspec_helper.go b/pkg/kn/flags/podspec_helper.go index 98e4e84bfc..ac7bb7f628 100644 --- a/pkg/kn/flags/podspec_helper.go +++ b/pkg/kn/flags/podspec_helper.go @@ -20,6 +20,8 @@ import ( "strconv" "strings" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/api/resource" "k8s.io/apimachinery/pkg/util/yaml" @@ -310,6 +312,60 @@ func UpdateContainers(spec *corev1.PodSpec, containers []corev1.Container) { } } +// UpdateLivenessProbe updates container liveness probe based on provided string +func UpdateLivenessProbe(spec *corev1.PodSpec, probeString string) error { + c := containerOfPodSpec(spec) + handler, err := resolveProbeHandler(probeString) + if err != nil { + return err + } + if c.LivenessProbe == nil { + c.LivenessProbe = &corev1.Probe{} + } + c.LivenessProbe.ProbeHandler = *handler + return nil +} + +// UpdateLivenessProbeOpts updates container liveness probe commons options based on provided string +func UpdateLivenessProbeOpts(spec *corev1.PodSpec, probeString string) error { + c := containerOfPodSpec(spec) + if c.LivenessProbe == nil { + c.LivenessProbe = &corev1.Probe{} + } + err := resolveProbeOptions(c.LivenessProbe, probeString) + if err != nil { + return err + } + return nil +} + +// UpdateReadinessProbe updates container readiness probe based on provided string +func UpdateReadinessProbe(spec *corev1.PodSpec, probeString string) error { + c := containerOfPodSpec(spec) + handler, err := resolveProbeHandler(probeString) + if err != nil { + return err + } + if c.ReadinessProbe == nil { + c.ReadinessProbe = &corev1.Probe{} + } + c.ReadinessProbe.ProbeHandler = *handler + return nil +} + +// UpdateReadinessProbeOpts updates container readiness probe commons options based on provided string +func UpdateReadinessProbeOpts(spec *corev1.PodSpec, probeString string) error { + c := containerOfPodSpec(spec) + if c.ReadinessProbe == nil { + c.ReadinessProbe = &corev1.Probe{} + } + err := resolveProbeOptions(c.ReadinessProbe, probeString) + if err != nil { + return err + } + return nil +} + // UpdateImagePullPolicy updates the pull policy for the given revision template func UpdateImagePullPolicy(spec *corev1.PodSpec, imagePullPolicy string) error { container := containerOfPodSpec(spec) @@ -879,3 +935,112 @@ func decodeContainersFromFile(filename string) (*corev1.PodSpec, error) { } return podSpec, nil } + +// ======================================================================================= +// Probes + +// resolveProbe parses probes as a string +// It's split into two functions: +// - resolveProbeOptions() -> common probe opts +// - resolveProbeHandler() -> probe handler [HTTPGet, Exec, TCPSocket] +// Format: +// - [http,https]:host:port:path +// - exec:cmd,cmd,... +// - tcp:host:port +// Common opts (comma separated, case insensitive): +// - InitialDelaySeconds=,FailureThreshold=, +// SuccessThreshold=,PeriodSeconds==,TimeoutSeconds= + +// resolveProbeOptions parses probe commons options +func resolveProbeOptions(probe *corev1.Probe, probeString string) error { + options := strings.Split(probeString, ",") + mappedOptions, err := util.MapFromArray(options, "=") + if err != nil { + return err + } + for k, v := range mappedOptions { + // Trim & verify value is convertible to int + intValue, err := strconv.ParseInt(strings.TrimSpace(v), 0, 32) + if err != nil { + return fmt.Errorf("not a nummeric value for parameter '%s'", k) + } + // Lower case param name mapping + switch strings.TrimSpace(strings.ToLower(k)) { + case "initialdelayseconds": + probe.InitialDelaySeconds = int32(intValue) + case "timeoutseconds": + probe.TimeoutSeconds = int32(intValue) + case "periodseconds": + probe.PeriodSeconds = int32(intValue) + case "successthreshold": + probe.SuccessThreshold = int32(intValue) + case "failurethreshold": + probe.FailureThreshold = int32(intValue) + default: + return fmt.Errorf("not a valid probe parameter name '%s'", k) + } + } + return nil +} + +// resolveProbeHandler parses probe handler options +func resolveProbeHandler(probeString string) (*corev1.ProbeHandler, error) { + if len(probeString) == 0 { + return nil, fmt.Errorf("no probe parameters detected") + } + probeParts := strings.Split(probeString, ":") + if len(probeParts) > 4 { + return nil, fmt.Errorf("too many probe parameters provided, please check the format") + } + var probeHandler *corev1.ProbeHandler + switch probeParts[0] { + case "http", "https": + if len(probeParts) != 4 { + return nil, fmt.Errorf("unexpected probe format, please use 'http:host:port:path'") + } + handler := corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{}, + } + if probeParts[0] == "https" { + handler.HTTPGet.Scheme = v1.URISchemeHTTPS + } + handler.HTTPGet.Host = probeParts[1] + if probeParts[2] != "" { + // Cosmetic fix to have default 'port: 0' instead of empty string 'port: ""' + handler.HTTPGet.Port = intstr.Parse(probeParts[2]) + } + handler.HTTPGet.Path = probeParts[3] + + probeHandler = &handler + case "exec": + if len(probeParts) != 2 { + return nil, fmt.Errorf("unexpected probe format, please use 'exec:[,,...]'") + } + if len(probeParts[1]) == 0 { + return nil, fmt.Errorf("at least one command parameter is required for Exec probe") + } + handler := corev1.ProbeHandler{ + Exec: &corev1.ExecAction{}, + } + cmd := strings.Split(probeParts[1], ",") + handler.Exec.Command = cmd + + probeHandler = &handler + case "tcp": + if len(probeParts) != 3 { + return nil, fmt.Errorf("unexpected probe format, please use 'tcp:host:port") + } + handler := corev1.ProbeHandler{ + TCPSocket: &corev1.TCPSocketAction{}, + } + handler.TCPSocket.Host = probeParts[1] + handler.TCPSocket.Port = intstr.Parse(probeParts[2]) + + probeHandler = &handler + default: + return nil, fmt.Errorf("unsupported probe type '%s'; supported types: http, https, exec, tcp", probeParts[0]) + } + return probeHandler, nil +} + +// ======================================================================================= diff --git a/pkg/kn/flags/podspec_helper_test.go b/pkg/kn/flags/podspec_helper_test.go index 4db760f6e7..78017c279d 100644 --- a/pkg/kn/flags/podspec_helper_test.go +++ b/pkg/kn/flags/podspec_helper_test.go @@ -17,6 +17,7 @@ limitations under the License. package flags import ( + "errors" "fmt" "io/ioutil" "os" @@ -24,6 +25,8 @@ import ( "strings" "testing" + "k8s.io/apimachinery/pkg/util/intstr" + "k8s.io/apimachinery/pkg/api/resource" "knative.dev/client/lib/test" @@ -1109,3 +1112,372 @@ func TestUpdateImagePullPolicyError(t *testing.T) { err := UpdateImagePullPolicy(podSpec, "InvalidPolicy") assert.Assert(t, util.ContainsAll(err.Error(), "invalid --pull-policy", "Valid arguments", "Always | Never | IfNotPresent")) } + +func TestUpdateProbes(t *testing.T) { + podSpec := &corev1.PodSpec{ + Containers: []corev1.Container{{}}, + } + expected := &corev1.PodSpec{ + Containers: []corev1.Container{ + { + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Port: intstr.Parse("8080"), Path: "/path"}}}, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Port: intstr.Parse("8080"), Path: "/path"}}}, + }, + }, + } + t.Run("Update readiness & liveness", func(t *testing.T) { + err := UpdateLivenessProbe(podSpec, "http::8080:/path") + assert.NilError(t, err) + err = UpdateReadinessProbe(podSpec, "http::8080:/path") + assert.NilError(t, err) + assert.DeepEqual(t, podSpec, expected) + }) + t.Run("Update readiness with error", func(t *testing.T) { + err := UpdateReadinessProbe(podSpec, "http-probe::8080:/path") + assert.Assert(t, err != nil) + assert.ErrorContains(t, err, "unsupported probe type") + }) + t.Run("Update liveness with error", func(t *testing.T) { + err := UpdateLivenessProbe(podSpec, "http-probe::8080:/path") + assert.Assert(t, err != nil) + assert.ErrorContains(t, err, "unsupported probe type") + }) +} + +func TestUpdateProbesOpts(t *testing.T) { + podSpec := &corev1.PodSpec{ + Containers: []corev1.Container{{}}, + } + expected := &corev1.PodSpec{ + Containers: []corev1.Container{ + { + LivenessProbe: &corev1.Probe{ + InitialDelaySeconds: 5, + TimeoutSeconds: 10}, + ReadinessProbe: &corev1.Probe{ + InitialDelaySeconds: 5, + TimeoutSeconds: 10}, + }, + }, + } + t.Run("Update readiness & liveness", func(t *testing.T) { + err := UpdateLivenessProbeOpts(podSpec, "initialdelayseconds=5") + assert.NilError(t, err) + err = UpdateLivenessProbeOpts(podSpec, "timeoutseconds=10") + assert.NilError(t, err) + err = UpdateReadinessProbeOpts(podSpec, "initialdelayseconds=5,timeoutseconds=10") + assert.NilError(t, err) + assert.DeepEqual(t, podSpec, expected) + }) + t.Run("Update readiness with error", func(t *testing.T) { + err := UpdateReadinessProbeOpts(podSpec, "timeout=10") + assert.Assert(t, err != nil) + assert.ErrorContains(t, err, "not a valid probe parameter") + }) + t.Run("Update liveness with error", func(t *testing.T) { + err := UpdateLivenessProbeOpts(podSpec, "initdelay=5") + assert.Assert(t, err != nil) + assert.ErrorContains(t, err, "not a valid probe parameter") + }) +} + +func TestUpdateProbesWithOpts(t *testing.T) { + podSpec := &corev1.PodSpec{ + Containers: []corev1.Container{{}}, + } + expected := &corev1.PodSpec{ + Containers: []corev1.Container{ + { + LivenessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Port: intstr.Parse("8080"), Path: "/path"}}, + InitialDelaySeconds: 10}, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: corev1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{ + Port: intstr.Parse("8080"), Path: "/path"}}, + TimeoutSeconds: 10}, + }, + }, + } + t.Run("Update readiness & liveness", func(t *testing.T) { + err := UpdateLivenessProbe(podSpec, "http::8080:/path") + assert.NilError(t, err) + err = UpdateLivenessProbeOpts(podSpec, "initialdelayseconds=10") + assert.NilError(t, err) + err = UpdateReadinessProbeOpts(podSpec, "timeoutseconds=10") + assert.NilError(t, err) + err = UpdateReadinessProbe(podSpec, "http::8080:/path") + assert.NilError(t, err) + assert.DeepEqual(t, podSpec, expected) + }) +} + +func TestResolveProbeHandlerError(t *testing.T) { + for _, tc := range []struct { + name string + probeString string + err error + }{ + { + name: "Probe string empty", + probeString: "", + err: errors.New("no probe parameters detected"), + }, + { + name: "Probe string too many parameters", + probeString: "http:too-many:test-host:8080:/", + err: errors.New("too many probe parameters provided, please check the format"), + }, + { + name: "Probe invalid prefix", + probeString: "http-probe:test-host:8080:/", + err: errors.New("unsupported probe type 'http-probe'; supported types: http, https, exec, tcp"), + }, + } { + t.Run(tc.name, func(t *testing.T) { + actual, err := resolveProbeHandler(tc.probeString) + assert.Assert(t, actual == nil) + assert.Error(t, err, tc.err.Error()) + assert.ErrorType(t, err, tc.err) + + }) + } +} + +func TestResolveProbeHandlerHTTP(t *testing.T) { + for _, tc := range []struct { + name string + probeString string + expected *corev1.ProbeHandler + err error + }{ + { + name: "HTTP probe empty params", + probeString: "http:::", + expected: &corev1.ProbeHandler{HTTPGet: &corev1.HTTPGetAction{}}, + err: nil, + }, + { + name: "HTTP probe all params", + probeString: "http:test-host:8080:/", + expected: &corev1.ProbeHandler{HTTPGet: &corev1.HTTPGetAction{ + Host: "test-host", Port: intstr.Parse("8080"), Path: "/", + }}, + err: nil, + }, + { + name: "HTTPS probe all params", + probeString: "https:test-host:8080:/", + expected: &corev1.ProbeHandler{HTTPGet: &corev1.HTTPGetAction{ + Host: "test-host", Scheme: corev1.URISchemeHTTPS, Port: intstr.Parse("8080"), Path: "/", + }}, + err: nil, + }, + { + name: "HTTP probe port path params", + probeString: "http::8080:/", + expected: &corev1.ProbeHandler{HTTPGet: &corev1.HTTPGetAction{ + Port: intstr.Parse("8080"), Path: "/", + }}, + err: nil, + }, + { + name: "HTTP probe path params", + probeString: "http:::/path", + expected: &corev1.ProbeHandler{HTTPGet: &corev1.HTTPGetAction{ + Path: "/path", + }}, + err: nil, + }, + { + name: "HTTP probe named port path params", + probeString: "http::namedPort:/", + expected: &corev1.ProbeHandler{HTTPGet: &corev1.HTTPGetAction{ + Port: intstr.Parse("namedPort"), Path: "/", + }}, + err: nil, + }, + { + name: "HTTP probe not enough params", + probeString: "http::", + expected: nil, + err: errors.New("unexpected probe format, please use 'http:host:port:path'"), + }, + } { + t.Run(tc.name, func(t *testing.T) { + actual, err := resolveProbeHandler(tc.probeString) + if tc.err == nil { + assert.NilError(t, err) + assert.DeepEqual(t, actual, tc.expected) + } else { + assert.Assert(t, actual == nil) + assert.Error(t, err, tc.err.Error()) + assert.ErrorType(t, err, tc.err) + } + + }) + } +} + +func TestResolveProbeHandlerExec(t *testing.T) { + for _, tc := range []struct { + name string + probeString string + expected *corev1.ProbeHandler + err error + }{ + { + name: "Exec probe single cmd params", + probeString: "exec:/bin/cmd/with/slashes", + expected: &corev1.ProbeHandler{Exec: &corev1.ExecAction{ + Command: []string{"/bin/cmd/with/slashes"}, + }}, + err: nil, + }, + { + name: "Exec probe multiple cmd params", + probeString: "exec:/bin/cmd,arg,arg", + expected: &corev1.ProbeHandler{Exec: &corev1.ExecAction{ + Command: []string{"/bin/cmd", "arg", "arg"}, + }}, + err: nil, + }, + { + name: "Exec probe empty command param", + probeString: "exec:", + expected: nil, + err: errors.New("at least one command parameter is required for Exec probe"), + }, + { + name: "Exec probe too many params", + probeString: "exec:cmd:cmd", + expected: nil, + err: errors.New("unexpected probe format, please use 'exec:[,,...]'"), + }, + } { + t.Run(tc.name, func(t *testing.T) { + actual, err := resolveProbeHandler(tc.probeString) + if tc.err == nil { + assert.NilError(t, err) + assert.DeepEqual(t, actual, tc.expected) + } else { + assert.Assert(t, actual == nil) + assert.Error(t, err, tc.err.Error()) + assert.ErrorType(t, err, tc.err) + } + + }) + } +} + +func TestResolveProbeHandlerTCP(t *testing.T) { + for _, tc := range []struct { + name string + probeString string + expected *corev1.ProbeHandler + err error + }{ + { + name: "TCPSocket probe host port", + probeString: "tcp:test-host:8080", + expected: &corev1.ProbeHandler{TCPSocket: &corev1.TCPSocketAction{ + Host: "test-host", Port: intstr.Parse("8080"), + }}, + err: nil, + }, + { + name: "TCPSocket probe host named port", + probeString: "tcp:test-host:myPort", + expected: &corev1.ProbeHandler{TCPSocket: &corev1.TCPSocketAction{ + Host: "test-host", Port: intstr.Parse("myPort"), + }}, + err: nil, + }, + { + name: "TCPSocket probe empty command param", + probeString: "tcp:", + expected: nil, + err: errors.New("unexpected probe format, please use 'tcp:host:port"), + }, + { + name: "TCPSocket probe too many params", + probeString: "tcp:host:port:more", + expected: nil, + err: errors.New("unexpected probe format, please use 'tcp:host:port"), + }, + } { + t.Run(tc.name, func(t *testing.T) { + actual, err := resolveProbeHandler(tc.probeString) + if tc.err == nil { + assert.NilError(t, err) + assert.DeepEqual(t, actual, tc.expected) + } else { + assert.Assert(t, actual == nil) + assert.Error(t, err, tc.err.Error()) + assert.ErrorType(t, err, tc.err) + } + + }) + } +} + +func TestResolveProbeOptions(t *testing.T) { + for _, tc := range []struct { + name string + probeString string + expected *corev1.Probe + err error + }{ + { + name: "Common options all", + probeString: "InitialDelaySeconds=1,TimeoutSeconds=2,PeriodSeconds=3,SuccessThreshold=4,FailureThreshold=5", + expected: &corev1.Probe{ + InitialDelaySeconds: 1, + TimeoutSeconds: 2, + PeriodSeconds: 3, + SuccessThreshold: 4, + FailureThreshold: 5, + }, + err: nil, + }, + { + name: "Error duplicate value", + probeString: "InitialDelaySeconds=2,InitialDelaySeconds=3", + expected: nil, + err: errors.New("The key \"InitialDelaySeconds\" has been duplicate in [InitialDelaySeconds=2 InitialDelaySeconds=3]"), + }, + { + name: "Error not a numeric value", + probeString: "InitialDelaySeconds=v", + expected: nil, + err: errors.New("not a nummeric value for parameter 'InitialDelaySeconds'"), + }, + { + name: "Error invalid parameter name", + probeString: "InitialDelay=5", + expected: nil, + err: errors.New("not a valid probe parameter name 'InitialDelay'"), + }, + } { + t.Run(tc.name, func(t *testing.T) { + actual := &corev1.Probe{} + err := resolveProbeOptions(actual, tc.probeString) + if tc.err == nil { + assert.NilError(t, err) + assert.DeepEqual(t, actual, tc.expected) + } else { + assert.Error(t, err, tc.err.Error()) + assert.ErrorType(t, err, tc.err) + } + + }) + } +} diff --git a/pkg/kn/flags/podspec_test.go b/pkg/kn/flags/podspec_test.go index 480fa64c38..e86e9da7b3 100644 --- a/pkg/kn/flags/podspec_test.go +++ b/pkg/kn/flags/podspec_test.go @@ -23,6 +23,8 @@ import ( "path/filepath" "testing" + "k8s.io/apimachinery/pkg/util/intstr" + "knative.dev/client/lib/test" "github.com/spf13/cobra" @@ -70,7 +72,8 @@ func TestPodSpecResolve(t *testing.T) { "--port", "8080", "--limit", "cpu=1000m", "--limit", "memory=1024Mi", "--cmd", "/app/start", "--arg", "myArg1", "--service-account", "foo-bar-account", "--mount", "/mount/path=volume-name", "--volume", "volume-name=cm:config-map-name", - "--env-from", "config-map:config-map-name", "--user", "1001", "--pull-policy", "always"} + "--env-from", "config-map:config-map-name", "--user", "1001", "--pull-policy", "always", + "--probe-readiness", "http::8080:/path", "--probe-liveness", "http::8080:/path"} expectedPodSpec := corev1.PodSpec{ Containers: []corev1.Container{ { @@ -83,6 +86,16 @@ func TestPodSpecResolve(t *testing.T) { ContainerPort: 8080, }, }, + ReadinessProbe: &corev1.Probe{ + ProbeHandler: v1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{Port: intstr.Parse("8080"), Path: "/path"}, + }, + }, + LivenessProbe: &corev1.Probe{ + ProbeHandler: v1.ProbeHandler{ + HTTPGet: &corev1.HTTPGetAction{Port: intstr.Parse("8080"), Path: "/path"}, + }, + }, Env: []corev1.EnvVar{{Name: "b", Value: "c"}}, EnvFrom: []corev1.EnvFromSource{ {