Compare commits

...

3 Commits

Author SHA1 Message Date
berg 13d62b8920 Performance optimized PodProbeMarker to reduce many invalid patch operations (#2007)
Signed-off-by: liheng.zms <liheng.zms@alibaba-inc.com>
2025-05-06 11:16:32 +08:00
berg b65901eb4f JobSidecarTerminator support ignore exit code capability via env (#1949)
Signed-off-by: liheng.zms <liheng.zms@alibaba-inc.com>
2025-05-06 11:16:18 +08:00
Zhen Zhang 50c1e29ef1 Bump k8s.io/kubernetes from 1.30.9 to 1.30.10 (#1924)
Bumps [k8s.io/kubernetes](https://github.com/kubernetes/kubernetes) from 1.30.9 to 1.30.10.
- [Release notes](https://github.com/kubernetes/kubernetes/releases)
- [Commits](https://github.com/kubernetes/kubernetes/compare/v1.30.9...v1.30.10)

---
updated-dependencies:
- dependency-name: k8s.io/kubernetes
  dependency-type: direct:production
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2025-02-17 15:47:16 +08:00
13 changed files with 986 additions and 212 deletions

View File

@ -29,4 +29,7 @@ const (
// using in-place update strategy to kill sidecar. This image must be given if you want to use in-place update
// strategy to terminate sidecar containers.
KruiseTerminateSidecarWithImageEnv = "KRUISE_TERMINATE_SIDECAR_WHEN_JOB_EXIT_WITH_IMAGE"
// KruiseIgnoreContainerExitCodeEnv is an env name, which represents a switch to ignore the exit code of sidecar container.
KruiseIgnoreContainerExitCodeEnv = "KRUISE_TERMINATE_SIDECAR_IGNORE_EXIT_CODE"
)

90
go.mod
View File

@ -18,20 +18,20 @@ require (
golang.org/x/time v0.3.0
gomodules.xyz/jsonpatch/v2 v2.4.0
google.golang.org/grpc v1.63.0
k8s.io/api v0.30.9
k8s.io/apiextensions-apiserver v0.30.9
k8s.io/apimachinery v0.30.9
k8s.io/apiserver v0.30.9
k8s.io/client-go v0.30.9
k8s.io/code-generator v0.30.9
k8s.io/component-base v0.30.9
k8s.io/component-helpers v0.30.9
k8s.io/cri-api v0.30.9
k8s.io/api v0.30.10
k8s.io/apiextensions-apiserver v0.30.10
k8s.io/apimachinery v0.30.10
k8s.io/apiserver v0.30.10
k8s.io/client-go v0.30.10
k8s.io/code-generator v0.30.10
k8s.io/component-base v0.30.10
k8s.io/component-helpers v0.30.10
k8s.io/cri-api v0.30.10
k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01
k8s.io/klog/v2 v2.120.1
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340
k8s.io/kubelet v0.30.9
k8s.io/kubernetes v1.30.9
k8s.io/kubelet v0.30.10
k8s.io/kubernetes v1.30.10
k8s.io/utils v0.0.0-20230726121419-3b25d923346b
sigs.k8s.io/controller-runtime v0.18.6
)
@ -60,9 +60,9 @@ require (
golang.org/x/net v0.33.0 // indirect
google.golang.org/genproto v0.0.0-20240227224415-6ceb2ff114de // indirect
gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
k8s.io/controller-manager v0.30.9 // indirect
k8s.io/controller-manager v0.30.10 // indirect
k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 // indirect
k8s.io/kms v0.30.9 // indirect
k8s.io/kms v0.30.10 // indirect
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 // indirect
)
@ -134,11 +134,11 @@ require (
gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/cloud-provider v0.30.9 // indirect
k8s.io/csi-translation-lib v0.30.9 // indirect
k8s.io/dynamic-resource-allocation v0.30.9 // indirect
k8s.io/kube-scheduler v0.30.9 // indirect
k8s.io/mount-utils v0.30.9 // indirect
k8s.io/cloud-provider v0.30.10 // indirect
k8s.io/csi-translation-lib v0.30.10 // indirect
k8s.io/dynamic-resource-allocation v0.30.10 // indirect
k8s.io/kube-scheduler v0.30.10 // indirect
k8s.io/mount-utils v0.30.10 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/structured-merge-diff/v4 v4.4.1 // indirect
sigs.k8s.io/yaml v1.4.0 // indirect
@ -146,32 +146,32 @@ require (
replace (
golang.org/x/sys => golang.org/x/sys v0.19.0
k8s.io/api => k8s.io/api v0.30.9
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.30.9
k8s.io/apimachinery => k8s.io/apimachinery v0.30.9
k8s.io/apiserver => k8s.io/apiserver v0.30.9
k8s.io/cli-runtime => k8s.io/cli-runtime v0.30.9
k8s.io/client-go => k8s.io/client-go v0.30.9
k8s.io/cloud-provider => k8s.io/cloud-provider v0.30.9
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.30.9
k8s.io/code-generator => k8s.io/code-generator v0.30.9
k8s.io/component-base => k8s.io/component-base v0.30.9
k8s.io/component-helpers => k8s.io/component-helpers v0.30.9
k8s.io/controller-manager => k8s.io/controller-manager v0.30.9
k8s.io/cri-api => k8s.io/cri-api v0.30.9
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.30.9
k8s.io/dynamic-resource-allocation => k8s.io/dynamic-resource-allocation v0.30.9
k8s.io/endpointslice => k8s.io/endpointslice v0.30.9
k8s.io/kms => k8s.io/kms v0.30.9
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.30.9
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.30.9
k8s.io/api => k8s.io/api v0.30.10
k8s.io/apiextensions-apiserver => k8s.io/apiextensions-apiserver v0.30.10
k8s.io/apimachinery => k8s.io/apimachinery v0.30.10
k8s.io/apiserver => k8s.io/apiserver v0.30.10
k8s.io/cli-runtime => k8s.io/cli-runtime v0.30.10
k8s.io/client-go => k8s.io/client-go v0.30.10
k8s.io/cloud-provider => k8s.io/cloud-provider v0.30.10
k8s.io/cluster-bootstrap => k8s.io/cluster-bootstrap v0.30.10
k8s.io/code-generator => k8s.io/code-generator v0.30.10
k8s.io/component-base => k8s.io/component-base v0.30.10
k8s.io/component-helpers => k8s.io/component-helpers v0.30.10
k8s.io/controller-manager => k8s.io/controller-manager v0.30.10
k8s.io/cri-api => k8s.io/cri-api v0.30.10
k8s.io/csi-translation-lib => k8s.io/csi-translation-lib v0.30.10
k8s.io/dynamic-resource-allocation => k8s.io/dynamic-resource-allocation v0.30.10
k8s.io/endpointslice => k8s.io/endpointslice v0.30.10
k8s.io/kms => k8s.io/kms v0.30.10
k8s.io/kube-aggregator => k8s.io/kube-aggregator v0.30.10
k8s.io/kube-controller-manager => k8s.io/kube-controller-manager v0.30.10
k8s.io/kube-proxy => k8s.io/kube-proxy v0.30.9
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.30.9
k8s.io/kubectl => k8s.io/kubectl v0.30.9
k8s.io/kubelet => k8s.io/kubelet v0.30.9
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.30.9
k8s.io/metrics => k8s.io/metrics v0.30.9
k8s.io/mount-utils => k8s.io/mount-utils v0.30.9
k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.30.9
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.30.9
k8s.io/kube-scheduler => k8s.io/kube-scheduler v0.30.10
k8s.io/kubectl => k8s.io/kubectl v0.30.10
k8s.io/kubelet => k8s.io/kubelet v0.30.10
k8s.io/legacy-cloud-providers => k8s.io/legacy-cloud-providers v0.30.10
k8s.io/metrics => k8s.io/metrics v0.30.10
k8s.io/mount-utils => k8s.io/mount-utils v0.30.10
k8s.io/pod-security-admission => k8s.io/pod-security-admission v0.30.10
k8s.io/sample-apiserver => k8s.io/sample-apiserver v0.30.10
)

72
go.sum
View File

@ -373,32 +373,32 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
k8s.io/api v0.30.9 h1:yojLzwl7TBV3XusCHXvR2AnowQFVnL9Ui3/wAga3pv4=
k8s.io/api v0.30.9/go.mod h1:FGOLP66cj572P8rjO1H5x5+0vzmvf3bLc8pQlyQeBqk=
k8s.io/apiextensions-apiserver v0.30.9 h1:9Z9ADALJhh0iJYm9V4c2Pk+czkO9amMm5CEtafE2gPY=
k8s.io/apiextensions-apiserver v0.30.9/go.mod h1:RNWVgxNEnTaeBHPMiiUB5sb8IjuX98NcOsF85cnJQMg=
k8s.io/apimachinery v0.30.9 h1:wDbY7vSPd3ALl5Fpw0yEiDyW5ozMyCpqsQ6anaCkpII=
k8s.io/apimachinery v0.30.9/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/apiserver v0.30.9 h1:AvRI7YteLGKBTv9nJ/LGz5WlyJr6h1Me4TfqKkSmOvc=
k8s.io/apiserver v0.30.9/go.mod h1:pumPieZ4vKcfQRe8YYIUYyOQIFnCTBp1UXLtbsJWqyo=
k8s.io/client-go v0.30.9 h1:nIO9MJIWK/H/rDHT0PikZhEmK0MSK5hyfdT9YMTMMC0=
k8s.io/client-go v0.30.9/go.mod h1:JObO2rfBeqrWn45GNMNnDReUfa6lgP4p+RjRLPJMaE8=
k8s.io/cloud-provider v0.30.9 h1:cjoY8Yjv6jRbZ9ClBBqEQOD4y/jDP6eP5ucCkN40Ekg=
k8s.io/cloud-provider v0.30.9/go.mod h1:hlErhj0ekIDHiO3SfbXDwL8cTqOfqq6adOdNAdvOmZM=
k8s.io/code-generator v0.30.9 h1:K8v/UOOK39OsQureRJImTWHI6R5dVoaJRqDa9BCjM8U=
k8s.io/code-generator v0.30.9/go.mod h1:hvORxGSX68n4rYoPMiaqpP+kvc/7MGz+09AJpzby/zI=
k8s.io/component-base v0.30.9 h1:Zw/Q5esZ1Zj/s5VEyHZMCI41tTtgO2mYLuUtQXimXLg=
k8s.io/component-base v0.30.9/go.mod h1:GWgCrimYzk+Wty984dLEAQ5oYAF7CSDywoMpb62V/6M=
k8s.io/component-helpers v0.30.9 h1:hBqWnzasEfndn0yKc3MpTWn46Mo/A7zrYj06B5VcwR8=
k8s.io/component-helpers v0.30.9/go.mod h1:77gb0dDoZ5XULw0pOelLfQFi7+B+84McfD9e6yH5BUU=
k8s.io/controller-manager v0.30.9 h1:/6M4Ftz3O1/26lCl4W8Y9bv2JxCD3e0vKLnrylNe/+w=
k8s.io/controller-manager v0.30.9/go.mod h1:RH/RtVBCUIilHdIz+lYc/1niuyd3WFGS9GRvOYH+Tas=
k8s.io/cri-api v0.30.9 h1:B7uhtB8GO2y6EZCSo4rhHEviyxtdhFQs4UkTT406hV8=
k8s.io/cri-api v0.30.9/go.mod h1://4/umPJSW1ISNSNng4OwjpkvswJOQwU8rnkvO8P+xg=
k8s.io/csi-translation-lib v0.30.9 h1:lHhJiT3Ehi4qtJpGq6f4eJipAUE6rCOU+mUTmeYuNgE=
k8s.io/csi-translation-lib v0.30.9/go.mod h1:aQgmkyLJ3KTzBRD4INwQXF2czQl64hZDWeF3pFI/pVU=
k8s.io/dynamic-resource-allocation v0.30.9 h1:F44b/4XQOJFKMH0WYoFHPYB2pe509yNbw4DaszVzA4Y=
k8s.io/dynamic-resource-allocation v0.30.9/go.mod h1:odyqweypcWRFFXr/TBgH00x8CDVw8Y081mTVpqbVueo=
k8s.io/api v0.30.10 h1:2YvzRF/BELgCvxbQqFKaan5hnj2+y7JOuqu2WpVk3gg=
k8s.io/api v0.30.10/go.mod h1:Hyz3ZuK7jVLJBUFvwzDSGwxHuDdsrGs5RzF16wfHIn4=
k8s.io/apiextensions-apiserver v0.30.10 h1:Im5wWRzf0L4URt08K41e+Uh2bqkHN8rWH8+gk6+8/wY=
k8s.io/apiextensions-apiserver v0.30.10/go.mod h1:yGWw2UU3WFGLYQjVEs/dgY57U3hNFv1SiT8PrONFZPA=
k8s.io/apimachinery v0.30.10 h1:UflKuJeSSArttm05wjYP0GwpTlvjnMbDKFn6F7rKkKU=
k8s.io/apimachinery v0.30.10/go.mod h1:iexa2somDaxdnj7bha06bhb43Zpa6eWH8N8dbqVjTUc=
k8s.io/apiserver v0.30.10 h1:ozSFhvzw/lauRFFs1auniIoHNVa2hjjkN0/7OYGlfME=
k8s.io/apiserver v0.30.10/go.mod h1:lJtWYEWEDLkQ1zCLFQrjLQ0X19TlXyaa56K92C1a+f4=
k8s.io/client-go v0.30.10 h1:C0oWM82QMvosIl/IdJhWfTUb7rIxM52rNSutFBknAVY=
k8s.io/client-go v0.30.10/go.mod h1:OfTvt0yuo8VpMViOsgvYQb+tMJQLNWVBqXWkzdFXSq4=
k8s.io/cloud-provider v0.30.10 h1:irijCPElYtcA6rVuwBtn8LlNR13CY8ewfMnJ4foPOTg=
k8s.io/cloud-provider v0.30.10/go.mod h1:OWNfg4OCbSe/BNQe9e1seODtfdAp1BTxR/ZkknxHfGA=
k8s.io/code-generator v0.30.10 h1:1p47NC8/zijsgCuqI0F20ErxsWE3VLUGEEcxoiweMeo=
k8s.io/code-generator v0.30.10/go.mod h1:b5HvR9KGVjQOK1fbnZfP/FL4Qe3Zox5CfXJ5Wp7tqQo=
k8s.io/component-base v0.30.10 h1:UJi0vTnTvtwWnVHcQeV1hzansnvTSKzFfMxtYAa8/GY=
k8s.io/component-base v0.30.10/go.mod h1:q+6CkRDb/JOlqEpDzmuprysj4R/b/zzQO5vVBRynYQA=
k8s.io/component-helpers v0.30.10 h1:julw9dAWv4vybIbSE/eksTqJrE609LJjyn7V9O1x19c=
k8s.io/component-helpers v0.30.10/go.mod h1:3ID/1BxSX2ML5CLe1KdXitA7SYeSCrz0MXkPcP7M88A=
k8s.io/controller-manager v0.30.10 h1:yEOaypLMR5d3IfwbFhISUfgsXj0kW+ind+XcKjQYe2w=
k8s.io/controller-manager v0.30.10/go.mod h1:HCHIK96kf9/jhCfOE64MpTWjSOQtc4QLtvgBDKapXcg=
k8s.io/cri-api v0.30.10 h1:2wPj1QY2N0j2xgYb2xZ1iQw8XvD4a+wkFSNJ4Rj2/ag=
k8s.io/cri-api v0.30.10/go.mod h1://4/umPJSW1ISNSNng4OwjpkvswJOQwU8rnkvO8P+xg=
k8s.io/csi-translation-lib v0.30.10 h1:/Etnsm6rJveijZYjyQHwblh8s3YW2SUyj2TvUZHvAtE=
k8s.io/csi-translation-lib v0.30.10/go.mod h1:4UIc8sXk0Pp0U2kJdxIxmY9rEN7sIuyYjIK7tY74CLs=
k8s.io/dynamic-resource-allocation v0.30.10 h1:I+c9QKAjHm/HaJK7aZXfKAJ+IsXgsJPdzP/BBzmFMTs=
k8s.io/dynamic-resource-allocation v0.30.10/go.mod h1:AZgx4x5L2cObVVYbOqfBjIN968IlbbHe0kvqRrkquHw=
k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01 h1:pWEwq4Asjm4vjW7vcsmijwBhOr1/shsbSYiWXmNGlks=
k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E=
k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 h1:NGrVE502P0s0/1hudf8zjgwki1X/TByhmAoILTarmzo=
@ -406,18 +406,18 @@ k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0
k8s.io/klog/v2 v2.2.0/go.mod h1:Od+F08eJP+W3HUb4pSrPpgp9DGU4GzlpG/TmITuYh/Y=
k8s.io/klog/v2 v2.120.1 h1:QXU6cPEOIslTGvZaXvFWiP9VKyeet3sawzTOvdXb4Vw=
k8s.io/klog/v2 v2.120.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
k8s.io/kms v0.30.9 h1:w4bNc7ruJf6PDEfyyeb2fjVuIqMBMMGch5KmE1sCYNA=
k8s.io/kms v0.30.9/go.mod h1:GrMurD0qk3G4yNgGcsCEmepqf9KyyIrTXYR2lyUOJC4=
k8s.io/kms v0.30.10 h1:VaoJHFouvS4hGZ1Djusoc9HOksh/02uEspV0Mfy0I/Q=
k8s.io/kms v0.30.10/go.mod h1:GrMurD0qk3G4yNgGcsCEmepqf9KyyIrTXYR2lyUOJC4=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 h1:BZqlfIlq5YbRMFko6/PM7FjZpUb45WallggurYhKGag=
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340/go.mod h1:yD4MZYeKMBwQKVht279WycxKyM84kkAx2DPrTXaeb98=
k8s.io/kube-scheduler v0.30.9 h1:iUj7WsjU4NvPSX8XukGow2v3nh5p/dvw6/g+5OMVq2o=
k8s.io/kube-scheduler v0.30.9/go.mod h1:2PAn/o0AZUGn6LfPFVEWk+qYkdDTnXsFxnwXrxirfvs=
k8s.io/kubelet v0.30.9 h1:E8YYc/1CsRAXgzFuhAQGA2GnmftESSg/eJkGT1fh0nQ=
k8s.io/kubelet v0.30.9/go.mod h1:j95DrESGPi/QVAZnuLTz5+45He4mF/EqVi9xKiXJyYw=
k8s.io/kubernetes v1.30.9 h1:wo56bPk/+zZcqjiMTWpPuHPyh5vKY/bl75fEKIizneA=
k8s.io/kubernetes v1.30.9/go.mod h1:hV3c+sqOEO0eVqgSo0KW5dOJ6UjGJ2l3Pd9+Qvft8UI=
k8s.io/mount-utils v0.30.9 h1:TJhmRDz0yKWlTHCeDDYsOZSPXQUYV3yDZh0pNpWi9WA=
k8s.io/mount-utils v0.30.9/go.mod h1:9sCVmwGLcV1MPvbZ+rToMDnl1QcGozy+jBPd0MsQLIo=
k8s.io/kube-scheduler v0.30.10 h1:AcapNuui4pWivsnN1yRbyS8a3C9AnZrolMZctQX2r5Q=
k8s.io/kube-scheduler v0.30.10/go.mod h1:jPpxCRJqO1O+tRohfmmy5agbF79OYjtRbqdKEX3D1ks=
k8s.io/kubelet v0.30.10 h1:R8pQq2XiQdsAJVco/TyZjWFarM5YZ5uK/ckPd+qt5Ck=
k8s.io/kubelet v0.30.10/go.mod h1:FO8v1212JoblFctyW/V3ZvL8S47sjV71RZLrTvtsiJA=
k8s.io/kubernetes v1.30.10 h1:/x/z+MTfPkKuEjMJwWdRVxNx7xB54GlGWpcFM6KDwZc=
k8s.io/kubernetes v1.30.10/go.mod h1:DGWYRXHx5NhImLiR9FvIVBsOKxwKZOX6bPF/YP7TqHY=
k8s.io/mount-utils v0.30.10 h1:0dUgagHFCY6G/JvPgOKrHDqguDzP3tKThH9VqAQ+ZIs=
k8s.io/mount-utils v0.30.10/go.mod h1:9sCVmwGLcV1MPvbZ+rToMDnl1QcGozy+jBPd0MsQLIo=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b h1:sgn3ZU783SCgtaSJjpcVVlRqd6GSnlTLKgpAAttJvpI=
k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0 h1:/U5vjBbQn3RChhv7P11uhYvCSm5G2GaIi5AIGBS6r4c=

View File

@ -28,7 +28,7 @@ import (
"k8s.io/klog/v2"
)
func (r *ReconcileSidecarTerminator) executeInPlaceUpdateAction(originalPod *corev1.Pod, sidecars sets.String) error {
func (r *ReconcileSidecarTerminator) executeInPlaceUpdateAction(originalPod *corev1.Pod, sidecars sets.Set[string]) error {
uncompletedSidecars := filterUncompletedSidecars(originalPod, sidecars)
if uncompletedSidecars.Len() == 0 {
return nil
@ -60,22 +60,26 @@ func (r *ReconcileSidecarTerminator) executeInPlaceUpdateAction(originalPod *cor
klog.V(3).InfoS("SidecarTerminator -- InPlace update pod successfully", "pod", klog.KObj(originalPod))
r.recorder.Eventf(originalPod, corev1.EventTypeNormal, "SidecarTerminator",
"Kruise SidecarTerminator is trying to terminate sidecar %v using in-place update", uncompletedSidecars.List())
"Kruise SidecarTerminator is trying to terminate sidecar %v using in-place update", uncompletedSidecars.UnsortedList())
}
return err
}
// updateSidecarsForInPlaceUpdate replace sidecar image with KruiseTerminateSidecarWithImageEnv
func updateSidecarsForInPlaceUpdate(pod *corev1.Pod, sidecars sets.String) (bool, *corev1.Pod) {
func updateSidecarsForInPlaceUpdate(pod *corev1.Pod, sidecars sets.Set[string]) (bool, *corev1.Pod) {
changed := false
for i := range pod.Spec.Containers {
container := &pod.Spec.Containers[i]
if !sidecars.Has(container.Name) {
continue
}
newImage := getImageFromEnv(container)
if container.Image == newImage {
continue
}
changed = true
container.Image = getImageFromEnv(container)
container.Image = newImage
}
return changed, pod
}

View File

@ -29,7 +29,7 @@ import (
"sigs.k8s.io/controller-runtime/pkg/client"
)
func (r *ReconcileSidecarTerminator) executeKillContainerAction(pod *corev1.Pod, sidecars sets.String) error {
func (r *ReconcileSidecarTerminator) executeKillContainerAction(pod *corev1.Pod, sidecars sets.Set[string]) error {
uncompletedSidecars := filterUncompletedSidecars(pod, sidecars)
if uncompletedSidecars.Len() == 0 {
return nil
@ -47,7 +47,7 @@ func (r *ReconcileSidecarTerminator) executeKillContainerAction(pod *corev1.Pod,
}
var sidecarContainers []appsv1alpha1.ContainerRecreateRequestContainer
for _, name := range uncompletedSidecars.List() {
for _, name := range uncompletedSidecars.UnsortedList() {
sidecarContainers = append(sidecarContainers, appsv1alpha1.ContainerRecreateRequestContainer{
Name: name,
})
@ -78,14 +78,14 @@ func (r *ReconcileSidecarTerminator) executeKillContainerAction(pod *corev1.Pod,
klog.V(3).InfoS("SidecarTerminator -- Creating CRR successfully", "containerRecreateRequest", klog.KObj(crr))
r.recorder.Eventf(pod, corev1.EventTypeNormal, "SidecarTerminator",
"Kruise SidecarTerminator is trying to terminate sidecar %v using crr", uncompletedSidecars.List())
"Kruise SidecarTerminator is trying to terminate sidecar %v using crr", uncompletedSidecars.UnsortedList())
}
return err
}
func filterUncompletedSidecars(pod *corev1.Pod, sidecars sets.String) sets.String {
uncompletedSidecars := sets.NewString(sidecars.List()...)
func filterUncompletedSidecars(pod *corev1.Pod, sidecars sets.Set[string]) sets.Set[string] {
uncompletedSidecars := sets.New[string](sidecars.UnsortedList()...)
for i := range pod.Status.ContainerStatuses {
status := &pod.Status.ContainerStatuses[i]
if sidecars.Has(status.Name) && status.State.Terminated != nil {

View File

@ -21,7 +21,6 @@ import (
"encoding/json"
"flag"
"fmt"
"strings"
"time"
corev1 "k8s.io/api/core/v1"
@ -38,7 +37,6 @@ import (
"sigs.k8s.io/controller-runtime/pkg/reconcile"
"sigs.k8s.io/controller-runtime/pkg/source"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
"github.com/openkruise/kruise/pkg/features"
"github.com/openkruise/kruise/pkg/util"
utilclient "github.com/openkruise/kruise/pkg/util/client"
@ -136,22 +134,31 @@ func (r *ReconcileSidecarTerminator) doReconcile(pod *corev1.Pod) (reconcile.Res
if !isInterestingPod(pod) {
return reconcile.Result{}, nil
}
sidecarNeedToExecuteKillContainer, sidecarNeedToExecuteInPlaceUpdate, err := r.groupSidecars(pod)
vk, err := IsPodRunningOnVirtualKubelet(pod, r.Client)
if err != nil {
klog.ErrorS(err, "SidecarTerminator -- Error occurred when try to check if pod is running on virtual-kubelet", "pod", klog.KObj(pod))
return reconcile.Result{}, err
}
if err := r.executeInPlaceUpdateAction(pod, sidecarNeedToExecuteInPlaceUpdate); err != nil {
normalSidecarNames, ignoreExitCodeSidecarNames, inplaceUpdateSidecarNames := getSidecarContainerNames(pod, vk)
if err = r.executeInPlaceUpdateAction(pod, inplaceUpdateSidecarNames); err != nil {
return reconcile.Result{}, err
}
if err = r.executeKillContainerAction(pod, normalSidecarNames); err != nil {
return reconcile.Result{}, err
}
if ignoreExitCodeSidecarNames.Len() == 0 || !containersCompleted(pod, normalSidecarNames) || !containersCompleted(pod, inplaceUpdateSidecarNames) {
return reconcile.Result{}, nil
}
if err := r.markJobPodTerminated(pod); err != nil {
return reconcile.Result{}, err
}
if err := r.executeKillContainerAction(pod, sidecarNeedToExecuteKillContainer); err != nil {
// JobSidecarTerminator relies on the exit code of the sidecar to be zero. If it is non-zero, it will result in a Pod Status of Failed.
// To solve this problem, we can set the Pod Status to Completed in advance.
// However, the above will also have some problems. For example, there are some CSI and ENI controllers that recycle resources based on Pod completion.
// If you set it in advance, the scheduler will think that the resources have been recovered, but the bottom reason is that the resources are still being used by the previous Pod,
// which will result in resource conflict, and then the new Pod startup will fail.
// Because of this, we open a new ENV KRUISE_TERMINATE_SIDECAR_IGNORE_EXIT_CODE to open this ability, the user also need to understand the risk behind, before deciding whether to use.
if err := r.executeKillContainerAction(pod, ignoreExitCodeSidecarNames); err != nil {
return reconcile.Result{}, err
}
@ -184,8 +191,9 @@ func (r *ReconcileSidecarTerminator) markJobPodTerminated(pod *corev1.Pod) error
},
}
mainContainers, _ := groupMainSidecarContainers(pod)
// patch pod phase
if containersSucceeded(pod, getMain(pod)) {
if containersSucceeded(pod, mainContainers) {
status.Phase = corev1.PodSucceeded
} else {
status.Phase = corev1.PodFailed
@ -203,32 +211,7 @@ func (r *ReconcileSidecarTerminator) markJobPodTerminated(pod *corev1.Pod) error
return nil
}
func (r *ReconcileSidecarTerminator) groupSidecars(pod *corev1.Pod) (sets.String, sets.String, error) {
runningOnVK, err := IsPodRunningOnVirtualKubelet(pod, r.Client)
if err != nil {
return nil, nil, client.IgnoreNotFound(err)
}
inPlaceUpdate := sets.NewString()
killContainer := sets.NewString()
for i := range pod.Spec.Containers {
container := &pod.Spec.Containers[i]
for j := range container.Env {
if !runningOnVK && container.Env[j].Name == appsv1alpha1.KruiseTerminateSidecarEnv &&
strings.EqualFold(container.Env[j].Value, "true") {
killContainer.Insert(container.Name)
break
}
if container.Env[j].Name == appsv1alpha1.KruiseTerminateSidecarWithImageEnv &&
container.Env[j].Value != "" {
inPlaceUpdate.Insert(container.Name)
}
}
}
return killContainer, inPlaceUpdate, nil
}
func containersCompleted(pod *corev1.Pod, containers sets.String) bool {
func containersCompleted(pod *corev1.Pod, containers sets.Set[string]) bool {
if len(pod.Spec.Containers) != len(pod.Status.ContainerStatuses) {
return false
}
@ -242,7 +225,7 @@ func containersCompleted(pod *corev1.Pod, containers sets.String) bool {
return true
}
func containersSucceeded(pod *corev1.Pod, containers sets.String) bool {
func containersSucceeded(pod *corev1.Pod, containers sets.Set[string]) bool {
if len(pod.Spec.Containers) != len(pod.Status.ContainerStatuses) {
return false
}

View File

@ -20,21 +20,20 @@ import (
"context"
"encoding/json"
"reflect"
"sort"
"testing"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/api/errors"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/client-go/tools/record"
"sigs.k8s.io/controller-runtime/pkg/client"
"sigs.k8s.io/controller-runtime/pkg/client/fake"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
)
const (
@ -250,24 +249,6 @@ func TestKruiseDaemonStrategy(t *testing.T) {
},
expectedPod: func() *corev1.Pod {
pod := podDemo.DeepCopy()
pod.Status.Phase = corev1.PodFailed
return pod
},
},
{
name: "normal pod with sidecar, restartPolicy=Never, main containers failed and sidecar running",
getIn: func() *corev1.Pod {
podIn := podDemo.DeepCopy()
podIn.Status.ContainerStatuses[0] = failedMainContainerStatus
podIn.Status.ContainerStatuses[1] = runningSidecarContainerStatus
return podIn
},
getCRR: func() *appsv1alpha1.ContainerRecreateRequest {
return crrDemo.DeepCopy()
},
expectedPod: func() *corev1.Pod {
pod := podDemo.DeepCopy()
pod.Status.Phase = corev1.PodFailed
return pod
},
},
@ -302,7 +283,6 @@ func TestKruiseDaemonStrategy(t *testing.T) {
},
expectedPod: func() *corev1.Pod {
pod := podDemo.DeepCopy()
pod.Status.Phase = corev1.PodSucceeded
return pod
},
},
@ -387,7 +367,6 @@ func TestKruiseDaemonStrategy(t *testing.T) {
},
expectedPod: func() *corev1.Pod {
pod := podDemo.DeepCopy()
pod.Status.Phase = corev1.PodSucceeded
return pod
},
},
@ -419,7 +398,6 @@ func TestKruiseDaemonStrategy(t *testing.T) {
},
expectedPod: func() *corev1.Pod {
pod := podDemo.DeepCopy()
pod.Status.Phase = corev1.PodSucceeded
return pod
},
},
@ -451,7 +429,6 @@ func TestKruiseDaemonStrategy(t *testing.T) {
},
expectedPod: func() *corev1.Pod {
pod := podDemo.DeepCopy()
pod.Status.Phase = corev1.PodSucceeded
return pod
},
},
@ -482,6 +459,82 @@ func TestKruiseDaemonStrategy(t *testing.T) {
return pod
},
},
{
name: "normal pod with sidecar, restartPolicy=OnFailure, 2 succeeded main containers, 3 sidecars uncompleted",
getIn: func() *corev1.Pod {
podIn := podDemo.DeepCopy()
podIn.Spec.Containers = []corev1.Container{
mainContainerFactory("main-1"),
mainContainerFactory("main-2"),
sidecarContainerFactory("sidecar-1", "true"),
sidecarContainerFactory("sidecar-2", "true"),
sidecarContainerFactory("sidecar-3", "true"),
}
podIn.Spec.Containers[4].Env = append(podIn.Spec.Containers[4].Env, corev1.EnvVar{
Name: appsv1alpha1.KruiseIgnoreContainerExitCodeEnv,
Value: "true",
})
podIn.Spec.RestartPolicy = corev1.RestartPolicyOnFailure
podIn.Status.ContainerStatuses = []corev1.ContainerStatus{
rename(succeededMainContainerStatus.DeepCopy(), "main-1"),
rename(succeededMainContainerStatus.DeepCopy(), "main-2"),
rename(uncompletedSidecarContainerStatus.DeepCopy(), "sidecar-1"),
rename(uncompletedSidecarContainerStatus.DeepCopy(), "sidecar-2"),
rename(uncompletedSidecarContainerStatus.DeepCopy(), "sidecar-3"),
}
return podIn
},
getCRR: func() *appsv1alpha1.ContainerRecreateRequest {
crr := crrDemo.DeepCopy()
crr.Spec.Containers = []appsv1alpha1.ContainerRecreateRequestContainer{
{Name: "sidecar-2"},
{Name: "sidecar-1"},
}
return crr
},
expectedPod: func() *corev1.Pod {
pod := podDemo.DeepCopy()
return pod
},
},
{
name: "normal pod with sidecar, restartPolicy=OnFailure, 2 succeeded main containers, 2 sidecars completed, 1 uncompleted",
getIn: func() *corev1.Pod {
podIn := podDemo.DeepCopy()
podIn.Spec.Containers = []corev1.Container{
mainContainerFactory("main-1"),
mainContainerFactory("main-2"),
sidecarContainerFactory("sidecar-1", "true"),
sidecarContainerFactory("sidecar-2", "true"),
sidecarContainerFactory("sidecar-3", "true"),
}
podIn.Spec.Containers[4].Env = append(podIn.Spec.Containers[4].Env, corev1.EnvVar{
Name: appsv1alpha1.KruiseIgnoreContainerExitCodeEnv,
Value: "true",
})
podIn.Spec.RestartPolicy = corev1.RestartPolicyOnFailure
podIn.Status.ContainerStatuses = []corev1.ContainerStatus{
rename(succeededMainContainerStatus.DeepCopy(), "main-1"),
rename(succeededMainContainerStatus.DeepCopy(), "main-2"),
rename(completedSidecarContainerStatus.DeepCopy(), "sidecar-1"),
rename(completedSidecarContainerStatus.DeepCopy(), "sidecar-2"),
rename(uncompletedSidecarContainerStatus.DeepCopy(), "sidecar-3"),
}
return podIn
},
getCRR: func() *appsv1alpha1.ContainerRecreateRequest {
crr := crrDemo.DeepCopy()
crr.Spec.Containers = []appsv1alpha1.ContainerRecreateRequestContainer{
{Name: "sidecar-3"},
}
return crr
},
expectedPod: func() *corev1.Pod {
pod := podDemo.DeepCopy()
pod.Status.Phase = corev1.PodSucceeded
return pod
},
},
}
for _, cs := range cases {
@ -514,9 +567,14 @@ func TestKruiseDaemonStrategy(t *testing.T) {
realCRR.TypeMeta.APIVersion = appsv1alpha1.SchemeGroupVersion.String()
realCRR.TypeMeta.Kind = "ContainerRecreateRequest"
if realCRR != nil {
sort.Sort(SortContainers(realCRR.Spec.Containers))
}
if expectCRR != nil {
sort.Sort(SortContainers(expectCRR.Spec.Containers))
}
realBy, _ := json.Marshal(realCRR)
expectBy, _ := json.Marshal(expectCRR)
if !(expectCRR == nil && errors.IsNotFound(err) || reflect.DeepEqual(realBy, expectBy)) {
t.Fatal("Get unexpected CRR")
}
@ -559,6 +617,7 @@ func TestInPlaceUpdateStrategy(t *testing.T) {
podIn.Spec.Containers[1].Env = []corev1.EnvVar{
{Name: appsv1alpha1.KruiseTerminateSidecarWithImageEnv, Value: ExitQuicklyImage},
}
podIn.Spec.NodeName = vkNode.Name
return podIn
},
expectedNumber: 1,
@ -735,3 +794,15 @@ func rename(status *corev1.ContainerStatus, name string) corev1.ContainerStatus
status.Name = name
return *status
}
type SortContainers []appsv1alpha1.ContainerRecreateRequestContainer
func (s SortContainers) Len() int {
return len(s)
}
func (s SortContainers) Swap(i, j int) {
s[i], s[j] = s[j], s[i]
}
func (s SortContainers) Less(i, j int) bool {
return s[i].Name < s[j].Name
}

View File

@ -18,18 +18,14 @@ package sidecarterminator
import (
"context"
"strings"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/types"
"k8s.io/apimachinery/pkg/util/sets"
"k8s.io/client-go/util/workqueue"
"sigs.k8s.io/controller-runtime/pkg/event"
"sigs.k8s.io/controller-runtime/pkg/handler"
"sigs.k8s.io/controller-runtime/pkg/reconcile"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
)
var _ handler.TypedEventHandler[*corev1.Pod] = &enqueueRequestForPod{}
@ -75,55 +71,3 @@ func (p *enqueueRequestForPod) handlePodUpdate(q workqueue.RateLimitingInterface
})
}
}
func isInterestingPod(pod *corev1.Pod) bool {
if pod.DeletionTimestamp != nil ||
pod.Status.Phase == corev1.PodPending ||
pod.Spec.RestartPolicy == corev1.RestartPolicyAlways {
return false
}
sidecars := getSidecar(pod)
if sidecars.Len() == 0 || containersCompleted(pod, sidecars) {
return false
}
switch pod.Spec.RestartPolicy {
case corev1.RestartPolicyNever:
return containersCompleted(pod, getMain(pod))
case corev1.RestartPolicyOnFailure:
return containersSucceeded(pod, getMain(pod))
}
return false
}
func getMain(pod *corev1.Pod) sets.String {
mainNames := sets.NewString()
for i := range pod.Spec.Containers {
if !isSidecar(pod.Spec.Containers[i]) {
mainNames.Insert(pod.Spec.Containers[i].Name)
}
}
return mainNames
}
func getSidecar(pod *corev1.Pod) sets.String {
sidecarNames := sets.NewString()
for i := range pod.Spec.Containers {
if isSidecar(pod.Spec.Containers[i]) {
sidecarNames.Insert(pod.Spec.Containers[i].Name)
}
}
return sidecarNames
}
func isSidecar(container corev1.Container) bool {
for _, env := range container.Env {
if env.Name == appsv1alpha1.KruiseTerminateSidecarEnv && strings.EqualFold(env.Value, "true") {
return true
} else if env.Name == appsv1alpha1.KruiseTerminateSidecarWithImageEnv && env.Value != "" {
return true
}
}
return false
}

View File

@ -0,0 +1,113 @@
/*
Copyright 2023 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package sidecarterminator
import (
"strings"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
corev1 "k8s.io/api/core/v1"
"k8s.io/apimachinery/pkg/util/sets"
)
func isInterestingPod(pod *corev1.Pod) bool {
if pod.DeletionTimestamp != nil ||
pod.Status.Phase == corev1.PodPending ||
pod.Spec.RestartPolicy == corev1.RestartPolicyAlways {
return false
}
mainContainers, sidecarContainers := groupMainSidecarContainers(pod)
if sidecarContainers.Len() == 0 || containersCompleted(pod, sidecarContainers) {
return false
}
switch pod.Spec.RestartPolicy {
case corev1.RestartPolicyNever:
return containersCompleted(pod, mainContainers)
case corev1.RestartPolicyOnFailure:
return containersSucceeded(pod, mainContainers)
}
return false
}
func groupMainSidecarContainers(pod *corev1.Pod) (sets.Set[string], sets.Set[string]) {
mainNames := sets.New[string]()
sidecarNames := sets.New[string]()
for i := range pod.Spec.Containers {
container := pod.Spec.Containers[i]
if isSidecarContainer(container) {
sidecarNames.Insert(pod.Spec.Containers[i].Name)
} else {
mainNames.Insert(pod.Spec.Containers[i].Name)
}
}
return mainNames, sidecarNames
}
func getSidecarContainerNames(pod *corev1.Pod, vk bool) (sets.Set[string], sets.Set[string], sets.Set[string]) {
normalSidecarNames := sets.New[string]()
ignoreExitCodeSidecarNames := sets.New[string]()
inplaceUpdateSidecarNames := sets.New[string]()
for i := range pod.Spec.Containers {
container := pod.Spec.Containers[i]
if vk && isInplaceUpdateSidecar(container) {
inplaceUpdateSidecarNames.Insert(container.Name)
} else if !vk && isIgnoreExitCodeSidecar(container) {
ignoreExitCodeSidecarNames.Insert(container.Name)
} else if !vk && isNormalSidecar(container) {
normalSidecarNames.Insert(container.Name)
}
}
return normalSidecarNames, ignoreExitCodeSidecarNames, inplaceUpdateSidecarNames
}
func isNormalSidecar(container corev1.Container) bool {
for _, env := range container.Env {
if env.Name == appsv1alpha1.KruiseTerminateSidecarEnv && strings.EqualFold(env.Value, "true") {
return true
}
}
return false
}
func isIgnoreExitCodeSidecar(container corev1.Container) bool {
if !isNormalSidecar(container) {
return false
}
for _, env := range container.Env {
if env.Name == appsv1alpha1.KruiseIgnoreContainerExitCodeEnv && strings.EqualFold(env.Value, "true") {
return true
}
}
return false
}
func isInplaceUpdateSidecar(container corev1.Container) bool {
for _, env := range container.Env {
if env.Name == appsv1alpha1.KruiseTerminateSidecarWithImageEnv && env.Value != "" {
return true
}
}
return false
}
func isSidecarContainer(container corev1.Container) bool {
return isNormalSidecar(container) || isInplaceUpdateSidecar(container)
}

View File

@ -0,0 +1,329 @@
/*
Copyright 2023 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package sidecarterminator
import (
"testing"
appsv1alpha1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
corev1 "k8s.io/api/core/v1"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/apimachinery/pkg/util/sets"
)
func TestInterestingPod(t *testing.T) {
cases := []struct {
name string
getPod func() *corev1.Pod
expected bool
}{
{
name: "Interesting, test1",
getPod: func() *corev1.Pod {
return &corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "main",
},
{
Name: "normal-sidecar1",
Env: []corev1.EnvVar{
{
Name: appsv1alpha1.KruiseTerminateSidecarEnv,
Value: "true",
},
},
},
},
RestartPolicy: corev1.RestartPolicyNever,
},
Status: corev1.PodStatus{
ContainerStatuses: []corev1.ContainerStatus{
{
Name: "main",
State: corev1.ContainerState{
Terminated: &corev1.ContainerStateTerminated{
ExitCode: 127,
},
},
},
{
Name: "normal-sidecar1",
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{
StartedAt: metav1.Now(),
},
},
},
},
Phase: corev1.PodRunning,
},
}
},
expected: true,
},
{
name: "Interesting, test2",
getPod: func() *corev1.Pod {
return &corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "main",
},
{
Name: "normal-sidecar1",
Env: []corev1.EnvVar{
{
Name: appsv1alpha1.KruiseTerminateSidecarEnv,
Value: "true",
},
},
},
{
Name: "ignore-sidecar1",
Env: []corev1.EnvVar{
{
Name: appsv1alpha1.KruiseTerminateSidecarEnv,
Value: "true",
},
{
Name: appsv1alpha1.KruiseIgnoreContainerExitCodeEnv,
Value: "true",
},
},
},
},
RestartPolicy: corev1.RestartPolicyOnFailure,
},
Status: corev1.PodStatus{
ContainerStatuses: []corev1.ContainerStatus{
{
Name: "main",
State: corev1.ContainerState{
Terminated: &corev1.ContainerStateTerminated{
ExitCode: 0,
},
},
},
{
Name: "normal-sidecar1",
State: corev1.ContainerState{
Terminated: &corev1.ContainerStateTerminated{
ExitCode: 0,
},
},
},
{
Name: "ignore-sidecar1",
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{
StartedAt: metav1.Now(),
},
},
},
},
},
}
},
expected: true,
},
{
name: "Interesting, test3",
getPod: func() *corev1.Pod {
return &corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "main",
},
{
Name: "normal-sidecar1",
Env: []corev1.EnvVar{
{
Name: appsv1alpha1.KruiseTerminateSidecarEnv,
Value: "true",
},
},
},
{
Name: "inplace-update-sidecar1",
Env: []corev1.EnvVar{
{
Name: appsv1alpha1.KruiseTerminateSidecarWithImageEnv,
Value: "inplace-update/image",
},
},
},
},
RestartPolicy: corev1.RestartPolicyOnFailure,
},
Status: corev1.PodStatus{
ContainerStatuses: []corev1.ContainerStatus{
{
Name: "main",
State: corev1.ContainerState{
Terminated: &corev1.ContainerStateTerminated{
ExitCode: 0,
},
},
},
{
Name: "normal-sidecar1",
State: corev1.ContainerState{
Terminated: &corev1.ContainerStateTerminated{
ExitCode: 0,
},
},
},
{
Name: "inplace-update-sidecar1",
State: corev1.ContainerState{
Running: &corev1.ContainerStateRunning{
StartedAt: metav1.Now(),
},
},
},
},
},
}
},
expected: true,
},
}
for _, cs := range cases {
pod := cs.getPod()
if isInterestingPod(pod) != cs.expected {
t.Errorf("case %s failed, expected %v, got %v", cs.name, cs.expected, isInterestingPod(pod))
}
}
}
func TestGetSidecarContainerNames(t *testing.T) {
cases := []struct {
name string
getPod func() *corev1.Pod
isVK bool
expected func() (sets.Set[string], sets.Set[string], sets.Set[string])
}{
{
name: "normal, test1",
isVK: false,
getPod: func() *corev1.Pod {
return &corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "main",
},
{
Name: "normal-sidecar1",
Env: []corev1.EnvVar{
{
Name: appsv1alpha1.KruiseTerminateSidecarEnv,
Value: "true",
},
{
Name: appsv1alpha1.KruiseTerminateSidecarWithImageEnv,
Value: "true",
},
},
},
{
Name: "ignore-sidecar1",
Env: []corev1.EnvVar{
{
Name: appsv1alpha1.KruiseTerminateSidecarEnv,
Value: "true",
},
{
Name: appsv1alpha1.KruiseIgnoreContainerExitCodeEnv,
Value: "true",
},
},
},
},
},
}
},
expected: func() (sets.Set[string], sets.Set[string], sets.Set[string]) {
return sets.New[string]("normal-sidecar1"), sets.New[string]("ignore-sidecar1"), sets.New[string]()
},
},
{
name: "vk, test2",
isVK: true,
getPod: func() *corev1.Pod {
return &corev1.Pod{
Spec: corev1.PodSpec{
Containers: []corev1.Container{
{
Name: "main",
},
{
Name: "vk-sidecar1",
Env: []corev1.EnvVar{
{
Name: appsv1alpha1.KruiseTerminateSidecarEnv,
Value: "true",
},
{
Name: appsv1alpha1.KruiseTerminateSidecarWithImageEnv,
Value: "true",
},
},
},
{
Name: "ignore-sidecar1",
Env: []corev1.EnvVar{
{
Name: appsv1alpha1.KruiseTerminateSidecarEnv,
Value: "true",
},
{
Name: appsv1alpha1.KruiseIgnoreContainerExitCodeEnv,
Value: "true",
},
},
},
},
},
}
},
expected: func() (sets.Set[string], sets.Set[string], sets.Set[string]) {
return sets.New[string](), sets.New[string](), sets.New[string]("vk-sidecar1")
},
},
}
for _, cs := range cases {
pod := cs.getPod()
normalSidecarNames, ignoreExitCodeSidecarNames, inplaceUpdateSidecarNames := getSidecarContainerNames(pod, cs.isVK)
expected1, expected2, expected3 := cs.expected()
if !normalSidecarNames.Equal(expected1) {
t.Errorf("case %s failed, expected %v, got %v", cs.name, expected1, normalSidecarNames)
}
if !ignoreExitCodeSidecarNames.Equal(expected2) {
t.Errorf("case %s failed, expected %v, got %v", cs.name, expected2, ignoreExitCodeSidecarNames)
}
if !inplaceUpdateSidecarNames.Equal(expected3) {
t.Errorf("case %s failed, expected %v, got %v", cs.name, expected3, inplaceUpdateSidecarNames)
}
}
}

View File

@ -63,7 +63,7 @@ func (m *resultManager) listResults() []Update {
func (m *resultManager) set(id string, key probeKey, result appsv1alpha1.ProbeState, msg string) {
currentTime := metav1.Now()
prev, exists := m.cache.Load(id)
if !exists || prev.(Update).State != result || currentTime.Sub(prev.(Update).LastProbeTime.Time) >= maxSyncProbeTime {
if !exists || prev.(Update).State != result || prev.(Update).Msg != msg || currentTime.Sub(prev.(Update).LastProbeTime.Time).Seconds() >= maxSyncProbeTime {
m.cache.Store(id, Update{id, key, result, msg, currentTime})
m.queue.Add("updateStatus")
}

View File

@ -0,0 +1,323 @@
/*
Copyright 2025 The Kruise Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package podprobe
import (
"crypto/rand"
"math/big"
"testing"
"time"
appsalphav1 "github.com/openkruise/kruise/apis/apps/v1alpha1"
"github.com/openkruise/kruise/pkg/util"
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
"k8s.io/client-go/util/workqueue"
)
func TestResultManagerSet(t *testing.T) {
cases := []struct {
name string
getPrev func() *Update
getCur func() Update
expectLen int
result func() []Update
}{
{
name: "first probe",
getPrev: func() *Update {
return nil
},
getCur: func() Update {
obj := Update{
ContainerID: "cc4c81e9128bf1489fa0975f8cd2ca949423acd840a909c31dfa753fa6cc61f6",
Key: probeKey{
podNs: "default",
podName: "test-pod",
podUID: "fe2ad1bb-f32a-407d-9f43-884b5dff267e",
containerName: "main",
probeName: "healthy",
},
State: appsalphav1.ProbeSucceeded,
Msg: "success, version1",
LastProbeTime: metav1.Now(),
}
return obj
},
expectLen: 1,
result: func() []Update {
obj := Update{
ContainerID: "cc4c81e9128bf1489fa0975f8cd2ca949423acd840a909c31dfa753fa6cc61f6",
Key: probeKey{
podNs: "default",
podName: "test-pod",
podUID: "fe2ad1bb-f32a-407d-9f43-884b5dff267e",
containerName: "main",
probeName: "healthy",
},
State: appsalphav1.ProbeSucceeded,
Msg: "success, version1",
LastProbeTime: metav1.Now(),
}
return []Update{obj}
},
},
{
name: "second probe, no changed",
getPrev: func() *Update {
obj := Update{
ContainerID: "cc4c81e9128bf1489fa0975f8cd2ca949423acd840a909c31dfa753fa6cc61f6",
Key: probeKey{
podNs: "default",
podName: "test-pod",
podUID: "fe2ad1bb-f32a-407d-9f43-884b5dff267e",
containerName: "main",
probeName: "healthy",
},
State: appsalphav1.ProbeSucceeded,
Msg: "success, version1",
LastProbeTime: metav1.Time{Time: metav1.Now().Add(-time.Second * 10)},
}
return &obj
},
getCur: func() Update {
obj := Update{
ContainerID: "cc4c81e9128bf1489fa0975f8cd2ca949423acd840a909c31dfa753fa6cc61f6",
Key: probeKey{
podNs: "default",
podName: "test-pod",
podUID: "fe2ad1bb-f32a-407d-9f43-884b5dff267e",
containerName: "main",
probeName: "healthy",
},
State: appsalphav1.ProbeSucceeded,
Msg: "success, version1",
LastProbeTime: metav1.Now(),
}
return obj
},
expectLen: 0,
result: func() []Update {
obj := Update{
ContainerID: "cc4c81e9128bf1489fa0975f8cd2ca949423acd840a909c31dfa753fa6cc61f6",
Key: probeKey{
podNs: "default",
podName: "test-pod",
podUID: "fe2ad1bb-f32a-407d-9f43-884b5dff267e",
containerName: "main",
probeName: "healthy",
},
State: appsalphav1.ProbeSucceeded,
Msg: "success, version1",
LastProbeTime: metav1.Now(),
}
return []Update{obj}
},
},
{
name: "second probe, state changed",
getPrev: func() *Update {
obj := Update{
ContainerID: "cc4c81e9128bf1489fa0975f8cd2ca949423acd840a909c31dfa753fa6cc61f6",
Key: probeKey{
podNs: "default",
podName: "test-pod",
podUID: "fe2ad1bb-f32a-407d-9f43-884b5dff267e",
containerName: "main",
probeName: "healthy",
},
State: appsalphav1.ProbeSucceeded,
Msg: "success, version1",
LastProbeTime: metav1.Time{Time: metav1.Now().Add(-time.Second * 10)},
}
return &obj
},
getCur: func() Update {
obj := Update{
ContainerID: "cc4c81e9128bf1489fa0975f8cd2ca949423acd840a909c31dfa753fa6cc61f6",
Key: probeKey{
podNs: "default",
podName: "test-pod",
podUID: "fe2ad1bb-f32a-407d-9f43-884b5dff267e",
containerName: "main",
probeName: "healthy",
},
State: appsalphav1.ProbeFailed,
Msg: "failed, version1",
LastProbeTime: metav1.Now(),
}
return obj
},
expectLen: 1,
result: func() []Update {
obj := Update{
ContainerID: "cc4c81e9128bf1489fa0975f8cd2ca949423acd840a909c31dfa753fa6cc61f6",
Key: probeKey{
podNs: "default",
podName: "test-pod",
podUID: "fe2ad1bb-f32a-407d-9f43-884b5dff267e",
containerName: "main",
probeName: "healthy",
},
State: appsalphav1.ProbeFailed,
Msg: "failed, version1",
LastProbeTime: metav1.Now(),
}
return []Update{obj}
},
},
{
name: "second probe, msg changed",
getPrev: func() *Update {
obj := Update{
ContainerID: "cc4c81e9128bf1489fa0975f8cd2ca949423acd840a909c31dfa753fa6cc61f6",
Key: probeKey{
podNs: "default",
podName: "test-pod",
podUID: "fe2ad1bb-f32a-407d-9f43-884b5dff267e",
containerName: "main",
probeName: "healthy",
},
State: appsalphav1.ProbeSucceeded,
Msg: "success, version1",
LastProbeTime: metav1.Time{Time: metav1.Now().Add(-time.Second * 10)},
}
return &obj
},
getCur: func() Update {
obj := Update{
ContainerID: "cc4c81e9128bf1489fa0975f8cd2ca949423acd840a909c31dfa753fa6cc61f6",
Key: probeKey{
podNs: "default",
podName: "test-pod",
podUID: "fe2ad1bb-f32a-407d-9f43-884b5dff267e",
containerName: "main",
probeName: "healthy",
},
State: appsalphav1.ProbeSucceeded,
Msg: "success, version2",
LastProbeTime: metav1.Now(),
}
return obj
},
expectLen: 1,
result: func() []Update {
obj := Update{
ContainerID: "cc4c81e9128bf1489fa0975f8cd2ca949423acd840a909c31dfa753fa6cc61f6",
Key: probeKey{
podNs: "default",
podName: "test-pod",
podUID: "fe2ad1bb-f32a-407d-9f43-884b5dff267e",
containerName: "main",
probeName: "healthy",
},
State: appsalphav1.ProbeSucceeded,
Msg: "success, version2",
LastProbeTime: metav1.Now(),
}
return []Update{obj}
},
},
{
name: "100 probe, no changed, but over ten minutes",
getPrev: func() *Update {
obj := Update{
ContainerID: "cc4c81e9128bf1489fa0975f8cd2ca949423acd840a909c31dfa753fa6cc61f6",
Key: probeKey{
podNs: "default",
podName: "test-pod",
podUID: "fe2ad1bb-f32a-407d-9f43-884b5dff267e",
containerName: "main",
probeName: "healthy",
},
State: appsalphav1.ProbeSucceeded,
Msg: "success, version1",
LastProbeTime: metav1.Time{Time: metav1.Now().Add(-time.Second * 610)},
}
return &obj
},
getCur: func() Update {
obj := Update{
ContainerID: "cc4c81e9128bf1489fa0975f8cd2ca949423acd840a909c31dfa753fa6cc61f6",
Key: probeKey{
podNs: "default",
podName: "test-pod",
podUID: "fe2ad1bb-f32a-407d-9f43-884b5dff267e",
containerName: "main",
probeName: "healthy",
},
State: appsalphav1.ProbeSucceeded,
Msg: "success, version1",
LastProbeTime: metav1.Now(),
}
return obj
},
expectLen: 1,
result: func() []Update {
obj := Update{
ContainerID: "cc4c81e9128bf1489fa0975f8cd2ca949423acd840a909c31dfa753fa6cc61f6",
Key: probeKey{
podNs: "default",
podName: "test-pod",
podUID: "fe2ad1bb-f32a-407d-9f43-884b5dff267e",
containerName: "main",
probeName: "healthy",
},
State: appsalphav1.ProbeSucceeded,
Msg: "success, version1",
LastProbeTime: metav1.Now(),
}
return []Update{obj}
},
},
}
randInt, _ := rand.Int(rand.Reader, big.NewInt(5000))
for _, cs := range cases {
t.Run(cs.name, func(t *testing.T) {
updateQueue := workqueue.NewNamedRateLimitingQueue(
// Backoff duration from 500ms to 50~55s
workqueue.NewItemExponentialFailureRateLimiter(500*time.Millisecond, 50*time.Second+time.Millisecond*time.Duration(randInt.Int64())),
"update_node_pod_probe_status",
)
r := newResultManager(updateQueue)
prev := cs.getPrev()
if prev != nil {
r.cache.Store(prev.ContainerID, Update{prev.ContainerID, prev.Key, prev.State, prev.Msg, prev.LastProbeTime})
}
cur := cs.getCur()
r.set(cur.ContainerID, cur.Key, cur.State, cur.Msg)
if r.queue.Len() != cs.expectLen {
t.Fatalf("expect(%v), but get(%v)", cs.expectLen, r.queue.Len())
}
objs := r.listResults()
for _, result := range cs.result() {
found := false
for _, obj := range objs {
if result.Key == obj.Key {
found = true
if result.Msg != obj.Msg || result.State != obj.State {
t.Fatalf("expect(%v), but get(%v)", util.DumpJSON(result), util.DumpJSON(obj))
}
}
}
if !found {
t.Fatalf("Not found result %s", result.Key)
}
}
})
}
}

View File

@ -62,6 +62,10 @@ var _ = SIGDescribe("SidecarTerminator", func() {
Name: appsv1alpha1.KruiseTerminateSidecarEnv,
Value: "true",
},
{
Name: appsv1alpha1.KruiseIgnoreContainerExitCodeEnv,
Value: "true",
},
},
}