├── .dockerignore
├── .github
├── PULL_REQUEST_TEMPLATE.md
└── workflows
│ ├── ci.yaml
│ ├── docker-image.yaml
│ ├── e2e-advanced-deployment-1.19.yaml
│ ├── e2e-advanced-deployment-1.23.yaml
│ ├── e2e-advanced-deployment-1.26.yaml
│ ├── e2e-cloneset-1.19.yaml
│ ├── e2e-cloneset-1.23.yaml
│ ├── e2e-cloneset-1.26.yaml
│ ├── e2e-custom.yaml
│ ├── e2e-daemonset-1.19.yaml
│ ├── e2e-daemonset-1.23.yaml
│ ├── e2e-daemonset-1.26.yaml
│ ├── e2e-deployment-1.19.yaml
│ ├── e2e-deployment-1.23.yaml
│ ├── e2e-deployment-1.26.yaml
│ ├── e2e-gateway.yaml
│ ├── e2e-multi-network-provider.yaml
│ ├── e2e-others-1.19.yaml
│ ├── e2e-others-1.23.yaml
│ ├── e2e-others-1.26.yaml
│ ├── e2e-statefulset-1.19.yaml
│ ├── e2e-statefulset-1.23.yaml
│ ├── e2e-statefulset-1.26.yaml
│ ├── e2e-v1beta1-bluegreen-1.19.yaml
│ ├── e2e-v1beta1-bluegreen-1.23.yaml
│ ├── e2e-v1beta1-bluegreen-1.26.yaml
│ ├── e2e-v1beta1-jump-1.19.yaml
│ ├── e2e-v1beta1-jump-1.23.yaml
│ ├── e2e-v1beta1-jump-1.26.yaml
│ └── license.yml
├── .gitignore
├── .golangci.yml
├── .license
├── README.md
└── dependency_decisions.yml
├── .lift.toml
├── CHANGELOG.md
├── CONTRIBUTING.md
├── Dockerfile
├── Dockerfile_multiarch
├── LICENSE
├── Makefile
├── OWNERS
├── PROJECT
├── README.md
├── api
├── addtoscheme_apps_v1alpha1.go
├── addtoscheme_apps_v1beta1.go
├── apis.go
├── v1alpha1
│ ├── batchrelease_plan_types.go
│ ├── batchrelease_types.go
│ ├── conversion.go
│ ├── deployment_types.go
│ ├── groupversion_info.go
│ ├── rollout_types.go
│ ├── rollouthistory_types.go
│ ├── trafficrouting_types.go
│ └── zz_generated.deepcopy.go
└── v1beta1
│ ├── batchrelease_plan_types.go
│ ├── batchrelease_types.go
│ ├── convertion.go
│ ├── deployment_types.go
│ ├── groupversion_info.go
│ ├── rollout_types.go
│ ├── trafficrouting.go
│ └── zz_generated.deepcopy.go
├── config
├── crd
│ ├── bases
│ │ ├── rollouts.kruise.io_batchreleases.yaml
│ │ ├── rollouts.kruise.io_rollouthistories.yaml
│ │ ├── rollouts.kruise.io_rollouts.yaml
│ │ └── rollouts.kruise.io_trafficroutings.yaml
│ ├── kustomization.yaml
│ ├── kustomizeconfig.yaml
│ └── patches
│ │ ├── cainjection_in_batchreleases.yaml
│ │ ├── cainjection_in_rollouts.yaml
│ │ ├── webhook_in_batchreleases.yaml
│ │ └── webhook_in_rollouts.yaml
├── default
│ ├── kustomization.yaml
│ ├── manager_auth_proxy_patch.yaml
│ └── manager_config_patch.yaml
├── manager
│ ├── controller_manager_config.yaml
│ ├── kustomization.yaml
│ └── manager.yaml
├── prometheus
│ ├── kustomization.yaml
│ └── monitor.yaml
├── rbac
│ ├── auth_proxy_client_clusterrole.yaml
│ ├── auth_proxy_role.yaml
│ ├── auth_proxy_role_binding.yaml
│ ├── auth_proxy_service.yaml
│ ├── batchrelease_editor_role.yaml
│ ├── batchrelease_viewer_role.yaml
│ ├── kustomization.yaml
│ ├── leader_election_role.yaml
│ ├── leader_election_role_binding.yaml
│ ├── role.yaml
│ ├── role_binding.yaml
│ ├── rollout_editor_role.yaml
│ ├── rollout_viewer_role.yaml
│ └── service_account.yaml
├── samples
│ ├── rollouts_v1alpha1_batchrelease.yaml
│ └── rollouts_v1alpha1_rollout.yaml
└── webhook
│ ├── kustomization.yaml
│ ├── kustomizeconfig.yaml
│ ├── manifests.yaml
│ ├── patch_manifests.yaml
│ └── service.yaml
├── docs
├── contributing
│ └── debug.md
├── getting_started
│ ├── installation.md
│ └── introduction.md
├── images
│ ├── approve_rollout.png
│ ├── continuous_release.png
│ ├── deploy_rollout.png
│ ├── echoserver_application.png
│ ├── rollback_echoserver.png
│ ├── rollout-arch.png
│ ├── rollout_canary.png
│ ├── rollout_intro.png
│ ├── upgrade_echoserver.png
│ └── upgrade_failed.png
├── proposals
│ ├── 20220803-rollouthistory.md
│ └── 20231107-v1beta1-apis.md
└── tutorials
│ └── basic_usage.md
├── go.mod
├── go.sum
├── hack
└── boilerplate.go.txt
├── lua_configuration
├── convert_test_case_to_lua_object.go
├── networking.istio.io
│ ├── DestinationRule
│ │ ├── testdata
│ │ │ └── traffic_routing_with_a_match.yaml
│ │ └── trafficRouting.lua
│ └── VirtualService
│ │ ├── testdata
│ │ ├── rollout_with_three_steps.yaml
│ │ ├── traffic_routing_with_a_match.yaml
│ │ ├── traffic_routing_with_matches.yaml
│ │ └── traffic_routing_with_weight.yaml
│ │ └── trafficRouting.lua
└── trafficrouting_ingress
│ ├── aliyun-alb.lua
│ ├── higress.lua
│ ├── mse.lua
│ └── nginx.lua
├── main.go
├── pkg
├── controller
│ ├── batchrelease
│ │ ├── batchrelease_controller.go
│ │ ├── batchrelease_controller_test.go
│ │ ├── batchrelease_event_handler.go
│ │ ├── batchrelease_event_handler_test.go
│ │ ├── batchrelease_executor.go
│ │ ├── batchrelease_status.go
│ │ ├── context
│ │ │ ├── context.go
│ │ │ └── context_test.go
│ │ ├── control
│ │ │ ├── apis.go
│ │ │ ├── bluegreenstyle
│ │ │ │ ├── cloneset
│ │ │ │ │ ├── control.go
│ │ │ │ │ └── control_test.go
│ │ │ │ ├── control_plane.go
│ │ │ │ ├── deployment
│ │ │ │ │ ├── control.go
│ │ │ │ │ └── control_test.go
│ │ │ │ ├── hpa
│ │ │ │ │ ├── hpa.go
│ │ │ │ │ └── hpa_test.go
│ │ │ │ └── interface.go
│ │ │ ├── canarystyle
│ │ │ │ ├── control_plane.go
│ │ │ │ ├── deployment
│ │ │ │ │ ├── canary.go
│ │ │ │ │ ├── control.go
│ │ │ │ │ ├── control_test.go
│ │ │ │ │ └── stable.go
│ │ │ │ └── interface.go
│ │ │ ├── interface.go
│ │ │ ├── partitionstyle
│ │ │ │ ├── cloneset
│ │ │ │ │ ├── control.go
│ │ │ │ │ └── control_test.go
│ │ │ │ ├── control_plane.go
│ │ │ │ ├── daemonset
│ │ │ │ │ ├── control.go
│ │ │ │ │ └── control_test.go
│ │ │ │ ├── deployment
│ │ │ │ │ ├── control.go
│ │ │ │ │ └── control_test.go
│ │ │ │ ├── interface.go
│ │ │ │ └── statefulset
│ │ │ │ │ ├── control.go
│ │ │ │ │ └── control_test.go
│ │ │ ├── util.go
│ │ │ └── util_test.go
│ │ └── labelpatch
│ │ │ ├── filter.go
│ │ │ ├── filter_test.go
│ │ │ ├── patcher.go
│ │ │ └── patcher_test.go
│ ├── deployment
│ │ ├── controller.go
│ │ ├── deployment_controller.go
│ │ ├── deployment_controller_test.go
│ │ ├── deployment_event_handler.go
│ │ ├── progress.go
│ │ ├── rolling.go
│ │ ├── rolling_test.go
│ │ ├── sync.go
│ │ └── util
│ │ │ ├── deployment_util.go
│ │ │ └── deployment_util_test.go
│ ├── rollout
│ │ ├── rollout_bluegreen.go
│ │ ├── rollout_bluegreen_test.go
│ │ ├── rollout_canary.go
│ │ ├── rollout_canary_test.go
│ │ ├── rollout_controller.go
│ │ ├── rollout_controller_test.go
│ │ ├── rollout_event_handler.go
│ │ ├── rollout_event_handler_test.go
│ │ ├── rollout_progressing.go
│ │ ├── rollout_progressing_test.go
│ │ ├── rollout_releaseManager.go
│ │ ├── rollout_status.go
│ │ └── rollout_status_test.go
│ ├── rollouthistory
│ │ ├── rollouthistory_controller.go
│ │ ├── rollouthistory_controller_test.go
│ │ ├── rollouthistory_event_handler.go
│ │ └── util.go
│ └── trafficrouting
│ │ ├── trafficrouting_controller.go
│ │ └── trafficrouting_controller_test.go
├── feature
│ ├── rollout_features.go
│ └── switch.go
├── trafficrouting
│ ├── manager.go
│ ├── manager_test.go
│ └── network
│ │ ├── composite.go
│ │ ├── customNetworkProvider
│ │ ├── custom_network_provider.go
│ │ ├── custom_network_provider_test.go
│ │ └── lua_configuration
│ │ │ ├── networking.error.io
│ │ │ └── LuaError
│ │ │ │ └── trafficRouting.lua
│ │ │ └── networking.istio.io
│ │ │ ├── DestinationRule
│ │ │ └── trafficRouting.lua
│ │ │ └── VirtualService
│ │ │ └── trafficRouting.lua
│ │ ├── gateway
│ │ ├── gateway.go
│ │ └── gateway_test.go
│ │ ├── ingress
│ │ ├── ingress.go
│ │ └── ingress_test.go
│ │ └── interface.go
├── util
│ ├── client
│ │ ├── client.go
│ │ ├── delegating_client.go
│ │ └── no_deepcopy_lister.go
│ ├── condition.go
│ ├── configuration
│ │ └── configuration.go
│ ├── constant.go
│ ├── controller_finder.go
│ ├── controller_finder_test.go
│ ├── errors
│ │ └── types.go
│ ├── expectation
│ │ ├── resource_expectations.go
│ │ └── resource_expectations_test.go
│ ├── feature
│ │ └── feature_gate.go
│ ├── grace
│ │ ├── grace_expectations.go
│ │ ├── grace_expectations_test.go
│ │ ├── grace_wrapper.go
│ │ └── grace_wrapper_test.go
│ ├── labels
│ │ ├── labels.go
│ │ └── labels_test.go
│ ├── lua_configuration.go
│ ├── luamanager
│ │ ├── json.go
│ │ ├── lua.go
│ │ └── lua_test.go
│ ├── meta.go
│ ├── parse_utils.go
│ ├── parse_utils_test.go
│ ├── patch
│ │ ├── patch_utils.go
│ │ └── patch_utils_test.go
│ ├── pod_utils.go
│ ├── ratelimiter
│ │ └── rate_limiter.go
│ ├── rollout_utils.go
│ ├── slices_utils.go
│ ├── workloads_utils.go
│ └── workloads_utils_test.go
└── webhook
│ ├── add_rollout.go
│ ├── add_workload.go
│ ├── rollout
│ └── validating
│ │ ├── rollout_create_update_handler.go
│ │ ├── rollout_create_update_handler_test.go
│ │ ├── validate_v1alphal_rollout.go
│ │ └── webhooks.go
│ ├── server.go
│ ├── util
│ ├── configuration
│ │ └── configuration.go
│ ├── controller
│ │ └── webhook_controller.go
│ ├── crd
│ │ └── crd.go
│ ├── generator
│ │ ├── certgenerator.go
│ │ ├── fake
│ │ │ └── certgenerator.go
│ │ ├── selfsigned.go
│ │ └── util.go
│ ├── matcher.go
│ ├── util.go
│ └── writer
│ │ ├── atomic
│ │ └── atomic_writer.go
│ │ ├── certwriter.go
│ │ ├── error.go
│ │ ├── fs.go
│ │ └── secret.go
│ └── workload
│ └── mutating
│ ├── unified_update_handler.go
│ ├── unified_update_handler_test.go
│ ├── webhooks.go
│ ├── workload_update_handler.go
│ └── workload_update_handler_test.go
├── rollouts.rollouts.kruise.io-rollouts-demo.yaml
├── scripts
└── deploy_kind.sh
└── test
├── e2e
├── deployment_test.go
├── rollout_test.go
├── rollout_v1beta1_test.go
├── suite_test.go
└── test_data
│ ├── batchrelease
│ ├── cloneset.yaml
│ ├── cloneset_number_100.yaml
│ ├── cloneset_percentage_100.yaml
│ ├── cloneset_percentage_50.yaml
│ ├── deployment.yaml
│ ├── deployment_number_100.yaml
│ ├── deployment_percentage_100.yaml
│ ├── deployment_percentage_50.yaml
│ └── sts.yaml
│ ├── crds
│ └── httproutes.yaml
│ ├── customNetworkProvider
│ ├── destinationrule.yaml
│ ├── istio_crd.yaml
│ ├── lua_script_configmap.yaml
│ ├── rollout_with_multi_trafficrouting.yaml
│ ├── rollout_with_trafficrouting.yaml
│ ├── rollout_without_trafficrouting.yaml
│ ├── trafficrouting.yaml
│ └── virtualservice_without_destinationrule.yaml
│ ├── deployment
│ └── deployment.yaml
│ ├── gateway
│ ├── httproute-test.yaml
│ └── rollout-test.yaml
│ └── rollout
│ ├── advanced_statefulset.yaml
│ ├── cloneset.yaml
│ ├── daemonset.yaml
│ ├── deployment.yaml
│ ├── deployment_disabled.yaml
│ ├── headless_service.yaml
│ ├── hpa_v1.yaml
│ ├── hpa_v2.yaml
│ ├── native_statefulset.yaml
│ ├── nginx_ingress.yaml
│ ├── rollout-configuration.yaml
│ ├── rollout_canary_base.yaml
│ ├── rollout_canary_daemonset_base.yaml
│ ├── rollout_canary_daemonset_interrupt.yaml
│ ├── rollout_disabled.yaml
│ ├── rollout_v1beta1_bluegreen_base.yaml
│ ├── rollout_v1beta1_bluegreen_cloneset_base.yaml
│ ├── rollout_v1beta1_canary_base.yaml
│ ├── rollout_v1beta1_partition_base.yaml
│ └── service.yaml
├── images
└── image_utils.go
└── kind-conf.yaml
/.dockerignore:
--------------------------------------------------------------------------------
1 | # More info: https://docs.docker.com/engine/reference/builder/#dockerignore-file
2 | # Ignore all files which are not go type
3 | !**/*.go
4 | !**/*.mod
5 | !**/*.sum
6 |
--------------------------------------------------------------------------------
/.github/PULL_REQUEST_TEMPLATE.md:
--------------------------------------------------------------------------------
1 |
4 |
5 | ### Ⅰ. Describe what this PR does
6 |
7 |
8 | ### Ⅱ. Does this pull request fix one issue?
9 |
10 |
11 | ### Ⅲ. Special notes for reviews
12 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yaml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - release-*
8 | pull_request: {}
9 | workflow_dispatch: {}
10 |
11 | env:
12 | # Common versions
13 | GO_VERSION: '1.19'
14 | GOLANGCI_VERSION: 'v1.52'
15 |
16 | jobs:
17 |
18 | golangci-lint:
19 | runs-on: ubuntu-22.04
20 | steps:
21 | - name: Checkout
22 | uses: actions/checkout@v2
23 | with:
24 | submodules: true
25 | - name: Setup Go
26 | uses: actions/setup-go@v2
27 | with:
28 | go-version: ${{ env.GO_VERSION }}
29 | - name: Cache Go Dependencies
30 | uses: actions/cache@v4
31 | with:
32 | path: ~/go/pkg/mod
33 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
34 | restore-keys: ${{ runner.os }}-go-
35 | - name: Code generate
36 | run: |
37 | make generate
38 | - name: Lint golang code
39 | uses: golangci/golangci-lint-action@v6
40 | with:
41 | version: ${{ env.GOLANGCI_VERSION }}
42 | args: --verbose
43 |
44 | unit-tests:
45 | runs-on: ubuntu-22.04
46 | steps:
47 | - uses: actions/checkout@v2
48 | with:
49 | submodules: true
50 | - name: Fetch History
51 | run: git fetch --prune --unshallow
52 | - name: Setup Go
53 | uses: actions/setup-go@v2
54 | with:
55 | go-version: ${{ env.GO_VERSION }}
56 | - name: Cache Go Dependencies
57 | uses: actions/cache@v4
58 | with:
59 | path: ~/go/pkg/mod
60 | key: ${{ runner.os }}-go-${{ hashFiles('**/go.sum') }}
61 | restore-keys: ${{ runner.os }}-go-
62 | - name: Run Unit Tests
63 | run: |
64 | make test
65 | git status
66 | - name: Publish Unit Test Coverage
67 | uses: codecov/codecov-action@v1
68 | with:
69 | flags: unittests
70 | file: cover.out
71 | - name: Check diff
72 | run: '[[ -z $(git status -s) ]] || (printf "Existing modified/untracked files.\nPlease run \"make generate manifests\" and push again.\n"; exit 1)'
73 |
--------------------------------------------------------------------------------
/.github/workflows/docker-image.yaml:
--------------------------------------------------------------------------------
1 | name: Docker Image CI
2 |
3 | on:
4 | workflow_dispatch:
5 |
6 | # Declare default permissions as read only.
7 | permissions: read-all
8 |
9 | jobs:
10 |
11 | build:
12 |
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2
17 | - name: Set up Docker Buildx
18 | uses: docker/setup-buildx-action@v3
19 | - name: Login to Docker Hub
20 | uses: docker/login-action@v3
21 | with:
22 | username: ${{ vars.DOCKERHUB_USERNAME }}
23 | password: ${{ secrets.HUB_KRUISE }}
24 | - name: Build the Docker image
25 | run: |
26 | docker buildx create --use --platform=linux/amd64,linux/arm64,linux/ppc64le --name multi-platform-builder
27 | docker buildx ls
28 | IMG=openkruise/kruise-rollout:${{ github.ref_name }} make docker-multiarch
29 |
--------------------------------------------------------------------------------
/.github/workflows/e2e-custom.yaml:
--------------------------------------------------------------------------------
1 | name: E2E-Custom
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - release-*
8 | pull_request: {}
9 | workflow_dispatch: {}
10 |
11 | env:
12 | # Common versions
13 | GO_VERSION: '1.19'
14 | KIND_VERSION: 'v0.18.0'
15 | KIND_IMAGE: 'kindest/node:v1.26.3'
16 | KIND_CLUSTER_NAME: 'ci-testing'
17 |
18 | jobs:
19 |
20 | rollout:
21 | runs-on: ubuntu-22.04
22 | steps:
23 | - uses: actions/checkout@v2
24 | with:
25 | submodules: true
26 | - name: Setup Go
27 | uses: actions/setup-go@v2
28 | with:
29 | go-version: ${{ env.GO_VERSION }}
30 | - name: Setup Kind Cluster
31 | uses: helm/kind-action@v1.2.0
32 | with:
33 | version: ${{ env.KIND_VERSION }}
34 | node_image: ${{ env.KIND_IMAGE }}
35 | cluster_name: ${{ env.KIND_CLUSTER_NAME }}
36 | config: ./test/kind-conf.yaml
37 | - name: Build image
38 | run: |
39 | export IMAGE="openkruise/kruise-rollout:e2e-${GITHUB_RUN_ID}"
40 | docker build --pull --no-cache . -t $IMAGE
41 | kind load docker-image --name=${KIND_CLUSTER_NAME} $IMAGE || { echo >&2 "kind not installed or error loading image: $IMAGE"; exit 1; }
42 | - name: Install Kruise Rollout
43 | run: |
44 | set -ex
45 | kubectl cluster-info
46 | IMG=openkruise/kruise-rollout:e2e-${GITHUB_RUN_ID} ./scripts/deploy_kind.sh
47 | for ((i=1;i<10;i++));
48 | do
49 | set +e
50 | PODS=$(kubectl get pod -n kruise-rollout | grep '1/1' | wc -l)
51 | set -e
52 | if [ "$PODS" -eq "1" ]; then
53 | break
54 | fi
55 | sleep 3
56 | done
57 | set +e
58 | PODS=$(kubectl get pod -n kruise-rollout | grep '1/1' | wc -l)
59 | kubectl get node -o yaml
60 | kubectl get all -n kruise-rollout -o yaml
61 | set -e
62 | if [ "$PODS" -eq "1" ]; then
63 | echo "Wait for kruise-rollout ready successfully"
64 | else
65 | echo "Timeout to wait for kruise-rollout ready"
66 | exit 1
67 | fi
68 | - name: Run E2E Tests
69 | run: |
70 | export KUBECONFIG=/home/runner/.kube/config
71 | kubectl apply -f ./test/e2e/test_data/customNetworkProvider/istio_crd.yaml
72 | kubectl apply -f ./test/e2e/test_data/customNetworkProvider/lua_script_configmap.yaml
73 | make ginkgo
74 | set +e
75 | ./bin/ginkgo -timeout 60m -v --focus='Canary rollout with custom network provider' test/e2e
76 | retVal=$?
77 | # kubectl get pod -n kruise-rollout --no-headers | grep manager | awk '{print $1}' | xargs kubectl logs -n kruise-rollout
78 | restartCount=$(kubectl get pod -n kruise-rollout --no-headers | awk '{print $4}')
79 | if [ "${restartCount}" -eq "0" ];then
80 | echo "Kruise-rollout has not restarted"
81 | else
82 | kubectl get pod -n kruise-rollout --no-headers
83 | echo "Kruise-rollout has restarted, abort!!!"
84 | kubectl get pod -n kruise-rollout --no-headers| awk '{print $1}' | xargs kubectl logs -p -n kruise-rollout
85 | exit 1
86 | fi
87 | exit $retVal
88 |
--------------------------------------------------------------------------------
/.github/workflows/e2e-gateway.yaml:
--------------------------------------------------------------------------------
1 | name: E2E-Gateway
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - release-*
8 | pull_request: {}
9 | workflow_dispatch: {}
10 |
11 | env:
12 | # Common versions
13 | GO_VERSION: '1.19'
14 | KIND_IMAGE: 'kindest/node:v1.23.3'
15 | KIND_CLUSTER_NAME: 'ci-testing'
16 |
17 | jobs:
18 |
19 | rollout:
20 | runs-on: ubuntu-22.04
21 | steps:
22 | - uses: actions/checkout@v2
23 | with:
24 | submodules: true
25 | - name: Setup Go
26 | uses: actions/setup-go@v2
27 | with:
28 | go-version: ${{ env.GO_VERSION }}
29 | - name: Setup Kind Cluster
30 | uses: helm/kind-action@v1.2.0
31 | with:
32 | node_image: ${{ env.KIND_IMAGE }}
33 | cluster_name: ${{ env.KIND_CLUSTER_NAME }}
34 | config: ./test/kind-conf.yaml
35 | - name: Build image
36 | run: |
37 | export IMAGE="openkruise/kruise-rollout:e2e-${GITHUB_RUN_ID}"
38 | docker build --pull --no-cache . -t $IMAGE
39 | kind load docker-image --name=${KIND_CLUSTER_NAME} $IMAGE || { echo >&2 "kind not installed or error loading image: $IMAGE"; exit 1; }
40 | - name: Install Kruise Rollout
41 | run: |
42 | set -ex
43 | kubectl cluster-info
44 | IMG=openkruise/kruise-rollout:e2e-${GITHUB_RUN_ID} ./scripts/deploy_kind.sh
45 | for ((i=1;i<10;i++));
46 | do
47 | set +e
48 | PODS=$(kubectl get pod -n kruise-rollout | grep '1/1' | wc -l)
49 | set -e
50 | if [ "$PODS" -eq "1" ]; then
51 | break
52 | fi
53 | sleep 3
54 | done
55 | set +e
56 | PODS=$(kubectl get pod -n kruise-rollout | grep '1/1' | wc -l)
57 | kubectl get node -o yaml
58 | kubectl get all -n kruise-rollout -o yaml
59 | set -e
60 | if [ "$PODS" -eq "1" ]; then
61 | echo "Wait for kruise-rollout ready successfully"
62 | else
63 | echo "Timeout to wait for kruise-rollout ready"
64 | exit 1
65 | fi
66 | - name: Run E2E Tests
67 | run: |
68 | export KUBECONFIG=/home/runner/.kube/config
69 | make ginkgo
70 | set +e
71 | ./bin/ginkgo -timeout 60m -v --focus='Canary rollout with Gateway API' test/e2e
72 | retVal=$?
73 | # kubectl get pod -n kruise-rollout --no-headers | grep manager | awk '{print $1}' | xargs kubectl logs -n kruise-rollout
74 | restartCount=$(kubectl get pod -n kruise-rollout --no-headers | awk '{print $4}')
75 | if [ "${restartCount}" -eq "0" ];then
76 | echo "Kruise-rollout has not restarted"
77 | else
78 | kubectl get pod -n kruise-rollout --no-headers
79 | echo "Kruise-rollout has restarted, abort!!!"
80 | kubectl get pod -n kruise-rollout --no-headers| awk '{print $1}' | xargs kubectl logs -p -n kruise-rollout
81 | exit 1
82 | fi
83 | exit $retVal
84 |
--------------------------------------------------------------------------------
/.github/workflows/e2e-multi-network-provider.yaml:
--------------------------------------------------------------------------------
1 | name: E2E-Multiple-NetworkProvider
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - release-*
8 | pull_request: {}
9 | workflow_dispatch: {}
10 |
11 | env:
12 | # Common versions
13 | GO_VERSION: '1.19'
14 | KIND_VERSION: 'v0.18.0'
15 | KIND_IMAGE: 'kindest/node:v1.26.3'
16 | KIND_CLUSTER_NAME: 'ci-testing'
17 |
18 | jobs:
19 |
20 | rollout:
21 | runs-on: ubuntu-22.04
22 | steps:
23 | - uses: actions/checkout@v2
24 | with:
25 | submodules: true
26 | - name: Setup Go
27 | uses: actions/setup-go@v2
28 | with:
29 | go-version: ${{ env.GO_VERSION }}
30 | - name: Setup Kind Cluster
31 | uses: helm/kind-action@v1.2.0
32 | with:
33 | version: ${{ env.KIND_VERSION }}
34 | node_image: ${{ env.KIND_IMAGE }}
35 | cluster_name: ${{ env.KIND_CLUSTER_NAME }}
36 | config: ./test/kind-conf.yaml
37 | - name: Build image
38 | run: |
39 | export IMAGE="openkruise/kruise-rollout:e2e-${GITHUB_RUN_ID}"
40 | docker build --pull --no-cache . -t $IMAGE
41 | kind load docker-image --name=${KIND_CLUSTER_NAME} $IMAGE || { echo >&2 "kind not installed or error loading image: $IMAGE"; exit 1; }
42 | - name: Install Kruise Rollout
43 | run: |
44 | set -ex
45 | kubectl cluster-info
46 | IMG=openkruise/kruise-rollout:e2e-${GITHUB_RUN_ID} ./scripts/deploy_kind.sh
47 | for ((i=1;i<10;i++));
48 | do
49 | set +e
50 | PODS=$(kubectl get pod -n kruise-rollout | grep '1/1' | wc -l)
51 | set -e
52 | if [ "$PODS" -eq "1" ]; then
53 | break
54 | fi
55 | sleep 3
56 | done
57 | set +e
58 | PODS=$(kubectl get pod -n kruise-rollout | grep '1/1' | wc -l)
59 | kubectl get node -o yaml
60 | kubectl get all -n kruise-rollout -o yaml
61 | set -e
62 | if [ "$PODS" -eq "1" ]; then
63 | echo "Wait for kruise-rollout ready successfully"
64 | else
65 | echo "Timeout to wait for kruise-rollout ready"
66 | exit 1
67 | fi
68 | - name: Run E2E Tests
69 | run: |
70 | export KUBECONFIG=/home/runner/.kube/config
71 | kubectl apply -f ./test/e2e/test_data/customNetworkProvider/istio_crd.yaml
72 | kubectl apply -f ./test/e2e/test_data/customNetworkProvider/lua_script_configmap.yaml
73 | make ginkgo
74 | set +e
75 | ./bin/ginkgo -timeout 60m -v --focus='Canary rollout with multiple network providers' test/e2e
76 | retVal=$?
77 | # kubectl get pod -n kruise-rollout --no-headers | grep manager | awk '{print $1}' | xargs kubectl logs -n kruise-rollout
78 | restartCount=$(kubectl get pod -n kruise-rollout --no-headers | awk '{print $4}')
79 | if [ "${restartCount}" -eq "0" ];then
80 | echo "Kruise-rollout has not restarted"
81 | else
82 | kubectl get pod -n kruise-rollout --no-headers
83 | echo "Kruise-rollout has restarted, abort!!!"
84 | kubectl get pod -n kruise-rollout --no-headers| awk '{print $1}' | xargs kubectl logs -p -n kruise-rollout
85 | exit 1
86 | fi
87 | exit $retVal
88 |
--------------------------------------------------------------------------------
/.github/workflows/e2e-others-1.19.yaml:
--------------------------------------------------------------------------------
1 | name: E2E-Others-1.19
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - release-*
8 | pull_request: {}
9 | workflow_dispatch: {}
10 |
11 | env:
12 | # Common versions
13 | GO_VERSION: '1.19'
14 | KIND_IMAGE: 'kindest/node:v1.19.16'
15 | KIND_CLUSTER_NAME: 'ci-testing'
16 |
17 | jobs:
18 |
19 | rollout:
20 | runs-on: ubuntu-22.04
21 | steps:
22 | - uses: actions/checkout@v2
23 | with:
24 | submodules: true
25 | - name: Setup Go
26 | uses: actions/setup-go@v2
27 | with:
28 | go-version: ${{ env.GO_VERSION }}
29 | - name: Setup Kind Cluster
30 | uses: helm/kind-action@v1.2.0
31 | with:
32 | node_image: ${{ env.KIND_IMAGE }}
33 | cluster_name: ${{ env.KIND_CLUSTER_NAME }}
34 | config: ./test/kind-conf.yaml
35 | - name: Build image
36 | run: |
37 | export IMAGE="openkruise/kruise-rollout:e2e-${GITHUB_RUN_ID}"
38 | docker build --pull --no-cache . -t $IMAGE
39 | kind load docker-image --name=${KIND_CLUSTER_NAME} $IMAGE || { echo >&2 "kind not installed or error loading image: $IMAGE"; exit 1; }
40 | - name: Install Kruise
41 | run: |
42 | set -ex
43 | kubectl cluster-info
44 | make helm
45 | helm repo add openkruise https://openkruise.github.io/charts/
46 | helm repo update
47 | helm install kruise openkruise/kruise --version 1.7.0
48 | for ((i=1;i<10;i++));
49 | do
50 | set +e
51 | PODS=$(kubectl get pod -n kruise-system | grep '1/1' | grep kruise-controller-manager | wc -l)
52 | set -e
53 | if [ "$PODS" -eq "2" ]; then
54 | break
55 | fi
56 | sleep 3
57 | done
58 | set +e
59 | PODS=$(kubectl get pod -n kruise-system | grep '1/1' | grep kruise-controller-manager | wc -l)
60 | set -e
61 | if [ "$PODS" -eq "2" ]; then
62 | echo "Wait for kruise-manager ready successfully"
63 | else
64 | echo "Timeout to wait for kruise-manager ready"
65 | exit 1
66 | fi
67 | - name: Install Kruise Rollout
68 | run: |
69 | set -ex
70 | kubectl cluster-info
71 | IMG=openkruise/kruise-rollout:e2e-${GITHUB_RUN_ID} ./scripts/deploy_kind.sh
72 | for ((i=1;i<10;i++));
73 | do
74 | set +e
75 | PODS=$(kubectl get pod -n kruise-rollout | grep '1/1' | wc -l)
76 | set -e
77 | if [ "$PODS" -eq "1" ]; then
78 | break
79 | fi
80 | sleep 3
81 | done
82 | set +e
83 | PODS=$(kubectl get pod -n kruise-rollout | grep '1/1' | wc -l)
84 | kubectl get node -o yaml
85 | kubectl get all -n kruise-rollout -o yaml
86 | set -e
87 | if [ "$PODS" -eq "1" ]; then
88 | echo "Wait for kruise-rollout ready successfully"
89 | else
90 | echo "Timeout to wait for kruise-rollout ready"
91 | exit 1
92 | fi
93 | - name: Run E2E Tests
94 | run: |
95 | export KUBECONFIG=/home/runner/.kube/config
96 | make ginkgo
97 | set +e
98 | ./bin/ginkgo -timeout 60m -v --focus='Others' test/e2e
99 | retVal=$?
100 | # kubectl get pod -n kruise-rollout --no-headers | grep manager | awk '{print $1}' | xargs kubectl logs -n kruise-rollout
101 | restartCount=$(kubectl get pod -n kruise-rollout --no-headers | awk '{print $4}')
102 | if [ "${restartCount}" -eq "0" ];then
103 | echo "Kruise-rollout has not restarted"
104 | else
105 | kubectl get pod -n kruise-rollout --no-headers
106 | echo "Kruise-rollout has restarted, abort!!!"
107 | kubectl get pod -n kruise-rollout --no-headers| awk '{print $1}' | xargs kubectl logs -p -n kruise-rollout
108 | exit 1
109 | fi
110 | exit $retVal
111 |
--------------------------------------------------------------------------------
/.github/workflows/e2e-others-1.23.yaml:
--------------------------------------------------------------------------------
1 | name: E2E-Others-1.23
2 |
3 | on:
4 | push:
5 | branches:
6 | - master
7 | - release-*
8 | pull_request: {}
9 | workflow_dispatch: {}
10 |
11 | env:
12 | # Common versions
13 | GO_VERSION: '1.19'
14 | KIND_IMAGE: 'kindest/node:v1.23.3'
15 | KIND_CLUSTER_NAME: 'ci-testing'
16 |
17 | jobs:
18 |
19 | rollout:
20 | runs-on: ubuntu-22.04
21 | steps:
22 | - uses: actions/checkout@v2
23 | with:
24 | submodules: true
25 | - name: Setup Go
26 | uses: actions/setup-go@v2
27 | with:
28 | go-version: ${{ env.GO_VERSION }}
29 | - name: Setup Kind Cluster
30 | uses: helm/kind-action@v1.2.0
31 | with:
32 | node_image: ${{ env.KIND_IMAGE }}
33 | cluster_name: ${{ env.KIND_CLUSTER_NAME }}
34 | config: ./test/kind-conf.yaml
35 | - name: Build image
36 | run: |
37 | export IMAGE="openkruise/kruise-rollout:e2e-${GITHUB_RUN_ID}"
38 | docker build --pull --no-cache . -t $IMAGE
39 | kind load docker-image --name=${KIND_CLUSTER_NAME} $IMAGE || { echo >&2 "kind not installed or error loading image: $IMAGE"; exit 1; }
40 | - name: Install Kruise
41 | run: |
42 | set -ex
43 | kubectl cluster-info
44 | make helm
45 | helm repo add openkruise https://openkruise.github.io/charts/
46 | helm repo update
47 | helm install kruise openkruise/kruise --version 1.7.0
48 | for ((i=1;i<10;i++));
49 | do
50 | set +e
51 | PODS=$(kubectl get pod -n kruise-system | grep '1/1' | grep kruise-controller-manager | wc -l)
52 | set -e
53 | if [ "$PODS" -eq "2" ]; then
54 | break
55 | fi
56 | sleep 3
57 | done
58 | set +e
59 | PODS=$(kubectl get pod -n kruise-system | grep '1/1' | grep kruise-controller-manager | wc -l)
60 | set -e
61 | if [ "$PODS" -eq "2" ]; then
62 | echo "Wait for kruise-manager ready successfully"
63 | else
64 | echo "Timeout to wait for kruise-manager ready"
65 | exit 1
66 | fi
67 | - name: Install Kruise Rollout
68 | run: |
69 | set -ex
70 | kubectl cluster-info
71 | IMG=openkruise/kruise-rollout:e2e-${GITHUB_RUN_ID} ./scripts/deploy_kind.sh
72 | for ((i=1;i<10;i++));
73 | do
74 | set +e
75 | PODS=$(kubectl get pod -n kruise-rollout | grep '1/1' | wc -l)
76 | set -e
77 | if [ "$PODS" -eq "1" ]; then
78 | break
79 | fi
80 | sleep 3
81 | done
82 | set +e
83 | PODS=$(kubectl get pod -n kruise-rollout | grep '1/1' | wc -l)
84 | kubectl get node -o yaml
85 | kubectl get all -n kruise-rollout -o yaml
86 | set -e
87 | if [ "$PODS" -eq "1" ]; then
88 | echo "Wait for kruise-rollout ready successfully"
89 | else
90 | echo "Timeout to wait for kruise-rollout ready"
91 | exit 1
92 | fi
93 | - name: Run E2E Tests
94 | run: |
95 | export KUBECONFIG=/home/runner/.kube/config
96 | make ginkgo
97 | set +e
98 | ./bin/ginkgo -timeout 60m -v --focus='Others' test/e2e
99 | retVal=$?
100 | # kubectl get pod -n kruise-rollout --no-headers | grep manager | awk '{print $1}' | xargs kubectl logs -n kruise-rollout
101 | restartCount=$(kubectl get pod -n kruise-rollout --no-headers | awk '{print $4}')
102 | if [ "${restartCount}" -eq "0" ];then
103 | echo "Kruise-rollout has not restarted"
104 | else
105 | kubectl get pod -n kruise-rollout --no-headers
106 | echo "Kruise-rollout has restarted, abort!!!"
107 | kubectl get pod -n kruise-rollout --no-headers| awk '{print $1}' | xargs kubectl logs -p -n kruise-rollout
108 | exit 1
109 | fi
110 | exit $retVal
111 |
--------------------------------------------------------------------------------
/.github/workflows/license.yml:
--------------------------------------------------------------------------------
1 | name: License
2 | on:
3 | push:
4 | branches:
5 | - master
6 | - release-*
7 | workflow_dispatch: {}
8 | pull_request:
9 | branches:
10 | - master
11 | - release-*
12 |
13 | jobs:
14 | license_check:
15 | runs-on: ubuntu-latest
16 | name: Check for unapproved licenses
17 | steps:
18 | - uses: actions/checkout@v2
19 | - name: Set up Ruby
20 | uses: ruby/setup-ruby@v1
21 | with:
22 | ruby-version: 2.6
23 | - name: Install dependencies
24 | run: gem install license_finder
25 | - name: Run tests
26 | run: license_finder --decisions_file .license/dependency_decisions.yml
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 |
2 | # Binaries for programs and plugins
3 | *.exe
4 | *.exe~
5 | *.dll
6 | *.so
7 | *.dylib
8 | bin/
9 | testbin/
10 | vendor/
11 | .temp
12 |
13 | # Test binary, build with `go test -c`
14 | *.test
15 | test/e2e/generated/bindata.go
16 |
17 | # Output of the go coverage tool, specifically when used with LiteIDE
18 | *.out
19 |
20 | # Kubernetes Generated files - skip generated files, except for vendored files
21 |
22 | !vendor/**/zz_generated.*
23 |
24 | # editor and IDE paraphernalia
25 | .idea
26 | *.swp
27 | *.swo
28 | *~
29 | .vscode
30 |
31 | .DS_Store
32 |
33 | lua_configuration/networking.istio.io/**/testdata/*.lua
34 |
--------------------------------------------------------------------------------
/.golangci.yml:
--------------------------------------------------------------------------------
1 | # options for analysis running
2 | run:
3 | # default concurrency is a available CPU number
4 | concurrency: 4
5 |
6 | # timeout for analysis, e.g. 30s, 5m, default is 1m
7 | deadline: 5m
8 |
9 | # exit code when at least one issue was found, default is 1
10 | issues-exit-code: 1
11 |
12 | # include test files or not, default is true
13 | tests: true
14 |
15 | # list of build tags, all linters use it. Default is empty list.
16 | #build-tags:
17 | # - mytag
18 |
19 | # which dirs to skip: they won't be analyzed;
20 | # can use regexp here: generated.*, regexp is applied on full path;
21 | # default value is empty list, but next dirs are always skipped independently
22 | # from this option's value:
23 | # third_party$, testdata$, examples$, Godeps$, builtin$
24 | skip-dirs:
25 | - vendor
26 |
27 | # which files to skip: they will be analyzed, but issues from them
28 | # won't be reported. Default value is empty list, but there is
29 | # no need to include all autogenerated files, we confidently recognize
30 | # autogenerated files. If it's not please let us know.
31 | skip-files:
32 | # - ".*\\.my\\.go$"
33 | # - lib/bad.go
34 |
35 | # output configuration options
36 | output:
37 | # colored-line-number|line-number|json|tab|checkstyle, default is "colored-line-number"
38 | format: colored-line-number
39 |
40 | # print lines of code with issue, default is true
41 | print-issued-lines: true
42 |
43 | # print linter name in the end of issue text, default is true
44 | print-linter-name: true
45 |
46 |
47 | # all available settings of specific linters
48 | linters-settings:
49 | golint:
50 | # minimal confidence for issues, default is 0.8
51 | min-confidence: 0.8
52 | gofmt:
53 | # simplify code: gofmt with `-s` option, true by default
54 | simplify: true
55 | goimports:
56 | # put imports beginning with prefix after 3rd-party packages;
57 | # it's a comma-separated list of prefixes
58 | #local-prefixes: github.com/openkruise/rollouts
59 | misspell:
60 | # Correct spellings using locale preferences for US or UK.
61 | # Default is to use a neutral variety of English.
62 | # Setting locale to US will correct the British spelling of 'colour' to 'color'.
63 | locale: default
64 | #ignore-words:
65 | # - someword
66 |
67 | linters:
68 | fast: false
69 | disable-all: true
70 | enable:
71 | # TODO Enforce the below linters later
72 | - gofmt
73 | - goimports
74 | - ineffassign
75 | - misspell
76 | issues:
77 | exclude:
78 | # staticcheck
79 | - 'SA1019: package github.com/golang/protobuf/proto is deprecated: Use the "google.golang.org/protobuf/proto" package instead'
80 |
--------------------------------------------------------------------------------
/.license/README.md:
--------------------------------------------------------------------------------
1 | # License Checker
2 |
3 | Our license checker CI rely on [LicenseFinder](https://github.com/pivotal/LicenseFinder).
4 |
5 | ## How to add a new license
6 |
7 | LicenseFinder is a ruby project, so make sure you have ruby installed.
8 |
9 | ### Install the tool
10 |
11 | ```shell
12 | gem install license_finder
13 | ```
14 |
15 | ### Add a license
16 |
17 | ```shell
18 | license_finder permitted_licenses add MIT --decisions_file .license/dependency_decisions.yml
19 | ```
20 |
--------------------------------------------------------------------------------
/.license/dependency_decisions.yml:
--------------------------------------------------------------------------------
1 | ---
2 | - - :permit
3 | - MIT
4 | - :who:
5 | :why:
6 | :versions: []
7 | :when: 2021-03-12 07:35:34.645031000 Z
8 | - - :permit
9 | - Apache 2.0
10 | - :who:
11 | :why:
12 | :versions: []
13 | :when: 2021-03-12 07:19:18.243194000 Z
14 | - - :permit
15 | - New BSD
16 | - :who:
17 | :why:
18 | :versions: []
19 | :when: 2021-03-12 07:19:28.540675000 Z
20 | - - :permit
21 | - Simplified BSD
22 | - :who:
23 | :why:
24 | :versions: []
25 | :when: 2021-03-12 07:20:01.774212000 Z
26 | - - :permit
27 | - Mozilla Public License 2.0
28 | - :who:
29 | :why:
30 | :versions: []
31 | :when: 2021-03-12 07:21:05.194536000 Z
32 | - - :permit
33 | - unknown
34 | - :who:
35 | :why:
36 | :versions: []
37 | :when: 2021-03-12 07:21:43.379269000 Z
38 | - - :permit
39 | - ISC
40 | - :who:
41 | :why:
42 | :versions: []
43 | :when: 2021-03-12 07:22:07.265966000 Z
44 |
--------------------------------------------------------------------------------
/.lift.toml:
--------------------------------------------------------------------------------
1 | # Ignore results from vendor directories
2 | ignoreFiles = """
3 | vendor/
4 | lua_configuration/
5 | """
6 |
--------------------------------------------------------------------------------
/Dockerfile:
--------------------------------------------------------------------------------
1 | # Build the manager binary
2 | FROM golang:1.19-alpine3.17 AS builder
3 |
4 | WORKDIR /workspace
5 |
6 | # Copy the Go Modules manifests
7 | COPY go.mod go.mod
8 | COPY go.sum go.sum
9 |
10 | # Copy the go source
11 | COPY main.go main.go
12 | COPY api/ api/
13 | COPY pkg/ pkg/
14 |
15 | # Build
16 | RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -a -o manager main.go
17 |
18 | # Use distroless as minimal base image to package the manager binary
19 | # Refer to https://github.com/GoogleContainerTools/distroless for more details
20 | FROM gcr.io/distroless/static:nonroot
21 | WORKDIR /
22 | COPY --from=builder /workspace/manager .
23 | COPY lua_configuration /lua_configuration
24 | USER 65532:65532
25 |
26 | ENTRYPOINT ["/manager"]
27 |
--------------------------------------------------------------------------------
/Dockerfile_multiarch:
--------------------------------------------------------------------------------
1 | # Build the manager binary
2 | ARG BASE_IMAGE=alpine
3 | ARG BASE_IMAGE_VERION=3.17
4 | FROM --platform=$BUILDPLATFORM golang:1.19-alpine3.17 as builder
5 |
6 | WORKDIR /workspace
7 |
8 | # Copy the Go Modules manifests
9 | COPY go.mod go.mod
10 | COPY go.sum go.sum
11 |
12 | # Copy the go source
13 | COPY main.go main.go
14 | COPY api/ api/
15 | COPY pkg/ pkg/
16 |
17 | # Build
18 | ARG TARGETOS
19 | ARG TARGETARCH
20 | RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} CGO_ENABLED=0 GO111MODULE=on go build -a -o manager main.go
21 |
22 | ARG BASE_IMAGE
23 | ARG BASE_IMAGE_VERION
24 | FROM ${BASE_IMAGE}:${BASE_IMAGE_VERION}
25 |
26 | RUN set -eux; \
27 | apk --no-cache --update upgrade && \
28 | apk --no-cache add ca-certificates && \
29 | apk --no-cache add tzdata && \
30 | rm -rf /var/cache/apk/* && \
31 | update-ca-certificates && \
32 | echo "only include root and nobody user" && \
33 | echo -e "root:x:0:0:root:/root:/bin/ash\nnobody:x:65534:65534:nobody:/:/sbin/nologin" | tee /etc/passwd && \
34 | echo -e "root:x:0:root\nnobody:x:65534:" | tee /etc/group && \
35 | rm -rf /usr/local/sbin/* && \
36 | rm -rf /usr/local/bin/* && \
37 | rm -rf /usr/sbin/* && \
38 | rm -rf /usr/bin/* && \
39 | rm -rf /sbin/* && \
40 | rm -rf /bin/*
41 |
42 | WORKDIR /
43 | COPY --from=builder /workspace/manager .
44 | COPY lua_configuration /lua_configuration
45 | USER 65534
46 |
47 | ENTRYPOINT ["/manager"]
48 |
--------------------------------------------------------------------------------
/OWNERS:
--------------------------------------------------------------------------------
1 | # See: https://go.k8s.io/owners
2 | approvers:
3 | - FillZpp
4 | - furykerry
5 | - zmberg
6 | reviewers:
7 | - FillZpp
8 | - furykerry
9 | - zmberg
10 | - veophi
11 |
--------------------------------------------------------------------------------
/PROJECT:
--------------------------------------------------------------------------------
1 | domain: kruise.io
2 | layout:
3 | - go.kubebuilder.io/v3
4 | projectName: rollouts
5 | repo: github.com/openkruise/rollouts
6 | resources:
7 | - api:
8 | crdVersion: v1
9 | namespaced: true
10 | domain: kruise.io
11 | group: rollouts
12 | kind: Rollout
13 | path: github.com/openkruise/rollouts/api/v1alpha1
14 | version: v1alpha1
15 | - api:
16 | crdVersion: v1
17 | namespaced: true
18 | controller: true
19 | domain: kruise.io
20 | group: rollouts
21 | kind: BatchRelease
22 | path: github.com/openkruise/rollouts/api/v1alpha1
23 | version: v1alpha1
24 | version: "3"
25 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Rollouts
2 | [](https://www.apache.org/licenses/LICENSE-2.0.html)
3 |
4 | ## Introduction
5 | Kruise Rollouts is a **Bypass** component that offers **Advanced Progressive Delivery Features**. Its support for canary, multi-batch, and A/B testing delivery modes can be helpful in achieving smooth and controlled rollouts of changes to your application, while its compatibility with Gateway API and various Ingress implementations makes it easier to integrate with your existing infrastructure. Overall, Kruise Rollouts is a valuable tool for Kubernetes users looking to optimize their deployment processes!
6 |
7 |

8 |
9 | ## Why Kruise Rollouts?
10 | - **Functionality**:
11 | - Supports canary and multi-batch delivery for various workloads, such as Deployment, CloneSet, and StatefulSet.
12 | - Supports Fine-grained traffic orchestration of application with Kubernetes Ingress and [Gateway API](https://gateway-api.sigs.k8s.io/).
13 |
14 | - **Flexibility**:
15 | - Handles both incremental and existing workloads with ease.
16 | - Be compatible with workload-referencing components like HPA, allowing for easy deployment and management of workloads.
17 | - Supports plug-and-play and hot-swapping, with immediate effect upon application, and the flexibility to be easily deleted at any stage, including during the rollout process.
18 |
19 | - **Extensibility**:
20 | - Extend to other workloads and traffic types easily with pluggable lua scripts.
21 |
22 | ## Quick Start
23 | - See [Getting Started](https://openkruise.io/rollouts/introduction/) documents in OpenKruise official website.
24 |
25 | ## Contributing
26 | You are warmly welcome to hack on Kruise Rollout. We have prepared a detailed guide [CONTRIBUTING.md](CONTRIBUTING.md).
27 |
28 | ## Community
29 | Active communication channels:
30 |
31 | - Slack: [OpenKruise channel](https://kubernetes.slack.com/channels/openkruise) (*English*)
32 | - DingTalk:Search GroupID `23330762` (*Chinese*)
33 | - WeChat: Search User `openkruise` and let the robot invite you (*Chinese*)
34 | - Bi-weekly Community Meeting (APAC, *Chinese*):
35 | - Thursday 19:00 GMT+8 (Asia/Shanghai), [Calendar](https://calendar.google.com/calendar/u/2?cid=MjdtbDZucXA2bjVpNTFyYTNpazV2dW8ybHNAZ3JvdXAuY2FsZW5kYXIuZ29vZ2xlLmNvbQ)
36 | - [Meeting Link(zoom)](https://us02web.zoom.us/j/87059136652?pwd=NlI4UThFWXVRZkxIU0dtR1NINncrQT09)
37 | - [Notes and agenda](https://shimo.im/docs/gXqmeQOYBehZ4vqo)
38 | - Bi-weekly Community Meeting (*English*): TODO
39 |
40 | ## Acknowledge
41 | - The global idea is from both OpenKruise and KubeVela communities, and the basic code of rollout is inherited from the KubeVela Rollout.
42 | - This project is maintained by both contributors from [OpenKruise](https://openkruise.io/) and [KubeVela](https://kubevela.io).
43 |
44 | ## License
45 | Kruise Rollout is licensed under the Apache License, Version 2.0. See [LICENSE](./LICENSE.md) for the full license text.
46 |
47 |
--------------------------------------------------------------------------------
/api/addtoscheme_apps_v1alpha1.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package apis
18 |
19 | import (
20 | "github.com/openkruise/rollouts/api/v1alpha1"
21 | )
22 |
23 | func init() {
24 | // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
25 | AddToSchemes = append(AddToSchemes, v1alpha1.SchemeBuilder.AddToScheme)
26 | }
27 |
--------------------------------------------------------------------------------
/api/addtoscheme_apps_v1beta1.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package apis
18 |
19 | import (
20 | "github.com/openkruise/rollouts/api/v1beta1"
21 | )
22 |
23 | func init() {
24 | // Register the types with the Scheme so the components can map objects to GroupVersionKinds and back
25 | AddToSchemes = append(AddToSchemes, v1beta1.SchemeBuilder.AddToScheme)
26 | }
27 |
--------------------------------------------------------------------------------
/api/apis.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package apis
18 |
19 | import (
20 | "k8s.io/apimachinery/pkg/runtime"
21 | )
22 |
23 | // AddToSchemes may be used to add all resources defined in the project to a Scheme
24 | var AddToSchemes runtime.SchemeBuilder
25 |
26 | // AddToScheme adds all Resources to the Scheme
27 | func AddToScheme(s *runtime.Scheme) error {
28 | return AddToSchemes.AddToScheme(s)
29 | }
30 |
--------------------------------------------------------------------------------
/api/v1alpha1/batchrelease_types.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package v1alpha1
18 |
19 | import (
20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21 | )
22 |
23 | // +genclient
24 | // +k8s:openapi-gen=true
25 | // +kubebuilder:object:root=true
26 | // +kubebuilder:subresource:status
27 | // +kubebuilder:printcolumn:name="KIND",type=string,JSONPath=`.spec.targetReference.workloadRef.kind`
28 | // +kubebuilder:printcolumn:name="PHASE",type=string,JSONPath=`.status.phase`
29 | // +kubebuilder:printcolumn:name="BATCH",type=integer,JSONPath=`.status.canaryStatus.currentBatch`
30 | // +kubebuilder:printcolumn:name="BATCH-STATE",type=string,JSONPath=`.status.canaryStatus.batchState`
31 | // +kubebuilder:printcolumn:name="AGE",type=date,JSONPath=".metadata.creationTimestamp"
32 |
33 | type BatchRelease struct {
34 | metav1.TypeMeta `json:",inline"`
35 | metav1.ObjectMeta `json:"metadata,omitempty"`
36 |
37 | Spec BatchReleaseSpec `json:"spec,omitempty"`
38 | Status BatchReleaseStatus `json:"status,omitempty"`
39 | }
40 |
41 | // BatchReleaseSpec defines how to describe an update between different compRevision
42 | type BatchReleaseSpec struct {
43 | // TargetRef contains the GVK and name of the workload that we need to upgrade to.
44 | TargetRef ObjectRef `json:"targetReference"`
45 | // ReleasePlan is the details on how to rollout the resources
46 | ReleasePlan ReleasePlan `json:"releasePlan"`
47 | }
48 |
49 | // BatchReleaseList contains a list of BatchRelease
50 | // +kubebuilder:object:root=true
51 | type BatchReleaseList struct {
52 | metav1.TypeMeta `json:",inline"`
53 | metav1.ListMeta `json:"metadata,omitempty"`
54 | Items []BatchRelease `json:"items"`
55 | }
56 |
57 | func init() {
58 | SchemeBuilder.Register(&BatchRelease{}, &BatchReleaseList{})
59 | }
60 |
--------------------------------------------------------------------------------
/api/v1alpha1/groupversion_info.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package v1alpha1 contains API Schema definitions for the apps v1alpha1 API group
18 | // +kubebuilder:object:generate=true
19 | // +groupName=rollouts.kruise.io
20 | package v1alpha1
21 |
22 | import (
23 | "k8s.io/apimachinery/pkg/runtime/schema"
24 | "sigs.k8s.io/controller-runtime/pkg/scheme"
25 | )
26 |
27 | var (
28 | // GroupVersion is group version used to register these objects
29 | GroupVersion = schema.GroupVersion{Group: "rollouts.kruise.io", Version: "v1alpha1"}
30 |
31 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme
32 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
33 |
34 | // AddToScheme adds the types in this group-version to the given scheme.
35 | AddToScheme = SchemeBuilder.AddToScheme
36 |
37 | SchemeGroupVersion = GroupVersion
38 | )
39 |
40 | // Resource is required by pkg/client/listers/...
41 | func Resource(resource string) schema.GroupResource {
42 | return SchemeGroupVersion.WithResource(resource).GroupResource()
43 | }
44 |
--------------------------------------------------------------------------------
/api/v1beta1/batchrelease_types.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package v1beta1
18 |
19 | import (
20 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
21 | )
22 |
23 | // +genclient
24 | // +k8s:openapi-gen=true
25 | // +kubebuilder:object:root=true
26 | // +kubebuilder:subresource:status
27 | // +kubebuilder:storageversion
28 | // +kubebuilder:printcolumn:name="KIND",type=string,JSONPath=`.spec.targetReference.workloadRef.kind`
29 | // +kubebuilder:printcolumn:name="PHASE",type=string,JSONPath=`.status.phase`
30 | // +kubebuilder:printcolumn:name="BATCH",type=integer,JSONPath=`.status.canaryStatus.currentBatch`
31 | // +kubebuilder:printcolumn:name="BATCH-STATE",type=string,JSONPath=`.status.canaryStatus.batchState`
32 | // +kubebuilder:printcolumn:name="AGE",type=date,JSONPath=".metadata.creationTimestamp"
33 |
34 | type BatchRelease struct {
35 | metav1.TypeMeta `json:",inline"`
36 | metav1.ObjectMeta `json:"metadata,omitempty"`
37 |
38 | Spec BatchReleaseSpec `json:"spec,omitempty"`
39 | Status BatchReleaseStatus `json:"status,omitempty"`
40 | }
41 |
42 | // BatchReleaseSpec defines how to describe an update between different compRevision
43 | type BatchReleaseSpec struct {
44 | // WorkloadRef contains enough information to let you identify a workload for Rollout
45 | // Batch release of the bypass
46 | WorkloadRef ObjectRef `json:"workloadRef,omitempty"`
47 | // ReleasePlan is the details on how to rollout the resources
48 | ReleasePlan ReleasePlan `json:"releasePlan"`
49 | }
50 |
51 | // BatchReleaseList contains a list of BatchRelease
52 | // +kubebuilder:object:root=true
53 | type BatchReleaseList struct {
54 | metav1.TypeMeta `json:",inline"`
55 | metav1.ListMeta `json:"metadata,omitempty"`
56 | Items []BatchRelease `json:"items"`
57 | }
58 |
59 | func init() {
60 | SchemeBuilder.Register(&BatchRelease{}, &BatchReleaseList{})
61 | }
62 |
--------------------------------------------------------------------------------
/api/v1beta1/convertion.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package v1beta1
18 |
19 | func (*Rollout) Hub() {}
20 |
21 | func (*BatchRelease) Hub() {}
22 |
--------------------------------------------------------------------------------
/api/v1beta1/groupversion_info.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | // Package v1beta1 contains API Schema definitions for the apps v1beta1 API group
18 | // +kubebuilder:object:generate=true
19 | // +groupName=rollouts.kruise.io
20 | package v1beta1
21 |
22 | import (
23 | "k8s.io/apimachinery/pkg/runtime/schema"
24 | "sigs.k8s.io/controller-runtime/pkg/scheme"
25 | )
26 |
27 | var (
28 | // GroupVersion is group version used to register these objects
29 | GroupVersion = schema.GroupVersion{Group: "rollouts.kruise.io", Version: "v1beta1"}
30 |
31 | // SchemeBuilder is used to add go types to the GroupVersionKind scheme
32 | SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion}
33 |
34 | // AddToScheme adds the types in this group-version to the given scheme.
35 | AddToScheme = SchemeBuilder.AddToScheme
36 |
37 | SchemeGroupVersion = GroupVersion
38 | )
39 |
40 | // Resource is required by pkg/client/listers/...
41 | func Resource(resource string) schema.GroupResource {
42 | return SchemeGroupVersion.WithResource(resource).GroupResource()
43 | }
44 |
--------------------------------------------------------------------------------
/api/v1beta1/trafficrouting.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package v1beta1
18 |
19 | // TrafficRoutingRef hosts all the different configuration for supported service meshes to enable more fine-grained traffic routing
20 | type TrafficRoutingRef struct {
21 | // Service holds the name of a service which selects pods with stable version and don't select any pods with canary version.
22 | Service string `json:"service"`
23 | // Optional duration in seconds the traffic provider(e.g. nginx ingress controller) consumes the service, ingress configuration changes gracefully.
24 | // +kubebuilder:default=3
25 | GracePeriodSeconds int32 `json:"gracePeriodSeconds,omitempty"`
26 | // Ingress holds Ingress specific configuration to route traffic, e.g. Nginx, Alb.
27 | Ingress *IngressTrafficRouting `json:"ingress,omitempty"`
28 | // Gateway holds Gateway specific configuration to route traffic
29 | // Gateway configuration only supports >= v0.4.0 (v1alpha2).
30 | Gateway *GatewayTrafficRouting `json:"gateway,omitempty"`
31 | // CustomNetworkRefs hold a list of custom providers to route traffic
32 | CustomNetworkRefs []ObjectRef `json:"customNetworkRefs,omitempty"`
33 | }
34 |
35 | // IngressTrafficRouting configuration for ingress controller to control traffic routing
36 | type IngressTrafficRouting struct {
37 | // ClassType refers to the type of `Ingress`.
38 | // current support nginx, aliyun-alb. default is nginx.
39 | // +optional
40 | ClassType string `json:"classType,omitempty"`
41 | // Name refers to the name of an `Ingress` resource in the same namespace as the `Rollout`
42 | Name string `json:"name"`
43 | }
44 |
45 | // GatewayTrafficRouting configuration for gateway api
46 | type GatewayTrafficRouting struct {
47 | // HTTPRouteName refers to the name of an `HTTPRoute` resource in the same namespace as the `Rollout`
48 | HTTPRouteName *string `json:"httpRouteName,omitempty"`
49 | // TCPRouteName *string `json:"tcpRouteName,omitempty"`
50 | // UDPRouteName *string `json:"udpRouteName,omitempty"`
51 | }
52 |
--------------------------------------------------------------------------------
/config/crd/kustomization.yaml:
--------------------------------------------------------------------------------
1 | # This kustomization.yaml is not intended to be run by itself,
2 | # since it depends on service name and namespace that are out of this kustomize package.
3 | # It should be run by config/default
4 | resources:
5 | - bases/rollouts.kruise.io_rollouts.yaml
6 | - bases/rollouts.kruise.io_batchreleases.yaml
7 | - bases/rollouts.kruise.io_rollouthistories.yaml
8 | - bases/rollouts.kruise.io_trafficroutings.yaml
9 | #+kubebuilder:scaffold:crdkustomizeresource
10 |
11 | patchesStrategicMerge:
12 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix.
13 | # patches here are for enabling the conversion webhook for each CRD
14 | - patches/webhook_in_rollouts.yaml
15 | - patches/webhook_in_batchreleases.yaml
16 | #+kubebuilder:scaffold:crdkustomizewebhookpatch
17 |
18 | # [CERTMANAGER] To enable webhook, uncomment all the sections with [CERTMANAGER] prefix.
19 | # patches here are for enabling the CA injection for each CRD
20 | #- patches/cainjection_in_rollouts.yaml
21 | #- patches/cainjection_in_batchreleases.yaml
22 | #+kubebuilder:scaffold:crdkustomizecainjectionpatch
23 |
24 | # the following config is for teaching kustomize how to do kustomization for CRDs.
25 | configurations:
26 | - kustomizeconfig.yaml
27 |
--------------------------------------------------------------------------------
/config/crd/kustomizeconfig.yaml:
--------------------------------------------------------------------------------
1 | # This file is for teaching kustomize how to substitute name and namespace reference in CRD
2 | nameReference:
3 | - kind: Service
4 | version: v1
5 | fieldSpecs:
6 | - kind: CustomResourceDefinition
7 | version: v1
8 | group: apiextensions.k8s.io
9 | path: spec/conversion/webhook/clientConfig/service/name
10 |
11 | namespace:
12 | - kind: CustomResourceDefinition
13 | version: v1
14 | group: apiextensions.k8s.io
15 | path: spec/conversion/webhook/clientConfig/service/namespace
16 | create: false
17 |
18 | varReference:
19 | - path: metadata/annotations
20 |
--------------------------------------------------------------------------------
/config/crd/patches/cainjection_in_batchreleases.yaml:
--------------------------------------------------------------------------------
1 | # The following patch adds a directive for certmanager to inject CA into the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
7 | name: batchreleases.rollouts.kruise.io
8 |
--------------------------------------------------------------------------------
/config/crd/patches/cainjection_in_rollouts.yaml:
--------------------------------------------------------------------------------
1 | # The following patch adds a directive for certmanager to inject CA into the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | annotations:
6 | cert-manager.io/inject-ca-from: $(CERTIFICATE_NAMESPACE)/$(CERTIFICATE_NAME)
7 | name: rollouts.rollouts.kruise.io
8 |
--------------------------------------------------------------------------------
/config/crd/patches/webhook_in_batchreleases.yaml:
--------------------------------------------------------------------------------
1 | # The following patch enables a conversion webhook for the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | name: batchreleases.rollouts.kruise.io
6 | spec:
7 | conversion:
8 | strategy: Webhook
9 | webhook:
10 | clientConfig:
11 | service:
12 | namespace: system
13 | name: webhook-service
14 | path: /convert
15 | conversionReviewVersions:
16 | - v1beta1
17 |
--------------------------------------------------------------------------------
/config/crd/patches/webhook_in_rollouts.yaml:
--------------------------------------------------------------------------------
1 | # The following patch enables a conversion webhook for the CRD
2 | apiVersion: apiextensions.k8s.io/v1
3 | kind: CustomResourceDefinition
4 | metadata:
5 | name: rollouts.rollouts.kruise.io
6 | spec:
7 | conversion:
8 | strategy: Webhook
9 | webhook:
10 | clientConfig:
11 | service:
12 | namespace: system
13 | name: webhook-service
14 | path: /convert
15 | conversionReviewVersions:
16 | - v1beta1
17 |
--------------------------------------------------------------------------------
/config/default/kustomization.yaml:
--------------------------------------------------------------------------------
1 | # Adds namespace to all resources.
2 | namespace: kruise-rollout
3 |
4 | # Value of this field is prepended to the
5 | # names of all resources, e.g. a deployment named
6 | # "wordpress" becomes "alices-wordpress".
7 | # Note that it should also match with the prefix (text before '-') of the namespace
8 | # field above.
9 | namePrefix: kruise-rollout-
10 |
11 | # Labels to add to all resources and selectors.
12 | #commonLabels:
13 | # someName: someValue
14 |
15 | bases:
16 | - ../crd
17 | - ../manager
18 | - ../rbac
19 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
20 | # crd/kustomization.yaml
21 | - ../webhook
22 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'. 'WEBHOOK' components are required.
23 | #- ../certmanager
24 | # [PROMETHEUS] To enable prometheus monitor, uncomment all sections with 'PROMETHEUS'.
25 | #- ../prometheus
26 |
27 | patchesStrategicMerge:
28 | # Protect the /metrics endpoint by putting it behind auth.
29 | # If you want your controller-manager to expose the /metrics
30 | # endpoint w/o any authn/z, please comment the following line.
31 | - manager_auth_proxy_patch.yaml
32 |
33 | # Mount the controller config file for loading manager configurations
34 | # through a ComponentConfig type
35 | #- manager_config_patch.yaml
36 |
37 | # [WEBHOOK] To enable webhook, uncomment all the sections with [WEBHOOK] prefix including the one in
38 | # crd/kustomization.yaml
39 | #- manager_webhook_patch.yaml
40 |
41 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER'.
42 | # Uncomment 'CERTMANAGER' sections in crd/kustomization.yaml to enable the CA injection in the admission webhooks.
43 | # 'CERTMANAGER' needs to be enabled to use ca injection
44 | #- webhookcainjection_patch.yaml
45 |
46 | # the following config is for teaching kustomize how to do var substitution
47 | vars:
48 | # [CERTMANAGER] To enable cert-manager, uncomment all sections with 'CERTMANAGER' prefix.
49 | #- name: CERTIFICATE_NAMESPACE # namespace of the certificate CR
50 | # objref:
51 | # kind: Certificate
52 | # group: cert-manager.io
53 | # version: v1
54 | # name: serving-cert # this name should match the one in certificate.yaml
55 | # fieldref:
56 | # fieldpath: metadata.namespace
57 | #- name: CERTIFICATE_NAME
58 | # objref:
59 | # kind: Certificate
60 | # group: cert-manager.io
61 | # version: v1
62 | # name: serving-cert # this name should match the one in certificate.yaml
63 | #- name: SERVICE_NAMESPACE # namespace of the service
64 | # objref:
65 | # kind: Service
66 | # version: v1
67 | # name: webhook-service
68 | # fieldref:
69 | # fieldpath: metadata.namespace
70 | #- name: SERVICE_NAME
71 | # objref:
72 | # kind: Service
73 | # version: v1
74 | # name: webhook-service
75 |
--------------------------------------------------------------------------------
/config/default/manager_auth_proxy_patch.yaml:
--------------------------------------------------------------------------------
1 | # This patch inject a sidecar container which is a HTTP proxy for the
2 | # controller manager, it performs RBAC authorization against the Kubernetes API using SubjectAccessReviews.
3 | apiVersion: apps/v1
4 | kind: Deployment
5 | metadata:
6 | name: controller-manager
7 | namespace: system
8 | spec:
9 | template:
10 | spec:
11 | containers:
12 | - name: manager
13 | args:
14 | - "--health-probe-bind-address=:8081"
15 | - "--metrics-bind-address=127.0.0.1:8080"
16 | - "--leader-elect"
17 | - "--feature-gates=AdvancedDeployment=true"
18 | - "--v=5"
19 | env:
20 | - name: KUBE_CACHE_MUTATION_DETECTOR
21 | value: "true"
22 |
--------------------------------------------------------------------------------
/config/default/manager_config_patch.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: controller-manager
5 | namespace: system
6 | spec:
7 | template:
8 | spec:
9 | containers:
10 | - name: manager
11 | imagePullPolicy: Always
12 | args:
13 | - "--config=controller_manager_config.yaml"
14 | volumeMounts:
15 | - name: manager-config
16 | mountPath: /controller_manager_config.yaml
17 | subPath: controller_manager_config.yaml
18 | volumes:
19 | - name: manager-config
20 | configMap:
21 | name: manager-config
22 |
--------------------------------------------------------------------------------
/config/manager/controller_manager_config.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: controller-runtime.sigs.k8s.io/v1alpha1
2 | kind: ControllerManagerConfig
3 | health:
4 | healthProbeBindAddress: :8081
5 | metrics:
6 | bindAddress: 127.0.0.1:8080
7 | webhook:
8 | port: 9443
9 | leaderElection:
10 | leaderElect: true
11 | resourceName: 71ddec2c.kruise.io
12 |
--------------------------------------------------------------------------------
/config/manager/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - manager.yaml
3 |
--------------------------------------------------------------------------------
/config/manager/manager.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Namespace
3 | metadata:
4 | labels:
5 | control-plane: controller-manager
6 | name: system
7 | ---
8 | apiVersion: apps/v1
9 | kind: Deployment
10 | metadata:
11 | name: controller-manager
12 | namespace: system
13 | labels:
14 | control-plane: controller-manager
15 | spec:
16 | selector:
17 | matchLabels:
18 | control-plane: controller-manager
19 | replicas: 1
20 | template:
21 | metadata:
22 | labels:
23 | control-plane: controller-manager
24 | spec:
25 | securityContext:
26 | runAsNonRoot: true
27 | containers:
28 | - command:
29 | - /manager
30 | args:
31 | - --leader-elect
32 | - --feature-gates=AdvancedDeployment=true
33 | image: controller:latest
34 | name: manager
35 | securityContext:
36 | allowPrivilegeEscalation: false
37 | livenessProbe:
38 | httpGet:
39 | path: /healthz
40 | port: 8081
41 | initialDelaySeconds: 15
42 | periodSeconds: 20
43 | readinessProbe:
44 | httpGet:
45 | path: /readyz
46 | port: 8081
47 | initialDelaySeconds: 5
48 | periodSeconds: 10
49 | resources:
50 | limits:
51 | cpu: 100m
52 | memory: 100Mi
53 | requests:
54 | cpu: 100m
55 | memory: 100Mi
56 | serviceAccountName: controller-manager
57 | terminationGracePeriodSeconds: 10
58 |
--------------------------------------------------------------------------------
/config/prometheus/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - monitor.yaml
3 |
--------------------------------------------------------------------------------
/config/prometheus/monitor.yaml:
--------------------------------------------------------------------------------
1 |
2 | # Prometheus Monitor Service (Metrics)
3 | apiVersion: monitoring.coreos.com/v1
4 | kind: ServiceMonitor
5 | metadata:
6 | labels:
7 | control-plane: controller-manager
8 | name: controller-manager-metrics-monitor
9 | namespace: system
10 | spec:
11 | endpoints:
12 | - path: /metrics
13 | port: https
14 | scheme: https
15 | bearerTokenFile: /var/run/secrets/kubernetes.io/serviceaccount/token
16 | tlsConfig:
17 | insecureSkipVerify: true
18 | selector:
19 | matchLabels:
20 | control-plane: controller-manager
21 |
--------------------------------------------------------------------------------
/config/rbac/auth_proxy_client_clusterrole.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: metrics-reader
5 | rules:
6 | - nonResourceURLs:
7 | - "/metrics"
8 | verbs:
9 | - get
10 |
--------------------------------------------------------------------------------
/config/rbac/auth_proxy_role.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRole
3 | metadata:
4 | name: proxy-role
5 | rules:
6 | - apiGroups:
7 | - authentication.k8s.io
8 | resources:
9 | - tokenreviews
10 | verbs:
11 | - create
12 | - apiGroups:
13 | - authorization.k8s.io
14 | resources:
15 | - subjectaccessreviews
16 | verbs:
17 | - create
18 |
--------------------------------------------------------------------------------
/config/rbac/auth_proxy_role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | name: proxy-rolebinding
5 | roleRef:
6 | apiGroup: rbac.authorization.k8s.io
7 | kind: ClusterRole
8 | name: proxy-role
9 | subjects:
10 | - kind: ServiceAccount
11 | name: controller-manager
12 | namespace: system
13 |
--------------------------------------------------------------------------------
/config/rbac/auth_proxy_service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | labels:
5 | control-plane: controller-manager
6 | name: controller-manager-metrics-service
7 | namespace: system
8 | spec:
9 | ports:
10 | - name: https
11 | port: 8443
12 | targetPort: https
13 | selector:
14 | control-plane: controller-manager
15 |
--------------------------------------------------------------------------------
/config/rbac/batchrelease_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit batchreleases.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: batchrelease-editor-role
6 | rules:
7 | - apiGroups:
8 | - rollouts.kruise.io
9 | resources:
10 | - batchreleases
11 | verbs:
12 | - create
13 | - delete
14 | - get
15 | - list
16 | - patch
17 | - update
18 | - watch
19 | - apiGroups:
20 | - rollouts.kruise.io
21 | resources:
22 | - batchreleases/status
23 | verbs:
24 | - get
25 |
--------------------------------------------------------------------------------
/config/rbac/batchrelease_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view batchreleases.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: batchrelease-viewer-role
6 | rules:
7 | - apiGroups:
8 | - rollouts.kruise.io
9 | resources:
10 | - batchreleases
11 | verbs:
12 | - get
13 | - list
14 | - watch
15 | - apiGroups:
16 | - rollouts.kruise.io
17 | resources:
18 | - batchreleases/status
19 | verbs:
20 | - get
21 |
--------------------------------------------------------------------------------
/config/rbac/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | # All RBAC will be applied under this service account in
3 | # the deployment namespace. You may comment out this resource
4 | # if your manager will use a service account that exists at
5 | # runtime. Be sure to update RoleBinding and ClusterRoleBinding
6 | # subjects if changing service account names.
7 | - service_account.yaml
8 | - role.yaml
9 | - role_binding.yaml
10 | - leader_election_role.yaml
11 | - leader_election_role_binding.yaml
12 | # Comment the following 4 lines if you want to disable
13 | # the auth proxy (https://github.com/brancz/kube-rbac-proxy)
14 | # which protects your /metrics endpoint.
15 | #- auth_proxy_service.yaml
16 | #- auth_proxy_role.yaml
17 | #- auth_proxy_role_binding.yaml
18 | #- auth_proxy_client_clusterrole.yaml
19 |
--------------------------------------------------------------------------------
/config/rbac/leader_election_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions to do leader election.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: Role
4 | metadata:
5 | name: leader-election-role
6 | rules:
7 | - apiGroups:
8 | - ""
9 | resources:
10 | - configmaps
11 | verbs:
12 | - get
13 | - list
14 | - watch
15 | - create
16 | - update
17 | - patch
18 | - delete
19 | - apiGroups:
20 | - coordination.k8s.io
21 | resources:
22 | - leases
23 | verbs:
24 | - get
25 | - list
26 | - watch
27 | - create
28 | - update
29 | - patch
30 | - delete
31 | - apiGroups:
32 | - ""
33 | resources:
34 | - events
35 | verbs:
36 | - create
37 | - patch
38 |
--------------------------------------------------------------------------------
/config/rbac/leader_election_role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: RoleBinding
3 | metadata:
4 | name: leader-election-rolebinding
5 | roleRef:
6 | apiGroup: rbac.authorization.k8s.io
7 | kind: Role
8 | name: leader-election-role
9 | subjects:
10 | - kind: ServiceAccount
11 | name: controller-manager
12 | namespace: system
13 |
--------------------------------------------------------------------------------
/config/rbac/role_binding.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rbac.authorization.k8s.io/v1
2 | kind: ClusterRoleBinding
3 | metadata:
4 | name: manager-rolebinding
5 | roleRef:
6 | apiGroup: rbac.authorization.k8s.io
7 | kind: ClusterRole
8 | name: manager-role
9 | subjects:
10 | - kind: ServiceAccount
11 | name: controller-manager
12 | namespace: system
13 | ---
14 | apiVersion: rbac.authorization.k8s.io/v1
15 | kind: RoleBinding
16 | metadata:
17 | name: manager-rolebinding
18 | namespace: system
19 | roleRef:
20 | apiGroup: rbac.authorization.k8s.io
21 | kind: Role
22 | name: manager-role
23 | subjects:
24 | - kind: ServiceAccount
25 | name: controller-manager
26 | namespace: system
27 |
--------------------------------------------------------------------------------
/config/rbac/rollout_editor_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to edit rollouts.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: rollout-editor-role
6 | rules:
7 | - apiGroups:
8 | - rollouts.kruise.io
9 | resources:
10 | - rollouts
11 | verbs:
12 | - create
13 | - delete
14 | - get
15 | - list
16 | - patch
17 | - update
18 | - watch
19 | - apiGroups:
20 | - rollouts.kruise.io
21 | resources:
22 | - rollouts/status
23 | verbs:
24 | - get
25 |
--------------------------------------------------------------------------------
/config/rbac/rollout_viewer_role.yaml:
--------------------------------------------------------------------------------
1 | # permissions for end users to view rollouts.
2 | apiVersion: rbac.authorization.k8s.io/v1
3 | kind: ClusterRole
4 | metadata:
5 | name: rollout-viewer-role
6 | rules:
7 | - apiGroups:
8 | - rollouts.kruise.io
9 | resources:
10 | - rollouts
11 | verbs:
12 | - get
13 | - list
14 | - watch
15 | - apiGroups:
16 | - rollouts.kruise.io
17 | resources:
18 | - rollouts/status
19 | verbs:
20 | - get
21 |
--------------------------------------------------------------------------------
/config/rbac/service_account.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ServiceAccount
3 | metadata:
4 | name: controller-manager
5 | namespace: system
6 |
--------------------------------------------------------------------------------
/config/samples/rollouts_v1alpha1_batchrelease.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: BatchRelease
3 | metadata:
4 | name: batchrelease-sample
5 | spec:
6 | # Add fields here
7 | foo: bar
8 |
--------------------------------------------------------------------------------
/config/samples/rollouts_v1alpha1_rollout.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: Rollout
3 | metadata:
4 | name: rollout-sample
5 | spec:
6 | # Add fields here
7 | foo: bar
8 |
--------------------------------------------------------------------------------
/config/webhook/kustomization.yaml:
--------------------------------------------------------------------------------
1 | resources:
2 | - manifests.yaml
3 | - service.yaml
4 |
5 | patchesStrategicMerge:
6 | - patch_manifests.yaml
7 |
8 | configurations:
9 | - kustomizeconfig.yaml
10 |
--------------------------------------------------------------------------------
/config/webhook/kustomizeconfig.yaml:
--------------------------------------------------------------------------------
1 | # the following config is for teaching kustomize where to look at when substituting vars.
2 | # It requires kustomize v2.1.0 or newer to work properly.
3 | nameReference:
4 | - kind: Service
5 | version: v1
6 | fieldSpecs:
7 | - kind: MutatingWebhookConfiguration
8 | group: admissionregistration.k8s.io
9 | path: webhooks/clientConfig/service/name
10 | - kind: ValidatingWebhookConfiguration
11 | group: admissionregistration.k8s.io
12 | path: webhooks/clientConfig/service/name
13 |
14 | namespace:
15 | - kind: MutatingWebhookConfiguration
16 | group: admissionregistration.k8s.io
17 | path: webhooks/clientConfig/service/namespace
18 | create: true
19 | - kind: ValidatingWebhookConfiguration
20 | group: admissionregistration.k8s.io
21 | path: webhooks/clientConfig/service/namespace
22 | create: true
23 |
24 | varReference:
25 | - path: metadata/annotations
26 |
--------------------------------------------------------------------------------
/config/webhook/manifests.yaml:
--------------------------------------------------------------------------------
1 | ---
2 | apiVersion: admissionregistration.k8s.io/v1
3 | kind: MutatingWebhookConfiguration
4 | metadata:
5 | creationTimestamp: null
6 | name: mutating-webhook-configuration
7 | webhooks:
8 | - admissionReviewVersions:
9 | - v1
10 | - v1beta1
11 | clientConfig:
12 | service:
13 | name: webhook-service
14 | namespace: system
15 | path: /mutate-apps-kruise-io-v1alpha1-cloneset
16 | failurePolicy: Fail
17 | name: mcloneset.kb.io
18 | rules:
19 | - apiGroups:
20 | - apps.kruise.io
21 | apiVersions:
22 | - v1alpha1
23 | operations:
24 | - UPDATE
25 | resources:
26 | - clonesets
27 | sideEffects: None
28 | - admissionReviewVersions:
29 | - v1
30 | - v1beta1
31 | clientConfig:
32 | service:
33 | name: webhook-service
34 | namespace: system
35 | path: /mutate-apps-kruise-io-v1alpha1-daemonset
36 | failurePolicy: Fail
37 | name: mdaemonset.kb.io
38 | rules:
39 | - apiGroups:
40 | - apps.kruise.io
41 | apiVersions:
42 | - v1alpha1
43 | operations:
44 | - UPDATE
45 | resources:
46 | - daemonsets
47 | sideEffects: None
48 | - admissionReviewVersions:
49 | - v1
50 | - v1beta1
51 | clientConfig:
52 | service:
53 | name: webhook-service
54 | namespace: system
55 | path: /mutate-apps-v1-deployment
56 | failurePolicy: Fail
57 | name: mdeployment.kb.io
58 | rules:
59 | - apiGroups:
60 | - apps
61 | apiVersions:
62 | - v1
63 | operations:
64 | - UPDATE
65 | resources:
66 | - deployments
67 | sideEffects: None
68 | - admissionReviewVersions:
69 | - v1
70 | - v1beta1
71 | clientConfig:
72 | service:
73 | name: webhook-service
74 | namespace: system
75 | path: /mutate-unified-workload
76 | failurePolicy: Fail
77 | name: munifiedworload.kb.io
78 | rules:
79 | - apiGroups:
80 | - '*'
81 | apiVersions:
82 | - '*'
83 | operations:
84 | - CREATE
85 | - UPDATE
86 | resources:
87 | - '*'
88 | sideEffects: None
89 | ---
90 | apiVersion: admissionregistration.k8s.io/v1
91 | kind: ValidatingWebhookConfiguration
92 | metadata:
93 | creationTimestamp: null
94 | name: validating-webhook-configuration
95 | webhooks:
96 | - admissionReviewVersions:
97 | - v1
98 | - v1beta1
99 | clientConfig:
100 | service:
101 | name: webhook-service
102 | namespace: system
103 | path: /validate-rollouts-kruise-io-rollout
104 | failurePolicy: Fail
105 | name: vrollout.kb.io
106 | rules:
107 | - apiGroups:
108 | - rollouts.kruise.io
109 | apiVersions:
110 | - v1alpha1
111 | - v1beta1
112 | operations:
113 | - CREATE
114 | - UPDATE
115 | resources:
116 | - rollouts
117 | sideEffects: None
118 |
--------------------------------------------------------------------------------
/config/webhook/patch_manifests.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: admissionregistration.k8s.io/v1
2 | kind: MutatingWebhookConfiguration
3 | metadata:
4 | name: mutating-webhook-configuration
5 | webhooks:
6 | - name: munifiedworload.kb.io
7 | objectSelector:
8 | matchExpressions:
9 | - key: rollouts.kruise.io/workload-type
10 | operator: Exists
11 | - name: mcloneset.kb.io
12 | objectSelector:
13 | matchExpressions:
14 | - key: rollouts.kruise.io/workload-type
15 | operator: Exists
16 | - name: mdaemonset.kb.io
17 | objectSelector:
18 | matchExpressions:
19 | - key: rollouts.kruise.io/workload-type
20 | operator: Exists
21 | # - name: mstatefulset.kb.io
22 | # objectSelector:
23 | # matchExpressions:
24 | # - key: rollouts.kruise.io/workload-type
25 | # operator: Exists
26 | # - name: madvancedstatefulset.kb.io
27 | # objectSelector:
28 | # matchExpressions:
29 | # - key: rollouts.kruise.io/workload-type
30 | # operator: Exists
31 | - name: mdeployment.kb.io
32 | objectSelector:
33 | matchExpressions:
34 | - key: control-plane
35 | operator: NotIn
36 | values:
37 | - controller-manager
38 | - key: rollouts.kruise.io/workload-type
39 | operator: Exists
40 |
--------------------------------------------------------------------------------
/config/webhook/service.yaml:
--------------------------------------------------------------------------------
1 |
2 | apiVersion: v1
3 | kind: Service
4 | metadata:
5 | name: webhook-service
6 | namespace: system
7 | spec:
8 | ports:
9 | - port: 443
10 | targetPort: 9876
11 | selector:
12 | control-plane: controller-manager
13 |
--------------------------------------------------------------------------------
/docs/getting_started/installation.md:
--------------------------------------------------------------------------------
1 | # Installation
2 |
3 | ## Requirements
4 | - Install Kubernetes Cluster, requires **Kubernetes version >= 1.19**.
5 | - (Optional, If Use CloneSet) Helm installation of OpenKruise, **Since v1.1.0**, Reference [Install OpenKruise](https://openkruise.io/docs/installation).
6 |
7 | ## Install with helm
8 |
9 | Kruise Rollout can be simply installed by helm v3.1+, which is a simple command-line tool and you can get it from [here](https://github.com/helm/helm/releases).
10 |
11 | ```bash
12 | # Firstly add openkruise charts repository if you haven't do this.
13 | $ helm repo add openkruise https://openkruise.github.io/charts/
14 |
15 | # [Optional]
16 | $ helm repo update
17 |
18 | # Install the latest version.
19 | $ helm install kruise-rollout openkruise/kruise-rollout --version 0.1.0
20 | ```
21 |
22 | ## Uninstall
23 |
24 | Note that this will lead to all resources created by Kruise Rollout, including webhook configurations, services, namespace, CRDs and CR instances Kruise Rollout controller, to be deleted!
25 |
26 | Please do this ONLY when you fully understand the consequence.
27 |
28 | To uninstall kruise rollout if it is installed with helm charts:
29 |
30 | ```bash
31 | $ helm uninstall kruise-rollout
32 | release "kruise-rollout" uninstalled
33 | ```
34 |
35 | ## What's Next
36 | Here are some recommended next steps:
37 | - Learn Kruise Rollout's [Basic Usage](../tutorials/basic_usage.md).
38 |
--------------------------------------------------------------------------------
/docs/getting_started/introduction.md:
--------------------------------------------------------------------------------
1 | # Introduction
2 | ## What is Kruise Rollout?
3 | Kruise Rollouts is **a Bypass component which provides advanced deployment capabilities such as canary, traffic routing, and progressive delivery features, for a series of Kubernetes workloads, such as Deployment and CloneSet**.
4 |
5 | Kruise Rollout integrates with ingress controllers and service meshes, leveraging their traffic shaping abilities to gradually shift traffic to the new version during an update.
6 | In addition, the business Pods metrics analysis can be used during rollout to determine whether the release will continue or be suspended.
7 |
8 | 
9 |
10 | ## Why is Kruise Rollout?
11 | The native Kubernetes Deployment Object supports the **RollingUpdate** strategy which provides a basic set of safety guarantees(maxUnavailable, maxSurge) during an update. However the rolling update strategy faces many limitations:
12 | - **The process of batch release cannot be strictly controlled**, e.g. 20%, 40% etc. Although maxUnavailable, maxSurge can control the release rate, it will release the next batch as soon as the previous batch has been released.
13 | - **Can't precisely control traffic flow during the release**, e.g. 20% traffic flow rate to the new version of Pods.
14 | - **No ability to query external metrics to verify whether the business indicators during the upgrade process are normal**.
15 |
16 | ## Features
17 | - **Functionality**:
18 | - Support multi-batch delivery for Deployment/CloneSet.
19 | - Support Nginx/ALB/Istio traffic routing control during rollout.
20 |
21 | - **Flexibility**:
22 | - Support scaling up/down to workloads during rollout.
23 | - Can be applied to newly-created or existing workload objects directly;
24 | - Can be ridden out of at any time when you needn't it without consideration of unavailable workloads and traffic problems.
25 | - Can cooperate with other native/third-part Kubernetes controllers/operators, such as HPA and WorkloadSpread.
26 |
27 | - **Non-Invasion**:
28 | - Does not invade native workload controllers.
29 | - Does not replace user-defined workload and traffic configurations.
30 |
31 | - **Extensibility**:
32 | - Easily extend to other traffic routing types, or workload types via plugin codes.
33 |
34 | - **Easy-integration**:
35 | - Easily integrate with classic or GitOps-style Kubernetes-based PaaS.
36 |
37 | ## What's Next
38 | Here are some recommended next steps:
39 | - Start to [Install Kruise Rollout](./installation.md).
40 | - Learn Kruise Rollout's [Basic Usage](../tutorials/basic_usage.md).
41 | - [Demo video](https://www.bilibili.com/video/BV1wT4y1Q7eD?spm_id_from=333.880.my_history.page.click)
42 |
--------------------------------------------------------------------------------
/docs/images/approve_rollout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openkruise/rollouts/6554cfcf08abd3b2c959f7fa30a4e3509315ef8c/docs/images/approve_rollout.png
--------------------------------------------------------------------------------
/docs/images/continuous_release.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openkruise/rollouts/6554cfcf08abd3b2c959f7fa30a4e3509315ef8c/docs/images/continuous_release.png
--------------------------------------------------------------------------------
/docs/images/deploy_rollout.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openkruise/rollouts/6554cfcf08abd3b2c959f7fa30a4e3509315ef8c/docs/images/deploy_rollout.png
--------------------------------------------------------------------------------
/docs/images/echoserver_application.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openkruise/rollouts/6554cfcf08abd3b2c959f7fa30a4e3509315ef8c/docs/images/echoserver_application.png
--------------------------------------------------------------------------------
/docs/images/rollback_echoserver.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openkruise/rollouts/6554cfcf08abd3b2c959f7fa30a4e3509315ef8c/docs/images/rollback_echoserver.png
--------------------------------------------------------------------------------
/docs/images/rollout-arch.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openkruise/rollouts/6554cfcf08abd3b2c959f7fa30a4e3509315ef8c/docs/images/rollout-arch.png
--------------------------------------------------------------------------------
/docs/images/rollout_canary.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openkruise/rollouts/6554cfcf08abd3b2c959f7fa30a4e3509315ef8c/docs/images/rollout_canary.png
--------------------------------------------------------------------------------
/docs/images/rollout_intro.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openkruise/rollouts/6554cfcf08abd3b2c959f7fa30a4e3509315ef8c/docs/images/rollout_intro.png
--------------------------------------------------------------------------------
/docs/images/upgrade_echoserver.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openkruise/rollouts/6554cfcf08abd3b2c959f7fa30a4e3509315ef8c/docs/images/upgrade_echoserver.png
--------------------------------------------------------------------------------
/docs/images/upgrade_failed.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/openkruise/rollouts/6554cfcf08abd3b2c959f7fa30a4e3509315ef8c/docs/images/upgrade_failed.png
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/openkruise/rollouts
2 |
3 | go 1.19
4 |
5 | require (
6 | github.com/davecgh/go-spew v1.1.1
7 | github.com/evanphx/json-patch v4.12.0+incompatible
8 | github.com/onsi/ginkgo v1.16.5
9 | github.com/onsi/gomega v1.24.1
10 | github.com/openkruise/kruise-api v1.3.0
11 | github.com/spf13/pflag v1.0.5
12 | github.com/stretchr/testify v1.8.2
13 | github.com/yuin/gopher-lua v0.0.0-20220504180219-658193537a64
14 | golang.org/x/time v0.3.0
15 | gopkg.in/yaml.v2 v2.4.0
16 | k8s.io/api v0.26.3
17 | k8s.io/apiextensions-apiserver v0.26.3
18 | k8s.io/apimachinery v0.26.3
19 | k8s.io/apiserver v0.26.3
20 | k8s.io/client-go v0.26.3
21 | k8s.io/component-base v0.26.3
22 | k8s.io/klog/v2 v2.100.1
23 | k8s.io/utils v0.0.0-20221128185143-99ec85e7a448
24 | layeh.com/gopher-json v0.0.0-20201124131017-552bb3c4c3bf
25 | sigs.k8s.io/controller-runtime v0.14.6
26 | sigs.k8s.io/gateway-api v0.7.1
27 | sigs.k8s.io/yaml v1.3.0
28 | )
29 |
30 | require (
31 | github.com/beorn7/perks v1.0.1 // indirect
32 | github.com/blang/semver/v4 v4.0.0 // indirect
33 | github.com/cespare/xxhash/v2 v2.1.2 // indirect
34 | github.com/emicklei/go-restful/v3 v3.9.0 // indirect
35 | github.com/evanphx/json-patch/v5 v5.6.0 // indirect
36 | github.com/fsnotify/fsnotify v1.6.0 // indirect
37 | github.com/go-logr/logr v1.2.3 // indirect
38 | github.com/go-logr/zapr v1.2.3 // indirect
39 | github.com/go-openapi/jsonpointer v0.19.5 // indirect
40 | github.com/go-openapi/jsonreference v0.20.0 // indirect
41 | github.com/go-openapi/swag v0.19.14 // indirect
42 | github.com/gogo/protobuf v1.3.2 // indirect
43 | github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
44 | github.com/golang/protobuf v1.5.2 // indirect
45 | github.com/google/gnostic v0.5.7-v3refs // indirect
46 | github.com/google/go-cmp v0.5.9 // indirect
47 | github.com/google/gofuzz v1.1.0 // indirect
48 | github.com/google/uuid v1.1.2 // indirect
49 | github.com/imdario/mergo v0.3.12 // indirect
50 | github.com/josharian/intern v1.0.0 // indirect
51 | github.com/json-iterator/go v1.1.12 // indirect
52 | github.com/mailru/easyjson v0.7.6 // indirect
53 | github.com/matttproud/golang_protobuf_extensions v1.0.2 // indirect
54 | github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
55 | github.com/modern-go/reflect2 v1.0.2 // indirect
56 | github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
57 | github.com/nxadm/tail v1.4.8 // indirect
58 | github.com/pkg/errors v0.9.1 // indirect
59 | github.com/pmezard/go-difflib v1.0.0 // indirect
60 | github.com/prometheus/client_golang v1.14.0 // indirect
61 | github.com/prometheus/client_model v0.3.0 // indirect
62 | github.com/prometheus/common v0.37.0 // indirect
63 | github.com/prometheus/procfs v0.8.0 // indirect
64 | go.uber.org/atomic v1.7.0 // indirect
65 | go.uber.org/multierr v1.6.0 // indirect
66 | go.uber.org/zap v1.24.0 // indirect
67 | golang.org/x/net v0.7.0 // indirect
68 | golang.org/x/oauth2 v0.0.0-20220223155221-ee480838109b // indirect
69 | golang.org/x/sys v0.5.0 // indirect
70 | golang.org/x/term v0.5.0 // indirect
71 | golang.org/x/text v0.7.0 // indirect
72 | gomodules.xyz/jsonpatch/v2 v2.2.0 // indirect
73 | google.golang.org/appengine v1.6.7 // indirect
74 | google.golang.org/protobuf v1.28.1 // indirect
75 | gopkg.in/inf.v0 v0.9.1 // indirect
76 | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
77 | gopkg.in/yaml.v3 v3.0.1 // indirect
78 | k8s.io/kube-openapi v0.0.0-20221012153701-172d655c2280 // indirect
79 | sigs.k8s.io/json v0.0.0-20220713155537-f223a00ba0e2 // indirect
80 | sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect
81 | )
82 |
--------------------------------------------------------------------------------
/hack/boilerplate.go.txt:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
--------------------------------------------------------------------------------
/lua_configuration/networking.istio.io/DestinationRule/testdata/traffic_routing_with_a_match.yaml:
--------------------------------------------------------------------------------
1 | trafficRouting:
2 | apiVersion: rollouts.kruise.io/v1alpha1
3 | kind: TrafficRouting
4 | metadata:
5 | name: tr-demo
6 | spec:
7 | strategy:
8 | matches:
9 | - headers:
10 | - type: Exact
11 | name: version
12 | value: canary
13 | objectRef:
14 | - service: svc-demo
15 | customNetworkRefs:
16 | - apiVersion: networking.istio.io/v1beta1
17 | kind: DestinationRule
18 | name: ds-demo
19 | original:
20 | apiVersion: networking.istio.io/v1beta1
21 | kind: DestinationRule
22 | metadata:
23 | name: ds-demo
24 | spec:
25 | host: svc-demo
26 | trafficPolicy:
27 | loadBalancer:
28 | simple: ROUND_ROBIN
29 | subsets:
30 | - labels:
31 | version: base
32 | name: version-base
33 | expected:
34 | - apiVersion: networking.istio.io/v1beta1
35 | kind: DestinationRule
36 | metadata:
37 | name: ds-demo
38 | spec:
39 | host: svc-demo
40 | trafficPolicy:
41 | loadBalancer:
42 | simple: ROUND_ROBIN
43 | subsets:
44 | - labels:
45 | version: base
46 | name: version-base
47 | - labels:
48 | istio.service.tag: gray
49 | name: canary
--------------------------------------------------------------------------------
/lua_configuration/networking.istio.io/DestinationRule/trafficRouting.lua:
--------------------------------------------------------------------------------
1 | local spec = obj.data.spec
2 | local canary = {}
3 | canary.labels = {}
4 | canary.name = "canary"
5 | local podLabelKey = "istio.service.tag"
6 | canary.labels[podLabelKey] = "gray"
7 | table.insert(spec.subsets, canary)
8 | return obj.data
--------------------------------------------------------------------------------
/lua_configuration/networking.istio.io/VirtualService/testdata/rollout_with_three_steps.yaml:
--------------------------------------------------------------------------------
1 | rollout:
2 | apiVersion: rollouts.kruise.io/v1beta1
3 | kind: Rollout
4 | metadata:
5 | name: rollouts-demo
6 | spec:
7 | workloadRef:
8 | apiVersion: apps/v1
9 | kind: Deployment
10 | name: deploy-demo
11 | strategy:
12 | canary:
13 | steps:
14 | - matches:
15 | - headers:
16 | - type: Exact
17 | name: user-agent
18 | value: pc
19 | - type: RegularExpression
20 | name: name
21 | value: ".*demo"
22 | requestHeaderModifier:
23 | set:
24 | - name: "header-foo"
25 | value: "bar"
26 | - matches:
27 | - headers:
28 | - type: Exact
29 | name: user-agent
30 | value: pc
31 | - headers:
32 | - type: RegularExpression
33 | name: name
34 | value: ".*demo"
35 | - traffic: "50%"
36 | trafficRoutings:
37 | - service: svc-demo
38 | customNetworkRefs:
39 | - apiVersion: networking.istio.io/v1alpha3
40 | kind: VirtualService
41 | name: vs-demo
42 | original:
43 | apiVersion: networking.istio.io/v1alpha3
44 | kind: VirtualService
45 | metadata:
46 | name: vs-demo
47 | spec:
48 | hosts:
49 | - "*"
50 | gateways:
51 | - nginx-gateway
52 | http:
53 | - route:
54 | - destination:
55 | host: svc-demo
56 | expected:
57 | - apiVersion: networking.istio.io/v1alpha3
58 | kind: VirtualService
59 | metadata:
60 | name: vs-demo
61 | spec:
62 | hosts:
63 | - "*"
64 | gateways:
65 | - nginx-gateway
66 | http:
67 | - match:
68 | - headers:
69 | user-agent:
70 | exact: pc
71 | name:
72 | regex: .*demo
73 | headers:
74 | request:
75 | set:
76 | header-foo: bar
77 | route:
78 | - destination:
79 | host: svc-demo-canary
80 | - route:
81 | - destination:
82 | host: svc-demo
83 | - apiVersion: networking.istio.io/v1alpha3
84 | kind: VirtualService
85 | metadata:
86 | name: vs-demo
87 | spec:
88 | hosts:
89 | - "*"
90 | gateways:
91 | - nginx-gateway
92 | http:
93 | - match:
94 | - headers:
95 | name:
96 | regex: .*demo
97 | route:
98 | - destination:
99 | host: svc-demo-canary
100 | - match:
101 | - headers:
102 | user-agent:
103 | exact: pc
104 | route:
105 | - destination:
106 | host: svc-demo-canary
107 | - route:
108 | - destination:
109 | host: svc-demo
110 | - apiVersion: networking.istio.io/v1alpha3
111 | kind: VirtualService
112 | metadata:
113 | name: vs-demo
114 | spec:
115 | hosts:
116 | - "*"
117 | gateways:
118 | - nginx-gateway
119 | http:
120 | - route:
121 | - destination:
122 | host: svc-demo
123 | weight: 50
124 | - destination:
125 | host: svc-demo-canary
126 | weight: 50
127 |
--------------------------------------------------------------------------------
/lua_configuration/networking.istio.io/VirtualService/testdata/traffic_routing_with_a_match.yaml:
--------------------------------------------------------------------------------
1 | trafficRouting:
2 | apiVersion: rollouts.kruise.io/v1alpha1
3 | kind: TrafficRouting
4 | metadata:
5 | name: tr-demo
6 | spec:
7 | strategy:
8 | matches:
9 | - headers:
10 | - type: Exact
11 | name: user-agent
12 | value: pc
13 | - type: RegularExpression
14 | name: name
15 | value: ".*demo"
16 | requestHeaderModifier:
17 | set:
18 | - name: "header-foo"
19 | value: "bar"
20 | objectRef:
21 | - service: svc-demo
22 | customNetworkRefs:
23 | - apiVersion: networking.istio.io/v1alpha3
24 | kind: VirtualService
25 | name: vs-demo
26 | original:
27 | apiVersion: networking.istio.io/v1alpha3
28 | kind: VirtualService
29 | metadata:
30 | name: vs-demo
31 | spec:
32 | hosts:
33 | - "*"
34 | gateways:
35 | - nginx-gateway
36 | http:
37 | - route:
38 | - destination:
39 | host: svc-demo
40 | subset: base
41 | expected:
42 | - apiVersion: networking.istio.io/v1alpha3
43 | kind: VirtualService
44 | metadata:
45 | name: vs-demo
46 | spec:
47 | hosts:
48 | - "*"
49 | gateways:
50 | - nginx-gateway
51 | http:
52 | - match:
53 | - headers:
54 | user-agent:
55 | exact: pc
56 | name:
57 | regex: .*demo
58 | headers:
59 | request:
60 | set:
61 | header-foo: bar
62 | route:
63 | - destination:
64 | host: svc-demo
65 | subset: canary
66 | - route:
67 | - destination:
68 | host: svc-demo
69 | subset: base
70 |
--------------------------------------------------------------------------------
/lua_configuration/networking.istio.io/VirtualService/testdata/traffic_routing_with_matches.yaml:
--------------------------------------------------------------------------------
1 | trafficRouting:
2 | apiVersion: rollouts.kruise.io/v1alpha1
3 | kind: TrafficRouting
4 | metadata:
5 | name: tr-demo
6 | spec:
7 | strategy:
8 | matches:
9 | - headers:
10 | - type: Exact
11 | name: user-agent
12 | value: pc
13 | - headers:
14 | - type: RegularExpression
15 | name: name
16 | value: ".*demo"
17 | requestHeaderModifier:
18 | set:
19 | - name: "header-foo"
20 | value: "bar"
21 | objectRef:
22 | - service: svc-demo
23 | customNetworkRefs:
24 | - apiVersion: networking.istio.io/v1alpha3
25 | kind: VirtualService
26 | name: vs-demo
27 | original:
28 | apiVersion: networking.istio.io/v1alpha3
29 | kind: VirtualService
30 | metadata:
31 | name: vs-demo
32 | spec:
33 | hosts:
34 | - "*"
35 | gateways:
36 | - nginx-gateway
37 | http:
38 | - route:
39 | - destination:
40 | host: svc-demo
41 | subset: base
42 | expected:
43 | - apiVersion: networking.istio.io/v1alpha3
44 | kind: VirtualService
45 | metadata:
46 | name: vs-demo
47 | spec:
48 | hosts:
49 | - "*"
50 | gateways:
51 | - nginx-gateway
52 | http:
53 | - match:
54 | - headers:
55 | name:
56 | regex: .*demo
57 | headers:
58 | request:
59 | set:
60 | header-foo: bar
61 | route:
62 | - destination:
63 | host: svc-demo
64 | subset: canary
65 | - match:
66 | - headers:
67 | user-agent:
68 | exact: pc
69 | headers:
70 | request:
71 | set:
72 | header-foo: bar
73 | route:
74 | - destination:
75 | host: svc-demo
76 | subset: canary
77 | - route:
78 | - destination:
79 | host: svc-demo
80 | subset: base
--------------------------------------------------------------------------------
/lua_configuration/networking.istio.io/VirtualService/testdata/traffic_routing_with_weight.yaml:
--------------------------------------------------------------------------------
1 | trafficRouting:
2 | apiVersion: rollouts.kruise.io/v1alpha1
3 | kind: TrafficRouting
4 | metadata:
5 | name: tr-demo
6 | spec:
7 | strategy:
8 | weight: 50
9 | objectRef:
10 | - service: svc-demo
11 | customNetworkRefs:
12 | - apiVersion: networking.istio.io/v1alpha3
13 | kind: VirtualService
14 | name: vs-demo
15 | original:
16 | apiVersion: networking.istio.io/v1alpha3
17 | kind: VirtualService
18 | metadata:
19 | name: vs-demo
20 | spec:
21 | hosts:
22 | - "*"
23 | gateways:
24 | - nginx-gateway
25 | http:
26 | - route:
27 | - destination:
28 | host: svc-demo
29 | subset: base
30 | expected:
31 | - apiVersion: networking.istio.io/v1alpha3
32 | kind: VirtualService
33 | metadata:
34 | name: nginx-vs
35 | namespace: demo
36 | spec:
37 | hosts:
38 | - "*"
39 | gateways:
40 | - nginx-gateway
41 | http:
42 | - route:
43 | - destination:
44 | host: svc-demo
45 | subset: base
46 | weight: 50
47 | - destination:
48 | host: svc-demo
49 | subset: canary
50 | weight: 50
51 |
--------------------------------------------------------------------------------
/lua_configuration/trafficrouting_ingress/aliyun-alb.lua:
--------------------------------------------------------------------------------
1 | annotations = {}
2 | if ( obj.annotations )
3 | then
4 | annotations = obj.annotations
5 | end
6 | annotations["alb.ingress.kubernetes.io/canary"] = "true"
7 | annotations["alb.ingress.kubernetes.io/canary-by-cookie"] = nil
8 | annotations["alb.ingress.kubernetes.io/canary-by-header"] = nil
9 | annotations["alb.ingress.kubernetes.io/canary-by-header-pattern"] = nil
10 | annotations["alb.ingress.kubernetes.io/canary-by-header-value"] = nil
11 | annotations["alb.ingress.kubernetes.io/canary-weight"] = nil
12 | annotations["alb.ingress.kubernetes.io/order"] = "1"
13 | if ( obj.weight ~= "-1" )
14 | then
15 | annotations["alb.ingress.kubernetes.io/canary-weight"] = obj.weight
16 | end
17 | if ( not obj.matches )
18 | then
19 | return annotations
20 | end
21 | for _,match in ipairs(obj.matches) do
22 | local header = match.headers[1]
23 | if ( header.name == "canary-by-cookie" )
24 | then
25 | annotations["alb.ingress.kubernetes.io/canary-by-cookie"] = header.value
26 | else
27 | annotations["alb.ingress.kubernetes.io/canary-by-header"] = header.name
28 | if ( header.type == "RegularExpression" )
29 | then
30 | annotations["alb.ingress.kubernetes.io/canary-by-header-pattern"] = header.value
31 | else
32 | annotations["alb.ingress.kubernetes.io/canary-by-header-value"] = header.value
33 | end
34 | end
35 | end
36 | return annotations
37 |
--------------------------------------------------------------------------------
/lua_configuration/trafficrouting_ingress/higress.lua:
--------------------------------------------------------------------------------
1 | annotations = {}
2 | -- obj.annotations is ingress annotations, it is recommended not to remove the part of the lua script, it must be kept
3 | if ( obj.annotations )
4 | then
5 | annotations = obj.annotations
6 | end
7 | -- indicates the ingress is nginx canary api
8 | annotations["nginx.ingress.kubernetes.io/canary"] = "true"
9 | -- First, set all canary api to nil
10 | annotations["nginx.ingress.kubernetes.io/canary-by-cookie"] = nil
11 | annotations["nginx.ingress.kubernetes.io/canary-by-header"] = nil
12 | annotations["nginx.ingress.kubernetes.io/canary-by-header-pattern"] = nil
13 | annotations["nginx.ingress.kubernetes.io/canary-by-header-value"] = nil
14 | annotations["nginx.ingress.kubernetes.io/canary-weight"] = nil
15 | -- if rollout.spec.strategy.canary.steps.weight is nil, obj.weight will be -1,
16 | -- then we need remove the canary-weight annotation
17 | if ( obj.weight ~= "-1" )
18 | then
19 | annotations["nginx.ingress.kubernetes.io/canary-weight"] = obj.weight
20 | end
21 | -- if don't contains headers, immediate return annotations
22 | if ( not obj.matches )
23 | then
24 | return annotations
25 | end
26 | -- headers & cookie apis
27 | -- traverse matches
28 | for _,match in ipairs(obj.matches) do
29 | local header = match.headers[1]
30 | -- cookie
31 | if ( header.name == "canary-by-cookie" )
32 | then
33 | annotations["nginx.ingress.kubernetes.io/canary-by-cookie"] = header.value
34 | else
35 | annotations["nginx.ingress.kubernetes.io/canary-by-header"] = header.name
36 | -- if regular expression
37 | if ( header.type == "RegularExpression" )
38 | then
39 | annotations["nginx.ingress.kubernetes.io/canary-by-header-pattern"] = header.value
40 | else
41 | annotations["nginx.ingress.kubernetes.io/canary-by-header-value"] = header.value
42 | end
43 | end
44 | end
45 | -- must be return annotations
46 | return annotations
--------------------------------------------------------------------------------
/lua_configuration/trafficrouting_ingress/mse.lua:
--------------------------------------------------------------------------------
1 | function split(input, delimiter)
2 | local arr = {}
3 | string.gsub(input, '[^' .. delimiter ..']+', function(w) table.insert(arr, w) end)
4 | return arr
5 | end
6 |
7 | annotations = obj.annotations
8 | annotations["nginx.ingress.kubernetes.io/canary"] = "true"
9 | annotations["nginx.ingress.kubernetes.io/canary-by-cookie"] = nil
10 | annotations["nginx.ingress.kubernetes.io/canary-by-header"] = nil
11 | annotations["nginx.ingress.kubernetes.io/canary-by-header-pattern"] = nil
12 | annotations["nginx.ingress.kubernetes.io/canary-by-header-value"] = nil
13 | -- MSE extended annotations
14 | annotations["mse.ingress.kubernetes.io/canary-by-query"] = nil
15 | annotations["mse.ingress.kubernetes.io/canary-by-query-pattern"] = nil
16 | annotations["mse.ingress.kubernetes.io/canary-by-query-value"] = nil
17 | annotations["nginx.ingress.kubernetes.io/canary-weight"] = nil
18 | if ( obj.weight ~= "-1" )
19 | then
20 | annotations["nginx.ingress.kubernetes.io/canary-weight"] = obj.weight
21 | end
22 | if ( annotations["mse.ingress.kubernetes.io/service-subset"] )
23 | then
24 | annotations["mse.ingress.kubernetes.io/service-subset"] = "gray"
25 | end
26 |
27 | if ( obj.requestHeaderModifier )
28 | then
29 | local str = ''
30 | for _,header in ipairs(obj.requestHeaderModifier.set) do
31 | str = str..string.format("%s %s", header.name, header.value)
32 | end
33 | annotations["mse.ingress.kubernetes.io/request-header-control-update"] = str
34 | end
35 | if ( not obj.matches )
36 | then
37 | return annotations
38 | end
39 | for _,match in ipairs(obj.matches) do
40 | if match.headers and next(match.headers) ~= nil then
41 | header = match.headers[1]
42 | if ( header.name == "canary-by-cookie" )
43 | then
44 | annotations["nginx.ingress.kubernetes.io/canary-by-cookie"] = header.value
45 | else
46 | annotations["nginx.ingress.kubernetes.io/canary-by-header"] = header.name
47 | if ( header.type == "RegularExpression" )
48 | then
49 | annotations["nginx.ingress.kubernetes.io/canary-by-header-pattern"] = header.value
50 | else
51 | annotations["nginx.ingress.kubernetes.io/canary-by-header-value"] = header.value
52 | end
53 | end
54 | end
55 | if match.queryParams and next(match.queryParams) ~= nil then
56 | queryParam = match.queryParams[1]
57 | annotations["nginx.ingress.kubernetes.io/canary-by-query"] = queryParam.name
58 | if ( queryParam.type == "RegularExpression" )
59 | then
60 | annotations["nginx.ingress.kubernetes.io/canary-by-query-pattern"] = queryParam.value
61 | else
62 | annotations["nginx.ingress.kubernetes.io/canary-by-query-value"] = queryParam.value
63 | end
64 | end
65 | end
66 | return annotations
--------------------------------------------------------------------------------
/lua_configuration/trafficrouting_ingress/nginx.lua:
--------------------------------------------------------------------------------
1 | annotations = {}
2 | -- obj.annotations is ingress annotations, it is recommended not to remove the part of the lua script, it must be kept
3 | if ( obj.annotations )
4 | then
5 | annotations = obj.annotations
6 | end
7 | -- indicates the ingress is nginx canary api
8 | annotations["nginx.ingress.kubernetes.io/canary"] = "true"
9 | -- First, set all canary api to nil
10 | annotations["nginx.ingress.kubernetes.io/canary-by-cookie"] = nil
11 | annotations["nginx.ingress.kubernetes.io/canary-by-header"] = nil
12 | annotations["nginx.ingress.kubernetes.io/canary-by-header-pattern"] = nil
13 | annotations["nginx.ingress.kubernetes.io/canary-by-header-value"] = nil
14 | annotations["nginx.ingress.kubernetes.io/canary-weight"] = nil
15 | -- if rollout.spec.strategy.canary.steps.weight is nil, obj.weight will be -1,
16 | -- then we need remove the canary-weight annotation
17 | if ( obj.weight ~= "-1" )
18 | then
19 | annotations["nginx.ingress.kubernetes.io/canary-weight"] = obj.weight
20 | end
21 | -- if don't contains headers, immediate return annotations
22 | if ( not obj.matches )
23 | then
24 | return annotations
25 | end
26 | -- headers & cookie apis
27 | -- traverse matches
28 | for _,match in ipairs(obj.matches) do
29 | if match.headers and next(match.headers) ~= nil then
30 | local header = match.headers[1]
31 | -- cookie
32 | if ( header.name == "canary-by-cookie" )
33 | then
34 | annotations["nginx.ingress.kubernetes.io/canary-by-cookie"] = header.value
35 | else
36 | annotations["nginx.ingress.kubernetes.io/canary-by-header"] = header.name
37 | -- if regular expression
38 | if ( header.type == "RegularExpression" )
39 | then
40 | annotations["nginx.ingress.kubernetes.io/canary-by-header-pattern"] = header.value
41 | else
42 | annotations["nginx.ingress.kubernetes.io/canary-by-header-value"] = header.value
43 | end
44 | end
45 | end
46 | end
47 | -- must be return annotations
48 | return annotations
49 |
--------------------------------------------------------------------------------
/pkg/controller/batchrelease/control/apis.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package control
18 |
19 | import "k8s.io/apimachinery/pkg/util/intstr"
20 |
21 | // OriginalDeploymentStrategy stores part of the fileds of a workload,
22 | // so that it can be restored when finalizing.
23 | // It is only used for BlueGreen Release
24 | // Similar to DeploymentStrategy, it is an annotation used in workload
25 | // However, unlike DeploymentStrategy, it is only used to store and restore the user's strategy
26 | type OriginalDeploymentStrategy struct {
27 | // +optional
28 | MaxUnavailable *intstr.IntOrString `json:"maxUnavailable,omitempty" protobuf:"bytes,1,opt,name=maxUnavailable"`
29 | // +optional
30 | MaxSurge *intstr.IntOrString `json:"maxSurge,omitempty" protobuf:"bytes,2,opt,name=maxSurge"`
31 | // Minimum number of seconds for which a newly created pod should be ready
32 | // without any of its container crashing, for it to be considered available.
33 | // Defaults to 0 (pod will be considered available as soon as it is ready)
34 | // +optional
35 | MinReadySeconds int32 `json:"minReadySeconds,omitempty" protobuf:"varint,5,opt,name=minReadySeconds"`
36 | // The maximum time in seconds for a deployment to make progress before it
37 | // is considered to be failed. The deployment controller will continue to
38 | // process failed deployments and a condition with a ProgressDeadlineExceeded
39 | // reason will be surfaced in the deployment status. Note that progress will
40 | // not be estimated during the time a deployment is paused. Defaults to 600s.
41 | ProgressDeadlineSeconds *int32 `json:"progressDeadlineSeconds,omitempty" protobuf:"varint,9,opt,name=progressDeadlineSeconds"`
42 | }
43 |
--------------------------------------------------------------------------------
/pkg/controller/batchrelease/control/bluegreenstyle/interface.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package bluegreenstyle
18 |
19 | import (
20 | "github.com/openkruise/rollouts/api/v1beta1"
21 | batchcontext "github.com/openkruise/rollouts/pkg/controller/batchrelease/context"
22 | "github.com/openkruise/rollouts/pkg/util"
23 | corev1 "k8s.io/api/core/v1"
24 | )
25 |
26 | type Interface interface {
27 | // BuildController will get workload object and parse workload info,
28 | // and return a initialized controller for workload.
29 | BuildController() (Interface, error)
30 | // GetWorkloadInfo return workload information.
31 | GetWorkloadInfo() *util.WorkloadInfo
32 | // ListOwnedPods fetch the pods owned by the workload.
33 | // Note that we should list pod only if we really need it.
34 | // reserved for future use
35 | ListOwnedPods() ([]*corev1.Pod, error)
36 | // CalculateBatchContext calculate current batch context
37 | // according to release plan and current status of workload.
38 | CalculateBatchContext(release *v1beta1.BatchRelease) (*batchcontext.BatchContext, error)
39 | // Initialize do something before rolling out, for example:
40 | // - pause the workload
41 | // - update: MinReadySeconds, ProgressDeadlineSeconds, Strategy
42 | Initialize(release *v1beta1.BatchRelease) error
43 | // UpgradeBatch upgrade workload according current batch context.
44 | UpgradeBatch(ctx *batchcontext.BatchContext) error
45 | // Finalize do something after rolling out, for example:
46 | // - set pause to false, restore the original setting, delete annotation
47 | Finalize(release *v1beta1.BatchRelease) error
48 | }
49 |
--------------------------------------------------------------------------------
/pkg/controller/batchrelease/control/canarystyle/deployment/stable.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package deployment
18 |
19 | import (
20 | "context"
21 | "fmt"
22 |
23 | "github.com/openkruise/rollouts/api/v1beta1"
24 | "github.com/openkruise/rollouts/pkg/controller/batchrelease/control"
25 | "github.com/openkruise/rollouts/pkg/util"
26 | apps "k8s.io/api/apps/v1"
27 | "k8s.io/apimachinery/pkg/types"
28 | "sigs.k8s.io/controller-runtime/pkg/client"
29 | )
30 |
31 | type realStableController struct {
32 | stableInfo *util.WorkloadInfo
33 | stableObject *apps.Deployment
34 | stableClient client.Client
35 | stableKey types.NamespacedName
36 | }
37 |
38 | func newStable(cli client.Client, key types.NamespacedName) realStableController {
39 | return realStableController{stableClient: cli, stableKey: key}
40 | }
41 |
42 | func (rc *realStableController) GetStableInfo() *util.WorkloadInfo {
43 | return rc.stableInfo
44 | }
45 |
46 | func (rc *realStableController) Initialize(release *v1beta1.BatchRelease) error {
47 | if control.IsControlledByBatchRelease(release, rc.stableObject) {
48 | return nil
49 | }
50 |
51 | d := util.GetEmptyObjectWithKey(rc.stableObject)
52 | owner := control.BuildReleaseControlInfo(release)
53 |
54 | body := fmt.Sprintf(`{"metadata":{"annotations":{"%s":"%s"}}}`, util.BatchReleaseControlAnnotation, owner)
55 | return rc.stableClient.Patch(context.TODO(), d, client.RawPatch(types.StrategicMergePatchType, []byte(body)))
56 | }
57 |
58 | func (rc *realStableController) Finalize(release *v1beta1.BatchRelease) error {
59 | if rc.stableObject == nil {
60 | return nil // no need to process deleted object
61 | }
62 |
63 | // if batchPartition == nil, workload should be promoted;
64 | pause := release.Spec.ReleasePlan.BatchPartition != nil
65 | body := fmt.Sprintf(`{"metadata":{"annotations":{"%s":null}},"spec":{"paused":%v}}`,
66 | util.BatchReleaseControlAnnotation, pause)
67 |
68 | d := util.GetEmptyObjectWithKey(rc.stableObject)
69 | if err := rc.stableClient.Patch(context.TODO(), d, client.RawPatch(types.StrategicMergePatchType, []byte(body))); err != nil {
70 | return err
71 | }
72 | if control.ShouldWaitResume(release) {
73 | return waitAllUpdatedAndReady(d.(*apps.Deployment))
74 | }
75 | return nil
76 | }
77 |
78 | func waitAllUpdatedAndReady(deployment *apps.Deployment) error {
79 | if deployment.Spec.Paused {
80 | return fmt.Errorf("promote error: deployment should not be paused")
81 | }
82 |
83 | createdReplicas := deployment.Status.Replicas
84 | updatedReplicas := deployment.Status.UpdatedReplicas
85 | if createdReplicas != updatedReplicas {
86 | return fmt.Errorf("promote error: all replicas should be upgraded")
87 | }
88 |
89 | availableReplicas := deployment.Status.AvailableReplicas
90 | allowedUnavailable := util.DeploymentMaxUnavailable(deployment)
91 | if allowedUnavailable+availableReplicas < createdReplicas {
92 | return fmt.Errorf("promote error: ready replicas should satisfy maxUnavailable")
93 | }
94 | return nil
95 | }
96 |
--------------------------------------------------------------------------------
/pkg/controller/batchrelease/control/canarystyle/interface.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package canarystyle
18 |
19 | import (
20 | "github.com/openkruise/rollouts/api/v1beta1"
21 | batchcontext "github.com/openkruise/rollouts/pkg/controller/batchrelease/context"
22 | "github.com/openkruise/rollouts/pkg/util"
23 | )
24 |
25 | type Interface interface {
26 | CanaryInterface
27 | StableInterface
28 | // BuildStableController will get stable workload object and parse
29 | // stable workload info, and return a controller for stable workload.
30 | BuildStableController() (StableInterface, error)
31 | // BuildCanaryController will get canary workload object and parse
32 | // canary workload info, and return a controller for canary workload.
33 | BuildCanaryController(release *v1beta1.BatchRelease) (CanaryInterface, error)
34 | // CalculateBatchContext calculate the current batch context according to
35 | // our release plan and the statues of stable workload and canary workload.
36 | CalculateBatchContext(release *v1beta1.BatchRelease) (*batchcontext.BatchContext, error)
37 | }
38 |
39 | // CanaryInterface contains the methods about canary workload
40 | type CanaryInterface interface {
41 | // GetCanaryInfo return the information about canary workload
42 | GetCanaryInfo() *util.WorkloadInfo
43 | // UpgradeBatch upgrade canary workload according to current batch context
44 | UpgradeBatch(*batchcontext.BatchContext) error
45 | // Create creates canary workload before rolling out
46 | Create(controller *v1beta1.BatchRelease) error
47 | // Delete deletes canary workload after rolling out
48 | Delete(controller *v1beta1.BatchRelease) error
49 | }
50 |
51 | // StableInterface contains the methods about stable workload
52 | type StableInterface interface {
53 | // GetStableInfo return the information about stable workload
54 | GetStableInfo() *util.WorkloadInfo
55 | // Initialize claim the stable workload is under rollout control
56 | Initialize(controller *v1beta1.BatchRelease) error
57 | // Finalize do something after rolling out, for example:
58 | // - free the stable workload from rollout control;
59 | // - resume stable workload and wait all pods updated if we need.
60 | Finalize(controller *v1beta1.BatchRelease) error
61 | }
62 |
--------------------------------------------------------------------------------
/pkg/controller/batchrelease/control/interface.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package control
18 |
19 | import (
20 | "github.com/openkruise/rollouts/pkg/util"
21 | )
22 |
23 | type WorkloadEventType string
24 |
25 | const (
26 | // WorkloadNormalState means workload is normal and event should be ignored.
27 | WorkloadNormalState WorkloadEventType = "workload-is-at-normal-state"
28 | // WorkloadUnknownState means workload state is unknown and should retry.
29 | WorkloadUnknownState WorkloadEventType = "workload-is-at-unknown-state"
30 | // WorkloadPodTemplateChanged means workload revision changed, should be stopped to execute batch release plan.
31 | WorkloadPodTemplateChanged WorkloadEventType = "workload-pod-template-changed"
32 | // WorkloadReplicasChanged means workload is scaling during rollout, should recalculate upgraded pods in current batch.
33 | WorkloadReplicasChanged WorkloadEventType = "workload-replicas-changed"
34 | // WorkloadStillReconciling means workload status is untrusted Untrustworthy, we should wait workload controller to reconcile.
35 | WorkloadStillReconciling WorkloadEventType = "workload-is-reconciling"
36 | // WorkloadHasGone means workload is deleted during rollout, we should do something finalizing works if this event occurs.
37 | WorkloadHasGone WorkloadEventType = "workload-has-gone"
38 | // WorkloadRollbackInBatch means workload is rollback according to BatchRelease batch plan.
39 | WorkloadRollbackInBatch WorkloadEventType = "workload-rollback-in-batch"
40 | )
41 |
42 | // Interface is the interface that all type of control plane implements for rollout.
43 | type Interface interface {
44 | // Initialize make sure that the resource is ready to be progressed.
45 | // this function is tasked to do any initialization work on the resources.
46 | // it returns nil if the preparation is succeeded, else the preparation should retry.
47 | Initialize() error
48 |
49 | // UpgradeBatch tries to upgrade old replicas according to the release plan.
50 | // it will upgrade the old replicas as the release plan allows in the current batch.
51 | // this function is tasked to do any initialization work on the resources.
52 | // it returns nil if the preparation is succeeded, else the preparation should retry.
53 | UpgradeBatch() error
54 |
55 | // EnsureBatchPodsReadyAndLabeled checks how many replicas are ready to serve requests in the current batch.
56 | // this function is tasked to do any initialization work on the resources.
57 | // it returns nil if the preparation is succeeded, else the preparation should retry.
58 | EnsureBatchPodsReadyAndLabeled() error
59 |
60 | // Finalize makes sure the resources are in a good final state.
61 | // this function is tasked to do any initialization work on the resources.
62 | // it returns nil if the preparation is succeeded, else the preparation should retry.
63 | Finalize() error
64 |
65 | // SyncWorkloadInformation will watch and compare the status recorded in Status of BatchRelease
66 | // and the real-time workload information. If workload status is inconsistent with that recorded
67 | // in BatchRelease, it will return the corresponding WorkloadEventType and info.
68 | SyncWorkloadInformation() (WorkloadEventType, *util.WorkloadInfo, error)
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/controller/batchrelease/control/partitionstyle/interface.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package partitionstyle
18 |
19 | import (
20 | "github.com/openkruise/rollouts/api/v1beta1"
21 | batchcontext "github.com/openkruise/rollouts/pkg/controller/batchrelease/context"
22 | "github.com/openkruise/rollouts/pkg/util"
23 | corev1 "k8s.io/api/core/v1"
24 | )
25 |
26 | type Interface interface {
27 | // BuildController will get workload object and parse workload info,
28 | // and return a initialized controller for workload.
29 | BuildController() (Interface, error)
30 | // GetWorkloadInfo return workload information.
31 | GetWorkloadInfo() *util.WorkloadInfo
32 | // ListOwnedPods fetch the pods owned by the workload.
33 | // Note that we should list pod only if we really need it.
34 | ListOwnedPods() ([]*corev1.Pod, error)
35 | // CalculateBatchContext calculate current batch context
36 | // according to release plan and current status of workload.
37 | CalculateBatchContext(release *v1beta1.BatchRelease) (*batchcontext.BatchContext, error)
38 |
39 | // Initialize do something before rolling out, for example:
40 | // - claim the workload is under our control;
41 | // - other things related with specific type of workload, such as 100% partition settings.
42 | Initialize(release *v1beta1.BatchRelease) error
43 | // UpgradeBatch upgrade workload according current batch context.
44 | UpgradeBatch(ctx *batchcontext.BatchContext) error
45 | // Finalize do something after rolling out, for example:
46 | // - free the stable workload from rollout control;
47 | // - resume workload if we need.
48 | Finalize(release *v1beta1.BatchRelease) error
49 | }
50 |
--------------------------------------------------------------------------------
/pkg/controller/deployment/deployment_event_handler.go:
--------------------------------------------------------------------------------
1 | package deployment
2 |
3 | import (
4 | "context"
5 |
6 | admissionregistrationv1 "k8s.io/api/admissionregistration/v1"
7 | appsv1 "k8s.io/api/apps/v1"
8 | "k8s.io/client-go/util/workqueue"
9 | "k8s.io/klog/v2"
10 | "sigs.k8s.io/controller-runtime/pkg/client"
11 | "sigs.k8s.io/controller-runtime/pkg/event"
12 | "sigs.k8s.io/controller-runtime/pkg/reconcile"
13 |
14 | "github.com/openkruise/rollouts/api/v1alpha1"
15 | "github.com/openkruise/rollouts/pkg/webhook/util/configuration"
16 | )
17 |
18 | type MutatingWebhookEventHandler struct {
19 | client.Reader
20 | }
21 |
22 | func (m MutatingWebhookEventHandler) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
23 | config, ok := evt.Object.(*admissionregistrationv1.MutatingWebhookConfiguration)
24 | if !ok || config == nil || !isKruiseRolloutMutatingConfiguration(config) || config.DeletionTimestamp.IsZero() {
25 | return
26 | }
27 | m.enqueue(q)
28 | }
29 |
30 | func (m MutatingWebhookEventHandler) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
31 | config, ok := evt.Object.(*admissionregistrationv1.MutatingWebhookConfiguration)
32 | if !ok || config == nil || !isKruiseRolloutMutatingConfiguration(config) || config.DeletionTimestamp.IsZero() {
33 | return
34 | }
35 | m.enqueue(q)
36 | }
37 |
38 | func (m MutatingWebhookEventHandler) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
39 | config, ok := evt.ObjectNew.(*admissionregistrationv1.MutatingWebhookConfiguration)
40 | if !ok || config == nil || !isKruiseRolloutMutatingConfiguration(config) || config.DeletionTimestamp.IsZero() {
41 | return
42 | }
43 | m.enqueue(q)
44 | }
45 |
46 | func (m MutatingWebhookEventHandler) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
47 | config, ok := evt.Object.(*admissionregistrationv1.MutatingWebhookConfiguration)
48 | if !ok || config == nil || !isKruiseRolloutMutatingConfiguration(config) {
49 | return
50 | }
51 | m.enqueue(q)
52 | }
53 |
54 | func (m MutatingWebhookEventHandler) enqueue(q workqueue.RateLimitingInterface) {
55 | deploymentLister := appsv1.DeploymentList{}
56 | err := m.List(context.TODO(), &deploymentLister, client.MatchingLabels(map[string]string{v1alpha1.AdvancedDeploymentControlLabel: "true"}))
57 | if err != nil {
58 | klog.Errorf("Failed to list deployment, error: %v", err)
59 | }
60 | for index := range deploymentLister.Items {
61 | if deploymentLister.Items[index].Spec.Strategy.Type == appsv1.RollingUpdateDeploymentStrategyType {
62 | continue
63 | }
64 | q.Add(reconcile.Request{NamespacedName: client.ObjectKeyFromObject(&deploymentLister.Items[index])})
65 | }
66 | }
67 |
68 | func isKruiseRolloutMutatingConfiguration(object client.Object) bool {
69 | return object.GetName() == configuration.MutatingWebhookConfigurationName
70 | }
71 |
--------------------------------------------------------------------------------
/pkg/controller/rollout/rollout_event_handler_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package rollout
18 |
19 | import (
20 | "context"
21 | "testing"
22 |
23 | "k8s.io/client-go/util/workqueue"
24 | "sigs.k8s.io/controller-runtime/pkg/client/fake"
25 | "sigs.k8s.io/controller-runtime/pkg/event"
26 | )
27 |
28 | func TestPodEventHandler(t *testing.T) {
29 | fakeClient := fake.NewClientBuilder().WithScheme(scheme).Build()
30 | handler := enqueueRequestForWorkload{reader: fakeClient, scheme: scheme}
31 |
32 | err := fakeClient.Create(context.TODO(), rolloutDemo.DeepCopy())
33 | if nil != err {
34 | t.Fatalf("unexpected create rollout %s failed: %v", rolloutDemo.Name, err)
35 | }
36 |
37 | // create
38 | createQ := workqueue.NewRateLimitingQueue(workqueue.DefaultControllerRateLimiter())
39 | createEvt := event.CreateEvent{
40 | Object: deploymentDemo,
41 | }
42 | handler.Create(createEvt, createQ)
43 | if createQ.Len() != 1 {
44 | t.Errorf("unexpected create event handle queue size, expected 1 actual %d", createQ.Len())
45 | }
46 |
47 | // other namespace
48 | demo1 := deploymentDemo.DeepCopy()
49 | demo1.Namespace = "other-ns"
50 | createEvt = event.CreateEvent{
51 | Object: demo1,
52 | }
53 | handler.Create(createEvt, createQ)
54 | if createQ.Len() != 1 {
55 | t.Errorf("unexpected create event handle queue size, expected 1 actual %d", createQ.Len())
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/pkg/controller/rollouthistory/rollouthistory_event_handler.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package rollouthistory
18 |
19 | import (
20 | rolloutv1alpha1 "github.com/openkruise/rollouts/api/v1alpha1"
21 | "k8s.io/apimachinery/pkg/types"
22 | "k8s.io/client-go/util/workqueue"
23 | "sigs.k8s.io/controller-runtime/pkg/client"
24 | "sigs.k8s.io/controller-runtime/pkg/event"
25 | "sigs.k8s.io/controller-runtime/pkg/handler"
26 | "sigs.k8s.io/controller-runtime/pkg/reconcile"
27 | )
28 |
29 | var _ handler.EventHandler = &enqueueRequestForRolloutHistory{}
30 |
31 | type enqueueRequestForRolloutHistory struct {
32 | }
33 |
34 | func (w *enqueueRequestForRolloutHistory) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
35 | w.handleEvent(q, evt.Object)
36 | }
37 |
38 | func (w *enqueueRequestForRolloutHistory) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
39 | }
40 |
41 | func (w *enqueueRequestForRolloutHistory) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
42 | }
43 |
44 | func (w *enqueueRequestForRolloutHistory) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
45 | w.handleEvent(q, evt.ObjectNew)
46 | }
47 |
48 | func (w *enqueueRequestForRolloutHistory) handleEvent(q workqueue.RateLimitingInterface, obj client.Object) {
49 | // In fact, rolloutHistory which is created by controller must have rolloutNameLabel and rolloutIDLabe
50 | rolloutName, ok1 := obj.(*rolloutv1alpha1.RolloutHistory).Labels[rolloutNameLabel]
51 | _, ok2 := obj.(*rolloutv1alpha1.RolloutHistory).Labels[rolloutIDLabel]
52 | if !ok1 || !ok2 {
53 | return
54 | }
55 | // add rollout which just creates a rolloutHistory to queue
56 | nsn := types.NamespacedName{Namespace: obj.GetNamespace(), Name: rolloutName}
57 | q.Add(reconcile.Request{NamespacedName: nsn})
58 | }
59 |
60 | var _ handler.EventHandler = &enqueueRequestForRollout{}
61 |
62 | type enqueueRequestForRollout struct {
63 | }
64 |
65 | func (w *enqueueRequestForRollout) Create(evt event.CreateEvent, q workqueue.RateLimitingInterface) {
66 | w.handleEvent(q, evt.Object)
67 | }
68 |
69 | func (w *enqueueRequestForRollout) Delete(evt event.DeleteEvent, q workqueue.RateLimitingInterface) {
70 | }
71 |
72 | func (w *enqueueRequestForRollout) Generic(evt event.GenericEvent, q workqueue.RateLimitingInterface) {
73 | }
74 |
75 | func (w *enqueueRequestForRollout) Update(evt event.UpdateEvent, q workqueue.RateLimitingInterface) {
76 | w.handleEvent(q, evt.ObjectNew)
77 | }
78 |
79 | func (w *enqueueRequestForRollout) handleEvent(q workqueue.RateLimitingInterface, obj client.Object) {
80 | // RolloutID shouldn't be empty
81 | rollout := obj.(*rolloutv1alpha1.Rollout)
82 | if rollout.Status.CanaryStatus == nil || rollout.Status.CanaryStatus.ObservedRolloutID == "" {
83 | return
84 | }
85 | // add rollout with RolloutID to queue
86 | nsn := types.NamespacedName{Namespace: obj.GetNamespace(), Name: obj.GetName()}
87 | q.Add(reconcile.Request{NamespacedName: nsn})
88 | }
89 |
--------------------------------------------------------------------------------
/pkg/feature/rollout_features.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package feature
18 |
19 | import (
20 | "k8s.io/apimachinery/pkg/util/runtime"
21 | "k8s.io/component-base/featuregate"
22 |
23 | utilfeature "github.com/openkruise/rollouts/pkg/util/feature"
24 | )
25 |
26 | const (
27 | // RolloutHistoryGate enable recording history for each rolling.
28 | RolloutHistoryGate featuregate.Feature = "RolloutHistory"
29 | // AdvancedDeploymentGate enable advanced deployment controller.
30 | AdvancedDeploymentGate featuregate.Feature = "AdvancedDeployment"
31 | // AppendServiceSelectorGate enable appending pod labels from PodTemplateMetadata to the canary service selector.
32 | AppendServiceSelectorGate featuregate.Feature = "AppendPodSelector"
33 | )
34 |
35 | var defaultFeatureGates = map[featuregate.Feature]featuregate.FeatureSpec{
36 | RolloutHistoryGate: {Default: false, PreRelease: featuregate.Alpha},
37 | AdvancedDeploymentGate: {Default: false, PreRelease: featuregate.Alpha},
38 | AppendServiceSelectorGate: {Default: false, PreRelease: featuregate.Alpha},
39 | }
40 |
41 | func init() {
42 | runtime.Must(utilfeature.DefaultMutableFeatureGate.Add(defaultFeatureGates))
43 | }
44 |
--------------------------------------------------------------------------------
/pkg/feature/switch.go:
--------------------------------------------------------------------------------
1 | package feature
2 |
3 | import "flag"
4 |
5 | var (
6 | // If workloadTypeFilterSwitch is true, webhook and controllers
7 | // will filter out unsupported workload type. Currently, rollout
8 | // support Deployment, CloneSet, StatefulSet and Advanced StatefulSet.
9 | // Default to true.
10 | workloadTypeFilterSwitch bool
11 | )
12 |
13 | func init() {
14 | flag.BoolVar(&workloadTypeFilterSwitch, "filter-workload-type", true, "filter known workload gvk for rollout controller")
15 | }
16 |
17 | func NeedFilterWorkloadType() bool {
18 | return workloadTypeFilterSwitch
19 | }
20 |
--------------------------------------------------------------------------------
/pkg/trafficrouting/network/composite.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2024 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package network
18 |
19 | import (
20 | "context"
21 |
22 | "k8s.io/apimachinery/pkg/util/validation/field"
23 |
24 | "github.com/openkruise/rollouts/api/v1beta1"
25 | )
26 |
27 | var (
28 | _ NetworkProvider = (CompositeController)(nil)
29 | )
30 |
31 | // CompositeController is a set of NetworkProvider
32 | type CompositeController []NetworkProvider
33 |
34 | func (c CompositeController) Initialize(ctx context.Context) error {
35 | for _, provider := range c {
36 | if err := provider.Initialize(ctx); err != nil {
37 | return err
38 | }
39 | }
40 | return nil
41 | }
42 |
43 | func (c CompositeController) EnsureRoutes(ctx context.Context, strategy *v1beta1.TrafficRoutingStrategy) (bool, error) {
44 | done := true
45 | for _, provider := range c {
46 | if innerDone, innerErr := provider.EnsureRoutes(ctx, strategy); innerErr != nil {
47 | return false, innerErr
48 | } else if !innerDone {
49 | done = false
50 | }
51 | }
52 | return done, nil
53 | }
54 |
55 | func (c CompositeController) Finalise(ctx context.Context) (bool, error) {
56 | modified := false
57 | errList := field.ErrorList{}
58 | for _, provider := range c {
59 | if updated, innerErr := provider.Finalise(ctx); innerErr != nil {
60 | errList = append(errList, field.InternalError(field.NewPath("FinalizeChildNetworkProvider"), innerErr))
61 | } else if updated {
62 | modified = true
63 | }
64 | }
65 | return modified, errList.ToAggregate()
66 | }
67 |
--------------------------------------------------------------------------------
/pkg/trafficrouting/network/customNetworkProvider/lua_configuration/networking.error.io/LuaError/trafficRouting.lua:
--------------------------------------------------------------------------------
1 | -- the lua script contains error
2 | local spec = obj.error
3 | return sepc.error
--------------------------------------------------------------------------------
/pkg/trafficrouting/network/customNetworkProvider/lua_configuration/networking.istio.io/DestinationRule/trafficRouting.lua:
--------------------------------------------------------------------------------
1 | local spec = obj.data.spec
2 | local canary = {}
3 | canary.labels = {}
4 | canary.name = "canary"
5 | local podLabelKey = "istio.service.tag"
6 | canary.labels[podLabelKey] = "gray"
7 | table.insert(spec.subsets, canary)
8 | return obj.data
--------------------------------------------------------------------------------
/pkg/trafficrouting/network/interface.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package network
18 |
19 | import (
20 | "context"
21 |
22 | "github.com/openkruise/rollouts/api/v1beta1"
23 | )
24 |
25 | // NetworkProvider common function across all TrafficRouting implementation
26 | type NetworkProvider interface {
27 | // Initialize only determine if the network resources(ingress & gateway api) exist.
28 | // If error is nil, then the network resources exist.
29 | Initialize(ctx context.Context) error
30 | // EnsureRoutes check and set routes, e.g. canary weight and match conditions.
31 | // 1. Canary weight specifies the relative proportion of traffic to be forwarded to the canary service within the range of [0,100]
32 | // 2. Match conditions indicates rules to be satisfied for A/B testing scenarios, such as header, cookie, queryParams etc.
33 | // Return true if and only if the route resources have been correctly updated and does not change in this round of reconciliation.
34 | // Otherwise, return false to wait for the eventual consistency.
35 | EnsureRoutes(ctx context.Context, strategy *v1beta1.TrafficRoutingStrategy) (bool, error)
36 | // Finalise will do some cleanup work after the canary rollout complete, such as delete canary ingress.
37 | // if error is nil, the return bool value means if the resources are modified
38 | Finalise(ctx context.Context) (bool, error)
39 | }
40 |
--------------------------------------------------------------------------------
/pkg/util/client/client.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package client
18 |
19 | import (
20 | "fmt"
21 |
22 | kruiseclientset "github.com/openkruise/kruise-api/client/clientset/versioned"
23 | "k8s.io/apimachinery/pkg/runtime"
24 | "k8s.io/client-go/discovery"
25 | kubeclientset "k8s.io/client-go/kubernetes"
26 | "k8s.io/client-go/rest"
27 | )
28 |
29 | // GenericClientset defines a generic client
30 | type GenericClientset struct {
31 | DiscoveryClient discovery.DiscoveryInterface
32 | KubeClient kubeclientset.Interface
33 | KruiseClient kruiseclientset.Interface
34 | }
35 |
36 | var (
37 | cfg *rest.Config
38 | defaultGenericClient *GenericClientset
39 | )
40 |
41 | // newForConfig creates a new Clientset for the given config.
42 | func newForConfig(c *rest.Config) (*GenericClientset, error) {
43 | cWithProtobuf := rest.CopyConfig(c)
44 | cWithProtobuf.ContentType = runtime.ContentTypeProtobuf
45 | discoveryClient, err := discovery.NewDiscoveryClientForConfig(cWithProtobuf)
46 | if err != nil {
47 | return nil, err
48 | }
49 | kubeClient, err := kubeclientset.NewForConfig(cWithProtobuf)
50 | if err != nil {
51 | return nil, err
52 | }
53 | kruiseClient, err := kruiseclientset.NewForConfig(c)
54 | if err != nil {
55 | return nil, err
56 | }
57 | return &GenericClientset{
58 | DiscoveryClient: discoveryClient,
59 | KubeClient: kubeClient,
60 | KruiseClient: kruiseClient,
61 | }, nil
62 | }
63 |
64 | // NewRegistry creates clientset by client-go
65 | func NewRegistry(c *rest.Config) error {
66 | var err error
67 | defaultGenericClient, err = newForConfig(c)
68 | if err != nil {
69 | return err
70 | }
71 | cfgCopy := *c
72 | cfg = &cfgCopy
73 | return nil
74 | }
75 |
76 | // GetGenericClient returns default clientset
77 | func GetGenericClient() *GenericClientset {
78 | return defaultGenericClient
79 | }
80 |
81 | // GetGenericClientWithName returns clientset with given name as user-agent
82 | func GetGenericClientWithName(name string) *GenericClientset {
83 | if cfg == nil {
84 | return nil
85 | }
86 | newCfg := *cfg
87 | newCfg.UserAgent = fmt.Sprintf("%s/%s", cfg.UserAgent, name)
88 | clientset, _ := newForConfig(cfg)
89 | return clientset
90 | }
91 |
--------------------------------------------------------------------------------
/pkg/util/configuration/configuration.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package configuration
18 |
19 | import (
20 | "context"
21 | "fmt"
22 |
23 | "github.com/openkruise/rollouts/pkg/util"
24 | corev1 "k8s.io/api/core/v1"
25 | "k8s.io/apimachinery/pkg/api/errors"
26 | "sigs.k8s.io/controller-runtime/pkg/client"
27 | )
28 |
29 | const (
30 | // kruise rollout configmap name
31 | RolloutConfigurationName = "kruise-rollout-configuration"
32 |
33 | LuaTrafficRoutingIngressTypePrefix = "lua.traffic.routing.ingress"
34 | LuaTrafficRoutingCustomTypePrefix = "lua.traffic.routing"
35 | )
36 |
37 | func GetTrafficRoutingIngressLuaScript(client client.Client, iType string) (string, error) {
38 | data, err := getRolloutConfiguration(client)
39 | if err != nil {
40 | return "", err
41 | } else if len(data) == 0 {
42 | return "", nil
43 | }
44 | value, ok := data[fmt.Sprintf("%s.%s", LuaTrafficRoutingIngressTypePrefix, iType)]
45 | if !ok {
46 | return "", nil
47 | }
48 | return value, nil
49 | }
50 |
51 | func getRolloutConfiguration(c client.Client) (map[string]string, error) {
52 | cfg := &corev1.ConfigMap{}
53 | err := c.Get(context.TODO(), client.ObjectKey{Namespace: util.GetRolloutNamespace(), Name: RolloutConfigurationName}, cfg)
54 | if err != nil {
55 | if errors.IsNotFound(err) {
56 | return nil, nil
57 | }
58 | return nil, err
59 | }
60 | return cfg.Data, nil
61 | }
62 |
--------------------------------------------------------------------------------
/pkg/util/constant.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package util
18 |
19 | import (
20 | "fmt"
21 |
22 | "github.com/openkruise/rollouts/api/v1alpha1"
23 | )
24 |
25 | // For Rollout and BatchRelease
26 | const (
27 | // BatchReleaseControlAnnotation is controller info about batchRelease when rollout
28 | BatchReleaseControlAnnotation = "batchrelease.rollouts.kruise.io/control-info"
29 | // InRolloutProgressingAnnotation marks workload as entering the rollout progressing process
30 | // and does not allow paused=false during this process. However, blueGreen is an exception,
31 | // which allows paused=false during progressing.
32 | InRolloutProgressingAnnotation = "rollouts.kruise.io/in-progressing"
33 | // RolloutHashAnnotation record observed rollout spec hash
34 | RolloutHashAnnotation = "rollouts.kruise.io/hash"
35 | )
36 |
37 | // For Workloads
38 | const (
39 | // CanaryDeploymentLabel is to label canary deployment that is created by batchRelease controller
40 | CanaryDeploymentLabel = "rollouts.kruise.io/canary-deployment"
41 | // CanaryDeploymentFinalizer is a finalizer to resources patched by batchRelease controller
42 | CanaryDeploymentFinalizer = "finalizer.rollouts.kruise.io/batch-release"
43 | // KruiseRolloutFinalizer is a finalizer for deployment/service/ingress/gateway/etc
44 | KruiseRolloutFinalizer = "rollouts.kruise.io/rollout"
45 | // WorkloadTypeLabel is a label to identify workload type
46 | WorkloadTypeLabel = "rollouts.kruise.io/workload-type"
47 | // DeploymentRevisionAnnotation is the revision annotation of a deployment's replica sets which records its rollout sequence
48 | DeploymentRevisionAnnotation = "deployment.kubernetes.io/revision"
49 | )
50 |
51 | const (
52 | TrafficRoutingFinalizer = "rollouts.kruise.io/trafficrouting"
53 | )
54 |
55 | // For Pods
56 | const (
57 | // NoNeedUpdatePodLabel will be patched to pod when rollback in batches if the pods no need to rollback
58 | NoNeedUpdatePodLabel = "rollouts.kruise.io/no-need-update"
59 | )
60 |
61 | // For Others
62 | const (
63 | // We omit vowels from the set of available characters to reduce the chances
64 | // of "bad words" being formed.
65 | alphanums = "bcdfghjklmnpqrstvwxz2456789"
66 |
67 | // CloneSetType DeploymentType and StatefulSetType are values to WorkloadTypeLabel
68 | CloneSetType WorkloadType = "cloneset"
69 | DeploymentType WorkloadType = "deployment"
70 | StatefulSetType WorkloadType = "statefulset"
71 | DaemonSetType WorkloadType = "daemonset"
72 |
73 | AddFinalizerOpType FinalizerOpType = "Add"
74 | RemoveFinalizerOpType FinalizerOpType = "Remove"
75 | )
76 |
77 | type WorkloadType string
78 |
79 | type FinalizerOpType string
80 |
81 | func ProgressingRolloutFinalizer(name string) string {
82 | return fmt.Sprintf("%s/%s", v1alpha1.ProgressingRolloutFinalizerPrefix, name)
83 | }
84 |
--------------------------------------------------------------------------------
/pkg/util/errors/types.go:
--------------------------------------------------------------------------------
1 | package errors
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | )
7 |
8 | // RetryError represents a benign error that can be handled or ignored by the caller.
9 | // It encapsulates information that is non-critical and does not require immediate attention.
10 | type RetryError struct {
11 | Err error
12 | }
13 |
14 | // Error implements the error interface for RetryError.
15 | // It returns the error message of the encapsulated error or a default message.
16 | func (e *RetryError) Error() string {
17 | if e.Err != nil {
18 | return fmt.Sprintf("[retry]: %s", e.Err.Error())
19 | }
20 | return "retry error"
21 | }
22 |
23 | // NewRetryError creates a new instance of RetryError.
24 | func NewRetryError(err error) *RetryError {
25 | return &RetryError{Err: err}
26 | }
27 |
28 | func IsRetryError(err error) bool {
29 | var re *RetryError
30 | return errors.As(err, &re)
31 | }
32 |
33 | func AsRetryError(err error, target **RetryError) bool {
34 | return errors.As(err, target)
35 | }
36 |
37 | // BadRequestError represents a fatal error that requires special handling.
38 | // Such errors are critical and may necessitate logging, alerts, or even program termination.
39 | type BadRequestError struct {
40 | Err error
41 | }
42 |
43 | // Error implements the error interface for BadRequestError.
44 | // It returns the error message of the encapsulated error or a default message.
45 | func (e *BadRequestError) Error() string {
46 | if e.Err != nil {
47 | return e.Err.Error()
48 | }
49 | return "fatal error"
50 | }
51 |
52 | // NewBadRequestError creates a new instance of BadRequestError.
53 | // It encapsulates the provided error, marking it as critical.
54 | func NewBadRequestError(err error) *BadRequestError {
55 | return &BadRequestError{Err: err}
56 | }
57 |
58 | // IsBadRequest checks whether the provided error is of type BadRequestError.
59 | // It returns true if the error is a BadRequestError or wraps a BadRequestError, false otherwise.
60 | func IsBadRequest(err error) bool {
61 | var brErr *BadRequestError
62 | return AsBadRequest(err, &brErr)
63 | }
64 |
65 | // AsBadRequest attempts to cast the provided error to a BadRequestError.
66 | // It returns true if the casting is successful, allowing the caller to handle it accordingly.
67 | func AsBadRequest(err error, target **BadRequestError) bool {
68 | return errors.As(err, target)
69 | }
70 |
--------------------------------------------------------------------------------
/pkg/util/expectation/resource_expectations_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package expectations
18 |
19 | import (
20 | "testing"
21 | )
22 |
23 | func TestResourceExpectations(t *testing.T) {
24 | e := NewResourceExpectations()
25 | controllerKey01 := "default/cs01"
26 | controllerKey02 := "default/cs02"
27 | pod01 := "pod01"
28 | pod02 := "pod02"
29 |
30 | e.Expect(controllerKey01, Create, pod01)
31 | e.Expect(controllerKey01, Create, pod02)
32 | e.Expect(controllerKey01, Delete, pod01)
33 | if ok, _, _ := e.SatisfiedExpectations(controllerKey01); ok {
34 | t.Fatalf("expected not satisfied")
35 | }
36 |
37 | e.Observe(controllerKey01, Create, pod02)
38 | e.Observe(controllerKey01, Create, pod01)
39 | if ok, _, _ := e.SatisfiedExpectations(controllerKey01); ok {
40 | t.Fatalf("expected not satisfied")
41 | }
42 |
43 | e.Observe(controllerKey02, Delete, pod01)
44 | if ok, _, _ := e.SatisfiedExpectations(controllerKey01); ok {
45 | t.Fatalf("expected not satisfied")
46 | }
47 |
48 | e.Observe(controllerKey01, Delete, pod01)
49 | if ok, _, _ := e.SatisfiedExpectations(controllerKey01); !ok {
50 | t.Fatalf("expected satisfied")
51 | }
52 |
53 | e.Observe(controllerKey01, Create, pod01)
54 | e.Observe(controllerKey01, Create, pod02)
55 | e.DeleteExpectations(controllerKey01)
56 | if ok, _, _ := e.SatisfiedExpectations(controllerKey01); !ok {
57 | t.Fatalf("expected satisfied")
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/pkg/util/feature/feature_gate.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 | package feature
17 |
18 | import (
19 | "k8s.io/component-base/featuregate"
20 | )
21 |
22 | var (
23 | // DefaultMutableFeatureGate is a mutable version of DefaultFeatureGate.
24 | // Only top-level commands/options setup and the k8s.io/component-base/featuregate/testing package should make use of this.
25 | // Tests that need to modify feature gates for the duration of their test should use:
26 | // defer featuregatetesting.SetFeatureGateDuringTest(t, utilfeature.DefaultFeatureGate, features., )()
27 | DefaultMutableFeatureGate featuregate.MutableFeatureGate = featuregate.NewFeatureGate()
28 |
29 | // DefaultFeatureGate is a shared global FeatureGate.
30 | // Top-level commands/options setup that needs to modify this feature gate should use DefaultMutableFeatureGate.
31 | DefaultFeatureGate featuregate.FeatureGate = DefaultMutableFeatureGate
32 | )
33 |
--------------------------------------------------------------------------------
/pkg/util/grace/grace_wrapper.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package grace
18 |
19 | import (
20 | "time"
21 |
22 | "k8s.io/klog/v2"
23 | )
24 |
25 | func RunWithGraceSeconds(key, action string, graceSeconds int32, f func() (bool, error)) (bool, time.Duration, error) {
26 | return runWithGraceSeconds(DefaultGraceExpectations, key, action, graceSeconds, f)
27 | }
28 |
29 | // Returns:
30 | // - error: If the passed function itself returns an error, the error is directly returned.
31 | // - bool: Tells the caller whether a retry is needed.
32 | // - time.Duration: The remaining time to wait. (only valid when error is nil)
33 | // The passed function `f` needs to be idempotent.
34 | // - The bool value returned by `f` indicates whether the related resources were updated in this call.
35 | // - If resources were updated, we need to wait for `graceSeconds`.
36 | func runWithGraceSeconds(e *realGraceExpectations, key, action string, graceSeconds int32, f func() (bool, error)) (bool, time.Duration, error) {
37 | modified, err := f()
38 | if err != nil {
39 | return true, 0, err
40 | }
41 | // if user specify 0, it means no need to wait
42 | if graceSeconds == 0 {
43 | e.Observe(key, Action(action))
44 | return false, 0, nil
45 | }
46 | // if f return true, it means some resources are modified by f in this call
47 | // we need to wait a grace period
48 | if modified {
49 | e.Expect(key, Action(action))
50 | klog.Infof("function return modified, expectation created, key: %s, action: %s, expect to wait %d seconds", key, action, graceSeconds)
51 | return true, time.Duration(graceSeconds) * time.Second, nil
52 | }
53 | if satisfied, remaining := e.SatisfiedExpectations(key, Action(action), graceSeconds); !satisfied {
54 | klog.Infof("expectation unsatisfied, key: %s, action: %s, remaining/graceSeconds: %.1f/%d", key, action, remaining.Seconds(), graceSeconds)
55 | return true, remaining, nil
56 | }
57 | e.Observe(key, Action(action))
58 | klog.Infof("expectation satisfied, key: %s, action: %s", key, action)
59 | return false, 0, nil
60 | }
61 |
62 | // warning: only used for test
63 | func ResetExpectations() {
64 | DefaultGraceExpectations.resetExpectations()
65 | }
66 |
--------------------------------------------------------------------------------
/pkg/util/lua_configuration.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package util
18 |
19 | import (
20 | "io/ioutil"
21 | "os"
22 | "path/filepath"
23 |
24 | "k8s.io/klog/v2"
25 | )
26 |
27 | // patch -> file.Content,
28 | // for example: lua_configuration/trafficrouting_ingress/ingress.lua -> ingress.lua content
29 | var luaConfigurationList map[string]string
30 |
31 | func init() {
32 | luaConfigurationList = map[string]string{}
33 | _ = filepath.Walk("./lua_configuration", func(path string, f os.FileInfo, err error) error {
34 | if err != nil {
35 | klog.Warningf("filepath walk ./lua_configuration failed: %s", err.Error())
36 | return err
37 | }
38 | if f.IsDir() {
39 | return nil
40 | }
41 | var data []byte
42 | data, err = ioutil.ReadFile(filepath.Clean(path))
43 | if err != nil {
44 | klog.Errorf("Read file %s failed: %s", path, err.Error())
45 | return err
46 | }
47 | luaConfigurationList[path] = string(data)
48 | return nil
49 | })
50 | klog.Infof("Init Lua Configuration(%s)", DumpJSON(luaConfigurationList))
51 | }
52 |
53 | func GetLuaConfigurationContent(key string) string {
54 | return luaConfigurationList[key]
55 | }
56 |
--------------------------------------------------------------------------------
/pkg/util/luamanager/lua.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package luamanager
18 |
19 | import (
20 | "context"
21 | "encoding/json"
22 | "time"
23 |
24 | lua "github.com/yuin/gopher-lua"
25 | "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured"
26 | )
27 |
28 | type LuaManager struct{}
29 |
30 | func (m *LuaManager) RunLuaScript(obj *unstructured.Unstructured, script string) (*lua.LState, error) {
31 | l := lua.NewState(lua.Options{SkipOpenLibs: true})
32 | defer l.Close()
33 | for _, pair := range []struct {
34 | n string
35 | f lua.LGFunction
36 | }{
37 | {lua.MathLibName, lua.OpenMath},
38 | {lua.BaseLibName, lua.OpenBase},
39 | {lua.TabLibName, lua.OpenTable},
40 | {lua.StringLibName, lua.OpenString},
41 | {JsonLibName, OpenJson},
42 | } {
43 | if err := l.CallByParam(lua.P{
44 | Fn: l.NewFunction(pair.f),
45 | NRet: 0,
46 | Protect: true,
47 | }, lua.LString(pair.n)); err != nil {
48 | return nil, err
49 | }
50 | }
51 | ctx, cancel := context.WithTimeout(context.Background(), 1*time.Second)
52 | defer cancel()
53 | l.SetContext(ctx)
54 | objectValue := decodeValue(l, obj.Object)
55 | l.SetGlobal("obj", objectValue)
56 | err := l.DoString(script)
57 | return l, err
58 | }
59 |
60 | func decodeValue(L *lua.LState, value interface{}) lua.LValue {
61 | switch converted := value.(type) {
62 | case bool:
63 | return lua.LBool(converted)
64 | case float64:
65 | return lua.LNumber(converted)
66 | case string:
67 | return lua.LString(converted)
68 | case json.Number:
69 | return lua.LString(converted)
70 | case int:
71 | return lua.LNumber(converted)
72 | case int32:
73 | return lua.LNumber(converted)
74 | case int64:
75 | return lua.LNumber(converted)
76 | case []interface{}:
77 | arr := L.CreateTable(len(converted), 0)
78 | for _, item := range converted {
79 | arr.Append(decodeValue(L, item))
80 | }
81 | return arr
82 | case map[string]interface{}:
83 | tbl := L.CreateTable(0, len(converted))
84 | for key, item := range converted {
85 | tbl.RawSetH(lua.LString(key), decodeValue(L, item))
86 | }
87 | return tbl
88 | case nil:
89 | return lua.LNil
90 | }
91 |
92 | return lua.LNil
93 | }
94 |
--------------------------------------------------------------------------------
/pkg/util/meta.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package util
18 |
19 | import "os"
20 |
21 | func GetRolloutNamespace() string {
22 | if ns := os.Getenv("POD_NAMESPACE"); len(ns) > 0 {
23 | return ns
24 | }
25 | return "kruise-rollout"
26 | }
27 |
--------------------------------------------------------------------------------
/pkg/util/patch/patch_utils_test.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package patch
18 |
19 | import (
20 | "fmt"
21 | "reflect"
22 | "testing"
23 |
24 | "github.com/openkruise/rollouts/pkg/util"
25 | v1 "k8s.io/api/core/v1"
26 | )
27 |
28 | func TestCommonPatch(t *testing.T) {
29 | condition := v1.PodCondition{Type: v1.ContainersReady, Status: v1.ConditionTrue, Message: "just for test"}
30 | patchReq := NewStrategicPatch().
31 | AddFinalizer("new-finalizer").
32 | RemoveFinalizer("old-finalizer").
33 | InsertLabel("new-label", "foo1").
34 | DeleteLabel("old-label").
35 | InsertAnnotation("new-annotation", "foo2").
36 | DeleteAnnotation("old-annotation").
37 | UpdatePodCondition(condition)
38 |
39 | expectedPatchBody := fmt.Sprintf(`{"metadata":{"$deleteFromPrimitiveList/finalizers":["old-finalizer"],"annotations":{"new-annotation":"foo2","old-annotation":null},"finalizers":["new-finalizer"],"labels":{"new-label":"foo1","old-label":null}},"status":{"conditions":[%s]}}`, util.DumpJSON(condition))
40 |
41 | if !reflect.DeepEqual(patchReq.String(), expectedPatchBody) {
42 | t.Fatalf("Not equal: \n%s \n%s", expectedPatchBody, patchReq.String())
43 | }
44 |
45 | }
46 |
--------------------------------------------------------------------------------
/pkg/util/ratelimiter/rate_limiter.go:
--------------------------------------------------------------------------------
1 | package ratelimiter
2 |
3 | import (
4 | "flag"
5 | "time"
6 |
7 | "golang.org/x/time/rate"
8 | "k8s.io/client-go/util/workqueue"
9 | )
10 |
11 | func init() {
12 | flag.DurationVar(&baseDelay, "rate-limiter-base-delay", time.Millisecond*5, "The base delay for rate limiter. Defaults 5ms")
13 | flag.DurationVar(&maxDelay, "rate-limiter-max-delay", time.Second*1000, "The max delay for rate limiter. Defaults 1000s")
14 | flag.IntVar(&qps, "rate-limiter-qps", 10, "The qps for rate limier. Defaults 10")
15 | flag.IntVar(&bucketSize, "rate-limiter-bucket-size", 100, "The bucket size for rate limier. Defaults 100")
16 | }
17 |
18 | var baseDelay, maxDelay time.Duration
19 | var qps, bucketSize int
20 |
21 | func DefaultControllerRateLimiter() workqueue.RateLimiter {
22 | return workqueue.NewMaxOfRateLimiter(
23 | workqueue.NewItemExponentialFailureRateLimiter(baseDelay, maxDelay),
24 | &workqueue.BucketRateLimiter{Limiter: rate.NewLimiter(rate.Limit(qps), bucketSize)},
25 | )
26 | }
27 |
--------------------------------------------------------------------------------
/pkg/util/slices_utils.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 | Licensed under the Apache License, Version 2.0 (the "License");
4 | you may not use this file except in compliance with the License.
5 | You may obtain a copy of the License at
6 | http://www.apache.org/licenses/LICENSE-2.0
7 | Unless required by applicable law or agreed to in writing, software
8 | distributed under the License is distributed on an "AS IS" BASIS,
9 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10 | See the License for the specific language governing permissions and
11 | limitations under the License.
12 | */
13 |
14 | package util
15 |
16 | import (
17 | "github.com/openkruise/rollouts/api/v1beta1"
18 | )
19 |
20 | func FilterHttpRouteMatch(s []v1beta1.HttpRouteMatch, f func(v1beta1.HttpRouteMatch) bool) []v1beta1.HttpRouteMatch {
21 | s2 := make([]v1beta1.HttpRouteMatch, 0, len(s))
22 | for _, e := range s {
23 | if f(e) {
24 | s2 = append(s2, e)
25 | }
26 | }
27 | return s2
28 | }
29 |
--------------------------------------------------------------------------------
/pkg/webhook/add_rollout.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package webhook
18 |
19 | import (
20 | "github.com/openkruise/rollouts/pkg/webhook/rollout/validating"
21 | )
22 |
23 | func init() {
24 | addHandlers(validating.HandlerMap)
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/webhook/add_workload.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2022 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package webhook
18 |
19 | import (
20 | "github.com/openkruise/rollouts/pkg/webhook/workload/mutating"
21 | )
22 |
23 | func init() {
24 | addHandlers(mutating.HandlerMap)
25 | }
26 |
--------------------------------------------------------------------------------
/pkg/webhook/rollout/validating/webhooks.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package validating
18 |
19 | import (
20 | "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
21 | )
22 |
23 | // +kubebuilder:webhook:path=/validate-rollouts-kruise-io-rollout,mutating=false,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=rollouts.kruise.io,resources=rollouts,verbs=create;update,versions=v1alpha1;v1beta1,name=vrollout.kb.io
24 |
25 | var (
26 | // HandlerMap contains admission webhook handlers
27 | HandlerMap = map[string]admission.Handler{
28 | "validate-rollouts-kruise-io-rollout": &RolloutCreateUpdateHandler{},
29 | }
30 | )
31 |
--------------------------------------------------------------------------------
/pkg/webhook/util/crd/crd.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2023 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package crd
18 |
19 | import (
20 | "context"
21 | "fmt"
22 | "reflect"
23 |
24 | rolloutapi "github.com/openkruise/rollouts/api"
25 | webhookutil "github.com/openkruise/rollouts/pkg/webhook/util"
26 | apiextensionsv1 "k8s.io/apiextensions-apiserver/pkg/apis/apiextensions/v1"
27 | apiextensionsclientset "k8s.io/apiextensions-apiserver/pkg/client/clientset/clientset"
28 | apiextensionslisters "k8s.io/apiextensions-apiserver/pkg/client/listers/apiextensions/v1"
29 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
30 | "k8s.io/apimachinery/pkg/labels"
31 | "k8s.io/apimachinery/pkg/runtime"
32 | "k8s.io/apimachinery/pkg/runtime/schema"
33 | utilruntime "k8s.io/apimachinery/pkg/util/runtime"
34 | )
35 |
36 | var (
37 | scheme = runtime.NewScheme()
38 | )
39 |
40 | func init() {
41 | utilruntime.Must(rolloutapi.AddToScheme(scheme))
42 | }
43 |
44 | func Ensure(client apiextensionsclientset.Interface, lister apiextensionslisters.CustomResourceDefinitionLister, caBundle []byte) error {
45 | crdList, err := lister.List(labels.Everything())
46 | if err != nil {
47 | return fmt.Errorf("failed to list crds: %v", err)
48 | }
49 |
50 | webhookConfig := apiextensionsv1.WebhookClientConfig{
51 | CABundle: caBundle,
52 | }
53 | path := "/convert"
54 | if host := webhookutil.GetHost(); len(host) > 0 {
55 | url := fmt.Sprintf("https://%s:%d%s", host, webhookutil.GetPort(), path)
56 | webhookConfig.URL = &url
57 | } else {
58 | var port int32 = 443
59 | webhookConfig.Service = &apiextensionsv1.ServiceReference{
60 | Namespace: webhookutil.GetNamespace(),
61 | Name: webhookutil.GetServiceName(),
62 | Port: &port,
63 | Path: &path,
64 | }
65 | }
66 |
67 | for _, crd := range crdList {
68 | if len(crd.Spec.Versions) == 0 || crd.Spec.Conversion == nil || crd.Spec.Conversion.Strategy != apiextensionsv1.WebhookConverter {
69 | continue
70 | }
71 | if !scheme.Recognizes(schema.GroupVersionKind{Group: crd.Spec.Group, Version: crd.Spec.Versions[0].Name, Kind: crd.Spec.Names.Kind}) {
72 | continue
73 | }
74 |
75 | if crd.Spec.Conversion.Webhook == nil || !reflect.DeepEqual(crd.Spec.Conversion.Webhook.ClientConfig, webhookConfig) {
76 | newCRD := crd.DeepCopy()
77 | newCRD.Spec.Conversion.Webhook = &apiextensionsv1.WebhookConversion{
78 | ClientConfig: webhookConfig.DeepCopy(),
79 | ConversionReviewVersions: []string{"v1", "v1beta1"},
80 | }
81 | if _, err := client.ApiextensionsV1().CustomResourceDefinitions().Update(context.TODO(), newCRD, metav1.UpdateOptions{}); err != nil {
82 | return fmt.Errorf("failed to update CRD %s: %v", newCRD.Name, err)
83 | }
84 | }
85 | }
86 | return nil
87 | }
88 |
--------------------------------------------------------------------------------
/pkg/webhook/util/generator/certgenerator.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2020 The Kruise Authors.
3 | Copyright 2018 The Kubernetes Authors.
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 |
9 | http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
16 | */
17 |
18 | package generator
19 |
20 | // Artifacts hosts a private key, its corresponding serving certificate and
21 | // the CA certificate that signs the serving certificate.
22 | type Artifacts struct {
23 | // PEM encoded private key
24 | Key []byte
25 | // PEM encoded serving certificate
26 | Cert []byte
27 | // PEM encoded CA private key
28 | CAKey []byte
29 | // PEM encoded CA certificate
30 | CACert []byte
31 | // Resource version of the certs
32 | ResourceVersion string
33 | }
34 |
35 | // CertGenerator is an interface to provision the serving certificate.
36 | type CertGenerator interface {
37 | // Generate returns a Artifacts struct.
38 | Generate(CommonName string) (*Artifacts, error)
39 | // SetCA sets the PEM-encoded CA private key and CA cert for signing the generated serving cert.
40 | SetCA(caKey, caCert []byte)
41 | }
42 |
--------------------------------------------------------------------------------
/pkg/webhook/util/generator/fake/certgenerator.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2020 The Kruise Authors.
3 | Copyright 2018 The Kubernetes Authors.
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 |
9 | http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
16 | */
17 |
18 | package fake
19 |
20 | import (
21 | "bytes"
22 | "fmt"
23 |
24 | "github.com/openkruise/rollouts/pkg/webhook/util/generator"
25 | )
26 |
27 | // CertGenerator is a certGenerator for testing.
28 | type CertGenerator struct {
29 | CAKey []byte
30 | CACert []byte
31 | DNSNameToCertArtifacts map[string]*generator.Artifacts
32 | }
33 |
34 | var _ generator.CertGenerator = &CertGenerator{}
35 |
36 | // SetCA sets the PEM-encoded CA private key and CA cert for signing the generated serving cert.
37 | func (cp *CertGenerator) SetCA(CAKey, CACert []byte) {
38 | cp.CAKey = CAKey
39 | cp.CACert = CACert
40 | }
41 |
42 | // Generate generates certificates by matching a common name.
43 | func (cp *CertGenerator) Generate(commonName string) (*generator.Artifacts, error) {
44 | certs, found := cp.DNSNameToCertArtifacts[commonName]
45 | if !found {
46 | return nil, fmt.Errorf("failed to find common name %q in the certGenerator", commonName)
47 | }
48 | if cp.CAKey != nil && cp.CACert != nil &&
49 | !bytes.Contains(cp.CAKey, []byte("invalid")) && !bytes.Contains(cp.CACert, []byte("invalid")) {
50 | certs.CAKey = cp.CAKey
51 | certs.CACert = cp.CACert
52 | }
53 | return certs, nil
54 | }
55 |
--------------------------------------------------------------------------------
/pkg/webhook/util/generator/util.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2020 The Kruise Authors.
3 | Copyright 2018 The Kubernetes Authors.
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 |
9 | http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
16 | */
17 |
18 | package generator
19 |
20 | import (
21 | "crypto/tls"
22 | "crypto/x509"
23 | "encoding/pem"
24 | "time"
25 | )
26 |
27 | // ValidCACert think cert and key are valid if they meet the following requirements:
28 | // - key and cert are valid pair
29 | // - caCert is the root ca of cert
30 | // - cert is for dnsName
31 | // - cert won't expire before time
32 | func ValidCACert(key, cert, caCert []byte, dnsName string, time time.Time) bool {
33 | if len(key) == 0 || len(cert) == 0 || len(caCert) == 0 {
34 | return false
35 | }
36 | // Verify key and cert are valid pair
37 | _, err := tls.X509KeyPair(cert, key)
38 | if err != nil {
39 | return false
40 | }
41 |
42 | // Verify cert is valid for at least 1 year.
43 | pool := x509.NewCertPool()
44 | if !pool.AppendCertsFromPEM(caCert) {
45 | return false
46 | }
47 | block, _ := pem.Decode(cert)
48 | if block == nil {
49 | return false
50 | }
51 | c, err := x509.ParseCertificate(block.Bytes)
52 | if err != nil {
53 | return false
54 | }
55 | ops := x509.VerifyOptions{
56 | DNSName: dnsName,
57 | Roots: pool,
58 | CurrentTime: time,
59 | }
60 | _, err = c.Verify(ops)
61 | return err == nil
62 | }
63 |
--------------------------------------------------------------------------------
/pkg/webhook/util/matcher.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2017 The Kubernetes Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package util
18 |
19 | import (
20 | "strings"
21 |
22 | v1 "k8s.io/api/admissionregistration/v1"
23 | metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
24 | "k8s.io/apimachinery/pkg/runtime/schema"
25 | "k8s.io/apiserver/pkg/admission"
26 | )
27 |
28 | // Matcher determines if the Attr matches the Rule.
29 | type Matcher struct {
30 | Rule v1.RuleWithOperations
31 | Attr admission.Attributes
32 | }
33 |
34 | // Matches returns if the Attr matches the Rule.
35 | func (r *Matcher) Matches() bool {
36 | return r.scope() &&
37 | r.operation() &&
38 | r.group() &&
39 | r.version() &&
40 | r.resource()
41 | }
42 |
43 | func exactOrWildcard(items []string, requested string) bool {
44 | for _, item := range items {
45 | if item == "*" {
46 | return true
47 | }
48 | if item == requested {
49 | return true
50 | }
51 | }
52 |
53 | return false
54 | }
55 |
56 | var namespaceResource = schema.GroupVersionResource{Group: "", Version: "v1", Resource: "namespaces"}
57 |
58 | func (r *Matcher) scope() bool {
59 | if r.Rule.Scope == nil || *r.Rule.Scope == v1.AllScopes {
60 | return true
61 | }
62 | // attr.GetNamespace() is set to the name of the namespace for requests of the namespace object itself.
63 | switch *r.Rule.Scope {
64 | case v1.NamespacedScope:
65 | // first make sure that we are not requesting a namespace object (namespace objects are cluster-scoped)
66 | return r.Attr.GetResource() != namespaceResource && r.Attr.GetNamespace() != metav1.NamespaceNone
67 | case v1.ClusterScope:
68 | // also return true if the request is for a namespace object (namespace objects are cluster-scoped)
69 | return r.Attr.GetResource() == namespaceResource || r.Attr.GetNamespace() == metav1.NamespaceNone
70 | default:
71 | return false
72 | }
73 | }
74 |
75 | func (r *Matcher) group() bool {
76 | return exactOrWildcard(r.Rule.APIGroups, r.Attr.GetResource().Group)
77 | }
78 |
79 | func (r *Matcher) version() bool {
80 | return exactOrWildcard(r.Rule.APIVersions, r.Attr.GetResource().Version)
81 | }
82 |
83 | func (r *Matcher) operation() bool {
84 | attrOp := r.Attr.GetOperation()
85 | for _, op := range r.Rule.Operations {
86 | if op == v1.OperationAll {
87 | return true
88 | }
89 | // The constants are the same such that this is a valid cast (and this
90 | // is tested).
91 | if op == v1.OperationType(attrOp) {
92 | return true
93 | }
94 | }
95 | return false
96 | }
97 |
98 | func splitResource(resSub string) (res, sub string) {
99 | parts := strings.SplitN(resSub, "/", 2)
100 | if len(parts) == 2 {
101 | return parts[0], parts[1]
102 | }
103 | return parts[0], ""
104 | }
105 |
106 | func (r *Matcher) resource() bool {
107 | opRes, opSub := r.Attr.GetResource().Resource, r.Attr.GetSubresource()
108 | for _, res := range r.Rule.Resources {
109 | res, sub := splitResource(res)
110 | resMatch := res == "*" || res == opRes
111 | subMatch := sub == "*" || sub == opSub
112 | if resMatch && subMatch {
113 | return true
114 | }
115 | }
116 | return false
117 | }
118 |
--------------------------------------------------------------------------------
/pkg/webhook/util/util.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2020 The rollout Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package util
18 |
19 | import (
20 | "os"
21 | "strconv"
22 |
23 | "k8s.io/klog/v2"
24 | )
25 |
26 | func GetHost() string {
27 | return os.Getenv("WEBHOOK_HOST")
28 | }
29 |
30 | func GetNamespace() string {
31 | if ns := os.Getenv("POD_NAMESPACE"); len(ns) > 0 {
32 | return ns
33 | }
34 | return "kruise-rollout"
35 | }
36 |
37 | func GetSecretName() string {
38 | if name := os.Getenv("SECRET_NAME"); len(name) > 0 {
39 | return name
40 | }
41 | return "kruise-rollout-webhook-certs"
42 | }
43 |
44 | func GetServiceName() string {
45 | if name := os.Getenv("SERVICE_NAME"); len(name) > 0 {
46 | return name
47 | }
48 | return "kruise-rollout-webhook-service"
49 | }
50 |
51 | func GetPort() int {
52 | port := 9876
53 | if p := os.Getenv("WEBHOOK_PORT"); len(p) > 0 {
54 | if p, err := strconv.ParseInt(p, 10, 32); err == nil {
55 | port = int(p)
56 | } else {
57 | klog.Fatalf("failed to convert WEBHOOK_PORT=%v in env: %v", p, err)
58 | }
59 | }
60 | return port
61 | }
62 |
63 | func GetCertDir() string {
64 | if p := os.Getenv("WEBHOOK_CERT_DIR"); len(p) > 0 {
65 | return p
66 | }
67 | return "/tmp/kruise-rollout-webhook-certs"
68 | }
69 |
70 | func GetCertWriter() string {
71 | return os.Getenv("WEBHOOK_CERT_WRITER")
72 | }
73 |
--------------------------------------------------------------------------------
/pkg/webhook/util/writer/error.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2020 The Kruise Authors.
3 | Copyright 2018 The Kubernetes Authors.
4 |
5 | Licensed under the Apache License, Version 2.0 (the "License");
6 | you may not use this file except in compliance with the License.
7 | You may obtain a copy of the License at
8 |
9 | http://www.apache.org/licenses/LICENSE-2.0
10 |
11 | Unless required by applicable law or agreed to in writing, software
12 | distributed under the License is distributed on an "AS IS" BASIS,
13 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 | See the License for the specific language governing permissions and
15 | limitations under the License.
16 | */
17 |
18 | package writer
19 |
20 | type notFoundError struct {
21 | err error
22 | }
23 |
24 | func (e notFoundError) Error() string {
25 | return e.err.Error()
26 | }
27 |
28 | func isNotFound(err error) bool {
29 | _, ok := err.(notFoundError)
30 | return ok
31 | }
32 |
33 | type alreadyExistError struct {
34 | err error
35 | }
36 |
37 | func (e alreadyExistError) Error() string {
38 | return e.err.Error()
39 | }
40 |
41 | func isAlreadyExists(err error) bool {
42 | _, ok := err.(alreadyExistError)
43 | return ok
44 | }
45 |
--------------------------------------------------------------------------------
/pkg/webhook/workload/mutating/webhooks.go:
--------------------------------------------------------------------------------
1 | /*
2 | Copyright 2019 The Kruise Authors.
3 |
4 | Licensed under the Apache License, Version 2.0 (the "License");
5 | you may not use this file except in compliance with the License.
6 | You may obtain a copy of the License at
7 |
8 | http://www.apache.org/licenses/LICENSE-2.0
9 |
10 | Unless required by applicable law or agreed to in writing, software
11 | distributed under the License is distributed on an "AS IS" BASIS,
12 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | See the License for the specific language governing permissions and
14 | limitations under the License.
15 | */
16 |
17 | package mutating
18 |
19 | import (
20 | "sigs.k8s.io/controller-runtime/pkg/webhook/admission"
21 | )
22 |
23 | // +kubebuilder:webhook:path=/mutate-apps-kruise-io-v1alpha1-cloneset,mutating=true,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=apps.kruise.io,resources=clonesets,verbs=update,versions=v1alpha1,name=mcloneset.kb.io
24 | // +kubebuilder:webhook:path=/mutate-apps-kruise-io-v1alpha1-daemonset,mutating=true,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=apps.kruise.io,resources=daemonsets,verbs=update,versions=v1alpha1,name=mdaemonset.kb.io
25 | // +kubebuilder:webhook:path=/mutate-apps-v1-deployment,mutating=true,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=apps,resources=deployments,verbs=update,versions=v1,name=mdeployment.kb.io
26 | // +kubebuilder:webhook:path=/mutate-unified-workload,mutating=true,failurePolicy=fail,sideEffects=None,admissionReviewVersions=v1;v1beta1,groups=*,resources=*,verbs=create;update,versions=*,name=munifiedworload.kb.io
27 |
28 | var (
29 | // HandlerMap contains admission webhook handlers
30 | HandlerMap = map[string]admission.Handler{
31 | "mutate-apps-kruise-io-v1alpha1-cloneset": &WorkloadHandler{},
32 | "mutate-apps-v1-deployment": &WorkloadHandler{},
33 | "mutate-apps-kruise-io-v1alpha1-daemonset": &WorkloadHandler{},
34 | "mutate-unified-workload": &UnifiedWorkloadHandler{},
35 | }
36 | )
37 |
--------------------------------------------------------------------------------
/rollouts.rollouts.kruise.io-rollouts-demo.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: Rollout
3 | metadata:
4 | annotations:
5 | rollouts.kruise.io/hash: d8c8v2w74286d77c4925wdw58v65z8725z7z4cw4f6cc485cvv62vx9cdwfv7b76
6 | rollouts.kruise.io/rolling-style: canary
7 | creationTimestamp: "2023-09-25T13:49:00Z"
8 | deletionGracePeriodSeconds: 0
9 | deletionTimestamp: "2023-09-25T13:52:34Z"
10 | generation: 2
11 | name: rollouts-demo
12 | namespace: rollout-59e5b9cbeb2ed91f
13 | resourceVersion: "2699"
14 | uid: e861a61c-07f6-4064-8486-154bc6d86e29
15 | spec:
16 | disabled: false
17 | objectRef:
18 | workloadRef:
19 | apiVersion: apps/v1
20 | kind: Deployment
21 | name: echoserver
22 | strategy:
23 | canary:
24 | steps:
25 | - matches:
26 | - headers:
27 | - name: user-agent
28 | type: Exact
29 | value: pc
30 | pause: {}
31 | replicas: 1
32 | - pause: {}
33 | weight: 50
34 | trafficRoutings:
35 | - customNetworkRefs:
36 | - apiVersion: networking.istio.io/v1alpha3
37 | kind: VirtualService
38 | name: vs-demo
39 | service: echoserver
40 | status:
41 | canaryStatus:
42 | canaryReadyReplicas: 0
43 | canaryReplicas: 0
44 | canaryRevision: 684554996d
45 | currentStepIndex: 2
46 | currentStepState: Completed
47 | observedWorkloadGeneration: 1
48 | podTemplateHash: ""
49 | rolloutHash: d8c8v2w74286d77c4925wdw58v65z8725z7z4cw4f6cc485cvv62vx9cdwfv7b76
50 | stableRevision: 567694c97d
51 | conditions:
52 | - lastTransitionTime: "2023-09-25T13:50:00Z"
53 | lastUpdateTime: "2023-09-25T13:50:00Z"
54 | message: Rollout is in Progressing
55 | reason: Initializing
56 | status: "True"
57 | type: Progressing
58 | message: workload deployment is completed
59 | observedGeneration: 1
60 | phase: Progressing
61 |
--------------------------------------------------------------------------------
/scripts/deploy_kind.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | if [ -z "$IMG" ]; then
4 | echo "no found IMG env"
5 | exit 1
6 | fi
7 |
8 | set -e
9 |
10 | make kustomize
11 | KUSTOMIZE=$(pwd)/bin/kustomize
12 | (cd config/manager && "${KUSTOMIZE}" edit set image controller="${IMG}")
13 | "${KUSTOMIZE}" build config/default | sed -e 's/imagePullPolicy: Always/imagePullPolicy: IfNotPresent/g' > /tmp/rollout-kustomization.yaml
14 | echo -e "resources:\n- manager.yaml" > config/manager/kustomization.yaml
15 | kubectl apply -f /tmp/rollout-kustomization.yaml
16 |
--------------------------------------------------------------------------------
/test/e2e/test_data/batchrelease/cloneset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps.kruise.io/v1alpha1
2 | kind: CloneSet
3 | metadata:
4 | labels:
5 | app: busybox
6 | name: sample
7 | spec:
8 | replicas: 5
9 | updateStrategy:
10 | maxUnavailable: 0
11 | maxSurge: 1
12 | selector:
13 | matchLabels:
14 | app: busybox
15 | template:
16 | metadata:
17 | labels:
18 | app: busybox
19 | spec:
20 | containers:
21 | - name: busybox
22 | image: busybox:1.32
23 | imagePullPolicy: IfNotPresent
24 | command: ["bin/sh", "-c", "sleep 10000000"]
25 | resources:
26 | limits:
27 | memory: "10Mi"
28 | cpu: "10m"
29 |
--------------------------------------------------------------------------------
/test/e2e/test_data/batchrelease/cloneset_number_100.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: BatchRelease
3 | metadata:
4 | name: release-cloneset-100
5 | spec:
6 | targetReference:
7 | type: workloadRef
8 | workloadRef:
9 | apiVersion: apps.kruise.io/v1alpha1
10 | kind: CloneSet
11 | name: sample
12 | releasePlan:
13 | batches:
14 | - canaryReplicas: 1
15 | pauseSeconds: 20
16 | - canaryReplicas: 3
17 | pauseSeconds: 20
18 | - canaryReplicas: 6
19 | pauseSeconds: 20
20 | - canaryReplicas: 10
21 | pauseSeconds: 20
22 |
23 |
24 |
--------------------------------------------------------------------------------
/test/e2e/test_data/batchrelease/cloneset_percentage_100.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: BatchRelease
3 | metadata:
4 | name: release-cloneset-100
5 | spec:
6 | targetReference:
7 | type: workloadRef
8 | workloadRef:
9 | apiVersion: apps.kruise.io/v1alpha1
10 | kind: CloneSet
11 | name: sample
12 | releasePlan:
13 | batches:
14 | - canaryReplicas: 20%
15 | pauseSeconds: 20
16 | - canaryReplicas: 40%
17 | pauseSeconds: 20
18 | - canaryReplicas: 60%
19 | pauseSeconds: 20
20 | - canaryReplicas: 100%
21 | pauseSeconds: 10
22 |
23 |
24 |
--------------------------------------------------------------------------------
/test/e2e/test_data/batchrelease/cloneset_percentage_50.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: BatchRelease
3 | metadata:
4 | name: release-cloneset-50
5 | spec:
6 | targetReference:
7 | type: workloadRef
8 | workloadRef:
9 | apiVersion: apps.kruise.io/v1alpha1
10 | kind: CloneSet
11 | name: sample
12 | releasePlan:
13 | batches:
14 | - canaryReplicas: 20%
15 | pauseSeconds: 30
16 | - canaryReplicas: 40%
17 | pauseSeconds: 60
18 | - canaryReplicas: 50%
19 | pauseSeconds: 30
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/test/e2e/test_data/batchrelease/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: sample
5 | labels:
6 | app: busybox
7 | spec:
8 | replicas: 5
9 | strategy:
10 | type: RollingUpdate
11 | rollingUpdate:
12 | maxUnavailable: 0
13 | maxSurge: 1
14 | selector:
15 | matchLabels:
16 | app: busybox
17 | template:
18 | metadata:
19 | labels:
20 | app: busybox
21 | spec:
22 | containers:
23 | - name: busybox
24 | image: busybox:1.32
25 | imagePullPolicy: IfNotPresent
26 | command: ["/bin/sh", "-c", "sleep 10000"]
27 | resources:
28 | limits:
29 | memory: "10Mi"
30 | cpu: "10m"
31 |
--------------------------------------------------------------------------------
/test/e2e/test_data/batchrelease/deployment_number_100.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: BatchRelease
3 | metadata:
4 | name: release-deployment-number-100
5 | spec:
6 | targetReference:
7 | workloadRef:
8 | apiVersion: apps/v1
9 | kind: Deployment
10 | name: sample
11 | releasePlan:
12 | batches:
13 | - canaryReplicas: 1
14 | pauseSeconds: 20
15 | - canaryReplicas: 2
16 | pauseSeconds: 20
17 | - canaryReplicas: 3
18 | pauseSeconds: 20
19 | - canaryReplicas: 5
20 | pauseSeconds: 10
21 |
22 |
23 |
--------------------------------------------------------------------------------
/test/e2e/test_data/batchrelease/deployment_percentage_100.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: BatchRelease
3 | metadata:
4 | name: release-deployment-percentage-100
5 | spec:
6 | targetReference:
7 | type: workloadRef
8 | workloadRef:
9 | apiVersion: apps/v1
10 | kind: Deployment
11 | name: sample
12 | releasePlan:
13 | batches:
14 | - canaryReplicas: 20%
15 | pauseSeconds: 20
16 | - canaryReplicas: 40%
17 | pauseSeconds: 20
18 | - canaryReplicas: 60%
19 | pauseSeconds: 20
20 | - canaryReplicas: 100%
21 | pauseSeconds: 10
22 |
--------------------------------------------------------------------------------
/test/e2e/test_data/batchrelease/deployment_percentage_50.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: BatchRelease
3 | metadata:
4 | name: release-deployment-percentage-50
5 | spec:
6 | targetReference:
7 | type: workloadRef
8 | workloadRef:
9 | apiVersion: apps/v1
10 | kind: Deployment
11 | name: sample
12 | releasePlan:
13 | batches:
14 | - canaryReplicas: 20%
15 | pauseSeconds: 30
16 | - canaryReplicas: 40%
17 | pauseSeconds: 60
18 | - canaryReplicas: 50%
19 | pauseSeconds: 30
20 |
21 |
22 |
23 |
--------------------------------------------------------------------------------
/test/e2e/test_data/batchrelease/sts.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: Rollout
3 | metadata:
4 | name: sts-demo
5 | spec:
6 | objectRef:
7 | workloadRef:
8 | apiVersion: apps/v1
9 | kind: StatefulSet
10 | name: sts-demo
11 | strategy:
12 | canary:
13 | steps:
14 | - weight: 20
15 | pause: {}
16 | - weight: 40
17 | pause: {duration: 10}
18 | - weight: 60
19 | pause: {duration: 10}
20 | - weight: 80
21 | pause: {duration: 10}
22 | - weight: 100
23 | ---
24 | apiVersion: apps/v1
25 | kind: StatefulSet
26 | metadata:
27 | name: sts-demo
28 | spec:
29 | selector:
30 | matchLabels:
31 | app: sts-demo
32 | serviceName: sts-demo
33 | replicas: 5
34 | template:
35 | metadata:
36 | labels:
37 | app: sts-demo
38 | spec:
39 | containers:
40 | - name: myapp
41 | image: busybox:1.33
42 | imagePullPolicy: IfNotPresent
43 | command: ["/bin/sh", "-c", "sleep 100000000"]
44 | ports:
45 | - containerPort: 80
46 | name: web
47 | volumeMounts:
48 | - name: www
49 | mountPath: /usr/share/nginx/html
50 | volumeClaimTemplates:
51 | - metadata:
52 | name: www
53 | spec:
54 | accessModes: [ "ReadWriteOnce" ]
55 | resources:
56 | requests:
57 | storage: 20Mi
58 | ---
59 | apiVersion: v1
60 | kind: Service
61 | metadata:
62 | name: sts-demo
63 | labels:
64 | app: sts-demo
65 | spec:
66 | ports:
67 | - port: 80
68 | name: web
69 | clusterIP: None
70 | selector:
71 | app: myapp
--------------------------------------------------------------------------------
/test/e2e/test_data/customNetworkProvider/destinationrule.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.istio.io/v1beta1
2 | kind: DestinationRule
3 | metadata:
4 | name: ds-demo
5 | spec:
6 | host: svc-demo
7 | trafficPolicy:
8 | loadBalancer:
9 | simple: ROUND_ROBIN
10 | subsets:
11 | - labels:
12 | version: base
13 | name: echoserver
--------------------------------------------------------------------------------
/test/e2e/test_data/customNetworkProvider/rollout_with_multi_trafficrouting.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1beta1
2 | kind: Rollout
3 | metadata:
4 | name: rollouts-demo
5 | spec:
6 | disabled: false
7 | workloadRef:
8 | apiVersion: apps/v1
9 | kind: Deployment
10 | name: echoserver
11 | strategy:
12 | canary:
13 | enableExtraWorkloadForCanary: true
14 | steps:
15 | - replicas: 1
16 | matches:
17 | - headers:
18 | - type: Exact
19 | name: user-agent
20 | value: pc
21 | - queryParams:
22 | - type: Exact
23 | name: user-agent
24 | value: pc
25 | - path:
26 | value: /pc
27 | - replicas: "50%"
28 | traffic: "50%"
29 | trafficRoutings:
30 | - service: echoserver
31 | ingress:
32 | classType: nginx
33 | name: echoserver
34 | customNetworkRefs:
35 | - apiVersion: networking.istio.io/v1alpha3
36 | kind: VirtualService
37 | name: vs-demo
38 |
--------------------------------------------------------------------------------
/test/e2e/test_data/customNetworkProvider/rollout_with_trafficrouting.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1beta1
2 | kind: Rollout
3 | metadata:
4 | name: rollouts-demo
5 | spec:
6 | disabled: false
7 | workloadRef:
8 | apiVersion: apps/v1
9 | kind: Deployment
10 | name: echoserver
11 | strategy:
12 | canary:
13 | enableExtraWorkloadForCanary: true
14 | steps:
15 | - replicas: 1
16 | matches:
17 | - headers:
18 | - type: Exact
19 | name: user-agent
20 | value: pc
21 | - queryParams:
22 | - type: Exact
23 | name: user-agent
24 | value: pc
25 | - path:
26 | value: /pc
27 | - replicas: "50%"
28 | traffic: "50%"
29 | trafficRoutings:
30 | - service: echoserver
31 | customNetworkRefs:
32 | - apiVersion: networking.istio.io/v1alpha3
33 | kind: VirtualService
34 | name: vs-demo
--------------------------------------------------------------------------------
/test/e2e/test_data/customNetworkProvider/rollout_without_trafficrouting.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: Rollout
3 | metadata:
4 | name: rollouts-demo
5 | annotations:
6 | rollouts.kruise.io/rolling-style: canary
7 | rollouts.kruise.io/trafficrouting: tr-demo
8 | spec:
9 | disabled: false
10 | objectRef:
11 | workloadRef:
12 | apiVersion: apps/v1
13 | kind: Deployment
14 | name: echoserver
15 | strategy:
16 | canary:
17 | steps:
18 | - replicas: 1
--------------------------------------------------------------------------------
/test/e2e/test_data/customNetworkProvider/trafficrouting.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: TrafficRouting
3 | metadata:
4 | name: tr-demo
5 | spec:
6 | strategy:
7 | matches:
8 | - headers:
9 | - type: Exact
10 | name: user-agent
11 | value: pc
12 | objectRef:
13 | - service: echoserver
14 | customNetworkRefs:
15 | - apiVersion: networking.istio.io/v1alpha3
16 | kind: VirtualService
17 | name: vs-demo
18 | - apiVersion: networking.istio.io/v1alpha3
19 | kind: DestinationRule
20 | name: ds-demo
--------------------------------------------------------------------------------
/test/e2e/test_data/customNetworkProvider/virtualservice_without_destinationrule.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.istio.io/v1alpha3
2 | kind: VirtualService
3 | metadata:
4 | name: vs-demo
5 | spec:
6 | hosts:
7 | - "*"
8 | gateways:
9 | - nginx-gateway
10 | http:
11 | - route:
12 | - destination:
13 | host: echoserver
--------------------------------------------------------------------------------
/test/e2e/test_data/deployment/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: sample
5 | labels:
6 | app: busybox
7 | Annotations:
8 | batchrelease.rollouts.kruise.io/control-info: '{"apiVersion":"rollouts.kruise.io/v1alpha1","kind":"BatchRelease","name":"rollouts-demo","uid":"45891961-8c29-4ea9-8e61-fd5a1fd19ffa","controller":true,"blockOwnerDeletion":true}'
9 | rollouts.kruise.io/deployment-strategy: '{"rollingUpdate":{"maxUnavailable":"25%","maxSurge":"25%"}}'
10 | spec:
11 | paused: true
12 | replicas: 5
13 | strategy:
14 | type: Recreate
15 | selector:
16 | matchLabels:
17 | app: busybox
18 | template:
19 | metadata:
20 | labels:
21 | app: busybox
22 | spec:
23 | containers:
24 | - name: busybox
25 | image: busybox:1.32
26 | imagePullPolicy: IfNotPresent
27 | command: ["/bin/sh", "-c", "sleep 10000"]
28 | env:
29 | - name: VERSION
30 | value: version1
31 | resources:
32 | limits:
33 | memory: "10Mi"
34 | cpu: "10m"
--------------------------------------------------------------------------------
/test/e2e/test_data/gateway/httproute-test.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: gateway.networking.k8s.io/v1alpha2
2 | kind: HTTPRoute
3 | metadata:
4 | name: echoserver
5 | spec:
6 | hostnames:
7 | - test.app.domain
8 | rules:
9 | - backendRefs:
10 | - group: ""
11 | name: echoserver
12 | port: 80
13 | # - group: ""
14 | # kind: Service
15 | # name: echoserver-canary
16 | # port: 80
17 | # weight: 40
18 | matches:
19 | - path:
20 | type: PathPrefix
21 | value: /apis/echo
22 |
--------------------------------------------------------------------------------
/test/e2e/test_data/gateway/rollout-test.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: Rollout
3 | metadata:
4 | name: rollouts-demo
5 | spec:
6 | objectRef:
7 | workloadRef:
8 | apiVersion: apps/v1
9 | kind: Deployment
10 | name: echoserver
11 | strategy:
12 | canary:
13 | steps:
14 | - weight: 20
15 | pause: {}
16 | - weight: 40
17 | pause: {duration: 10}
18 | - weight: 60
19 | pause: {duration: 10}
20 | - weight: 80
21 | pause: {duration: 10}
22 | - weight: 100
23 | trafficRoutings:
24 | - service: echoserver
25 | gateway:
26 | httpRouteName: echoserver
27 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/advanced_statefulset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps.kruise.io/v1beta1
2 | kind: StatefulSet
3 | metadata:
4 | name: echoserver
5 | labels:
6 | apps: echoserver
7 | spec:
8 | selector:
9 | matchLabels:
10 | app: echoserver
11 | serviceName: headless-service
12 | replicas: 5
13 | template:
14 | metadata:
15 | labels:
16 | app: echoserver
17 | spec:
18 | containers:
19 | - name: echoserver
20 | image: cilium/echoserver:latest
21 | imagePullPolicy: IfNotPresent
22 | ports:
23 | - containerPort: 8080
24 | env:
25 | - name: PORT
26 | value: '8080'
27 | - name: POD_NAME
28 | valueFrom:
29 | fieldRef:
30 | fieldPath: metadata.name
31 | - name: POD_NAMESPACE
32 | valueFrom:
33 | fieldRef:
34 | fieldPath: metadata.namespace
35 | - name: POD_IP
36 | valueFrom:
37 | fieldRef:
38 | fieldPath: status.podIP
39 | - name: NODE_NAME
40 | value: version1
41 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/cloneset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps.kruise.io/v1alpha1
2 | kind: CloneSet
3 | metadata:
4 | name: echoserver
5 | labels:
6 | app: echoserver
7 | annotations:
8 | rollouts.kruise.io/e2e-test-sample: "true"
9 | spec:
10 | replicas: 5
11 | updateStrategy:
12 | maxUnavailable: 0
13 | maxSurge: 1
14 | selector:
15 | matchLabels:
16 | app: echoserver
17 | template:
18 | metadata:
19 | labels:
20 | app: echoserver
21 | spec:
22 | containers:
23 | - name: echoserver
24 | image: cilium/echoserver:latest
25 | imagePullPolicy: IfNotPresent
26 | ports:
27 | - containerPort: 8080
28 | env:
29 | - name: PORT
30 | value: '8080'
31 | - name: POD_NAME
32 | valueFrom:
33 | fieldRef:
34 | fieldPath: metadata.name
35 | - name: POD_NAMESPACE
36 | valueFrom:
37 | fieldRef:
38 | fieldPath: metadata.namespace
39 | - name: POD_IP
40 | valueFrom:
41 | fieldRef:
42 | fieldPath: status.podIP
43 | - name: NODE_NAME
44 | value: version1
45 |
46 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/daemonset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps.kruise.io/v1alpha1
2 | kind: DaemonSet
3 | metadata:
4 | name: fluentd-elasticsearch
5 | namespace: kube-system
6 | labels:
7 | k8s-app: fluentd-logging
8 | spec:
9 | selector:
10 | matchLabels:
11 | name: fluentd-elasticsearch
12 | template:
13 | metadata:
14 | labels:
15 | name: fluentd-elasticsearch
16 | spec:
17 | tolerations:
18 | # these tolerations are to have the daemonset runnable on control plane nodes
19 | # remove them if your control plane nodes should not run pods
20 | - key: node-role.kubernetes.io/control-plane
21 | operator: Exists
22 | effect: NoSchedule
23 | - key: node-role.kubernetes.io/master
24 | operator: Exists
25 | effect: NoSchedule
26 | containers:
27 | - name: fluentd-elasticsearch
28 | image: quay.io/fluentd_elasticsearch/fluentd:v2.5.2
29 | resources:
30 | limits:
31 | memory: 200Mi
32 | requests:
33 | cpu: 100m
34 | memory: 200Mi
35 | volumeMounts:
36 | - name: varlog
37 | mountPath: /var/log
38 | terminationGracePeriodSeconds: 30
39 | volumes:
40 | - name: varlog
41 | hostPath:
42 | path: /var/log
43 | # updateStrategy:
44 | # type: RollingUpdate
45 | # rollingUpdate:
46 | # rollingUpdateType: Standard
47 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/deployment.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: echoserver
5 | labels:
6 | app: echoserver
7 | spec:
8 | replicas: 5
9 | strategy:
10 | type: RollingUpdate
11 | rollingUpdate:
12 | maxUnavailable: 0
13 | maxSurge: 1
14 | selector:
15 | matchLabels:
16 | app: echoserver
17 | template:
18 | metadata:
19 | labels:
20 | app: echoserver
21 | spec:
22 | containers:
23 | - name: echoserver
24 | image: cilium/echoserver:latest
25 | # For ARM-based env
26 | # image: jmalloc/echo-server:latest
27 | imagePullPolicy: IfNotPresent
28 | ports:
29 | - containerPort: 8080
30 | env:
31 | - name: PORT
32 | value: '8080'
33 | - name: POD_NAME
34 | valueFrom:
35 | fieldRef:
36 | fieldPath: metadata.name
37 | - name: POD_NAMESPACE
38 | valueFrom:
39 | fieldRef:
40 | fieldPath: metadata.namespace
41 | - name: POD_IP
42 | valueFrom:
43 | fieldRef:
44 | fieldPath: status.podIP
45 | - name: NODE_NAME
46 | value: version1
47 |
48 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/deployment_disabled.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: Deployment
3 | metadata:
4 | name: workload-demo
5 | namespace: default
6 | spec:
7 | replicas: 10
8 | selector:
9 | matchLabels:
10 | app: demo
11 | template:
12 | metadata:
13 | labels:
14 | app: demo
15 | spec:
16 | containers:
17 | - name: busybox
18 | image: busybox:latest
19 | imagePullPolicy: IfNotPresent
20 | command: ["/bin/sh", "-c", "sleep 100d"]
21 | env:
22 | - name: VERSION
23 | value: "version-1"
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/headless_service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: headless-service
5 | labels:
6 | app: echoserver
7 | spec:
8 | ports:
9 | - port: 8080
10 | name: web
11 | clusterIP: None
12 | selector:
13 | app: echoserver
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/hpa_v1.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: autoscaling/v1
2 | kind: HorizontalPodAutoscaler
3 | metadata:
4 | name: hpa-dp
5 | spec:
6 | scaleTargetRef:
7 | apiVersion: apps/v1
8 | kind: Deployment
9 | name: echoserver
10 | minReplicas: 2
11 | maxReplicas: 6
12 | targetCPUUtilizationPercentage: 1
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/hpa_v2.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: autoscaling/v2
2 | kind: HorizontalPodAutoscaler
3 | metadata:
4 | name: hpa-dp
5 | spec:
6 | scaleTargetRef:
7 | apiVersion: apps/v1
8 | kind: Deployment
9 | name: echoserver
10 | behavior:
11 | scaleDown:
12 | stabilizationWindowSeconds: 10
13 | # selectPolicy: Disabled
14 | # scaleUp:
15 | # selectPolicy: Disabled
16 | minReplicas: 2
17 | maxReplicas: 6
18 | metrics:
19 | - type: Resource
20 | resource:
21 | name: cpu
22 | target:
23 | type: AverageValue
24 | averageValue: '1m'
25 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/native_statefulset.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: apps/v1
2 | kind: StatefulSet
3 | metadata:
4 | name: echoserver
5 | labels:
6 | apps: echoserver
7 | spec:
8 | selector:
9 | matchLabels:
10 | app: echoserver
11 | serviceName: headless-service
12 | replicas: 5
13 | template:
14 | metadata:
15 | labels:
16 | app: echoserver
17 | spec:
18 | containers:
19 | - name: echoserver
20 | image: cilium/echoserver:latest
21 | imagePullPolicy: IfNotPresent
22 | ports:
23 | - containerPort: 8080
24 | env:
25 | - name: PORT
26 | value: '8080'
27 | - name: POD_NAME
28 | valueFrom:
29 | fieldRef:
30 | fieldPath: metadata.name
31 | - name: POD_NAMESPACE
32 | valueFrom:
33 | fieldRef:
34 | fieldPath: metadata.namespace
35 | - name: POD_IP
36 | valueFrom:
37 | fieldRef:
38 | fieldPath: status.podIP
39 | - name: NODE_NAME
40 | value: version1
41 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/nginx_ingress.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: networking.k8s.io/v1
2 | kind: Ingress
3 | metadata:
4 | name: echoserver
5 | annotations:
6 | kubernetes.io/ingress.class: nginx
7 | spec:
8 | rules:
9 | - host: echoserver.example.com
10 | http:
11 | paths:
12 | - backend:
13 | service:
14 | name: echoserver
15 | port:
16 | number: 80
17 | path: /apis/echo
18 | pathType: Exact
19 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/rollout-configuration.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: ConfigMap
3 | metadata:
4 | name: kruise-rollout-configuration
5 | namespace: kruise-rollout
6 | data:
7 | "lua.traffic.routing.ingress.aliyun-alb": |
8 | function split(input, delimiter)
9 | local arr = {}
10 | string.gsub(input, '[^' .. delimiter ..']+', function(w) table.insert(arr, w) end)
11 | return arr
12 | end
13 | annotations = {}
14 | if ( obj.annotations )
15 | then
16 | annotations = obj.annotations
17 | end
18 |
19 | annotations["alb.ingress.kubernetes.io/canary"] = "true"
20 | annotations["alb.ingress.kubernetes.io/canary-by-cookie"] = nil
21 | annotations["alb.ingress.kubernetes.io/canary-by-header"] = nil
22 | annotations["alb.ingress.kubernetes.io/canary-by-header-pattern"] = nil
23 | annotations["alb.ingress.kubernetes.io/canary-by-header-value"] = nil
24 | annotations["alb.ingress.kubernetes.io/canary-weight"] = nil
25 | conditionKey = string.format("alb.ingress.kubernetes.io/conditions.%s", obj.canaryService)
26 | annotations[conditionKey] = nil
27 | if ( obj.weight ~= "-1" )
28 | then
29 | annotations["alb.ingress.kubernetes.io/canary-weight"] = obj.weight
30 | end
31 | if ( not obj.matches )
32 | then
33 | return annotations
34 | end
35 | conditions = {}
36 | match = obj.matches[1]
37 | for _,header in ipairs(match.headers) do
38 | local condition = {}
39 | if ( header.name == "Cookie" )
40 | then
41 | condition.type = "Cookie"
42 | condition.cookieConfig = {}
43 | cookies = split(header.value, ";")
44 | values = {}
45 | for _,cookieStr in ipairs(cookies) do
46 | cookie = split(cookieStr, "=")
47 | value = {}
48 | value.key = cookie[1]
49 | value.value = cookie[2]
50 | table.insert(values, value)
51 | end
52 | condition.cookieConfig.values = values
53 | elseif ( header.name == "SourceIp" )
54 | then
55 | condition.type = "SourceIp"
56 | condition.sourceIpConfig = {}
57 | ips = split(header.value, ";")
58 | values = {}
59 | for _,ip in ipairs(ips) do
60 | table.insert(values, ip)
61 | end
62 | condition.sourceIpConfig.values = values
63 | else
64 | condition.type = "Header"
65 | condition.headerConfig = {}
66 | condition.headerConfig.key = header.name
67 | vals = split(header.value, ";")
68 | values = {}
69 | for _,val in ipairs(vals) do
70 | table.insert(values, val)
71 | end
72 | condition.headerConfig.values = values
73 | end
74 | table.insert(conditions, condition)
75 | end
76 | annotations[conditionKey] = json.encode(conditions)
77 | return annotations
78 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/rollout_canary_base.yaml:
--------------------------------------------------------------------------------
1 | # we recommend that new test cases or modifications to existing test cases should
2 | # use v1beta1 Rollout, eg. use rollout_v1beta1_canary_base.yaml
3 | apiVersion: rollouts.kruise.io/v1alpha1
4 | kind: Rollout
5 | metadata:
6 | name: rollouts-demo
7 | spec:
8 | objectRef:
9 | workloadRef:
10 | apiVersion: apps/v1
11 | kind: Deployment
12 | name: echoserver
13 | strategy:
14 | canary:
15 | steps:
16 | - weight: 20
17 | pause: {}
18 | - weight: 40
19 | pause: {duration: 10}
20 | - weight: 60
21 | pause: {duration: 10}
22 | - weight: 80
23 | pause: {duration: 10}
24 | - weight: 100
25 | pause: {duration: 0}
26 | trafficRoutings:
27 | - service: echoserver
28 | ingress:
29 | classType: nginx
30 | name: echoserver
31 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/rollout_canary_daemonset_base.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: Rollout
3 | metadata:
4 | name: rollouts-test
5 | # The rollout resource needs to be in the same namespace as the corresponding workload
6 | namespace: kube-system
7 | # This annotation can help us upgrade the Deployment using partition, just like StatefulSet/CloneSet.
8 | annotations:
9 | rollouts.kruise.io/rolling-style: partition
10 | spec:
11 | objectRef:
12 | # rollout of published workloads, currently only supports Deployment, CloneSet, StatefulSet, Advanced StatefulSet
13 | workloadRef:
14 | apiVersion: apps.kruise.io/v1alpha1
15 | kind: DaemonSet
16 | name: fluentd-elasticsearch
17 | strategy:
18 | canary:
19 | steps:
20 | - replicas: 1
21 | - replicas: 100%
22 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/rollout_canary_daemonset_interrupt.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: Rollout
3 | metadata:
4 | name: rollouts-test
5 | # The rollout resource needs to be in the same namespace as the corresponding workload
6 | namespace: kube-system
7 | # This annotation can help us upgrade the Deployment using partition, just like StatefulSet/CloneSet.
8 | annotations:
9 | rollouts.kruise.io/rolling-style: partition
10 | spec:
11 | objectRef:
12 | # rollout of published workloads, currently only supports Deployment, CloneSet, StatefulSet, Advanced StatefulSet
13 | workloadRef:
14 | apiVersion: apps.kruise.io/v1alpha1
15 | kind: DaemonSet
16 | name: fluentd-elasticsearch
17 | strategy:
18 | canary:
19 | steps:
20 | - replicas: 1
21 | - replicas: 2
22 | - replicas: 100%
23 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/rollout_disabled.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1alpha1
2 | kind: Rollout
3 | metadata:
4 | name: rollouts-demo
5 | namespace: default
6 | annotations:
7 | rollouts.kruise.io/rolling-style: partition
8 | spec:
9 | disabled: false
10 | objectRef:
11 | workloadRef:
12 | apiVersion: apps/v1
13 | kind: Deployment
14 | name: workload-demo
15 | strategy:
16 | canary:
17 | steps:
18 | - replicas: 2
19 | - replicas: 50%
20 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/rollout_v1beta1_bluegreen_base.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1beta1 # we use v1beta1
2 | kind: Rollout
3 | metadata:
4 | name: rollouts-demo
5 | spec:
6 | workloadRef:
7 | apiVersion: apps/v1
8 | kind: Deployment
9 | name: echoserver
10 | strategy:
11 | blueGreen:
12 | steps:
13 | - replicas: 50%
14 | traffic: 0%
15 | pause: {}
16 | - replicas: 100%
17 | traffic: 0%
18 | - replicas: 100%
19 | traffic: 50%
20 | - replicas: 100%
21 | traffic: 100%
22 | trafficRoutings:
23 | - service: echoserver
24 | ingress:
25 | classType: nginx
26 | name: echoserver
27 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/rollout_v1beta1_bluegreen_cloneset_base.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1beta1 # we use v1beta1
2 | kind: Rollout
3 | metadata:
4 | name: rollouts-demo
5 | spec:
6 | workloadRef:
7 | apiVersion: apps.kruise.io/v1alpha1
8 | kind: CloneSet
9 | name: echoserver
10 | strategy:
11 | blueGreen:
12 | steps:
13 | - replicas: 100%
14 | traffic: 0%
15 | pause: {}
16 | - replicas: 100%
17 | traffic: 50%
18 | - replicas: 100%
19 | traffic: 100%
20 | trafficRoutings:
21 | - service: echoserver
22 | ingress:
23 | classType: nginx
24 | name: echoserver
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/rollout_v1beta1_canary_base.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1beta1 # we use v1beta1
2 | kind: Rollout
3 | metadata:
4 | name: rollouts-demo
5 | spec:
6 | workloadRef:
7 | apiVersion: apps/v1
8 | kind: Deployment
9 | name: echoserver
10 | strategy:
11 | canary:
12 | enableExtraWorkloadForCanary: true
13 | steps:
14 | - traffic: 20%
15 | replicas: 20%
16 | pause: {}
17 | - traffic: 40%
18 | replicas: 40%
19 | pause: {duration: 10}
20 | - traffic: 60%
21 | replicas: 60%
22 | pause: {duration: 10}
23 | - traffic: 80%
24 | replicas: 80%
25 | pause: {duration: 10}
26 | - traffic: 100%
27 | replicas: 100%
28 | pause: {duration: 0}
29 | trafficRoutings:
30 | - service: echoserver
31 | ingress:
32 | classType: nginx
33 | name: echoserver
34 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/rollout_v1beta1_partition_base.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: rollouts.kruise.io/v1beta1 # we use v1beta1
2 | kind: Rollout
3 | metadata:
4 | name: rollouts-demo
5 | spec:
6 | workloadRef:
7 | apiVersion: apps/v1
8 | kind: Deployment
9 | name: echoserver
10 | strategy:
11 | canary:
12 | enableExtraWorkloadForCanary: false
13 | steps:
14 | - traffic: 20%
15 | replicas: 20%
16 | pause: {}
17 | - replicas: 40%
18 | pause: {duration: 10}
19 | - replicas: 60%
20 | pause: {duration: 10}
21 | - replicas: 80%
22 | pause: {duration: 10}
23 | - replicas: 100%
24 | pause: {duration: 0}
25 | trafficRoutings:
26 | - service: echoserver
27 | ingress:
28 | classType: nginx
29 | name: echoserver
30 |
--------------------------------------------------------------------------------
/test/e2e/test_data/rollout/service.yaml:
--------------------------------------------------------------------------------
1 | apiVersion: v1
2 | kind: Service
3 | metadata:
4 | name: echoserver
5 | labels:
6 | app: echoserver
7 | spec:
8 | ports:
9 | - port: 80
10 | targetPort: 8080
11 | protocol: TCP
12 | name: http
13 | selector:
14 | app: echoserver
15 |
--------------------------------------------------------------------------------
/test/kind-conf.yaml:
--------------------------------------------------------------------------------
1 | kind: Cluster
2 | apiVersion: kind.x-k8s.io/v1alpha4
3 | nodes:
4 | - role: control-plane
5 | - role: worker
6 | - role: worker
7 | featureGates:
8 | EphemeralContainers: true
9 |
--------------------------------------------------------------------------------