diff --git a/api/v1alpha1/image_types.go b/api/v1alpha1/image_types.go index 6f3ad40..b1dfe86 100644 --- a/api/v1alpha1/image_types.go +++ b/api/v1alpha1/image_types.go @@ -20,9 +20,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - const ( ImageRepositoryLabel = "sbombastic.rancher.io/repository" ImageRegistryLabel = "sbombastic.rancher.io/registry" @@ -30,11 +27,19 @@ const ( // ImageSpec defines the desired state of Image type ImageSpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file + // list of the layers that make the image + Layers []ImageLayer `json:"layers,omitempty"` +} - // Foo is an example field of Image. Edit image_types.go to remove/update - Foo string `json:"foo,omitempty"` +// ImageLayer define a layer part of an OCI Image +type ImageLayer struct { + // command is the command that led to the creation + // of the layer. The contents are base64 encoded + Command string `json:"command"` + // digest is the Hash of the compressed layer + Digest string `json:"digest"` + // diffID is the Hash of the uncompressed layer + DiffID string `json:"diffID"` } // ImageStatus defines the observed state of Image diff --git a/api/v1alpha1/registry_types.go b/api/v1alpha1/registry_types.go index d1b1b64..5d33ffd 100644 --- a/api/v1alpha1/registry_types.go +++ b/api/v1alpha1/registry_types.go @@ -20,9 +20,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -// EDIT THIS FILE! THIS IS SCAFFOLDING FOR YOU TO OWN! -// NOTE: json tags are required. Any new fields you add must have json tags for the fields to be serialized. - const ( RegistryLastDiscoveredAtAnnotation = "sbombastic.rancher.io/last-discovered-at" RegistryLastScannedAtAnnotation = "sbombastic.rancher.io/last-scanned-at" @@ -30,12 +27,10 @@ const ( // RegistrySpec defines the desired state of Registry type RegistrySpec struct { - // INSERT ADDITIONAL SPEC FIELDS - desired state of cluster - // Important: Run "make" to regenerate code after modifying this file - // URL is the URL of the container registry URL string `json:"url,omitempty"` // Repositories is the list of the repositories to be scanned + // An empty list means all the repositories found in the registry are going to be scanned Repositories []string `json:"repositories,omitempty"` // AuthSecret is the name of the secret in the same namespace that contains the credentials to access the registry. AuthSecret string `json:"authSecret,omitempty"` diff --git a/api/v1alpha1/zz_generated.deepcopy.go b/api/v1alpha1/zz_generated.deepcopy.go index 74455c8..e9a6c53 100644 --- a/api/v1alpha1/zz_generated.deepcopy.go +++ b/api/v1alpha1/zz_generated.deepcopy.go @@ -30,7 +30,7 @@ func (in *Image) DeepCopyInto(out *Image) { *out = *in out.TypeMeta = in.TypeMeta in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) - out.Spec = in.Spec + in.Spec.DeepCopyInto(&out.Spec) out.Status = in.Status } @@ -52,6 +52,21 @@ func (in *Image) DeepCopyObject() runtime.Object { return nil } +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *ImageLayer) DeepCopyInto(out *ImageLayer) { + *out = *in +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageLayer. +func (in *ImageLayer) DeepCopy() *ImageLayer { + if in == nil { + return nil + } + out := new(ImageLayer) + in.DeepCopyInto(out) + return out +} + // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ImageList) DeepCopyInto(out *ImageList) { *out = *in @@ -87,6 +102,11 @@ func (in *ImageList) DeepCopyObject() runtime.Object { // DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. func (in *ImageSpec) DeepCopyInto(out *ImageSpec) { *out = *in + if in.Layers != nil { + in, out := &in.Layers, &out.Layers + *out = make([]ImageLayer, len(*in)) + copy(*out, *in) + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ImageSpec. diff --git a/go.mod b/go.mod index 4c915d2..2c2ed0a 100644 --- a/go.mod +++ b/go.mod @@ -6,6 +6,7 @@ godebug default=go1.23 require ( github.com/Masterminds/squirrel v1.5.4 + github.com/google/go-containerregistry v0.20.2 github.com/google/uuid v1.6.0 github.com/jmoiron/sqlx v1.4.0 github.com/nats-io/nats-server/v2 v2.10.21 @@ -14,6 +15,7 @@ require ( github.com/onsi/gomega v1.34.2 github.com/spf13/cobra v1.8.1 github.com/stretchr/testify v1.9.0 + go.uber.org/zap v1.27.0 k8s.io/apimachinery v0.31.1 k8s.io/apiserver v0.31.1 k8s.io/client-go v0.31.1 @@ -26,23 +28,23 @@ require ( sigs.k8s.io/structured-merge-diff/v4 v4.4.1 ) -// This is needed to consume the fix for the client-go extension generation -// See https://github.com/kubernetes/code-generator/commit/06ae20b53fb128366a33c1f25f7ff57d68195b43 -replace k8s.io/code-generator => k8s.io/code-generator v0.0.0-20240928043524-06ae20b53fb1 - require ( github.com/NYTimes/gziphandler v1.1.1 // indirect - github.com/antlr4-go/antlr/v4 v4.13.1 // indirect - github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect + github.com/antlr4-go/antlr/v4 v4.13.0 // indirect + github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/cenkalti/backoff/v4 v4.3.0 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.14.3 // indirect github.com/coreos/go-semver v0.3.1 // indirect github.com/coreos/go-systemd/v22 v22.5.0 // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect + github.com/docker/cli v27.1.1+incompatible // indirect + github.com/docker/distribution v2.8.2+incompatible // indirect + github.com/docker/docker-credential-helpers v0.7.0 // indirect github.com/dustin/go-humanize v1.0.1 // indirect - github.com/emicklei/go-restful/v3 v3.12.1 // indirect + github.com/emicklei/go-restful/v3 v3.11.0 // indirect github.com/evanphx/json-patch/v5 v5.9.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect github.com/fsnotify/fsnotify v1.7.0 // indirect @@ -51,31 +53,31 @@ require ( github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/zapr v1.3.0 // indirect github.com/go-openapi/jsonpointer v0.21.0 // indirect - github.com/go-openapi/jsonreference v0.21.0 // indirect + github.com/go-openapi/jsonreference v0.20.2 // indirect github.com/go-openapi/swag v0.23.0 // indirect github.com/go-task/slim-sprig/v3 v3.0.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/cel-go v0.21.0 // indirect + github.com/google/cel-go v0.20.1 // indirect github.com/google/gnostic-models v0.6.8 // indirect github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5 // indirect github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 // indirect github.com/hashicorp/golang-lru/v2 v2.0.7 // indirect - github.com/imdario/mergo v0.3.16 // indirect + github.com/imdario/mergo v0.3.6 // indirect github.com/inconshreveable/mousetrap v1.1.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.10 // indirect - github.com/kylelemons/godebug v1.1.0 // indirect + github.com/klauspost/compress v1.17.9 // indirect github.com/lann/builder v0.0.0-20180802200727-47ae307949d0 // indirect github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 // indirect github.com/mailru/easyjson v0.7.7 // indirect github.com/mattn/go-isatty v0.0.20 // indirect github.com/minio/highwayhash v1.0.3 // indirect + github.com/mitchellh/go-homedir v1.1.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 @@ -83,47 +85,49 @@ require ( github.com/nats-io/nkeys v0.4.7 // indirect github.com/nats-io/nuid v1.0.1 // indirect github.com/ncruces/go-strftime v0.1.9 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.0-rc3 // 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.4 // indirect + github.com/prometheus/client_golang v1.19.1 // indirect github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.60.0 // indirect + github.com/prometheus/common v0.55.0 // indirect github.com/prometheus/procfs v0.15.1 // indirect github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/sirupsen/logrus v1.9.3 // indirect github.com/spf13/pflag v1.0.5 // indirect - github.com/stoewer/go-strcase v1.3.0 // indirect + github.com/stoewer/go-strcase v1.2.0 // indirect github.com/stretchr/objx v0.5.2 // indirect + github.com/vbatts/tar-split v0.11.3 // indirect github.com/x448/float16 v0.8.4 // indirect - go.etcd.io/etcd/api/v3 v3.5.16 // indirect - go.etcd.io/etcd/client/pkg/v3 v3.5.16 // indirect - go.etcd.io/etcd/client/v3 v3.5.16 // indirect - go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 // indirect - go.opentelemetry.io/otel v1.30.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0 // indirect - go.opentelemetry.io/otel/metric v1.30.0 // indirect - go.opentelemetry.io/otel/sdk v1.30.0 // indirect - go.opentelemetry.io/otel/trace v1.30.0 // indirect + go.etcd.io/etcd/api/v3 v3.5.14 // indirect + go.etcd.io/etcd/client/pkg/v3 v3.5.14 // indirect + go.etcd.io/etcd/client/v3 v3.5.14 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 // indirect + go.opentelemetry.io/otel v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 // indirect + go.opentelemetry.io/otel/metric v1.28.0 // indirect + go.opentelemetry.io/otel/sdk v1.28.0 // indirect + go.opentelemetry.io/otel/trace v1.28.0 // indirect go.opentelemetry.io/proto/otlp v1.3.1 // indirect go.uber.org/multierr v1.11.0 // indirect - go.uber.org/zap v1.27.0 // indirect golang.org/x/crypto v0.27.0 // indirect - golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 // indirect - golang.org/x/mod v0.21.0 // indirect - golang.org/x/net v0.29.0 // indirect - golang.org/x/oauth2 v0.23.0 // indirect + golang.org/x/exp v0.0.0-20240719175910-8a7402abbf56 // indirect + golang.org/x/mod v0.20.0 // indirect + golang.org/x/net v0.28.0 // indirect + golang.org/x/oauth2 v0.21.0 // indirect golang.org/x/sync v0.8.0 // indirect golang.org/x/sys v0.25.0 // indirect golang.org/x/term v0.24.0 // indirect golang.org/x/text v0.18.0 // indirect golang.org/x/time v0.6.0 // indirect - golang.org/x/tools v0.25.0 // indirect + golang.org/x/tools v0.24.0 // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f // indirect - google.golang.org/grpc v1.67.1 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 // indirect + google.golang.org/grpc v1.65.0 // indirect google.golang.org/protobuf v1.34.2 // indirect gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect @@ -145,3 +149,7 @@ require ( sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/yaml v1.4.0 // indirect ) + +// This is needed to consume the fix for the client-go extension generation +// See https://github.com/kubernetes/code-generator/commit/06ae20b53fb128366a33c1f25f7ff57d68195b43 +replace k8s.io/code-generator => k8s.io/code-generator v0.0.0-20240928043524-06ae20b53fb1 diff --git a/go.sum b/go.sum index b9af4b9..d4aa493 100644 --- a/go.sum +++ b/go.sum @@ -1,13 +1,14 @@ filippo.io/edwards25519 v1.1.0 h1:FNf4tywRC1HmFuKW5xopWpigGjJKiJSV0Cqo0cJWDaA= filippo.io/edwards25519 v1.1.0/go.mod h1:BxyFTGdWcka3PhytdK4V28tE5sGfRvvvRV7EaN4VDT4= +github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/Masterminds/squirrel v1.5.4 h1:uUcX/aBc8O7Fg9kaISIUsHXdKuqehiXAMQTYX8afzqM= github.com/Masterminds/squirrel v1.5.4/go.mod h1:NNaOrjSoIDfDA40n7sr2tPNZRfjzjA400rg+riTZj10= github.com/NYTimes/gziphandler v1.1.1 h1:ZUDjpQae29j0ryrS0u/B8HZfJBtBQHjqw2rQ2cqUQ3I= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= -github.com/antlr4-go/antlr/v4 v4.13.1 h1:SqQKkuVZ+zWkMMNkjy5FZe5mr5WURWnlpmOuzYWrPrQ= -github.com/antlr4-go/antlr/v4 v4.13.1/go.mod h1:GKmUxMtwp6ZgGwZSva4eWPC5mS6vUAmOABFgjdkM7Nw= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 h1:DklsrG3dyBCFEj5IhUbnKptjxatkF07cF2ak3yi77so= -github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2/go.mod h1:WaHUgvxTVq04UNunO+XhnAqY/wQc+bxr74GqbsZ/Jqw= +github.com/antlr4-go/antlr/v4 v4.13.0 h1:lxCg3LAv+EUK6t1i0y1V6/SLeUi0eKEKdhQAlS8TVTI= +github.com/antlr4-go/antlr/v4 v4.13.0/go.mod h1:pfChB/xh/Unjila75QW7+VU4TSnWnnk9UTnmpPaOR2g= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= @@ -16,19 +17,29 @@ github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK3 github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/stargz-snapshotter/estargz v0.14.3 h1:OqlDCK3ZVUO6C3B/5FSkDwbkEETK84kQgEeFwDC+62k= +github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= github.com/coreos/go-semver v0.3.1/go.mod h1:irMmmIw/7yzSRPWryHsK7EYSg09caPQL03VsM8rvUec= github.com/coreos/go-systemd/v22 v22.5.0 h1:RrqgGjYQKalulkV8NGVIfkXQf6YYmOyiJKk8iXXhfZs= github.com/coreos/go-systemd/v22 v22.5.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.4/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/docker/cli v27.1.1+incompatible h1:goaZxOqs4QKxznZjjBWKONQci/MywhtRv2oNn0GkeZE= +github.com/docker/cli v27.1.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/distribution v2.8.2+incompatible h1:T3de5rq0dB1j30rp0sA2rER+m322EBzniBPB6ZIzuh8= +github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= +github.com/docker/docker-credential-helpers v0.7.0 h1:xtCHsjxogADNZcdv1pKUHXryefjlVRqWqIhk/uXJp0A= +github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= -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/emicklei/go-restful/v3 v3.11.0 h1:rAQeMHw1c7zTmncogyy8VvRZwtkmkZ4FxERmMY4rD+g= +github.com/emicklei/go-restful/v3 v3.11.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch v0.5.2 h1:xVCHIVMUu1wtM/VkR9jVZ45N3FhZfYMMYGorLCR8P3k= github.com/evanphx/json-patch v0.5.2/go.mod h1:ZWS5hhDbVDyob71nXKNL0+PWn6ToqBHMikGIFbs31qQ= github.com/evanphx/json-patch/v5 v5.9.0 h1:kcBlZQbplgElYIlo/n1hJbls2z/1awpXxpRi0/FOJfg= @@ -46,10 +57,12 @@ 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-logr/zapr v1.3.0 h1:XGdV8XW8zdwFiwOA2Dryh1gj2KRQyOOoNmBy4EplIcQ= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= 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/jsonreference v0.21.0 h1:Rs+Y7hSXT83Jacb7kFyjn4ijOuVGSvOdF2+tg1TRrwQ= -github.com/go-openapi/jsonreference v0.21.0/go.mod h1:LmZmgsrTkVg9LG4EaHeY8cBDslNPMo06cago5JNLkm4= +github.com/go-openapi/jsonreference v0.20.2 h1:3sVjiK66+uXK/6oQ8xgcRKcFgQ5KXa2KvnJRumpMGbE= +github.com/go-openapi/jsonreference v0.20.2/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= +github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= 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-sql-driver/mysql v1.8.1 h1:LedoTUt/eveggdHS9qUFC1EFSa8bU2+1pZjSRpvNJ1Y= @@ -67,13 +80,15 @@ github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= -github.com/google/cel-go v0.21.0 h1:cl6uW/gxN+Hy50tNYvI691+sXxioCnstFzLp2WO4GCI= -github.com/google/cel-go v0.21.0/go.mod h1:rHUlWCcBKgyEk+eV03RPdZUekPp6YcJwV0FxuUksYxc= +github.com/google/cel-go v0.20.1 h1:nDx9r8S3L4pE61eDdt8igGj8rf5kjYR3ILxWIpWNi84= +github.com/google/cel-go v0.20.1/go.mod h1:kWcIzTsPX0zmQ+H3TirHstLLf9ep5QTsZBN9u4dOYLg= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= 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-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= +github.com/google/go-containerregistry v0.20.2/go.mod h1:z38EKdKh4h7IP2gSfUUqEvalZBqs6AoLeWfUy34nQC8= 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= @@ -89,12 +104,12 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92Bcuy github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway v1.16.0 h1:gmcG1KaJ57LophUzW0Hy8NmPhnMZb4M0+kPpLofRdBo= github.com/grpc-ecosystem/grpc-gateway v1.16.0/go.mod h1:BDjrQk3hbvj6Nolgz8mAMFbcEtjT1g+wF4CSlocrBnw= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0 h1:asbCHRVmodnJTuQ3qamDwqVOIjwqUPTYmYuemVOx+Ys= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.22.0/go.mod h1:ggCgvZ2r7uOoQjOyu2Y1NhHmEPPzzuhWgcza5M1Ji1I= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0 h1:bkypFPDjIYGfCYD5mRBvpqxfYX1YCS1PXdKYWi8FsN0= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.20.0/go.mod h1:P+Lt/0by1T8bfcF3z737NnSbmxQAppXMRziHUxPOC8k= github.com/hashicorp/golang-lru/v2 v2.0.7 h1:a+bsQ5rvGLjzHuww6tVxozPZFVghXaHOwFs4luLUK2k= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/imdario/mergo v0.3.16 h1:wwQJbIsHYGMUyLSPrEq1CT16AhnhNJQ51+4fdHUnCl4= -github.com/imdario/mergo v0.3.16/go.mod h1:WBLT9ZmE3lPoWsEzCh9LPo3TiwVN+ZKEjmz+hD27ysY= +github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= +github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/jmoiron/sqlx v1.4.0 h1:1PLqN7S1UYp5t4SrVVnt4nUVNemrDAtxlulVe+Qgm3o= @@ -107,14 +122,15 @@ 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.10 h1:oXAz+Vh0PMUvJczoi+flxpnBEPxoER1IaAnU/NMPtT0= -github.com/klauspost/compress v1.17.10/go.mod h1:pMDklpSncoRMuLFrf1W9Ss9KT+0rH90U12bZKk7uwG0= +github.com/klauspost/compress v1.17.9 h1:6KIumPrER1LHsvBVuDa0r5xaG0Es51mhhB9BQB2qeMA= +github.com/klauspost/compress v1.17.9/go.mod h1:Di0epgTjJY877eYKx5yC51cX2A2Vl2ibi7bDH9ttBbw= +github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -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/lann/builder v0.0.0-20180802200727-47ae307949d0 h1:SOEGU9fKiNWd/HOJuq6+3iTQz8KNCLtVX6idSoTLdUw= github.com/lann/builder v0.0.0-20180802200727-47ae307949d0/go.mod h1:dXGbAdH5GtBTC4WfIxhKZfyBF/HBFgRZSWwZ9g/He9o= github.com/lann/ps v0.0.0-20150810152359-62de8c46ede0 h1:P6pPBnrTSX3DEVR4fDembhRWSsG5rVo6hYhAB/ADZrk= @@ -129,6 +145,8 @@ github.com/mattn/go-sqlite3 v1.14.22 h1:2gZY6PC6kBnID23Tichd1K+Z0oS6nE/XwU+Vz/5o github.com/mattn/go-sqlite3 v1.14.22/go.mod h1:Uh1q+B4BYcTPb+yiD3kU8Ct7aC0hY9fxUwlHK0RXw+Y= github.com/minio/highwayhash v1.0.3 h1:kbnuUMoHYyVl7szWjSxJnxw11k2U709jqFPPmIUyD6Q= github.com/minio/highwayhash v1.0.3/go.mod h1:GGYsuwP/fPD6Y9hMiXuapVvlIUEhFhMTh0rxU3ik1LQ= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= github.com/modern-go/concurrent v0.0.0-20180228061459-e0a39a4cb421/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd h1:TRLaZ9cD/w8PVh93nsPXa1VrQ6jlwL5oN8l14QlcNfg= github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd/go.mod h1:6dJC0mAP4ikYIbvyc7fijjWJddQyLn8Ig3JB5CqoB9Q= @@ -152,17 +170,21 @@ github.com/onsi/ginkgo/v2 v2.20.2 h1:7NVCeyIWROIAheY21RLS+3j2bb52W0W82tkberYytp4 github.com/onsi/ginkgo/v2 v2.20.2/go.mod h1:K9gyxPIlb+aIvnZ8bd9Ak+YP18w3APlR+5coaZoE2ag= github.com/onsi/gomega v1.34.2 h1:pNCwDkzrsv7MS9kpaQvVb1aVLahQXyJ/Tv5oAZMI3i8= github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.0-rc3 h1:fzg1mXZFj8YdPeNkRXMg+zb88BFV0Ys52cJydRwBkb8= +github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= 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.4 h1:Tgh3Yr67PaOv/uTqloMsCEdeuFTatm5zIq5+qNN23vI= -github.com/prometheus/client_golang v1.20.4/go.mod h1:PIEt8X02hGcP8JWbeHyeZ53Y/jReSnHgO035n//V5WE= +github.com/prometheus/client_golang v1.19.1 h1:wZWJDwK+NameRJuPGDhlnFgx8e8HN3XHQeLaYJFJBOE= +github.com/prometheus/client_golang v1.19.1/go.mod h1:mP78NwGzrVks5S2H6ab8+ZZGJLZUq1hoULYBAYBw1Ho= 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.60.0 h1:+V9PAREWNvJMAuJ1x1BaWl9dewMW4YrHZQbx0sJNllA= -github.com/prometheus/common v0.60.0/go.mod h1:h0LYf1R1deLSKtD4Vdg8gy4RuOvENW2J/h19V5NADQw= +github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= +github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= 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/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= @@ -170,6 +192,7 @@ github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qq github.com/rogpeppe/go-internal v1.12.0 h1:exVL4IDcn6na9z1rAb56Vxr+CgyK3nn3O+epU5NdKM8= github.com/rogpeppe/go-internal v1.12.0/go.mod h1:E+RYuTGaKKdloAfM02xzb0FW3Paa99yedzYV+kq4uf4= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/soheilhy/cmux v0.1.5 h1:jjzc5WVemNEDTLwv9tlmemhC73tI08BNOIGwBOo10Js= @@ -178,8 +201,8 @@ github.com/spf13/cobra v1.8.1 h1:e5/vxKd/rZsfSJMUX1agtjeTDf+qv1/JdBF8gg5k9ZM= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= 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/stoewer/go-strcase v1.3.0 h1:g0eASXYtp+yvN9fK8sH94oCIk0fau9uV1/ZdJ0AVEzs= -github.com/stoewer/go-strcase v1.3.0/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= +github.com/stoewer/go-strcase v1.2.0 h1:Z2iHWqGXH00XYgqDmNgQbIBxf3wrNq0F3feEy0ainaU= +github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= @@ -187,6 +210,8 @@ 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.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= @@ -194,6 +219,9 @@ github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsT github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75 h1:6fotK7otjonDflCTK0BCfls4SPy3NcCVb5dqqmbRknE= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= +github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= +github.com/vbatts/tar-split v0.11.3 h1:hLFqsOLQ1SsppQNTMpkpPXClLDfC2A3Zgy9OUU+RVck= +github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= github.com/x448/float16 v0.8.4 h1:qLwI1I70+NjRFUR3zs1JPUCgaCXSh3SW62uAKT1mSBM= github.com/x448/float16 v0.8.4/go.mod h1:14CWIYCyZA/cWjXOioeEpHeN/83MdbZDRQHoFcYsOfg= github.com/xiang90/probing v0.0.0-20190116061207-43a291ad63a2 h1:eY9dn8+vbi4tKz5Qo6v2eYzo7kUS51QINcR5jNpbZS8= @@ -202,36 +230,36 @@ github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9de github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= go.etcd.io/bbolt v1.3.9 h1:8x7aARPEXiXbHmtUwAIv7eV2fQFHrLLavdiJ3uzJXoI= go.etcd.io/bbolt v1.3.9/go.mod h1:zaO32+Ti0PK1ivdPtgMESzuzL2VPoIG1PCQNvOdo/dE= -go.etcd.io/etcd/api/v3 v3.5.16 h1:WvmyJVbjWqK4R1E+B12RRHz3bRGy9XVfh++MgbN+6n0= -go.etcd.io/etcd/api/v3 v3.5.16/go.mod h1:1P4SlIP/VwkDmGo3OlOD7faPeP8KDIFhqvciH5EfN28= -go.etcd.io/etcd/client/pkg/v3 v3.5.16 h1:ZgY48uH6UvB+/7R9Yf4x574uCO3jIx0TRDyetSfId3Q= -go.etcd.io/etcd/client/pkg/v3 v3.5.16/go.mod h1:V8acl8pcEK0Y2g19YlOV9m9ssUe6MgiDSobSoaBAM0E= +go.etcd.io/etcd/api/v3 v3.5.14 h1:vHObSCxyB9zlF60w7qzAdTcGaglbJOpSj1Xj9+WGxq0= +go.etcd.io/etcd/api/v3 v3.5.14/go.mod h1:BmtWcRlQvwa1h3G2jvKYwIQy4PkHlDej5t7uLMUdJUU= +go.etcd.io/etcd/client/pkg/v3 v3.5.14 h1:SaNH6Y+rVEdxfpA2Jr5wkEvN6Zykme5+YnbCkxvuWxQ= +go.etcd.io/etcd/client/pkg/v3 v3.5.14/go.mod h1:8uMgAokyG1czCtIdsq+AGyYQMvpIKnSvPjFMunkgeZI= go.etcd.io/etcd/client/v2 v2.305.13 h1:RWfV1SX5jTU0lbCvpVQe3iPQeAHETWdOTb6pxhd77C8= go.etcd.io/etcd/client/v2 v2.305.13/go.mod h1:iQnL7fepbiomdXMb3om1rHq96htNNGv2sJkEcZGDRRg= -go.etcd.io/etcd/client/v3 v3.5.16 h1:sSmVYOAHeC9doqi0gv7v86oY/BTld0SEFGaxsU9eRhE= -go.etcd.io/etcd/client/v3 v3.5.16/go.mod h1:X+rExSGkyqxvu276cr2OwPLBaeqFu1cIl4vmRjAD/50= +go.etcd.io/etcd/client/v3 v3.5.14 h1:CWfRs4FDaDoSz81giL7zPpZH2Z35tbOrAJkkjMqOupg= +go.etcd.io/etcd/client/v3 v3.5.14/go.mod h1:k3XfdV/VIHy/97rqWjoUzrj9tk7GgJGH9J8L4dNXmAk= go.etcd.io/etcd/pkg/v3 v3.5.13 h1:st9bDWNsKkBNpP4PR1MvM/9NqUPfvYZx/YXegsYEH8M= go.etcd.io/etcd/pkg/v3 v3.5.13/go.mod h1:N+4PLrp7agI/Viy+dUYpX7iRtSPvKq+w8Y14d1vX+m0= go.etcd.io/etcd/raft/v3 v3.5.13 h1:7r/NKAOups1YnKcfro2RvGGo2PTuizF/xh26Z2CTAzA= go.etcd.io/etcd/raft/v3 v3.5.13/go.mod h1:uUFibGLn2Ksm2URMxN1fICGhk8Wu96EfDQyuLhAcAmw= go.etcd.io/etcd/server/v3 v3.5.13 h1:V6KG+yMfMSqWt+lGnhFpP5z5dRUj1BDRJ5k1fQ9DFok= go.etcd.io/etcd/server/v3 v3.5.13/go.mod h1:K/8nbsGupHqmr5MkgaZpLlH1QdX1pcNQLAkODy44XcQ= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0 h1:hCq2hNMwsegUvPzI7sPOvtO9cqyy5GbWt/Ybp2xrx8Q= -go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.55.0/go.mod h1:LqaApwGx/oUmzsbqxkzuBvyoPpkxk3JQWnqfVrJ3wCA= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0 h1:ZIg3ZT/aQ7AfKqdwp7ECpOK6vHqquXXuyTjIO8ZdmPs= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.55.0/go.mod h1:DQAwmETtZV00skUwgD6+0U89g80NKsJE3DCKeLLPQMI= -go.opentelemetry.io/otel v1.30.0 h1:F2t8sK4qf1fAmY9ua4ohFS/K+FUuOPemHUIXHtktrts= -go.opentelemetry.io/otel v1.30.0/go.mod h1:tFw4Br9b7fOS+uEao81PJjVMjW/5fvNCbpsDIXqP0pc= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0 h1:lsInsfvhVIfOI6qHVyysXMNDnjO9Npvl7tlDPJFBVd4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.30.0/go.mod h1:KQsVNh4OjgjTG0G6EiNi1jVpnaeeKsKMRwbLN+f1+8M= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0 h1:m0yTiGDLUvVYaTFbAvCkVYIYcvwKt3G7OLoN77NUs/8= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.30.0/go.mod h1:wBQbT4UekBfegL2nx0Xk1vBcnzyBPsIVm9hRG4fYcr4= -go.opentelemetry.io/otel/metric v1.30.0 h1:4xNulvn9gjzo4hjg+wzIKG7iNFEaBMX00Qd4QIZs7+w= -go.opentelemetry.io/otel/metric v1.30.0/go.mod h1:aXTfST94tswhWEb+5QjlSqG+cZlmyXy/u8jFpor3WqQ= -go.opentelemetry.io/otel/sdk v1.30.0 h1:cHdik6irO49R5IysVhdn8oaiR9m8XluDaJAs4DfOrYE= -go.opentelemetry.io/otel/sdk v1.30.0/go.mod h1:p14X4Ok8S+sygzblytT1nqG98QG2KYKv++HE0LY/mhg= -go.opentelemetry.io/otel/trace v1.30.0 h1:7UBkkYzeg3C7kQX8VAidWh2biiQbtAKjyIML8dQ9wmc= -go.opentelemetry.io/otel/trace v1.30.0/go.mod h1:5EyKqTzzmyqB9bwtCCq6pDLktPK6fmGf/Dph+8VI02o= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0 h1:9G6E0TXzGFVfTnawRzrPl83iHOAV7L8NJiR8RSGYV1g= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.53.0/go.mod h1:azvtTADFQJA8mX80jIH/akaE7h+dbm/sVuaHqN13w74= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0 h1:4K4tsIXefpVJtvA/8srF4V4y0akAoPHkIslgAkjixJA= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.53.0/go.mod h1:jjdQuTGVsXV4vSs+CJ2qYDeDPf9yIJV23qlIzBm73Vg= +go.opentelemetry.io/otel v1.28.0 h1:/SqNcYk+idO0CxKEUOtKQClMK/MimZihKYMruSMViUo= +go.opentelemetry.io/otel v1.28.0/go.mod h1:q68ijF8Fc8CnMHKyzqL6akLO46ePnjkgfIMIjUIX9z4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0 h1:3Q/xZUyC1BBkualc9ROb4G8qkH90LXEIICcs5zv1OYY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.28.0/go.mod h1:s75jGIWA9OfCMzF0xr+ZgfrB5FEbbV7UuYo32ahUiFI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0 h1:qFffATk0X+HD+f1Z8lswGiOQYKHRlzfmdJm0wEaVrFA= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.27.0/go.mod h1:MOiCmryaYtc+V0Ei+Tx9o5S1ZjA7kzLucuVuyzBZloQ= +go.opentelemetry.io/otel/metric v1.28.0 h1:f0HGvSl1KRAU1DLgLGFjrwVyismPlnuU6JD6bOeuA5Q= +go.opentelemetry.io/otel/metric v1.28.0/go.mod h1:Fb1eVBFZmLVTMb6PPohq3TO9IIhUisDsbJoL/+uQW4s= +go.opentelemetry.io/otel/sdk v1.28.0 h1:b9d7hIry8yZsgtbmM0DKyPWMMUMlK9NEKuIG4aBqWyE= +go.opentelemetry.io/otel/sdk v1.28.0/go.mod h1:oYj7ClPUA7Iw3m+r7GeEjz0qckQRJK2B8zjcZEfu7Pg= +go.opentelemetry.io/otel/trace v1.28.0 h1:GhQ9cUuQGmNDd5BTCP2dAvv75RdMxEfTmYejp+lkx9g= +go.opentelemetry.io/otel/trace v1.28.0/go.mod h1:jPyXzNPg6da9+38HEwElrQiHlVMTnVfM3/yv2OlIHaI= go.opentelemetry.io/proto/otlp v1.3.1 h1:TrMUixzpM0yuc/znrFTP9MMRh8trP93mkCiDVeXrui0= go.opentelemetry.io/proto/otlp v1.3.1/go.mod h1:0X1WI4de4ZsLrrJNLAQbFeLCm3T7yBkR0XqQ7niQU+8= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= @@ -245,20 +273,20 @@ golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8U golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.27.0 h1:GXm2NjJrPaiv/h1tb2UH8QfgC/hOf/+z0p6PT8o1w7A= golang.org/x/crypto v0.27.0/go.mod h1:1Xngt8kV6Dvbssa53Ziq6Eqn0HqbZi5Z6R0ZpwQzt70= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0 h1:e66Fs6Z+fZTbFBAxKfP3PALWBtpfqks2bwGcexMxgtk= -golang.org/x/exp v0.0.0-20240909161429-701f63a606c0/go.mod h1:2TbTHSBQa924w8M6Xs1QcRcFwyucIwBGpK1p2f1YFFY= +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.21.0 h1:vvrHzRwRfVKSiLrG+d4FMl/Qi4ukBCE6kZlTUkDYRT0= -golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= +golang.org/x/mod v0.20.0 h1:utOm6MM3R3dnawAiJgn0y+xvuYRsm1RKM/4giyfDgV0= +golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= 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.29.0 h1:5ORfpBpCs4HzDYoodCDBbwHzdR5UrLBZ3sOnUJmFoHo= -golang.org/x/net v0.29.0/go.mod h1:gLkgy8jTGERgjzMic6DS9+SP0ajcu6Xu3Orq/SpETg0= -golang.org/x/oauth2 v0.23.0 h1:PbgcYx2W7i4LvjJWEbf0ngHV6qJYr86PkAV3bXdLEbs= -golang.org/x/oauth2 v0.23.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= +golang.org/x/net v0.28.0 h1:a9JDOJc5GMUJ0+UDqmLT86WiEy7iWyIhz8gz8E4e5hE= +golang.org/x/net v0.28.0/go.mod h1:yqtgsTWOOnlGLG9GFRrK3++bGOUEkNBoHZc8MEDWPNg= +golang.org/x/oauth2 v0.21.0 h1:tsimM75w1tF/uws5rbeHzIWxEqElMehnc+iW793zsZs= +golang.org/x/oauth2 v0.21.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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= @@ -267,6 +295,8 @@ golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= 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-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.25.0 h1:r+8e+loiHxRqhXVl6ML1nO3l1+oFoWbnlu2Ehimmi34= @@ -283,22 +313,22 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm 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.25.0 h1:oFU9pkj/iJgs+0DT+VMHrx+oBKs/LJMV+Uvg78sl+fE= -golang.org/x/tools v0.25.0/go.mod h1:/vtpO8WL1N9cQC3FN5zPqb//fRXskFHbLKk4OW1Q7rg= +golang.org/x/tools v0.24.0 h1:J1shsA93PJUEVaUSaay7UXAyE8aimq3GW0pjlolpa24= +golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= 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= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17 h1:wpZ8pe2x1Q3f2KyT5f8oP/fa9rHAKgFPr/HZdNuS+PQ= -google.golang.org/genproto v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:J7XzRzVy1+IPwWHZUzoD0IccYZIrXILAQpc+Qy9CMhY= -google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f h1:jTm13A2itBi3La6yTGqn8bVSrc3ZZ1r8ENHlIXBfnRA= -google.golang.org/genproto/googleapis/api v0.0.0-20240930140551-af27646dc61f/go.mod h1:CLGoBuH1VHxAUXVPP8FfPwPEVJB6lz3URE5mY2SuayE= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f h1:cUMEy+8oS78BWIH9OWazBkzbr090Od9tWBNtZHkOhf0= -google.golang.org/genproto/googleapis/rpc v0.0.0-20240930140551-af27646dc61f/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= -google.golang.org/grpc v1.67.1 h1:zWnc1Vrcno+lHZCOofnIMvycFcc0QRGIzm9dhnDX68E= -google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d h1:VBu5YqKPv6XiJ199exd8Br+Aetz+o08F+PLMnwJQHAY= +google.golang.org/genproto v0.0.0-20230822172742-b8732ec3820d/go.mod h1:yZTlhN0tQnXo3h00fuXNCxJdLdIdnVFVBaRJ5LWBbw4= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094 h1:BwIjyKYGsK9dMCBOorzRri8MQwmi7mT9rGHsCEinZkA= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240701130421-f6361c86f094/go.mod h1:Ue6ibwXGpU+dqIcODieyLOcgj7z8+IcskoNIgZxtrFY= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= google.golang.org/protobuf v1.34.2 h1:6xV6lTsCfpGD21XK49h7MhtcApnLqkfYgPcdHftf6hg= google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWniOlNbLDw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= @@ -310,12 +340,15 @@ gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY= gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.0.3 h1:4AuOwCGf4lLR9u3YOe2awrHygurzhO/HeQ6laiA6Sx0= +gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= k8s.io/api v0.31.1 h1:Xe1hX/fPW3PXYYv8BlozYqw63ytA92snr96zMW9gWTU= k8s.io/api v0.31.1/go.mod h1:sbN1g6eY6XVLeqNsZGLnI5FwVseTrZX7Fv3O26rhAaI= k8s.io/apiextensions-apiserver v0.31.0 h1:fZgCVhGwsclj3qCw1buVXCV6khjRzKC5eCFt24kyLSk= diff --git a/helm/templates/crd/sbombastic.rancher.io_images.yaml b/helm/templates/crd/sbombastic.rancher.io_images.yaml index d27d96c..75da0b2 100644 --- a/helm/templates/crd/sbombastic.rancher.io_images.yaml +++ b/helm/templates/crd/sbombastic.rancher.io_images.yaml @@ -39,10 +39,28 @@ spec: spec: description: ImageSpec defines the desired state of Image properties: - foo: - description: Foo is an example field of Image. Edit image_types.go - to remove/update - type: string + layers: + description: list of the layers that make the image + items: + description: ImageLayer define a layer part of an OCI Image + properties: + command: + description: |- + command is the command that led to the creation + of the layer + type: string + diffID: + description: diffID is the Hash of the uncompressed layer + type: string + digest: + description: digest is the Hash of the compressed layer + type: string + required: + - command + - diffID + - digest + type: object + type: array type: object status: description: ImageStatus defines the observed state of Image diff --git a/helm/templates/crd/sbombastic.rancher.io_registries.yaml b/helm/templates/crd/sbombastic.rancher.io_registries.yaml index 271f8ba..7eff051 100644 --- a/helm/templates/crd/sbombastic.rancher.io_registries.yaml +++ b/helm/templates/crd/sbombastic.rancher.io_registries.yaml @@ -52,7 +52,9 @@ spec: when set to true. type: boolean repositories: - description: Repositories is the list of the repositories to be scanned + description: |- + Repositories is the list of the repositories to be scanned + An empty list means all the repositories found in the registry are going to be scanned items: type: string type: array diff --git a/internal/handlers/cataloger.go b/internal/handlers/cataloger.go new file mode 100644 index 0000000..c32ebaa --- /dev/null +++ b/internal/handlers/cataloger.go @@ -0,0 +1,277 @@ +// cataloger package contains the cataloger interface and its implementation. +package handlers + +import ( + "context" + "crypto/sha256" + "crypto/tls" + "crypto/x509" + "encoding/base64" + "encoding/hex" + "fmt" + "net/http" + "path" + + "github.com/google/go-containerregistry/pkg/name" + cranev1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/remote" + sbombasticv1alpha1 "github.com/rancher/sbombastic/api/v1alpha1" + registryclient "github.com/rancher/sbombastic/internal/handlers/registry" + "go.uber.org/zap" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + imageDigestLabel = "sbombastic.rancher.io/digest" + imagePlatformLabel = "sbombastic.rancher.io/platform" + imageRegistryLabel = "sbombastic.rancher.io/registry" + imageRepositoryLabel = "sbombastic.rancher.io/repository" + imageTagLabel = "sbombastic.rancher.io/tag" +) + +type Cataloger struct { + registry name.Registry + repositories []string + client registryclient.Client +} + +func buildTransport(insecure bool, caBundle []byte) http.RoundTripper { + transport := remote.DefaultTransport.(*http.Transport).Clone() + transport.TLSClientConfig = &tls.Config{ + InsecureSkipVerify: insecure, //nolint:gosec // this a user provided option + } + + if len(caBundle) > 0 { + rootCAs, err := x509.SystemCertPool() + if err != nil { + zap.L().Error("cannot load system cert pool, using empty pool", zap.Error(err)) + rootCAs = x509.NewCertPool() + } + + ok := rootCAs.AppendCertsFromPEM(caBundle) + if ok { + transport.TLSClientConfig.RootCAs = rootCAs + } else { + zap.L().Info("cannot load the given CA bundle") + } + } + + return transport +} + +func NewCataloger(registry sbombasticv1alpha1.Registry) (*Cataloger, error) { + reg, err := name.NewRegistry(registry.Spec.URL) + if err != nil { + return nil, fmt.Errorf("cannot parse registry %+v: %w", registry, err) + } + + client := registryclient.NewCraneRegistryClient(buildTransport(registry.Spec.Insecure, []byte(registry.Spec.CABundle))) + + return &Cataloger{ + registry: reg, + repositories: registry.Spec.Repositories, + client: client, + }, nil +} + +func (c *Cataloger) Catalogue(ctx context.Context) ([]sbombasticv1alpha1.Image, error) { + zap.L().Debug("Discovering images", zap.String("registry", c.registry.Name())) + repos, err := c.repositoriesToCatalogue(ctx) + if err != nil { + return []sbombasticv1alpha1.Image{}, err + } + + var imageNames []string + for _, repo := range repos { + repoImages, err := c.discoverImages(ctx, repo) + if err != nil { + zap.L().Error( + "cannot discover images", + zap.String("repository", repo), + zap.Error(err), + ) + continue + } + imageNames = append(imageNames, repoImages...) + } + + var images []sbombasticv1alpha1.Image + for _, imageName := range imageNames { + ref, err := name.ParseReference(imageName) + if err != nil { + zap.L().Error( + "cannot parse image name", + zap.String("image", imageName), + zap.Error(err), + ) + continue + } + + refImages, err := c.refToImages(ref) + if err != nil { + zap.L().Error( + "cannot convert reference to Image", + zap.String("image", ref.Name()), + zap.Error(err), + ) + continue + } + images = append(images, refImages...) + } + + return images, nil +} + +func (c *Cataloger) repositoriesToCatalogue(ctx context.Context) ([]string, error) { + if len(c.repositories) == 0 { + allRepositories, err := c.client.Catalogue(ctx, c.registry) + if err != nil { + return []string{}, fmt.Errorf("cannot discover repositories: %w", err) + } + + return allRepositories, nil + } + + repos := []string{} + for _, repo := range c.repositories { + repos = append(repos, path.Join(c.registry.Name(), repo)) + } + + return repos, nil +} + +// discoverImages discovers all the images defined inside of a repository. +// Returns the list of fully qualified image names (e.g. registryclientexample.com/repo:tag) +func (c *Cataloger) discoverImages(ctx context.Context, repository string) ([]string, error) { + repo, err := name.NewRepository(repository) + if err != nil { + return []string{}, fmt.Errorf("cannot parse repository name %q: %w", repository, err) + } + //nolint: wrapcheck // no need to wrap the error, this is already done inside of the client + return c.client.ListRepositoryContents(ctx, repo) +} + +func (c *Cataloger) refToImages(ref name.Reference) ([]sbombasticv1alpha1.Image, error) { + platforms, err := c.refToPlatforms(ref) + if err != nil { + return []sbombasticv1alpha1.Image{}, fmt.Errorf("cannot get platforms for %s: %w", ref, err) + } + if platforms == nil { + // add a `nil` platform to the list of platforms, this will be used to get the default platform + platforms = append(platforms, nil) + } + + images := []sbombasticv1alpha1.Image{} + + for _, platform := range platforms { + imageDetails, err := c.client.GetImageDetails(ref, platform) + if err != nil { + platformStr := "default" + if platform != nil { + platformStr = platform.String() + } + + zap.L().Error( + "cannot get image details", + zap.String("image", ref.Name()), + zap.String("platform", platformStr), + zap.Error(err)) + continue + } + + image, err := imageDetailsToImage(ref, imageDetails) + if err != nil { + zap.L().Error("cannot convert image details to image", zap.Error(err)) + continue + } + + images = append(images, image) + } + + return images, nil +} + +// refToPlatforms returns the list of platforms for the given image reference. +// If the image is not multi-architecture, it returns an empty list. +func (c *Cataloger) refToPlatforms(ref name.Reference) ([]*cranev1.Platform, error) { + imgIndex, err := c.client.GetImageIndex(ref) + if err != nil { + zap.L().Debug( + "image doesn't seem to be multi-architecture", + zap.String("image", ref.Name()), + zap.Error(err)) + return []*cranev1.Platform(nil), nil + } + + manifest, err := imgIndex.IndexManifest() + if err != nil { + return []*cranev1.Platform(nil), fmt.Errorf("cannot read index manifest of %s: %w", ref, err) + } + + platforms := make([]*cranev1.Platform, len(manifest.Manifests)) + for i, manifest := range manifest.Manifests { + platforms[i] = manifest.Platform + } + + return platforms, nil +} + +func imageDetailsToImage(ref name.Reference, details registryclient.ImageDetails) (sbombasticv1alpha1.Image, error) { + imageLayers := []sbombasticv1alpha1.ImageLayer{} + + // There can be more history entries than layers, as some history entries are empty layers + // For example, a command like "ENV VAR=1" will create a new history entry but no new layer + + layerCounter := 0 + for _, history := range details.History { + if history.EmptyLayer { + continue + } + + if len(details.Layers) < layerCounter { + return sbombasticv1alpha1.Image{}, fmt.Errorf("layer %d not found - got only %d layers", layerCounter, len(details.Layers)) + } + layer := details.Layers[layerCounter] + digest, err := layer.Digest() + if err != nil { + return sbombasticv1alpha1.Image{}, fmt.Errorf("cannot read layer digest: %w", err) + } + diffID, err := layer.DiffID() + if err != nil { + return sbombasticv1alpha1.Image{}, fmt.Errorf("cannot read layer diffID: %w", err) + } + + imageLayers = append(imageLayers, sbombasticv1alpha1.ImageLayer{ + Command: base64.StdEncoding.EncodeToString([]byte(history.CreatedBy)), + Digest: digest.String(), + DiffID: diffID.String(), + }) + + layerCounter++ + } + + image := sbombasticv1alpha1.Image{ + ObjectMeta: metav1.ObjectMeta{ + Name: buildImageUID(ref, details.Digest.String()), + Labels: map[string]string{ + imageRegistryLabel: ref.Context().RegistryStr(), + imageRepositoryLabel: ref.Context().RepositoryStr(), + imageTagLabel: ref.Identifier(), + imagePlatformLabel: details.Platform.String(), + imageDigestLabel: details.Digest.String(), + }, + }, + Spec: sbombasticv1alpha1.ImageSpec{ + Layers: imageLayers, + }, + } + + return image, nil +} + +// return the sha256 of “@sha256:` +func buildImageUID(ref name.Reference, digest string) string { + sha := sha256.New() + sha.Write([]byte(fmt.Sprintf("%s:%s@%s", ref.Context().Name(), ref.Identifier(), digest))) + return hex.EncodeToString(sha.Sum(nil)) +} diff --git a/internal/handlers/cataloger_test.go b/internal/handlers/cataloger_test.go new file mode 100644 index 0000000..7342bc4 --- /dev/null +++ b/internal/handlers/cataloger_test.go @@ -0,0 +1,255 @@ +package handlers + +import ( + "context" + "encoding/base64" + "fmt" + "path" + "strconv" + "strings" + "testing" + "time" + + "github.com/google/go-containerregistry/pkg/name" + cranev1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/types" + "github.com/rancher/sbombastic/internal/handlers/registry" + "github.com/rancher/sbombastic/internal/handlers/registry/mocks" + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/mock" + "github.com/stretchr/testify/require" +) + +func TestRepositoriesToCatalogueWhenRepositoriesAreNotSpecified(t *testing.T) { + registryFQDN := "registry.test" + + registry, err := name.NewRegistry(registryFQDN) + require.NoError(t, err) + + registryRepos := []string{"repo1", "repo2"} + expectedRepositories := []string{"registry.test/repo1", "registry.test/repo2"} + mockClient := mocks.NewClient(t) + + mockClient.On( + "Catalogue", + context.Background(), + registry, + ).Return( + func(_ context.Context, _ name.Registry) ([]string, error) { + values := []string{} + for _, repo := range registryRepos { + values = append(values, path.Join(registryFQDN, repo)) + } + return values, nil + }) + + c := Cataloger{ + client: mockClient, + registry: registry, + repositories: []string{}, + } + + actual, err := c.repositoriesToCatalogue(context.Background()) + require.NoError(t, err) + assert.ElementsMatch(t, actual, expectedRepositories) +} + +func TestRepositoriesToCatalogueWhenRepositoriesAreSpecified(t *testing.T) { + registryFQDN := "registry.test" + + registry, err := name.NewRegistry(registryFQDN) + require.NoError(t, err) + + repositories := []string{"repo3"} + expectedRepositories := []string{"registry.test/repo3"} + + mockClient := mocks.NewClient(t) + + c := Cataloger{ + client: mockClient, + registry: registry, + repositories: repositories, + } + + actual, err := c.repositoriesToCatalogue(context.Background()) + require.NoError(t, err) + assert.ElementsMatch(t, actual, expectedRepositories) + mockClient.AssertNotCalled(t, "Catalogue", mock.Anything, mock.Anything) +} + +func fakeDigestAndDiffID(t *testing.T, layerIndex int) (cranev1.Hash, cranev1.Hash) { + random := strings.Repeat(strconv.Itoa(layerIndex), 63) + digestStr := fmt.Sprintf("sha256:a%s", random) + diffIDStr := fmt.Sprintf("sha256:b%s", random) + + digest, err := cranev1.NewHash(digestStr) + require.NoError(t, err) + + diffID, err := cranev1.NewHash(diffIDStr) + require.NoError(t, err) + + return digest, diffID +} + +func buildImageDetails(t *testing.T, digest cranev1.Hash, platform cranev1.Platform) registry.ImageDetails { + numberOfLayers := 8 + + layers := make([]cranev1.Layer, 0, numberOfLayers) + history := make([]cranev1.History, 0, numberOfLayers*2) + + for i := range numberOfLayers { + layerDigest, layerDiffID := fakeDigestAndDiffID(t, i) + + layer := mocks.NewLayer(t) + + layer.On("Digest").Return(layerDigest, nil) + layer.On("DiffID").Return(layerDiffID, nil) + + layers = append(layers, layer) + + history = append(history, cranev1.History{ + Author: fmt.Sprintf("author-layer-%d", i), + Created: cranev1.Time{Time: time.Now()}, + CreatedBy: fmt.Sprintf("command-%d", i), + Comment: fmt.Sprintf("comment-layer-%d", i), + EmptyLayer: false, + }) + + history = append(history, cranev1.History{ + Author: fmt.Sprintf("author-empty-layer-%d", i), + Created: cranev1.Time{Time: time.Now()}, + CreatedBy: fmt.Sprintf("command-empty-layer-%d", i), + Comment: fmt.Sprintf("comment-empty-layer-%d", i), + EmptyLayer: true, + }) + } + + return registry.ImageDetails{ + Digest: digest, + Layers: layers, + History: history, + Platform: platform, + } +} + +func TestImageDetailsToImage(t *testing.T) { + digest, err := cranev1.NewHash("sha256:f41b7d70c5779beba4a570ca861f788d480156321de2876ce479e072fb0246f1") + require.NoError(t, err) + + platform, err := cranev1.ParsePlatform("linux/amd64") + require.NoError(t, err) + + details := buildImageDetails(t, digest, *platform) + numberOfLayers := len(details.Layers) + + registry := "registry.test" + repo := "repo1" + tag := "latest" + ref, err := name.ParseReference(fmt.Sprintf("%s/%s:%s", registry, repo, tag)) + require.NoError(t, err) + + image, err := imageDetailsToImage(ref, details) + require.NoError(t, err) + + assert.Equal(t, image.Name, buildImageUID(ref, digest.String())) + assert.Equal(t, image.Labels[imageRegistryLabel], registry) + assert.Equal(t, image.Labels[imageRepositoryLabel], repo) + assert.Equal(t, image.Labels[imageTagLabel], tag) + assert.Equal(t, image.Labels[imagePlatformLabel], platform.String()) + assert.Equal(t, image.Labels[imageDigestLabel], digest.String()) + + assert.Len(t, image.Spec.Layers, numberOfLayers) + for i := range numberOfLayers { + expectedDigest, expectedDiffID := fakeDigestAndDiffID(t, i) + + layer := image.Spec.Layers[i] + assert.Equal(t, expectedDigest.String(), layer.Digest) + assert.Equal(t, expectedDiffID.String(), layer.DiffID) + + command, err := base64.StdEncoding.DecodeString(layer.Command) + require.NoError(t, err) + assert.Equal(t, fmt.Sprintf("command-%d", i), string(command)) + } +} + +func TestCatalogue(t *testing.T) { + registryClient := mocks.NewClient(t) + + registryStr := "registry.test" + registry, err := name.NewRegistry(registryStr) + require.NoError(t, err) + + repositoryStr := "repo1" + repo, err := name.NewRepository(path.Join(registryStr, repositoryStr)) + require.NoError(t, err) + + tagStr := "tag1" + + image, err := name.ParseReference(fmt.Sprintf("%s/%s:%s", registryStr, repositoryStr, tagStr)) + require.NoError(t, err) + + registryClient.On("ListRepositoryContents", context.Background(), repo). + Return([]string{fmt.Sprintf("%s/%s:%s", registryStr, repositoryStr, tagStr)}, nil) + + imageIndex := mocks.NewImageIndex(t) + + platformLinuxAmd64 := cranev1.Platform{ + Architecture: "amd64", + OS: "linux", + } + platformLinuxArm64 := cranev1.Platform{ + Architecture: "arm64", + OS: "linux", + } + + digestLinuxAmd64, err := cranev1.NewHash("sha256:8ec69d882e7f29f0652d537557160e638168550f738d0d49f90a7ef96bf31787") + require.NoError(t, err) + digestLinuxArm64, err := cranev1.NewHash("sha256:ca9d8b5d1cc2f2186983fc6b9507da6ada5eb92f2b518c06af1128d5396c6f34") + require.NoError(t, err) + + indexManifest := cranev1.IndexManifest{ + SchemaVersion: 2, + MediaType: types.OCIManifestSchema1, + Manifests: []cranev1.Descriptor{ + { + MediaType: types.OCIManifestSchema1, + Size: 100, + Digest: digestLinuxAmd64, + Data: []byte(""), + URLs: []string{}, + Annotations: map[string]string{}, + Platform: &platformLinuxAmd64, + ArtifactType: "", + }, + { + MediaType: types.OCIManifestSchema1, + Size: 100, + Digest: digestLinuxArm64, + Data: []byte(""), + URLs: []string{}, + Annotations: map[string]string{}, + Platform: &platformLinuxArm64, + ArtifactType: "", + }, + }, + } + imageIndex.On("IndexManifest").Return(&indexManifest, nil) + + registryClient.On("GetImageIndex", image).Return(imageIndex, nil) + + imageDetailsLinuxAmd64 := buildImageDetails(t, digestLinuxAmd64, platformLinuxAmd64) + imageDetailsLinuxArm64 := buildImageDetails(t, digestLinuxArm64, platformLinuxArm64) + + registryClient.On("GetImageDetails", image, &platformLinuxAmd64).Return(imageDetailsLinuxAmd64, nil) + registryClient.On("GetImageDetails", image, &platformLinuxArm64).Return(imageDetailsLinuxArm64, nil) + + cataloger := Cataloger{ + registry: registry, + client: registryClient, + repositories: []string{repositoryStr}, + } + + images, err := cataloger.Catalogue(context.Background()) + require.NoError(t, err) + require.Len(t, images, 2) +} diff --git a/internal/handlers/registry/client.go b/internal/handlers/registry/client.go new file mode 100644 index 0000000..aa8253c --- /dev/null +++ b/internal/handlers/registry/client.go @@ -0,0 +1,178 @@ +package registry + +import ( + "context" + "fmt" + "net/http" + "path" + + "github.com/google/go-containerregistry/pkg/authn" + "github.com/google/go-containerregistry/pkg/name" + cranev1 "github.com/google/go-containerregistry/pkg/v1" + "github.com/google/go-containerregistry/pkg/v1/remote" + "go.uber.org/zap" +) + +type ImageDetails struct { + Digest cranev1.Hash + Layers []cranev1.Layer + History []cranev1.History + Platform cranev1.Platform +} + +//go:generate go run github.com/vektra/mockery/v2@v2.46.2 --name ImageIndex --srcpkg github.com/google/go-containerregistry/pkg/v1 --filename image_index.go +//go:generate go run github.com/vektra/mockery/v2@v2.46.2 --name Layer --srcpkg github.com/google/go-containerregistry/pkg/v1 --filename layer.go + +//go:generate go run github.com/vektra/mockery/v2@v2.46.2 --name Client --filename registry_client.go +type Client interface { + // Catalog returns a list of repositories in the registry. + // The registries are fully qualified (e.g. registry.example.com/repo) + Catalogue(ctx context.Context, registry name.Registry) ([]string, error) + + // ListRepositories returns a list of the images defined inside of a repository. + // + // Params: + // - `repository` is the fully qualified name of the repository (e.g. registry.example.com/repo) + // + // Returns a list of images found inside of the repository. + // The name of the image is fully qualified (e.g. registry.example.com/repo:tag) + ListRepositoryContents(ctx context.Context, repository name.Repository) ([]string, error) + + // GetIndex returns the ImageIndex of the given image. + // Note well: the reference might not point to an ImageIndex, + // but to an image manifest. In which case an error will be returned. + GetImageIndex(ref name.Reference) (cranev1.ImageIndex, error) + + // GetImageDetails returns the details of the image. + // When platform is nil, the default platform is used. + GetImageDetails(ref name.Reference, platform *cranev1.Platform) (ImageDetails, error) +} + +type craneRegistryClient struct { + transport http.RoundTripper +} + +func NewCraneRegistryClient(transport http.RoundTripper) Client { + return &craneRegistryClient{transport: transport} +} + +func (c *craneRegistryClient) Catalogue(ctx context.Context, registry name.Registry) ([]string, error) { + zap.L().Debug("catalogue", zap.String("registry", registry.Name())) + puller, err := remote.NewPuller( + remote.WithAuthFromKeychain(authn.DefaultKeychain), + remote.WithTransport(c.transport), + ) + if err != nil { + return []string{}, fmt.Errorf("cannot create puller: %w", err) + } + + catalogger, err := puller.Catalogger(ctx, registry) + if err != nil { + return []string{}, fmt.Errorf("cannot create catologger for %s: %w", registry.Name(), err) + } + + repositories := []string{} + + for catalogger.HasNext() { + repos, err := catalogger.Next(ctx) + if err != nil { + return []string{}, fmt.Errorf("cannot iterate over repository %s contents: %w", registry.Name(), err) + } + for _, repo := range repos.Repos { + repositories = append(repositories, path.Join(registry.Name(), repo)) + } + } + + zap.L().Debug("repositories found", + zap.String("registry", registry.Name()), + zap.Int("number", len(repositories)), + zap.Strings("repositories", repositories)) + + return repositories, nil +} + +func (c *craneRegistryClient) ListRepositoryContents(ctx context.Context, repo name.Repository) ([]string, error) { + zap.L().Debug("list repository contents", zap.String("repository", repo.Name())) + puller, err := remote.NewPuller( + remote.WithAuthFromKeychain(authn.DefaultKeychain), + remote.WithTransport(c.transport), + ) + if err != nil { + return []string{}, fmt.Errorf("cannot create puller: %w", err) + } + + lister, err := puller.Lister(ctx, repo) + if err != nil { + return []string{}, fmt.Errorf("cannot create lister for repository %s: %w", repo, err) + } + + images := []string{} + for lister.HasNext() { + tags, err := lister.Next(ctx) + if err != nil { + return []string{}, fmt.Errorf("cannot iterate over repository contents: %w", err) + } + for _, tag := range tags.Tags { + images = append(images, repo.Tag(tag).String()) + } + } + + zap.L().Debug("images found", + zap.String("repository", repo.Name()), + zap.Int("number", len(images)), + zap.Strings("images", images)) + + return images, nil +} + +func (c *craneRegistryClient) GetImageIndex(ref name.Reference) (cranev1.ImageIndex, error) { + index, err := remote.Index(ref, + remote.WithAuthFromKeychain(authn.DefaultKeychain), + remote.WithTransport(c.transport), + ) + if err != nil { + return nil, fmt.Errorf("cannot fetch image index %q: %w", ref, err) + } + return index, nil +} + +func (c *craneRegistryClient) GetImageDetails(ref name.Reference, platform *cranev1.Platform) (ImageDetails, error) { + zap.L().Debug("get image details", zap.String("image", ref.Name()), zap.Any("platform", platform)) + options := []remote.Option{ + remote.WithAuthFromKeychain(authn.DefaultKeychain), + remote.WithTransport(c.transport), + } + if platform != nil { + options = append(options, remote.WithPlatform(*platform)) + } + + img, err := remote.Image(ref, options...) + if err != nil { + return ImageDetails{}, fmt.Errorf("cannot fetch image %q: %w", ref, err) + } + + imageDigest, err := img.Digest() + if err != nil { + return ImageDetails{}, fmt.Errorf("cannot compute image digest %q: %w", ref, err) + } + + cfgFile, err := img.ConfigFile() + if err != nil { + return ImageDetails{}, fmt.Errorf("cannot read config for %s: %w", ref, err) + } + + // ensure platform is always set + platform = cfgFile.Platform() + + layers, err := img.Layers() + if err != nil { + return ImageDetails{}, fmt.Errorf("cannot read layers for %s: %w", ref, err) + } + + return ImageDetails{ + History: cfgFile.History, + Layers: layers, + Platform: *platform, + Digest: imageDigest, + }, nil +} diff --git a/internal/handlers/registry/mocks/image_index.go b/internal/handlers/registry/mocks/image_index.go new file mode 100644 index 0000000..1efb640 --- /dev/null +++ b/internal/handlers/registry/mocks/image_index.go @@ -0,0 +1,232 @@ +// Code generated by mockery v2.46.2. DO NOT EDIT. + +package mocks + +import ( + v1 "github.com/google/go-containerregistry/pkg/v1" + types "github.com/google/go-containerregistry/pkg/v1/types" + mock "github.com/stretchr/testify/mock" +) + +// ImageIndex is an autogenerated mock type for the ImageIndex type +type ImageIndex struct { + mock.Mock +} + +// Digest provides a mock function with given fields: +func (_m *ImageIndex) Digest() (v1.Hash, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Digest") + } + + var r0 v1.Hash + var r1 error + if rf, ok := ret.Get(0).(func() (v1.Hash, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() v1.Hash); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(v1.Hash) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Image provides a mock function with given fields: _a0 +func (_m *ImageIndex) Image(_a0 v1.Hash) (v1.Image, error) { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for Image") + } + + var r0 v1.Image + var r1 error + if rf, ok := ret.Get(0).(func(v1.Hash) (v1.Image, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(v1.Hash) v1.Image); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(v1.Image) + } + } + + if rf, ok := ret.Get(1).(func(v1.Hash) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ImageIndex provides a mock function with given fields: _a0 +func (_m *ImageIndex) ImageIndex(_a0 v1.Hash) (v1.ImageIndex, error) { + ret := _m.Called(_a0) + + if len(ret) == 0 { + panic("no return value specified for ImageIndex") + } + + var r0 v1.ImageIndex + var r1 error + if rf, ok := ret.Get(0).(func(v1.Hash) (v1.ImageIndex, error)); ok { + return rf(_a0) + } + if rf, ok := ret.Get(0).(func(v1.Hash) v1.ImageIndex); ok { + r0 = rf(_a0) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(v1.ImageIndex) + } + } + + if rf, ok := ret.Get(1).(func(v1.Hash) error); ok { + r1 = rf(_a0) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// IndexManifest provides a mock function with given fields: +func (_m *ImageIndex) IndexManifest() (*v1.IndexManifest, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for IndexManifest") + } + + var r0 *v1.IndexManifest + var r1 error + if rf, ok := ret.Get(0).(func() (*v1.IndexManifest, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() *v1.IndexManifest); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(*v1.IndexManifest) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MediaType provides a mock function with given fields: +func (_m *ImageIndex) MediaType() (types.MediaType, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for MediaType") + } + + var r0 types.MediaType + var r1 error + if rf, ok := ret.Get(0).(func() (types.MediaType, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() types.MediaType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(types.MediaType) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// RawManifest provides a mock function with given fields: +func (_m *ImageIndex) RawManifest() ([]byte, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for RawManifest") + } + + var r0 []byte + var r1 error + if rf, ok := ret.Get(0).(func() ([]byte, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() []byte); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]byte) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Size provides a mock function with given fields: +func (_m *ImageIndex) Size() (int64, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Size") + } + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func() (int64, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() int64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewImageIndex creates a new instance of ImageIndex. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewImageIndex(t interface { + mock.TestingT + Cleanup(func()) +}) *ImageIndex { + mock := &ImageIndex{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/handlers/registry/mocks/layer.go b/internal/handlers/registry/mocks/layer.go new file mode 100644 index 0000000..5ea7b4d --- /dev/null +++ b/internal/handlers/registry/mocks/layer.go @@ -0,0 +1,203 @@ +// Code generated by mockery v2.46.2. DO NOT EDIT. + +package mocks + +import ( + io "io" + + types "github.com/google/go-containerregistry/pkg/v1/types" + mock "github.com/stretchr/testify/mock" + + v1 "github.com/google/go-containerregistry/pkg/v1" +) + +// Layer is an autogenerated mock type for the Layer type +type Layer struct { + mock.Mock +} + +// Compressed provides a mock function with given fields: +func (_m *Layer) Compressed() (io.ReadCloser, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Compressed") + } + + var r0 io.ReadCloser + var r1 error + if rf, ok := ret.Get(0).(func() (io.ReadCloser, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() io.ReadCloser); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(io.ReadCloser) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// DiffID provides a mock function with given fields: +func (_m *Layer) DiffID() (v1.Hash, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for DiffID") + } + + var r0 v1.Hash + var r1 error + if rf, ok := ret.Get(0).(func() (v1.Hash, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() v1.Hash); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(v1.Hash) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Digest provides a mock function with given fields: +func (_m *Layer) Digest() (v1.Hash, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Digest") + } + + var r0 v1.Hash + var r1 error + if rf, ok := ret.Get(0).(func() (v1.Hash, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() v1.Hash); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(v1.Hash) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// MediaType provides a mock function with given fields: +func (_m *Layer) MediaType() (types.MediaType, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for MediaType") + } + + var r0 types.MediaType + var r1 error + if rf, ok := ret.Get(0).(func() (types.MediaType, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() types.MediaType); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(types.MediaType) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Size provides a mock function with given fields: +func (_m *Layer) Size() (int64, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Size") + } + + var r0 int64 + var r1 error + if rf, ok := ret.Get(0).(func() (int64, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() int64); ok { + r0 = rf() + } else { + r0 = ret.Get(0).(int64) + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// Uncompressed provides a mock function with given fields: +func (_m *Layer) Uncompressed() (io.ReadCloser, error) { + ret := _m.Called() + + if len(ret) == 0 { + panic("no return value specified for Uncompressed") + } + + var r0 io.ReadCloser + var r1 error + if rf, ok := ret.Get(0).(func() (io.ReadCloser, error)); ok { + return rf() + } + if rf, ok := ret.Get(0).(func() io.ReadCloser); ok { + r0 = rf() + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(io.ReadCloser) + } + } + + if rf, ok := ret.Get(1).(func() error); ok { + r1 = rf() + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewLayer creates a new instance of Layer. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewLayer(t interface { + mock.TestingT + Cleanup(func()) +}) *Layer { + mock := &Layer{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +} diff --git a/internal/handlers/registry/mocks/registry_client.go b/internal/handlers/registry/mocks/registry_client.go new file mode 100644 index 0000000..6af79b0 --- /dev/null +++ b/internal/handlers/registry/mocks/registry_client.go @@ -0,0 +1,151 @@ +// Code generated by mockery v2.46.2. DO NOT EDIT. + +package mocks + +import ( + context "context" + + name "github.com/google/go-containerregistry/pkg/name" + mock "github.com/stretchr/testify/mock" + + registry "github.com/rancher/sbombastic/internal/handlers/registry" + + v1 "github.com/google/go-containerregistry/pkg/v1" +) + +// Client is an autogenerated mock type for the Client type +type Client struct { + mock.Mock +} + +// Catalogue provides a mock function with given fields: ctx, _a1 +func (_m *Client) Catalogue(ctx context.Context, _a1 name.Registry) ([]string, error) { + ret := _m.Called(ctx, _a1) + + if len(ret) == 0 { + panic("no return value specified for Catalogue") + } + + var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, name.Registry) ([]string, error)); ok { + return rf(ctx, _a1) + } + if rf, ok := ret.Get(0).(func(context.Context, name.Registry) []string); ok { + r0 = rf(ctx, _a1) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, name.Registry) error); ok { + r1 = rf(ctx, _a1) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetImageDetails provides a mock function with given fields: ref, platform +func (_m *Client) GetImageDetails(ref name.Reference, platform *v1.Platform) (registry.ImageDetails, error) { + ret := _m.Called(ref, platform) + + if len(ret) == 0 { + panic("no return value specified for GetImageDetails") + } + + var r0 registry.ImageDetails + var r1 error + if rf, ok := ret.Get(0).(func(name.Reference, *v1.Platform) (registry.ImageDetails, error)); ok { + return rf(ref, platform) + } + if rf, ok := ret.Get(0).(func(name.Reference, *v1.Platform) registry.ImageDetails); ok { + r0 = rf(ref, platform) + } else { + r0 = ret.Get(0).(registry.ImageDetails) + } + + if rf, ok := ret.Get(1).(func(name.Reference, *v1.Platform) error); ok { + r1 = rf(ref, platform) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// GetImageIndex provides a mock function with given fields: ref +func (_m *Client) GetImageIndex(ref name.Reference) (v1.ImageIndex, error) { + ret := _m.Called(ref) + + if len(ret) == 0 { + panic("no return value specified for GetImageIndex") + } + + var r0 v1.ImageIndex + var r1 error + if rf, ok := ret.Get(0).(func(name.Reference) (v1.ImageIndex, error)); ok { + return rf(ref) + } + if rf, ok := ret.Get(0).(func(name.Reference) v1.ImageIndex); ok { + r0 = rf(ref) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).(v1.ImageIndex) + } + } + + if rf, ok := ret.Get(1).(func(name.Reference) error); ok { + r1 = rf(ref) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// ListRepositoryContents provides a mock function with given fields: ctx, repository +func (_m *Client) ListRepositoryContents(ctx context.Context, repository name.Repository) ([]string, error) { + ret := _m.Called(ctx, repository) + + if len(ret) == 0 { + panic("no return value specified for ListRepositoryContents") + } + + var r0 []string + var r1 error + if rf, ok := ret.Get(0).(func(context.Context, name.Repository) ([]string, error)); ok { + return rf(ctx, repository) + } + if rf, ok := ret.Get(0).(func(context.Context, name.Repository) []string); ok { + r0 = rf(ctx, repository) + } else { + if ret.Get(0) != nil { + r0 = ret.Get(0).([]string) + } + } + + if rf, ok := ret.Get(1).(func(context.Context, name.Repository) error); ok { + r1 = rf(ctx, repository) + } else { + r1 = ret.Error(1) + } + + return r0, r1 +} + +// NewClient creates a new instance of Client. It also registers a testing interface on the mock and a cleanup function to assert the mocks expectations. +// The first argument is typically a *testing.T value. +func NewClient(t interface { + mock.TestingT + Cleanup(func()) +}) *Client { + mock := &Client{} + mock.Mock.Test(t) + + t.Cleanup(func() { mock.AssertExpectations(t) }) + + return mock +}