mirror of
https://github.com/vmware-tanzu/velero.git
synced 2026-09-18 14:04:39 +00:00
Merge branch 'release-1.18' into backport-10370-to-release-1.18
Run the E2E test on kind / setup-test-matrix (push) Failing after 4s
e2e-test-kind.yaml / extract (push) Successful in 14s
Run the E2E test on kind / get-go-version (push) Successful in 15s
Run the E2E test on kind / build (push) Failing after 19s
Run the E2E test on kind / run-e2e-test (push) Skipped
Run the E2E test on kind / setup-test-matrix (push) Failing after 4s
e2e-test-kind.yaml / extract (push) Successful in 14s
Run the E2E test on kind / get-go-version (push) Successful in 15s
Run the E2E test on kind / build (push) Failing after 19s
Run the E2E test on kind / run-e2e-test (push) Skipped
This commit is contained in:
@@ -93,8 +93,35 @@ jobs:
|
||||
# and removes older patches of the same minor version
|
||||
# awk -F. '{if(!a[$1"."$2]++)print $1"."$2"."$NF}'
|
||||
run: |
|
||||
set -euo pipefail
|
||||
candidates=$(wget -q -O - "https://hub.docker.com/v2/namespaces/kindest/repositories/node/tags?page_size=50" | grep -o '"name": *"[^"]*' | grep -o '[^"]*$' | grep -E "^v[1-9]\.(2[5-9]|[3-9][0-9])\.[0-9]+$" | awk -F. '{if(!a[$1"."$2]++)print $1"."$2"."$NF}' | sort -r | sed s/v//g)
|
||||
|
||||
# Docker Hub's tag listing can include tags whose manifest was never
|
||||
# published or has since been removed (e.g. kindest/node:v1.37.1). If such
|
||||
# a tag reaches the matrix, its job is guaranteed to fail with
|
||||
# "manifest unknown" once helm/kind-action tries to pull it, which clogs
|
||||
# up the e2e queue on every PR with a red job unrelated to the change
|
||||
# under test. Test-pull each candidate's manifest here and drop any tag
|
||||
# that isn't actually available before building the matrix.
|
||||
valid=()
|
||||
while IFS= read -r v; do
|
||||
[ -z "$v" ] && continue
|
||||
echo "Verifying kindest/node:v${v} image is available..."
|
||||
if docker manifest inspect "kindest/node:v${v}" > /dev/null 2>&1; then
|
||||
valid+=("$v")
|
||||
else
|
||||
echo "::warning::kindest/node:v${v} manifest not found on Docker Hub; excluding from e2e test matrix"
|
||||
fi
|
||||
done <<< "$candidates"
|
||||
|
||||
if [ ${#valid[@]} -eq 0 ]; then
|
||||
echo "::warning::No kindest/node tags passed the manifest availability check; the e2e test matrix will have no Kubernetes versions to test"
|
||||
fi
|
||||
|
||||
k8s_json=$(printf '%s\n' "${valid[@]+"${valid[@]}"}" | jq -R -c -s 'split("\n") | map(select(length > 0))')
|
||||
|
||||
echo "matrix={\
|
||||
\"k8s\":$(wget -q -O - "https://hub.docker.com/v2/namespaces/kindest/repositories/node/tags?page_size=50" | grep -o '"name": *"[^"]*' | grep -o '[^"]*$' | grep -E "^v[1-9]\.(2[5-9]|[3-9][0-9])\.[0-9]+$" | awk -F. '{if(!a[$1"."$2]++)print $1"."$2"."$NF}' | sort -r | sed s/v//g | jq -R -c -s 'split("\n")[:-1]'),\
|
||||
\"k8s\":${k8s_json},\
|
||||
\"labels\":[\
|
||||
\"Basic && (ClusterResource || NodePort || StorageClass)\", \
|
||||
\"ResourceFiltering && !Restic\", \
|
||||
|
||||
@@ -28,5 +28,5 @@ jobs:
|
||||
- name: Linter check
|
||||
uses: golangci/golangci-lint-action@v9
|
||||
with:
|
||||
version: v2.5.0
|
||||
version: v2.13.1
|
||||
args: --verbose
|
||||
|
||||
+3
-3
@@ -13,7 +13,7 @@
|
||||
# limitations under the License.
|
||||
|
||||
# Velero binary build section
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25.11-trixie AS velero-builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.26.7-trixie AS velero-builder
|
||||
|
||||
ARG GOPROXY
|
||||
ARG BIN
|
||||
@@ -49,7 +49,7 @@ RUN mkdir -p /output/usr/bin && \
|
||||
go clean -modcache -cache
|
||||
|
||||
# Restic binary build section
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25.11-trixie AS restic-builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.26.7-trixie AS restic-builder
|
||||
|
||||
ARG GOPROXY
|
||||
ARG BIN
|
||||
@@ -73,7 +73,7 @@ RUN mkdir -p /output/usr/bin && \
|
||||
go clean -modcache -cache
|
||||
|
||||
# Velero image packing section
|
||||
FROM paketobuildpacks/run-jammy-tiny:0.2.139
|
||||
FROM paketobuildpacks/run-jammy-tiny:0.2.165
|
||||
|
||||
LABEL maintainer="Xun Jiang <jxun@vmware.com>"
|
||||
|
||||
|
||||
+1
-1
@@ -15,7 +15,7 @@
|
||||
ARG OS_VERSION=1809
|
||||
|
||||
# Velero binary build section
|
||||
FROM --platform=$BUILDPLATFORM golang:1.25.11-trixie AS velero-builder
|
||||
FROM --platform=$BUILDPLATFORM golang:1.26.7-trixie AS velero-builder
|
||||
|
||||
ARG GOPROXY
|
||||
ARG BIN
|
||||
|
||||
@@ -52,7 +52,7 @@ git_sha = str(local("git rev-parse HEAD", quiet = True, echo_off = True)).strip(
|
||||
|
||||
tilt_helper_dockerfile_header = """
|
||||
# Tilt image
|
||||
FROM golang:1.25.11 as tilt-helper
|
||||
FROM golang:1.26.7 as tilt-helper
|
||||
|
||||
# Support live reloading with Tilt
|
||||
RUN wget --output-document /restart.sh --quiet https://raw.githubusercontent.com/windmilleng/rerun-process-wrapper/master/restart.sh && \
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
Fix issue #10341, avoid mutating the cached node-agent LoadAffinity so the OS node selector term is not appended repeatedly to data mover pods
|
||||
@@ -0,0 +1 @@
|
||||
Fail backup validation when built-in data mover is requested but no node-agent pods are running
|
||||
@@ -0,0 +1 @@
|
||||
Avoid duplicated InitContainer names generated in velero install CLI.
|
||||
@@ -2,7 +2,7 @@ module github.com/vmware-tanzu/velero
|
||||
|
||||
go 1.25.8
|
||||
|
||||
toolchain go1.25.11
|
||||
toolchain go1.26.7
|
||||
|
||||
require (
|
||||
cloud.google.com/go/storage v1.57.2
|
||||
@@ -43,12 +43,12 @@ require (
|
||||
github.com/vmware-tanzu/crash-diagnostics v0.4.3
|
||||
go.uber.org/zap v1.28.0
|
||||
go.yaml.in/yaml/v3 v3.0.4
|
||||
golang.org/x/mod v0.36.0
|
||||
golang.org/x/mod v0.40.0
|
||||
golang.org/x/oauth2 v0.36.0
|
||||
golang.org/x/sys v0.46.0
|
||||
golang.org/x/text v0.38.0
|
||||
golang.org/x/sys v0.47.0
|
||||
golang.org/x/text v0.41.0
|
||||
google.golang.org/api v0.283.0
|
||||
google.golang.org/grpc v1.81.1
|
||||
google.golang.org/grpc v1.82.1
|
||||
google.golang.org/protobuf v1.36.11
|
||||
k8s.io/api v0.33.12
|
||||
k8s.io/apiextensions-apiserver v0.33.12
|
||||
@@ -75,7 +75,7 @@ require (
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2 // indirect
|
||||
github.com/Azure/go-ansiterm v0.0.0-20230124172434-306776ec8161 // indirect
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect
|
||||
github.com/Masterminds/semver/v3 v3.4.0 // indirect
|
||||
@@ -180,7 +180,7 @@ require (
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
github.com/zeebo/blake3 v0.2.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.42.0 // indirect
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.43.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
|
||||
go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
@@ -191,16 +191,16 @@ require (
|
||||
go.starlark.net v0.0.0-20241226192728-8dfa5b98479f // indirect
|
||||
go.uber.org/multierr v1.11.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.4 // indirect
|
||||
golang.org/x/crypto v0.53.0 // indirect
|
||||
golang.org/x/crypto v0.55.0 // indirect
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect
|
||||
golang.org/x/net v0.55.0 // indirect
|
||||
golang.org/x/sync v0.21.0 // indirect
|
||||
golang.org/x/term v0.44.0 // indirect
|
||||
golang.org/x/net v0.58.0 // indirect
|
||||
golang.org/x/sync v0.22.0 // indirect
|
||||
golang.org/x/term v0.45.0 // indirect
|
||||
golang.org/x/time v0.15.0 // indirect
|
||||
golang.org/x/tools v0.45.0 // indirect
|
||||
golang.org/x/tools v0.49.0 // indirect
|
||||
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 // indirect
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68 // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
|
||||
@@ -50,8 +50,8 @@ github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0 h1:XRzhVemXdgv
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v1.6.0/go.mod h1:HKpQxkWaGLJ+D/5H8QRpyQXA1eKjxkFlOMwck5+33Jk=
|
||||
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5 h1:IEjq88XO4PuBDcvmjQJcQGg+w+UaafSy8G5Kcb5tBhI=
|
||||
github.com/GehirnInc/crypt v0.0.0-20230320061759-8cc1b52080c5/go.mod h1:exZ0C/1emQJAw5tHOaUDyY1ycttqBAPcxuzf7QbY6ec=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 h1:rIkQfkCOVKc1OiRCNcSDD8ml5RJlZbH/Xsq7lbpynwc=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0/go.mod h1:RD2SsorTmYhF6HkTmDw7KmPYQk8OBYwTkuasChwv7R4=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs=
|
||||
github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg=
|
||||
@@ -440,8 +440,8 @@ github.com/zeebo/pcg v1.0.1 h1:lyqfGeWiv4ahac6ttHs+I5hwtH/+1mrhlCtVNQM2kHo=
|
||||
github.com/zeebo/pcg v1.0.1/go.mod h1:09F0S9iiKrwn9rlI5yjLkmrug154/YRW6KnnXVDM/l4=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.42.0 h1:kpt2PEJuOuqYkPcktfJqWWDjTEd/FNgrxcniL7kQrXQ=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.42.0/go.mod h1:W9zQ439utxymRrXsUOzZbFX4JhLxXU4+ZnCt8GG7yA8=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.43.0 h1:62yY3dT7/ShwOxzA0RsKRgshBmfElKI4d/Myu2OxDFU=
|
||||
go.opentelemetry.io/contrib/detectors/gcp v1.43.0/go.mod h1:RyaZMFY7yi1kAs45S6mbFGz8O8rqB0dTY14uzvG4LCs=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc=
|
||||
go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
|
||||
@@ -473,27 +473,27 @@ go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
|
||||
golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 h1:2dVuKD2vS7b0QIHQbpyTISPd0LeHDbnYEryqj5Q1ug8=
|
||||
golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56/go.mod h1:M4RDyNAINzryxdtnbRXRL/OHtkFuWGRjvuhBJpk2IlY=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
golang.org/x/mod v0.36.0 h1:JJjpVx6myfUsUdAzZuOSTTmRE0PfZeNWzzvKrP7amb4=
|
||||
golang.org/x/mod v0.36.0/go.mod h1:moc6ELqsWcOw5Ef3xVprK5ul/MvtVvkIXLziUOICjUQ=
|
||||
golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=
|
||||
golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE=
|
||||
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
|
||||
golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
|
||||
golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
|
||||
golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
|
||||
golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
|
||||
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
|
||||
golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -505,22 +505,22 @@ golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBc
|
||||
golang.org/x/sys v0.0.0-20220503163025-988cb79eb6c6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
|
||||
golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.45.0 h1:NwWyBmoJCbfTHpxrWoZ9C6/VxOf7ic219I8xZZFdrf0=
|
||||
golang.org/x/term v0.45.0/go.mod h1:9aqxs0blBcrm/n0L9QW0aRVD+ktan8ssZromtqJC43w=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
|
||||
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
|
||||
golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
|
||||
golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
|
||||
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE=
|
||||
golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA=
|
||||
golang.org/x/tools v0.45.0 h1:18qN3FAooORvApf5XjCXgsuayZOEtXf6JK18I3+ONa8=
|
||||
golang.org/x/tools v0.45.0/go.mod h1:LuUGqqaXcXMEFEruIVJVm5mgDD8vww/z/SR1gQ4uE/0=
|
||||
golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI=
|
||||
golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
@@ -533,12 +533,12 @@ google.golang.org/api v0.283.0 h1:0lkp8u0MPwJVHqRL+nJlMAoZVVzbmiXmFHXMOTmSPik=
|
||||
google.golang.org/api v0.283.0/go.mod h1:6Wssta4c5n9qHq5CBhmlai5h/PUa1djdDAIhYEHyvcM=
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0=
|
||||
google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
|
||||
google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68 h1:PvEgGJf9C/1u5CHkInMg7UFYYUoiaQmW2LbtH0pjB78=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
|
||||
google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
|
||||
google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
|
||||
google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
|
||||
google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
# See the License for the specific language governing permissions and
|
||||
# limitations under the License.
|
||||
|
||||
FROM --platform=$TARGETPLATFORM golang:1.25.11-trixie
|
||||
FROM --platform=$TARGETPLATFORM golang:1.26.7-trixie
|
||||
|
||||
ARG GOPROXY
|
||||
|
||||
@@ -98,7 +98,7 @@ RUN ARCH=$(go env GOARCH) && \
|
||||
# get golangci-lint
|
||||
# Use "go install" so the download goes through GOPROXY instead of the GitHub
|
||||
# release API/CDN, which has been returning intermittent/persistent HTTP 504s.
|
||||
RUN go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.5.0
|
||||
RUN go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@v2.13.1
|
||||
|
||||
# install kubectl
|
||||
RUN curl -LO https://storage.googleapis.com/kubernetes-release/release/$(curl -s https://storage.googleapis.com/kubernetes-release/release/stable.txt)/bin/linux/$(go env GOARCH)/kubectl
|
||||
|
||||
+32
-32
@@ -1,5 +1,5 @@
|
||||
diff --git a/go.mod b/go.mod
|
||||
index 5f939c481..891dbd4e7 100644
|
||||
index 5f939c4..2bd75e5 100644
|
||||
--- a/go.mod
|
||||
+++ b/go.mod
|
||||
@@ -1,15 +1,15 @@
|
||||
@@ -33,13 +33,13 @@ index 5f939c481..891dbd4e7 100644
|
||||
- golang.org/x/term v0.4.0
|
||||
- golang.org/x/text v0.6.0
|
||||
- google.golang.org/api v0.106.0
|
||||
+ golang.org/x/crypto v0.52.0
|
||||
+ golang.org/x/net v0.55.0
|
||||
+ golang.org/x/crypto v0.53.0
|
||||
+ golang.org/x/net v0.56.0
|
||||
+ golang.org/x/oauth2 v0.36.0
|
||||
+ golang.org/x/sync v0.20.0
|
||||
+ golang.org/x/sys v0.45.0
|
||||
+ golang.org/x/term v0.43.0
|
||||
+ golang.org/x/text v0.37.0
|
||||
+ golang.org/x/sync v0.21.0
|
||||
+ golang.org/x/sys v0.46.0
|
||||
+ golang.org/x/term v0.44.0
|
||||
+ golang.org/x/text v0.39.0
|
||||
+ google.golang.org/api v0.283.0
|
||||
)
|
||||
|
||||
@@ -56,7 +56,7 @@ index 5f939c481..891dbd4e7 100644
|
||||
+ cloud.google.com/go/iam v1.5.3 // indirect
|
||||
+ cloud.google.com/go/monitoring v1.24.3 // indirect
|
||||
github.com/Azure/azure-sdk-for-go/sdk/internal v1.1.2 // indirect
|
||||
+ github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 // indirect
|
||||
+ github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 // indirect
|
||||
+ github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 // indirect
|
||||
+ github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/resourcemapping v0.53.0 // indirect
|
||||
+ github.com/cncf/xds/go v0.0.0-20260202195803-dba9d589def2 // indirect
|
||||
@@ -99,7 +99,7 @@ index 5f939c481..891dbd4e7 100644
|
||||
- google.golang.org/protobuf v1.28.1 // indirect
|
||||
+ github.com/spiffe/go-spiffe/v2 v2.6.0 // indirect
|
||||
+ go.opentelemetry.io/auto/sdk v1.2.1 // indirect
|
||||
+ go.opentelemetry.io/contrib/detectors/gcp v1.42.0 // indirect
|
||||
+ go.opentelemetry.io/contrib/detectors/gcp v1.43.0 // indirect
|
||||
+ go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 // indirect
|
||||
+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 // indirect
|
||||
+ go.opentelemetry.io/otel v1.43.0 // indirect
|
||||
@@ -109,9 +109,9 @@ index 5f939c481..891dbd4e7 100644
|
||||
+ go.opentelemetry.io/otel/trace v1.43.0 // indirect
|
||||
+ golang.org/x/time v0.15.0 // indirect
|
||||
+ google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 // indirect
|
||||
+ google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 // indirect
|
||||
+ google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 // indirect
|
||||
+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68 // indirect
|
||||
+ google.golang.org/grpc v1.81.1 // indirect
|
||||
+ google.golang.org/grpc v1.82.1 // indirect
|
||||
+ google.golang.org/protobuf v1.36.11 // indirect
|
||||
gopkg.in/ini.v1 v1.67.0 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
@@ -120,7 +120,7 @@ index 5f939c481..891dbd4e7 100644
|
||||
-go 1.18
|
||||
+go 1.25.8
|
||||
diff --git a/go.sum b/go.sum
|
||||
index 026e1d2fa..94d984253 100644
|
||||
index 026e1d2..738e46e 100644
|
||||
--- a/go.sum
|
||||
+++ b/go.sum
|
||||
@@ -1,42 +1,61 @@
|
||||
@@ -169,8 +169,8 @@ index 026e1d2fa..94d984253 100644
|
||||
github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1 h1:BWe8a+f/t+7KY7zH2mqygeUD0t8hNFXe08p1Pb3/jKE=
|
||||
-github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
|
||||
+github.com/AzureAD/microsoft-authentication-library-for-go v0.5.1/go.mod h1:Vt9sXTKwMyGcOxSmLDMnGPgqsUg7m8pe215qMLrDXw4=
|
||||
+github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0 h1:DHa2U07rk8syqvCge0QIGMCE1WxGj9njT44GH7zNJLQ=
|
||||
+github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.31.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0=
|
||||
+github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0 h1:rIkQfkCOVKc1OiRCNcSDD8ml5RJlZbH/Xsq7lbpynwc=
|
||||
+github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.32.0/go.mod h1:RD2SsorTmYhF6HkTmDw7KmPYQk8OBYwTkuasChwv7R4=
|
||||
+github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0 h1:owcC2UnmsZycprQ5RfRgjydWhuoxg71LUfyiQdijZuM=
|
||||
+github.com/GoogleCloudPlatform/opentelemetry-operations-go/exporter/metric v0.53.0/go.mod h1:ZPpqegjbE99EPKsu3iUWV22A04wzGPcAY/ziSIQEEgs=
|
||||
+github.com/GoogleCloudPlatform/opentelemetry-operations-go/internal/cloudmock v0.53.0 h1:4LP6hvB4I5ouTbGgWtixJhgED6xdf67twf9PoY96Tbg=
|
||||
@@ -358,8 +358,8 @@ index 026e1d2fa..94d984253 100644
|
||||
-go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo=
|
||||
+go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
|
||||
+go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
|
||||
+go.opentelemetry.io/contrib/detectors/gcp v1.42.0 h1:kpt2PEJuOuqYkPcktfJqWWDjTEd/FNgrxcniL7kQrXQ=
|
||||
+go.opentelemetry.io/contrib/detectors/gcp v1.42.0/go.mod h1:W9zQ439utxymRrXsUOzZbFX4JhLxXU4+ZnCt8GG7yA8=
|
||||
+go.opentelemetry.io/contrib/detectors/gcp v1.43.0 h1:62yY3dT7/ShwOxzA0RsKRgshBmfElKI4d/Myu2OxDFU=
|
||||
+go.opentelemetry.io/contrib/detectors/gcp v1.43.0/go.mod h1:RyaZMFY7yi1kAs45S6mbFGz8O8rqB0dTY14uzvG4LCs=
|
||||
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04=
|
||||
+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc=
|
||||
+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
|
||||
@@ -386,8 +386,8 @@ index 026e1d2fa..94d984253 100644
|
||||
-golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
-golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
-golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc=
|
||||
+golang.org/x/crypto v0.52.0 h1:RMs7fP2rXdep0CftQlK8Uf+kibLm7qkCcradZWYz988=
|
||||
+golang.org/x/crypto v0.52.0/go.mod h1:1QgfPxDqh0T2M/elOJtp9RvuR95kVjir0e6/BvEmGbc=
|
||||
+golang.org/x/crypto v0.53.0 h1:QZ4Muo8THX6CizN2vPPd5fBGHyogrdK9fG4wLPFUsto=
|
||||
+golang.org/x/crypto v0.53.0/go.mod h1:DNLU434OwVakk9PzuwV8w62mAJpRJL3vsgcfp4Qnsio=
|
||||
golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
|
||||
-golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
-golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
|
||||
@@ -406,8 +406,8 @@ index 026e1d2fa..94d984253 100644
|
||||
-golang.org/x/oauth2 v0.4.0/go.mod h1:RznEsdpjGAINPTOF0UH/t+xJ75L18YO3Ho6Pyn+uRec=
|
||||
-golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
-golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
+golang.org/x/net v0.55.0 h1:bcvxaJn3e1U6InsFWt1JUq1aSjnRxLzT2rtD2KfkDF8=
|
||||
+golang.org/x/net v0.55.0/go.mod h1:L5U2KuzuOe1lY7Z+aWVIKK6qEeJXnXV9yzGA+WCHJww=
|
||||
+golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
|
||||
+golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
|
||||
+golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
|
||||
+golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
@@ -415,8 +415,8 @@ index 026e1d2fa..94d984253 100644
|
||||
-golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o=
|
||||
-golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
-golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
+golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
|
||||
+golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
+golang.org/x/sync v0.21.0 h1:HLII4xRRTtCRkxYp4HNFF0Js/Og6q2i++KXbg0gHCwM=
|
||||
+golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
@@ -431,21 +431,21 @@ index 026e1d2fa..94d984253 100644
|
||||
golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
-golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
|
||||
-golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
+golang.org/x/sys v0.45.0 h1:dO4czNzziLiiXplLQgBCEpCvXQ3dnkn0SdaZSYdQ+FY=
|
||||
+golang.org/x/sys v0.45.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
+golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
|
||||
+golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
-golang.org/x/term v0.4.0 h1:O7UWfv5+A2qiuulQk30kVinPoMtoIPeVaKLEgLpVkvg=
|
||||
-golang.org/x/term v0.4.0/go.mod h1:9P2UbLfCdcvo3p/nzKvsmas4TnlujnuoV9hGgYzW1lQ=
|
||||
+golang.org/x/term v0.43.0 h1:S4RLU2sB31O/NCl+zFN9Aru9A/Cq2aqKpTZJ6B+DwT4=
|
||||
+golang.org/x/term v0.43.0/go.mod h1:lrhlHNdQJHO+1qVYiHfFKVuVioJIheAc3fBSMFYEIsk=
|
||||
+golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
|
||||
+golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
-golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
|
||||
-golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
|
||||
-golang.org/x/text v0.6.0 h1:3XmdazWV+ubf7QgHSTWeykHOci5oeekaGJBLkrkaw4k=
|
||||
-golang.org/x/text v0.6.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
|
||||
+golang.org/x/text v0.37.0 h1:Cqjiwd9eSg8e0QAkyCaQTNHFIIzWtidPahFWR83rTrc=
|
||||
+golang.org/x/text v0.37.0/go.mod h1:a5sjxXGs9hsn/AJVwuElvCAo9v8QYLzvavO5z2PiM38=
|
||||
+golang.org/x/text v0.39.0 h1:UbZz4pLOvn600D6Oh6GGEI6VAmndrEBLv8/6BEXzyus=
|
||||
+golang.org/x/text v0.39.0/go.mod h1:3UwRclnC2g0TU9x8PZiyfOajCd1zaUNHF9cvqcQZ+ZM=
|
||||
+golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
|
||||
+golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
|
||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
|
||||
@@ -498,12 +498,12 @@ index 026e1d2fa..94d984253 100644
|
||||
+google.golang.org/api v0.283.0/go.mod h1:6Wssta4c5n9qHq5CBhmlai5h/PUa1djdDAIhYEHyvcM=
|
||||
+google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0=
|
||||
+google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I=
|
||||
+google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI=
|
||||
+google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y=
|
||||
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478 h1:yQugLulqltosq0B/f8l4w9VryjV+N/5gcW0jQ3N8Qec=
|
||||
+google.golang.org/genproto/googleapis/api v0.0.0-20260414002931-afd174a4e478/go.mod h1:C6ADNqOxbgdUUeRTU+LCHDPB9ttAMCTff6auwCVa4uc=
|
||||
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68 h1:PvEgGJf9C/1u5CHkInMg7UFYYUoiaQmW2LbtH0pjB78=
|
||||
+google.golang.org/genproto/googleapis/rpc v0.0.0-20260523011958-0a33c5d7ca68/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
|
||||
+google.golang.org/grpc v1.81.1 h1:VnnIIZ88UzOOKLukQi+ImGz8O1Wdp8nAGGnvOfEIWQQ=
|
||||
+google.golang.org/grpc v1.81.1/go.mod h1:xGH9GfzOyMTGIOXBJmXt+BX/V0kcdQbdcuwQ/zNw42I=
|
||||
+google.golang.org/grpc v1.82.1 h1:NnAxzGRA0677vCa4BUkOAnO5+FfQqVl9iUXeD0IqcGE=
|
||||
+google.golang.org/grpc v1.82.1/go.mod h1:yzTZ1TB1Z3SG+LIYaI+WiE8D5+PZ3ArnrSp8zF3+/ZA=
|
||||
+google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
|
||||
+google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
||||
@@ -47,8 +47,10 @@ import (
|
||||
velerov1api "github.com/vmware-tanzu/velero/pkg/apis/velero/v1"
|
||||
velerov2alpha1 "github.com/vmware-tanzu/velero/pkg/apis/velero/v2alpha1"
|
||||
veleroclient "github.com/vmware-tanzu/velero/pkg/client"
|
||||
"github.com/vmware-tanzu/velero/pkg/datamover"
|
||||
"github.com/vmware-tanzu/velero/pkg/kuberesource"
|
||||
"github.com/vmware-tanzu/velero/pkg/label"
|
||||
"github.com/vmware-tanzu/velero/pkg/nodeagent"
|
||||
plugincommon "github.com/vmware-tanzu/velero/pkg/plugin/framework/common"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/utils/volumehelper"
|
||||
"github.com/vmware-tanzu/velero/pkg/plugin/velero"
|
||||
@@ -335,6 +337,17 @@ func (p *pvcBackupItemAction) Execute(
|
||||
return nil, nil, "", nil, err
|
||||
}
|
||||
|
||||
// validate that the node-agent daemonset is ready when snapshot data movement with
|
||||
// the built-in data mover is requested. Without this, the DataUpload CR will be
|
||||
// created but never processed (the DataUpload controller runs inside node-agent),
|
||||
// causing the backup to hang until itemOperationTimeout expires.
|
||||
if boolptr.IsSetToTrue(backup.Spec.SnapshotMoveData) && datamover.IsBuiltInUploader(backup.Spec.DataMover) {
|
||||
if err := nodeagent.IsReady(context.TODO(), backup.Namespace, p.crClient); err != nil {
|
||||
p.log.WithError(err).Error("cannot perform snapshot data movement without running node-agent pods")
|
||||
return nil, nil, "", nil, errors.Wrap(err, "CSI PVC BIA cannot proceed: node-agent is not ready for snapshot data movement")
|
||||
}
|
||||
}
|
||||
|
||||
vs, err := p.getVolumeSnapshotReference(context.TODO(), pvc, backup)
|
||||
if err != nil {
|
||||
return nil, nil, "", nil, err
|
||||
|
||||
@@ -40,6 +40,7 @@ import (
|
||||
"github.com/cockroachdb/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
"github.com/stretchr/testify/require"
|
||||
appsv1api "k8s.io/api/apps/v1"
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
storagev1api "k8s.io/api/storage/v1"
|
||||
"k8s.io/apimachinery/pkg/api/resource"
|
||||
@@ -94,6 +95,7 @@ func TestExecute(t *testing.T) {
|
||||
expectedDataUpload *velerov2alpha1.DataUpload
|
||||
expectedPVC *corev1api.PersistentVolumeClaim
|
||||
resourcePolicy *corev1api.ConfigMap
|
||||
extraObjects []runtime.Object
|
||||
failVSCreate bool
|
||||
skipVSReadyUpdate bool // New flag to control VS readiness
|
||||
}{
|
||||
@@ -122,12 +124,21 @@ func TestExecute(t *testing.T) {
|
||||
expectErr: true, // Expect an error, but the exact message can vary
|
||||
},
|
||||
{
|
||||
name: "Test SnapshotMoveData",
|
||||
backup: builder.ForBackup("velero", "test").SnapshotMoveData(true).CSISnapshotTimeout(1 * time.Minute).Result(),
|
||||
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").StorageClass("testSC").Phase(corev1api.ClaimBound).Result(),
|
||||
pv: builder.ForPersistentVolume("testPV").CSI("hostpath", "testVolume").Result(),
|
||||
sc: builder.ForStorageClass("testSC").Provisioner("hostpath").Result(),
|
||||
vsClass: builder.ForVolumeSnapshotClass("testVSClass").Driver("hostpath").ObjectMeta(builder.WithLabels(velerov1api.VolumeSnapshotClassSelectorLabel, "")).Result(),
|
||||
name: "Test SnapshotMoveData",
|
||||
backup: builder.ForBackup("velero", "test").SnapshotMoveData(true).CSISnapshotTimeout(1 * time.Minute).Result(),
|
||||
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").StorageClass("testSC").Phase(corev1api.ClaimBound).Result(),
|
||||
pv: builder.ForPersistentVolume("testPV").CSI("hostpath", "testVolume").Result(),
|
||||
sc: builder.ForStorageClass("testSC").Provisioner("hostpath").Result(),
|
||||
vsClass: builder.ForVolumeSnapshotClass("testVSClass").Driver("hostpath").ObjectMeta(builder.WithLabels(velerov1api.VolumeSnapshotClassSelectorLabel, "")).Result(),
|
||||
extraObjects: []runtime.Object{
|
||||
&corev1api.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "linux-node", Labels: map[string]string{"kubernetes.io/os": "linux"}},
|
||||
},
|
||||
&appsv1api.DaemonSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "velero", Name: "node-agent"},
|
||||
Status: appsv1api.DaemonSetStatus{NumberReady: 3},
|
||||
},
|
||||
},
|
||||
operationID: ".",
|
||||
expectedDataUpload: &velerov2alpha1.DataUpload{
|
||||
TypeMeta: metav1.TypeMeta{
|
||||
@@ -167,18 +178,37 @@ func TestExecute(t *testing.T) {
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "Verify PVC is modified as expected",
|
||||
backup: builder.ForBackup("velero", "test").SnapshotMoveData(true).CSISnapshotTimeout(1 * time.Minute).Result(),
|
||||
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").StorageClass("testSC").Phase(corev1api.ClaimBound).Result(),
|
||||
pv: builder.ForPersistentVolume("testPV").CSI("hostpath", "testVolume").Result(),
|
||||
sc: builder.ForStorageClass("testSC").Provisioner("hostpath").Result(),
|
||||
vsClass: builder.ForVolumeSnapshotClass("tescVSClass").Driver("hostpath").ObjectMeta(builder.WithLabels(velerov1api.VolumeSnapshotClassSelectorLabel, "")).Result(),
|
||||
name: "Verify PVC is modified as expected",
|
||||
backup: builder.ForBackup("velero", "test").SnapshotMoveData(true).CSISnapshotTimeout(1 * time.Minute).Result(),
|
||||
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").StorageClass("testSC").Phase(corev1api.ClaimBound).Result(),
|
||||
pv: builder.ForPersistentVolume("testPV").CSI("hostpath", "testVolume").Result(),
|
||||
sc: builder.ForStorageClass("testSC").Provisioner("hostpath").Result(),
|
||||
vsClass: builder.ForVolumeSnapshotClass("tescVSClass").Driver("hostpath").ObjectMeta(builder.WithLabels(velerov1api.VolumeSnapshotClassSelectorLabel, "")).Result(),
|
||||
extraObjects: []runtime.Object{
|
||||
&corev1api.Node{
|
||||
ObjectMeta: metav1.ObjectMeta{Name: "linux-node", Labels: map[string]string{"kubernetes.io/os": "linux"}},
|
||||
},
|
||||
&appsv1api.DaemonSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "velero", Name: "node-agent"},
|
||||
Status: appsv1api.DaemonSetStatus{NumberReady: 3},
|
||||
},
|
||||
},
|
||||
operationID: ".",
|
||||
expectedPVC: builder.ForPersistentVolumeClaim("velero", "testPVC").
|
||||
ObjectMeta(builder.WithAnnotations(velerov1api.MustIncludeAdditionalItemAnnotation, "true", velerov1api.DataUploadNameAnnotation, "velero/"),
|
||||
builder.WithLabels(velerov1api.BackupNameLabel, "test")).
|
||||
VolumeName("testPV").StorageClass("testSC").Phase(corev1api.ClaimBound).Result(),
|
||||
},
|
||||
{
|
||||
name: "Test SnapshotMoveData without node-agent",
|
||||
backup: builder.ForBackup("velero", "test").SnapshotMoveData(true).CSISnapshotTimeout(1 * time.Minute).Result(),
|
||||
pvc: builder.ForPersistentVolumeClaim("velero", "testPVC").VolumeName("testPV").StorageClass("testSC").Phase(corev1api.ClaimBound).Result(),
|
||||
pv: builder.ForPersistentVolume("testPV").CSI("hostpath", "testVolume").Result(),
|
||||
sc: builder.ForStorageClass("testSC").Provisioner("hostpath").Result(),
|
||||
vsClass: builder.ForVolumeSnapshotClass("testVSClass").Driver("hostpath").ObjectMeta(builder.WithLabels(velerov1api.VolumeSnapshotClassSelectorLabel, "")).Result(),
|
||||
expectErr: true,
|
||||
skipVSReadyUpdate: true,
|
||||
},
|
||||
{
|
||||
name: "Test ResourcePolicy",
|
||||
backup: builder.ForBackup("velero", "test").ResourcePolicies("resourcePolicy").SnapshotVolumes(false).CSISnapshotTimeout(time.Duration(3600) * time.Second).Result(),
|
||||
@@ -210,6 +240,7 @@ func TestExecute(t *testing.T) {
|
||||
if tc.resourcePolicy != nil {
|
||||
objects = append(objects, tc.resourcePolicy)
|
||||
}
|
||||
objects = append(objects, tc.extraObjects...)
|
||||
|
||||
var crClient crclient.Client
|
||||
if tc.failVSCreate {
|
||||
|
||||
@@ -18,10 +18,12 @@ package builder
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
apimachineryRuntime "k8s.io/apimachinery/pkg/runtime"
|
||||
utilrand "k8s.io/apimachinery/pkg/util/rand"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/label"
|
||||
)
|
||||
@@ -42,15 +44,22 @@ func ForContainer(name, image string) *ContainerBuilder {
|
||||
}
|
||||
|
||||
// ForPluginContainer is a helper builder specifically for plugin init containers
|
||||
func ForPluginContainer(image string, pullPolicy corev1api.PullPolicy) *ContainerBuilder {
|
||||
func ForPluginContainer(image string, pullPolicy corev1api.PullPolicy, existingContainers []corev1api.Container) *ContainerBuilder {
|
||||
volumeMount := ForVolumeMount("plugins", "/target").Result()
|
||||
return ForContainer(getName(image), image).PullPolicy(pullPolicy).VolumeMounts(volumeMount)
|
||||
return ForContainer(getName(image, existingContainers), image).PullPolicy(pullPolicy).VolumeMounts(volumeMount)
|
||||
}
|
||||
|
||||
// getName returns the 'name' component of a docker image that includes the entire string
|
||||
// except the registry name, and transforms the combined string into a DNS-1123 compatible name
|
||||
// that fits within the 63-character limit for Kubernetes container names.
|
||||
func getName(image string) string {
|
||||
// It appends a random string if there is a collision with existing container names.
|
||||
func getName(image string, existingContainers []corev1api.Container) string {
|
||||
// Convert existingContainers to a map for O(1) collision lookups
|
||||
existingNames := make(map[string]bool, len(existingContainers))
|
||||
for _, c := range existingContainers {
|
||||
existingNames[c.Name] = true
|
||||
}
|
||||
|
||||
slashIndex := strings.Index(image, "/")
|
||||
slashCount := 0
|
||||
if slashIndex >= 0 {
|
||||
@@ -88,7 +97,20 @@ func getName(image string) string {
|
||||
name := re.Replace(image[start:end])
|
||||
|
||||
// Ensure the name doesn't exceed Kubernetes container name length limit
|
||||
return label.GetValidName(name)
|
||||
name = label.GetValidName(name)
|
||||
|
||||
for existingNames[name] {
|
||||
name = re.Replace(image[start:end])
|
||||
if len(name) > 57 {
|
||||
// Leave 6 characters for "-xxxxx" random string
|
||||
name = name[:57]
|
||||
name = strings.TrimSuffix(name, "-")
|
||||
}
|
||||
name = fmt.Sprintf("%s-%s", name, utilrand.String(5))
|
||||
name = label.GetValidName(name)
|
||||
}
|
||||
|
||||
return name
|
||||
}
|
||||
|
||||
// Result returns the built Container.
|
||||
|
||||
@@ -16,16 +16,19 @@ limitations under the License.
|
||||
package builder
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func TestGetName(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
image string
|
||||
expected string
|
||||
name string
|
||||
image string
|
||||
existingContainers []corev1api.Container
|
||||
expected string
|
||||
}{
|
||||
{
|
||||
name: "image name with registry hostname and tag",
|
||||
@@ -92,11 +95,25 @@ func TestGetName(t *testing.T) {
|
||||
image: "quay.io/vmware-tanzu/velero@sha256:a75f9e8c3ced3943515f249597be389f8233e1258d289b11184796edceaa7dab",
|
||||
expected: "vmware-tanzu-velero",
|
||||
},
|
||||
{
|
||||
name: "duplicate plugin name",
|
||||
image: "gcr.io/my-repo/my-image:latest",
|
||||
existingContainers: []corev1api.Container{
|
||||
{Name: "my-repo-my-image"},
|
||||
},
|
||||
expected: "my-repo-my-image-", // we will check it has the prefix
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
assert.Equal(t, test.expected, getName(test.image))
|
||||
if test.name == "duplicate plugin name" {
|
||||
result := getName(test.image, test.existingContainers)
|
||||
assert.True(t, strings.HasPrefix(result, test.expected), "expected prefix %s in %s", test.expected, result)
|
||||
assert.Len(t, result, len(test.expected)+5)
|
||||
} else {
|
||||
assert.Equal(t, test.expected, getName(test.image, test.existingContainers))
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -117,7 +134,7 @@ func TestGetNameWithLongPaths(t *testing.T) {
|
||||
// Should be exactly 63 characters (truncated with hash)
|
||||
assert.Len(t, result, 63)
|
||||
// Should be deterministic
|
||||
result2 := getName("arohcpsvcdev.azurecr.io/redhat-user-workloads/ocp-art-tenant/oadp-hypershift-oadp-plugin-main@sha256:adb840bf3890b4904a8cdda1a74c82cf8d96c52eba9944ac10e795335d6fd450")
|
||||
result2 := getName("arohcpsvcdev.azurecr.io/redhat-user-workloads/ocp-art-tenant/oadp-hypershift-oadp-plugin-main@sha256:adb840bf3890b4904a8cdda1a74c82cf8d96c52eba9944ac10e795335d6fd450", nil)
|
||||
assert.Equal(t, result, result2)
|
||||
},
|
||||
},
|
||||
@@ -142,7 +159,7 @@ func TestGetNameWithLongPaths(t *testing.T) {
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
result := getName(test.image)
|
||||
result := getName(test.image, nil)
|
||||
test.validate(t, result)
|
||||
})
|
||||
}
|
||||
|
||||
@@ -111,7 +111,7 @@ func NewAddCommand(f client.Factory) *cobra.Command {
|
||||
}
|
||||
|
||||
// add the plugin as an init container
|
||||
plugin := *builder.ForPluginContainer(args[0], corev1api.PullPolicy(imagePullPolicyFlag.String())).Result()
|
||||
plugin := *builder.ForPluginContainer(args[0], corev1api.PullPolicy(imagePullPolicyFlag.String()), veleroDeploy.Spec.Template.Spec.InitContainers).Result()
|
||||
|
||||
veleroDeploy.Spec.Template.Spec.InitContainers = append(veleroDeploy.Spec.Template.Spec.InitContainers, plugin)
|
||||
|
||||
|
||||
@@ -254,12 +254,12 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request
|
||||
|
||||
if r.vgdpCounter != nil && r.vgdpCounter.IsConstrained(ctx, r.logger) {
|
||||
log.Debug("Data path initiation is constrained, requeue later")
|
||||
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
}
|
||||
|
||||
if _, err := r.getTargetPVC(ctx, dd); err != nil {
|
||||
log.WithField("error", err).Debugf("Cannot find target PVC for DataDownload yet. Retry later.")
|
||||
return ctrl.Result{Requeue: true}, nil
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
}
|
||||
|
||||
log.Info("Data download starting")
|
||||
@@ -350,7 +350,7 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request
|
||||
if err != nil {
|
||||
if err == datapath.ConcurrentLimitExceed {
|
||||
log.Debug("Data path instance is concurrent limited requeue later")
|
||||
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
} else {
|
||||
return r.errorOut(ctx, dd, err, "error to create data path", log)
|
||||
}
|
||||
@@ -381,7 +381,7 @@ func (r *DataDownloadReconciler) Reconcile(ctx context.Context, req ctrl.Request
|
||||
log.WithError(err).Warnf("Failed to update datadownload %s to InProgress, will data path close and retry", dd.Name)
|
||||
|
||||
r.closeDataPath(ctx, dd.Name)
|
||||
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
}
|
||||
|
||||
if terminated {
|
||||
|
||||
@@ -315,12 +315,12 @@ func TestDataDownloadReconcile(t *testing.T) {
|
||||
dd: dataDownloadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
constrained: true,
|
||||
expected: dataDownloadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5},
|
||||
expectedResult: &ctrl.Result{RequeueAfter: time.Second * 5},
|
||||
},
|
||||
{
|
||||
name: "new dd but no target PVC",
|
||||
dd: dataDownloadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
expectedResult: &ctrl.Result{Requeue: true},
|
||||
expectedResult: &ctrl.Result{RequeueAfter: time.Second * 5},
|
||||
},
|
||||
{
|
||||
name: "new dd but accept failed",
|
||||
@@ -390,7 +390,7 @@ func TestDataDownloadReconcile(t *testing.T) {
|
||||
dataMgr: datapath.NewManager(0),
|
||||
notNilExpose: true,
|
||||
notMockCleanUp: true,
|
||||
expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5},
|
||||
expectedResult: &ctrl.Result{RequeueAfter: time.Second * 5},
|
||||
},
|
||||
{
|
||||
name: "data path init error",
|
||||
@@ -594,7 +594,6 @@ func TestDataDownloadReconcile(t *testing.T) {
|
||||
}
|
||||
|
||||
if test.expectedResult != nil {
|
||||
assert.Equal(t, test.expectedResult.Requeue, actualResult.Requeue)
|
||||
assert.Equal(t, test.expectedResult.RequeueAfter, actualResult.RequeueAfter)
|
||||
}
|
||||
|
||||
|
||||
@@ -265,7 +265,7 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
||||
|
||||
if r.vgdpCounter != nil && r.vgdpCounter.IsConstrained(ctx, r.logger) {
|
||||
log.Debug("Data path initiation is constrained, requeue later")
|
||||
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
}
|
||||
|
||||
log.Info("Data upload starting")
|
||||
@@ -359,7 +359,7 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
||||
if err != nil {
|
||||
if err == datapath.ConcurrentLimitExceed {
|
||||
log.Debug("Data path instance is concurrent limited requeue later")
|
||||
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
} else {
|
||||
return r.errorOut(ctx, du, err, "error to create data path", log)
|
||||
}
|
||||
@@ -391,7 +391,7 @@ func (r *DataUploadReconciler) Reconcile(ctx context.Context, req ctrl.Request)
|
||||
log.WithError(err).Warnf("Failed to update dataupload %s to InProgress, will data path close and retry", du.Name)
|
||||
|
||||
r.closeDataPath(ctx, du.Name)
|
||||
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
}
|
||||
|
||||
if terminated {
|
||||
|
||||
@@ -478,7 +478,7 @@ func TestReconcile(t *testing.T) {
|
||||
du: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
constrained: true,
|
||||
expected: dataUploadBuilder().Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5},
|
||||
expectedResult: &ctrl.Result{RequeueAfter: time.Second * 5},
|
||||
},
|
||||
{
|
||||
name: "new du but accept failed",
|
||||
@@ -548,7 +548,7 @@ func TestReconcile(t *testing.T) {
|
||||
name: "Error in data path is concurrent limited",
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Result(),
|
||||
dataMgr: datapath.NewManager(0),
|
||||
expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5},
|
||||
expectedResult: &ctrl.Result{RequeueAfter: time.Second * 5},
|
||||
},
|
||||
{
|
||||
name: "data path init error",
|
||||
@@ -562,7 +562,7 @@ func TestReconcile(t *testing.T) {
|
||||
du: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).SnapshotType(fakeSnapshotType).Finalizers([]string{DataUploadDownloadFinalizer}).Node("test-node").Result(),
|
||||
needErrs: []bool{false, false, true, false},
|
||||
expected: dataUploadBuilder().Phase(velerov2alpha1api.DataUploadPhasePrepared).Finalizers([]string{DataUploadDownloadFinalizer}).Result(),
|
||||
expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5},
|
||||
expectedResult: &ctrl.Result{RequeueAfter: time.Second * 5},
|
||||
},
|
||||
{
|
||||
name: "data path start error",
|
||||
@@ -694,7 +694,6 @@ func TestReconcile(t *testing.T) {
|
||||
}
|
||||
|
||||
if test.expectedResult != nil {
|
||||
assert.Equal(t, test.expectedResult.Requeue, actualResult.Requeue)
|
||||
assert.Equal(t, test.expectedResult.RequeueAfter, actualResult.RequeueAfter)
|
||||
}
|
||||
|
||||
|
||||
@@ -238,7 +238,7 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
||||
|
||||
if r.vgdpCounter != nil && r.vgdpCounter.IsConstrained(ctx, r.logger) {
|
||||
log.Debug("Data path initiation is constrained, requeue later")
|
||||
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
}
|
||||
|
||||
log.Info("Accepting PVB")
|
||||
@@ -314,7 +314,7 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
||||
if err != nil {
|
||||
if err == datapath.ConcurrentLimitExceed {
|
||||
log.Debug("Data path instance is concurrent limited requeue later")
|
||||
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
} else {
|
||||
return r.errorOut(ctx, pvb, err, "error to create data path", log)
|
||||
}
|
||||
@@ -346,7 +346,7 @@ func (r *PodVolumeBackupReconciler) Reconcile(ctx context.Context, req ctrl.Requ
|
||||
log.WithError(err).Warnf("Failed to update PVB %s to InProgress, will data path close and retry", pvb.Name)
|
||||
|
||||
r.closeDataPath(ctx, pvb.Name)
|
||||
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
}
|
||||
|
||||
if terminated {
|
||||
|
||||
@@ -327,7 +327,7 @@ func TestPVBReconcile(t *testing.T) {
|
||||
pvb: pvbBuilder().Finalizers([]string{PodVolumeFinalizer}).Node("test-node").Result(),
|
||||
constrained: true,
|
||||
expected: pvbBuilder().Finalizers([]string{PodVolumeFinalizer}).Result(),
|
||||
expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5},
|
||||
expectedResult: &ctrl.Result{RequeueAfter: time.Second * 5},
|
||||
},
|
||||
{
|
||||
name: "new pvb but accept failed",
|
||||
@@ -394,7 +394,7 @@ func TestPVBReconcile(t *testing.T) {
|
||||
pvb: pvbBuilder().Phase(velerov1api.PodVolumeBackupPhasePrepared).Finalizers([]string{PodVolumeFinalizer}).Node("test-node").Result(),
|
||||
needMockExposer: true,
|
||||
dataMgr: datapath.NewManager(0),
|
||||
expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5},
|
||||
expectedResult: &ctrl.Result{RequeueAfter: time.Second * 5},
|
||||
},
|
||||
{
|
||||
name: "data path init error",
|
||||
@@ -410,7 +410,7 @@ func TestPVBReconcile(t *testing.T) {
|
||||
needMockExposer: true,
|
||||
needErrs: []bool{false, false, true, false},
|
||||
expected: pvbBuilder().Phase(velerov1api.PodVolumeBackupPhasePrepared).Finalizers([]string{PodVolumeFinalizer}).Result(),
|
||||
expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5},
|
||||
expectedResult: &ctrl.Result{RequeueAfter: time.Second * 5},
|
||||
},
|
||||
{
|
||||
name: "data path start error",
|
||||
@@ -537,7 +537,6 @@ func TestPVBReconcile(t *testing.T) {
|
||||
}
|
||||
|
||||
if test.expectedResult != nil {
|
||||
assert.Equal(t, test.expectedResult.Requeue, actualResult.Requeue)
|
||||
assert.Equal(t, test.expectedResult.RequeueAfter, actualResult.RequeueAfter)
|
||||
}
|
||||
|
||||
|
||||
@@ -246,7 +246,7 @@ func (r *PodVolumeRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Req
|
||||
|
||||
if r.vgdpCounter != nil && r.vgdpCounter.IsConstrained(ctx, r.logger) {
|
||||
log.Debug("Data path initiation is constrained, requeue later")
|
||||
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
}
|
||||
|
||||
log.Info("Accepting PVR")
|
||||
@@ -328,7 +328,7 @@ func (r *PodVolumeRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Req
|
||||
if err != nil {
|
||||
if err == datapath.ConcurrentLimitExceed {
|
||||
log.Debug("Data path instance is concurrent limited requeue later")
|
||||
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
} else {
|
||||
return r.errorOut(ctx, pvr, err, "error to create data path", log)
|
||||
}
|
||||
@@ -358,7 +358,7 @@ func (r *PodVolumeRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Req
|
||||
log.WithError(err).Warnf("Failed to update PVR %s to InProgress, will data path close and retry", pvr.Name)
|
||||
|
||||
r.closeDataPath(ctx, pvr.Name)
|
||||
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
}
|
||||
|
||||
if terminated {
|
||||
|
||||
@@ -150,7 +150,7 @@ func (c *PodVolumeRestoreReconcilerLegacy) Reconcile(ctx context.Context, req ct
|
||||
fsRestore, err := c.dataPathMgr.CreateFileSystemBR(pvr.Name, pVBRRequestor, ctx, c.Client, pvr.Namespace, callbacks, log)
|
||||
if err != nil {
|
||||
if err == datapath.ConcurrentLimitExceed {
|
||||
return ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5}, nil
|
||||
return ctrl.Result{RequeueAfter: time.Second * 5}, nil
|
||||
} else {
|
||||
return c.errorOut(ctx, pvr, err, "error to create data path", log)
|
||||
}
|
||||
|
||||
@@ -790,7 +790,7 @@ func TestPodVolumeRestoreReconcile(t *testing.T) {
|
||||
targetPod: builder.ForPod("test-ns", "test-pod").InitContainers(&corev1api.Container{Name: restorehelper.WaitInitContainer}).InitContainerState(corev1api.ContainerState{Running: &corev1api.ContainerStateRunning{}}).Result(),
|
||||
constrained: true,
|
||||
expected: builder.ForPodVolumeRestore(velerov1api.DefaultNamespace, pvrName).Finalizers([]string{PodVolumeFinalizer}).Result(),
|
||||
expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5},
|
||||
expectedResult: &ctrl.Result{RequeueAfter: time.Second * 5},
|
||||
},
|
||||
{
|
||||
name: "new pvr but accept failed",
|
||||
@@ -858,7 +858,7 @@ func TestPodVolumeRestoreReconcile(t *testing.T) {
|
||||
dataMgr: datapath.NewManager(0),
|
||||
notNilExpose: true,
|
||||
notMockCleanUp: true,
|
||||
expectedResult: &ctrl.Result{Requeue: true, RequeueAfter: time.Second * 5},
|
||||
expectedResult: &ctrl.Result{RequeueAfter: time.Second * 5},
|
||||
},
|
||||
{
|
||||
name: "data path init error",
|
||||
@@ -1057,7 +1057,6 @@ func TestPodVolumeRestoreReconcile(t *testing.T) {
|
||||
}
|
||||
|
||||
if test.expectedResult != nil {
|
||||
assert.Equal(t, test.expectedResult.Requeue, actualResult.Requeue)
|
||||
assert.Equal(t, test.expectedResult.RequeueAfter, actualResult.RequeueAfter)
|
||||
}
|
||||
|
||||
|
||||
@@ -125,7 +125,7 @@ var _ = Describe("Server Status Request Reconciler", func() {
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
expectedRequeue: ctrl.Result{Requeue: false, RequeueAfter: statusRequestResyncPeriod},
|
||||
expectedRequeue: ctrl.Result{RequeueAfter: statusRequestResyncPeriod},
|
||||
}),
|
||||
Entry("with phase=new will be processed and phased successfully patched", request{
|
||||
req: statusRequestBuilder("1").
|
||||
@@ -158,7 +158,7 @@ var _ = Describe("Server Status Request Reconciler", func() {
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
expectedRequeue: ctrl.Result{Requeue: false, RequeueAfter: statusRequestResyncPeriod},
|
||||
expectedRequeue: ctrl.Result{RequeueAfter: statusRequestResyncPeriod},
|
||||
}),
|
||||
Entry("with phase=Processed does not get deleted if not expired", request{
|
||||
req: statusRequestBuilder("1").
|
||||
@@ -191,7 +191,7 @@ var _ = Describe("Server Status Request Reconciler", func() {
|
||||
},
|
||||
}).
|
||||
Result(),
|
||||
expectedRequeue: ctrl.Result{Requeue: false, RequeueAfter: statusRequestResyncPeriod},
|
||||
expectedRequeue: ctrl.Result{RequeueAfter: statusRequestResyncPeriod},
|
||||
}),
|
||||
Entry("with phase=Processed gets deleted if expired", request{
|
||||
req: statusRequestBuilder("1").
|
||||
@@ -214,7 +214,7 @@ var _ = Describe("Server Status Request Reconciler", func() {
|
||||
},
|
||||
},
|
||||
expected: nil,
|
||||
expectedRequeue: ctrl.Result{Requeue: false, RequeueAfter: statusRequestResyncPeriod},
|
||||
expectedRequeue: ctrl.Result{RequeueAfter: statusRequestResyncPeriod},
|
||||
}),
|
||||
Entry("with invalid phase returns an error and does not requeue", request{
|
||||
req: statusRequestBuilder("1").
|
||||
@@ -237,7 +237,7 @@ var _ = Describe("Server Status Request Reconciler", func() {
|
||||
},
|
||||
},
|
||||
expectedErrMsg: "unexpected ServerStatusRequest phase",
|
||||
expectedRequeue: ctrl.Result{Requeue: false, RequeueAfter: 0},
|
||||
expectedRequeue: ctrl.Result{RequeueAfter: 0},
|
||||
}),
|
||||
)
|
||||
})
|
||||
|
||||
@@ -509,7 +509,7 @@ func Deployment(namespace string, opts ...podTemplateOption) *appsv1api.Deployme
|
||||
|
||||
if len(c.plugins) > 0 {
|
||||
for _, image := range c.plugins {
|
||||
container := *builder.ForPluginContainer(image, pullPolicy).Result()
|
||||
container := *builder.ForPluginContainer(image, pullPolicy, deployment.Spec.Template.Spec.InitContainers).Result()
|
||||
deployment.Spec.Template.Spec.InitContainers = append(deployment.Spec.Template.Spec.InitContainers, container)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import (
|
||||
"fmt"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
appsv1api "k8s.io/api/apps/v1"
|
||||
corev1api "k8s.io/api/core/v1"
|
||||
apierrors "k8s.io/apimachinery/pkg/api/errors"
|
||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||
@@ -80,6 +81,36 @@ func KbClientIsRunningInNode(ctx context.Context, namespace string, nodeName str
|
||||
return isRunningInNode(ctx, namespace, nodeName, nil, kubeClient)
|
||||
}
|
||||
|
||||
// IsReady checks whether the node-agent daemonset has at least one ready pod
|
||||
// by inspecting the DaemonSet status.
|
||||
func IsReady(ctx context.Context, namespace string, crClient ctrlclient.Client) error {
|
||||
dsLinux := new(appsv1api.DaemonSet)
|
||||
if err := crClient.Get(ctx, ctrlclient.ObjectKey{Namespace: namespace, Name: daemonSet}, dsLinux); err != nil {
|
||||
dsLinux = nil
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return errors.Wrap(err, "failed to get linux node-agent daemonset")
|
||||
}
|
||||
}
|
||||
|
||||
dsWindows := new(appsv1api.DaemonSet)
|
||||
if err := crClient.Get(ctx, ctrlclient.ObjectKey{Namespace: namespace, Name: daemonsetWindows}, dsWindows); err != nil {
|
||||
dsWindows = nil
|
||||
if !apierrors.IsNotFound(err) {
|
||||
return errors.Wrap(err, "failed to get windows node-agent daemonset")
|
||||
}
|
||||
}
|
||||
|
||||
if dsLinux != nil && dsLinux.Status.NumberReady > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
if dsWindows != nil && dsWindows.Status.NumberReady > 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return errors.New("node-agent is not ready: no ready pods found")
|
||||
}
|
||||
|
||||
// IsRunningInNode checks if the node agent pod is running properly in a specified node through controller client. If not, return the error found
|
||||
func IsRunningInNode(ctx context.Context, namespace string, nodeName string, crClient ctrlclient.Client) error {
|
||||
return isRunningInNode(ctx, namespace, nodeName, crClient, nil)
|
||||
|
||||
@@ -17,6 +17,7 @@ limitations under the License.
|
||||
package nodeagent
|
||||
|
||||
import (
|
||||
"context"
|
||||
"testing"
|
||||
|
||||
"github.com/cockroachdb/errors"
|
||||
@@ -28,7 +29,9 @@ import (
|
||||
"k8s.io/apimachinery/pkg/runtime"
|
||||
"k8s.io/client-go/kubernetes/fake"
|
||||
clientTesting "k8s.io/client-go/testing"
|
||||
ctrlclient "sigs.k8s.io/controller-runtime/pkg/client"
|
||||
clientFake "sigs.k8s.io/controller-runtime/pkg/client/fake"
|
||||
"sigs.k8s.io/controller-runtime/pkg/client/interceptor"
|
||||
|
||||
"github.com/vmware-tanzu/velero/pkg/builder"
|
||||
velerotypes "github.com/vmware-tanzu/velero/pkg/types"
|
||||
@@ -213,6 +216,152 @@ func TestIsRunningInNode(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestIsReady(t *testing.T) {
|
||||
scheme := runtime.NewScheme()
|
||||
appsv1api.AddToScheme(scheme)
|
||||
|
||||
dsLinuxNotReady := &appsv1api.DaemonSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "fake-ns", Name: "node-agent"},
|
||||
Status: appsv1api.DaemonSetStatus{NumberReady: 0},
|
||||
}
|
||||
dsLinuxReady := &appsv1api.DaemonSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "fake-ns", Name: "node-agent"},
|
||||
Status: appsv1api.DaemonSetStatus{NumberReady: 3},
|
||||
}
|
||||
dsWindowsNotReady := &appsv1api.DaemonSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "fake-ns", Name: "node-agent-windows"},
|
||||
Status: appsv1api.DaemonSetStatus{NumberReady: 0},
|
||||
}
|
||||
dsWindowsReady := &appsv1api.DaemonSet{
|
||||
ObjectMeta: metav1.ObjectMeta{Namespace: "fake-ns", Name: "node-agent-windows"},
|
||||
Status: appsv1api.DaemonSetStatus{NumberReady: 2},
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
kubeClientObj []runtime.Object
|
||||
namespace string
|
||||
interceptor *interceptor.Funcs
|
||||
expectErr string
|
||||
}{
|
||||
{
|
||||
name: "both daemonsets not found",
|
||||
namespace: "fake-ns",
|
||||
expectErr: "node-agent is not ready: no ready pods found",
|
||||
},
|
||||
{
|
||||
name: "linux daemonset get error",
|
||||
namespace: "fake-ns",
|
||||
interceptor: &interceptor.Funcs{
|
||||
Get: func(ctx context.Context, c ctrlclient.WithWatch, key ctrlclient.ObjectKey, obj ctrlclient.Object, opts ...ctrlclient.GetOption) error {
|
||||
if key.Name == "node-agent" {
|
||||
return errors.New("fake-get-error")
|
||||
}
|
||||
return c.Get(ctx, key, obj, opts...)
|
||||
},
|
||||
},
|
||||
expectErr: "failed to get linux node-agent daemonset: fake-get-error",
|
||||
},
|
||||
{
|
||||
name: "windows daemonset get error",
|
||||
namespace: "fake-ns",
|
||||
interceptor: &interceptor.Funcs{
|
||||
Get: func(ctx context.Context, c ctrlclient.WithWatch, key ctrlclient.ObjectKey, obj ctrlclient.Object, opts ...ctrlclient.GetOption) error {
|
||||
if key.Name == "node-agent-windows" {
|
||||
return errors.New("fake-get-error")
|
||||
}
|
||||
return c.Get(ctx, key, obj, opts...)
|
||||
},
|
||||
},
|
||||
expectErr: "failed to get windows node-agent daemonset: fake-get-error",
|
||||
},
|
||||
{
|
||||
name: "linux ds exist but no ready pods",
|
||||
namespace: "fake-ns",
|
||||
kubeClientObj: []runtime.Object{
|
||||
dsLinuxNotReady,
|
||||
},
|
||||
expectErr: "node-agent is not ready: no ready pods found",
|
||||
},
|
||||
{
|
||||
name: "linux ds with ready pods",
|
||||
namespace: "fake-ns",
|
||||
kubeClientObj: []runtime.Object{
|
||||
dsLinuxReady,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "windows ds exist but no ready pods",
|
||||
namespace: "fake-ns",
|
||||
kubeClientObj: []runtime.Object{
|
||||
dsWindowsNotReady,
|
||||
},
|
||||
expectErr: "node-agent is not ready: no ready pods found",
|
||||
},
|
||||
{
|
||||
name: "windows ds with ready pods",
|
||||
namespace: "fake-ns",
|
||||
kubeClientObj: []runtime.Object{
|
||||
dsWindowsReady,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "both daemonsets exist but no ready pods",
|
||||
namespace: "fake-ns",
|
||||
kubeClientObj: []runtime.Object{
|
||||
dsLinuxNotReady,
|
||||
dsWindowsNotReady,
|
||||
},
|
||||
expectErr: "node-agent is not ready: no ready pods found",
|
||||
},
|
||||
{
|
||||
name: "both daemonsets exist, linux ready",
|
||||
namespace: "fake-ns",
|
||||
kubeClientObj: []runtime.Object{
|
||||
dsLinuxReady,
|
||||
dsWindowsNotReady,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "both daemonsets exist, windows ready",
|
||||
namespace: "fake-ns",
|
||||
kubeClientObj: []runtime.Object{
|
||||
dsLinuxNotReady,
|
||||
dsWindowsReady,
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "both daemonsets exist, both ready",
|
||||
namespace: "fake-ns",
|
||||
kubeClientObj: []runtime.Object{
|
||||
dsLinuxReady,
|
||||
dsWindowsReady,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
builder := clientFake.NewClientBuilder().
|
||||
WithScheme(scheme).
|
||||
WithRuntimeObjects(test.kubeClientObj...)
|
||||
|
||||
if test.interceptor != nil {
|
||||
builder = builder.WithInterceptorFuncs(*test.interceptor)
|
||||
}
|
||||
|
||||
fakeClient := builder.Build()
|
||||
|
||||
err := IsReady(t.Context(), test.namespace, fakeClient)
|
||||
if test.expectErr == "" {
|
||||
assert.NoError(t, err)
|
||||
} else {
|
||||
assert.ErrorContains(t, err, test.expectErr)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetPodSpec(t *testing.T) {
|
||||
podSpec := corev1api.PodSpec{
|
||||
NodeName: "fake-node",
|
||||
|
||||
+19
-2
@@ -319,9 +319,26 @@ func ExitPodWithMessage(logger logrus.FieldLogger, succeed bool, message string,
|
||||
funcExit(exitCode)
|
||||
}
|
||||
|
||||
// deepCopy returns a deep copy of the LoadAffinity, so that the returned value
|
||||
// can be safely modified without affecting the source.
|
||||
func (a *LoadAffinity) deepCopy() *LoadAffinity {
|
||||
if a == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
result := &LoadAffinity{
|
||||
StorageClass: a.StorageClass,
|
||||
}
|
||||
a.NodeSelector.DeepCopyInto(&result.NodeSelector)
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// GetLoadAffinityByStorageClass retrieves the LoadAffinity from the parameter affinityList.
|
||||
// The function first try to find by the scName. If there is no such LoadAffinity,
|
||||
// it will try to get the LoadAffinity whose StorageClass has no value.
|
||||
// The returned LoadAffinity is a deep copy of the matched element, so that the
|
||||
// callers can modify it without corrupting the shared node-agent configuration.
|
||||
func GetLoadAffinityByStorageClass(
|
||||
affinityList []*LoadAffinity,
|
||||
scName string,
|
||||
@@ -332,7 +349,7 @@ func GetLoadAffinityByStorageClass(
|
||||
for _, affinity := range affinityList {
|
||||
if affinity.StorageClass == scName {
|
||||
logger.WithField("StorageClass", scName).Info("Found pod's affinity setting per StorageClass.")
|
||||
return affinity
|
||||
return affinity.deepCopy()
|
||||
}
|
||||
|
||||
if affinity.StorageClass == "" && globalAffinity == nil {
|
||||
@@ -346,5 +363,5 @@ func GetLoadAffinityByStorageClass(
|
||||
logger.Info("No Affinity is found for pod.")
|
||||
}
|
||||
|
||||
return globalAffinity
|
||||
return globalAffinity.deepCopy()
|
||||
}
|
||||
|
||||
@@ -1545,3 +1545,82 @@ func TestGetLoadAffinityByStorageClass(t *testing.T) {
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestGetLoadAffinityByStorageClassReturnsCopy(t *testing.T) {
|
||||
newAffinityList := func() []*LoadAffinity {
|
||||
return []*LoadAffinity{
|
||||
{
|
||||
NodeSelector: metav1.LabelSelector{
|
||||
MatchLabels: map[string]string{"pool": "backup"},
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: corev1api.LabelArchStable,
|
||||
Operator: metav1.LabelSelectorOpIn,
|
||||
Values: []string{"amd64"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
NodeSelector: metav1.LabelSelector{
|
||||
MatchExpressions: []metav1.LabelSelectorRequirement{
|
||||
{
|
||||
Key: corev1api.LabelArchStable,
|
||||
Operator: metav1.LabelSelectorOpIn,
|
||||
Values: []string{"arm64"},
|
||||
},
|
||||
},
|
||||
},
|
||||
StorageClass: "storage-class-01",
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
name string
|
||||
scName string
|
||||
}{
|
||||
{
|
||||
name: "global affinity",
|
||||
scName: "no-such-storage-class",
|
||||
},
|
||||
{
|
||||
name: "affinity matched by StorageClass",
|
||||
scName: "storage-class-01",
|
||||
},
|
||||
}
|
||||
|
||||
for _, test := range tests {
|
||||
t.Run(test.name, func(t *testing.T) {
|
||||
affinityList := newAffinityList()
|
||||
|
||||
// Simulate the exposers, which append an OS related term to the returned
|
||||
// affinity on every expose call. The source list must not be affected.
|
||||
for range 3 {
|
||||
result := GetLoadAffinityByStorageClass(affinityList, test.scName, velerotest.NewLogger())
|
||||
require.NotNil(t, result)
|
||||
|
||||
result.NodeSelector.MatchExpressions = append(result.NodeSelector.MatchExpressions, metav1.LabelSelectorRequirement{
|
||||
Key: NodeOSLabel,
|
||||
Operator: metav1.LabelSelectorOpNotIn,
|
||||
Values: []string{NodeOSWindows},
|
||||
})
|
||||
|
||||
assert.Len(t, result.NodeSelector.MatchExpressions, 2)
|
||||
}
|
||||
|
||||
assert.Equal(t, newAffinityList(), affinityList)
|
||||
|
||||
// The other fields must be copied as well.
|
||||
result := GetLoadAffinityByStorageClass(affinityList, test.scName, velerotest.NewLogger())
|
||||
require.NotNil(t, result)
|
||||
result.StorageClass = "modified"
|
||||
result.NodeSelector.MatchExpressions[0].Values[0] = "modified"
|
||||
if result.NodeSelector.MatchLabels != nil {
|
||||
result.NodeSelector.MatchLabels["pool"] = "modified"
|
||||
}
|
||||
|
||||
assert.Equal(t, newAffinityList(), affinityList)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user