Allow to jump between steps (#218)
* allow jump between steps Signed-off-by: yunbo <yunbo10124scut@gmail.com> allow jump between steps Signed-off-by: yunbo <yunbo10124scut@gmail.com> allow jump among steps Signed-off-by: yunbo <yunbo10124scut@gmail.com> safte index check Signed-off-by: yunbo <yunbo10124scut@gmail.com> add e2e test for step jump Signed-off-by: yunbo <yunbo10124scut@gmail.com> amend: style-agonstic reference for webhook Signed-off-by: yunbo <yunbo10124scut@gmail.com> improve existing e2e logic to avoid unexpected behaviour Signed-off-by: yunbo <yunbo10124scut@gmail.com> jump: nextStep Index default value from 0 to -1 Signed-off-by: yunbo <yunbo10124scut@gmail.com> after rebase Signed-off-by: yunbo <yunbo10124scut@gmail.com> * jump: fix out of range Signed-off-by: yunbo <yunbo10124scut@gmail.com> --------- Signed-off-by: yunbo <yunbo10124scut@gmail.com> Co-authored-by: yunbo <yunbo10124scut@gmail.com>
This commit is contained in:
parent
1e8af4a4c1
commit
aa28f4e12e
|
|
@ -0,0 +1,110 @@
|
|||
name: E2E-V1Beta1-1.19
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- release-*
|
||||
pull_request: {}
|
||||
workflow_dispatch: {}
|
||||
|
||||
env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.19'
|
||||
KIND_IMAGE: 'kindest/node:v1.19.16'
|
||||
KIND_CLUSTER_NAME: 'ci-testing'
|
||||
|
||||
jobs:
|
||||
|
||||
rollout:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
- name: Setup Kind Cluster
|
||||
uses: helm/kind-action@v1.2.0
|
||||
with:
|
||||
node_image: ${{ env.KIND_IMAGE }}
|
||||
cluster_name: ${{ env.KIND_CLUSTER_NAME }}
|
||||
config: ./test/kind-conf.yaml
|
||||
- name: Build image
|
||||
run: |
|
||||
export IMAGE="openkruise/kruise-rollout:e2e-${GITHUB_RUN_ID}"
|
||||
docker build --pull --no-cache . -t $IMAGE
|
||||
kind load docker-image --name=${KIND_CLUSTER_NAME} $IMAGE || { echo >&2 "kind not installed or error loading image: $IMAGE"; exit 1; }
|
||||
- name: Install Kruise
|
||||
run: |
|
||||
set -ex
|
||||
kubectl cluster-info
|
||||
make helm
|
||||
helm repo add openkruise https://openkruise.github.io/charts/
|
||||
helm repo update
|
||||
helm install kruise openkruise/kruise
|
||||
for ((i=1;i<10;i++));
|
||||
do
|
||||
set +e
|
||||
PODS=$(kubectl get pod -n kruise-system | grep '1/1' | grep kruise-controller-manager | wc -l)
|
||||
set -e
|
||||
if [ "$PODS" -eq "2" ]; then
|
||||
break
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
set +e
|
||||
PODS=$(kubectl get pod -n kruise-system | grep '1/1' | grep kruise-controller-manager | wc -l)
|
||||
set -e
|
||||
if [ "$PODS" -eq "2" ]; then
|
||||
echo "Wait for kruise-manager ready successfully"
|
||||
else
|
||||
echo "Timeout to wait for kruise-manager ready"
|
||||
exit 1
|
||||
fi
|
||||
- name: Install Kruise Rollout
|
||||
run: |
|
||||
set -ex
|
||||
kubectl cluster-info
|
||||
IMG=openkruise/kruise-rollout:e2e-${GITHUB_RUN_ID} ./scripts/deploy_kind.sh
|
||||
for ((i=1;i<10;i++));
|
||||
do
|
||||
set +e
|
||||
PODS=$(kubectl get pod -n kruise-rollout | grep '1/1' | wc -l)
|
||||
set -e
|
||||
if [ "$PODS" -eq "1" ]; then
|
||||
break
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
set +e
|
||||
PODS=$(kubectl get pod -n kruise-rollout | grep '1/1' | wc -l)
|
||||
kubectl get node -o yaml
|
||||
kubectl get all -n kruise-rollout -o yaml
|
||||
set -e
|
||||
if [ "$PODS" -eq "1" ]; then
|
||||
echo "Wait for kruise-rollout ready successfully"
|
||||
else
|
||||
echo "Timeout to wait for kruise-rollout ready"
|
||||
exit 1
|
||||
fi
|
||||
- name: Run E2E Tests
|
||||
run: |
|
||||
export KUBECONFIG=/home/runner/.kube/config
|
||||
make ginkgo
|
||||
set +e
|
||||
./bin/ginkgo -timeout 60m -v --focus='Step Jump' test/e2e
|
||||
retVal=$?
|
||||
# kubectl get pod -n kruise-rollout --no-headers | grep manager | awk '{print $1}' | xargs kubectl logs -n kruise-rollout
|
||||
restartCount=$(kubectl get pod -n kruise-rollout --no-headers | awk '{print $4}')
|
||||
if [ "${restartCount}" -eq "0" ];then
|
||||
echo "Kruise-rollout has not restarted"
|
||||
else
|
||||
kubectl get pod -n kruise-rollout --no-headers
|
||||
echo "Kruise-rollout has restarted, abort!!!"
|
||||
kubectl get pod -n kruise-rollout --no-headers| awk '{print $1}' | xargs kubectl logs -p -n kruise-rollout
|
||||
exit 1
|
||||
fi
|
||||
exit $retVal
|
||||
|
|
@ -0,0 +1,110 @@
|
|||
name: E2E-V1Beta1-1.23
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
- release-*
|
||||
pull_request: {}
|
||||
workflow_dispatch: {}
|
||||
|
||||
env:
|
||||
# Common versions
|
||||
GO_VERSION: '1.19'
|
||||
KIND_IMAGE: 'kindest/node:v1.23.3'
|
||||
KIND_CLUSTER_NAME: 'ci-testing'
|
||||
|
||||
jobs:
|
||||
|
||||
rollout:
|
||||
runs-on: ubuntu-20.04
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
with:
|
||||
submodules: true
|
||||
- name: Setup Go
|
||||
uses: actions/setup-go@v2
|
||||
with:
|
||||
go-version: ${{ env.GO_VERSION }}
|
||||
- name: Setup Kind Cluster
|
||||
uses: helm/kind-action@v1.2.0
|
||||
with:
|
||||
node_image: ${{ env.KIND_IMAGE }}
|
||||
cluster_name: ${{ env.KIND_CLUSTER_NAME }}
|
||||
config: ./test/kind-conf.yaml
|
||||
- name: Build image
|
||||
run: |
|
||||
export IMAGE="openkruise/kruise-rollout:e2e-${GITHUB_RUN_ID}"
|
||||
docker build --pull --no-cache . -t $IMAGE
|
||||
kind load docker-image --name=${KIND_CLUSTER_NAME} $IMAGE || { echo >&2 "kind not installed or error loading image: $IMAGE"; exit 1; }
|
||||
- name: Install Kruise
|
||||
run: |
|
||||
set -ex
|
||||
kubectl cluster-info
|
||||
make helm
|
||||
helm repo add openkruise https://openkruise.github.io/charts/
|
||||
helm repo update
|
||||
helm install kruise openkruise/kruise
|
||||
for ((i=1;i<10;i++));
|
||||
do
|
||||
set +e
|
||||
PODS=$(kubectl get pod -n kruise-system | grep '1/1' | grep kruise-controller-manager | wc -l)
|
||||
set -e
|
||||
if [ "$PODS" -eq "2" ]; then
|
||||
break
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
set +e
|
||||
PODS=$(kubectl get pod -n kruise-system | grep '1/1' | grep kruise-controller-manager | wc -l)
|
||||
set -e
|
||||
if [ "$PODS" -eq "2" ]; then
|
||||
echo "Wait for kruise-manager ready successfully"
|
||||
else
|
||||
echo "Timeout to wait for kruise-manager ready"
|
||||
exit 1
|
||||
fi
|
||||
- name: Install Kruise Rollout
|
||||
run: |
|
||||
set -ex
|
||||
kubectl cluster-info
|
||||
IMG=openkruise/kruise-rollout:e2e-${GITHUB_RUN_ID} ./scripts/deploy_kind.sh
|
||||
for ((i=1;i<10;i++));
|
||||
do
|
||||
set +e
|
||||
PODS=$(kubectl get pod -n kruise-rollout | grep '1/1' | wc -l)
|
||||
set -e
|
||||
if [ "$PODS" -eq "1" ]; then
|
||||
break
|
||||
fi
|
||||
sleep 3
|
||||
done
|
||||
set +e
|
||||
PODS=$(kubectl get pod -n kruise-rollout | grep '1/1' | wc -l)
|
||||
kubectl get node -o yaml
|
||||
kubectl get all -n kruise-rollout -o yaml
|
||||
set -e
|
||||
if [ "$PODS" -eq "1" ]; then
|
||||
echo "Wait for kruise-rollout ready successfully"
|
||||
else
|
||||
echo "Timeout to wait for kruise-rollout ready"
|
||||
exit 1
|
||||
fi
|
||||
- name: Run E2E Tests
|
||||
run: |
|
||||
export KUBECONFIG=/home/runner/.kube/config
|
||||
make ginkgo
|
||||
set +e
|
||||
./bin/ginkgo -timeout 60m -v --focus='Step Jump' test/e2e
|
||||
retVal=$?
|
||||
# kubectl get pod -n kruise-rollout --no-headers | grep manager | awk '{print $1}' | xargs kubectl logs -n kruise-rollout
|
||||
restartCount=$(kubectl get pod -n kruise-rollout --no-headers | awk '{print $4}')
|
||||
if [ "${restartCount}" -eq "0" ];then
|
||||
echo "Kruise-rollout has not restarted"
|
||||
else
|
||||
kubectl get pod -n kruise-rollout --no-headers
|
||||
echo "Kruise-rollout has restarted, abort!!!"
|
||||
kubectl get pod -n kruise-rollout --no-headers| awk '{print $1}' | xargs kubectl logs -p -n kruise-rollout
|
||||
exit 1
|
||||
fi
|
||||
exit $retVal
|
||||
|
|
@ -102,6 +102,10 @@ func (r *RolloutStrategy) IsCanaryStragegy() bool {
|
|||
return r.GetRollingStyle() == CanaryRollingStyle || r.GetRollingStyle() == PartitionRollingStyle
|
||||
}
|
||||
|
||||
func (r *RolloutStrategy) IsEmptyRelease() bool {
|
||||
return r.BlueGreen == nil && r.Canary == nil
|
||||
}
|
||||
|
||||
// Get the steps based on the rolling style
|
||||
func (r *RolloutStrategy) GetSteps() []CanaryStep {
|
||||
switch r.GetRollingStyle() {
|
||||
|
|
|
|||
|
|
@ -615,13 +615,13 @@ spec:
|
|||
description: CanaryStep defines a step of a canary workload.
|
||||
properties:
|
||||
matches:
|
||||
description: Matches define conditions used for matching
|
||||
the incoming HTTP requests to canary service. Each
|
||||
description: "Matches define conditions used for matching
|
||||
incoming HTTP requests to the canary service. Each
|
||||
match is independent, i.e. this rule will be matched
|
||||
if **any** one of the matches is satisfied. If Gateway
|
||||
API, current only support one match. And cannot support
|
||||
both weight and matches, if both are configured, then
|
||||
matches takes precedence.
|
||||
as long as **any** one of the matches is satisfied.
|
||||
\n It cannot support Traffic (weight-based routing)
|
||||
and Matches simultaneously, if both are configured.
|
||||
In such cases, Matches takes precedence."
|
||||
items:
|
||||
properties:
|
||||
headers:
|
||||
|
|
@ -683,6 +683,88 @@ spec:
|
|||
type: object
|
||||
maxItems: 16
|
||||
type: array
|
||||
x-kubernetes-list-map-keys:
|
||||
- name
|
||||
x-kubernetes-list-type: map
|
||||
path:
|
||||
description: 'Path specifies a HTTP request path
|
||||
matcher. Supported list: - Istio: https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPMatchRequest
|
||||
- GatewayAPI: If path is defined, the whole
|
||||
HttpRouteMatch will be used as a standalone
|
||||
matcher'
|
||||
properties:
|
||||
type:
|
||||
default: PathPrefix
|
||||
description: "Type specifies how to match
|
||||
against the path Value. \n Support: Core
|
||||
(Exact, PathPrefix) \n Support: Custom (RegularExpression)"
|
||||
enum:
|
||||
- Exact
|
||||
- PathPrefix
|
||||
- RegularExpression
|
||||
type: string
|
||||
value:
|
||||
default: /
|
||||
description: Value of the HTTP path to match
|
||||
against.
|
||||
maxLength: 1024
|
||||
type: string
|
||||
type: object
|
||||
queryParams:
|
||||
description: 'QueryParams specifies HTTP query
|
||||
parameter matchers. Multiple match values are
|
||||
ANDed together, meaning, a request must match
|
||||
all the specified query parameters to select
|
||||
the route. Supported list: - Istio: https://istio.io/latest/docs/reference/config/networking/virtual-service/#HTTPMatchRequest
|
||||
- MSE Ingress: https://help.aliyun.com/zh/ack/ack-managed-and-ack-dedicated/user-guide/annotations-supported-by-mse-ingress-gateways-1 Header/Cookie
|
||||
> QueryParams - Gateway API'
|
||||
items:
|
||||
description: HTTPQueryParamMatch describes how
|
||||
to select a HTTP route by matching HTTP query
|
||||
parameters.
|
||||
properties:
|
||||
name:
|
||||
description: "Name is the name of the HTTP
|
||||
query param to be matched. This must be
|
||||
an exact string match. (See https://tools.ietf.org/html/rfc7230#section-2.7.3).
|
||||
\n If multiple entries specify equivalent
|
||||
query param names, only the first entry
|
||||
with an equivalent name MUST be considered
|
||||
for a match. Subsequent entries with an
|
||||
equivalent query param name MUST be ignored."
|
||||
maxLength: 256
|
||||
minLength: 1
|
||||
type: string
|
||||
type:
|
||||
default: Exact
|
||||
description: "Type specifies how to match
|
||||
against the value of the query parameter.
|
||||
\n Support: Extended (Exact) \n Support:
|
||||
Custom (RegularExpression) \n Since RegularExpression
|
||||
QueryParamMatchType has custom conformance,
|
||||
implementations can support POSIX, PCRE
|
||||
or any other dialects of regular expressions.
|
||||
Please read the implementation's documentation
|
||||
to determine the supported dialect."
|
||||
enum:
|
||||
- Exact
|
||||
- RegularExpression
|
||||
type: string
|
||||
value:
|
||||
description: Value is the value of HTTP
|
||||
query param to be matched.
|
||||
maxLength: 1024
|
||||
minLength: 1
|
||||
type: string
|
||||
required:
|
||||
- name
|
||||
- value
|
||||
type: object
|
||||
maxItems: 16
|
||||
type: array
|
||||
x-kubernetes-list-map-keys:
|
||||
- name
|
||||
x-kubernetes-list-type: map
|
||||
type: object
|
||||
type: array
|
||||
pause:
|
||||
|
|
|
|||
|
|
@ -81,7 +81,7 @@ func objectToTable(path string) error {
|
|||
rollout := testCase.Rollout
|
||||
trafficRouting := testCase.TrafficRouting
|
||||
if rollout != nil {
|
||||
steps := rollout.Spec.Strategy.Canary.Steps
|
||||
steps := rollout.Spec.Strategy.GetSteps()
|
||||
for i, step := range steps {
|
||||
var weight *int32
|
||||
if step.TrafficRoutingStrategy.Traffic != nil {
|
||||
|
|
@ -92,7 +92,7 @@ func objectToTable(path string) error {
|
|||
weight = utilpointer.Int32(-1)
|
||||
}
|
||||
var canaryService string
|
||||
stableService := rollout.Spec.Strategy.Canary.TrafficRoutings[0].Service
|
||||
stableService := rollout.Spec.Strategy.GetTrafficRouting()[0].Service
|
||||
canaryService = fmt.Sprintf("%s-canary", stableService)
|
||||
data := &custom.LuaData{
|
||||
Data: custom.Data{
|
||||
|
|
|
|||
|
|
@ -73,20 +73,20 @@ func (bc *BatchContext) Log() string {
|
|||
// IsBatchReady return nil if the batch is ready
|
||||
func (bc *BatchContext) IsBatchReady() error {
|
||||
if bc.UpdatedReplicas < bc.DesiredUpdatedReplicas {
|
||||
return fmt.Errorf("current batch not ready: updated replicas not satified")
|
||||
return fmt.Errorf("current batch not ready: updated replicas not satisfied, UpdatedReplicas %d < DesiredUpdatedReplicas %d", bc.UpdatedReplicas, bc.DesiredUpdatedReplicas)
|
||||
}
|
||||
|
||||
unavailableToleration := allowedUnavailable(bc.FailureThreshold, bc.UpdatedReplicas)
|
||||
if unavailableToleration+bc.UpdatedReadyReplicas < bc.DesiredUpdatedReplicas {
|
||||
return fmt.Errorf("current batch not ready: updated ready replicas not satified")
|
||||
return fmt.Errorf("current batch not ready: updated ready replicas not satisfied, allowedUnavailable + UpdatedReadyReplicas %d < DesiredUpdatedReplicas %d", unavailableToleration+bc.UpdatedReadyReplicas, bc.DesiredUpdatedReplicas)
|
||||
}
|
||||
|
||||
if bc.DesiredUpdatedReplicas > 0 && bc.UpdatedReadyReplicas == 0 {
|
||||
return fmt.Errorf("current batch not ready: no updated ready replicas")
|
||||
return fmt.Errorf("current batch not ready: no updated ready replicas, DesiredUpdatedReplicas %d > 0 and UpdatedReadyReplicas %d = 0", bc.DesiredUpdatedReplicas, bc.UpdatedReadyReplicas)
|
||||
}
|
||||
|
||||
if !batchLabelSatisfied(bc.Pods, bc.RolloutID, bc.PlannedUpdatedReplicas) {
|
||||
return fmt.Errorf("current batch not ready: pods with batch label not satified")
|
||||
return fmt.Errorf("current batch not ready: pods with batch label not satisfied, RolloutID %s, PlannedUpdatedReplicas %d", bc.RolloutID, bc.PlannedUpdatedReplicas)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
|
|
|||
|
|
@ -70,6 +70,11 @@ func (m *canaryReleaseManager) runCanary(c *RolloutContext) error {
|
|||
if canaryStatus.PodTemplateHash == "" {
|
||||
canaryStatus.PodTemplateHash = c.Workload.PodTemplateHash
|
||||
}
|
||||
|
||||
if m.doCanaryJump(c) {
|
||||
klog.Infof("rollout(%s/%s) canary step jumped", c.Rollout.Namespace, c.Rollout.Name)
|
||||
return nil
|
||||
}
|
||||
// When the first batch is trafficRouting rolling and the next steps are rolling release,
|
||||
// We need to clean up the canary-related resources first and then rollout the rest of the batch.
|
||||
currentStep := c.Rollout.Spec.Strategy.Canary.Steps[canaryStatus.CurrentStepIndex-1]
|
||||
|
|
@ -87,6 +92,13 @@ func (m *canaryReleaseManager) runCanary(c *RolloutContext) error {
|
|||
}
|
||||
}
|
||||
switch canaryStatus.CurrentStepState {
|
||||
// before CanaryStepStateUpgrade, handle some special cases, to prevent traffic loss
|
||||
case v1beta1.CanaryStepStateInit:
|
||||
// placeholder for the later traffic modification Pull Request
|
||||
canaryStatus.NextStepIndex = util.NextBatchIndex(c.Rollout, canaryStatus.CurrentStepIndex)
|
||||
canaryStatus.CurrentStepState = v1beta1.CanaryStepStateUpgrade
|
||||
fallthrough
|
||||
|
||||
case v1beta1.CanaryStepStateUpgrade:
|
||||
klog.Infof("rollout(%s/%s) run canary strategy, and state(%s)", c.Rollout.Namespace, c.Rollout.Name, v1beta1.CanaryStepStateUpgrade)
|
||||
done, err := m.doCanaryUpgrade(c)
|
||||
|
|
@ -144,7 +156,8 @@ func (m *canaryReleaseManager) runCanary(c *RolloutContext) error {
|
|||
if len(c.Rollout.Spec.Strategy.Canary.Steps) > int(canaryStatus.CurrentStepIndex) {
|
||||
canaryStatus.LastUpdateTime = &metav1.Time{Time: time.Now()}
|
||||
canaryStatus.CurrentStepIndex++
|
||||
canaryStatus.CurrentStepState = v1beta1.CanaryStepStateUpgrade
|
||||
canaryStatus.NextStepIndex = util.NextBatchIndex(c.Rollout, canaryStatus.CurrentStepIndex)
|
||||
canaryStatus.CurrentStepState = v1beta1.CanaryStepStateInit
|
||||
klog.Infof("rollout(%s/%s) canary step from(%d) -> to(%d)", c.Rollout.Namespace, c.Rollout.Name, canaryStatus.CurrentStepIndex-1, canaryStatus.CurrentStepIndex)
|
||||
} else {
|
||||
klog.Infof("rollout(%s/%s) canary run all steps, and completed", c.Rollout.Namespace, c.Rollout.Name)
|
||||
|
|
@ -204,12 +217,6 @@ func (m *canaryReleaseManager) doCanaryPaused(c *RolloutContext) (bool, error) {
|
|||
canaryStatus := c.NewStatus.CanaryStatus
|
||||
currentStep := c.Rollout.Spec.Strategy.Canary.Steps[canaryStatus.CurrentStepIndex-1]
|
||||
steps := len(c.Rollout.Spec.Strategy.Canary.Steps)
|
||||
// If it is the last step, and 100% of pods, then return true
|
||||
if int32(steps) == canaryStatus.CurrentStepIndex {
|
||||
if currentStep.Replicas != nil && currentStep.Replicas.StrVal == "100%" {
|
||||
return true, nil
|
||||
}
|
||||
}
|
||||
cond := util.GetRolloutCondition(*c.NewStatus, v1beta1.RolloutConditionProgressing)
|
||||
// need manual confirmation
|
||||
if currentStep.Pause.Duration == nil {
|
||||
|
|
@ -232,6 +239,46 @@ func (m *canaryReleaseManager) doCanaryPaused(c *RolloutContext) (bool, error) {
|
|||
return false, nil
|
||||
}
|
||||
|
||||
func (m *canaryReleaseManager) doCanaryJump(c *RolloutContext) (jumped bool) {
|
||||
canaryStatus := c.NewStatus.CanaryStatus
|
||||
nextIndex := canaryStatus.NextStepIndex
|
||||
/*
|
||||
we set the CurrentStepIndex same as NextStepIndex to prevent currentStepIndex from out of range
|
||||
for example, if we had a rollout with 4 steps and CurrentStepIndex was 2
|
||||
then, the user removed 3 steps from the plan, we can calculate NextStepIndex is 1 correctly,
|
||||
but CurrentStepIndex remains 2, which could cause out of range.
|
||||
*/
|
||||
resetCurrentIndex := false
|
||||
if int(canaryStatus.CurrentStepIndex) > len(c.Rollout.Spec.Strategy.Canary.Steps) {
|
||||
canaryStatus.CurrentStepIndex = nextIndex
|
||||
resetCurrentIndex = true
|
||||
}
|
||||
currentStep := c.Rollout.Spec.Strategy.Canary.Steps[canaryStatus.CurrentStepIndex-1]
|
||||
if resetCurrentIndex || nextIndex != util.NextBatchIndex(c.Rollout, canaryStatus.CurrentStepIndex) && nextIndex > 0 {
|
||||
currentIndexBackup := canaryStatus.CurrentStepIndex
|
||||
currentStepStateBackup := canaryStatus.CurrentStepState
|
||||
canaryStatus.CurrentStepIndex = nextIndex
|
||||
canaryStatus.NextStepIndex = util.NextBatchIndex(c.Rollout, nextIndex)
|
||||
nextStep := c.Rollout.Spec.Strategy.Canary.Steps[nextIndex-1]
|
||||
// if the Replicas between currentStep and nextStep is same, we can jump to
|
||||
// the TrafficRouting step; otherwise, we should start from the Init step
|
||||
if reflect.DeepEqual(nextStep.Replicas, currentStep.Replicas) && !resetCurrentIndex {
|
||||
canaryStatus.LastUpdateTime = &metav1.Time{Time: time.Now()}
|
||||
canaryStatus.CurrentStepState = v1beta1.CanaryStepStateTrafficRouting
|
||||
klog.Infof("rollout(%s/%s) step(%d) state from(%s) -> to(%s)", c.Rollout.Namespace, c.Rollout.Name,
|
||||
canaryStatus.CurrentStepIndex, currentStepStateBackup, canaryStatus.CurrentStepState)
|
||||
} else {
|
||||
canaryStatus.LastUpdateTime = &metav1.Time{Time: time.Now()}
|
||||
canaryStatus.CurrentStepState = v1beta1.CanaryStepStateInit
|
||||
klog.Infof("rollout(%s/%s) step(%d) state from(%s) -> to(%s)", c.Rollout.Namespace, c.Rollout.Name,
|
||||
canaryStatus.CurrentStepIndex, currentStepStateBackup, v1beta1.CanaryStepStateInit)
|
||||
}
|
||||
klog.Infof("rollout(%s/%s) canary step from(%d) -> to(%d)", c.Rollout.Namespace, c.Rollout.Name, currentIndexBackup, canaryStatus.CurrentStepIndex)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// cleanup after rollout is completed or finished
|
||||
func (m *canaryReleaseManager) doCanaryFinalising(c *RolloutContext) (bool, error) {
|
||||
// when CanaryStatus is nil, which means canary action hasn't started yet, don't need doing cleanup
|
||||
|
|
|
|||
|
|
@ -84,7 +84,6 @@ func (r *RolloutReconciler) reconcileRolloutProgressing(rollout *v1beta1.Rollout
|
|||
UpdatedRevision: rolloutContext.Workload.CanaryRevision,
|
||||
}
|
||||
} else {
|
||||
commonStatus.CurrentStepState = v1beta1.CanaryStepStateUpgrade
|
||||
newStatus.CanaryStatus = &v1beta1.CanaryStatus{
|
||||
CommonStatus: commonStatus,
|
||||
CanaryRevision: rolloutContext.Workload.CanaryRevision,
|
||||
|
|
@ -164,7 +163,7 @@ func (r *RolloutReconciler) reconcileRolloutProgressing(rollout *v1beta1.Rollout
|
|||
|
||||
func (r *RolloutReconciler) doProgressingInitializing(c *RolloutContext) (bool, error) {
|
||||
// Traffic routing
|
||||
if len(c.Rollout.Spec.Strategy.Canary.TrafficRoutings) > 0 {
|
||||
if c.Rollout.Spec.Strategy.HasTrafficRoutings() {
|
||||
if err := r.trafficRoutingManager.InitializeTrafficRouting(newTrafficRoutingContext(c)); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
|
@ -223,14 +222,15 @@ func (r *RolloutReconciler) handleRolloutPaused(rollout *v1beta1.Rollout, newSta
|
|||
func (r *RolloutReconciler) handleContinuousRelease(c *RolloutContext) error {
|
||||
r.Recorder.Eventf(c.Rollout, corev1.EventTypeNormal, "Progressing", "workload continuous publishing canaryRevision, then restart publishing")
|
||||
klog.Infof("rollout(%s/%s) workload continuous publishing canaryRevision from(%s) -> to(%s), then restart publishing",
|
||||
c.Rollout.Namespace, c.Rollout.Name, c.NewStatus.CanaryStatus.CanaryRevision, c.Workload.CanaryRevision)
|
||||
c.Rollout.Namespace, c.Rollout.Name, c.NewStatus.GetCanaryRevision(), c.Workload.CanaryRevision)
|
||||
|
||||
done, err := r.doProgressingReset(c)
|
||||
if err != nil {
|
||||
klog.Errorf("rollout(%s/%s) doProgressingReset failed: %s", c.Rollout.Namespace, c.Rollout.Name, err.Error())
|
||||
return err
|
||||
} else if done {
|
||||
c.NewStatus.CanaryStatus = nil
|
||||
// clear SubStatus
|
||||
c.NewStatus.Clear()
|
||||
progressingStateTransition(c.NewStatus, corev1.ConditionTrue, v1alpha1.ProgressingReasonInitializing, "Workload is continuous release")
|
||||
klog.Infof("rollout(%s/%s) workload is continuous publishing, reset complete", c.Rollout.Namespace, c.Rollout.Name)
|
||||
} else {
|
||||
|
|
@ -243,7 +243,7 @@ func (r *RolloutReconciler) handleContinuousRelease(c *RolloutContext) error {
|
|||
}
|
||||
|
||||
func (r *RolloutReconciler) handleRollbackDirectly(rollout *v1beta1.Rollout, workload *util.Workload, newStatus *v1beta1.RolloutStatus) error {
|
||||
newStatus.CanaryStatus.CanaryRevision = workload.CanaryRevision
|
||||
newStatus.SetCanaryRevision(workload.CanaryRevision)
|
||||
r.Recorder.Eventf(rollout, corev1.EventTypeNormal, "Progressing", "workload has been rollback, then rollout is canceled")
|
||||
klog.Infof("rollout(%s/%s) workload has been rollback directly, then rollout canceled", rollout.Namespace, rollout.Name)
|
||||
progressingStateTransition(newStatus, corev1.ConditionTrue, v1alpha1.ProgressingReasonCancelling, "The workload has been rolled back and the rollout process will be cancelled")
|
||||
|
|
@ -252,11 +252,12 @@ func (r *RolloutReconciler) handleRollbackDirectly(rollout *v1beta1.Rollout, wor
|
|||
|
||||
func (r *RolloutReconciler) handleRollbackInBatches(rollout *v1beta1.Rollout, workload *util.Workload, newStatus *v1beta1.RolloutStatus) error {
|
||||
// restart from the beginning
|
||||
newStatus.CanaryStatus.CurrentStepIndex = 1
|
||||
newStatus.CanaryStatus.CanaryRevision = workload.CanaryRevision
|
||||
newStatus.CanaryStatus.CurrentStepState = v1beta1.CanaryStepStateUpgrade
|
||||
newStatus.CanaryStatus.LastUpdateTime = &metav1.Time{Time: time.Now()}
|
||||
newStatus.CanaryStatus.RolloutHash = rollout.Annotations[util.RolloutHashAnnotation]
|
||||
newStatus.GetSubStatus().CurrentStepIndex = 1
|
||||
newStatus.GetSubStatus().NextStepIndex = util.NextBatchIndex(rollout, 1)
|
||||
newStatus.SetCanaryRevision(workload.CanaryRevision)
|
||||
newStatus.GetSubStatus().CurrentStepState = v1beta1.CanaryStepStateInit
|
||||
newStatus.GetSubStatus().LastUpdateTime = &metav1.Time{Time: time.Now()}
|
||||
newStatus.GetSubStatus().RolloutHash = rollout.Annotations[util.RolloutHashAnnotation]
|
||||
klog.Infof("rollout(%s/%s) workload has been rollback in batches, then restart from beginning", rollout.Namespace, rollout.Name)
|
||||
return nil
|
||||
}
|
||||
|
|
@ -267,24 +268,46 @@ func (r *RolloutReconciler) handleRolloutPlanChanged(c *RolloutContext) error {
|
|||
klog.Errorf("rollout(%s/%s) reCalculate Canary StepIndex failed: %s", c.Rollout.Namespace, c.Rollout.Name, err.Error())
|
||||
return err
|
||||
}
|
||||
// canary step configuration change causes current step index change
|
||||
c.NewStatus.CanaryStatus.CurrentStepIndex = newStepIndex
|
||||
c.NewStatus.CanaryStatus.CurrentStepState = v1beta1.CanaryStepStateUpgrade
|
||||
c.NewStatus.CanaryStatus.LastUpdateTime = &metav1.Time{Time: time.Now()}
|
||||
c.NewStatus.CanaryStatus.RolloutHash = c.Rollout.Annotations[util.RolloutHashAnnotation]
|
||||
klog.Infof("rollout(%s/%s) canary step configuration change, and stepIndex(%d) state(%s)",
|
||||
c.Rollout.Namespace, c.Rollout.Name, c.NewStatus.CanaryStatus.CurrentStepIndex, c.NewStatus.CanaryStatus.CurrentStepState)
|
||||
// if the target step index is the same as the NextStepIndex
|
||||
// we simply set the CurrentStepState to Ready
|
||||
if c.NewStatus.GetSubStatus().NextStepIndex == newStepIndex {
|
||||
c.NewStatus.GetSubStatus().CurrentStepState = v1beta1.CanaryStepStateReady
|
||||
c.NewStatus.GetSubStatus().LastUpdateTime = &metav1.Time{Time: time.Now()}
|
||||
c.NewStatus.GetSubStatus().RolloutHash = c.Rollout.Annotations[util.RolloutHashAnnotation]
|
||||
klog.Infof("rollout(%s/%s) canary step configuration change, and NextStepIndex(%d) state(%s)",
|
||||
c.Rollout.Namespace, c.Rollout.Name, c.NewStatus.GetSubStatus().NextStepIndex, c.NewStatus.GetSubStatus().CurrentStepState)
|
||||
return nil
|
||||
}
|
||||
|
||||
// otherwise, execute the "jump" logic
|
||||
c.NewStatus.GetSubStatus().NextStepIndex = newStepIndex
|
||||
c.NewStatus.GetSubStatus().LastUpdateTime = &metav1.Time{Time: time.Now()}
|
||||
c.NewStatus.GetSubStatus().RolloutHash = c.Rollout.Annotations[util.RolloutHashAnnotation]
|
||||
releaseManager, err := r.getReleaseManager(c.Rollout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
releaseManager.doCanaryJump(c)
|
||||
klog.Infof("rollout(%s/%s) canary step configuration change, and NextStepIndex(%d) state(%s)",
|
||||
c.Rollout.Namespace, c.Rollout.Name, c.NewStatus.GetSubStatus().NextStepIndex, c.NewStatus.GetSubStatus().CurrentStepState)
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *RolloutReconciler) handleNormalRolling(c *RolloutContext) error {
|
||||
// check if canary is done
|
||||
if c.NewStatus.CanaryStatus.CurrentStepState == v1beta1.CanaryStepStateCompleted {
|
||||
if c.NewStatus.GetSubStatus().CurrentStepState == v1beta1.CanaryStepStateCompleted {
|
||||
klog.Infof("rollout(%s/%s) progressing rolling done", c.Rollout.Namespace, c.Rollout.Name)
|
||||
progressingStateTransition(c.NewStatus, corev1.ConditionTrue, v1alpha1.ProgressingReasonFinalising, "Rollout has been completed and some closing work is being done")
|
||||
return nil
|
||||
}
|
||||
return r.canaryManager.runCanary(c)
|
||||
// in case user modifies it with inappropriate value
|
||||
util.CheckNextBatchIndexWithCorrect(c.Rollout)
|
||||
|
||||
releaseManager, err := r.getReleaseManager(c.Rollout)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return releaseManager.runCanary(c)
|
||||
}
|
||||
|
||||
// name is rollout name, tr is trafficRouting name
|
||||
|
|
@ -344,30 +367,41 @@ func (r *RolloutReconciler) finalizeTrafficRouting(namespace, name, tr string) e
|
|||
|
||||
***********************************************************************
|
||||
*/
|
||||
|
||||
func (r *RolloutReconciler) getReleaseManager(rollout *v1beta1.Rollout) (ReleaseManager, error) {
|
||||
if rollout.Spec.Strategy.IsCanaryStragegy() {
|
||||
return r.canaryManager, nil
|
||||
} else if rollout.Spec.Strategy.IsBlueGreenRelease() {
|
||||
// placeholder for upcoming PR
|
||||
// return r.blueGreenManager, nil
|
||||
}
|
||||
return nil, fmt.Errorf("unknown rolling style: %s, and thus cannot call corresponding release manager", rollout.Spec.Strategy.GetRollingStyle())
|
||||
}
|
||||
|
||||
func isRolloutPaused(rollout *v1beta1.Rollout) bool {
|
||||
return rollout.Spec.Strategy.Paused
|
||||
}
|
||||
|
||||
func isRolloutPlanChanged(rollout *v1beta1.Rollout) bool {
|
||||
status := &rollout.Status
|
||||
return status.CanaryStatus.RolloutHash != "" && status.CanaryStatus.RolloutHash != rollout.Annotations[util.RolloutHashAnnotation]
|
||||
return status.GetSubStatus().RolloutHash != "" && status.GetSubStatus().RolloutHash != rollout.Annotations[util.RolloutHashAnnotation]
|
||||
}
|
||||
|
||||
func isContinuousRelease(rollout *v1beta1.Rollout, workload *util.Workload) bool {
|
||||
status := &rollout.Status
|
||||
return status.CanaryStatus.CanaryRevision != "" && workload.CanaryRevision != status.CanaryStatus.CanaryRevision && !workload.IsInRollback
|
||||
return status.GetCanaryRevision() != "" && workload.CanaryRevision != status.GetCanaryRevision() && !workload.IsInRollback
|
||||
}
|
||||
|
||||
func isRollingBackDirectly(rollout *v1beta1.Rollout, workload *util.Workload) bool {
|
||||
status := &rollout.Status
|
||||
inBatch := util.IsRollbackInBatchPolicy(rollout, workload.Labels)
|
||||
return workload.IsInRollback && workload.CanaryRevision != status.CanaryStatus.CanaryRevision && !inBatch
|
||||
return workload.IsInRollback && workload.CanaryRevision != status.GetCanaryRevision() && !inBatch
|
||||
}
|
||||
|
||||
func isRollingBackInBatches(rollout *v1beta1.Rollout, workload *util.Workload) bool {
|
||||
status := &rollout.Status
|
||||
inBatch := util.IsRollbackInBatchPolicy(rollout, workload.Labels)
|
||||
return workload.IsInRollback && workload.CanaryRevision != status.CanaryStatus.CanaryRevision && inBatch
|
||||
return workload.IsInRollback && workload.CanaryRevision != status.GetCanaryRevision() && inBatch
|
||||
}
|
||||
|
||||
// 1. modify network api(ingress or gateway api) configuration, and route 100% traffic to stable pods
|
||||
|
|
@ -393,16 +427,35 @@ func (r *RolloutReconciler) doProgressingReset(c *RolloutContext) (bool, error)
|
|||
}
|
||||
|
||||
func (r *RolloutReconciler) recalculateCanaryStep(c *RolloutContext) (int32, error) {
|
||||
batch, err := r.canaryManager.fetchBatchRelease(c.Rollout.Namespace, c.Rollout.Name)
|
||||
releaseManager, err := r.getReleaseManager(c.Rollout)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
batch, err := releaseManager.fetchBatchRelease(c.Rollout.Namespace, c.Rollout.Name)
|
||||
if errors.IsNotFound(err) {
|
||||
return 1, nil
|
||||
} else if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
currentReplicas, _ := intstr.GetScaledValueFromIntOrPercent(&batch.Spec.ReleasePlan.Batches[*batch.Spec.ReleasePlan.BatchPartition].CanaryReplicas, int(c.Workload.Replicas), true)
|
||||
var stepIndex int32
|
||||
for i := range c.Rollout.Spec.Strategy.Canary.Steps {
|
||||
step := c.Rollout.Spec.Strategy.Canary.Steps[i]
|
||||
var stepIndex, currentIndex int32
|
||||
if c.NewStatus != nil {
|
||||
currentIndex = c.NewStatus.GetSubStatus().CurrentStepIndex - 1
|
||||
}
|
||||
steps := append([]int{}, int(currentIndex))
|
||||
// we don't distinguish between the changes in Replicas and Traffic
|
||||
// Whatever the change is, we recalculate the step.
|
||||
// we put the current step index first for retrieval, so that if Traffic is the only change,
|
||||
// usually we will get the target step index same as current step index
|
||||
for i := 0; i < len(c.Rollout.Spec.Strategy.GetSteps()); i++ {
|
||||
if i == int(currentIndex) {
|
||||
continue
|
||||
}
|
||||
steps = append(steps, i)
|
||||
}
|
||||
|
||||
for _, i := range steps {
|
||||
step := c.Rollout.Spec.Strategy.GetSteps()[i]
|
||||
var desiredReplicas int
|
||||
desiredReplicas, _ = intstr.GetScaledValueFromIntOrPercent(step.Replicas, int(c.Workload.Replicas), true)
|
||||
stepIndex = int32(i + 1)
|
||||
|
|
@ -410,6 +463,7 @@ func (r *RolloutReconciler) recalculateCanaryStep(c *RolloutContext) (int32, err
|
|||
break
|
||||
}
|
||||
}
|
||||
klog.Infof("RolloutPlan Change detected, rollout(%s/%s) currentStepIndex %d, jumps to %d", c.Rollout.Namespace, c.Rollout.Name, currentIndex+1, stepIndex)
|
||||
return stepIndex, nil
|
||||
}
|
||||
|
||||
|
|
@ -422,7 +476,11 @@ func (r *RolloutReconciler) doFinalising(c *RolloutContext) (bool, error) {
|
|||
return false, err
|
||||
}
|
||||
}
|
||||
done, err := r.canaryManager.doCanaryFinalising(c)
|
||||
releaseManager, err := r.getReleaseManager(c.Rollout)
|
||||
if err != nil {
|
||||
return false, err
|
||||
}
|
||||
done, err := releaseManager.doCanaryFinalising(c)
|
||||
if err != nil {
|
||||
klog.Errorf("rollout(%s/%s) Progressing failed: %s", c.Rollout.Namespace, c.Rollout.Name, err.Error())
|
||||
return false, err
|
||||
|
|
@ -460,7 +518,16 @@ func setRolloutSucceededCondition(status *v1beta1.RolloutStatus, condStatus core
|
|||
}
|
||||
|
||||
func newTrafficRoutingContext(c *RolloutContext) *trafficrouting.TrafficRoutingContext {
|
||||
currentStep := c.Rollout.Spec.Strategy.Canary.Steps[c.NewStatus.CanaryStatus.CurrentStepIndex-1]
|
||||
currentIndex := c.NewStatus.GetSubStatus().CurrentStepIndex - 1
|
||||
var currentStep v1beta1.CanaryStep
|
||||
if currentIndex < 0 || int(currentIndex) >= len(c.Rollout.Spec.Strategy.GetSteps()) {
|
||||
klog.Warningf("Rollout(%s/%s) encounters a special case when constructing newTrafficRoutingContext", c.Rollout.Namespace, c.Rollout.Name)
|
||||
// usually this only happens when deleting the rollout or rolling back (ie. Finalising)
|
||||
// in these scenarios, it's not important which step the current is
|
||||
currentStep = c.Rollout.Spec.Strategy.GetSteps()[0]
|
||||
} else {
|
||||
currentStep = c.Rollout.Spec.Strategy.GetSteps()[currentIndex]
|
||||
}
|
||||
var revisionLabelKey string
|
||||
if c.Workload != nil {
|
||||
revisionLabelKey = c.Workload.RevisionLabelKey
|
||||
|
|
@ -468,13 +535,13 @@ func newTrafficRoutingContext(c *RolloutContext) *trafficrouting.TrafficRoutingC
|
|||
return &trafficrouting.TrafficRoutingContext{
|
||||
Key: fmt.Sprintf("Rollout(%s/%s)", c.Rollout.Namespace, c.Rollout.Name),
|
||||
Namespace: c.Rollout.Namespace,
|
||||
ObjectRef: c.Rollout.Spec.Strategy.Canary.TrafficRoutings,
|
||||
ObjectRef: c.Rollout.Spec.Strategy.GetTrafficRouting(),
|
||||
Strategy: currentStep.TrafficRoutingStrategy,
|
||||
OwnerRef: *metav1.NewControllerRef(c.Rollout, rolloutControllerKind),
|
||||
RevisionLabelKey: revisionLabelKey,
|
||||
StableRevision: c.NewStatus.CanaryStatus.StableRevision,
|
||||
CanaryRevision: c.NewStatus.CanaryStatus.PodTemplateHash,
|
||||
LastUpdateTime: c.NewStatus.CanaryStatus.LastUpdateTime,
|
||||
DisableGenerateCanaryService: c.Rollout.Spec.Strategy.Canary.DisableGenerateCanaryService,
|
||||
StableRevision: c.NewStatus.GetSubStatus().StableRevision,
|
||||
CanaryRevision: c.NewStatus.GetSubStatus().PodTemplateHash,
|
||||
LastUpdateTime: c.NewStatus.GetSubStatus().LastUpdateTime,
|
||||
DisableGenerateCanaryService: c.Rollout.Spec.Strategy.DisableGenerateCanaryService(),
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -67,8 +67,11 @@ func TestReconcileRolloutProgressing(t *testing.T) {
|
|||
s.CanaryStatus.StableRevision = "pod-template-hash-v1"
|
||||
s.CanaryStatus.CanaryRevision = "6f8cc56547"
|
||||
s.CanaryStatus.CurrentStepIndex = 1
|
||||
// s.CanaryStatus.NextStepIndex will be initialized as 0 in ReconcileRolloutProgressing.
|
||||
// util.NextBatchIndex(rollout, s.CanaryStatus.CurrentStepIndex), which is 2 here.
|
||||
s.CanaryStatus.NextStepIndex = 2
|
||||
s.CanaryStatus.CurrentStepState = v1beta1.CanaryStepStateUpgrade
|
||||
// now the first step is no longer StepStateUpgrade, it is StepStateInit now
|
||||
s.CanaryStatus.CurrentStepState = v1beta1.CanaryStepStateInit
|
||||
return s
|
||||
},
|
||||
expectTr: func() *v1alpha1.TrafficRouting {
|
||||
|
|
@ -101,7 +104,7 @@ func TestReconcileRolloutProgressing(t *testing.T) {
|
|||
s.CanaryStatus.CanaryRevision = "6f8cc56547"
|
||||
s.CanaryStatus.CurrentStepIndex = 1
|
||||
s.CanaryStatus.NextStepIndex = 2
|
||||
s.CanaryStatus.CurrentStepState = v1beta1.CanaryStepStateUpgrade
|
||||
s.CanaryStatus.CurrentStepState = v1beta1.CanaryStepStateInit
|
||||
cond := util.GetRolloutCondition(*s, v1beta1.RolloutConditionProgressing)
|
||||
cond.Reason = v1alpha1.ProgressingReasonInRolling
|
||||
util.SetRolloutCondition(s, *cond)
|
||||
|
|
@ -142,6 +145,7 @@ func TestReconcileRolloutProgressing(t *testing.T) {
|
|||
obj.Status.CanaryStatus.StableRevision = "pod-template-hash-v1"
|
||||
obj.Status.CanaryStatus.CanaryRevision = "6f8cc56547"
|
||||
obj.Status.CanaryStatus.CurrentStepIndex = 1
|
||||
obj.Status.CanaryStatus.NextStepIndex = 2
|
||||
obj.Status.CanaryStatus.CurrentStepState = v1beta1.CanaryStepStateUpgrade
|
||||
cond := util.GetRolloutCondition(obj.Status, v1beta1.RolloutConditionProgressing)
|
||||
cond.Reason = v1alpha1.ProgressingReasonInRolling
|
||||
|
|
@ -156,6 +160,7 @@ func TestReconcileRolloutProgressing(t *testing.T) {
|
|||
s.CanaryStatus.CanaryRevision = "6f8cc56547"
|
||||
s.CanaryStatus.PodTemplateHash = "pod-template-hash-v2"
|
||||
s.CanaryStatus.CurrentStepIndex = 1
|
||||
s.CanaryStatus.NextStepIndex = 2
|
||||
s.CanaryStatus.CurrentStepState = v1beta1.CanaryStepStateUpgrade
|
||||
cond := util.GetRolloutCondition(*s, v1beta1.RolloutConditionProgressing)
|
||||
cond.Reason = v1alpha1.ProgressingReasonInRolling
|
||||
|
|
@ -212,6 +217,7 @@ func TestReconcileRolloutProgressing(t *testing.T) {
|
|||
s.CanaryStatus.CanaryRevision = "6f8cc56547"
|
||||
s.CanaryStatus.PodTemplateHash = "pod-template-hash-v2"
|
||||
s.CanaryStatus.CurrentStepIndex = 4
|
||||
s.CanaryStatus.NextStepIndex = 0
|
||||
s.CanaryStatus.CurrentStepState = v1beta1.CanaryStepStateCompleted
|
||||
cond := util.GetRolloutCondition(*s, v1beta1.RolloutConditionProgressing)
|
||||
cond.Reason = v1alpha1.ProgressingReasonFinalising
|
||||
|
|
@ -270,6 +276,7 @@ func TestReconcileRolloutProgressing(t *testing.T) {
|
|||
s.CanaryStatus.CanaryRevision = "6f8cc56547"
|
||||
s.CanaryStatus.PodTemplateHash = "pod-template-hash-v2"
|
||||
s.CanaryStatus.CurrentStepIndex = 4
|
||||
s.CanaryStatus.NextStepIndex = 0
|
||||
s.CanaryStatus.CurrentStepState = v1beta1.CanaryStepStateCompleted
|
||||
cond := util.GetRolloutCondition(*s, v1beta1.RolloutConditionProgressing)
|
||||
cond.Reason = v1alpha1.ProgressingReasonFinalising
|
||||
|
|
@ -330,6 +337,7 @@ func TestReconcileRolloutProgressing(t *testing.T) {
|
|||
s.CanaryStatus.CanaryRevision = "6f8cc56547"
|
||||
s.CanaryStatus.PodTemplateHash = "pod-template-hash-v2"
|
||||
s.CanaryStatus.CurrentStepIndex = 4
|
||||
s.CanaryStatus.NextStepIndex = 0
|
||||
s.CanaryStatus.CurrentStepState = v1beta1.CanaryStepStateCompleted
|
||||
cond2 := util.GetRolloutCondition(*s, v1beta1.RolloutConditionProgressing)
|
||||
cond2.Reason = v1alpha1.ProgressingReasonFinalising
|
||||
|
|
@ -379,6 +387,7 @@ func TestReconcileRolloutProgressing(t *testing.T) {
|
|||
s.CanaryStatus.CanaryRevision = "6f8cc56547"
|
||||
s.CanaryStatus.PodTemplateHash = "pod-template-hash-v2"
|
||||
s.CanaryStatus.CurrentStepIndex = 4
|
||||
s.CanaryStatus.NextStepIndex = 0
|
||||
s.CanaryStatus.CurrentStepState = v1beta1.CanaryStepStateCompleted
|
||||
cond2 := util.GetRolloutCondition(*s, v1beta1.RolloutConditionProgressing)
|
||||
cond2.Reason = v1alpha1.ProgressingReasonCompleted
|
||||
|
|
|
|||
|
|
@ -0,0 +1,13 @@
|
|||
package rollout
|
||||
|
||||
import (
|
||||
"github.com/openkruise/rollouts/api/v1beta1"
|
||||
)
|
||||
|
||||
type ReleaseManager interface {
|
||||
runCanary(c *RolloutContext) error
|
||||
doCanaryJump(c *RolloutContext) bool
|
||||
doCanaryFinalising(c *RolloutContext) (bool, error)
|
||||
fetchBatchRelease(ns, name string) (*v1beta1.BatchRelease, error)
|
||||
removeBatchRelease(c *RolloutContext) (bool, error)
|
||||
}
|
||||
|
|
@ -91,10 +91,10 @@ func (r *RolloutReconciler) calculateRolloutStatus(rollout *v1beta1.Rollout) (re
|
|||
// update workload generation to canaryStatus.ObservedWorkloadGeneration
|
||||
// rollout is a target ref bypass, so there needs to be a field to identify the rollout execution process or results,
|
||||
// which version of deployment is targeted, ObservedWorkloadGeneration that is to compare with the workload generation
|
||||
if newStatus.CanaryStatus != nil && newStatus.CanaryStatus.CanaryRevision != "" &&
|
||||
newStatus.CanaryStatus.CanaryRevision == workload.CanaryRevision {
|
||||
newStatus.CanaryStatus.ObservedRolloutID = getRolloutID(workload)
|
||||
newStatus.CanaryStatus.ObservedWorkloadGeneration = workload.Generation
|
||||
if !newStatus.IsSubStatusEmpty() && newStatus.GetCanaryRevision() != "" &&
|
||||
newStatus.GetCanaryRevision() == workload.CanaryRevision {
|
||||
newStatus.GetSubStatus().ObservedRolloutID = getRolloutID(workload)
|
||||
newStatus.GetSubStatus().ObservedWorkloadGeneration = workload.Generation
|
||||
}
|
||||
|
||||
switch newStatus.Phase {
|
||||
|
|
@ -110,7 +110,7 @@ func (r *RolloutReconciler) calculateRolloutStatus(rollout *v1beta1.Rollout) (re
|
|||
cond := util.NewRolloutCondition(v1beta1.RolloutConditionProgressing, corev1.ConditionTrue, v1alpha1.ProgressingReasonInitializing, "Rollout is in Progressing")
|
||||
util.SetRolloutCondition(newStatus, *cond)
|
||||
util.RemoveRolloutCondition(newStatus, v1beta1.RolloutConditionSucceeded)
|
||||
} else if newStatus.CanaryStatus == nil {
|
||||
} else if newStatus.IsSubStatusEmpty() {
|
||||
// The following logic is to make PaaS be able to judge whether the rollout is ready
|
||||
// at the first deployment of the Rollout/Workload. For example: generally, a PaaS
|
||||
// platform can use the following code to judge whether the rollout progression is completed:
|
||||
|
|
@ -159,15 +159,30 @@ func (r *RolloutReconciler) calculateRolloutStatus(rollout *v1beta1.Rollout) (re
|
|||
// rolloutHash mainly records the step batch information, when the user step changes,
|
||||
// the current batch can be recalculated
|
||||
func (r *RolloutReconciler) calculateRolloutHash(rollout *v1beta1.Rollout) error {
|
||||
canary := rollout.Spec.Strategy.Canary.DeepCopy()
|
||||
canary.FailureThreshold = nil
|
||||
canary.Steps = nil
|
||||
for i := range rollout.Spec.Strategy.Canary.Steps {
|
||||
step := rollout.Spec.Strategy.Canary.Steps[i].DeepCopy()
|
||||
step.Pause = v1beta1.RolloutPause{}
|
||||
canary.Steps = append(canary.Steps, *step)
|
||||
var data string
|
||||
if rollout.Spec.Strategy.IsCanaryStragegy() {
|
||||
canary := rollout.Spec.Strategy.Canary.DeepCopy()
|
||||
canary.FailureThreshold = nil
|
||||
canary.Steps = nil
|
||||
for i := range rollout.Spec.Strategy.Canary.Steps {
|
||||
step := rollout.Spec.Strategy.Canary.Steps[i].DeepCopy()
|
||||
step.Pause = v1beta1.RolloutPause{}
|
||||
canary.Steps = append(canary.Steps, *step)
|
||||
}
|
||||
data = util.DumpJSON(canary)
|
||||
} else if rollout.Spec.Strategy.IsBlueGreenRelease() {
|
||||
blueGreen := rollout.Spec.Strategy.BlueGreen.DeepCopy()
|
||||
blueGreen.FailureThreshold = nil
|
||||
blueGreen.Steps = nil
|
||||
for i := range rollout.Spec.Strategy.BlueGreen.Steps {
|
||||
step := rollout.Spec.Strategy.BlueGreen.Steps[i].DeepCopy()
|
||||
step.Pause = v1beta1.RolloutPause{}
|
||||
blueGreen.Steps = append(blueGreen.Steps, *step)
|
||||
}
|
||||
data = util.DumpJSON(blueGreen)
|
||||
} else {
|
||||
return fmt.Errorf("unknown rolling style: %s", rollout.Spec.Strategy.GetRollingStyle())
|
||||
}
|
||||
data := util.DumpJSON(canary)
|
||||
hash := rand.SafeEncodeString(util.EncodeHash(data))
|
||||
if rollout.Annotations[util.RolloutHashAnnotation] == hash {
|
||||
return nil
|
||||
|
|
|
|||
|
|
@ -27,7 +27,8 @@ const (
|
|||
// BatchReleaseControlAnnotation is controller info about batchRelease when rollout
|
||||
BatchReleaseControlAnnotation = "batchrelease.rollouts.kruise.io/control-info"
|
||||
// InRolloutProgressingAnnotation marks workload as entering the rollout progressing process
|
||||
//and does not allow paused=false during this process
|
||||
// and does not allow paused=false during this process. However, blueGreen is an exception,
|
||||
// which allows paused=false during progressing.
|
||||
InRolloutProgressingAnnotation = "rollouts.kruise.io/in-progressing"
|
||||
// RolloutHashAnnotation record observed rollout spec hash
|
||||
RolloutHashAnnotation = "rollouts.kruise.io/hash"
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ func NewControllerFinder(c client.Client) *ControllerFinder {
|
|||
|
||||
func (r *ControllerFinder) GetWorkloadForRef(rollout *rolloutv1beta1.Rollout) (*Workload, error) {
|
||||
workloadRef := rollout.Spec.WorkloadRef
|
||||
if rollout.Spec.Strategy.Canary.EnableExtraWorkloadForCanary {
|
||||
if rollout.Spec.Strategy.GetRollingStyle() == rolloutv1beta1.CanaryRollingStyle {
|
||||
for _, finder := range append(r.canaryStyleFinders(), r.partitionStyleFinders()...) {
|
||||
workload, err := finder(rollout.Namespace, &workloadRef)
|
||||
if workload != nil || err != nil {
|
||||
|
|
|
|||
|
|
@ -47,7 +47,7 @@ type RolloutState struct {
|
|||
|
||||
func IsRollbackInBatchPolicy(rollout *rolloutv1beta1.Rollout, labels map[string]string) bool {
|
||||
// currently, only support the case of no traffic routing
|
||||
if len(rollout.Spec.Strategy.Canary.TrafficRoutings) > 0 {
|
||||
if rollout.Spec.Strategy.HasTrafficRoutings() {
|
||||
return false
|
||||
}
|
||||
workloadRef := rollout.Spec.WorkloadRef
|
||||
|
|
@ -176,3 +176,17 @@ func NextBatchIndex(rollout *rolloutv1beta1.Rollout, CurrentStepIndex int32) int
|
|||
}
|
||||
return CurrentStepIndex + 1
|
||||
}
|
||||
|
||||
// check if NextStepIndex is legal, if not, correct it
|
||||
func CheckNextBatchIndexWithCorrect(rollout *rolloutv1beta1.Rollout) {
|
||||
if rollout == nil {
|
||||
return
|
||||
}
|
||||
nextStep := rollout.Status.GetSubStatus().NextStepIndex
|
||||
if nextStep <= 0 || nextStep > int32(len(rollout.Spec.Strategy.GetSteps())) {
|
||||
rollout.Status.GetSubStatus().NextStepIndex = NextBatchIndex(rollout, rollout.Status.GetSubStatus().CurrentStepIndex)
|
||||
if nextStep != rollout.Status.GetSubStatus().NextStepIndex {
|
||||
klog.Infof("rollout(%s/%s) invalid nextStepIndex(%d), reset to %d", rollout.Namespace, rollout.Name, nextStep, rollout.Status.GetSubStatus().NextStepIndex)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -242,7 +242,7 @@ func (h *WorkloadHandler) handleStatefulSetLikeWorkload(newObj, oldObj *unstruct
|
|||
rollout, err := h.fetchMatchedRollout(newObj)
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if rollout == nil || rollout.Spec.Strategy.Canary == nil {
|
||||
} else if rollout == nil || rollout.Spec.Strategy.IsEmptyRelease() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
|
@ -310,7 +310,7 @@ func (h *WorkloadHandler) handleDeployment(newObj, oldObj *apps.Deployment) (boo
|
|||
rollout, err := h.fetchMatchedRollout(newObj)
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if rollout == nil || rollout.Spec.Strategy.Canary == nil {
|
||||
} else if rollout == nil || rollout.Spec.Strategy.IsEmptyRelease() {
|
||||
return false, nil
|
||||
}
|
||||
rss, err := h.Finder.GetReplicaSetsForDeployment(newObj)
|
||||
|
|
@ -319,7 +319,7 @@ func (h *WorkloadHandler) handleDeployment(newObj, oldObj *apps.Deployment) (boo
|
|||
return false, nil
|
||||
}
|
||||
// if traffic routing, workload must only be one version of Pods
|
||||
if len(rollout.Spec.Strategy.Canary.TrafficRoutings) > 0 {
|
||||
if rollout.Spec.Strategy.HasTrafficRoutings() {
|
||||
if len(rss) != 1 {
|
||||
klog.Warningf("Because deployment(%s/%s) have multiple versions of Pods, so can not enter rollout progressing", newObj.Namespace, newObj.Name)
|
||||
return false, nil
|
||||
|
|
@ -334,6 +334,7 @@ func (h *WorkloadHandler) handleDeployment(newObj, oldObj *apps.Deployment) (boo
|
|||
if newObj.Labels == nil {
|
||||
newObj.Labels = map[string]string{}
|
||||
}
|
||||
// blueGreen also need the stable revision label
|
||||
newObj.Labels[appsv1alpha1.DeploymentStableRevisionLabel] = stableRS.Labels[apps.DefaultDeploymentUniqueLabelKey]
|
||||
}
|
||||
|
||||
|
|
@ -365,11 +366,11 @@ func (h *WorkloadHandler) handleCloneSet(newObj, oldObj *kruiseappsv1alpha1.Clon
|
|||
rollout, err := h.fetchMatchedRollout(newObj)
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if rollout == nil || rollout.Spec.Strategy.Canary == nil {
|
||||
} else if rollout == nil || rollout.Spec.Strategy.IsEmptyRelease() {
|
||||
return false, nil
|
||||
}
|
||||
// if traffic routing, there must only be one version of Pods
|
||||
if len(rollout.Spec.Strategy.Canary.TrafficRoutings) > 0 && newObj.Status.Replicas != newObj.Status.UpdatedReplicas {
|
||||
if rollout.Spec.Strategy.HasTrafficRoutings() && newObj.Status.Replicas != newObj.Status.UpdatedReplicas {
|
||||
klog.Warningf("Because cloneSet(%s/%s) have multiple versions of Pods, so can not enter rollout progressing", newObj.Namespace, newObj.Name)
|
||||
return false, nil
|
||||
}
|
||||
|
|
@ -398,7 +399,7 @@ func (h *WorkloadHandler) handleDaemonSet(newObj, oldObj *kruiseappsv1alpha1.Dae
|
|||
rollout, err := h.fetchMatchedRollout(newObj)
|
||||
if err != nil {
|
||||
return false, err
|
||||
} else if rollout == nil || rollout.Spec.Strategy.Canary == nil {
|
||||
} else if rollout == nil || rollout.Spec.Strategy.IsEmptyRelease() {
|
||||
return false, nil
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2684,7 +2684,7 @@ var _ = SIGDescribe("Rollout", func() {
|
|||
})
|
||||
})
|
||||
|
||||
KruiseDescribe("Canary rollout with custon network provider", func() {
|
||||
KruiseDescribe("Canary rollout with custom network provider", func() {
|
||||
It("V1->V2: Route traffic with header/queryParams/path matches and weight using rollout for VirtualService", func() {
|
||||
By("Creating Rollout...")
|
||||
rollout := &v1beta1.Rollout{}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,32 @@
|
|||
apiVersion: rollouts.kruise.io/v1beta1 # we use v1beta1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
name: rollouts-demo
|
||||
spec:
|
||||
workloadRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: echoserver
|
||||
strategy:
|
||||
blueGreen:
|
||||
steps:
|
||||
- traffic: 20%
|
||||
replicas: 20%
|
||||
pause: {}
|
||||
- traffic: 40%
|
||||
replicas: 40%
|
||||
pause: {duration: 10}
|
||||
- traffic: 60%
|
||||
replicas: 60%
|
||||
pause: {duration: 10}
|
||||
- traffic: 80%
|
||||
replicas: 80%
|
||||
pause: {duration: 10}
|
||||
- traffic: 100%
|
||||
replicas: 100%
|
||||
pause: {duration: 0}
|
||||
trafficRoutings:
|
||||
- service: echoserver
|
||||
ingress:
|
||||
classType: nginx
|
||||
name: echoserver
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
apiVersion: rollouts.kruise.io/v1beta1 # we use v1beta1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
name: rollouts-demo
|
||||
spec:
|
||||
workloadRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: echoserver
|
||||
strategy:
|
||||
canary:
|
||||
enableExtraWorkloadForCanary: true
|
||||
steps:
|
||||
- traffic: 20%
|
||||
replicas: 20%
|
||||
pause: {}
|
||||
- traffic: 40%
|
||||
replicas: 40%
|
||||
pause: {duration: 10}
|
||||
- traffic: 60%
|
||||
replicas: 60%
|
||||
pause: {duration: 10}
|
||||
- traffic: 80%
|
||||
replicas: 80%
|
||||
pause: {duration: 10}
|
||||
- traffic: 100%
|
||||
replicas: 100%
|
||||
pause: {duration: 0}
|
||||
trafficRoutings:
|
||||
- service: echoserver
|
||||
ingress:
|
||||
classType: nginx
|
||||
name: echoserver
|
||||
|
|
@ -0,0 +1,33 @@
|
|||
apiVersion: rollouts.kruise.io/v1beta1 # we use v1beta1
|
||||
kind: Rollout
|
||||
metadata:
|
||||
name: rollouts-demo
|
||||
spec:
|
||||
workloadRef:
|
||||
apiVersion: apps/v1
|
||||
kind: Deployment
|
||||
name: echoserver
|
||||
strategy:
|
||||
canary:
|
||||
enableExtraWorkloadForCanary: false
|
||||
steps:
|
||||
- traffic: 20%
|
||||
replicas: 20%
|
||||
pause: {}
|
||||
- traffic: 40%
|
||||
replicas: 40%
|
||||
pause: {duration: 10}
|
||||
- traffic: 60%
|
||||
replicas: 60%
|
||||
pause: {duration: 10}
|
||||
- traffic: 80%
|
||||
replicas: 80%
|
||||
pause: {duration: 10}
|
||||
- traffic: 100%
|
||||
replicas: 100%
|
||||
pause: {duration: 0}
|
||||
trafficRoutings:
|
||||
- service: echoserver
|
||||
ingress:
|
||||
classType: nginx
|
||||
name: echoserver
|
||||
Loading…
Reference in New Issue