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>
This commit is contained in:
yunbo 2024-05-27 21:30:49 +08:00
parent 1e8af4a4c1
commit d6c1809f82
20 changed files with 2266 additions and 78 deletions

110
.github/workflows/e2e-v1beta1-1.19.yaml vendored Normal file
View File

@ -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

110
.github/workflows/e2e-v1beta1-1.23.yaml vendored Normal file
View File

@ -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

View File

@ -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() {

View File

@ -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:

View File

@ -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{

View File

@ -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
}

View File

@ -87,6 +87,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 +151,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)
@ -201,15 +209,13 @@ func (m *canaryReleaseManager) doCanaryMetricsAnalysis(c *RolloutContext) (bool,
}
func (m *canaryReleaseManager) doCanaryPaused(c *RolloutContext) (bool, error) {
if m.doCanaryJump(c) {
klog.Infof("rollout(%s/%s) canary step jumped", c.Rollout.Namespace, c.Rollout.Name)
return false, nil
}
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 +238,34 @@ func (m *canaryReleaseManager) doCanaryPaused(c *RolloutContext) (bool, error) {
return false, nil
}
func (m *canaryReleaseManager) doCanaryJump(c *RolloutContext) (jumped bool) {
canaryStatus := c.NewStatus.CanaryStatus
currentStep := c.Rollout.Spec.Strategy.Canary.Steps[canaryStatus.CurrentStepIndex-1]
nextIndex := canaryStatus.NextStepIndex
if nextIndex != util.NextBatchIndex(c.Rollout, canaryStatus.CurrentStepIndex) && nextIndex > 0 {
currentIndexBackup := canaryStatus.CurrentStepIndex
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) {
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, v1beta1.CanaryStepStatePaused, 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, v1beta1.CanaryStepStatePaused, 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

View File

@ -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,42 @@ 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, we jump to step paused, where the "jump" logic exists
c.NewStatus.GetSubStatus().NextStepIndex = newStepIndex
c.NewStatus.GetSubStatus().CurrentStepState = v1beta1.CanaryStepStatePaused
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
}
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 +363,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 +423,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 +459,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 +472,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 +514,17 @@ 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
//TODO - need better designed logic
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
// in this situation, it's no matter 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 +532,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(),
}
}

View File

@ -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

View File

@ -0,0 +1,12 @@
package rollout
import (
"github.com/openkruise/rollouts/api/v1beta1"
)
type ReleaseManager interface {
runCanary(c *RolloutContext) error
doCanaryFinalising(c *RolloutContext) (bool, error)
fetchBatchRelease(ns, name string) (*v1beta1.BatchRelease, error)
removeBatchRelease(c *RolloutContext) (bool, error)
}

View File

@ -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

View File

@ -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"

View File

@ -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 {

View File

@ -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)
}
}
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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