Compare commits
55 Commits
client/v0.
...
main
Author | SHA1 | Date |
---|---|---|
|
ef8c754149 | |
|
c97af6ded7 | |
|
23f71dcc73 | |
|
2cada20f0e | |
|
8ce01f75db | |
|
2679ff7430 | |
|
b4c5257e14 | |
|
79d1d2c930 | |
|
dd71f20616 | |
|
42ee8a1846 | |
|
52b64d3d61 | |
|
c6bb4122a0 | |
|
c3c9a0c8fb | |
|
2573a139aa | |
|
6792e33c67 | |
|
dd30722a7f | |
|
82fabc44f5 | |
|
b5b81407ca | |
|
a23cdd52ed | |
|
1215c8348e | |
|
37613077c0 | |
|
9c6419c861 | |
|
ddf97bc550 | |
|
45ca357a45 | |
|
5a1bbaf19b | |
|
3a22cda0e3 | |
|
bd9ca45869 | |
|
db70631b3d | |
|
06cde7fede | |
|
7addbd6940 | |
|
561ca3e952 | |
|
7ed7406cac | |
|
e4b94181df | |
|
c3de1e728d | |
|
b20215b7a8 | |
|
7c3215e181 | |
|
5aad6aa22b | |
|
119ad245c9 | |
|
71c9aa541f | |
|
fd2842d5d4 | |
|
6b9e7dd61c | |
|
899832deba | |
|
404bfc8834 | |
|
5b53b9601b | |
|
eb4d494baf | |
|
e1f03ff875 | |
|
8190bcb76c | |
|
747f987028 | |
|
3dd6a033f9 | |
|
e49d2b7793 | |
|
2f5e9feae0 | |
|
c9a9260e37 | |
|
a078ef44e9 | |
|
dab5eae4e4 | |
|
8343ef9071 |
|
@ -6,8 +6,11 @@ on:
|
|||
|
||||
jobs:
|
||||
minikube-ci:
|
||||
name: Intergration test
|
||||
name: Integration test (with${{ matrix.audience == false && 'out' || '' }} audience parameter)
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
audience: [false, true]
|
||||
steps:
|
||||
- name: Check out the repo
|
||||
uses: actions/checkout@v4
|
||||
|
@ -30,6 +33,13 @@ jobs:
|
|||
run: |
|
||||
./scripts/deploy-snapshot-controller.sh deploy
|
||||
|
||||
- name: Configure audience parameter
|
||||
run: |
|
||||
if [ "${{ matrix.audience }}" = "true" ]; then
|
||||
echo "Enabling audience parameter"
|
||||
sed -i 's/# - "--audience=test-backup-client"/- "--audience=test-backup-client"/' deploy/example/csi-driver/csi-driver-with-snapshot-metadata-sidecar.yaml
|
||||
fi
|
||||
|
||||
- name: Deploy csi-hostpath-driver
|
||||
run: |
|
||||
kubectl apply -f ./client/config/crd/cbt.storage.k8s.io_snapshotmetadataservices.yaml
|
||||
|
@ -42,28 +52,25 @@ jobs:
|
|||
|
||||
kubectl wait --for=condition=Ready pod -l app.kubernetes.io/name=csi-hostpath-socat --timeout=300s
|
||||
|
||||
- name: Deploy backup client app
|
||||
- name: Deploy backup client app resources
|
||||
run: |
|
||||
kubectl create ns backup-app-namespace
|
||||
kubectl create -f deploy/snapshot-metadata-client-cluster-role.yaml
|
||||
kubectl create -f deploy/example/backup-app/service-account.yaml
|
||||
kubectl create -f deploy/example/backup-app/cluster-role-binding.yaml
|
||||
kubectl create -f deploy/example/backup-app/testdata/backup-app-pod.yaml
|
||||
|
||||
kubectl wait --for=condition=Ready -n backup-app-namespace pod/backup-app-client --timeout=300s
|
||||
kubectl create serviceaccount backup-app-service-account
|
||||
kubectl create clusterrolebinding csi-client-cluster-role-binding --clusterrole=external-snapshot-metadata-client-runner --serviceaccount=default:backup-app-service-account
|
||||
|
||||
go build -o snapshot-metadata-lister ./examples/snapshot-metadata-lister/main.go
|
||||
go build -o snapshot-metadata-verifier ./examples/snapshot-metadata-verifier/main.go
|
||||
|
||||
kubectl cp snapshot-metadata-lister backup-app-namespace/backup-app-client:/snapshot-metadata-lister
|
||||
|
||||
kubectl exec -n backup-app-namespace backup-app-client -- /snapshot-metadata-lister -h
|
||||
./snapshot-metadata-lister -h
|
||||
|
||||
- name: Execute tests
|
||||
run: |
|
||||
echo "Create a raw block PVC"
|
||||
kubectl create -f ~/csi-driver-host-path/examples/csi-storageclass.yaml
|
||||
yq -i '.spec.resources.requests.storage = "1Mi"' ~/csi-driver-host-path/examples/csi-pvc-block.yaml
|
||||
kubectl create -f ~/csi-driver-host-path/examples/csi-pvc-block.yaml
|
||||
|
||||
echo "Create a pod consuming raw block PVC"
|
||||
# Failed to pull image "gcr.io/google_containers/busybox":
|
||||
# [DEPRECATION NOTICE] Docker Image Format v1 and Docker Image manifest version 2,
|
||||
# schema 1 support is disabled by default and will be removed in an upcoming release.
|
||||
|
@ -74,29 +81,103 @@ jobs:
|
|||
kubectl create -f ~/csi-driver-host-path/examples/csi-pod-block.yaml
|
||||
kubectl wait --for=condition=Ready pod/pod-raw --timeout=300s
|
||||
|
||||
# write data into pod
|
||||
echo "Write data into pod"
|
||||
kubectl exec -i pod-raw -- sh -c "dd if=/dev/urandom of=/dev/block bs=4K count=1 oflag=direct"
|
||||
|
||||
# take snaphot snap-1
|
||||
echo "Take snaphot snap-1"
|
||||
yq -i '.metadata.name = "snap-1"' ~/csi-driver-host-path/examples/csi-snapshot-v1.yaml
|
||||
yq -i '.spec.source.persistentVolumeClaimName = "pvc-raw"' ~/csi-driver-host-path/examples/csi-snapshot-v1.yaml
|
||||
kubectl create -f ~/csi-driver-host-path/examples/csi-snapshot-v1.yaml
|
||||
kubectl wait volumesnapshot snap-1 --for=jsonpath='{.status.readyToUse}'=true --timeout=300s
|
||||
|
||||
echo "Restore snapshot into source-device PVC"
|
||||
yq -i '.metadata.name = "source-device"' ~/csi-driver-host-path/examples/csi-block-pvc-restore.yaml
|
||||
yq -i '.spec.dataSource.name = "snap-1"' ~/csi-driver-host-path/examples/csi-block-pvc-restore.yaml
|
||||
yq -i '.spec.resources.requests.storage = "1Mi"' ~/csi-driver-host-path/examples/csi-block-pvc-restore.yaml
|
||||
kubectl create -f ~/csi-driver-host-path/examples/csi-block-pvc-restore.yaml
|
||||
|
||||
echo "Create fresh target-device PVC"
|
||||
yq -i '.metadata.name = "target-device"' ~/csi-driver-host-path/examples/csi-pvc-block.yaml
|
||||
kubectl create -f ~/csi-driver-host-path/examples/csi-pvc-block.yaml
|
||||
|
||||
echo "Create backup-app-client pod which mounts source-device and target-device PVCs"
|
||||
kubectl create -f deploy/example/backup-app/testdata/backup-app-pod.yaml
|
||||
kubectl wait --for=condition=Ready pod/backup-app-client --timeout=300s
|
||||
|
||||
# call external-snapshot-metadata-client
|
||||
kubectl exec -n backup-app-namespace backup-app-client -- /snapshot-metadata-lister -max-results 10 -snapshot snap-1 -starting-offset 0 -namespace default
|
||||
echo "Copy snapshot-metadata-lister into backup-app-client pod"
|
||||
kubectl cp ./snapshot-metadata-lister default/backup-app-client:/snapshot-metadata-lister -c run-client
|
||||
kubectl cp ./snapshot-metadata-verifier default/backup-app-client:/snapshot-metadata-verifier -c run-client
|
||||
|
||||
echo "Execute external-snapshot-metadata-client to list allocated blocks in snap-1"
|
||||
kubectl exec backup-app-client -- /snapshot-metadata-lister -max-results 10 -snapshot snap-1 -starting-offset 0 -namespace default -kubeconfig=""
|
||||
|
||||
# write data into pod
|
||||
kubectl exec -i pod-raw -- sh -c "dd if=/dev/urandom of=/dev/block bs=4K count=5 oflag=direct"
|
||||
echo "Execute external-snapshot-metadata-client to list allocated blocks and verify the data allocated in snap-1"
|
||||
kubectl exec backup-app-client -- /snapshot-metadata-verifier -max-results 10 -snapshot snap-1 -starting-offset 0 -namespace default -kubeconfig="" -source-device-path /dev/source -target-device-path /dev/target
|
||||
|
||||
# take snapshot snap-2
|
||||
echo "Write more data into source-device to test negative case"
|
||||
kubectl exec -i backup-app-client -- sh -c "dd if=/dev/urandom of=/dev/source bs=4K count=6 oflag=direct"
|
||||
|
||||
echo "Execute external-snapshot-metadata-client to expect a failure"
|
||||
export failed=0
|
||||
kubectl exec backup-app-client -- /snapshot-metadata-verifier -max-results 10 -snapshot snap-1 -starting-offset 0 -namespace default -kubeconfig="" -source-device-path /dev/source -target-device-path /dev/target || { export failed=1; true;}
|
||||
if [ $failed -ne 1 ]; then
|
||||
echo "Command was expected to fail but it succeeded"
|
||||
exit 1
|
||||
else
|
||||
echo "Command failed as expected"
|
||||
fi
|
||||
|
||||
echo "Delete backup-app-client pod and source-device and target-device PVCs"
|
||||
kubectl delete -f deploy/example/backup-app/testdata/backup-app-pod.yaml
|
||||
kubectl delete pvc source-device target-device
|
||||
|
||||
echo "Write data into pod again"
|
||||
kubectl exec -i pod-raw -- sh -c "dd if=/dev/urandom of=/dev/block bs=4K count=5 oflag=direct"
|
||||
|
||||
echo "Take snapshot snap-2"
|
||||
yq -i '.metadata.name = "snap-2"' ~/csi-driver-host-path/examples/csi-snapshot-v1.yaml
|
||||
yq -i '.spec.source.persistentVolumeClaimName = "pvc-raw"' ~/csi-driver-host-path/examples/csi-snapshot-v1.yaml
|
||||
kubectl create -f ~/csi-driver-host-path/examples/csi-snapshot-v1.yaml
|
||||
kubectl wait volumesnapshot snap-2 --for=jsonpath='{.status.readyToUse}'=true --timeout=300s
|
||||
|
||||
# call external-snapshot-metadata-client
|
||||
kubectl exec -n backup-app-namespace backup-app-client -- /snapshot-metadata-lister -max-results 10 -previous-snapshot snap-1 -snapshot snap-2 -starting-offset 0 -namespace default
|
||||
echo "Restore snap-1 into target-device"
|
||||
yq -i '.metadata.name = "target-device"' ~/csi-driver-host-path/examples/csi-block-pvc-restore.yaml
|
||||
yq -i '.spec.dataSource.name = "snap-1"' ~/csi-driver-host-path/examples/csi-block-pvc-restore.yaml
|
||||
yq -i '.spec.resources.requests.storage = "1Mi"' ~/csi-driver-host-path/examples/csi-block-pvc-restore.yaml
|
||||
kubectl create -f ~/csi-driver-host-path/examples/csi-block-pvc-restore.yaml
|
||||
|
||||
echo "Restore snap-2 into source-device"
|
||||
yq -i '.metadata.name = "source-device"' ~/csi-driver-host-path/examples/csi-block-pvc-restore.yaml
|
||||
yq -i '.spec.dataSource.name = "snap-2"' ~/csi-driver-host-path/examples/csi-block-pvc-restore.yaml
|
||||
yq -i '.spec.resources.requests.storage = "1Mi"' ~/csi-driver-host-path/examples/csi-block-pvc-restore.yaml
|
||||
kubectl create -f ~/csi-driver-host-path/examples/csi-block-pvc-restore.yaml
|
||||
|
||||
echo "Create backup-app-client pod which mounts source-device and target-device PVCs"
|
||||
kubectl create -f deploy/example/backup-app/testdata/backup-app-pod.yaml
|
||||
kubectl wait --for=condition=Ready pod/backup-app-client --timeout=300s
|
||||
|
||||
echo "Copy snapshot-metadata-lister into backup-app-client pod"
|
||||
kubectl cp ./snapshot-metadata-lister default/backup-app-client:/snapshot-metadata-lister -c run-client
|
||||
kubectl cp ./snapshot-metadata-verifier default/backup-app-client:/snapshot-metadata-verifier -c run-client
|
||||
|
||||
echo "Execute external-snapshot-metadata-client to list changed blocks between snap-1 and snap-2"
|
||||
kubectl exec backup-app-client -- /snapshot-metadata-lister -max-results 10 -previous-snapshot snap-1 -snapshot snap-2 -starting-offset 0 -namespace default -kubeconfig=""
|
||||
|
||||
echo "Execute external-snapshot-metadata-client to verify the data between snap-1 and snap-2"
|
||||
kubectl exec backup-app-client -- /snapshot-metadata-verifier -max-results 10 -previous-snapshot snap-1 -snapshot snap-2 -starting-offset 0 -namespace default -kubeconfig="" -source-device-path /dev/source -target-device-path /dev/target
|
||||
|
||||
echo "Write more data into source-device to test negative case"
|
||||
kubectl exec -i backup-app-client -- sh -c "dd if=/dev/urandom of=/dev/source bs=4K count=6 oflag=direct"
|
||||
|
||||
echo "Execute external-snapshot-metadata-client to expect a failure"
|
||||
export failed=0
|
||||
kubectl exec backup-app-client -- /snapshot-metadata-verifier -max-results 10 -previous-snapshot snap-1 -snapshot snap-2 -starting-offset 0 -namespace default -kubeconfig="" -source-device-path /dev/source -target-device-path /dev/target || { export failed=1; true;}
|
||||
if [ $failed -ne 1 ]; then
|
||||
echo "Command was expected to fail but it succeeded"
|
||||
exit 1
|
||||
else
|
||||
echo "Command failed as expected"
|
||||
fi
|
||||
|
||||
- name: Log the status of the failed driver pod
|
||||
if: ${{ failure() }}
|
||||
|
@ -109,10 +190,10 @@ jobs:
|
|||
|
||||
kubectl logs -n kube-system -l app.kubernetes.io/name=csi-hostpath-socat
|
||||
|
||||
kubectl describe pod -n backup-app-namespace backup-app-client
|
||||
kubectl describe pod backup-app-client
|
||||
|
||||
kubectl logs -n backup-app-namespace backup-app-client
|
||||
kubectl logs backup-app-client
|
||||
|
||||
- name: Setup tmate session to debug
|
||||
if: ${{ failure() }}
|
||||
uses: mxschmitt/action-tmate@v3
|
||||
uses: mxschmitt/action-tmate@v3
|
|
@ -4,7 +4,8 @@ on:
|
|||
push:
|
||||
branches:
|
||||
- main
|
||||
pull_request:
|
||||
# TODO: revert once https://github.com/kubernetes-csi/external-snapshot-metadata/issues/119 is fixed
|
||||
# pull_request:
|
||||
jobs:
|
||||
build:
|
||||
name: Build
|
||||
|
|
|
@ -27,6 +27,7 @@ The repository provides the following components:
|
|||
example command to illustrate how a Kubernetes backup application could fetch snapshot metadata.
|
||||
This is based on [pkg/iterator](https://github.com/kubernetes-csi/external-snapshot-metadata/tree/master/pkg/iterator),
|
||||
which a backup application developer could use directly to fetch snapshot metadata, if desired.
|
||||
- A [snapshot-metadata-verifier](https://github.com/kubernetes-csi/external-snapshot-metadata/tree/master/examples/snapshot-metadata-verifier) command to provide a way to verify allocated or changed blocks metadata generated by CSI Drivers.
|
||||
|
||||
## Community, discussion, contribution, and support
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
module github.com/kubernetes-csi/external-snapshot-metadata/client
|
||||
|
||||
go 1.22.3
|
||||
go 1.23.6
|
||||
|
||||
require (
|
||||
k8s.io/apimachinery v0.30.1
|
||||
|
|
|
@ -2,7 +2,6 @@ apiVersion: v1
|
|||
kind: Pod
|
||||
metadata:
|
||||
name: backup-app-client
|
||||
namespace: backup-app-namespace
|
||||
spec:
|
||||
serviceAccountName: backup-app-service-account
|
||||
containers:
|
||||
|
@ -12,3 +11,15 @@ spec:
|
|||
- /bin/sh
|
||||
- -c
|
||||
- "tail -f /dev/null"
|
||||
volumeDevices:
|
||||
- name: source-device
|
||||
devicePath: /dev/source
|
||||
- name: target-device
|
||||
devicePath: /dev/target
|
||||
volumes:
|
||||
- name: source-device
|
||||
persistentVolumeClaim:
|
||||
claimName: source-device
|
||||
- name: target-device
|
||||
persistentVolumeClaim:
|
||||
claimName: target-device
|
|
@ -29,6 +29,10 @@ spec:
|
|||
- "--csi-address=/csi/csi.sock"
|
||||
- "--tls-cert=/tmp/certificates/tls.crt"
|
||||
- "--tls-key=/tmp/certificates/tls.key"
|
||||
# (optional) audience token string can be passed here.
|
||||
# If specified, the sidecar will use this instead of
|
||||
# fetching the token from SnapshotMetadataService CR.
|
||||
# - "--audience=test-backup-client"
|
||||
readinessProbe:
|
||||
exec:
|
||||
command:
|
||||
|
@ -44,10 +48,7 @@ spec:
|
|||
mountPath: /tmp/certificates
|
||||
readOnly: true
|
||||
- name: hostpath
|
||||
# TODO: Replace with correct repo and tag once support for
|
||||
# SnapshotMetadata service is added
|
||||
image: prasadg193/hostpathplugin:amd64-linux-canary
|
||||
#image: registry.k8s.io/sig-storage/hostpathplugin:v1.11.0
|
||||
image: registry.k8s.io/sig-storage/hostpathplugin:v1.17.0
|
||||
args:
|
||||
- "--v=5"
|
||||
- "--endpoint=$(CSI_ENDPOINT)"
|
||||
|
|
|
@ -0,0 +1,182 @@
|
|||
/*
|
||||
Copyright 2025 The Kubernetes 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 main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/signal"
|
||||
"path/filepath"
|
||||
|
||||
"k8s.io/client-go/rest"
|
||||
"k8s.io/client-go/tools/clientcmd"
|
||||
"k8s.io/client-go/util/homedir"
|
||||
|
||||
"github.com/kubernetes-csi/external-snapshot-metadata/pkg/iterator"
|
||||
)
|
||||
|
||||
const (
|
||||
shortUsageFmt = `Usage:
|
||||
|
||||
1. Verify allocated block metadata of a snapshot
|
||||
|
||||
%[1]s -n Namespace -s Snapshot -src /dev/source -tgt /dev/target [Additional flags ...]
|
||||
|
||||
2. Verify changed block metadata between two snapshots
|
||||
|
||||
%[1]s -n Namespace -s Snapshot -p PreviousSnapshot -src /dev/source -tgt /dev/target [Additional flags ...]
|
||||
|
||||
3. Display the full help message
|
||||
|
||||
%[1]s -h
|
||||
`
|
||||
usageFmt = `This command verifies allocated blocks of a VolumeSnapshot object.
|
||||
If a previous VolumeSnapshot object is also specified then changed blocks between
|
||||
the two snapshots, which must both be from the same PersistentVolume, are verified.
|
||||
|
||||
The command is usually invoked in a Pod in the cluster, as the gRPC client
|
||||
needs to resolve the DNS address in the SnapshotMetadataService CR.
|
||||
|
||||
` + shortUsageFmt + `
|
||||
|
||||
Flags:
|
||||
`
|
||||
)
|
||||
|
||||
// globals set by flags
|
||||
var (
|
||||
args iterator.Args
|
||||
kubeConfig string
|
||||
sourceDevicePath, targetDevicePath string
|
||||
)
|
||||
|
||||
func parseFlags() {
|
||||
stringFlag := func(sVar *string, longName, shortName, defaultValue, description string) {
|
||||
flag.StringVar(sVar, longName, defaultValue, description)
|
||||
flag.StringVar(sVar, shortName, defaultValue, fmt.Sprintf("Shorthand for -%s", longName))
|
||||
}
|
||||
|
||||
stringFlag(&args.Namespace, "namespace", "n", "", "The Namespace containing the VolumeSnapshot objects.")
|
||||
stringFlag(&args.SnapshotName, "snapshot", "s", "", "The name of the VolumeSnapshot for which metadata is to be displayed.")
|
||||
stringFlag(&args.PrevSnapshotName, "previous-snapshot", "p", "", "The name of an earlier VolumeSnapshot against which changed block metadata is to be displayed.")
|
||||
stringFlag(&sourceDevicePath, "source-device-path", "src", "", "Path of the source device. This device should be the PVC in block mode restored from the snapshot which is passed as the '-snapshot' flag.")
|
||||
stringFlag(&targetDevicePath, "target-device-path", "tgt", "", "Path of the target device. This device should be a PVC in block mode restored from the snapshot which is passed as the '-previous-snapshot' flag or a fresh PVC in block mode in case the flag is not passed.")
|
||||
|
||||
if home := homedir.HomeDir(); home != "" {
|
||||
flag.StringVar(&kubeConfig, "kubeconfig", filepath.Join(home, ".kube", "config"), "Path to the kubeconfig file.")
|
||||
} else {
|
||||
flag.StringVar(&kubeConfig, "kubeconfig", "", "Path to the kubeconfig file.")
|
||||
}
|
||||
|
||||
flag.StringVar(&args.SAName, "service-account", "", "ServiceAccount used to create a security token. If unspecified the ServiceAccount of the Pod in which the command is invoked will be used.")
|
||||
flag.StringVar(&args.SANamespace, "service-account-namespace", "", "Namespace of the ServiceAccount used to create a security token. If unspecified the Namespace of the Pod in which the command is invoked will be used.")
|
||||
|
||||
flag.Int64Var(&args.TokenExpirySecs, "token-expiry", 600, "Expiry time in seconds for the security token.")
|
||||
flag.Int64Var(&args.StartingOffset, "starting-offset", 0, "The starting byte offset.")
|
||||
|
||||
var maxResults int
|
||||
flag.IntVar(&maxResults, "max-results", 0, "The maximum results per record.")
|
||||
|
||||
var showHelp bool
|
||||
flag.BoolVar(&showHelp, "h", false, "Show the full usage message.")
|
||||
|
||||
progName := filepath.Base(os.Args[0])
|
||||
flag.Usage = func() {
|
||||
fmt.Fprintf(os.Stderr, "\n"+shortUsageFmt, progName)
|
||||
}
|
||||
|
||||
if len(os.Args) > 1 {
|
||||
flag.Parse()
|
||||
args.MaxResults = int32(maxResults)
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Missing required arguments\n")
|
||||
fmt.Fprintf(os.Stderr, shortUsageFmt, progName)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if showHelp {
|
||||
fmt.Fprintf(os.Stderr, usageFmt, progName)
|
||||
flag.PrintDefaults()
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
if sourceDevicePath == "" || targetDevicePath == "" {
|
||||
fmt.Fprintf(os.Stderr, "Missing required arguments\n")
|
||||
flag.Usage()
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
// parse flags and set the output emitter
|
||||
parseFlags()
|
||||
|
||||
// get the K8s config from either kubeConfig, in-cluster or default
|
||||
config, err := buildConfig(kubeConfig)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error loading kubeconfig %s: %v\n", kubeConfig, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
clients, err := iterator.BuildClients(config)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error creating clients: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
args.Clients = clients
|
||||
|
||||
ctx, stopFn := signal.NotifyContext(context.Background(), os.Interrupt)
|
||||
defer stopFn()
|
||||
|
||||
sourceDevice, err := os.Open(sourceDevicePath)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to open source device %s: %q", sourceDevicePath, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer sourceDevice.Close()
|
||||
|
||||
targetDevice, err := os.OpenFile(targetDevicePath, os.O_RDWR, 0644)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "failed to open target device %s: %q", targetDevicePath, err)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer targetDevice.Close()
|
||||
|
||||
args.Emitter = &iterator.VerifierEmitter{
|
||||
SourceDevice: sourceDevice,
|
||||
TargetDevice: targetDevice,
|
||||
}
|
||||
|
||||
if err := iterator.GetSnapshotMetadata(ctx, args); err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
func buildConfig(kubeconfigPath string) (*rest.Config, error) {
|
||||
// If kubeconfig exists, try from kubeconfig file
|
||||
if _, err := os.Stat(kubeconfigPath); err == nil {
|
||||
return clientcmd.BuildConfigFromFlags("", kubeconfigPath)
|
||||
}
|
||||
// try in-cluster config
|
||||
return rest.InClusterConfig()
|
||||
}
|
75
go.mod
75
go.mod
|
@ -1,22 +1,24 @@
|
|||
module github.com/kubernetes-csi/external-snapshot-metadata
|
||||
|
||||
go 1.23.1
|
||||
go 1.24.0
|
||||
|
||||
toolchain go1.24.1
|
||||
|
||||
require (
|
||||
github.com/container-storage-interface/spec v1.11.0
|
||||
github.com/golang/mock v1.6.0
|
||||
github.com/kubernetes-csi/csi-lib-utils v0.20.0
|
||||
github.com/kubernetes-csi/csi-lib-utils v0.22.0
|
||||
github.com/kubernetes-csi/csi-test/v5 v5.3.1
|
||||
github.com/kubernetes-csi/external-snapshot-metadata/client v0.0.0-20240708191355-ca55d80f214a
|
||||
github.com/kubernetes-csi/external-snapshotter/client/v8 v8.2.0
|
||||
github.com/kubernetes-csi/external-snapshotter/v8 v8.2.0
|
||||
github.com/kubernetes-csi/external-snapshotter/v8 v8.3.0
|
||||
github.com/stretchr/testify v1.10.0
|
||||
google.golang.org/grpc v1.69.4
|
||||
google.golang.org/protobuf v1.36.3
|
||||
k8s.io/api v0.32.1
|
||||
k8s.io/apimachinery v0.32.1
|
||||
k8s.io/apiserver v0.32.1
|
||||
k8s.io/client-go v0.32.1
|
||||
google.golang.org/grpc v1.72.0
|
||||
google.golang.org/protobuf v1.36.6
|
||||
k8s.io/api v0.33.2
|
||||
k8s.io/apimachinery v0.33.2
|
||||
k8s.io/apiserver v0.33.2
|
||||
k8s.io/client-go v0.33.2
|
||||
k8s.io/klog/v2 v2.130.1
|
||||
)
|
||||
|
||||
|
@ -25,53 +27,52 @@ require (
|
|||
github.com/blang/semver/v4 v4.0.0 // indirect
|
||||
github.com/cespare/xxhash/v2 v2.3.0 // indirect
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.1 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 // indirect
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 // indirect
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 // indirect
|
||||
github.com/go-logr/logr v1.4.2 // indirect
|
||||
github.com/go-logr/stdr v1.2.2 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.0 // indirect
|
||||
github.com/go-openapi/jsonpointer v0.21.1 // indirect
|
||||
github.com/go-openapi/jsonreference v0.21.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.0 // indirect
|
||||
github.com/go-openapi/swag v0.23.1 // indirect
|
||||
github.com/gogo/protobuf v1.3.2 // indirect
|
||||
github.com/golang/protobuf v1.5.4 // indirect
|
||||
github.com/google/gnostic-models v0.6.9 // indirect
|
||||
github.com/google/go-cmp v0.6.0 // indirect
|
||||
github.com/google/gofuzz v1.2.0 // indirect
|
||||
github.com/google/go-cmp v0.7.0 // indirect
|
||||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/josharian/intern v1.0.0 // indirect
|
||||
github.com/json-iterator/go v1.1.12 // indirect
|
||||
github.com/klauspost/compress v1.17.11 // indirect
|
||||
github.com/mailru/easyjson v0.9.0 // indirect
|
||||
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
|
||||
github.com/modern-go/reflect2 v1.0.2 // indirect
|
||||
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
|
||||
github.com/pkg/errors v0.9.1 // indirect
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
|
||||
github.com/prometheus/client_golang v1.20.5 // indirect
|
||||
github.com/prometheus/client_model v0.6.1 // indirect
|
||||
github.com/prometheus/common v0.62.0 // indirect
|
||||
github.com/prometheus/procfs v0.15.1 // indirect
|
||||
github.com/spf13/pflag v1.0.5 // indirect
|
||||
github.com/prometheus/client_golang v1.22.0 // indirect
|
||||
github.com/prometheus/client_model v0.6.2 // indirect
|
||||
github.com/prometheus/common v0.63.0 // indirect
|
||||
github.com/prometheus/procfs v0.16.1 // indirect
|
||||
github.com/spf13/pflag v1.0.6 // indirect
|
||||
github.com/x448/float16 v0.8.4 // indirect
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 // indirect
|
||||
go.opentelemetry.io/otel v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.34.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.34.0 // indirect
|
||||
golang.org/x/net v0.34.0 // indirect
|
||||
golang.org/x/oauth2 v0.25.0 // indirect
|
||||
golang.org/x/sys v0.29.0 // indirect
|
||||
golang.org/x/term v0.28.0 // indirect
|
||||
golang.org/x/text v0.21.0 // indirect
|
||||
golang.org/x/time v0.9.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 // indirect
|
||||
go.opentelemetry.io/otel v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/metric v1.35.0 // indirect
|
||||
go.opentelemetry.io/otel/trace v1.35.0 // indirect
|
||||
go.yaml.in/yaml/v2 v2.4.2 // indirect
|
||||
golang.org/x/net v0.39.0 // indirect
|
||||
golang.org/x/oauth2 v0.30.0 // indirect
|
||||
golang.org/x/sys v0.33.0 // indirect
|
||||
golang.org/x/term v0.31.0 // indirect
|
||||
golang.org/x/text v0.24.0 // indirect
|
||||
golang.org/x/time v0.11.0 // indirect
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a // indirect
|
||||
gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect
|
||||
gopkg.in/inf.v0 v0.9.1 // indirect
|
||||
gopkg.in/yaml.v3 v3.0.1 // indirect
|
||||
k8s.io/component-base v0.32.1 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 // indirect
|
||||
k8s.io/component-base v0.33.2 // indirect
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff // indirect
|
||||
k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 // indirect
|
||||
sigs.k8s.io/yaml v1.4.0 // indirect
|
||||
sigs.k8s.io/randfill v1.0.0 // indirect
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 // indirect
|
||||
sigs.k8s.io/yaml v1.5.0 // indirect
|
||||
)
|
||||
|
|
156
go.sum
156
go.sum
|
@ -10,21 +10,21 @@ github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSs
|
|||
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
|
||||
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
|
||||
github.com/emicklei/go-restful/v3 v3.12.1 h1:PJMDIM/ak7btuL8Ex0iYET9hxM3CI2sjZtzpL63nKAU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.1/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E=
|
||||
github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2 h1:DhwDP0vY3k8ZzE0RunuJy8GhNpPL6zqLkDf9B/a0/xU=
|
||||
github.com/emicklei/go-restful/v3 v3.12.2/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0 h1:fFtUGXUzXPHTIUdne5+zzMPTfffl3RD5qYnkY40vtxU=
|
||||
github.com/fxamacker/cbor/v2 v2.8.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ=
|
||||
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
|
||||
github.com/go-logr/logr v1.4.2 h1:6pFjapn8bFcIbiKo3XT4j/BhANplGihG6tvd+8rYgrY=
|
||||
github.com/go-logr/logr v1.4.2/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
|
||||
github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
|
||||
github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
|
||||
github.com/go-openapi/jsonpointer v0.21.0 h1:YgdVicSA9vH5RiHs9TZW5oyafXZFc6+2Vc1rr/O9oNQ=
|
||||
github.com/go-openapi/jsonpointer v0.21.0/go.mod h1:IUyH9l/+uyhIYQ/PXVA41Rexl+kOkAPDdXEYns6fzUY=
|
||||
github.com/go-openapi/jsonpointer v0.21.1 h1:whnzv/pNXtK2FbX/W9yJfRmE2gsmkfahjMKB0fZvcic=
|
||||
github.com/go-openapi/jsonpointer v0.21.1/go.mod h1:50I1STOfbY1ycR8jGz8DaMeLCdXiI6aDteEdRNNzpdk=
|
||||
github.com/go-openapi/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ=
|
||||
github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4=
|
||||
github.com/go-openapi/swag v0.23.0 h1:vsEVJDUo2hPJ2tu0/Xc+4noaxyEffXNIs3cOULZ+GrE=
|
||||
github.com/go-openapi/swag v0.23.0/go.mod h1:esZ8ITTYEsH1V2trKHjAN8Ai7xHb8RV+YSZ577vPjgQ=
|
||||
github.com/go-openapi/swag v0.23.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU=
|
||||
github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0=
|
||||
github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 h1:tfuBGBXKqDEevZMzYi5KSi8KkcZtzBcTgAUUtapy0OI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
|
||||
github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
|
||||
|
@ -37,11 +37,9 @@ github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6
|
|||
github.com/google/gnostic-models v0.6.9 h1:MU/8wDLif2qCXZmzncUQ/BOfxWfthHi63KqpoNbWqVw=
|
||||
github.com/google/gnostic-models v0.6.9/go.mod h1:CiWsm0s6BSQd1hRn8/QmxqB6BesYcbSZxsz9b0KuDBw=
|
||||
github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI=
|
||||
github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
|
||||
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
|
||||
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
|
||||
github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/gofuzz v1.2.0 h1:xRy4A+RhZaiKjJ1bPfwQ8sedCA+YS2YcCHW6ec7JMi0=
|
||||
github.com/google/gofuzz v1.2.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db h1:097atOisP2aRj7vFgYQBbFN4U4JNXUNYpxael3UzMyo=
|
||||
github.com/google/pprof v0.0.0-20241029153458-d1b30febd7db/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144=
|
||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||
|
@ -52,22 +50,22 @@ github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnr
|
|||
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
|
||||
github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI2bnpBCr8=
|
||||
github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck=
|
||||
github.com/klauspost/compress v1.17.11 h1:In6xLpyWOi1+C7tXUUWv2ot1QvBjxevKAaI6IXrJmUc=
|
||||
github.com/klauspost/compress v1.17.11/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0=
|
||||
github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
|
||||
github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
|
||||
github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
|
||||
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
|
||||
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
|
||||
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
|
||||
github.com/kubernetes-csi/csi-lib-utils v0.20.0 h1:JTvHRJugn+cByMnIU4nCnqPqOOUhuPzhlLqRvenwjDA=
|
||||
github.com/kubernetes-csi/csi-lib-utils v0.20.0/go.mod h1:3b/HFVURW11oxV/gUAKyhhkvFpxXO/zRdvh1wdEfCZY=
|
||||
github.com/kubernetes-csi/csi-lib-utils v0.22.0 h1:EUAs1+uHGps3OtVj4XVx16urhpI02eu+Z8Vps6plpHY=
|
||||
github.com/kubernetes-csi/csi-lib-utils v0.22.0/go.mod h1:f+PalKyS4Ujsjb9+m6Rj0W6c28y3nfea3paQ/VqjI28=
|
||||
github.com/kubernetes-csi/csi-test/v5 v5.3.1 h1:Wiukp1In+kif+BFo6q2ExjgB+MbrAz4jZWzGfijypuY=
|
||||
github.com/kubernetes-csi/csi-test/v5 v5.3.1/go.mod h1:7hA2cSYJ6T8CraEZPA6zqkLZwemjBD54XAnPsPC3VpA=
|
||||
github.com/kubernetes-csi/external-snapshot-metadata/client v0.0.0-20240708191355-ca55d80f214a h1:oT3F4HOfYcvm3K4+SNH7nX3NnSrY9Fa82RCEpTMfaGA=
|
||||
github.com/kubernetes-csi/external-snapshot-metadata/client v0.0.0-20240708191355-ca55d80f214a/go.mod h1:u3KUZClAM2oUbRclI0UT15yrrnhFs3lO2a+y/WP1DSE=
|
||||
github.com/kubernetes-csi/external-snapshotter/client/v8 v8.2.0 h1:Q3jQ1NkFqv5o+F8dMmHd8SfEmlcwNeo1immFApntEwE=
|
||||
github.com/kubernetes-csi/external-snapshotter/client/v8 v8.2.0/go.mod h1:E3vdYxHj2C2q6qo8/Da4g7P+IcwqRZyy3gJBzYybV9Y=
|
||||
github.com/kubernetes-csi/external-snapshotter/v8 v8.2.0 h1:tkAUjVI0K0s3wp+KjIHR284nkKNIv17KHOYeEy1fd3A=
|
||||
github.com/kubernetes-csi/external-snapshotter/v8 v8.2.0/go.mod h1:FPE5MqZgUNEaBmCrbgTDOU1VdwFMsJTgthkBTnNcqH8=
|
||||
github.com/kubernetes-csi/external-snapshotter/v8 v8.3.0 h1:qkUw53bWEuHY0iVaF4V+oIS3r2vm8Sz1/xzf+D+yi80=
|
||||
github.com/kubernetes-csi/external-snapshotter/v8 v8.3.0/go.mod h1:iFq0CxAt/6NfK8QFyYRpObTL3Ar/lG8H045Btk6fo7s=
|
||||
github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0SNc=
|
||||
github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw=
|
||||
github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4=
|
||||
|
@ -88,19 +86,21 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||
github.com/prometheus/client_golang v1.20.5 h1:cxppBPuYhUnsO6yo/aoRol4L7q7UFfdm+bR9r+8l63Y=
|
||||
github.com/prometheus/client_golang v1.20.5/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE=
|
||||
github.com/prometheus/client_model v0.6.1 h1:ZKSh/rekM+n3CeS952MLRAdFwIKqeY8b62p8ais2e9E=
|
||||
github.com/prometheus/client_model v0.6.1/go.mod h1:OrxVMOVHjw3lKMa8+x6HeMGkHMQyHDk9E3jmP2AmGiY=
|
||||
github.com/prometheus/common v0.62.0 h1:xasJaQlnWAeyHdUBeGjXmutelfJHWMRr+Fg4QszZ2Io=
|
||||
github.com/prometheus/common v0.62.0/go.mod h1:vyBcEuLSvWos9B1+CyL7JZ2up+uFzXhkqml0W5zIY1I=
|
||||
github.com/prometheus/procfs v0.15.1 h1:YagwOFzUgYfKKHX6Dr+sHT7km/hxC76UB0learggepc=
|
||||
github.com/prometheus/procfs v0.15.1/go.mod h1:fB45yRUv8NstnjriLhBQLuOUt+WW4BsoGhij/e3PBqk=
|
||||
github.com/prometheus/client_golang v1.22.0 h1:rb93p9lokFEsctTys46VnV1kLCDpVZ0a/Y92Vm0Zc6Q=
|
||||
github.com/prometheus/client_golang v1.22.0/go.mod h1:R7ljNsLXhuQXYZYtw6GAE9AZg8Y7vEW5scdCXrWRXC0=
|
||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||
github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
|
||||
github.com/prometheus/common v0.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k=
|
||||
github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18=
|
||||
github.com/prometheus/procfs v0.16.1 h1:hZ15bTNuirocR6u0JZ6BAHHmwS1p8B4P6MRqxtzMyRg=
|
||||
github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is=
|
||||
github.com/rogpeppe/go-internal v1.13.1 h1:KvO1DLK/DRN07sQ1LQKScxyZJuNnedQ5/wKSR38lUII=
|
||||
github.com/rogpeppe/go-internal v1.13.1/go.mod h1:uMEvuHeurkdAXX61udpOXGD/AzZDWNMNyH2VO9fmH0o=
|
||||
github.com/spf13/pflag v1.0.5 h1:iy+VFUOCP1a+8yFto/drg2CJ5u0yRoB7fZw3DKv/JXA=
|
||||
github.com/spf13/pflag v1.0.5/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o=
|
||||
github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg=
|
||||
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
|
||||
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
|
||||
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
|
||||
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
|
||||
github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA=
|
||||
github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
|
||||
|
@ -111,18 +111,24 @@ github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9dec
|
|||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
|
||||
go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0 h1:rgMkmiGfix9vFJDcDi1PK8WEQP4FLQwLDfhp5ZLpFeE=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.59.0/go.mod h1:ijPqXp5P6IRRByFVVg9DY8P5HkxkHE5ARIa+86aXPf4=
|
||||
go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
|
||||
go.opentelemetry.io/otel v1.34.0/go.mod h1:OWFPOQ+h4G8xpyjgqo4SxJYdDQ/qmRH+wivy7zzx9oI=
|
||||
go.opentelemetry.io/otel/metric v1.34.0 h1:+eTR3U0MyfWjRDhmFMxe2SsW64QrZ84AOhvqS7Y+PoQ=
|
||||
go.opentelemetry.io/otel/metric v1.34.0/go.mod h1:CEDrp0fy2D0MvkXE+dPV7cMi8tWZwX3dmaIhwPOaqHE=
|
||||
go.opentelemetry.io/otel/sdk v1.31.0 h1:xLY3abVHYZ5HSfOg3l2E5LUj2Cwva5Y7yGxnSW9H5Gk=
|
||||
go.opentelemetry.io/otel/sdk v1.31.0/go.mod h1:TfRbMdhvxIIr/B2N2LQW2S5v9m3gOQ/08KsbbO5BPT0=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0 h1:i9hxxLJF/9kkvfHppyLL55aW7iIJz4JjxTeYusH7zMc=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.31.0/go.mod h1:CRInTMVvNhUKgSAMbKyTMxqOBC0zgyxzW55lZzX43Y8=
|
||||
go.opentelemetry.io/otel/trace v1.34.0 h1:+ouXS2V8Rd4hp4580a8q23bg0azF2nI8cqLYnC8mh/k=
|
||||
go.opentelemetry.io/otel/trace v1.34.0/go.mod h1:Svm7lSjQD7kG7KJ/MUHPVXSDGz2OX4h0M2jHBhmSfRE=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0 h1:x7wzEgXfnzJcHDwStJT+mxOz4etr2EcexjqhBvmoakw=
|
||||
go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM=
|
||||
go.opentelemetry.io/otel v1.35.0 h1:xKWKPxrxB6OtMCbmMY021CqC45J+3Onta9MqjhnusiQ=
|
||||
go.opentelemetry.io/otel v1.35.0/go.mod h1:UEqy8Zp11hpkUrL73gSlELM0DupHoiq72dR+Zqel/+Y=
|
||||
go.opentelemetry.io/otel/metric v1.35.0 h1:0znxYu2SNyuMSQT4Y9WDWej0VpcsxkuklLa4/siN90M=
|
||||
go.opentelemetry.io/otel/metric v1.35.0/go.mod h1:nKVFgxBZ2fReX6IlyW28MgZojkoAkJGaE8CpgeAU3oE=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0 h1:95zS4k/2GOy069d321O8jWgYsW3MzVV+KuSPKp7Wr1A=
|
||||
go.opentelemetry.io/otel/sdk v1.34.0/go.mod h1:0e/pNiaMAqaykJGKbi+tSjWfNNHMTxoC9qANsCzbyxU=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0 h1:5CeK9ujjbFVL5c1PhLuStg1wxA7vQv7ce1EK0Gyvahk=
|
||||
go.opentelemetry.io/otel/sdk/metric v1.34.0/go.mod h1:jQ/r8Ze28zRKoNRdkjCZxfs6YvBTG1+YIqyFVFYec5w=
|
||||
go.opentelemetry.io/otel/trace v1.35.0 h1:dPpEfJu1sDIqruz7BHFG3c7528f6ddfSWfFDVt/xgMs=
|
||||
go.opentelemetry.io/otel/trace v1.35.0/go.mod h1:WUk7DtFp1Aw2MkvqGdwiXYDZZNvA/1J8o6xRXLrIkyc=
|
||||
go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
|
||||
go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
|
||||
go.yaml.in/yaml/v2 v2.4.2 h1:DzmwEr2rDGHl7lsFgAHxmNz/1NlQ7xLIrlN2h5d1eGI=
|
||||
go.yaml.in/yaml/v2 v2.4.2/go.mod h1:081UH+NErpNdqlCXm3TtEran0rJZGxAYx9hb/ELlsPU=
|
||||
go.yaml.in/yaml/v3 v3.0.3 h1:bXOww4E/J3f66rav3pX3m8w6jDE4knZjGOw8b5Y6iNE=
|
||||
go.yaml.in/yaml/v3 v3.0.3/go.mod h1:tBHosrYAkRZjRAOREWbDnBXUf08JOwYq++0QNwQiWzI=
|
||||
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=
|
||||
|
@ -134,10 +140,10 @@ golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLL
|
|||
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.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM=
|
||||
golang.org/x/net v0.34.0 h1:Mb7Mrk043xzHgnRM88suvJFwzVrRfHEHJEl5/71CKw0=
|
||||
golang.org/x/net v0.34.0/go.mod h1:di0qlW3YNM5oh6GqDGQr92MyTozJPmybPK4Ev/Gm31k=
|
||||
golang.org/x/oauth2 v0.25.0 h1:CY4y7XT9v0cRI9oupztF8AgiIu99L/ksR/Xp/6jrZ70=
|
||||
golang.org/x/oauth2 v0.25.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI=
|
||||
golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
|
||||
golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
|
||||
golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI=
|
||||
golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKlU=
|
||||
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=
|
||||
|
@ -148,17 +154,17 @@ golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7w
|
|||
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.29.0 h1:TPYlXGxvx1MGTn2GiZDhnjPA9wZzZeGKHHmKhHYvgaU=
|
||||
golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
|
||||
golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw=
|
||||
golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
|
||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
|
||||
golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg=
|
||||
golang.org/x/term v0.28.0/go.mod h1:Sw/lC2IAUZ92udQNf3WodGtn4k/XoLyZoh8v/8uiwek=
|
||||
golang.org/x/term v0.31.0 h1:erwDkOK1Msy6offm1mOgvspSkslFnIGsFnxOKoufg3o=
|
||||
golang.org/x/term v0.31.0/go.mod h1:R4BeIy7D95HzImkxGkTW1UQTtP54tio2RyHz7PwK0aw=
|
||||
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.21.0 h1:zyQAAkrwaneQ066sspRyJaG9VNi/YJ1NfzcGB3hZ/qo=
|
||||
golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
|
||||
golang.org/x/time v0.9.0 h1:EsRrnYcQiGH+5FfbgvV4AP7qEZstoyrHB0DzarOQ4ZY=
|
||||
golang.org/x/time v0.9.0/go.mod h1:3BpzKBy/shNhVucY/MWOyx10tF3SFh9QdLuxbVysPQM=
|
||||
golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
|
||||
golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
|
||||
golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0=
|
||||
golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg=
|
||||
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=
|
||||
|
@ -170,12 +176,12 @@ golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8T
|
|||
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=
|
||||
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f h1:OxYkA3wjPsZyBylwymxSHa7ViiW1Sml4ToBrncvFehI=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f/go.mod h1:+2Yz8+CLJbIfL9z73EW45avw8Lmge3xVElCP9zEKi50=
|
||||
google.golang.org/grpc v1.69.4 h1:MF5TftSMkd8GLw/m0KM6V8CMOCY6NZ1NQDPGFgbTt4A=
|
||||
google.golang.org/grpc v1.69.4/go.mod h1:vyjdE6jLBI76dgpDojsFGNaHlxdjXN9ghpnd2o7JGZ4=
|
||||
google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1BU=
|
||||
google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a h1:51aaUVRocpvUOSQKM6Q7VuoaktNIaMCLuhZB6DKksq4=
|
||||
google.golang.org/genproto/googleapis/rpc v0.0.0-20250218202821-56aae31c358a/go.mod h1:uRxBH1mhmO8PGhU89cMcHaXKZqO+OfakD8QQO0oYwlQ=
|
||||
google.golang.org/grpc v1.72.0 h1:S7UkcVa60b5AAQTaO6ZKamFp1zMZSU0fGDK2WZLbBnM=
|
||||
google.golang.org/grpc v1.72.0/go.mod h1:wH5Aktxcg25y1I3w7H69nHfXdOG3UiadoBtjh3izSDM=
|
||||
google.golang.org/protobuf v1.36.6 h1:z1NpPI8ku2WgiWnf+t9wTPsn6eP1L7ksHUlkfLvd9xY=
|
||||
google.golang.org/protobuf v1.36.6/go.mod h1:jduwjTPXsFjZGTmRluh+L6NjiWu7pchiJ2/5YcXBHnY=
|
||||
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
|
||||
gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
|
||||
|
@ -185,25 +191,29 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc=
|
|||
gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw=
|
||||
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.32.1 h1:f562zw9cy+GvXzXf0CKlVQ7yHJVYzLfL6JAS4kOAaOc=
|
||||
k8s.io/api v0.32.1/go.mod h1:/Yi/BqkuueW1BgpoePYBRdDYfjPF5sgTr5+YqDZra5k=
|
||||
k8s.io/apimachinery v0.32.1 h1:683ENpaCBjma4CYqsmZyhEzrGz6cjn1MY/X2jB2hkZs=
|
||||
k8s.io/apimachinery v0.32.1/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE=
|
||||
k8s.io/apiserver v0.32.1 h1:oo0OozRos66WFq87Zc5tclUX2r0mymoVHRq8JmR7Aak=
|
||||
k8s.io/apiserver v0.32.1/go.mod h1:UcB9tWjBY7aryeI5zAgzVJB/6k7E97bkr1RgqDz0jPw=
|
||||
k8s.io/client-go v0.32.1 h1:otM0AxdhdBIaQh7l1Q0jQpmo7WOFIk5FFa4bg6YMdUU=
|
||||
k8s.io/client-go v0.32.1/go.mod h1:aTTKZY7MdxUaJ/KiUs8D+GssR9zJZi77ZqtzcGXIiDg=
|
||||
k8s.io/component-base v0.32.1 h1:/5IfJ0dHIKBWysGV0yKTFfacZ5yNV1sulPh3ilJjRZk=
|
||||
k8s.io/component-base v0.32.1/go.mod h1:j1iMMHi/sqAHeG5z+O9BFNCF698a1u0186zkjMZQ28w=
|
||||
k8s.io/api v0.33.2 h1:YgwIS5jKfA+BZg//OQhkJNIfie/kmRsO0BmNaVSimvY=
|
||||
k8s.io/api v0.33.2/go.mod h1:fhrbphQJSM2cXzCWgqU29xLDuks4mu7ti9vveEnpSXs=
|
||||
k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY=
|
||||
k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM=
|
||||
k8s.io/apiserver v0.33.2 h1:KGTRbxn2wJagJowo29kKBp4TchpO1DRO3g+dB/KOJN4=
|
||||
k8s.io/apiserver v0.33.2/go.mod h1:9qday04wEAMLPWWo9AwqCZSiIn3OYSZacDyu/AcoM/M=
|
||||
k8s.io/client-go v0.33.2 h1:z8CIcc0P581x/J1ZYf4CNzRKxRvQAwoAolYPbtQes+E=
|
||||
k8s.io/client-go v0.33.2/go.mod h1:9mCgT4wROvL948w6f6ArJNb7yQd7QsvqavDeZHvNmHo=
|
||||
k8s.io/component-base v0.33.2 h1:sCCsn9s/dG3ZrQTX/Us0/Sx2R0G5kwa0wbZFYoVp/+0=
|
||||
k8s.io/component-base v0.33.2/go.mod h1:/41uw9wKzuelhN+u+/C59ixxf4tYQKW7p32ddkYNe2k=
|
||||
k8s.io/klog/v2 v2.130.1 h1:n9Xl7H1Xvksem4KFG4PYbdQCQxqc/tTUyrgXaOhHSzk=
|
||||
k8s.io/klog/v2 v2.130.1/go.mod h1:3Jpz1GvMt720eyJH1ckRHK1EDfpxISzJ7I9OYgaDtPE=
|
||||
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7 h1:hcha5B1kVACrLujCKLbr8XWMxCxzQx42DY8QKYJrDLg=
|
||||
k8s.io/kube-openapi v0.0.0-20241212222426-2c72e554b1e7/go.mod h1:GewRfANuJ70iYzvn+i4lezLDAFzvjxZYK1gn1lWcfas=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff h1:/usPimJzUKKu+m+TE36gUyGcf03XZEP0ZIKgKj35LS4=
|
||||
k8s.io/kube-openapi v0.0.0-20250318190949-c8a335a9a2ff/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8=
|
||||
k8s.io/utils v0.0.0-20241210054802-24370beab758 h1:sdbE21q2nlQtFh65saZY+rRM6x6aJJI8IUa1AmH/qa0=
|
||||
k8s.io/utils v0.0.0-20241210054802-24370beab758/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 h1:gBQPwqORJ8d8/YNZWEjoZs7npUVDpVXUUOFfW6CgAqE=
|
||||
sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0 h1:nbCitCK2hfnhyiKo6uf2HxUPTCodY6Qaf85SbDIaMBk=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.5.0/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4=
|
||||
sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E=
|
||||
sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/randfill v1.0.0 h1:JfjMILfT8A6RbawdsK2JXGBR5AQVfd+9TbzrlneTyrU=
|
||||
sigs.k8s.io/randfill v1.0.0/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0 h1:qPeWmscJcXP0snki5IYF79Z8xrl8ETFxgMd7wez1XkI=
|
||||
sigs.k8s.io/structured-merge-diff/v4 v4.7.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps=
|
||||
sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY=
|
||||
sigs.k8s.io/yaml v1.5.0 h1:M10b2U7aEUY6hRtU870n2VTPgR5RZiL/I6Lcc2F4NUQ=
|
||||
sigs.k8s.io/yaml v1.5.0/go.mod h1:wZs27Rbxoai4C0f8/9urLZtZtF3avA3gKvGyPdDqTO4=
|
||||
|
|
|
@ -56,6 +56,8 @@ type Args struct {
|
|||
HttpEndpoint string
|
||||
// MetricsPath is the path where metrics will be recorded
|
||||
MetricsPath string
|
||||
// Audience string is used for authentication.
|
||||
Audience string
|
||||
}
|
||||
|
||||
func (args *Args) Validate() error {
|
||||
|
@ -100,6 +102,7 @@ type Runtime struct {
|
|||
CSIConn *grpc.ClientConn
|
||||
MetricsManager metrics.CSIMetricsManager
|
||||
DriverName string
|
||||
Audience string
|
||||
}
|
||||
|
||||
// initialize obtains the clients and then the CSI driver name.
|
||||
|
|
|
@ -69,6 +69,11 @@ func (s *Server) authenticateRequest(ctx context.Context, securityToken string)
|
|||
}
|
||||
|
||||
func (s *Server) getAudienceForDriver(ctx context.Context) (string, error) {
|
||||
if audience := s.audience(); audience != "" {
|
||||
// If the audience string is set, return it.
|
||||
return audience, nil
|
||||
}
|
||||
|
||||
sms, err := s.cbtClient().CbtV1alpha1().SnapshotMetadataServices().Get(ctx, s.driverName(), apimetav1.GetOptions{})
|
||||
if err != nil {
|
||||
klog.FromContext(ctx).Error(err, msgInternalFailedToFindCR, "driver", s.driverName())
|
||||
|
|
|
@ -41,6 +41,14 @@ func TestAuthenticateAndAuthorize(t *testing.T) {
|
|||
assert.Error(t, err)
|
||||
assert.Empty(t, retAudience)
|
||||
|
||||
// set audience in Args
|
||||
testAudience := "test-audience"
|
||||
s.config.Runtime.Audience = testAudience
|
||||
retAudience, err = s.getAudienceForDriver(context.Background())
|
||||
assert.NoError(t, err)
|
||||
assert.Equal(t, testAudience, retAudience)
|
||||
s.config.Runtime.Audience = ""
|
||||
|
||||
// fail via authenticateAndAuthorize
|
||||
err = s.authenticateAndAuthorize(context.Background(), "some-token", "some-namespace")
|
||||
assert.Error(t, err)
|
||||
|
|
|
@ -101,6 +101,10 @@ func (s *Server) csiConnection() *grpc.ClientConn {
|
|||
return s.config.Runtime.CSIConn
|
||||
}
|
||||
|
||||
func (s *Server) audience() string {
|
||||
return s.config.Runtime.Audience
|
||||
}
|
||||
|
||||
func buildOptions(config ServerConfig) ([]grpc.ServerOption, error) {
|
||||
tlsOptions, err := buildTLSOption(config.Runtime.TLSCertFile, config.Runtime.TLSKeyFile)
|
||||
if err != nil {
|
||||
|
|
|
@ -57,7 +57,7 @@ type testHarness struct {
|
|||
// fake emitter
|
||||
InSnapshotMetadataIteratorRecordNum int
|
||||
InSnapshotMetadataIteratorRecordMeta []IteratorMetadata
|
||||
RetSnapshotMetadataIteratorRecord bool
|
||||
RetSnapshotMetadataIteratorRecord error
|
||||
|
||||
InSnapshotMetadataIteratorDoneNR int
|
||||
|
||||
|
@ -244,15 +244,17 @@ func (th *testHarness) GRPCSnapshotMetadataClient(t *testing.T) api.SnapshotMeta
|
|||
}
|
||||
|
||||
// fake emitter
|
||||
func (th *testHarness) SnapshotMetadataIteratorRecord(recordNumber int, metadata IteratorMetadata) bool {
|
||||
func (th *testHarness) SnapshotMetadataIteratorRecord(recordNumber int, metadata IteratorMetadata) error {
|
||||
th.InSnapshotMetadataIteratorRecordMeta = append(th.InSnapshotMetadataIteratorRecordMeta, metadata)
|
||||
th.InSnapshotMetadataIteratorRecordNum = recordNumber
|
||||
|
||||
return th.RetSnapshotMetadataIteratorRecord
|
||||
}
|
||||
|
||||
func (th *testHarness) SnapshotMetadataIteratorDone(numberRecords int) {
|
||||
func (th *testHarness) SnapshotMetadataIteratorDone(numberRecords int) error {
|
||||
th.InSnapshotMetadataIteratorDoneNR = numberRecords
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// fake helpers
|
||||
|
|
|
@ -19,9 +19,13 @@ package iterator
|
|||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/kubernetes-csi/external-snapshot-metadata/pkg/api"
|
||||
)
|
||||
|
||||
// JSONEmitter formats the metadata in JSON.
|
||||
|
@ -30,7 +34,7 @@ type JSONEmitter struct {
|
|||
listNotEmpty bool
|
||||
}
|
||||
|
||||
func (e *JSONEmitter) SnapshotMetadataIteratorRecord(recordNumber int, metadata IteratorMetadata) bool {
|
||||
func (e *JSONEmitter) SnapshotMetadataIteratorRecord(recordNumber int, metadata IteratorMetadata) error {
|
||||
prefix := "," // termination of previous record
|
||||
if recordNumber == 1 {
|
||||
prefix = "[" // no previous, start of list
|
||||
|
@ -42,15 +46,17 @@ func (e *JSONEmitter) SnapshotMetadataIteratorRecord(recordNumber int, metadata
|
|||
|
||||
fmt.Fprintf(e.Writer, "%s%s", prefix, strings.TrimSuffix(b.String(), "\n"))
|
||||
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *JSONEmitter) SnapshotMetadataIteratorDone(_ int) {
|
||||
func (e *JSONEmitter) SnapshotMetadataIteratorDone(_ int) error {
|
||||
if e.listNotEmpty {
|
||||
fmt.Fprintf(e.Writer, "]") // termination of previous, end of list
|
||||
} else {
|
||||
fmt.Fprintf(e.Writer, "[]") // empty list
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// TableEmitter formats the metadata as a table.
|
||||
|
@ -66,7 +72,7 @@ const (
|
|||
tableRowFmt = "%7d %14d %17s %14d %14d\n"
|
||||
)
|
||||
|
||||
func (e *TableEmitter) SnapshotMetadataIteratorRecord(recordNumber int, metadata IteratorMetadata) bool {
|
||||
func (e *TableEmitter) SnapshotMetadataIteratorRecord(recordNumber int, metadata IteratorMetadata) error {
|
||||
if recordNumber == 1 {
|
||||
fmt.Fprintf(e.Writer, "%s\n%s\n", tableHeader1, tableHeader2)
|
||||
}
|
||||
|
@ -76,7 +82,116 @@ func (e *TableEmitter) SnapshotMetadataIteratorRecord(recordNumber int, metadata
|
|||
fmt.Fprintf(e.Writer, tableRowFmt, recordNumber, metadata.VolumeCapacityBytes, bmt, bmd.ByteOffset, bmd.SizeBytes)
|
||||
}
|
||||
|
||||
return true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (e *TableEmitter) SnapshotMetadataIteratorDone(_ int) {}
|
||||
func (e *TableEmitter) SnapshotMetadataIteratorDone(_ int) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// VerifierEmitter will write the changed blocks from the source device to the target device at the designated offset and
|
||||
// compare the contents of the source and target devices at the end.
|
||||
type VerifierEmitter struct {
|
||||
// SourceDevice contains the source device file descriptor.
|
||||
SourceDevice *os.File
|
||||
|
||||
// TargetDevice contains the target device file descriptor.
|
||||
TargetDevice *os.File
|
||||
}
|
||||
|
||||
const chunkSize = int64(1024 * 1024) // 4KB
|
||||
var (
|
||||
ErrFailedToSeek = errors.New("failed to seek")
|
||||
ErrContentsDoNotMatch = errors.New("source and target device contents do not match")
|
||||
)
|
||||
|
||||
// copyBlock will copy chunks of 256 bytes at a time upto to sizeBytes from the source device to the target device.
|
||||
func (verifierEmitter *VerifierEmitter) copyBlock(bmd *api.BlockMetadata) error {
|
||||
// Seek to the byteOffset of the source device.
|
||||
_, err := verifierEmitter.SourceDevice.Seek(bmd.ByteOffset, io.SeekStart)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w (source device(%q), offset(%d)): %w", ErrFailedToSeek, verifierEmitter.SourceDevice.Name(), bmd.ByteOffset, err)
|
||||
}
|
||||
|
||||
// Seek to the byteOffset of the target device.
|
||||
_, err = verifierEmitter.TargetDevice.Seek(bmd.ByteOffset, io.SeekStart)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w (target device(%q), offset(%d)): %w", ErrFailedToSeek, verifierEmitter.TargetDevice.Name(), bmd.ByteOffset, err)
|
||||
}
|
||||
|
||||
buffer := make([]byte, chunkSize)
|
||||
for written := int64(0); written < bmd.SizeBytes; {
|
||||
remaining := bmd.SizeBytes - written
|
||||
if remaining < chunkSize {
|
||||
buffer = make([]byte, remaining)
|
||||
}
|
||||
|
||||
// Read a chunk from the source device.
|
||||
sourceBytes, sourceErr := verifierEmitter.SourceDevice.Read(buffer)
|
||||
if sourceErr != nil && sourceErr != io.EOF {
|
||||
return fmt.Errorf("failed to read from source device(%q): %w", verifierEmitter.SourceDevice.Name(), sourceErr)
|
||||
}
|
||||
|
||||
// Write the chunk to the target device.
|
||||
_, targetErr := verifierEmitter.TargetDevice.Write(buffer[:sourceBytes])
|
||||
if targetErr != nil {
|
||||
return fmt.Errorf("failed to write to target device(%q): %w", verifierEmitter.TargetDevice.Name(), targetErr)
|
||||
}
|
||||
|
||||
written += int64(sourceBytes)
|
||||
if sourceErr == io.EOF {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SnapshotMetadataIteratorRecord will write the block from the source device to the target device at the designated offset.
|
||||
func (verifierEmitter *VerifierEmitter) SnapshotMetadataIteratorRecord(_ int, metadata IteratorMetadata) error {
|
||||
for _, bmd := range metadata.BlockMetadata {
|
||||
if err := verifierEmitter.copyBlock(bmd); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// SnapshotMetadataIteratorDone will compare the contents of the source and target devices.
|
||||
func (verifierEmitter *VerifierEmitter) SnapshotMetadataIteratorDone(_ int) error {
|
||||
// Seek to the start of the source and target devices.
|
||||
_, err := verifierEmitter.SourceDevice.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w (source device(%q), offset(%d)): %w", ErrFailedToSeek, verifierEmitter.SourceDevice.Name(), io.SeekStart, err)
|
||||
}
|
||||
_, err = verifierEmitter.TargetDevice.Seek(0, io.SeekStart)
|
||||
if err != nil {
|
||||
return fmt.Errorf("%w (target device(%q), offset(%d)): %w", ErrFailedToSeek, verifierEmitter.TargetDevice.Name(), io.SeekStart, err)
|
||||
}
|
||||
|
||||
sourceBuffer := make([]byte, chunkSize)
|
||||
targetBuffer := make([]byte, chunkSize)
|
||||
for {
|
||||
// Read a chunk from the source and target devices.
|
||||
sourceBytes, sourceErr := verifierEmitter.SourceDevice.Read(sourceBuffer)
|
||||
targetBytes, targetErr := verifierEmitter.TargetDevice.Read(targetBuffer)
|
||||
|
||||
if sourceErr != nil || targetErr != nil {
|
||||
if sourceErr == io.EOF && targetErr == io.EOF {
|
||||
// Both devices have been read completely.
|
||||
return nil
|
||||
} else if sourceErr == io.EOF || targetErr == io.EOF {
|
||||
// One device has been read completely but the other has not.
|
||||
return ErrContentsDoNotMatch
|
||||
} else {
|
||||
// An error occurred while reading from both devices.
|
||||
return fmt.Errorf("error reading source and target device contents: source(%q) target(%q)", sourceErr, targetErr)
|
||||
}
|
||||
}
|
||||
|
||||
if (sourceBytes != targetBytes) || !bytes.Equal(sourceBuffer[:sourceBytes], targetBuffer[:targetBytes]) {
|
||||
return ErrContentsDoNotMatch
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -18,6 +18,9 @@ package iterator
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/rand"
|
||||
"math/big"
|
||||
"os"
|
||||
"testing"
|
||||
|
||||
"github.com/kubernetes-csi/external-snapshot-metadata/pkg/api"
|
||||
|
@ -156,3 +159,180 @@ func TestTableEmitter(t *testing.T) {
|
|||
assert.Equal(t, exp, b.String())
|
||||
})
|
||||
}
|
||||
|
||||
func TestVerifierEmitter(t *testing.T) {
|
||||
capacity := 1024 * 1024
|
||||
dataBuffer := make([]byte, capacity)
|
||||
tmpDir := t.TempDir()
|
||||
sourceDevice, err := os.CreateTemp(tmpDir, "sourceDevice")
|
||||
assert.NoError(t, err)
|
||||
_, err = sourceDevice.Write(dataBuffer)
|
||||
assert.NoError(t, err)
|
||||
|
||||
targetDevice, err := os.CreateTemp(tmpDir, "targetDevice")
|
||||
assert.NoError(t, err)
|
||||
_, err = targetDevice.Write(dataBuffer)
|
||||
assert.NoError(t, err)
|
||||
|
||||
smallerDevice, err := os.CreateTemp(tmpDir, "smallerDevice")
|
||||
assert.NoError(t, err)
|
||||
_, err = smallerDevice.Write(dataBuffer[:capacity/2])
|
||||
assert.NoError(t, err)
|
||||
|
||||
t.Run("no records", func(t *testing.T) {
|
||||
e := &VerifierEmitter{
|
||||
SourceDevice: sourceDevice,
|
||||
TargetDevice: targetDevice,
|
||||
}
|
||||
|
||||
err := e.SnapshotMetadataIteratorDone(100) // count doesn't matter
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("one record", func(t *testing.T) {
|
||||
sizeBytes := 1111
|
||||
byteOffset := 0
|
||||
e := &VerifierEmitter{
|
||||
SourceDevice: sourceDevice,
|
||||
TargetDevice: targetDevice,
|
||||
}
|
||||
writeRandomData(t, sourceDevice, sizeBytes, byteOffset)
|
||||
|
||||
// data contents should not match
|
||||
err = e.SnapshotMetadataIteratorDone(100) // count doesn't matter
|
||||
assert.ErrorIs(t, err, ErrContentsDoNotMatch)
|
||||
|
||||
// trigger copy of changed blocks
|
||||
err = e.SnapshotMetadataIteratorRecord(1, IteratorMetadata{
|
||||
BlockMetadataType: api.BlockMetadataType_FIXED_LENGTH,
|
||||
VolumeCapacityBytes: int64(capacity),
|
||||
BlockMetadata: []*api.BlockMetadata{
|
||||
{
|
||||
ByteOffset: int64(byteOffset),
|
||||
SizeBytes: int64(sizeBytes),
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// data contents should match
|
||||
err = e.SnapshotMetadataIteratorDone(100) // count doesn't matter
|
||||
assert.NoError(t, err)
|
||||
})
|
||||
|
||||
t.Run("multiple records", func(t *testing.T) {
|
||||
blockMetadataList := []*api.BlockMetadata{}
|
||||
bytesOffset := 0
|
||||
sizeBytes := 0
|
||||
e := &VerifierEmitter{
|
||||
SourceDevice: sourceDevice,
|
||||
TargetDevice: targetDevice,
|
||||
}
|
||||
for i := 0; i < 10; i++ {
|
||||
randOffset, err := rand.Int(rand.Reader, big.NewInt(1000))
|
||||
assert.NoError(t, err)
|
||||
randBytesSize, err := rand.Int(rand.Reader, big.NewInt(1000))
|
||||
assert.NoError(t, err)
|
||||
bytesOffset = bytesOffset + sizeBytes + int(randOffset.Int64())
|
||||
sizeBytes = int(randBytesSize.Int64())
|
||||
writeRandomData(t, sourceDevice, sizeBytes, bytesOffset)
|
||||
blockMetadataList = append(blockMetadataList, &api.BlockMetadata{
|
||||
ByteOffset: int64(bytesOffset),
|
||||
SizeBytes: int64(sizeBytes),
|
||||
})
|
||||
}
|
||||
// data contents should not match
|
||||
err = e.SnapshotMetadataIteratorDone(100) // count doesn't matter
|
||||
assert.ErrorIs(t, err, ErrContentsDoNotMatch)
|
||||
|
||||
// trigger copy of changed blocks
|
||||
err = e.SnapshotMetadataIteratorRecord(1, IteratorMetadata{
|
||||
BlockMetadataType: api.BlockMetadataType_FIXED_LENGTH,
|
||||
VolumeCapacityBytes: int64(capacity),
|
||||
BlockMetadata: blockMetadataList,
|
||||
})
|
||||
assert.NoError(t, err)
|
||||
|
||||
// data contents should match
|
||||
err = e.SnapshotMetadataIteratorDone(100) // count doesn't matter
|
||||
assert.NoError(t, err)
|
||||
|
||||
})
|
||||
|
||||
t.Run("source device is smaller than target device", func(t *testing.T) {
|
||||
e := &VerifierEmitter{
|
||||
SourceDevice: smallerDevice,
|
||||
TargetDevice: targetDevice,
|
||||
}
|
||||
err = e.SnapshotMetadataIteratorDone(100) // count doesn't matter
|
||||
assert.ErrorIs(t, err, ErrContentsDoNotMatch)
|
||||
})
|
||||
|
||||
t.Run("target device is smaller than source device", func(t *testing.T) {
|
||||
e := &VerifierEmitter{
|
||||
SourceDevice: sourceDevice,
|
||||
TargetDevice: smallerDevice,
|
||||
}
|
||||
err = e.SnapshotMetadataIteratorDone(100) // count doesn't matter
|
||||
assert.ErrorIs(t, err, ErrContentsDoNotMatch)
|
||||
})
|
||||
|
||||
t.Run("closed device is passed as source device", func(t *testing.T) {
|
||||
assert.Nil(t, smallerDevice.Close())
|
||||
e := &VerifierEmitter{
|
||||
SourceDevice: smallerDevice,
|
||||
TargetDevice: targetDevice,
|
||||
}
|
||||
err = e.SnapshotMetadataIteratorRecord(1, IteratorMetadata{
|
||||
BlockMetadataType: api.BlockMetadataType_FIXED_LENGTH,
|
||||
VolumeCapacityBytes: int64(capacity),
|
||||
BlockMetadata: []*api.BlockMetadata{
|
||||
{
|
||||
ByteOffset: 0,
|
||||
SizeBytes: 1000,
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.ErrorIs(t, err, ErrFailedToSeek)
|
||||
assert.Contains(t, err.Error(), "file already closed")
|
||||
|
||||
err := e.SnapshotMetadataIteratorDone(100) // count doesn't matter
|
||||
assert.ErrorIs(t, err, ErrFailedToSeek)
|
||||
assert.Contains(t, err.Error(), "file already closed")
|
||||
})
|
||||
|
||||
t.Run("closed device is passed as target device", func(t *testing.T) {
|
||||
e := &VerifierEmitter{
|
||||
SourceDevice: sourceDevice,
|
||||
TargetDevice: smallerDevice,
|
||||
}
|
||||
err = e.SnapshotMetadataIteratorRecord(1, IteratorMetadata{
|
||||
BlockMetadataType: api.BlockMetadataType_FIXED_LENGTH,
|
||||
VolumeCapacityBytes: int64(capacity),
|
||||
BlockMetadata: []*api.BlockMetadata{
|
||||
{
|
||||
ByteOffset: 0,
|
||||
SizeBytes: 1000,
|
||||
},
|
||||
},
|
||||
})
|
||||
assert.Error(t, err)
|
||||
assert.ErrorIs(t, err, ErrFailedToSeek)
|
||||
assert.Contains(t, err.Error(), "file already closed")
|
||||
|
||||
err = e.SnapshotMetadataIteratorDone(100) // count doesn't matter
|
||||
assert.ErrorIs(t, err, ErrFailedToSeek)
|
||||
assert.Contains(t, err.Error(), "file already closed")
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
func writeRandomData(t *testing.T, file *os.File, sizeBytes, byteOffset int) {
|
||||
dataBuffer := make([]byte, sizeBytes)
|
||||
// generate random data into dataBuffer
|
||||
_, err := rand.Read(dataBuffer)
|
||||
assert.NoError(t, err)
|
||||
_, err = file.WriteAt(dataBuffer, int64(byteOffset))
|
||||
assert.NoError(t, err)
|
||||
}
|
||||
|
|
|
@ -148,14 +148,11 @@ type IteratorMetadata struct {
|
|||
type IteratorEmitter interface {
|
||||
// SnapshotMetadataIteratorRecord is invoked for each record received
|
||||
// from the gRPC stream.
|
||||
// The operation should return true to continue or false to stop
|
||||
// enumerating the records. If false was returned then the iterator
|
||||
// will terminate with an ErrCancelled error.
|
||||
SnapshotMetadataIteratorRecord(recordNumber int, metadata IteratorMetadata) bool
|
||||
SnapshotMetadataIteratorRecord(recordNumber int, metadata IteratorMetadata) error
|
||||
|
||||
// SnapshotMetadataIteratorDone is called prior to termination as long as
|
||||
// no error was encountered.
|
||||
SnapshotMetadataIteratorDone(numberRecords int)
|
||||
SnapshotMetadataIteratorDone(numberRecords int) error
|
||||
}
|
||||
|
||||
type iterator struct {
|
||||
|
@ -241,11 +238,11 @@ func (iter *iterator) run(ctx context.Context) error {
|
|||
err = iter.h.getChangedBlocks(ctx, apiClient, securityToken)
|
||||
}
|
||||
|
||||
if err == nil {
|
||||
iter.Emitter.SnapshotMetadataIteratorDone(iter.recordNum)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
return iter.Emitter.SnapshotMetadataIteratorDone(iter.recordNum)
|
||||
}
|
||||
|
||||
func (iter *iterator) getDefaultServiceAccount(ctx context.Context) (namespace string, name string, err error) {
|
||||
|
@ -353,12 +350,13 @@ func (iter *iterator) getAllocatedBlocks(ctx context.Context, grpcClient api.Sna
|
|||
|
||||
iter.recordNum++
|
||||
|
||||
if !iter.Emitter.SnapshotMetadataIteratorRecord(iter.recordNum, IteratorMetadata{
|
||||
err = iter.Emitter.SnapshotMetadataIteratorRecord(iter.recordNum, IteratorMetadata{
|
||||
BlockMetadataType: resp.BlockMetadataType,
|
||||
VolumeCapacityBytes: resp.VolumeCapacityBytes,
|
||||
BlockMetadata: resp.BlockMetadata,
|
||||
}) {
|
||||
return ErrCancelled
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -388,12 +386,13 @@ func (iter *iterator) getChangedBlocks(ctx context.Context, grpcClient api.Snaps
|
|||
|
||||
iter.recordNum++
|
||||
|
||||
if !iter.Emitter.SnapshotMetadataIteratorRecord(iter.recordNum, IteratorMetadata{
|
||||
err = iter.Emitter.SnapshotMetadataIteratorRecord(iter.recordNum, IteratorMetadata{
|
||||
BlockMetadataType: resp.BlockMetadataType,
|
||||
VolumeCapacityBytes: resp.VolumeCapacityBytes,
|
||||
BlockMetadata: resp.BlockMetadata,
|
||||
}) {
|
||||
return ErrCancelled
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -617,7 +617,7 @@ func TestGetAllocatedBlocks(t *testing.T) {
|
|||
|
||||
t.Run("stream-rec-rec-EOF", func(t *testing.T) {
|
||||
th := newTestHarness()
|
||||
th.RetSnapshotMetadataIteratorRecord = true
|
||||
th.RetSnapshotMetadataIteratorRecord = nil
|
||||
iter := th.NewTestIterator()
|
||||
|
||||
mockController := gomock.NewController(t)
|
||||
|
@ -642,7 +642,7 @@ func TestGetAllocatedBlocks(t *testing.T) {
|
|||
|
||||
t.Run("stream-rec-rec-Err", func(t *testing.T) {
|
||||
th := newTestHarness()
|
||||
th.RetSnapshotMetadataIteratorRecord = true
|
||||
th.RetSnapshotMetadataIteratorRecord = nil
|
||||
iter := th.NewTestIterator()
|
||||
|
||||
mockController := gomock.NewController(t)
|
||||
|
@ -668,7 +668,7 @@ func TestGetAllocatedBlocks(t *testing.T) {
|
|||
|
||||
t.Run("stream-rec-ABORT", func(t *testing.T) {
|
||||
th := newTestHarness()
|
||||
th.RetSnapshotMetadataIteratorRecord = true
|
||||
th.RetSnapshotMetadataIteratorRecord = nil
|
||||
iter := th.NewTestIterator()
|
||||
|
||||
mockController := gomock.NewController(t)
|
||||
|
@ -678,7 +678,7 @@ func TestGetAllocatedBlocks(t *testing.T) {
|
|||
mockStream := k8sclientmocks.NewMockSnapshotMetadata_GetMetadataAllocatedClient(mockController)
|
||||
|
||||
mockStream.EXPECT().Recv().Return(responses[0], nil) // one record only
|
||||
th.RetSnapshotMetadataIteratorRecord = false // then abort
|
||||
th.RetSnapshotMetadataIteratorRecord = ErrCancelled // then abort
|
||||
|
||||
expReq := th.FakeGetMetadataAllocatedRequest()
|
||||
mockClient.EXPECT().GetMetadataAllocated(gomock.Any(), expReq).Return(mockStream, nil)
|
||||
|
@ -759,7 +759,7 @@ func TestGetChangedBlocks(t *testing.T) {
|
|||
|
||||
t.Run("stream-rec-rec-EOF", func(t *testing.T) {
|
||||
th := newTestHarness()
|
||||
th.RetSnapshotMetadataIteratorRecord = true
|
||||
th.RetSnapshotMetadataIteratorRecord = nil
|
||||
iter := th.NewTestIterator()
|
||||
|
||||
mockController := gomock.NewController(t)
|
||||
|
@ -784,7 +784,7 @@ func TestGetChangedBlocks(t *testing.T) {
|
|||
|
||||
t.Run("stream-rec-rec-Err", func(t *testing.T) {
|
||||
th := newTestHarness()
|
||||
th.RetSnapshotMetadataIteratorRecord = true
|
||||
th.RetSnapshotMetadataIteratorRecord = nil
|
||||
iter := th.NewTestIterator()
|
||||
|
||||
mockController := gomock.NewController(t)
|
||||
|
@ -810,7 +810,7 @@ func TestGetChangedBlocks(t *testing.T) {
|
|||
|
||||
t.Run("stream-rec-ABORT", func(t *testing.T) {
|
||||
th := newTestHarness()
|
||||
th.RetSnapshotMetadataIteratorRecord = true
|
||||
th.RetSnapshotMetadataIteratorRecord = nil
|
||||
iter := th.NewTestIterator()
|
||||
|
||||
mockController := gomock.NewController(t)
|
||||
|
@ -820,7 +820,7 @@ func TestGetChangedBlocks(t *testing.T) {
|
|||
mockStream := k8sclientmocks.NewMockSnapshotMetadata_GetMetadataDeltaClient(mockController)
|
||||
|
||||
mockStream.EXPECT().Recv().Return(responses[0], nil) // one record only
|
||||
th.RetSnapshotMetadataIteratorRecord = false // then abort
|
||||
th.RetSnapshotMetadataIteratorRecord = ErrCancelled // then abort
|
||||
|
||||
expReq := th.FakeGetMetadataDeltaRequest()
|
||||
mockClient.EXPECT().GetMetadataDelta(gomock.Any(), expReq).Return(mockStream, nil)
|
||||
|
|
|
@ -55,6 +55,7 @@ const (
|
|||
flagTLSCert = "tls-cert"
|
||||
flagTLSKey = "tls-key"
|
||||
flagVersion = "version"
|
||||
flagAudience = "audience"
|
||||
|
||||
// tlsCertEnvVar is an environment variable that specifies the path to tls certificate file.
|
||||
tlsCertEnvVar = "TLS_CERT_PATH"
|
||||
|
@ -130,6 +131,7 @@ type sidecarFlagSet struct {
|
|||
showVersion *bool
|
||||
tlsCert *string
|
||||
tlsKey *string
|
||||
audience *string
|
||||
}
|
||||
|
||||
var sidecarFlagSetErrorHandling flag.ErrorHandling = flag.ExitOnError // UT interception point.
|
||||
|
@ -149,6 +151,7 @@ func newSidecarFlagSet(name, version string) *sidecarFlagSet {
|
|||
s.grpcPort = s.Int(flagGRPCPort, defaultGRPCPort, "GRPC SnapshotMetadata service port number")
|
||||
s.tlsCert = s.String(flagTLSCert, os.Getenv(tlsCertEnvVar), "Path to the TLS certificate file. Can also be set with the environment variable "+tlsCertEnvVar+".")
|
||||
s.tlsKey = s.String(flagTLSKey, os.Getenv(tlsKeyEnvVar), "Path to the TLS private key file. Can also be set with the environment variable "+tlsKeyEnvVar+".")
|
||||
s.audience = s.String(flagAudience, "", "Audience string used for authentication.")
|
||||
|
||||
s.maxStreamingDurMin = s.Int(flagMaxStreamingDurationMin, defaultMaxStreamingDurationMin, "The maximum duration in minutes for any individual streaming session")
|
||||
|
||||
|
@ -193,6 +196,7 @@ func (s *sidecarFlagSet) runtimeArgsFromFlags() runtime.Args {
|
|||
TLSKeyFile: *s.tlsKey,
|
||||
HttpEndpoint: *s.httpEndpoint,
|
||||
MetricsPath: *s.metricsPath,
|
||||
Audience: *s.audience,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -86,7 +86,7 @@ configvar CSI_PROW_BUILD_PLATFORMS "linux amd64 amd64; linux ppc64le ppc64le -pp
|
|||
# which is disabled with GOFLAGS=-mod=vendor).
|
||||
configvar GOFLAGS_VENDOR "$( [ -d vendor ] && echo '-mod=vendor' )" "Go flags for using the vendor directory"
|
||||
|
||||
configvar CSI_PROW_GO_VERSION_BUILD "1.23.1" "Go version for building the component" # depends on component's source code
|
||||
configvar CSI_PROW_GO_VERSION_BUILD "1.24.2" "Go version for building the component" # depends on component's source code
|
||||
configvar CSI_PROW_GO_VERSION_E2E "" "override Go version for building the Kubernetes E2E test suite" # normally doesn't need to be set, see install_e2e
|
||||
configvar CSI_PROW_GO_VERSION_SANITY "${CSI_PROW_GO_VERSION_BUILD}" "Go version for building the csi-sanity test suite" # depends on CSI_PROW_SANITY settings below
|
||||
configvar CSI_PROW_GO_VERSION_KIND "${CSI_PROW_GO_VERSION_BUILD}" "Go version for building 'kind'" # depends on CSI_PROW_KIND_VERSION below
|
||||
|
@ -144,7 +144,7 @@ kind_version_default () {
|
|||
latest|master)
|
||||
echo main;;
|
||||
*)
|
||||
echo v0.14.0;;
|
||||
echo v0.25.0;;
|
||||
esac
|
||||
}
|
||||
|
||||
|
@ -155,13 +155,13 @@ configvar CSI_PROW_KIND_VERSION "$(kind_version_default)" "kind"
|
|||
|
||||
# kind images to use. Must match the kind version.
|
||||
# The release notes of each kind release list the supported images.
|
||||
configvar CSI_PROW_KIND_IMAGES "kindest/node:v1.24.0@sha256:0866296e693efe1fed79d5e6c7af8df71fc73ae45e3679af05342239cdc5bc8e
|
||||
kindest/node:v1.23.6@sha256:b1fa224cc6c7ff32455e0b1fd9cbfd3d3bc87ecaa8fcb06961ed1afb3db0f9ae
|
||||
kindest/node:v1.22.9@sha256:8135260b959dfe320206eb36b3aeda9cffcb262f4b44cda6b33f7bb73f453105
|
||||
kindest/node:v1.21.12@sha256:f316b33dd88f8196379f38feb80545ef3ed44d9197dca1bfd48bcb1583210207
|
||||
kindest/node:v1.20.15@sha256:6f2d011dffe182bad80b85f6c00e8ca9d86b5b8922cdf433d53575c4c5212248
|
||||
kindest/node:v1.19.16@sha256:d9c819e8668de8d5030708e484a9fdff44d95ec4675d136ef0a0a584e587f65c
|
||||
kindest/node:v1.18.20@sha256:738cdc23ed4be6cc0b7ea277a2ebcc454c8373d7d8fb991a7fcdbd126188e6d7" "kind images"
|
||||
configvar CSI_PROW_KIND_IMAGES "kindest/node:v1.32.0@sha256:2458b423d635d7b01637cac2d6de7e1c1dca1148a2ba2e90975e214ca849e7cb
|
||||
kindest/node:v1.31.2@sha256:18fbefc20a7113353c7b75b5c869d7145a6abd6269154825872dc59c1329912e
|
||||
kindest/node:v1.30.6@sha256:b6d08db72079ba5ae1f4a88a09025c0a904af3b52387643c285442afb05ab994
|
||||
kindest/node:v1.29.10@sha256:3b2d8c31753e6c8069d4fc4517264cd20e86fd36220671fb7d0a5855103aa84b
|
||||
kindest/node:v1.28.15@sha256:a7c05c7ae043a0b8c818f5a06188bc2c4098f6cb59ca7d1856df00375d839251
|
||||
kindest/node:v1.27.16@sha256:2d21a61643eafc439905e18705b8186f3296384750a835ad7a005dceb9546d20
|
||||
kindest/node:v1.26.15@sha256:c79602a44b4056d7e48dc20f7504350f1e87530fe953428b792def00bc1076dd" "kind images"
|
||||
|
||||
# By default, this script tests sidecars with the CSI hostpath driver,
|
||||
# using the install_csi_driver function. That function depends on
|
||||
|
|
Loading…
Reference in New Issue