mirror of
https://github.com/kyverno/kyverno.git
synced 2025-03-31 03:45:17 +00:00
e2e workflow added (#1021)
* e2e flow added * add kustomize image change in ci
This commit is contained in:
parent
6e827f912f
commit
9c0d54f35a
8 changed files with 580 additions and 16 deletions
40
.github/workflows/build.yaml
vendored
Normal file
40
.github/workflows/build.yaml
vendored
Normal file
|
@ -0,0 +1,40 @@
|
|||
name: build
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
pull_request:
|
||||
branches:
|
||||
- 'master'
|
||||
|
||||
jobs:
|
||||
releaser:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v2
|
||||
-
|
||||
name: Unshallow
|
||||
run: git fetch --prune --unshallow
|
||||
-
|
||||
name: Set up Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: 1.14
|
||||
|
||||
- name : binary build & kustomize
|
||||
run: |
|
||||
make ci
|
||||
- name: e2e testing
|
||||
run: |
|
||||
echo ">>> Install Kyverno"
|
||||
sed 's/imagePullPolicy:.*$/imagePullPolicy: IfNotPresent/g' ${GITHUB_WORKSPACE}/definitions/install.yaml | kubectl apply -f -
|
||||
kubectl apply -f ${GITHUB_WORKSPACE}/definitions/github/rbac.yaml
|
||||
chmod a+x ${GITHUB_WORKSPACE}/scripts/verify-deployment.sh
|
||||
sleep 50
|
||||
echo ">>> Check kyverno"
|
||||
kubectl get pods -n kyverno
|
||||
${GITHUB_WORKSPACE}/scripts/verify-deployment.sh -n kyverno kyverno
|
||||
echo ">>> Run Kyverno e2e test"
|
||||
make test-e2e
|
8
Makefile
8
Makefile
|
@ -76,6 +76,7 @@ docker-push-kyverno:
|
|||
@docker push $(REGISTRY)/nirmata/$(KYVERNO_IMAGE):latest
|
||||
|
||||
##################################
|
||||
|
||||
# Generate Docs for types.go
|
||||
##################################
|
||||
|
||||
|
@ -91,6 +92,13 @@ cli:
|
|||
GOOS=$(GOOS) go build -o $(PWD)/$(CLI_PATH)/kyverno -ldflags=$(LD_FLAGS) $(PWD)/$(CLI_PATH)/main.go
|
||||
|
||||
|
||||
##################################
|
||||
ci: docker-build-kyverno docker-build-initContainer
|
||||
echo "kustomize input"
|
||||
chmod a+x $(PWD)/scripts/ci.sh
|
||||
$(PWD)/scripts/ci.sh
|
||||
##################################
|
||||
|
||||
##################################
|
||||
# Testing & Code-Coverage
|
||||
##################################
|
||||
|
|
101
definitions/github/rbac.yaml
Normal file
101
definitions/github/rbac.yaml
Normal file
|
@ -0,0 +1,101 @@
|
|||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:userinfo
|
||||
rules:
|
||||
# get the roleRef for incoming api-request user
|
||||
- apiGroups:
|
||||
- "*"
|
||||
resources:
|
||||
- roles
|
||||
- clusterroles
|
||||
- rolebindings
|
||||
- clusterrolebindings
|
||||
- configmaps
|
||||
verbs:
|
||||
- watch
|
||||
- create
|
||||
- update
|
||||
- delete
|
||||
- patch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:customresources
|
||||
rules:
|
||||
# Kyverno CRs
|
||||
- apiGroups:
|
||||
- '*'
|
||||
resources:
|
||||
- clusterpolicies
|
||||
- clusterpolicies/status
|
||||
- clusterpolicyviolations
|
||||
- clusterpolicyviolations/status
|
||||
- policyviolations
|
||||
- policyviolations/status
|
||||
- generaterequests
|
||||
- generaterequests/status
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:policycontroller
|
||||
rules:
|
||||
# background processing, identify all existing resources
|
||||
- apiGroups:
|
||||
- '*'
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:generatecontroller
|
||||
rules:
|
||||
# process generate rules to generate resources
|
||||
- apiGroups:
|
||||
- "*"
|
||||
resources:
|
||||
- namespaces
|
||||
- networkpolicies
|
||||
- secrets
|
||||
- configmaps
|
||||
- resourcequotas
|
||||
- limitranges
|
||||
- roles
|
||||
- clusterroles
|
||||
- rolebindings
|
||||
- clusterrolebindings
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
# dynamic watches on trigger resources for generate rules
|
||||
# re-evaluate the policy if the resource is updated
|
||||
- apiGroups:
|
||||
- '*'
|
||||
resources:
|
||||
- namespaces
|
||||
verbs:
|
||||
- watch
|
|
@ -812,5 +812,6 @@ spec:
|
|||
memory: 50Mi
|
||||
initContainers:
|
||||
- image: nirmata/kyvernopre:v1.1.8
|
||||
imagePullPolicy: Always
|
||||
name: kyverno-pre
|
||||
serviceAccountName: kyverno-service-account
|
||||
|
|
|
@ -20,28 +20,29 @@ spec:
|
|||
initContainers:
|
||||
- name: kyverno-pre
|
||||
image: nirmata/kyvernopre:v1.1.8
|
||||
imagePullPolicy: Always
|
||||
containers:
|
||||
- name: kyverno
|
||||
image: nirmata/kyverno:v1.1.8
|
||||
imagePullPolicy: Always
|
||||
args:
|
||||
- "--filterK8Resources=[Event,*,*][*,kube-system,*][*,kube-public,*][*,kube-node-lease,*][Node,*,*][APIService,*,*][TokenReview,*,*][SubjectAccessReview,*,*][*,kyverno,*][Binding,*,*][ReplicaSet,*,*]"
|
||||
# customize webhook timeout
|
||||
#- "--webhooktimeout=4"
|
||||
# enable profiling
|
||||
# - "--profile"
|
||||
- "-v=2"
|
||||
- "--filterK8Resources=[Event,*,*][*,kube-system,*][*,kube-public,*][*,kube-node-lease,*][Node,*,*][APIService,*,*][TokenReview,*,*][SubjectAccessReview,*,*][*,kyverno,*][Binding,*,*][ReplicaSet,*,*]"
|
||||
# customize webhook timeout
|
||||
#- "--webhooktimeout=4"
|
||||
# enable profiling
|
||||
# - "--profile"
|
||||
- "-v=2"
|
||||
ports:
|
||||
- containerPort: 443
|
||||
- containerPort: 443
|
||||
env:
|
||||
- name: INIT_CONFIG
|
||||
value: init-config
|
||||
- name: KYVERNO_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: KYVERNO_SVC
|
||||
value: kyverno-svc
|
||||
- name: INIT_CONFIG
|
||||
value: init-config
|
||||
- name: KYVERNO_NAMESPACE
|
||||
valueFrom:
|
||||
fieldRef:
|
||||
fieldPath: metadata.namespace
|
||||
- name: KYVERNO_SVC
|
||||
value: kyverno-svc
|
||||
resources:
|
||||
requests:
|
||||
memory: "50Mi"
|
||||
|
@ -67,4 +68,4 @@ spec:
|
|||
periodSeconds: 10
|
||||
timeoutSeconds: 5
|
||||
failureThreshold: 4
|
||||
successThreshold: 1
|
||||
successThreshold: 1
|
268
definitions/rbac/rbac.yaml
Normal file
268
definitions/rbac/rbac.yaml
Normal file
|
@ -0,0 +1,268 @@
|
|||
---
|
||||
kind: Namespace
|
||||
apiVersion: v1
|
||||
metadata:
|
||||
name: "kyverno"
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: Service
|
||||
metadata:
|
||||
namespace: kyverno
|
||||
name: kyverno-svc
|
||||
labels:
|
||||
app: kyverno
|
||||
spec:
|
||||
ports:
|
||||
- port: 443
|
||||
targetPort: 443
|
||||
selector:
|
||||
app: kyverno
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ServiceAccount
|
||||
metadata:
|
||||
name: kyverno-service-account
|
||||
namespace: kyverno
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:policyviolations
|
||||
rules:
|
||||
- apiGroups: ["kyverno.io"]
|
||||
resources:
|
||||
- policyviolations
|
||||
verbs: ["get", "list", "watch"]
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: kyverno:webhook
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: kyverno:webhook
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kyverno-service-account
|
||||
namespace: kyverno
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: kyverno:userinfo
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: kyverno:userinfo
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kyverno-service-account
|
||||
namespace: kyverno
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: kyverno:customresources
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: kyverno:customresources
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kyverno-service-account
|
||||
namespace: kyverno
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: kyverno:policycontroller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: kyverno:policycontroller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kyverno-service-account
|
||||
namespace: kyverno
|
||||
---
|
||||
kind: ClusterRoleBinding
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
metadata:
|
||||
name: kyverno:generatecontroller
|
||||
roleRef:
|
||||
apiGroup: rbac.authorization.k8s.io
|
||||
kind: ClusterRole
|
||||
name: kyverno:generatecontroller
|
||||
subjects:
|
||||
- kind: ServiceAccount
|
||||
name: kyverno-service-account
|
||||
namespace: kyverno
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:webhook
|
||||
rules:
|
||||
# Dynamic creation of webhooks, events & certs
|
||||
- apiGroups:
|
||||
- '*'
|
||||
resources:
|
||||
- events
|
||||
- mutatingwebhookconfigurations
|
||||
- validatingwebhookconfigurations
|
||||
- certificatesigningrequests
|
||||
- certificatesigningrequests/approval
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- certificates.k8s.io
|
||||
resources:
|
||||
- certificatesigningrequests
|
||||
- certificatesigningrequests/approval
|
||||
- certificatesigningrequests/status
|
||||
resourceNames:
|
||||
- kubernetes.io/legacy-unknown
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- update
|
||||
- watch
|
||||
- apiGroups:
|
||||
- certificates.k8s.io
|
||||
resources:
|
||||
- signers
|
||||
resourceNames:
|
||||
- kubernetes.io/legacy-unknown
|
||||
verbs:
|
||||
- approve
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:userinfo
|
||||
rules:
|
||||
# get the roleRef for incoming api-request user
|
||||
- apiGroups:
|
||||
- "*"
|
||||
resources:
|
||||
- roles
|
||||
- clusterroles
|
||||
- rolebindings
|
||||
- clusterrolebindings
|
||||
- configmaps
|
||||
verbs:
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:customresources
|
||||
rules:
|
||||
# Kyverno CRs
|
||||
- apiGroups:
|
||||
- '*'
|
||||
resources:
|
||||
- clusterpolicies
|
||||
- clusterpolicies/status
|
||||
- clusterpolicyviolations
|
||||
- clusterpolicyviolations/status
|
||||
- policyviolations
|
||||
- policyviolations/status
|
||||
- generaterequests
|
||||
- generaterequests/status
|
||||
verbs:
|
||||
- create
|
||||
- delete
|
||||
- get
|
||||
- list
|
||||
- patch
|
||||
- update
|
||||
- watch
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:policycontroller
|
||||
rules:
|
||||
# background processing, identify all existing resources
|
||||
- apiGroups:
|
||||
- '*'
|
||||
resources:
|
||||
- '*'
|
||||
verbs:
|
||||
- get
|
||||
- list
|
||||
- update
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:generatecontroller
|
||||
rules:
|
||||
# process generate rules to generate resources
|
||||
- apiGroups:
|
||||
- "*"
|
||||
resources:
|
||||
- namespaces
|
||||
- networkpolicies
|
||||
- secrets
|
||||
- configmaps
|
||||
- resourcequotas
|
||||
- limitranges
|
||||
- clusterroles
|
||||
- rolebindings
|
||||
- clusterrolebindings
|
||||
verbs:
|
||||
- create
|
||||
- update
|
||||
- delete
|
||||
- get
|
||||
# dynamic watches on trigger resources for generate rules
|
||||
# re-evaluate the policy if the resource is updated
|
||||
- apiGroups:
|
||||
- '*'
|
||||
resources:
|
||||
- namespaces
|
||||
verbs:
|
||||
- watch
|
||||
---
|
||||
apiVersion: v1
|
||||
kind: ConfigMap
|
||||
metadata:
|
||||
name: init-config
|
||||
namespace: kyverno
|
||||
data:
|
||||
# resource types to be skipped by kyverno policy engine
|
||||
resourceFilters: "[Event,*,*][*,kube-system,*][*,kube-public,*][*,kube-node-lease,*][Node,*,*][APIService,*,*][TokenReview,*,*][SubjectAccessReview,*,*][*,kyverno,*][Binding,*,*][ReplicaSet,*,*]"
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:view-policyviolations
|
||||
labels:
|
||||
rbac.authorization.k8s.io/aggregate-to-view: "true"
|
||||
rules:
|
||||
- apiGroups: ["kyverno.io"]
|
||||
resources:
|
||||
- policyviolations
|
||||
verbs: ["get", "list", "watch"]
|
||||
---
|
||||
apiVersion: rbac.authorization.k8s.io/v1beta1
|
||||
kind: ClusterRole
|
||||
metadata:
|
||||
name: kyverno:view-clusterpolicyviolations
|
||||
labels:
|
||||
rbac.authorization.k8s.io/aggregate-to-admin: "true"
|
||||
rules:
|
||||
- apiGroups: ["kyverno.io"]
|
||||
resources:
|
||||
- clusterpolicyviolations
|
||||
verbs: ["get", "list", "watch"]
|
25
scripts/ci.sh
Executable file
25
scripts/ci.sh
Executable file
|
@ -0,0 +1,25 @@
|
|||
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
pwd=$(pwd)
|
||||
hash=$(git describe --always --tags)
|
||||
#
|
||||
## Install Kind
|
||||
curl -Lo $pwd/kind https://kind.sigs.k8s.io/dl/v0.8.1/kind-linux-amd64
|
||||
chmod a+x $pwd/kind
|
||||
|
||||
## Create Kind Cluster
|
||||
$pwd/kind create cluster
|
||||
$pwd/kind load docker-image nirmata/kyverno:$hash
|
||||
$pwd/kind load docker-image nirmata/kyvernopre:$hash
|
||||
|
||||
pwd=$(pwd)
|
||||
cd $pwd/definitions
|
||||
echo "Installing kustomize"
|
||||
curl -s "https://raw.githubusercontent.com/kubernetes-sigs/kustomize/master/hack/install_kustomize.sh" | bash
|
||||
chmod a+x $pwd/definitions/kustomize
|
||||
echo "Kustomize image edit"
|
||||
$pwd/definitions/kustomize edit set image nirmata/kyverno:$hash
|
||||
$pwd/definitions/kustomize edit set image nirmata/kyvernopre:$hash
|
||||
$pwd/definitions/kustomize build $pwd/definitions/ > $pwd/definitions/install.yaml
|
120
scripts/verify-deployment.sh
Normal file
120
scripts/verify-deployment.sh
Normal file
|
@ -0,0 +1,120 @@
|
|||
#!/bin/bash
|
||||
# Waits for a deployment to complete.
|
||||
#
|
||||
# Includes a two-step approach:
|
||||
#
|
||||
# 1. Wait for the observed generation to match the specified one.
|
||||
# 2. Waits for the number of available replicas to match the specified one.
|
||||
#
|
||||
|
||||
set -o errexit
|
||||
set -o pipefail
|
||||
set -o nounset
|
||||
# -m enables job control which is otherwise only enabled in interactive mode
|
||||
set -m
|
||||
|
||||
DEFAULT_TIMEOUT=60
|
||||
DEFAULT_NAMESPACE=default
|
||||
|
||||
monitor_timeout() {
|
||||
local -r wait_pid="$1"
|
||||
sleep "${timeout}"
|
||||
echo "Timeout ${timeout} exceeded" >&2
|
||||
kill "${wait_pid}"
|
||||
}
|
||||
|
||||
get_generation() {
|
||||
get_deployment_jsonpath '{.metadata.generation}'
|
||||
}
|
||||
|
||||
get_observed_generation() {
|
||||
get_deployment_jsonpath '{.status.observedGeneration}'
|
||||
}
|
||||
|
||||
get_specified_replicas() {
|
||||
get_deployment_jsonpath '{.spec.replicas}'
|
||||
}
|
||||
|
||||
get_replicas() {
|
||||
get_deployment_jsonpath '{.status.replicas}'
|
||||
}
|
||||
|
||||
get_updated_replicas() {
|
||||
get_deployment_jsonpath '{.status.updatedReplicas}'
|
||||
}
|
||||
|
||||
get_available_replicas() {
|
||||
get_deployment_jsonpath '{.status.availableReplicas}'
|
||||
}
|
||||
|
||||
get_deployment_jsonpath() {
|
||||
local -r jsonpath="$1"
|
||||
|
||||
kubectl --namespace "${namespace}" get deployment "${deployment}" -o "jsonpath=${jsonpath}"
|
||||
}
|
||||
|
||||
display_usage_and_exit() {
|
||||
echo "Usage: $(basename "$0") [-n <namespace>] [-t <timeout>] <deployment>" >&2
|
||||
echo "Arguments:" >&2
|
||||
echo "deployment REQUIRED: The name of the deployment the script should wait on" >&2
|
||||
echo "-n OPTIONAL: The namespace the deployment exists in, defaults is the 'default' namespace" >&2
|
||||
echo "-t OPTIONAL: How long to wait for the deployment to be available, defaults to ${DEFAULT_TIMEOUT} seconds, must be greater than 0" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
namespace=${DEFAULT_NAMESPACE}
|
||||
timeout=${DEFAULT_TIMEOUT}
|
||||
|
||||
while getopts ':n:t:' arg
|
||||
do
|
||||
case ${arg} in
|
||||
n) namespace=${OPTARG};;
|
||||
t) timeout=${OPTARG};;
|
||||
*) display_usage_and_exit
|
||||
esac
|
||||
done
|
||||
|
||||
shift $((OPTIND-1))
|
||||
if [ "$#" -ne 1 ] ; then
|
||||
display_usage_and_exit
|
||||
fi
|
||||
readonly deployment="$1"
|
||||
|
||||
if [[ ${timeout} -le 0 ]]; then
|
||||
display_usage_and_exit
|
||||
fi
|
||||
|
||||
echo "Waiting for deployment of ${deployment} in namespace ${namespace} with a timeout ${timeout} seconds"
|
||||
|
||||
monitor_timeout $$ &
|
||||
readonly timeout_monitor_pid=$!
|
||||
|
||||
trap 'kill -- -${timeout_monitor_pid}' EXIT #Stop timeout monitor
|
||||
|
||||
generation=$(get_generation); readonly generation
|
||||
current_generation=$(get_observed_generation)
|
||||
|
||||
echo "Expected generation for deployment ${deployment}: ${generation}"
|
||||
while [[ ${current_generation} -lt ${generation} ]]; do
|
||||
sleep .5
|
||||
echo "Currently observed generation: ${current_generation}"
|
||||
current_generation=$(get_observed_generation)
|
||||
done
|
||||
echo "Observed expected generation: ${current_generation}"
|
||||
|
||||
specified_replicas="$(get_specified_replicas)"; readonly specified_replicas
|
||||
echo "Specified replicas: ${specified_replicas}"
|
||||
|
||||
current_replicas=$(get_replicas)
|
||||
updated_replicas=$(get_updated_replicas)
|
||||
available_replicas=$(get_available_replicas)
|
||||
|
||||
while [[ ${updated_replicas} -lt ${specified_replicas} || ${current_replicas} -gt ${updated_replicas} || ${available_replicas} -lt ${updated_replicas} ]]; do
|
||||
sleep .5
|
||||
echo "current/updated/available replicas: ${current_replicas}/${updated_replicas}/${available_replicas}, waiting"
|
||||
current_replicas=$(get_replicas)
|
||||
updated_replicas=$(get_updated_replicas)
|
||||
available_replicas=$(get_available_replicas)
|
||||
done
|
||||
|
||||
echo "Deployment ${deployment} successful. All ${available_replicas} replicas are ready."
|
Loading…
Add table
Reference in a new issue