From 7ecf56680a82e5b19837d6ebe4a3c583a4894785 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Fri, 14 Nov 2025 08:37:33 +0100 Subject: [PATCH 01/24] Updates to docker-extension --- client-programs/go.mod | 234 ++++++------- .../pkg/cmd/docker_workshop_deploy_cmd.go | 31 +- docker-extension/ui/src/common/types.ts | 13 + .../WorkshopsTable/WorkshopsTable.tsx | 7 +- docker-extension/ui/src/views/App.tsx | 30 +- docker-extension/ui/tsconfig.json | 5 + go.work | 2 +- go.work.sum | 308 ++++++++++++++++++ 8 files changed, 491 insertions(+), 139 deletions(-) diff --git a/client-programs/go.mod b/client-programs/go.mod index 544c63099..56b20c822 100644 --- a/client-programs/go.mod +++ b/client-programs/go.mod @@ -1,51 +1,51 @@ module github.com/educates/educates-training-platform/client-programs -go 1.23.7 +go 1.24.10 -replace cloud.google.com/go/compute/metadata => cloud.google.com/go/compute/metadata v0.2.3 +// replace cloud.google.com/go/compute/metadata => cloud.google.com/go/compute/metadata v0.2.3 -replace github.com/google/cel-go => github.com/google/cel-go v0.22.1 +// replace github.com/google/cel-go => github.com/google/cel-go v0.22.1 -replace github.com/docker/docker => github.com/docker/docker v27.5.1+incompatible +// replace github.com/docker/docker => github.com/docker/docker v27.5.1+incompatible require ( - carvel.dev/imgpkg v0.44.2 - carvel.dev/kapp v0.64.0 - carvel.dev/kbld v0.45.0 - carvel.dev/vendir v0.43.0 - carvel.dev/ytt v0.51.1 + carvel.dev/imgpkg v0.46.1 + carvel.dev/kapp v0.64.2 + carvel.dev/kbld v0.46.0 + carvel.dev/vendir v0.44.0 + carvel.dev/ytt v0.52.1 github.com/adrg/xdg v0.5.3 - github.com/compose-spec/compose-go v1.20.2 - github.com/cppforlife/go-cli-ui v0.0.0-20220622150351-995494831c6c + github.com/compose-spec/compose-go/v2 v2.9.1 + github.com/cppforlife/go-cli-ui v0.0.0-20250603184554-47874c9078ad // Every time we update below version, we need to update Docker Desktop client to match the required version // or else downgrade CLI support via export DOCKER_API_VERSION=1.xx // Version compabitility: https://github.com/moby/moby/blob/master/docs/api/version-history.md - github.com/docker/docker v28.0.1+incompatible - github.com/docker/go-connections v0.5.0 - github.com/go-logr/logr v1.4.2 - github.com/gorilla/websocket v1.5.3 + github.com/docker/docker v28.5.2+incompatible + github.com/docker/go-connections v0.6.0 + github.com/go-logr/logr v1.4.3 + github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 github.com/joho/godotenv v1.5.1 github.com/mitchellh/go-homedir v1.1.0 github.com/pkg/errors v0.9.1 - github.com/spf13/cobra v1.9.1 - golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 + github.com/spf13/cobra v1.10.1 + golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 gopkg.in/yaml.v2 v2.4.0 - k8s.io/api v0.32.3 - k8s.io/apimachinery v0.32.3 - k8s.io/cli-runtime v0.32.3 - k8s.io/client-go v0.32.3 + k8s.io/api v0.34.2 + k8s.io/apimachinery v0.34.2 + k8s.io/cli-runtime v0.34.2 + k8s.io/client-go v0.34.2 + k8s.io/controller-manager v0.33.5 // indirect k8s.io/klog/v2 v2.130.1 - k8s.io/kubectl v0.32.3 - sigs.k8s.io/controller-runtime v0.20.3 - sigs.k8s.io/kind v0.27.0 - sigs.k8s.io/yaml v1.4.0 + k8s.io/kubectl v0.34.2 + sigs.k8s.io/controller-runtime v0.22.4 + sigs.k8s.io/kind v0.30.0 + sigs.k8s.io/yaml v1.6.0 ) require ( al.essio.dev/pkg/shellescape v1.6.0 // indirect - cel.dev/expr v0.22.0 // indirect - cloud.google.com/go/compute v1.34.1 // indirect - cloud.google.com/go/compute/metadata v0.6.0 // indirect + cel.dev/expr v0.25.1 // indirect + cloud.google.com/go/compute/metadata v0.9.0 // indirect github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect github.com/Azure/go-autorest v14.2.0+incompatible // indirect @@ -56,149 +56,161 @@ require ( github.com/Azure/go-autorest/autorest/date v0.3.1 // indirect github.com/Azure/go-autorest/logger v0.2.2 // indirect github.com/Azure/go-autorest/tracing v0.6.1 // indirect - github.com/BurntSushi/toml v1.4.0 // indirect + github.com/BurntSushi/toml v1.5.0 // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect - github.com/asaskevich/govalidator v0.0.0-20230301143203-a9d515a09cc2 // indirect - github.com/aws/aws-sdk-go-v2 v1.36.3 // indirect - github.com/aws/aws-sdk-go-v2/config v1.29.9 // indirect - github.com/aws/aws-sdk-go-v2/credentials v1.17.62 // indirect - github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 // indirect - github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 // indirect - github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 // indirect - github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 // indirect - github.com/aws/aws-sdk-go-v2/service/ecr v1.43.0 // indirect - github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.32.1 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 // indirect - github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 // indirect - github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 // indirect - github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 // indirect - github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 // indirect - github.com/aws/smithy-go v1.22.3 // indirect - github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.9.1 // indirect + github.com/aws/aws-sdk-go-v2 v1.39.6 // indirect + github.com/aws/aws-sdk-go-v2/config v1.31.19 // indirect + github.com/aws/aws-sdk-go-v2/credentials v1.18.23 // indirect + github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 // indirect + github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 // indirect + github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 // indirect + github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 // indirect + github.com/aws/aws-sdk-go-v2/service/ecr v1.51.3 // indirect + github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 // indirect + github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 // indirect + github.com/aws/aws-sdk-go-v2/service/sso v1.30.2 // indirect + github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.6 // indirect + github.com/aws/aws-sdk-go-v2/service/sts v1.40.1 // indirect + github.com/aws/smithy-go v1.23.2 // indirect + github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.11.0 // indirect github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/bmatcuk/doublestar v1.3.4 // indirect github.com/carvel-dev/semver/v4 v4.0.1-0.20240402203627-beb83fbf25e4 // indirect - github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/cheggaaa/pb/v3 v3.1.7 // indirect github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect - github.com/containerd/stargz-snapshotter/estargz v0.16.3 // indirect + github.com/clipperhouse/stringish v0.1.1 // indirect + github.com/clipperhouse/uax29/v2 v2.3.0 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/stargz-snapshotter/estargz v0.18.1 // indirect github.com/cppforlife/cobrautil v0.0.0-20221130162803-acdfead391ef // indirect github.com/cppforlife/color v1.9.1-0.20200716202919-6706ac40b835 // indirect github.com/cppforlife/go-patch v0.0.0-20240118020416-2147782e467b // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/cli v28.0.1+incompatible // indirect + github.com/docker/cli v29.0.0+incompatible // indirect github.com/docker/distribution v2.8.3+incompatible // indirect - github.com/docker/docker-credential-helpers v0.9.3 // indirect + github.com/docker/docker-credential-helpers v0.9.4 // indirect github.com/docker/go-units v0.5.0 // indirect - github.com/emicklei/go-restful/v3 v3.12.2 // indirect + github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect - github.com/fxamacker/cbor/v2 v2.7.0 // indirect + github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/stdr v1.2.2 // 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.1 // indirect + github.com/go-openapi/jsonpointer v0.22.1 // indirect + github.com/go-openapi/jsonreference v0.21.3 // indirect + github.com/go-openapi/swag v0.25.1 // indirect + github.com/go-openapi/swag/cmdutils v0.25.1 // indirect + github.com/go-openapi/swag/conv v0.25.1 // indirect + github.com/go-openapi/swag/fileutils v0.25.1 // indirect + github.com/go-openapi/swag/jsonname v0.25.1 // indirect + github.com/go-openapi/swag/jsonutils v0.25.1 // indirect + github.com/go-openapi/swag/loading v0.25.1 // indirect + github.com/go-openapi/swag/mangling v0.25.1 // indirect + github.com/go-openapi/swag/netutils v0.25.1 // indirect + github.com/go-openapi/swag/stringutils v0.25.1 // indirect + github.com/go-openapi/swag/typeutils v0.25.1 // indirect + github.com/go-openapi/swag/yamlutils v0.25.1 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect - github.com/golang/protobuf v1.5.4 // indirect - github.com/google/cel-go v0.24.1 // indirect - github.com/google/gnostic-models v0.6.9 // indirect + github.com/google/cel-go v0.26.1 // indirect + github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect - github.com/google/go-containerregistry v0.20.3 // indirect + github.com/google/go-containerregistry v0.20.6 // indirect github.com/google/go-github v17.0.0+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect - github.com/google/gofuzz v1.2.0 // indirect - github.com/google/safetext v0.0.0-20240722112252-5a72de7e7962 // indirect github.com/google/uuid v1.6.0 // indirect - github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect github.com/hashicorp/go-version v1.7.0 // indirect github.com/imdario/mergo v0.3.16 // 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/k14s/difflib v0.0.0-20240118055029-596a7a5585c3 // indirect github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368 // indirect github.com/k14s/ytt v0.39.0 // indirect - github.com/klauspost/compress v1.18.0 // indirect + github.com/klauspost/compress v1.18.1 // indirect github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de // indirect - github.com/mailru/easyjson v0.9.0 // indirect github.com/mattn/go-colorable v0.1.14 // indirect github.com/mattn/go-isatty v0.0.20 // indirect - github.com/mattn/go-runewidth v0.0.16 // indirect + github.com/mattn/go-runewidth v0.0.19 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/mapstructure v1.5.0 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/spdystream v0.5.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect - github.com/modern-go/reflect2 v1.0.2 // indirect + github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/openshift/crd-schema-checker v0.0.0-20250131182805-6a3e5c3bb298 // indirect + github.com/openshift/crd-schema-checker v0.0.0-20250905140724-c313b6407231 // indirect github.com/otiai10/copy v1.14.1 // indirect github.com/otiai10/mint v1.6.3 // indirect - github.com/pborman/uuid v1.2.1 // indirect github.com/pelletier/go-toml v1.9.5 // indirect - github.com/prometheus/client_golang v1.21.1 // indirect - github.com/prometheus/client_model v0.6.1 // indirect - github.com/prometheus/common v0.63.0 // indirect - github.com/prometheus/procfs v0.15.1 // indirect - github.com/rivo/uniseg v0.4.7 // indirect + github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect + github.com/prometheus/client_golang v1.23.2 // indirect + github.com/prometheus/client_model v0.6.2 // indirect + github.com/prometheus/common v0.67.2 // indirect + github.com/prometheus/procfs v0.19.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect - github.com/spf13/pflag v1.0.6 // indirect - github.com/stoewer/go-strcase v1.3.0 // indirect - github.com/vbatts/tar-split v0.12.1 // indirect - github.com/vito/go-interact v1.0.1 // indirect + github.com/spf13/pflag v1.0.10 // indirect + github.com/stoewer/go-strcase v1.3.1 // indirect + github.com/vbatts/tar-split v0.12.2 // indirect + github.com/vito/go-interact v1.0.2 // indirect github.com/vmware-tanzu/carvel-kapp-controller v0.51.3 // indirect github.com/x448/float16 v0.8.4 // indirect github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect github.com/xeipuuv/gojsonschema v1.2.0 // indirect - go.opentelemetry.io/auto/sdk v1.1.0 // indirect - go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0 // indirect - go.opentelemetry.io/otel v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0 // indirect - go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 // indirect - go.opentelemetry.io/otel/metric v1.35.0 // indirect - go.opentelemetry.io/otel/sdk v1.35.0 // indirect - go.opentelemetry.io/otel/trace v1.35.0 // indirect - go.opentelemetry.io/proto/otlp v1.5.0 // indirect - golang.org/x/crypto v0.36.0 // indirect - golang.org/x/net v0.38.0 // indirect - golang.org/x/oauth2 v0.28.0 // indirect - golang.org/x/sync v0.12.0 // indirect - golang.org/x/sys v0.31.0 // indirect - golang.org/x/term v0.30.0 // indirect - golang.org/x/text v0.23.0 // indirect - golang.org/x/time v0.11.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 // indirect - google.golang.org/grpc v1.71.0 // indirect - google.golang.org/protobuf v1.36.5 // indirect - gopkg.in/evanphx/json-patch.v4 v4.12.0 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect + go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.38.0 // indirect + go.opentelemetry.io/otel/sdk v1.38.0 // indirect + go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/proto/otlp v1.9.0 // indirect + go.yaml.in/yaml/v2 v2.4.3 // indirect + go.yaml.in/yaml/v3 v3.0.4 // indirect + golang.org/x/crypto v0.44.0 // indirect + golang.org/x/net v0.47.0 // indirect + golang.org/x/oauth2 v0.33.0 // indirect + golang.org/x/sync v0.18.0 // indirect + golang.org/x/sys v0.38.0 // indirect + golang.org/x/term v0.37.0 // indirect + golang.org/x/text v0.31.0 // indirect + golang.org/x/time v0.14.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect + google.golang.org/grpc v1.76.0 // indirect + google.golang.org/protobuf v1.36.10 // indirect + gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.32.3 // indirect - k8s.io/apiserver v0.32.3 // indirect - k8s.io/component-base v0.32.3 // indirect - k8s.io/component-helpers v0.32.3 // indirect - k8s.io/kube-openapi v0.0.0-20250304201544-e5f78fe3ede9 // indirect - k8s.io/kubernetes v1.32.7 // indirect - k8s.io/utils v0.0.0-20241210054802-24370beab758 // indirect - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.0 // indirect - sigs.k8s.io/json v0.0.0-20241014173422-cfa47c3a1cc8 // indirect + k8s.io/apiextensions-apiserver v0.34.1 // indirect + k8s.io/apiserver v0.34.1 // indirect + k8s.io/component-base v0.34.2 // indirect + k8s.io/component-helpers v0.34.2 // indirect + k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect + k8s.io/kubernetes v1.34.2 // indirect + k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect + sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // indirect + sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect - sigs.k8s.io/structured-merge-diff/v4 v4.6.0 // indirect -) + sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect +) \ No newline at end of file diff --git a/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go b/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go index b7ccd32b1..06c974085 100644 --- a/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go @@ -16,8 +16,8 @@ import ( "time" yttcmd "carvel.dev/ytt/pkg/cmd/template" - composeloader "github.com/compose-spec/compose-go/loader" - composetypes "github.com/compose-spec/compose-go/types" + composeloader "github.com/compose-spec/compose-go/v2/loader" + composetypes "github.com/compose-spec/compose-go/v2/types" "github.com/docker/docker/client" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" @@ -280,6 +280,14 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployOptions, networks["educates"] = &composetypes.ServiceNetworkConfig{} } + var extraHostsList composetypes.HostsList + if len(workshopExtraHosts) > 0 { + extraHostsList = make(composetypes.HostsList, len(workshopExtraHosts)) + for hostname, ip := range workshopExtraHosts { + extraHostsList[hostname] = []string{ip} + } + } + workshopServiceConfig := composetypes.ServiceConfig{ Name: "workshop", Image: workshopImageName, @@ -289,7 +297,7 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployOptions, Volumes: workshopVolumesConfig, Environment: composetypes.NewMappingWithEquals(workshopEnvironment), Labels: composetypes.Labels(workshopLabels), - ExtraHosts: composetypes.HostsList(workshopExtraHosts), + ExtraHosts: extraHostsList, DependsOn: composetypes.DependsOnConfig{}, Networks: networks, } @@ -320,13 +328,15 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployOptions, } } - workshopServices := []composetypes.ServiceConfig{workshopServiceConfig} + workshopServices := composetypes.Services{ + "workshop": workshopServiceConfig, + } composeConfig := composetypes.Project{ Name: originalName, Services: workshopServices, Networks: composetypes.Networks{ - "educates": {External: composetypes.External{External: true}}, + "educates": composetypes.NetworkConfig{Name: "educates", External: true}, }, Volumes: composetypes.Volumes{ "workshop": composetypes.VolumeConfig{}, @@ -334,12 +344,12 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployOptions, } if workshopComposeProject != nil { - for _, extraService := range workshopComposeProject.Services { + for serviceName, extraService := range workshopComposeProject.Services { extraService.Ports = []composetypes.ServicePortConfig{} - composeConfig.Services = append(composeConfig.Services, extraService) + composeConfig.Services[serviceName] = extraService - workshopServiceConfig.DependsOn[extraService.Name] = composetypes.ServiceDependency{ + workshopServiceConfig.DependsOn[serviceName] = composetypes.ServiceDependency{ Condition: composetypes.ServiceConditionStarted, } } @@ -352,7 +362,7 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployOptions, } if o.Cluster != "" { - composeConfig.Networks["kind"] = composetypes.NetworkConfig{External: composetypes.External{External: true}} + composeConfig.Networks["kind"] = composetypes.NetworkConfig{Name: "kind", External: true} } composeConfigBytes, err := yaml.Marshal(&composeConfig) @@ -904,10 +914,11 @@ func extractWorkshopComposeConfig(workshop *unstructured.Unstructured) (*compose ConfigFiles: []composetypes.ConfigFile{configFiles}, } - return composeloader.Load(composeConfigDetails, func(options *composeloader.Options) { + return composeloader.LoadWithContext(context.Background(), composeConfigDetails, func(options *composeloader.Options) { options.SkipConsistencyCheck = true options.SkipNormalization = true options.ResolvePaths = false + options.SkipValidation = true }) } diff --git a/docker-extension/ui/src/common/types.ts b/docker-extension/ui/src/common/types.ts index 664817a0a..e990bec56 100644 --- a/docker-extension/ui/src/common/types.ts +++ b/docker-extension/ui/src/common/types.ts @@ -25,3 +25,16 @@ export const NullWorkshop: Workshop = { source: "", status: "", }; + +// API response types +export type ListResponse = Workshop[]; + +export interface DeployResponse { + ok: boolean; + message?: string; +} + +export interface DeleteResponse { + ok: boolean; + message?: string; +} diff --git a/docker-extension/ui/src/components/WorkshopsTable/WorkshopsTable.tsx b/docker-extension/ui/src/components/WorkshopsTable/WorkshopsTable.tsx index b52f3f61c..a6e122c20 100644 --- a/docker-extension/ui/src/components/WorkshopsTable/WorkshopsTable.tsx +++ b/docker-extension/ui/src/components/WorkshopsTable/WorkshopsTable.tsx @@ -96,7 +96,12 @@ export default function WorkshopsTable({ rows, onStop, showPort }: WorkshopsTabl {showPort && ( - {row.url.split(":")[2]} + + {(() => { + const segments = row.url.split(":"); + return segments.length > 2 && segments[2] ? segments[2] : ""; + })()} + )} {row.source} diff --git a/docker-extension/ui/src/views/App.tsx b/docker-extension/ui/src/views/App.tsx index 473f1f80f..1182e3db0 100644 --- a/docker-extension/ui/src/views/App.tsx +++ b/docker-extension/ui/src/views/App.tsx @@ -4,7 +4,7 @@ import { Box, Grid, Link, TextField, Typography } from "@mui/material"; import BottomIntroPane from "../components/BottomIntroPane/BottomIntroPane"; import WorkshopsTable from "../components/WorkshopsTable/WorkshopsTable"; import { handleGoTo } from "../common/goto"; -import { Workshop } from "../common/types"; +import { Workshop, ListResponse, DeployResponse, DeleteResponse } from "../common/types"; import { isValidURL } from "../common/validations"; import OptionsPane from "../components/OptionsPane/OptionsPane"; import { LoadingButton } from "@mui/lab"; @@ -63,24 +63,20 @@ export function App() { }; useEffect(() => { - let interval: any = null; - if (interval != undefined) clearInterval(interval); - if (interval == undefined) { - interval = setInterval(() => { - list(); - }, 3000); - return () => clearInterval(interval); - } + const interval: ReturnType = setInterval(() => { + list(); + }, 3000); + return () => clearInterval(interval); }, []); const list = async () => { console.log("list"); ddClient.extension.vm?.service ?.get("/workshop/list") - .then((result: any) => { - setWorkshops(result); + .then((result: unknown) => { + setWorkshops(result as ListResponse); }) - .catch((err: any) => { + .catch((err: unknown) => { console.log(err); }); }; @@ -91,12 +87,13 @@ export function App() { setQueryingBackend(true); ddClient.extension.vm?.service ?.get("/workshop/deploy?url=" + encodeURIComponent(url) + "&port=" + port) - .then((result: any) => { + .then((result: unknown) => { + const _ = result as DeployResponse; setQueryingBackend(false); setUrl(""); list(); }) - .catch((err: any) => { + .catch((err: unknown) => { console.log(err); setQueryingBackend(false); }); @@ -109,10 +106,11 @@ export function App() { console.log("stop: " + name); ddClient.extension.vm?.service ?.get("/workshop/delete?name=" + name) - .then((result: any) => { + .then((result: unknown) => { + const _ = result as DeleteResponse; list(); }) - .catch((err: any) => { + .catch((err: unknown) => { console.log(err); }); }; diff --git a/docker-extension/ui/tsconfig.json b/docker-extension/ui/tsconfig.json index 3d0a51a86..31d882746 100644 --- a/docker-extension/ui/tsconfig.json +++ b/docker-extension/ui/tsconfig.json @@ -8,6 +8,11 @@ "esModuleInterop": false, "allowSyntheticDefaultImports": true, "strict": true, + "noUncheckedIndexedAccess": true, + "exactOptionalPropertyTypes": true, + "noImplicitReturns": true, + "noFallthroughCasesInSwitch": true, + "noPropertyAccessFromIndexSignature": true, "forceConsistentCasingInFileNames": true, "module": "ESNext", "moduleResolution": "Node", diff --git a/go.work b/go.work index a52409e99..b095f6393 100644 --- a/go.work +++ b/go.work @@ -1,3 +1,3 @@ -go 1.23.7 +go 1.24.10 use ./client-programs/ diff --git a/go.work.sum b/go.work.sum index 2cf8406c2..4913345cd 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,10 +1,22 @@ bitbucket.org/bertimus9/systemstat v0.5.0/go.mod h1:EkUWPp8lKFPMXP8vnbpT5JDI0W/sTiLZAvN8ONWErHY= carvel.dev/imgpkg v0.44.0 h1:5f7LZKn8MALx2xldwNXm5TD6vn9NDQuHJs8Nag2Fr0Y= +carvel.dev/imgpkg v0.46.1 h1:UOYaPllQJRsbzSl61IiNvmDZA5z4951i/KaSROAC1W0= +carvel.dev/imgpkg v0.46.1/go.mod h1:Q1E+7tpoiPbVNjb7HSmLZP7E1j0w6mWFzDarOXW1HiI= carvel.dev/kapp v0.59.2/go.mod h1:HAeURGw65eT00APPvnOQ8uDx5yvdrro2vtH5VYF1Zz0= +carvel.dev/kapp v0.64.2 h1:dJhtWVOkvPPgcS0f5A4OtOlrGie9gHvabtZBvB/h0+M= +carvel.dev/kapp v0.64.2/go.mod h1:5t0pWQzyoY9SzPVqrqgYhTlzgsuyMy+bvFdmrvtbDJw= +carvel.dev/kbld v0.46.0 h1:khSHTH3yiEE8imE9K245ZT67ZToixa1nC1938Oje1O4= +carvel.dev/kbld v0.46.0/go.mod h1:wmUYbnw0di759Id26P6dtRW59cBHy4UT9/FJgthiJ0I= +carvel.dev/vendir v0.44.0 h1:vfq5KgGbbLlxHrE0prY7gZgiEQpjwo4lS2akCaVkcxA= +carvel.dev/vendir v0.44.0/go.mod h1:gslrJ0HPiy8gtJYsQZHzIVuGfOG0nfDKDupEm7uBWVQ= carvel.dev/ytt v0.47.0/go.mod h1:Xarf0th61vX6VY07l3KBSi3uaMCQ2UyPPiCPiaVpHME= +carvel.dev/ytt v0.52.1 h1:I9rCwIunzClas2MH5nVGtCK5ujZdiGaqAfGol/wiRKQ= +carvel.dev/ytt v0.52.1/go.mod h1:lzkMguCvSVvxT2My9RG3gRMgTws97NpNXufKZ6iiP5E= cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8= cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo= cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= @@ -19,6 +31,7 @@ cloud.google.com/go v0.78.0/go.mod h1:QjdrLG0uq+YwhjoVOLsS1t7TW8fs36kLs4XO5R5ECH cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb8= cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go v0.99.0 h1:y/cM2iqGgGi5D5DQZl6D9STN/3dR/Vx5Mp8s752oJTY= +cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk= cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME= cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= @@ -66,6 +79,10 @@ cloud.google.com/go/compute v1.25.1/go.mod h1:oopOIR53ly6viBYxaDhBfJwzUAxf1zE//u cloud.google.com/go/compute v1.27.4 h1:XM8ulx6crjdl09XBfji7viFgZOEQuIxBwKmjRH9Rtmc= cloud.google.com/go/compute v1.27.4/go.mod h1:7JZS+h21ERAGHOy5qb7+EPyXlQwzshzrx1x6L9JhTqU= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= +cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/contactcenterinsights v1.11.1/go.mod h1:FeNP3Kg8iteKM80lMwSk3zZZKVxr+PGnAId6soKuXwE= cloud.google.com/go/container v1.26.1/go.mod h1:5smONjPRUxeEpDG7bMKWfDL4sauswqEtnBK1/KKpR04= cloud.google.com/go/containeranalysis v0.11.1/go.mod h1:rYlUOM7nem1OJMKwE1SadufX0JP3wnXj844EtZAwWLY= @@ -184,8 +201,11 @@ github.com/Azure/go-autorest/autorest/azure/cli v0.4.1/go.mod h1:JfDgiIO1/RPu6z4 github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/GoogleCloudPlatform/k8s-cloud-provider v1.18.1-0.20220218231025-f11817397a1b/go.mod h1:FNj4KYEAAHfYu68kRYolGoxkaJn+6mdEsaM12VTwuI0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw= github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA= github.com/Microsoft/hcsshim v0.8.25/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= github.com/Microsoft/hnslib v0.0.8/go.mod h1:EYveQJlhKh2obmEIRB3uKN6dBd9pj1frPsrTGFppKuk= @@ -199,6 +219,7 @@ github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdko github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= +github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= @@ -211,45 +232,77 @@ github.com/aws/aws-lambda-go v1.41.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8q github.com/aws/aws-sdk-go-v2 v1.7.1/go.mod h1:L5LuPC1ZgDr2xQS7AmIec/Jlc7O/Y1u2KxJyNVab250= github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E= github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= +github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk= +github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= github.com/aws/aws-sdk-go-v2/config v1.5.0/go.mod h1:RWlPOAW3E3tbtNAqTwvSW54Of/yP3oiZXMI0xfUdjyA= github.com/aws/aws-sdk-go-v2/config v1.29.6 h1:fqgqEKK5HaZVWLQoLiC9Q+xDlSp+1LYidp6ybGE2OGg= github.com/aws/aws-sdk-go-v2/config v1.29.6/go.mod h1:Ft+WLODzDQmCTHDvqAH1JfC2xxbZ0MxpZAcJqmE1LTQ= +github.com/aws/aws-sdk-go-v2/config v1.31.19 h1:qdUtOw4JhZr2YcKO3g0ho/IcFXfXrrb8xlX05Y6EvSw= +github.com/aws/aws-sdk-go-v2/config v1.31.19/go.mod h1:tMJ8bur01t8eEm0atLadkIIFA154OJ4JCKZeQ+o+R7k= github.com/aws/aws-sdk-go-v2/credentials v1.3.1/go.mod h1:r0n73xwsIVagq8RsxmZbGSRQFj9As3je72C2WzUIToc= github.com/aws/aws-sdk-go-v2/credentials v1.17.59 h1:9btwmrt//Q6JcSdgJOLI98sdr5p7tssS9yAsGe8aKP4= github.com/aws/aws-sdk-go-v2/credentials v1.17.59/go.mod h1:NM8fM6ovI3zak23UISdWidyZuI1ghNe2xjzUZAyT+08= +github.com/aws/aws-sdk-go-v2/credentials v1.18.23 h1:IQILcxVgMO2BVLaJ2aAv21dKWvE1MduNrbvuK43XL2Q= +github.com/aws/aws-sdk-go-v2/credentials v1.18.23/go.mod h1:JRodHszhVdh5TPUknxDzJzrMiznG+M+FfR3WSWKgCI8= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.3.0/go.mod h1:2LAuqPx1I6jNfaGDucWfA2zqQCYCOMCDHiCOciALyNw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 h1:KwsodFKVQTlI5EyhRSugALzsV6mG/SGrdjlMXSZSdso= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28/go.mod h1:EY3APf9MzygVhKuPXAc5H+MkGb8k/DOSQjWS0LgkKqI= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 h1:BjUcr3X3K0wZPGFg2bxOWW3VPN8rkE3/61zhP+IHviA= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32/go.mod h1:80+OGC/bgzzFFTUmcuwD0lb4YutwQeKLFpmt6hoWapU= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 h1:m1GeXHVMJsRsUAqG6HjZWx9dj7F5TR+cF1bjyfYyBd4= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32/go.mod h1:IitoQxGfaKdVLNg0hD8/DXmAqNy0H4K2H2Sf91ti8sI= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M= github.com/aws/aws-sdk-go-v2/internal/ini v1.1.1/go.mod h1:Zy8smImhTdOETZqfyn01iNOe0CNggVbPjCajyaz6Gvg= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= github.com/aws/aws-sdk-go-v2/service/ecr v1.4.1/go.mod h1:FglZcyeiBqcbvyinl+n14aT/EWC7S1MIH+Gan2iizt0= github.com/aws/aws-sdk-go-v2/service/ecr v1.40.3 h1:a+210FCU/pR5hhKRaskRfX/ogcyyzFBrehcTk5DTAyU= github.com/aws/aws-sdk-go-v2/service/ecr v1.40.3/go.mod h1:dtD3a4sjUjVL86e0NUvaqdGvds5ED6itUiZPDaT+Gh8= +github.com/aws/aws-sdk-go-v2/service/ecr v1.51.3 h1:+0AhrMCsfRxzlojjbJBOOBO1Ka5t1VsF28g+eHYbyEI= +github.com/aws/aws-sdk-go-v2/service/ecr v1.51.3/go.mod h1:1NVD1KuMjH2GqnPwMotPndQaT/MreKkWpjkF12d6oKU= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.4.1/go.mod h1:eD5Eo4drVP2FLTw0G+SMIPWNWvQRGGTtIZR2XeAagoA= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.31.2 h1:E6/Myrj9HgLF22medmDrKmbpm4ULsa+cIBNx3phirBk= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.31.2/go.mod h1:OQ8NALFcchBJ/qruak6zKUQodovnTKKaReTuCkc5/9Y= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.3 h1:2Mfho1EDuk815vcGZbiGsOY6mMGPMCsJTx2dWZdWudI= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.3/go.mod h1:x7gU4CAyAz4BsM9hlRkhHiYw2GIr1QCmN45uwQw9l/E= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.1/go.mod h1:zceowr5Z1Nh2WVP8bf/3ikB41IZW59E4yIYbg+pC6mw= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 h1:SYVGSFQHlchIcy6e7x12bsrxClCXSP5et8cqVhL8cuw= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13/go.mod h1:kizuDaLX37bG5WZaoxGPQR/LNFXpxp0vsUnqfkWXfNE= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg= github.com/aws/aws-sdk-go-v2/service/sso v1.3.1/go.mod h1:J3A3RGUvuCZjvSuZEcOpHDnzZP/sKbhDWV2T1EOzFIM= github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 h1:/eE3DogBjYlvlbhd2ssWyeuovWunHLxfgw3s/OJa4GQ= github.com/aws/aws-sdk-go-v2/service/sso v1.24.15/go.mod h1:2PCJYpi7EKeA5SkStAmZlF6fi0uUABuhtF8ILHjGc3Y= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.2 h1:/p6MxkbQoCzaGQT3WO0JwG0FlQyG9RD8VmdmoKc5xqU= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.2/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 h1:M/zwXiL2iXUrHputuXgmO94TVNmcenPHxgLXLutodKE= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14/go.mod h1:RVwIw3y/IqxC2YEXSIkAzRDdEU1iRabDPaYjpGCbCGQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.6 h1:0dES42T2dhICCbVB3JSTTn7+Bz93wfJEK1b7jksZIyQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.6/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo= github.com/aws/aws-sdk-go-v2/service/sts v1.6.0/go.mod h1:q7o0j7d7HrJk/vr9uUt3BVRASvcU7gYZB9PUgPiByXg= github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 h1:TzeR06UCMUq+KA3bDkujxK1GVGy+G8qQN/QVYzGLkQE= github.com/aws/aws-sdk-go-v2/service/sts v1.33.14/go.mod h1:dspXf/oYWGWo6DEvj98wpaTeqt5+DMidZD0A9BYTizc= +github.com/aws/aws-sdk-go-v2/service/sts v1.40.1 h1:5sbIM57lHLaEaNWdIx23JH30LNBsSDkjN/QXGcRLAFc= +github.com/aws/aws-sdk-go-v2/service/sts v1.40.1/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk= github.com/aws/smithy-go v1.6.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= +github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= +github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220517224237-e6f29200ae04/go.mod h1:Z+bXnIbhKJYSvxNwsNnwde7pDKxuqlEZCbUBoTwAqf0= +github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.11.0 h1:GOPttfOAf5qAgx7r6b+zCWZrvCsfKffkL4H6mSYx1kA= +github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.11.0/go.mod h1:a2HN6+p7k0JLDO8514sMr0l4cnrR52z4sWoZ/Uc82ho= github.com/bazelbuild/bazelisk v1.13.2/go.mod h1:jVD8/E7hMAXgWKCljEz8hOV0PZ+nFBgCpjIOJ6Xyzus= github.com/bazelbuild/rules_go v0.34.0/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= @@ -259,6 +312,8 @@ github.com/bmatcuk/doublestar v1.2.1 h1:eetYiv8DDYOZcBADY+pRvRytf3Dlz1FhnpvL2FsC github.com/bmatcuk/doublestar v1.2.1/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= @@ -271,6 +326,10 @@ github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwys github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY= +github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= +github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= +github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= +github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -279,32 +338,52 @@ github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4/go.mod h1:eXthEFrGJvWH github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido69INQaVO6d87Qn543Xr6nooe9Kz7oBFM= github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/compose-spec/compose-go/v2 v2.9.1 h1:8UwI+ujNU+9Ffkf/YgAm/qM9/eU7Jn8nHzWG721W4rs= +github.com/compose-spec/compose-go/v2 v2.9.1/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE= github.com/container-storage-interface/spec v1.8.0/go.mod h1:ROLik+GhPslwwWRNFF1KasPzroNARibH2rfz1rkg4H0= github.com/container-storage-interface/spec v1.9.0/go.mod h1:ZfDu+3ZRyeVqxZM0Ds19MVLkN2d1XJ5MAfi1L3VjlT0= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig= +github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc= github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= +github.com/containerd/stargz-snapshotter/estargz v0.18.1 h1:cy2/lpgBXDA3cDKSyEfNOFMA/c10O1axL69EU7iirO8= +github.com/containerd/stargz-snapshotter/estargz v0.18.1/go.mod h1:ALIEqa7B6oVDsrF37GkGN20SuvG/pIMm7FwP7ZmRb0Q= github.com/containerd/ttrpc v1.2.2/go.mod h1:sIT6l32Ph/H9cvnJsfXM5drIVzTr5A2flTf1G5tYZak= github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= +github.com/containerd/ttrpc v1.2.6/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= +github.com/containerd/typeurl/v2 v2.2.2/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= github.com/coredns/corefile-migration v1.0.21/go.mod h1:XnhgULOEouimnzgn0t4WPuFDN2/PJQcTxdWKC5eXNGE= github.com/coredns/corefile-migration v1.0.24/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= +github.com/coredns/corefile-migration v1.0.26/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-oidc v2.3.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cppforlife/cobrautil v0.0.0-20221021151949-d60711905d65/go.mod h1:2w+qxVu2KSGW78Ex/XaIqfh/OvBgjEsmN53S4T8vEyA= github.com/cppforlife/go-cli-ui v0.0.0-20220425131040-94f26b16bc14/go.mod h1:AlgTssDlstr4mf92TR4DPITLfl5+7wEY4cKStCmeeto= +github.com/cppforlife/go-cli-ui v0.0.0-20250603184554-47874c9078ad h1:PaYhzcFC4VCmlBNWLshK0VxWJyb5J+AdnrwR6hnfe+A= +github.com/cppforlife/go-cli-ui v0.0.0-20250603184554-47874c9078ad/go.mod h1:xZhzUOhCF76o47bEulESCNzmvP4xbwRFUSpN62Zu9FI= 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/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= +github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM= +github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8= +github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= @@ -312,12 +391,22 @@ github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQ github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM= github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v29.0.0+incompatible h1:KgsN2RUFMNM8wChxryicn4p46BdQWpXOA1XLGBGPGAw= +github.com/docker/cli v29.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v24.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U= github.com/docker/docker v27.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.0.2+incompatible h1:9BILleFwug5FSSqWBgVevgL3ewDJfWWWyZVqlDMttE8= +github.com/docker/docker v28.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= +github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= +github.com/docker/docker-credential-helpers v0.9.4 h1:76ItO69/AP/V4yT9V4uuuItG0B1N8hvt0T0c0NN/DzI= +github.com/docker/docker-credential-helpers v0.9.4/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= @@ -329,6 +418,8 @@ github.com/emicklei/go-restful v2.16.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQm github.com/emicklei/go-restful/v3 v3.8.0/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/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -359,9 +450,12 @@ github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSw github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getkin/kin-openapi v0.81.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -369,6 +463,7 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= @@ -378,6 +473,8 @@ github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= @@ -387,6 +484,8 @@ github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDB github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= +github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk= +github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= @@ -394,6 +493,8 @@ github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= 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/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc= +github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= @@ -401,16 +502,46 @@ github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= +github.com/go-openapi/swag v0.25.1 h1:6uwVsx+/OuvFVPqfQmOOPsqTcm5/GkBhNwLqIR916n8= +github.com/go-openapi/swag v0.25.1/go.mod h1:bzONdGlT0fkStgGPd3bhZf1MnuPkf2YAys6h+jZipOo= +github.com/go-openapi/swag/cmdutils v0.25.1 h1:nDke3nAFDArAa631aitksFGj2omusks88GF1VwdYqPY= +github.com/go-openapi/swag/cmdutils v0.25.1/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.1 h1:+9o8YUg6QuqqBM5X6rYL/p1dpWeZRhoIt9x7CCP+he0= +github.com/go-openapi/swag/conv v0.25.1/go.mod h1:Z1mFEGPfyIKPu0806khI3zF+/EUXde+fdeksUl2NiDs= +github.com/go-openapi/swag/fileutils v0.25.1 h1:rSRXapjQequt7kqalKXdcpIegIShhTPXx7yw0kek2uU= +github.com/go-openapi/swag/fileutils v0.25.1/go.mod h1:+NXtt5xNZZqmpIpjqcujqojGFek9/w55b3ecmOdtg8M= +github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU= +github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo= +github.com/go-openapi/swag/jsonutils v0.25.1 h1:AihLHaD0brrkJoMqEZOBNzTLnk81Kg9cWr+SPtxtgl8= +github.com/go-openapi/swag/jsonutils v0.25.1/go.mod h1:JpEkAjxQXpiaHmRO04N1zE4qbUEg3b7Udll7AMGTNOo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1/go.mod h1:kjmweouyPwRUEYMSrbAidoLMGeJ5p6zdHi9BgZiqmsg= +github.com/go-openapi/swag/loading v0.25.1 h1:6OruqzjWoJyanZOim58iG2vj934TysYVptyaoXS24kw= +github.com/go-openapi/swag/loading v0.25.1/go.mod h1:xoIe2EG32NOYYbqxvXgPzne989bWvSNoWoyQVWEZicc= +github.com/go-openapi/swag/mangling v0.25.1 h1:XzILnLzhZPZNtmxKaz/2xIGPQsBsvmCjrJOWGNz/ync= +github.com/go-openapi/swag/mangling v0.25.1/go.mod h1:CdiMQ6pnfAgyQGSOIYnZkXvqhnnwOn997uXZMAd/7mQ= +github.com/go-openapi/swag/netutils v0.25.1 h1:2wFLYahe40tDUHfKT1GRC4rfa5T1B4GWZ+msEFA4Fl4= +github.com/go-openapi/swag/netutils v0.25.1/go.mod h1:CAkkvqnUJX8NV96tNhEQvKz8SQo2KF0f7LleiJwIeRE= +github.com/go-openapi/swag/stringutils v0.25.1 h1:Xasqgjvk30eUe8VKdmyzKtjkVjeiXx1Iz0zDfMNpPbw= +github.com/go-openapi/swag/stringutils v0.25.1/go.mod h1:JLdSAq5169HaiDUbTvArA2yQxmgn4D6h4A+4HqVvAYg= +github.com/go-openapi/swag/typeutils v0.25.1 h1:rD/9HsEQieewNt6/k+JBwkxuAHktFtH3I3ysiFZqukA= +github.com/go-openapi/swag/typeutils v0.25.1/go.mod h1:9McMC/oCdS4BKwk2shEB7x17P6HmMmA6dQRtAkSnNb8= +github.com/go-openapi/swag/yamlutils v0.25.1 h1:mry5ez8joJwzvMbaTGLhw8pXUnhDK91oSJLDPF1bmGk= +github.com/go-openapi/swag/yamlutils v0.25.1/go.mod h1:cm9ywbzncy3y6uPm/97ysW8+wZ09qsks+9RS8fLWKqg= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= +github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= +github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -434,6 +565,7 @@ github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9 github.com/google/cadvisor v0.49.0/go.mod h1:s6Fqwb2KiWG6leCegVhw4KW40tf9f7m+SF1aXiE8Wsk= github.com/google/cadvisor v0.49.2/go.mod h1:s6Fqwb2KiWG6leCegVhw4KW40tf9f7m+SF1aXiE8Wsk= github.com/google/cadvisor v0.51.0/go.mod h1:czGE/c/P/i0QFpVNKTFrIEzord9Y10YfpwuaSWXELc0= +github.com/google/cadvisor v0.52.1/go.mod h1:OAhPcx1nOm5YwMh/JhpUOMKyv1YKLRtS9KgzWPndHmA= github.com/google/cel-go v0.12.7/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw= github.com/google/cel-go v0.17.7/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= github.com/google/cel-go v0.17.8/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= @@ -441,10 +573,16 @@ github.com/google/cel-go v0.22.0 h1:b3FJZxpiv1vTMo2/5RDUqAHPxkT8mmMfJIrq1llbf7g= github.com/google/cel-go v0.22.0/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= +github.com/google/cel-go v0.24.1 h1:jsBCtxG8mM5wiUJDSGUqU0K7Mtr3w7Eyv00rw4DiZxI= +github.com/google/cel-go v0.24.1/go.mod h1:Hdf9TqOaTNSFQA1ybQaRqATVoK7m/zcf7IMhGXP5zI8= +github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= +github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= 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/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -456,6 +594,8 @@ github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeN github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= github.com/google/go-containerregistry v0.17.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= +github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU= +github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y= github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -474,6 +614,7 @@ github.com/google/pprof v0.0.0-20210226084205-cbba55b83ad5/go.mod h1:kpwsk12EmLe github.com/google/pprof v0.0.0-20210407192527-94a9f03dee38/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= github.com/google/pprof v0.0.0-20240727154555-813a5fbdbec8/go.mod h1:K1liHPHnj73Fdn/EKuT8nrFqBihUSKXoLYU0BuatOYo= github.com/google/pprof v0.0.0-20240827171923-fa2c70bbbfe5/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= +github.com/google/pprof v0.0.0-20250315033105-103756e64e1d/go.mod h1:vavhavw2zAxS5dIdcRluK6cSGGPlZynqzFM8NdvU144= github.com/google/s2a-go v0.1.7/go.mod h1:50CgR4k1jNlWBu4UfS4AcfhVe1r6pdZPygJ3R8F0Qdw= github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM= github.com/google/safetext v0.0.0-20220905092116-b49f7bc46da2 h1:SJ+NtwL6QaZ21U+IrK7d0gGgpjGGvd2kz+FzTHVzdqI= @@ -491,14 +632,20 @@ github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEP github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= 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.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= @@ -507,11 +654,13 @@ github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1: github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg= +github.com/ishidawataru/sctp v0.0.0-20250521072954-ae8eb7fa7995/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -529,6 +678,8 @@ github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 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.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= +github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -543,9 +694,12 @@ github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= +github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= +github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= @@ -556,29 +710,37 @@ github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/ipvs v1.1.0/go.mod h1:4VJMWuf098bsUMmZEiD4Tjk/O7mOn3l1PTD3s4OoYAs= github.com/moby/moby v27.1.1+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= +github.com/onsi/ginkgo/v2 v2.23.0/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/opencontainers/cgroups v0.0.1/go.mod h1:s8lktyhlGUqM7OSRL5P7eAW6Wb+kWPNvt4qvVfzA5vs= github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= github.com/opencontainers/runc v1.2.1/go.mod h1:/PXzF0h531HTMsYQnmxXkBD7YaGShm/2zcRB79dksUc= @@ -590,6 +752,8 @@ github.com/openshift/build-machinery-go v0.0.0-20230824093055-6a18da01283c/go.mo github.com/openshift/build-machinery-go v0.0.0-20240613134303-8359781da660/go.mod h1:8jcm8UPtg2mCAsxfqKil1xrmRMI3a+XU2TZ9fF8A7TE= github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 h1:eTNDkNRNV5lZvUbVM9Nop0lBcljSnA8rZX6yQPZ0ZnU= github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11/go.mod h1:EmVJt97N+pfWFsli/ipXTBZqSG5F5KGQhm3c3IsGq1o= +github.com/openshift/crd-schema-checker v0.0.0-20250905140724-c313b6407231 h1:8lSGufji9rfiyDxtUl7A4uOyeeP4x0UOOXcsDBFfkGI= +github.com/openshift/crd-schema-checker v0.0.0-20250905140724-c313b6407231/go.mod h1:sTxJ4ZFW9r9fEdbW2v0yMRi6NcyTbx0fII4p83IQ+L8= github.com/openshift/generic-admission-server v1.14.1-0.20231020105858-8dcc3c9b298f/go.mod h1:/CLsleDcQ6AFTGKJe9VL3Y4rB9DqX3fQwQv47q2/ZJc= github.com/openshift/generic-admission-server v1.14.1-0.20240926143655-a882ebf9df19/go.mod h1:eNpBvr/3zce6zLOeCtBw48xbCp8SLAmQqu/rb7vFE9Y= github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k= @@ -611,27 +775,39 @@ github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= 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_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= +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.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= +github.com/prometheus/common v0.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyAEN8= +github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= github.com/seccomp/libseccomp-golang v0.10.0/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= @@ -643,16 +819,26 @@ github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkU github.com/spf13/cobra v1.2.0/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= +github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= +github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= +github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= +github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= @@ -662,94 +848,131 @@ github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZM github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI= +github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4= +github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.3.1-0.20250206174618-62fb240731fa/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= +github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= +github.com/vito/go-interact v1.0.2 h1:viJuANio3WH9utUG4rKbJC9V3JR5JgYNS+i0efeA+GU= +github.com/vito/go-interact v1.0.2/go.mod h1:s+y0jK9Z2etBYt5ZM6+DhpOsE5C7NNGC3jrJvW0BBpc= github.com/vmware-tanzu/carvel-imgpkg v0.36.0 h1:ha5a3WUPaqpGlP+QRkKBA9WyT85vUPh7+57x94Cmj58= github.com/vmware-tanzu/carvel-imgpkg v0.36.0/go.mod h1:8HeIt+froyx7iRjyZ/4py2wFMPXEFNyWUNUTQgAjD8M= github.com/vmware-tanzu/carvel-imgpkg v0.38.2/go.mod h1:v9BcO1qfXwwIQFw2zmksdUkx8eI1e+/a0Md3xG2BzDE= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= +github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= +go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.8/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k= go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI= +go.etcd.io/etcd/api/v3 v3.6.4/go.mod h1:eFhhvfR8Px1P6SEuLT600v+vrhdDTdcfMzmnxVXXSbk= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.8/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4= go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U= +go.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v2 v2.305.10/go.mod h1:m3CKZi69HzilhVqtPDcjhSGp+kA1OmbNn0qamH80xjA= go.etcd.io/etcd/client/v2 v2.305.16/go.mod h1:h9YxWCzcdvZENbfzBTFCnoNumr2ax3F19sKMqHFmXHE= go.etcd.io/etcd/client/v3 v3.5.8/go.mod h1:idZYIPVkttBJBiRigkB5EM0MmEyx8jcl18zCV3F5noc= go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc= +go.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo= go.etcd.io/etcd/pkg/v3 v3.5.10/go.mod h1:TKTuCKKcF1zxmfKWDkfz5qqYaE3JncKKZPFf8c1nFUs= go.etcd.io/etcd/pkg/v3 v3.5.16/go.mod h1:+lutCZHG5MBBFI/U4eYT5yL7sJfnexsoM20Y0t2uNuY= +go.etcd.io/etcd/pkg/v3 v3.6.4/go.mod h1:kKcYWP8gHuBRcteyv6MXWSN0+bVMnfgqiHueIZnKMtE= go.etcd.io/etcd/raft/v3 v3.5.10/go.mod h1:odD6kr8XQXTy9oQnyMPBOr0TVe+gT0neQhElQ6jbGRc= go.etcd.io/etcd/raft/v3 v3.5.16/go.mod h1:P4UP14AxofMJ/54boWilabqqWoW9eLodl6I5GdGzazI= go.etcd.io/etcd/server/v3 v3.5.10/go.mod h1:gBplPHfs6YI0L+RpGkTQO7buDbHv5HJGG/Bst0/zIPo= go.etcd.io/etcd/server/v3 v3.5.16/go.mod h1:ynhyZZpdDp1Gq49jkUg5mfkDWZwXnn3eIqCqtJnrD/s= +go.etcd.io/etcd/server/v3 v3.6.4/go.mod h1:aYCL/h43yiONOv0QIR82kH/2xZ7m+IWYjzRmyQfnCAg= +go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= +go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k= go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.42.0/go.mod h1:XiglO+8SPMqM3Mqh5/rtxR1VHc63o8tb38QrU6tm4mU= +go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.44.0/go.mod h1:uq8DrRaen3suIWTpdR/JNHCGpurSvMv9D5Nr5CU5TXc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0/go.mod h1:h8TWwRAhQpOd0aM5nYsRD8+flnkj+526GEIVlarH7eY= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.60.0/go.mod h1:rg+RlpR5dKwaS95IyyZqj5Wd4E13lk/msnTS0Xl9lJM= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.35.1/go.mod h1:9NiG9I2aHTKkcxqCILhjtyNA1QEiCjdBACv4IvrFQ+c= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= go.opentelemetry.io/otel v1.19.0/go.mod h1:i0QyjOq3UPoTzff0PJB2N66fb4S0+rSbSB15/oyH9fY= go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= +go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= +go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0/go.mod h1:Krqnjl22jUJ0HgMzw5eveuCvFDXY4nSYb4F8t5gdrag= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0/go.mod h1:OfUCyyIiDvNXHWpcWgbF+MWvqPZiNa3YDEnivcnYsV0= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= 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/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk= go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= go.opentelemetry.io/otel/metric v0.31.0/go.mod h1:ohmwj9KTSIeBnDBm/ZwH2PSZxZzoOaG2xZeekTRzL5A= go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= go.opentelemetry.io/otel/metric v1.19.0/go.mod h1:L5rUsV9kM1IxCj1MmSdS+JQAcVm319EUrDVLrt7jqt8= go.opentelemetry.io/otel/metric v1.33.0 h1:r+JOocAyeRVXD8lZpjdQjzMadVZp2M4WmQ+5WtEnklQ= go.opentelemetry.io/otel/metric v1.33.0/go.mod h1:L9+Fyctbp6HFTddIxClbQkjtubW6O9QS3Ann/M82u6M= +go.opentelemetry.io/otel/metric v1.38.0 h1:Kl6lzIYGAh5M159u9NgiRkmoMKjvbsKtYRwgfrA6WpA= +go.opentelemetry.io/otel/metric v1.38.0/go.mod h1:kB5n/QoRM8YwmUahxvI3bO34eVtQf2i4utNVLr9gEmI= go.opentelemetry.io/otel/sdk v0.20.0/go.mod h1:g/IcepuwNsoiX5Byy2nNV0ySUF1em498m7hBWC279Yc= go.opentelemetry.io/otel/sdk v1.10.0/go.mod h1:vO06iKzD5baltJz1zarxMCNHFpUlUiOy4s65ECtn6kE= go.opentelemetry.io/otel/sdk v1.19.0 h1:6USY6zH+L8uMH8L3t1enZPR3WFEmSTADlqldyHtJi3o= go.opentelemetry.io/otel/sdk v1.19.0/go.mod h1:NedEbbS4w3C6zElbLdPJKOpJQOrGUJ+GfzpjUvI0v1A= go.opentelemetry.io/otel/sdk v1.24.0/go.mod h1:KVrIYw6tEubO9E96HQpcmpTKDVn9gdv35HoYiQWGDFg= go.opentelemetry.io/otel/sdk v1.33.0/go.mod h1:A1Q5oi7/9XaMlIWzPSxLRWOI8nG3FnzHJNbiENQuihM= +go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5b+E= +go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= +go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM= go.opentelemetry.io/otel/trace v1.19.0 h1:DFVQmlVbfVeOuBRrwdtaehRrWiL1JoVs9CPIQ1Dzxpg= go.opentelemetry.io/otel/trace v1.19.0/go.mod h1:mfaSyvGyEJEI0nyV2I4qhNQnbBOUUmYZpYojqMnX2vo= go.opentelemetry.io/otel/trace v1.33.0 h1:cCJuF7LRjUFso9LPnEAHJDB2pqzp+hbO8eu1qqW2d/s= go.opentelemetry.io/otel/trace v1.33.0/go.mod h1:uIcdVUZMpTAmz0tI1z04GoVSezK37CbGV4fr1f2nBck= +go.opentelemetry.io/otel/trace v1.38.0 h1:Fxk5bKrDZJUH+AMyyIXGcFAPah0oRcT+LuNtJrmcNLE= +go.opentelemetry.io/otel/trace v1.38.0/go.mod h1:j1P9ivuFsTceSWe1oY+EeW3sc+Pp42sO++GHkg4wwhs= go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqeYNgFYFoEGnI= go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -760,6 +983,10 @@ go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -768,6 +995,8 @@ golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq golang.org/x/crypto v0.21.0 h1:X31++rzVUdKhX5sWmSOFZxx8UW/ldWx55cbf08iNAMA= golang.org/x/crypto v0.32.0 h1:euUpcYgM8WcP71gNpTqQCn6rC2t6ULUPiOzfWaXVVfc= golang.org/x/crypto v0.32.0/go.mod h1:ZnnJkOaASj8g0AjIduWNlq2NRxL0PlBrbKVyZ6V/Ugc= +golang.org/x/crypto v0.44.0 h1:A97SsFvM3AIwEEmTBiaxPPTYpDC47w720rdiiUvgoAU= +golang.org/x/crypto v0.44.0/go.mod h1:013i+Nw79BMiQiMsOPcVCB5ZIJbYkerPrGnOa00tvmc= golang.org/x/exp v0.0.0-20190125153040-c74c464bbbf2/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= golang.org/x/exp v0.0.0-20190312203227-4b39c73a6495/go.mod h1:ZjyILWgesfNpC6sMxTJOJm9Kp84zZh5NQWvqDGG3Qr8= golang.org/x/exp v0.0.0-20191129062945-2f5052295587/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= @@ -775,6 +1004,8 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= +golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= +golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -795,6 +1026,7 @@ golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -837,6 +1069,8 @@ golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= 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/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= @@ -850,6 +1084,8 @@ golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= 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/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= +golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -863,6 +1099,8 @@ golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= +golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20190616124812-15dcb6c0061f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -914,11 +1152,15 @@ golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= +golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -930,9 +1172,13 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -982,11 +1228,15 @@ golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0 golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= +golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCYdOS8GPY= gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= @@ -1047,6 +1297,8 @@ google.golang.org/genproto v0.0.0-20210310155132-4ce2db91004e/go.mod h1:FWY/as6D google.golang.org/genproto v0.0.0-20210319143718-93e7006c17a6/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaEmUacj36I+k7YKbEc5CXzPIeORRgDAUOu28A= google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= +google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g= +google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= @@ -1058,6 +1310,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1: google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba h1:B14OtaXuMaCQsl2deSvNkyPKIzq3BjfxQp8d00QyWx4= +google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:G5IanEx8/PgI9w6CFcYQf7jMtHQhZruvfM1i3qOqk5U= google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= google.golang.org/genproto/googleapis/rpc v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:swOH3j0KzcDDgGUWr+SNpyTen5YrXjS3eyPzFYKc6lc= google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7 h1:2035KHhUv+EpyB+hWgJnaWKJOdX1E95w2S8Rr4uWKTs= @@ -1065,6 +1319,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba h1:UKgtfRM7Yh93Sya0Fo8ZzhDP4qBckrrxEr2oF5UIVb8= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= google.golang.org/grpc v1.26.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= @@ -1086,6 +1342,8 @@ google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9Y google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFNg2AA= google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= +google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= +google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= @@ -1101,8 +1359,13 @@ google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWn google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= +gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= @@ -1114,28 +1377,46 @@ gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= +k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= +k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= k8s.io/apiextensions-apiserver v0.27.7/go.mod h1:x0p+b5a955lfPz9gaDeBy43obM12s+N9dNHK6+dUL+g= k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc= k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0= k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw= k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto= +k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= +k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= +k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= +k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= k8s.io/apiserver v0.25.6/go.mod h1:IEp2B2/FvQ8GmdspscUoUS0iFF/GGc6NVrJ/cTM4OaA= k8s.io/apiserver v0.27.7/go.mod h1:OrLG9RwCOerutAlo8QJW5EHzUG9Dad7k6rgcDUNSO/w= k8s.io/apiserver v0.29.0/go.mod h1:31n78PsRKPmfpee7/l9NYEv67u6hOL6AfcE761HapDM= k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4= k8s.io/apiserver v0.32.1 h1:oo0OozRos66WFq87Zc5tclUX2r0mymoVHRq8JmR7Aak= k8s.io/apiserver v0.32.1/go.mod h1:UcB9tWjBY7aryeI5zAgzVJB/6k7E97bkr1RgqDz0jPw= +k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA= +k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0= +k8s.io/cli-runtime v0.34.2 h1:cct1GEuWc3IyVT8MSCoIWzRGw9HJ/C5rgP32H60H6aE= +k8s.io/cli-runtime v0.34.2/go.mod h1:X13tsrYexYUCIq8MarCBy8lrm0k0weFPTpcaNo7lms4= +k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= +k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= k8s.io/code-generator v0.25.6/go.mod h1:aDxzxJynLKQkaa117y0FFcgZ5jG8+GobxZ2JUntmvKk= k8s.io/code-generator v0.27.7/go.mod h1:w1YF/xQcTg+d9Ag+04xuRqER+q8rDnJ70ynLql8/RLA= k8s.io/code-generator v0.30.3/go.mod h1:PFgBiv+miFV7TZYp+RXgROkhA+sWYZ+mtpbMLofMke8= k8s.io/code-generator v0.32.3/go.mod h1:+mbiYID5NLsBuqxjQTygKM/DAdKpAjvBzrJd64NU1G8= +k8s.io/code-generator v0.34.1/go.mod h1:DeWjekbDnJWRwpw3s0Jat87c+e0TgkxoR4ar608yqvg= k8s.io/component-base v0.28.6 h1:G4T8VrcQ7xZou3by/fY5NU5mfxOBlWaivS2lPrEltAo= k8s.io/component-base v0.28.6/go.mod h1:Dg62OOG3ALu2P4nAG00UdsuHoNLQJ5VsUZKQlLDcS+E= k8s.io/component-base v0.29.0/go.mod h1:sADonFTQ9Zc9yFLghpDpmNXEdHyQmFIGbiuZbqAXQ1M= +k8s.io/component-base v0.34.2 h1:HQRqK9x2sSAsd8+R4xxRirlTjowsg6fWCPwWYeSvogQ= +k8s.io/component-base v0.34.2/go.mod h1:9xw2FHJavUHBFpiGkZoKuYZ5pdtLKe97DEByaA+hHbM= k8s.io/component-helpers v0.29.0/go.mod h1:j2coxVfmzTOXWSE6sta0MTgNSr572Dcx68F6DD+8fWc= +k8s.io/component-helpers v0.34.2 h1:RIUGDdU+QFzeVKLZ9f05sXTNAtJrRJ3bnbMLrogCrvM= +k8s.io/component-helpers v0.34.2/go.mod h1:pLi+GByuRTeFjjcezln8gHL7LcT6HImkwVQ3A2SQaEE= +k8s.io/controller-manager v0.33.5/go.mod h1:KuQeAlf4vI2+qj5fwPVLaDlbtrTBA/8L/LqQvI74Ow0= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= @@ -1144,6 +1425,7 @@ k8s.io/gengo v0.0.0-20230829151522-9cce18d56c01/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAE k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= k8s.io/gengo/v2 v2.0.0-20240826214909-a7b603a56eb7/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= +k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= @@ -1155,19 +1437,30 @@ k8s.io/klog/v2 v2.80.1/go.mod h1:y1WjHnz7Dj687irZUWR/WLkLc5N1YHtjLdmgWjndZn0= k8s.io/kms v0.27.7/go.mod h1:JspOc8g6+cDlZfgW5GqnHS+OV6tAVyg4iXytCrqfNPw= k8s.io/kms v0.30.3/go.mod h1:GrMurD0qk3G4yNgGcsCEmepqf9KyyIrTXYR2lyUOJC4= k8s.io/kms v0.32.3/go.mod h1:Bk2evz/Yvk0oVrvm4MvZbgq8BD34Ksxs2SRHn4/UiOM= +k8s.io/kms v0.34.1/go.mod h1:s1CFkLG7w9eaTYvctOxosx88fl4spqmixnNpys0JAtM= k8s.io/kube-aggregator v0.22.17/go.mod h1:J557nueFVurHA1JiDrxT1HlgygNQ+2exsTVUXiz2T7k= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/kubectl v0.34.2 h1:+fWGrVlDONMUmmQLDaGkQ9i91oszjjRAa94cr37hzqA= +k8s.io/kubectl v0.34.2/go.mod h1:X2KTOdtZZNrTWmUD4oHApJ836pevSl+zvC5sI6oO2YQ= k8s.io/kubernetes v1.31.2 h1:VNSu4O7Xn5FFRsh9ePXyEPg6ucR21fOftarSdi053Gs= +k8s.io/kubernetes v1.34.2 h1:WQdDvYJazkmkwSncgNwGvVtaCt4TYXIU3wSMRgvp3MI= +k8s.io/kubernetes v1.34.2/go.mod h1:m6pZk6a179pRo2wsTiCPORJ86iOEQmfIzUvtyEF8BwA= k8s.io/metrics v0.29.0/go.mod h1:UCuTT4dC/x/x6ODSk87IWIZQnuAfcwxOjb1gjWJdjMA= k8s.io/metrics v0.30.3/go.mod h1:W06L2nXRhOwPkFYDJYWdEIS3u6JcJy3ebIPYbndRs6A= k8s.io/metrics v0.32.3/go.mod h1:9R1Wk5cb+qJpCQon9h52mgkVCcFeYxcY+YkumfwHVCU= +k8s.io/metrics v0.34.2/go.mod h1:Ydulln+8uZZctUM8yrUQX4rfq/Ay6UzsuXf24QJ37Vc= k8s.io/system-validators v1.8.0/go.mod h1:gP1Ky+R9wtrSiFbrpEPwWMeYz9yqyy1S/KOh0Vci7WI= k8s.io/system-validators v1.9.1/go.mod h1:d4UVrxKu52s0BHU984Peb9VpIq4V9sd8xjTBV/waY/I= +k8s.io/system-validators v1.10.2/go.mod h1:awfSS706v9R12VC7u7K89FKfqVy44G+E0L1A0FX9Wmw= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= @@ -1183,21 +1476,36 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0/go.mod h1:VHVDI/ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0/go.mod h1:z7+wmGM2dfIiLRfrC6jb5kV2Mq/sK1ZP303cxzkV5Y4= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= sigs.k8s.io/controller-runtime v0.15.3/go.mod h1:kp4jckA4vTx281S/0Yk2LFEEQe67mjg+ev/yknv47Ds= +sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= +sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= sigs.k8s.io/controller-tools v0.7.0/go.mod h1:bpBAo0VcSDDLuWt47evLhMLPxRPxMDInTEH/YbdeMK0= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/kind v0.30.0 h1:2Xi1KFEfSMm0XDcvKnUt15ZfgRPCT0OnCBbpgh8DztY= +sigs.k8s.io/kind v0.30.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= sigs.k8s.io/knftables v0.0.14/go.mod h1:f/5ZLKYEUPUhVjUCg6l80ACdL7CIIyeL0DxfgojGRTk= sigs.k8s.io/knftables v0.0.17/go.mod h1:f/5ZLKYEUPUhVjUCg6l80ACdL7CIIyeL0DxfgojGRTk= sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U= +sigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM= sigs.k8s.io/kustomize/kustomize/v5 v5.0.4-0.20230601165947-6ce0bf390ce3/go.mod h1:/d88dHCvoy7d0AKFT0yytezSGZKjsZBVs9YTkBHSGFk= sigs.k8s.io/kustomize/kustomize/v5 v5.5.0/go.mod h1:AeFCmgCrXzmvjWWaeZCyBp6XzG1Y0w1svYus8GhJEOE= +sigs.k8s.io/kustomize/kustomize/v5 v5.7.1/go.mod h1:+5/SrBcJ4agx1SJknGuR/c9thwRSKLxnKoI5BzXFaLU= sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag= sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo= +sigs.k8s.io/kustomize/kyaml v0.20.1/go.mod h1:0EmkQHRUsJxY8Ug9Niig1pUMSCGHxQ5RklbpV/Ri6po= sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:wWxsB5ozmmv/SG7nM11ayaAW51xMvak/t1r0CSlcokI= sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= From 6a84f6011ccf0a8f8748ce2f576663ca6e542e28 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Mon, 19 Jan 2026 17:31:54 +0100 Subject: [PATCH 02/24] Refactored workshop_xxx and tunnel_xxx commands --- client-programs/go.mod | 102 +- client-programs/go.sum | 728 +++++++----- .../pkg/cmd/cluster_workshop_update_cmd.go | 5 +- client-programs/pkg/cmd/tunnel_connect_cmd.go | 93 +- .../pkg/cmd/workshop_export_cmd.go | 102 +- client-programs/pkg/cmd/workshop_new_cmd.go | 22 +- .../pkg/cmd/workshop_publish_cmd.go | 307 +---- .../pkg/docker/workshop_manager.go | 1007 +++++++++++++++++ client-programs/pkg/tunnel/tunnel.go | 104 ++ client-programs/pkg/utils/cmd_error.go | 23 + client-programs/pkg/workshops/constants.go | 3 + client-programs/pkg/workshops/definition.go | 232 ++++ client-programs/pkg/workshops/manager.go | 372 ++++++ go.work.sum | 384 +++---- 14 files changed, 2476 insertions(+), 1008 deletions(-) create mode 100644 client-programs/pkg/docker/workshop_manager.go create mode 100644 client-programs/pkg/tunnel/tunnel.go create mode 100644 client-programs/pkg/utils/cmd_error.go create mode 100644 client-programs/pkg/workshops/constants.go create mode 100644 client-programs/pkg/workshops/definition.go create mode 100644 client-programs/pkg/workshops/manager.go diff --git a/client-programs/go.mod b/client-programs/go.mod index 56b20c822..1d88773a5 100644 --- a/client-programs/go.mod +++ b/client-programs/go.mod @@ -15,7 +15,7 @@ require ( carvel.dev/vendir v0.44.0 carvel.dev/ytt v0.52.1 github.com/adrg/xdg v0.5.3 - github.com/compose-spec/compose-go/v2 v2.9.1 + github.com/compose-spec/compose-go/v2 v2.10.0 github.com/cppforlife/go-cli-ui v0.0.0-20250603184554-47874c9078ad // Every time we update below version, we need to update Docker Desktop client to match the required version // or else downgrade CLI support via export DOCKER_API_VERSION=1.xx @@ -27,7 +27,7 @@ require ( github.com/joho/godotenv v1.5.1 github.com/mitchellh/go-homedir v1.1.0 github.com/pkg/errors v0.9.1 - github.com/spf13/cobra v1.10.1 + github.com/spf13/cobra v1.10.2 golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.34.2 @@ -42,6 +42,12 @@ require ( sigs.k8s.io/yaml v1.6.0 ) +require ( + github.com/docker/cli v29.0.0+incompatible + github.com/docker/compose/v5 v5.0.1 + go.yaml.in/yaml/v2 v2.4.3 +) + require ( al.essio.dev/pkg/shellescape v1.6.0 // indirect cel.dev/expr v0.25.1 // indirect @@ -57,9 +63,11 @@ require ( github.com/Azure/go-autorest/logger v0.2.2 // indirect github.com/Azure/go-autorest/tracing v0.6.1 // indirect github.com/BurntSushi/toml v1.5.0 // indirect + github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e // indirect github.com/MakeNowJust/heredoc v1.0.0 // indirect github.com/Microsoft/go-winio v0.6.2 // indirect github.com/VividCortex/ewma v1.2.0 // indirect + github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/aws/aws-sdk-go-v2 v1.39.6 // indirect github.com/aws/aws-sdk-go-v2/config v1.31.19 // indirect @@ -80,6 +88,7 @@ require ( github.com/beorn7/perks v1.0.1 // indirect github.com/blang/semver/v4 v4.0.0 // indirect github.com/bmatcuk/doublestar v1.3.4 // indirect + github.com/buger/goterm v1.0.4 // indirect github.com/carvel-dev/semver/v4 v4.0.1-0.20240402203627-beb83fbf25e4 // indirect github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cespare/xxhash/v2 v2.3.0 // indirect @@ -87,23 +96,34 @@ require ( github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb27589 // indirect github.com/clipperhouse/stringish v0.1.1 // indirect github.com/clipperhouse/uax29/v2 v2.3.0 // indirect + github.com/containerd/console v1.0.5 // indirect + github.com/containerd/containerd/api v1.10.0 // indirect + github.com/containerd/containerd/v2 v2.2.1-0.20251115011841-efd86f2b0bc2 // indirect + github.com/containerd/continuity v0.4.5 // indirect github.com/containerd/errdefs v1.0.0 // indirect github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v1.0.0-rc.2 // indirect github.com/containerd/stargz-snapshotter/estargz v0.18.1 // indirect + github.com/containerd/ttrpc v1.2.7 // indirect + github.com/containerd/typeurl/v2 v2.2.3 // indirect github.com/cppforlife/cobrautil v0.0.0-20221130162803-acdfead391ef // indirect github.com/cppforlife/color v1.9.1-0.20200716202919-6706ac40b835 // indirect github.com/cppforlife/go-patch v0.0.0-20240118020416-2147782e467b // indirect github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect github.com/dimchansky/utfbom v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect - github.com/docker/cli v29.0.0+incompatible // indirect + github.com/docker/buildx v0.30.1 // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.4 // indirect github.com/docker/go-units v0.5.0 // indirect + github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 // indirect github.com/emicklei/go-restful/v3 v3.13.0 // indirect github.com/evanphx/json-patch/v5 v5.9.11 // indirect github.com/fatih/color v1.18.0 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/fsnotify/fsevents v0.2.0 // indirect + github.com/fvbommel/sortorder v1.1.0 // indirect github.com/fxamacker/cbor/v2 v2.9.0 // indirect github.com/go-logr/stdr v1.2.2 // indirect github.com/go-openapi/jsonpointer v0.22.1 // indirect @@ -120,19 +140,28 @@ require ( github.com/go-openapi/swag/stringutils v0.25.1 // indirect github.com/go-openapi/swag/typeutils v0.25.1 // indirect github.com/go-openapi/swag/yamlutils v0.25.1 // indirect + github.com/go-viper/mapstructure/v2 v2.4.0 // indirect + github.com/gofrs/flock v0.13.0 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang-jwt/jwt/v4 v4.5.2 // indirect + github.com/golang-jwt/jwt/v5 v5.3.0 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/cel-go v0.26.1 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-containerregistry v0.20.6 // indirect github.com/google/go-github v17.0.0+incompatible // indirect github.com/google/go-querystring v1.1.0 // indirect + github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect - github.com/hashicorp/go-version v1.7.0 // indirect - github.com/imdario/mergo v0.3.16 // indirect + github.com/hashicorp/errwrap v1.1.0 // indirect + github.com/hashicorp/go-multierror v1.1.1 // indirect + github.com/hashicorp/go-version v1.8.0 // indirect + github.com/in-toto/in-toto-golang v0.9.0 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect + github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf // indirect + github.com/jonboulle/clockwork v0.5.0 // indirect github.com/json-iterator/go v1.1.12 // indirect github.com/k14s/difflib v0.0.0-20240118055029-596a7a5585c3 // indirect github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368 // indirect @@ -144,13 +173,26 @@ require ( github.com/mattn/go-runewidth v0.0.19 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect - github.com/mitchellh/mapstructure v1.5.0 // indirect + github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect + github.com/moby/buildkit v0.26.3 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/go-archive v0.1.0 // indirect + github.com/moby/locker v1.0.1 // indirect + github.com/moby/moby/api v1.52.0 // indirect + github.com/moby/moby/client v0.2.1 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/spdystream v0.5.0 // indirect + github.com/moby/sys/atomicwriter v0.1.0 // indirect + github.com/moby/sys/capability v0.4.0 // indirect github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/signal v0.7.1 // indirect + github.com/moby/sys/symlink v0.3.0 // indirect + github.com/moby/sys/user v0.4.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect github.com/moby/term v0.5.2 // indirect github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee // indirect + github.com/morikuni/aec v1.1.0 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/opencontainers/go-digest v1.0.0 // indirect @@ -159,47 +201,64 @@ require ( github.com/otiai10/copy v1.14.1 // indirect github.com/otiai10/mint v1.6.3 // indirect github.com/pelletier/go-toml v1.9.5 // indirect + github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 // indirect github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect github.com/prometheus/client_golang v1.23.2 // indirect github.com/prometheus/client_model v0.6.2 // indirect github.com/prometheus/common v0.67.2 // indirect github.com/prometheus/procfs v0.19.2 // indirect github.com/russross/blackfriday/v2 v2.1.0 // indirect + github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 // indirect + github.com/secure-systems-lab/go-securesystemslib v0.9.1 // indirect + github.com/shibumi/go-pathspec v1.3.0 // indirect github.com/sirupsen/logrus v1.9.3 // indirect + github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/spf13/pflag v1.0.10 // indirect github.com/stoewer/go-strcase v1.3.1 // indirect + github.com/stretchr/testify v1.11.1 // indirect + github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 // indirect + github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect + github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f // indirect + github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 // indirect + github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea // indirect + github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab // indirect github.com/vbatts/tar-split v0.12.2 // indirect github.com/vito/go-interact v1.0.2 // indirect github.com/vmware-tanzu/carvel-kapp-controller v0.51.3 // indirect github.com/x448/float16 v0.8.4 // indirect - github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb // indirect - github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 // indirect - github.com/xeipuuv/gojsonschema v1.2.0 // indirect + github.com/xhit/go-str2duration/v2 v2.1.0 // indirect go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0 // indirect go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 // indirect - go.opentelemetry.io/otel v1.38.0 // indirect + go.opentelemetry.io/otel v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 // indirect go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 // indirect - go.opentelemetry.io/otel/metric v1.38.0 // indirect - go.opentelemetry.io/otel/sdk v1.38.0 // indirect - go.opentelemetry.io/otel/trace v1.38.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 // indirect + go.opentelemetry.io/otel/metric v1.39.0 // indirect + go.opentelemetry.io/otel/sdk v1.39.0 // indirect + go.opentelemetry.io/otel/sdk/metric v1.39.0 // indirect + go.opentelemetry.io/otel/trace v1.39.0 // indirect go.opentelemetry.io/proto/otlp v1.9.0 // indirect - go.yaml.in/yaml/v2 v2.4.3 // indirect go.yaml.in/yaml/v3 v3.0.4 // indirect - golang.org/x/crypto v0.44.0 // indirect + go.yaml.in/yaml/v4 v4.0.0-rc.3 // indirect + golang.org/x/crypto v0.45.0 // indirect golang.org/x/net v0.47.0 // indirect golang.org/x/oauth2 v0.33.0 // indirect - golang.org/x/sync v0.18.0 // indirect - golang.org/x/sys v0.38.0 // indirect + golang.org/x/sync v0.19.0 // indirect + golang.org/x/sys v0.39.0 // indirect golang.org/x/term v0.37.0 // indirect golang.org/x/text v0.31.0 // indirect golang.org/x/time v0.14.0 // indirect - google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba // indirect - google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba // indirect - google.golang.org/grpc v1.76.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 // indirect + google.golang.org/grpc v1.77.0 // indirect google.golang.org/protobuf v1.36.10 // indirect gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect + gopkg.in/ini.v1 v1.67.0 // indirect gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.34.1 // indirect @@ -213,4 +272,5 @@ require ( sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect -) \ No newline at end of file + tags.cncf.io/container-device-interface v1.1.0 // indirect +) diff --git a/client-programs/go.sum b/client-programs/go.sum index f37313eb1..fe2370772 100644 --- a/client-programs/go.sum +++ b/client-programs/go.sum @@ -1,21 +1,21 @@ al.essio.dev/pkg/shellescape v1.6.0 h1:NxFcEqzFSEVCGN2yq7Huv/9hyCEGVa/TncnOOBBeXHA= al.essio.dev/pkg/shellescape v1.6.0/go.mod h1:6sIqp7X2P6mThCQ7twERpZTuigpr6KbZWtls1U8I890= -carvel.dev/imgpkg v0.44.2 h1:Hmk84hy4RIGvmxUMA+qM2LI/i9toDQJ+V9eGcomEQXU= -carvel.dev/imgpkg v0.44.2/go.mod h1:GyalpFa3gpD7z0X2IYnedUKUDjOuFYnjftZkhDl9j2I= -carvel.dev/kapp v0.64.0 h1:WeQ8XkccOonye7sCxOJnukKgRhWtHGDlt4tY4aFIMJM= -carvel.dev/kapp v0.64.0/go.mod h1:6DoB9+JP27u4ZZbolK7ObmS1vhaVoOVrfqX1pj0Z6MQ= -carvel.dev/kbld v0.45.0 h1:auEiQ6cI/WCg/IdVz4PuvDE8Mku+3QDeIcxNjQwx0rw= -carvel.dev/kbld v0.45.0/go.mod h1:c423olB3YbSfDFOV481uhXZwYdXAZcLWiRaKny5WfUE= -carvel.dev/vendir v0.43.0 h1:TEqLRQK4S/4rzyJEKB+ULrkS1BQSGUG5ZucBFCbZFxI= -carvel.dev/vendir v0.43.0/go.mod h1:pK7tOW8jwz2CdwL4VirKwu4G7FoOn5HQ38++T0I0E/I= -carvel.dev/ytt v0.51.1 h1:XWM+9tA+rJnhSel3dJf1jgrCES2wHw68cclkQhZcNRM= -carvel.dev/ytt v0.51.1/go.mod h1:bvN7TWCAHhpPgdulWTQ+gi80aR6TOZ1PJPOEHcKtvQY= -cel.dev/expr v0.22.0 h1:+hFFhLPmquBImfs1BiN2PZmkr5ASse2ZOuaxIs9e4R8= -cel.dev/expr v0.22.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= -cloud.google.com/go/compute v1.34.1 h1:scKcg/w9cg+/LFXraAKsFKXihYsyFUBvBJ2igv22VRE= -cloud.google.com/go/compute v1.34.1/go.mod h1:892Xcmz3MHG7wmiKt4UvRm9t5attc0WZi1JIl57xza0= -cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= -cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +carvel.dev/imgpkg v0.46.1 h1:UOYaPllQJRsbzSl61IiNvmDZA5z4951i/KaSROAC1W0= +carvel.dev/imgpkg v0.46.1/go.mod h1:Q1E+7tpoiPbVNjb7HSmLZP7E1j0w6mWFzDarOXW1HiI= +carvel.dev/kapp v0.64.2 h1:dJhtWVOkvPPgcS0f5A4OtOlrGie9gHvabtZBvB/h0+M= +carvel.dev/kapp v0.64.2/go.mod h1:5t0pWQzyoY9SzPVqrqgYhTlzgsuyMy+bvFdmrvtbDJw= +carvel.dev/kbld v0.46.0 h1:khSHTH3yiEE8imE9K245ZT67ZToixa1nC1938Oje1O4= +carvel.dev/kbld v0.46.0/go.mod h1:wmUYbnw0di759Id26P6dtRW59cBHy4UT9/FJgthiJ0I= +carvel.dev/vendir v0.44.0 h1:vfq5KgGbbLlxHrE0prY7gZgiEQpjwo4lS2akCaVkcxA= +carvel.dev/vendir v0.44.0/go.mod h1:gslrJ0HPiy8gtJYsQZHzIVuGfOG0nfDKDupEm7uBWVQ= +carvel.dev/ytt v0.52.1 h1:I9rCwIunzClas2MH5nVGtCK5ujZdiGaqAfGol/wiRKQ= +carvel.dev/ytt v0.52.1/go.mod h1:lzkMguCvSVvxT2My9RG3gRMgTws97NpNXufKZ6iiP5E= +cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= +cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= +cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible h1:fcYLmCpyNYRnvJbPerq7U0hS+6+I79yEDJBqVNcqUzU= github.com/Azure/azure-sdk-for-go v68.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg= @@ -46,64 +46,72 @@ github.com/Azure/go-autorest/logger v0.2.2/go.mod h1:I5fg9K52o+iuydlWfa9T5K6WFos github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-autorest/tracing v0.6.1 h1:YUMSrC/CeD1ZnnXcNYU4a/fzsO35u2Fsful9L/2nyR0= github.com/Azure/go-autorest/tracing v0.6.1/go.mod h1:/3EgjbsjraOqiicERAeu3m7/z0x1TzjQGAwDrJrXGkc= -github.com/BurntSushi/toml v1.4.0 h1:kuoIxZQy2WRRk1pttg9asf+WVv6tWQuBNVmK8+nqPr0= -github.com/BurntSushi/toml v1.4.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= +github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= +github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e h1:rd4bOvKmDIx0WeTv9Qz+hghsgyjikFiPrseXHlKepO0= +github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e/go.mod h1:blbwPQh4DTlCZEfk1BLU4oMIhLda2U+A840Uag9DsZw= github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ= github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE= github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/Microsoft/hcsshim v0.14.0-rc.1 h1:qAPXKwGOkVn8LlqgBN8GS0bxZ83hOJpcjxzmlQKxKsQ= +github.com/Microsoft/hcsshim v0.14.0-rc.1/go.mod h1:hTKFGbnDtQb1wHiOWv4v0eN+7boSWAHyK/tNAaYZL0c= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= +github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc= +github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= 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/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= -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/aws/aws-sdk-go-v2 v1.36.3 h1:mJoei2CxPutQVxaATCzDUjcZEjVRdpsiiXi2o38yqWM= -github.com/aws/aws-sdk-go-v2 v1.36.3/go.mod h1:LLXuLpgzEbD766Z5ECcRmi8AzSwfZItDtmABVkRLGzg= -github.com/aws/aws-sdk-go-v2/config v1.29.9 h1:Kg+fAYNaJeGXp1vmjtidss8O2uXIsXwaRqsQJKXVr+0= -github.com/aws/aws-sdk-go-v2/config v1.29.9/go.mod h1:oU3jj2O53kgOU4TXq/yipt6ryiooYjlkqqVaZk7gY/U= -github.com/aws/aws-sdk-go-v2/credentials v1.17.62 h1:fvtQY3zFzYJ9CfixuAQ96IxDrBajbBWGqjNTCa79ocU= -github.com/aws/aws-sdk-go-v2/credentials v1.17.62/go.mod h1:ElETBxIQqcxej++Cs8GyPBbgMys5DgQPTwo7cUPDKt8= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30 h1:x793wxmUWVDhshP8WW2mlnXuFrO4cOd3HLBroh1paFw= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.30/go.mod h1:Jpne2tDnYiFascUEs2AWHJL9Yp7A5ZVy3TNyxaAjD6M= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34 h1:ZK5jHhnrioRkUNOc+hOgQKlUL5JeC3S6JgLxtQ+Rm0Q= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.34/go.mod h1:p4VfIceZokChbA9FzMbRGz5OV+lekcVtHlPKEO0gSZY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34 h1:SZwFm17ZUNNg5Np0ioo/gq8Mn6u9w19Mri8DnJ15Jf0= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.34/go.mod h1:dFZsC0BLo346mvKQLWmoJxT+Sjp+qcVR1tRVHQGOH9Q= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3 h1:bIqFDwgGXXN1Kpp99pDOdKMTTb5d2KyU5X/BZxjOkRo= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.3/go.mod h1:H5O/EsxDWyU+LP/V8i5sm8cxoZgc2fdNR9bxlOFrQTo= -github.com/aws/aws-sdk-go-v2/service/ecr v1.43.0 h1:Ak4Ggvvbg8WYxPLoyLOtes1cIMQePvCAi/dUGqm8hOY= -github.com/aws/aws-sdk-go-v2/service/ecr v1.43.0/go.mod h1:iQ1skgw1XRK+6Lgkb0I9ODatAP72WoTILh0zXQ5DtbU= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.32.1 h1:Tgvq4EuB0AcM2Au4kOmEHt/sm2BSwKrOzsM402g3iwg= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.32.1/go.mod h1:RZL7ov7c72wSmoM8bIiVxRHgcVdzhNkVW2J36C8RF4s= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3 h1:eAh2A4b5IzM/lum78bZ590jy36+d/aFLgKF/4Vd1xPE= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.3/go.mod h1:0yKJC/kb8sAnmlYa6Zs3QVYqaC8ug2AbnNChv5Ox3uA= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15 h1:dM9/92u2F1JbDaGooxTq18wmmFzbJRfXfVfy96/1CXM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.15/go.mod h1:SwFBy2vjtA0vZbjjaFtfN045boopadnoVPhu4Fv66vY= -github.com/aws/aws-sdk-go-v2/service/sso v1.25.1 h1:8JdC7Gr9NROg1Rusk25IcZeTO59zLxsKgE0gkh5O6h0= -github.com/aws/aws-sdk-go-v2/service/sso v1.25.1/go.mod h1:qs4a9T5EMLl/Cajiw2TcbNt2UNo/Hqlyp+GiuG4CFDI= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1 h1:KwuLovgQPcdjNMfFt9OhUd9a2OwcOKhxfvF4glTzLuA= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.29.1/go.mod h1:MlYRNmYu/fGPoxBQVvBYr9nyr948aY/WLUvwBMBJubs= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.17 h1:PZV5W8yk4OtH1JAuhV2PXwwO9v5G5Aoj+eMCn4T+1Kc= -github.com/aws/aws-sdk-go-v2/service/sts v1.33.17/go.mod h1:cQnB8CUnxbMU82JvlqjKR2HBOm3fe9pWorWBza6MBJ4= -github.com/aws/smithy-go v1.22.3 h1:Z//5NuZCSW6R4PhQ93hShNbyBbn8BWCmCVCt+Q8Io5k= -github.com/aws/smithy-go v1.22.3/go.mod h1:t1ufH5HMublsJYulve2RKmHDC15xu1f26kHCp/HgceI= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.9.1 h1:50sS0RWhGpW/yZx2KcDNEb1u1MANv5BMEkJgcieEDTA= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.9.1/go.mod h1:ErZOtbzuHabipRTDTor0inoRlYwbsV1ovwSxjGs/uJo= +github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk= +github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= +github.com/aws/aws-sdk-go-v2/config v1.31.19 h1:qdUtOw4JhZr2YcKO3g0ho/IcFXfXrrb8xlX05Y6EvSw= +github.com/aws/aws-sdk-go-v2/config v1.31.19/go.mod h1:tMJ8bur01t8eEm0atLadkIIFA154OJ4JCKZeQ+o+R7k= +github.com/aws/aws-sdk-go-v2/credentials v1.18.23 h1:IQILcxVgMO2BVLaJ2aAv21dKWvE1MduNrbvuK43XL2Q= +github.com/aws/aws-sdk-go-v2/credentials v1.18.23/go.mod h1:JRodHszhVdh5TPUknxDzJzrMiznG+M+FfR3WSWKgCI8= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk= +github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40= +github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY= +github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= +github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= +github.com/aws/aws-sdk-go-v2/service/ecr v1.51.3 h1:+0AhrMCsfRxzlojjbJBOOBO1Ka5t1VsF28g+eHYbyEI= +github.com/aws/aws-sdk-go-v2/service/ecr v1.51.3/go.mod h1:1NVD1KuMjH2GqnPwMotPndQaT/MreKkWpjkF12d6oKU= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.3 h1:2Mfho1EDuk815vcGZbiGsOY6mMGPMCsJTx2dWZdWudI= +github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.3/go.mod h1:x7gU4CAyAz4BsM9hlRkhHiYw2GIr1QCmN45uwQw9l/E= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o= +github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM= +github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.2 h1:/p6MxkbQoCzaGQT3WO0JwG0FlQyG9RD8VmdmoKc5xqU= +github.com/aws/aws-sdk-go-v2/service/sso v1.30.2/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.6 h1:0dES42T2dhICCbVB3JSTTn7+Bz93wfJEK1b7jksZIyQ= +github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.6/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo= +github.com/aws/aws-sdk-go-v2/service/sts v1.40.1 h1:5sbIM57lHLaEaNWdIx23JH30LNBsSDkjN/QXGcRLAFc= +github.com/aws/aws-sdk-go-v2/service/sts v1.40.1/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk= +github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= +github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= +github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.11.0 h1:GOPttfOAf5qAgx7r6b+zCWZrvCsfKffkL4H6mSYx1kA= +github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.11.0/go.mod h1:a2HN6+p7k0JLDO8514sMr0l4cnrR52z4sWoZ/Uc82ho= 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= github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= +github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY= +github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= github.com/carvel-dev/semver/v4 v4.0.1-0.20240402203627-beb83fbf25e4 h1:F4rZiMGZyC66j9VB7doVOE4tFHF1yNEihQlOuht4jmM= github.com/carvel-dev/semver/v4 v4.0.1-0.20240402203627-beb83fbf25e4/go.mod h1:4cFTBLAr/U11ykiEEQMccu4uJ1i0GS+atJmeETHCFtI= -github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= -github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= 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/cheggaaa/pb/v3 v3.1.7 h1:2FsIW307kt7A/rz/ZI2lvPO+v3wKazzE4K/0LtTWsOI= @@ -113,28 +121,59 @@ github.com/chrismellard/docker-credential-acr-env v0.0.0-20230304212654-82a0ddb2 github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= -github.com/compose-spec/compose-go v1.20.2 h1:u/yfZHn4EaHGdidrZycWpxXgFffjYULlTbRfJ51ykjQ= -github.com/compose-spec/compose-go v1.20.2/go.mod h1:+MdqXV4RA7wdFsahh/Kb8U0pAJqkg7mr4PM9tFKU8RM= +github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= +github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= +github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= +github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= +github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= +github.com/compose-spec/compose-go/v2 v2.10.0 h1:K2C5LQ3KXvkYpy5N/SG6kIYB90iiAirA9btoTh/gB0Y= +github.com/compose-spec/compose-go/v2 v2.10.0/go.mod h1:Ohac1SzhO/4fXXrzWIztIVB6ckmKBv1Nt5Z5mGVESUg= +github.com/containerd/cgroups/v3 v3.1.0 h1:azxYVj+91ZgSnIBp2eI3k9y2iYQSR/ZQIgh9vKO+HSY= +github.com/containerd/cgroups/v3 v3.1.0/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= +github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= +github.com/containerd/console v1.0.5/go.mod h1:YynlIjWYF8myEu6sdkwKIvGQq+cOckRm6So2avqoYAk= +github.com/containerd/containerd/api v1.10.0 h1:5n0oHYVBwN4VhoX9fFykCV9dF1/BvAXeg2F8W6UYq1o= +github.com/containerd/containerd/api v1.10.0/go.mod h1:NBm1OAk8ZL+LG8R0ceObGxT5hbUYj7CzTmR3xh0DlMM= +github.com/containerd/containerd/v2 v2.2.1-0.20251115011841-efd86f2b0bc2 h1:WcvXNS/OmpiitTVdzRAudKwvShKxcOP4Elf2FyxSoTg= +github.com/containerd/containerd/v2 v2.2.1-0.20251115011841-efd86f2b0bc2/go.mod h1:YCMjKjA4ZA7egdHNi3/93bJR1+2oniYlnS+c0N62HdE= +github.com/containerd/continuity v0.4.5 h1:ZRoN1sXq9u7V6QoHMcVWGhOwDFqZ4B9i5H6un1Wh0x4= +github.com/containerd/continuity v0.4.5/go.mod h1:/lNJvtJKUQStBzpVQ1+rasXO1LAWtUQssk28EZvJ3nE= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/fifo v1.1.0 h1:4I2mbh5stb1u6ycIABlBw9zgtlK8viPI9QkQNRQEEmY= +github.com/containerd/fifo v1.1.0/go.mod h1:bmC4NWMbXlt2EZ0Hc7Fx7QzTFxgPID13eH0Qu+MAb2o= github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= -github.com/containerd/stargz-snapshotter/estargz v0.16.3 h1:7evrXtoh1mSbGj/pfRccTampEyKpjpOnS3CyiV1Ebr8= -github.com/containerd/stargz-snapshotter/estargz v0.16.3/go.mod h1:uyr4BfYfOj3G9WBVE8cOlQmXAbPN9VEQpBBeJIuOipU= +github.com/containerd/nydus-snapshotter v0.15.4 h1:l59kGRVMtwMLDLh322HsWhEsBCkRKMkGWYV5vBeLYCE= +github.com/containerd/nydus-snapshotter v0.15.4/go.mod h1:eRJqnxQDr48HNop15kZdLZpFF5B6vf6Q11Aq1K0E4Ms= +github.com/containerd/platforms v1.0.0-rc.2 h1:0SPgaNZPVWGEi4grZdV8VRYQn78y+nm6acgLGv/QzE4= +github.com/containerd/platforms v1.0.0-rc.2/go.mod h1:J71L7B+aiM5SdIEqmd9wp6THLVRzJGXfNuWCZCllLA4= +github.com/containerd/plugin v1.0.0 h1:c8Kf1TNl6+e2TtMHZt+39yAPDbouRH9WAToRjex483Y= +github.com/containerd/plugin v1.0.0/go.mod h1:hQfJe5nmWfImiqT1q8Si3jLv3ynMUIBB47bQ+KexvO8= +github.com/containerd/stargz-snapshotter/estargz v0.18.1 h1:cy2/lpgBXDA3cDKSyEfNOFMA/c10O1axL69EU7iirO8= +github.com/containerd/stargz-snapshotter/estargz v0.18.1/go.mod h1:ALIEqa7B6oVDsrF37GkGN20SuvG/pIMm7FwP7ZmRb0Q= +github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRqQ= +github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= +github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= +github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= 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/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo= +github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU= github.com/cppforlife/cobrautil v0.0.0-20221130162803-acdfead391ef h1:de10GNLe45JTMghl2qf9WH17H/BjGShK41X3vKAsPJA= github.com/cppforlife/cobrautil v0.0.0-20221130162803-acdfead391ef/go.mod h1:2w+qxVu2KSGW78Ex/XaIqfh/OvBgjEsmN53S4T8vEyA= github.com/cppforlife/color v1.9.1-0.20200716202919-6706ac40b835 h1:mYQweUIBD+TBRjIeQnJmXr0GSVMpI6O0takyb/aaOgo= github.com/cppforlife/color v1.9.1-0.20200716202919-6706ac40b835/go.mod h1:dYeVsKp1vvK8XjdTPR1gF+uk+9doxKeO3hqQTOCr7T4= -github.com/cppforlife/go-cli-ui v0.0.0-20220622150351-995494831c6c h1:Au0iPWQ6E8TMil9HiGW/DI4CBttUpBOZWkzgqwq+PLg= -github.com/cppforlife/go-cli-ui v0.0.0-20220622150351-995494831c6c/go.mod h1:ci7nWkU0g40w486NlpUpXXpTD3pkOBjH090Uc0wpER4= +github.com/cppforlife/go-cli-ui v0.0.0-20250603184554-47874c9078ad h1:PaYhzcFC4VCmlBNWLshK0VxWJyb5J+AdnrwR6hnfe+A= +github.com/cppforlife/go-cli-ui v0.0.0-20250603184554-47874c9078ad/go.mod h1:xZhzUOhCF76o47bEulESCNzmvP4xbwRFUSpN62Zu9FI= github.com/cppforlife/go-patch v0.0.0-20240118020416-2147782e467b h1:+8LQctLhaj+63L/37l8IK/5Q3odN6RzWlglonUwrKok= github.com/cppforlife/go-patch v0.0.0-20240118020416-2147782e467b/go.mod h1:67a7aIi94FHDZdoeGSJRRFDp66l9MhaAG1yGxpUoFD8= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= -github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= -github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= +github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= 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= @@ -143,45 +182,87 @@ github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= -github.com/docker/cli v28.0.1+incompatible h1:g0h5NQNda3/CxIsaZfH4Tyf6vpxFth7PYl3hgCPOKzs= -github.com/docker/cli v28.0.1+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxKI= +github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= +github.com/docker/buildx v0.30.1 h1:3vthfaTQOLt5QfN2nl7rKuPLUvx69nL5ZikFIXp//c8= +github.com/docker/buildx v0.30.1/go.mod h1:8nwT0V6UNYNo9rXq6WO/BQd9KrJ0JYcY/QX6x0y1Oro= +github.com/docker/cli v29.0.0+incompatible h1:KgsN2RUFMNM8wChxryicn4p46BdQWpXOA1XLGBGPGAw= +github.com/docker/cli v29.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/compose/v5 v5.0.1 h1:5yCjDJbwUqcuI+6WNFHNWz2/3vyBDsNnfe8LlFjyxEc= +github.com/docker/compose/v5 v5.0.1/go.mod h1:vuKBtnRuvsVIlYHzdPkF3SToljqR+pFJseO5lDBuF18= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= -github.com/docker/docker v27.5.1+incompatible h1:4PYU5dnBYqRQi0294d1FBECqT9ECWeQAIfE8q4YnPY8= -github.com/docker/docker v27.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker-credential-helpers v0.9.3 h1:gAm/VtF9wgqJMoxzT3Gj5p4AqIjCBS4wrsOh9yRqcz8= -github.com/docker/docker-credential-helpers v0.9.3/go.mod h1:x+4Gbw9aGmChi3qTLZj8Dfn0TD20M/fuWy0E5+WDeCo= -github.com/docker/go-connections v0.5.0 h1:USnMq7hx7gwdVZq1L49hLXaFtUdTADjXGp+uj1Br63c= -github.com/docker/go-connections v0.5.0/go.mod h1:ov60Kzw0kKElRwhNs9UlUHAE/F9Fe6GLaXnqyDdmEXc= +github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= +github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/docker-credential-helpers v0.9.4 h1:76ItO69/AP/V4yT9V4uuuItG0B1N8hvt0T0c0NN/DzI= +github.com/docker/docker-credential-helpers v0.9.4/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= +github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= -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/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg= +github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg= +github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= +github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= github.com/fatih/color v1.18.0/go.mod h1:4FelSpRwEGDpQ12mAdzqdOukCy4u8WUtOY6lkT/6HfU= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= -github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= -github.com/fsnotify/fsnotify v1.4.9/go.mod h1:znqG4EE+3YCdAaPaxE2ZRY/06pZUdp0tY4IgpuI1SZQ= -github.com/fsnotify/fsnotify v1.7.0 h1:8JEhPFa5W2WU7YfeZzPNqzMP6Lwt7L2715Ggo0nosvA= -github.com/fsnotify/fsnotify v1.7.0/go.mod h1:40Bi/Hjc2AVfZrqy+aj+yEI+/bRxZnMJyTJwOpGvigM= -github.com/fxamacker/cbor/v2 v2.7.0 h1:iM5WgngdRBanHcxugY4JySA0nk1wZorNOpTgCMedv5E= -github.com/fxamacker/cbor/v2 v2.7.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= +github.com/fsnotify/fsevents v0.2.0 h1:BRlvlqjvNTfogHfeBOFvSC9N0Ddy+wzQCQukyoD7o/c= +github.com/fsnotify/fsevents v0.2.0/go.mod h1:B3eEk39i4hz8y1zaWS/wPrAP4O6wkIl7HQwKBr1qH/w= +github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= +github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= +github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= +github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= +github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= +github.com/fxamacker/cbor/v2 v2.9.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/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/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.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.1 h1:lpsStH0n2ittzTnbaSloVZLuB5+fvSY/+hnagBjSNZU= -github.com/go-openapi/swag v0.23.1/go.mod h1:STZs8TbRvEQQKUA+JZNAm3EWlgaOBGpyFDqQnDHMef0= +github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk= +github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM= +github.com/go-openapi/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc= +github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4= +github.com/go-openapi/swag v0.25.1 h1:6uwVsx+/OuvFVPqfQmOOPsqTcm5/GkBhNwLqIR916n8= +github.com/go-openapi/swag v0.25.1/go.mod h1:bzONdGlT0fkStgGPd3bhZf1MnuPkf2YAys6h+jZipOo= +github.com/go-openapi/swag/cmdutils v0.25.1 h1:nDke3nAFDArAa631aitksFGj2omusks88GF1VwdYqPY= +github.com/go-openapi/swag/cmdutils v0.25.1/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= +github.com/go-openapi/swag/conv v0.25.1 h1:+9o8YUg6QuqqBM5X6rYL/p1dpWeZRhoIt9x7CCP+he0= +github.com/go-openapi/swag/conv v0.25.1/go.mod h1:Z1mFEGPfyIKPu0806khI3zF+/EUXde+fdeksUl2NiDs= +github.com/go-openapi/swag/fileutils v0.25.1 h1:rSRXapjQequt7kqalKXdcpIegIShhTPXx7yw0kek2uU= +github.com/go-openapi/swag/fileutils v0.25.1/go.mod h1:+NXtt5xNZZqmpIpjqcujqojGFek9/w55b3ecmOdtg8M= +github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU= +github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo= +github.com/go-openapi/swag/jsonutils v0.25.1 h1:AihLHaD0brrkJoMqEZOBNzTLnk81Kg9cWr+SPtxtgl8= +github.com/go-openapi/swag/jsonutils v0.25.1/go.mod h1:JpEkAjxQXpiaHmRO04N1zE4qbUEg3b7Udll7AMGTNOo= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1 h1:DSQGcdB6G0N9c/KhtpYc71PzzGEIc/fZ1no35x4/XBY= +github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1/go.mod h1:kjmweouyPwRUEYMSrbAidoLMGeJ5p6zdHi9BgZiqmsg= +github.com/go-openapi/swag/loading v0.25.1 h1:6OruqzjWoJyanZOim58iG2vj934TysYVptyaoXS24kw= +github.com/go-openapi/swag/loading v0.25.1/go.mod h1:xoIe2EG32NOYYbqxvXgPzne989bWvSNoWoyQVWEZicc= +github.com/go-openapi/swag/mangling v0.25.1 h1:XzILnLzhZPZNtmxKaz/2xIGPQsBsvmCjrJOWGNz/ync= +github.com/go-openapi/swag/mangling v0.25.1/go.mod h1:CdiMQ6pnfAgyQGSOIYnZkXvqhnnwOn997uXZMAd/7mQ= +github.com/go-openapi/swag/netutils v0.25.1 h1:2wFLYahe40tDUHfKT1GRC4rfa5T1B4GWZ+msEFA4Fl4= +github.com/go-openapi/swag/netutils v0.25.1/go.mod h1:CAkkvqnUJX8NV96tNhEQvKz8SQo2KF0f7LleiJwIeRE= +github.com/go-openapi/swag/stringutils v0.25.1 h1:Xasqgjvk30eUe8VKdmyzKtjkVjeiXx1Iz0zDfMNpPbw= +github.com/go-openapi/swag/stringutils v0.25.1/go.mod h1:JLdSAq5169HaiDUbTvArA2yQxmgn4D6h4A+4HqVvAYg= +github.com/go-openapi/swag/typeutils v0.25.1 h1:rD/9HsEQieewNt6/k+JBwkxuAHktFtH3I3ysiFZqukA= +github.com/go-openapi/swag/typeutils v0.25.1/go.mod h1:9McMC/oCdS4BKwk2shEB7x17P6HmMmA6dQRtAkSnNb8= +github.com/go-openapi/swag/yamlutils v0.25.1 h1:mry5ez8joJwzvMbaTGLhw8pXUnhDK91oSJLDPF1bmGk= +github.com/go-openapi/swag/yamlutils v0.25.1/go.mod h1:cm9ywbzncy3y6uPm/97ysW8+wZ09qsks+9RS8fLWKqg= +github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= +github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= 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= +github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= +github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= +github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= +github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= @@ -189,30 +270,23 @@ github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzw github.com/golang-jwt/jwt/v4 v4.5.0/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXeUI= github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= -github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= -github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= -github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= -github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= -github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= -github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= -github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= +github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= 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.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.22.1 h1:AfVXx3chM2qwoSbM7Da8g8hX8OVSkBFwX+rz2+PcK40= -github.com/google/cel-go v0.22.1/go.mod h1:BuznPXXfQDpXKWQ9sPW3TzlAJN5zzFe+i9tIs0yC4s8= -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.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= -github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= +github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= +github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= +github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9/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/go-containerregistry v0.20.3 h1:oNx7IdTI936V8CQRveCjaxOiegWwvM7kqkbXTpyiovI= -github.com/google/go-containerregistry v0.20.3/go.mod h1:w00pIgBRDVUDFM6bq+Qx8lwNWK+cxgCuX1vd3PIBDNI= +github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU= +github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y= github.com/google/go-github v17.0.0+incompatible h1:N0LgJ1j65A7kfXrZnUDaYCs/Sf4rEjNlfyDHW9dolSY= github.com/google/go-github v17.0.0+incompatible/go.mod h1:zLgOLi98H3fifZn+44m+umXrS52loVEgC2AApnigrVQ= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= @@ -220,32 +294,37 @@ github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17 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/safetext v0.0.0-20240722112252-5a72de7e7962 h1:+9C/TgFfcCmZBV7Fjb3kQCGlkpFrhtvFDgbdQHB9RaA= -github.com/google/safetext v0.0.0-20240722112252-5a72de7e7962/go.mod h1:H3K1Iu/utuCfa10JO+GsmKUYSWi7ug57Rk6GaDRHaaQ= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6 h1:EEHtgt9IwisQ2AZ4pIsMjahcegHh6rmhqxzIRQIyepY= +github.com/google/pprof v0.0.0-20250820193118-f64d9cf942d6/go.mod h1:I6V7YzU0XDpsHqbsyrghnFZLO1gwK6NPTNvmetQIk9U= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaUGG7oYTSPP8MxqL4YI3kZKwcP4= github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= -github.com/google/uuid v1.0.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= -github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= -github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= +github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3 h1:5ZPtiqj0JL5oKWmcsq4VMaAW5ukBEgSGXEN89zeH1Jo= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.26.3/go.mod h1:ndYquD05frm2vACXE1nsccT4oJzjhw2arTS2cpUD1PI= -github.com/hashicorp/go-version v1.7.0 h1:5tqGy27NaOTB8yJKUZELlFAS/LTKJkrmONwQKeRZfjY= -github.com/hashicorp/go-version v1.7.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= -github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= -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/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= +github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= +github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= +github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo= +github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= +github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= +github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= +github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= +github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s= +github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= -github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY= -github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= +github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= +github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= github.com/k14s/difflib v0.0.0-20240118055029-596a7a5585c3 h1:q2ikACDbDDbyUcN9JkDcNMGhIx1EBRkctAsPZMr35qM= @@ -256,8 +335,8 @@ github.com/k14s/ytt v0.39.0 h1:SSdF030TVUBTP9lGge51v5GLgUjgu49B7l/YPzzrm8g= github.com/k14s/ytt v0.39.0/go.mod h1:JLCkplRQQm6X+4FqgAYrwvDtVxzMCZxe88bH1kr4bgQ= 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.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= -github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= +github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= 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.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= @@ -268,8 +347,6 @@ github.com/kylelemons/godebug v1.1.0 h1:RPNrshWIDI6G2gRW9EHilWtl7Z6Sb1BR0xunSBf0 github.com/kylelemons/godebug v1.1.0/go.mod h1:9/0rRGxNHcop5bhtWyNeEfOS8JIWk580+fNqagV/RAw= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= -github.com/mailru/easyjson v0.9.0 h1:PrnmzHw7262yW8sTBwxi1PdJA3Iw/EKBa8psRf7d9a4= -github.com/mailru/easyjson v0.9.0/go.mod h1:1+xMtQp2MRNVL/V1bOzuP3aP8VNwRW55fQUto+XFtTU= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= @@ -277,322 +354,375 @@ github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hd github.com/mattn/go-isatty v0.0.11/go.mod h1:PhnuNfih5lzO57/f3n+odYbM4JtupLOxQOAqxQCu2WE= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= -github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc= -github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= +github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= +github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= 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/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= -github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= +github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= +github.com/moby/buildkit v0.26.3 h1:D+ruZVAk/3ipRq5XRxBH9/DIFpRjSlTtMbghT5gQP9g= +github.com/moby/buildkit v0.26.3/go.mod h1:4T4wJzQS4kYWIfFRjsbJry4QoxDBjK+UGOEOs1izL7w= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= +github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= +github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= +github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= +github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg= +github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= +github.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k= +github.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= github.com/moby/spdystream v0.5.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/sys/capability v0.4.0 h1:4D4mI6KlNtWMCM1Z/K0i7RV1FkX+DBDHKVJpCndZoHk= +github.com/moby/sys/capability v0.4.0/go.mod h1:4g9IK291rVkms3LKCDOoYlnV8xKwoDTpIrNEE35Wq0I= +github.com/moby/sys/mountinfo v0.7.2 h1:1shs6aH5s4o5H2zQLn796ADW1wMrIwHsyJ2v9KouLrg= +github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/signal v0.7.1 h1:PrQxdvxcGijdo6UXXo/lU/TvHUWyPhj7UOpSo8tuvk0= +github.com/moby/sys/signal v0.7.1/go.mod h1:Se1VGehYokAkrSQwL4tDzHvETwUZlnY7S5XtQ50mQp8= +github.com/moby/sys/symlink v0.3.0 h1:GZX89mEZ9u53f97npBy4Rc3vJKj7JBDj/PN2I22GrNU= +github.com/moby/sys/symlink v0.3.0/go.mod h1:3eNdhduHmYPcgsJtZXW1W4XUJdZGBIkttZ8xKqPUJq0= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= github.com/moby/term v0.5.2 h1:6qk3FJAFDs6i/q3W/pQ97SX192qKfZgGjCQqfCJkgzQ= github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFLc= 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= -github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= -github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= -github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= +github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= +github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ= +github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= -github.com/nxadm/tail v1.4.4/go.mod h1:kenIhsEOeOJmVchQTgglprH7qJGnHDVpk1VPCcaMI8A= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= -github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= -github.com/onsi/ginkgo v1.12.1/go.mod h1:zj2OWP4+oCPe1qIXoGWkgMRwljMUYCdkwsT2108oapk= -github.com/onsi/ginkgo v1.13.0/go.mod h1:+REjRxOmWfHCjfv9TTWB1jD1Frx4XydAD3zm1lskyM0= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= -github.com/onsi/ginkgo/v2 v2.22.0 h1:Yed107/8DjTr0lKCNt7Dn8yQ6ybuDRQoMGrNFKzMfHg= -github.com/onsi/ginkgo/v2 v2.22.0/go.mod h1:7Du3c42kxCUegi0IImZ1wUQzMBVecgIHjR1C+NkhLQo= -github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= -github.com/onsi/gomega v1.10.1/go.mod h1:iN09h71vgCQne3DLsj+A5owkum+a2tYe+TOCB1ybHNo= -github.com/onsi/gomega v1.36.1 h1:bJDPBO7ibjxcbHMgSCoo4Yj18UWbKDlLwX1x9sybDcw= -github.com/onsi/gomega v1.36.1/go.mod h1:PvZbdDc8J6XJEpDK4HCuRBm8a6Fzp9/DmhC9C7yFlog= +github.com/onsi/ginkgo/v2 v2.23.0 h1:FA1xjp8ieYDzlgS5ABTpdUDB7wtngggONc8a7ku2NqQ= +github.com/onsi/ginkgo/v2 v2.23.0/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= +github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= +github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= 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.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= -github.com/openshift/crd-schema-checker v0.0.0-20250131182805-6a3e5c3bb298 h1:IeMhpaGAj3JDKNvhfn+cKYbRTEVZkFUtGNGijIYWYg4= -github.com/openshift/crd-schema-checker v0.0.0-20250131182805-6a3e5c3bb298/go.mod h1:sTxJ4ZFW9r9fEdbW2v0yMRi6NcyTbx0fII4p83IQ+L8= +github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg= +github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8= +github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U= +github.com/openshift/crd-schema-checker v0.0.0-20250905140724-c313b6407231 h1:8lSGufji9rfiyDxtUl7A4uOyeeP4x0UOOXcsDBFfkGI= +github.com/openshift/crd-schema-checker v0.0.0-20250905140724-c313b6407231/go.mod h1:sTxJ4ZFW9r9fEdbW2v0yMRi6NcyTbx0fII4p83IQ+L8= github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I= github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= github.com/otiai10/mint v1.6.3/go.mod h1:MJm72SBthJjz8qhefc4z1PYEieWmy8Bku7CjcAqyUSM= -github.com/pborman/uuid v1.2.1 h1:+ZZIw58t/ozdjRaXh/3awHfmWRbzYxJoAdNJxe/3pvw= -github.com/pborman/uuid v1.2.1/go.mod h1:X/NO0urCmaxf9VXbdlT7C2Yzkj2IKimNn4k+gtPdI/k= github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8= github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= +github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= 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.21.1 h1:DOvXXTqVzvkIewV/CDPFdejpMCGeMcbGCQ8YOmu+Ibk= -github.com/prometheus/client_golang v1.21.1/go.mod h1:U9NM32ykUErtVBxdvD3zfi+EuFkkaBvMb09mIfe0Zgg= -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.63.0 h1:YR/EIY1o3mEFP/kZCD7iDMnLPlGyuU2Gb3HIcXnA98k= -github.com/prometheus/common v0.63.0/go.mod h1:VVFF/fBIoToEnWRVkYoXEkq3R3paCoxG9PXP74SnV18= -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/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= -github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -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/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= +github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +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.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyAEN8= +github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko= +github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= +github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= -github.com/sclevine/agouti v3.0.0+incompatible/go.mod h1:b4WX9W9L1sfQKXeJf1mUTLZKJ48R1S7H23Ji7oFO5Bw= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= +github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL5qTdn9lR8XKHf4RUyG1Sx3g= +github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU= +github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= +github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= -github.com/spf13/cobra v1.9.1 h1:CXSaggrXdbHK9CF+8ywj8Amf7PBRmPCOJugH954Nnlo= -github.com/spf13/cobra v1.9.1/go.mod h1:nDyEzZ8ogv936Cinf6g1RU9MRY64Ir93oCnqb9wxYW0= -github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= -github.com/spf13/pflag v1.0.6/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/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= +github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= +github.com/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XOmk= +github.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE= +github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= +github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= +github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= +github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= +github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= 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= +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.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= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= -github.com/vbatts/tar-split v0.12.1 h1:CqKoORW7BUWBe7UL/iqTVvkTBOF8UvOMKOIZykxnnbo= -github.com/vbatts/tar-split v0.12.1/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= -github.com/vito/go-interact v1.0.1 h1:O8xi8c93bRUv2Tb/v6HdiuGc+WnWt+AQzF74MOOdlBs= -github.com/vito/go-interact v1.0.1/go.mod h1:HrdHSJXD2yn1MhlTwSIMeFgQ5WftiIorszVGd3S/DAA= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 h1:QB54BJwA6x8QU9nHY3xJSZR2kX9bgpZekRKGkLTmEXA= +github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375/go.mod h1:xRroudyp5iVtxKqZCrA6n2TLFRBf8bmnjr1UD4x+z7g= +github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4= +github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323/go.mod h1:3Iuxbr0P7D3zUzBMAZB+ois3h/et0shEz0qApgHYGpY= +github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f h1:MoxeMfHAe5Qj/ySSBfL8A7l1V+hxuluj8owsIEEZipI= +github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f/go.mod h1:BKdcez7BiVtBvIcef90ZPc6ebqIWr4JWD7+EvLm6J98= +github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0 h1:2f304B10LaZdB8kkVEaoXvAMVan2tl9AiK4G0odjQtE= +github.com/tonistiigi/go-csvvalue v0.0.0-20240814133006-030d3b2625d0/go.mod h1:278M4p8WsNh3n4a1eqiFcV2FGk7wE5fwUpUom9mK9lE= +github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea h1:SXhTLE6pb6eld/v/cCndK0AMpt1wiVFb/YYmqB3/QG0= +github.com/tonistiigi/units v0.0.0-20180711220420-6950e57a87ea/go.mod h1:WPnis/6cRcDZSUvVmezrxJPkiO87ThFYsoUiMwWNDJk= +github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab h1:H6aJ0yKQ0gF49Qb2z5hI1UHxSQt4JMyxebFR15KnApw= +github.com/tonistiigi/vt100 v0.0.0-20240514184818-90bafcd6abab/go.mod h1:ulncasL3N9uLrVann0m+CDlJKWsIAP34MPcOJF6VRvc= +github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4= +github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= +github.com/vito/go-interact v1.0.2 h1:viJuANio3WH9utUG4rKbJC9V3JR5JgYNS+i0efeA+GU= +github.com/vito/go-interact v1.0.2/go.mod h1:s+y0jK9Z2etBYt5ZM6+DhpOsE5C7NNGC3jrJvW0BBpc= github.com/vmware-tanzu/carvel-kapp-controller v0.51.3 h1:TBeFKz1cmdI8vreaWT8waRNl8ycYk9NyZMPDXAYEO68= github.com/vmware-tanzu/carvel-kapp-controller v0.51.3/go.mod h1:Ndy9tru0vO/UwChzM8GL6OHHCLCSL7InFB82Qxdyc8Q= 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/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb h1:zGWFAtiMcyryUHoUjUJX0/lt1H2+i2Ka2n+D3DImSNo= -github.com/xeipuuv/gojsonpointer v0.0.0-20190905194746-02993c407bfb/go.mod h1:N2zxlSyiKSe5eX1tZViRH5QA0qijqEDrYZiPEAiq3wU= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415 h1:EzJWgHovont7NscjpAxXsDA8S8BMYve8Y5+7cuRE7R0= -github.com/xeipuuv/gojsonreference v0.0.0-20180127040603-bd5ef7bd5415/go.mod h1:GwrjFmJcFw6At/Gs6z4yjiIwzuJ1/+UwLxMQDVQXShQ= -github.com/xeipuuv/gojsonschema v1.2.0 h1:LhYJRs+L4fBtjZUfuSZIKGeVu0QRy8e5Xi7D17UxZ74= -github.com/xeipuuv/gojsonschema v1.2.0/go.mod h1:anYRn/JVcOK2ZgGU+IjEV4nwlhoK5sQluxsYJ78Id3Y= +github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= +github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -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/client/v3 v3.5.16 h1:sSmVYOAHeC9doqi0gv7v86oY/BTld0SEFGaxsU9eRhE= -go.etcd.io/etcd/client/v3 v3.5.16/go.mod h1:X+rExSGkyqxvu276cr2OwPLBaeqFu1cIl4vmRjAD/50= -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.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.60.0 h1:sbiXRNDSWJOTobXh5HyQKjq6wUC5tNybqjIqDpAY4CU= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.60.0/go.mod h1:69uWxva0WgAA/4bu2Yy70SLDBwZXuQ6PbBpbsa5iZrQ= -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/exporters/otlp/otlptrace v1.35.0 h1:1fTNlAIJZGWLP5FVu0fikVry1IsiUnXjf7QFvoNN3Xw= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.35.0/go.mod h1:zjPK58DtkqQFn+YUMbx0M2XV3QgKU0gS9LeGohREyK4= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0 h1:m639+BofXTvcY1q8CGs4ItwQarYtJPOWmVobfM1HpVI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.35.0/go.mod h1:LjReUci/F4BUyv+y4dwnq3h/26iNOeC3wAIqgvTIZVo= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0 h1:wpMfgF8E1rkrT1Z6meFh1NDtownE9Ii3n3X2GJYjsaU= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.33.0/go.mod h1:wAy0T/dUbs468uOlkT31xjvqQgEVXv58BRFWEgn5v/0= -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.35.0 h1:iPctf8iprVySXSKJffSS79eOjl9pvxV9ZqOWT0QejKY= -go.opentelemetry.io/otel/sdk v1.35.0/go.mod h1:+ga1bZliga3DxJ3CQGg3updiaAJoNECOgJREo9KHGQg= -go.opentelemetry.io/otel/sdk/metric v1.35.0 h1:1RriWBmCKgkeHEhM7a2uMjMUfP7MsOF5JpUCaEqEI9o= -go.opentelemetry.io/otel/sdk/metric v1.35.0/go.mod h1:is6XYCUMpcKi+ZsOvfluY5YstFnhW0BidkR+gL+qN+w= -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.opentelemetry.io/proto/otlp v1.5.0 h1:xJvq7gMzB31/d406fB8U5CBdyQGw4P399D1aQWU/3i4= -go.opentelemetry.io/proto/otlp v1.5.0/go.mod h1:keN8WnHxOy8PG0rQZjJJ5A2ebUoafqWp0eVQ4yIXvJ4= +go.etcd.io/etcd/api/v3 v3.6.4 h1:7F6N7toCKcV72QmoUKa23yYLiiljMrT4xCeBL9BmXdo= +go.etcd.io/etcd/api/v3 v3.6.4/go.mod h1:eFhhvfR8Px1P6SEuLT600v+vrhdDTdcfMzmnxVXXSbk= +go.etcd.io/etcd/client/pkg/v3 v3.6.4 h1:9HBYrjppeOfFjBjaMTRxT3R7xT0GLK8EJMVC4xg6ok0= +go.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI= +go.etcd.io/etcd/client/v3 v3.6.4 h1:YOMrCfMhRzY8NgtzUsHl8hC2EBSnuqbR3dh84Uryl7A= +go.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo= +go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= +go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0 h1:YH4g8lQroajqUwWbq/tr2QX1JFmEXaDLgG+ew9bLMWo= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.63.0/go.mod h1:fvPi2qXDqFs8M4B4fmJhE92TyQs9Ydjlg3RvfUp+NbQ= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0 h1:2pn7OzMewmYRiNtv1doZnLo3gONcnMHlFnmOR8Vgt+8= +go.opentelemetry.io/contrib/instrumentation/net/http/httptrace/otelhttptrace v0.63.0/go.mod h1:rjbQTDEPQymPE0YnRQp9/NuPwwtL0sesz/fnqRW/v84= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= +go.opentelemetry.io/otel v1.39.0 h1:8yPrr/S0ND9QEfTfdP9V+SiwT4E0G7Y5MO7p85nis48= +go.opentelemetry.io/otel v1.39.0/go.mod h1:kLlFTywNWrFyEdH0oj2xK0bFYZtHRYUdv1NklR/tgc8= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0 h1:cEf8jF6WbuGQWUVcqgyWtTR0kOOAWY1DYZ+UhvdmQPw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v1.39.0/go.mod h1:k1lzV5n5U3HkGvTCJHraTAGJ7MqsgL1wrGwTj1Isfiw= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0 h1:Oe2z/BCg5q7k4iXC3cqJxKYg0ieRiOqF0cecFYdPTwk= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetrichttp v1.38.0/go.mod h1:ZQM5lAJpOsKnYagGg/zV2krVqTtaVdYdDkhMoX6Oalg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0 h1:aTL7F04bJHUlztTsNGJ2l+6he8c+y/b//eR0jjjemT4= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.38.0/go.mod h1:kldtb7jDTeol0l3ewcmd8SDvx3EmIE7lyvqbasU3QC4= +go.opentelemetry.io/otel/metric v1.39.0 h1:d1UzonvEZriVfpNKEVmHXbdf909uGTOQjA0HF0Ls5Q0= +go.opentelemetry.io/otel/metric v1.39.0/go.mod h1:jrZSWL33sD7bBxg1xjrqyDjnuzTUB0x1nBERXd7Ftcs= +go.opentelemetry.io/otel/sdk v1.39.0 h1:nMLYcjVsvdui1B/4FRkwjzoRVsMK8uL/cj0OyhKzt18= +go.opentelemetry.io/otel/sdk v1.39.0/go.mod h1:vDojkC4/jsTJsE+kh+LXYQlbL8CgrEcwmt1ENZszdJE= +go.opentelemetry.io/otel/sdk/metric v1.39.0 h1:cXMVVFVgsIf2YL6QkRF4Urbr/aMInf+2WKg+sEJTtB8= +go.opentelemetry.io/otel/sdk/metric v1.39.0/go.mod h1:xq9HEVH7qeX69/JnwEfp6fVq5wosJsY1mt4lLfYdVew= +go.opentelemetry.io/otel/trace v1.39.0 h1:2d2vfpEDmCJ5zVYz7ijaJdOF59xLomrvj7bjt6/qCJI= +go.opentelemetry.io/otel/trace v1.39.0/go.mod h1:88w4/PnZSazkGzz/w84VHpQafiU4EtqqlVdxWy+rNOA= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= +go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= +go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= +go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= +go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= +go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= +go.yaml.in/yaml/v4 v4.0.0-rc.3 h1:3h1fjsh1CTAPjW7q/EMe+C8shx5d8ctzZTrLcs/j8Go= +go.yaml.in/yaml/v4 v4.0.0-rc.3/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= -golang.org/x/crypto v0.36.0 h1:AnAEvhDddvBdpY+uR+MyHmuZzzNqXSe/GvuDeob5L34= -golang.org/x/crypto v0.36.0/go.mod h1:Y4J0ReaxCR1IMaabaSMugxJES1EpwhBHhv2bDHklZvc= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394 h1:nDVHiLt8aIbd/VzvPWN6kSOPE7+F/fNFDSXLVYkE/Iw= -golang.org/x/exp v0.0.0-20250305212735-054e65f0b394/go.mod h1:sIifuuw/Yco/y6yb6+bDNfyeQ/MdPUy/hKEMYQV17cM= +golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= +golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= +golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= +golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= 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.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= -golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= +golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= 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-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= -golang.org/x/net v0.38.0 h1:vRMAPTMaeGqVhG5QyLJHqNDwecKTomGeqbnfZyKlBI8= -golang.org/x/net v0.38.0/go.mod h1:ivrbrMbzFq5J41QOQh0siUuly180yBYtLp+CKbEaFx8= -golang.org/x/oauth2 v0.28.0 h1:CrgCKl8PPAVtLnU3c+EDw6x11699EWlsDeWNWKdIOkc= -golang.org/x/oauth2 v0.28.0/go.mod h1:onh5ek6nERTohokkhCD/y2cV4Do3fxFHFuAejCkRWT8= -golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= +golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= +golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.12.0 h1:MHc5BpPuC30uJk597Ri8TV3CNZcTLu6B6z4lJy+g6Jw= -golang.org/x/sync v0.12.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= -golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= +golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190904154756-749cb33beabd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191005200804-aed5e4c7ecf9/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200323222414-85ca7c5b95cd/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.0.0-20200519105757-fe76b779f299/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220412211240-33da011f77ad/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= +golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.0.0-20220411215600-e5f449aeb171/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo= golang.org/x/term v0.15.0/go.mod h1:BDl952bC7+uMoWR75FIrCDx79TPU9oHkTZ9yRbYOrX0= -golang.org/x/term v0.30.0 h1:PQ39fJZ+mfadBm0y5WlL4vlM7Sx1Hgf13sMIY2+QS9Y= -golang.org/x/term v0.30.0/go.mod h1:NYYFdzHoI5wRh/h5tDMdMqCqPJZEuNqVR5xJLd/n67g= +golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= +golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= -golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= -golang.org/x/time v0.11.0 h1:/bpjEDfN9tkoN/ryeYHnv5hcMlc8ncjMcM4XBk5NWV0= -golang.org/x/time v0.11.0/go.mod h1:CDIdPxbZBQxdj6cxyCIdrNogrJKMJ7pr37NYpMcMDSg= +golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= +golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= +golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= -golang.org/x/tools v0.31.0 h1:0EedkvKDbh+qistFTd0Bcwe/YLh4vHwWEkiI0toFIBU= -golang.org/x/tools v0.31.0/go.mod h1:naFTU+Cev749tSJRXJlna0T3WxKvb1kWEx15xA4SdmQ= +golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= +golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= 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= -google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4 h1:IFnXJq3UPB3oBREOodn1v1aGQeZYQclEmvWRMN0PSsY= -google.golang.org/genproto/googleapis/api v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:c8q6Z6OCqnfVIqUFJkCzKcrj8eCvUrz+K4KRzSTuANg= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4 h1:iK2jbkWL86DXjEx0qiHcRE9dE4/Ahua5k6V8OWFb//c= -google.golang.org/genproto/googleapis/rpc v0.0.0-20250313205543-e70fdf4c4cb4/go.mod h1:LuRYeWDFV6WOn90g357N17oMCaxpgCnbi/44qJvDn2I= -google.golang.org/grpc v1.71.0 h1:kF77BGdPTQ4/JZWMlb9VpJ5pa25aqvVqogsxNHHdeBg= -google.golang.org/grpc v1.71.0/go.mod h1:H0GRtasmQOh9LkFoCPDu3ZrwUtD1YGE+b2vYBYd/8Ec= -google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= -google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= -google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= -google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= -google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= -google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= -google.golang.org/protobuf v1.36.5 h1:tPhr+woSbjfYvY6/GPufUoYizxw1cF/yFoxJ2fmpwlM= -google.golang.org/protobuf v1.36.5/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= +gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk= +gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1:fCvbg86sFXwdrl5LgVcTEvNC+2txB5mgROGmRL5mrls= +google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= +google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= +google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= +google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= 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= -gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= -gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= -gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= +gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= +gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= +gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= -gopkg.in/yaml.v2 v2.2.4/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= -gopkg.in/yaml.v2 v2.3.0/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.4.0 h1:ZazjZUfuVeZGLAmlKKuyv3IKP5orXcwtOwDQH6YVr6o= -gotest.tools/v3 v3.4.0/go.mod h1:CtbdzLSsqVhDgMtKsx03ird5YTGB3ar27v0u/yKBW5g= -k8s.io/api v0.32.3 h1:Hw7KqxRusq+6QSplE3NYG4MBxZw1BZnq4aP4cJVINls= -k8s.io/api v0.32.3/go.mod h1:2wEDTXADtm/HA7CCMD8D8bK4yuBUptzaRhYcYEEYA3k= -k8s.io/apiextensions-apiserver v0.32.3 h1:4D8vy+9GWerlErCwVIbcQjsWunF9SUGNu7O7hiQTyPY= -k8s.io/apiextensions-apiserver v0.32.3/go.mod h1:8YwcvVRMVzw0r1Stc7XfGAzB/SIVLunqApySV5V7Dss= -k8s.io/apimachinery v0.32.3 h1:JmDuDarhDmA/Li7j3aPrwhpNBA94Nvk5zLeOge9HH1U= -k8s.io/apimachinery v0.32.3/go.mod h1:GpHVgxoKlTxClKcteaeuF1Ul/lDVb74KpZcxcmLDElE= -k8s.io/apiserver v0.32.3 h1:kOw2KBuHOA+wetX1MkmrxgBr648ksz653j26ESuWNY8= -k8s.io/apiserver v0.32.3/go.mod h1:q1x9B8E/WzShF49wh3ADOh6muSfpmFL0I2t+TG0Zdgc= -k8s.io/cli-runtime v0.32.3 h1:khLF2ivU2T6Q77H97atx3REY9tXiA3OLOjWJxUrdvss= -k8s.io/cli-runtime v0.32.3/go.mod h1:vZT6dZq7mZAca53rwUfdFSZjdtLyfF61mkf/8q+Xjak= -k8s.io/client-go v0.32.3 h1:RKPVltzopkSgHS7aS98QdscAgtgah/+zmpAogooIqVU= -k8s.io/client-go v0.32.3/go.mod h1:3v0+3k4IcT9bXTc4V2rt+d2ZPPG700Xy6Oi0Gdl2PaY= -k8s.io/component-base v0.32.3 h1:98WJvvMs3QZ2LYHBzvltFSeJjEx7t5+8s71P7M74u8k= -k8s.io/component-base v0.32.3/go.mod h1:LWi9cR+yPAv7cu2X9rZanTiFKB2kHA+JjmhkKjCZRpI= -k8s.io/component-helpers v0.32.3 h1:9veHpOGTPLluqU4hAu5IPOwkOIZiGAJUhHndfVc5FT4= -k8s.io/component-helpers v0.32.3/go.mod h1:utTBXk8lhkJewBKNuNf32Xl3KT/0VV19DmiXU/SV4Ao= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= +k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= +k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= +k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= +k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= +k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= +k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= +k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA= +k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0= +k8s.io/cli-runtime v0.34.2 h1:cct1GEuWc3IyVT8MSCoIWzRGw9HJ/C5rgP32H60H6aE= +k8s.io/cli-runtime v0.34.2/go.mod h1:X13tsrYexYUCIq8MarCBy8lrm0k0weFPTpcaNo7lms4= +k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= +k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= +k8s.io/component-base v0.34.2 h1:HQRqK9x2sSAsd8+R4xxRirlTjowsg6fWCPwWYeSvogQ= +k8s.io/component-base v0.34.2/go.mod h1:9xw2FHJavUHBFpiGkZoKuYZ5pdtLKe97DEByaA+hHbM= +k8s.io/component-helpers v0.34.2 h1:RIUGDdU+QFzeVKLZ9f05sXTNAtJrRJ3bnbMLrogCrvM= +k8s.io/component-helpers v0.34.2/go.mod h1:pLi+GByuRTeFjjcezln8gHL7LcT6HImkwVQ3A2SQaEE= +k8s.io/controller-manager v0.33.5 h1:abmssknXnhOhW533583v2SYQObD5RhYiSL7Za1rezGM= +k8s.io/controller-manager v0.33.5/go.mod h1:KuQeAlf4vI2+qj5fwPVLaDlbtrTBA/8L/LqQvI74Ow0= 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-20250304201544-e5f78fe3ede9 h1:t0huyHnz6HsokckRxAF1bY0cqPFwzINKCL7yltEjZQc= -k8s.io/kube-openapi v0.0.0-20250304201544-e5f78fe3ede9/go.mod h1:5jIi+8yX4RIb8wk3XwBo5Pq2ccx4FP10ohkbSKCZoK8= -k8s.io/kubectl v0.32.3 h1:VMi584rbboso+yjfv0d8uBHwwxbC438LKq+dXd5tOAI= -k8s.io/kubectl v0.32.3/go.mod h1:6Euv2aso5GKzo/UVMacV6C7miuyevpfI91SvBvV9Zdg= -k8s.io/kubernetes v1.32.7 h1:izcZ6PLoXERxrzKQMNch8/9Nbedv94iTHrVjB+IJ17g= -k8s.io/kubernetes v1.32.7/go.mod h1:REY0Gok66BTTrbGyZaFMNKO9JhxvgBDW9B7aksWRFoY= -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/apiserver-network-proxy/konnectivity-client v0.32.0 h1:XotDXzqvJ8Nx5eiZZueLpTuafJz8SiodgOemI+w87QU= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.32.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/controller-runtime v0.20.3 h1:I6Ln8JfQjHH7JbtCD2HCYHoIzajoRxPNuvhvcDbZgkI= -sigs.k8s.io/controller-runtime v0.20.3/go.mod h1:xg2XB0K5ShQzAgsoujxuKN4LNXR2LfwwHsPj7Iaw+XY= -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/kind v0.27.0 h1:PQ3f0iAWNIj66LYkZ1ivhEg/+Zb6UPMbO+qVei/INZA= -sigs.k8s.io/kind v0.27.0/go.mod h1:RZVFmy6qcwlSWwp6xeIUv7kXCPF3i8MXsEXxW/J+gJY= -sigs.k8s.io/randfill v0.0.0-20250304075658-069ef1bbf016/go.mod h1:XeLlZ/jmk4i1HRopwe7/aU3H5n1zNUcX6TM94b3QxOY= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= +k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= +k8s.io/kubectl v0.34.2 h1:+fWGrVlDONMUmmQLDaGkQ9i91oszjjRAa94cr37hzqA= +k8s.io/kubectl v0.34.2/go.mod h1:X2KTOdtZZNrTWmUD4oHApJ836pevSl+zvC5sI6oO2YQ= +k8s.io/kubernetes v1.34.2 h1:WQdDvYJazkmkwSncgNwGvVtaCt4TYXIU3wSMRgvp3MI= +k8s.io/kubernetes v1.34.2/go.mod h1:m6pZk6a179pRo2wsTiCPORJ86iOEQmfIzUvtyEF8BwA= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= +k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= +pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= +sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= +sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= +sigs.k8s.io/kind v0.30.0 h1:2Xi1KFEfSMm0XDcvKnUt15ZfgRPCT0OnCBbpgh8DztY= +sigs.k8s.io/kind v0.30.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= 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.6.0 h1:IUA9nvMmnKWcj5jl84xn+T5MnlZKThmUW1TdblaLVAc= -sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= -sigs.k8s.io/yaml v1.4.0 h1:Mk1wCc2gy/F0THH0TAp1QYyJNzRm2KCLy3o5ASXVI5E= -sigs.k8s.io/yaml v1.4.0/go.mod h1:Ejl7/uTz7PSA4eKMyQCUTnhZYNmLIl+5c2lQPGR2BPY= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= +sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= +sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= +tags.cncf.io/container-device-interface v1.1.0 h1:RnxNhxF1JOu6CJUVpetTYvrXHdxw9j9jFYgZpI+anSY= +tags.cncf.io/container-device-interface v1.1.0/go.mod h1:76Oj0Yqp9FwTx/pySDc8Bxjpg+VqXfDb50cKAXVJ34Q= diff --git a/client-programs/pkg/cmd/cluster_workshop_update_cmd.go b/client-programs/pkg/cmd/cluster_workshop_update_cmd.go index cf6abf34d..c3075ded8 100644 --- a/client-programs/pkg/cmd/cluster_workshop_update_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_update_cmd.go @@ -11,9 +11,10 @@ import ( "path/filepath" yttcmd "carvel.dev/ytt/pkg/cmd/template" + "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/workshops" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/cluster" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" @@ -256,7 +257,7 @@ func loadWorkshopDefinition(name string, path string, portal string, workshopFil // Process the workshop YAML data in case it contains ytt templating. - if workshopData, err = processWorkshopDefinition(workshopData, dataValueFlags); err != nil { + if workshopData, err = workshops.ProcessWorkshopDefinition(workshopData, dataValueFlags); err != nil { return nil, errors.Wrap(err, "unable to process workshop definition as template") } diff --git a/client-programs/pkg/cmd/tunnel_connect_cmd.go b/client-programs/pkg/cmd/tunnel_connect_cmd.go index f5330ad36..1b2ad2814 100644 --- a/client-programs/pkg/cmd/tunnel_connect_cmd.go +++ b/client-programs/pkg/cmd/tunnel_connect_cmd.go @@ -1,13 +1,7 @@ package cmd import ( - "fmt" - "net/http" - "net/url" - "os" - - "github.com/gorilla/websocket" - "github.com/pkg/errors" + "github.com/educates/educates-training-platform/client-programs/pkg/tunnel" "github.com/spf13/cobra" ) @@ -15,46 +9,6 @@ type TunnelConnectOptions struct { Url string } -type session struct { - ws *websocket.Conn - errChan chan error -} - -func (o *TunnelConnectOptions) Run(cmd *cobra.Command) error { - dest, err := url.Parse(o.Url) - - if err != nil { - return errors.Wrap(err, "unable to parse websocket URL") - } - - originURL := *dest - - origin := originURL.String() - - headers := make(http.Header) - headers.Add("Origin", origin) - - dialer := websocket.Dialer{} - - ws, _, err := dialer.Dial(origin, headers) - - if err != nil { - return errors.Wrap(err, "unable to connect to websocket URL") - } - - sess := &session{ - ws: ws, - errChan: make(chan error), - } - - go sess.readInput() - go sess.readRemote() - - os.Stderr.WriteString(fmt.Sprintf("%s\n", <-sess.errChan)) - - return nil -} - func (p *ProjectInfo) NewTunnelConnectCmd() *cobra.Command { var o TunnelConnectOptions @@ -62,7 +16,7 @@ func (p *ProjectInfo) NewTunnelConnectCmd() *cobra.Command { Args: cobra.NoArgs, Use: "connect", Short: "SSH proxy for tunnelling over websockets", - RunE: func(cmd *cobra.Command, _ []string) error { return o.Run(cmd) }, + RunE: func(cmd *cobra.Command, _ []string) error { return tunnel.NewTunnel(o.Url).Start() }, } c.Flags().StringVar( @@ -76,46 +30,3 @@ func (p *ProjectInfo) NewTunnelConnectCmd() *cobra.Command { return c } - -func (s *session) readInput() { - in := os.Stdin - - const BUF_SIZE = 16384 - bufOut := make([]byte, BUF_SIZE) - - for { - var n int - var err error - - if n, err = in.Read(bufOut); err != nil || n == 0 { - break - } - - if err = s.ws.WriteMessage(websocket.BinaryMessage, bufOut[0:n]); err != nil { - break - } - } -} - -func (s *session) readRemote() { - out := os.Stdout - - for { - msgType, buf, err := s.ws.ReadMessage() - - if err != nil { - s.errChan <- err - return - } - - switch msgType { - case websocket.BinaryMessage: - if _, err = out.Write(buf); err != nil { - return - } - default: - s.errChan <- fmt.Errorf("unexpected websocket frame type: %d", msgType) - return - } - } -} diff --git a/client-programs/pkg/cmd/workshop_export_cmd.go b/client-programs/pkg/cmd/workshop_export_cmd.go index 51a706bd2..7efb647a0 100644 --- a/client-programs/pkg/cmd/workshop_export_cmd.go +++ b/client-programs/pkg/cmd/workshop_export_cmd.go @@ -1,21 +1,30 @@ package cmd import ( - "fmt" "os" "path/filepath" - "strings" yttcmd "carvel.dev/ytt/pkg/cmd/template" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" + "github.com/educates/educates-training-platform/client-programs/pkg/workshops" "github.com/pkg/errors" "github.com/spf13/cobra" - "gopkg.in/yaml.v2" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/kubectl/pkg/scheme" ) +var workshopExportExample = ` + # Export workshop resource definition in current directory + educates workshop export + + # Export workshop resource definition in my-workshop directory + educates workshop export my-workshop + + # Export workshop resource definition in my-workshop directory in a different workshop.yaml file + educates workshop export my-workshop --workshop-file ./workshop.yaml + + # Export workshop resource definition with data values + educates workshop export --image-repository ghcr.io/educates --workshop-version 1.0.0 +` + type FilesExportOptions struct { Repository string WorkshopFile string @@ -43,83 +52,32 @@ func (o *FilesExportOptions) Run(args []string) error { if err != nil || !fileInfo.IsDir() { return errors.New("workshop directory does not exist or path is not a directory") } - - return o.Export(directory) -} - -func (o *FilesExportOptions) Export(directory string) error { - // If image name hasn't been supplied read workshop definition file and - // try to work out image name to Export workshop as. - - rootDirectory := directory - workshopFilePath := o.WorkshopFile - - if !filepath.IsAbs(workshopFilePath) { - workshopFilePath = filepath.Join(rootDirectory, workshopFilePath) - } - - workshopFileData, err := os.ReadFile(workshopFilePath) - - if err != nil { - return errors.Wrapf(err, "cannot open workshop definition %q", workshopFilePath) - } - - // Process the workshop YAML data for ytt templating and data variables. - - if workshopFileData, err = processWorkshopDefinition(workshopFileData, o.DataValuesFlags); err != nil { - return errors.Wrap(err, "unable to process workshop definition as template") - } - - workshopFileData = []byte(strings.ReplaceAll(string(workshopFileData), "$(image_repository)", o.Repository)) - workshopFileData = []byte(strings.ReplaceAll(string(workshopFileData), "$(workshop_version)", o.WorkshopVersion)) - - decoder := serializer.NewCodecFactory(scheme.Scheme).UniversalDecoder() - - workshop := &unstructured.Unstructured{} - - err = runtime.DecodeInto(decoder, workshopFileData, workshop) - - if err != nil { - return errors.Wrap(err, "couldn't parse workshop definition") - } - - if workshop.GetAPIVersion() != "training.educates.dev/v1beta1" || workshop.GetKind() != "Workshop" { - return errors.New("invalid type for workshop definition") - } - - // Insert workshop version property if not specified. - - _, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") - - if !found && o.WorkshopVersion != "latest" { - unstructured.SetNestedField(workshop.Object, o.WorkshopVersion, "spec", "version") - } - - // Remove the publish section as will not be accurate after publising. - - unstructured.RemoveNestedField(workshop.Object, "spec", "publish") - - // Export modified workshop definition file. - - workshopFileData, err = yaml.Marshal(&workshop.Object) - - if err != nil { - return errors.Wrap(err, "couldn't convert workshop definition back to YAML") + config := workshops.WorkshopExportConfig{ + Repository: o.Repository, + WorkshopFile: o.WorkshopFile, + WorkshopVersion: o.WorkshopVersion, + DataValuesFlags: o.DataValuesFlags, } - fmt.Print(string(workshopFileData)) + manager := workshops.NewWorkshopManager() - return nil + return manager.Export(directory, &config) } func (p *ProjectInfo) NewWorkshopExportCmd() *cobra.Command { var o FilesExportOptions var c = &cobra.Command{ - Args: cobra.MaximumNArgs(1), + Args: func(cmd *cobra.Command, args []string) error { + if len(args) > 1 { + return utils.CmdError(cmd, "too many arguments", "[PATH]") + } + return nil + }, Use: "export [PATH]", Short: "Export workshop resource definition", RunE: func(cmd *cobra.Command, args []string) error { return o.Run(args) }, + Example: workshopExportExample, } c.Flags().StringVar( diff --git a/client-programs/pkg/cmd/workshop_new_cmd.go b/client-programs/pkg/cmd/workshop_new_cmd.go index bb8e52238..81ae17bc1 100644 --- a/client-programs/pkg/cmd/workshop_new_cmd.go +++ b/client-programs/pkg/cmd/workshop_new_cmd.go @@ -9,6 +9,7 @@ import ( "github.com/spf13/cobra" "github.com/educates/educates-training-platform/client-programs/pkg/templates" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" ) type WorkshopNewOptions struct { @@ -19,11 +20,29 @@ type WorkshopNewOptions struct { Image string } +var workshopNewExample = ` + # Create workshop files from template in my-workshop directory + educates workshop new my-workshop + + # Create workshop files from template in my-workshop directory + educates workshop new my-workshop --template hugo (default template is hugo) + + # Create workshop files from template in my-workshop directory with a different name + educates workshop new my-workshop --name "my-workshop" --title "My Workshop" --description "This is a workshop about my workshop" +` func (p *ProjectInfo) NewWorkshopNewCmd() *cobra.Command { var o WorkshopNewOptions var c = &cobra.Command{ - Args: cobra.ExactArgs(1), + Args: func(cmd *cobra.Command, args []string) error { + if len(args) < 1 { + return utils.CmdError(cmd, "path is required", "PATH") + } + if len(args) > 1 { + return utils.CmdError(cmd, "too many arguments", "PATH") + } + return nil + }, Use: "new PATH", Short: "Create workshop files from template", RunE: func(_ *cobra.Command, args []string) error { @@ -60,6 +79,7 @@ func (p *ProjectInfo) NewWorkshopNewCmd() *cobra.Command { return template.Apply(directory, parameters) }, + Example: workshopNewExample, } c.Flags().StringVarP( diff --git a/client-programs/pkg/cmd/workshop_publish_cmd.go b/client-programs/pkg/cmd/workshop_publish_cmd.go index b802d8e2f..58bc76b2c 100644 --- a/client-programs/pkg/cmd/workshop_publish_cmd.go +++ b/client-programs/pkg/cmd/workshop_publish_cmd.go @@ -1,29 +1,16 @@ package cmd import ( - "bytes" - "fmt" - "log" "os" "path/filepath" - "strings" "time" imgpkgcmd "carvel.dev/imgpkg/pkg/imgpkg/cmd" - "carvel.dev/kapp/pkg/kapp/cmd" - vendirsync "carvel.dev/vendir/pkg/vendir/cmd" yttcmd "carvel.dev/ytt/pkg/cmd/template" - yttcmdui "carvel.dev/ytt/pkg/cmd/ui" - "carvel.dev/ytt/pkg/files" - "carvel.dev/ytt/pkg/yamlmeta" - "github.com/cppforlife/go-cli-ui/ui" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" + "github.com/educates/educates-training-platform/client-programs/pkg/workshops" "github.com/pkg/errors" "github.com/spf13/cobra" - "gopkg.in/yaml.v2" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/kubectl/pkg/scheme" ) type FilesPublishOptions struct { @@ -36,6 +23,19 @@ type FilesPublishOptions struct { DataValuesFlags yttcmd.DataValuesFlags } +var workshopPublishExample = ` + # Publish workshop files to repository in current directory + educates workshop publish + + # Publish workshop files to repository in my-workshop directory + educates workshop publish my-workshop + + # Publish workshop files to repository with a specific image in my-workshop directory + educates workshop publish my-workshop --image=my-workshop-image-files + + # Publish workshop files to repository with a specific image and repository in my-workshop directory + educates workshop publish my-workshop --image=my-workshop-image-files --image-repository=ghcr.io/educates --workshop-version=1.0.0 +` func (o *FilesPublishOptions) Run(args []string) error { var err error @@ -57,245 +57,36 @@ func (o *FilesPublishOptions) Run(args []string) error { return errors.New("workshop directory does not exist or path is not a directory") } - return o.Publish(directory) -} - -func (o *FilesPublishOptions) Publish(directory string) error { - // If image name hasn't been supplied read workshop definition file and - // try to work out image name to publish workshop as. - - rootDirectory := directory - workshopFilePath := o.WorkshopFile - - workingDirectory, err := os.Getwd() - - if err != nil { - return errors.Wrap(err, "cannot determine current working directory") - } - - includePaths := []string{directory} - excludePaths := []string{".git"} - - if !filepath.IsAbs(workshopFilePath) { - workshopFilePath = filepath.Join(rootDirectory, workshopFilePath) - } - - workshopFileData, err := os.ReadFile(workshopFilePath) - - if err != nil { - return errors.Wrapf(err, "cannot open workshop definition %q", workshopFilePath) - } - - // Process the workshop YAML data for ytt templating and data variables. - - if workshopFileData, err = processWorkshopDefinition(workshopFileData, o.DataValuesFlags); err != nil { - return errors.Wrap(err, "unable to process workshop definition as template") - } - - workshopFileData = []byte(strings.ReplaceAll(string(workshopFileData), "$(image_repository)", o.Repository)) - workshopFileData = []byte(strings.ReplaceAll(string(workshopFileData), "$(workshop_version)", o.WorkshopVersion)) - - decoder := serializer.NewCodecFactory(scheme.Scheme).UniversalDecoder() - - workshop := &unstructured.Unstructured{} - - err = runtime.DecodeInto(decoder, workshopFileData, workshop) - - if err != nil { - return errors.Wrap(err, "couldn't parse workshop definition") - } - - fmt.Printf("Processing workshop with name %q.\n", workshop.GetName()) - - if workshop.GetAPIVersion() != "training.educates.dev/v1beta1" || workshop.GetKind() != "Workshop" { - return errors.New("invalid type for workshop definition") - } - - image := o.Image - - if image == "" { - image, _, _ = unstructured.NestedString(workshop.Object, "spec", "publish", "image") - } - - if image == "" { - return errors.Errorf("cannot find image name for publishing workshop %q", workshopFilePath) - } - - // Extract vendir snippet describing subset of files to package up as the - // workshop image. - - confUI := ui.NewConfUI(ui.NewNoopLogger()) - - uiFlags := cmd.UIFlags{ - Color: true, - JSON: false, - NonInteractive: true, - } - - uiFlags.ConfigureUI(confUI) - - defer confUI.Flush() - - if fileArtifacts, found, _ := unstructured.NestedSlice(workshop.Object, "spec", "publish", "files"); found && len(fileArtifacts) != 0 { - tempDir, err := os.MkdirTemp("", "educates-imgpkg") - - if err != nil { - return errors.Wrapf(err, "unable to create temporary working directory") - } - - defer os.RemoveAll(tempDir) - - for _, artifactEntry := range fileArtifacts { - vendirConfig := map[string]interface{}{ - "apiVersion": "vendir.k14s.io/v1alpha1", - "kind": "Config", - "directories": []interface{}{}, - } - - dir := filepath.Join(tempDir, "files") - - if filePath, found := artifactEntry.(map[string]interface{})["path"].(string); found { - dir = filepath.Join(tempDir, "files", filepath.Clean(filePath)) - } - - if directoryConfig, found := artifactEntry.(map[string]interface{})["directory"]; found { - if directoryPath, found := directoryConfig.(map[string]interface{})["path"].(string); found { - if !filepath.IsAbs(directoryPath) { - directoryConfig.(map[string]interface{})["path"] = filepath.Join(directory, directoryPath) - } - } - } - - artifactEntry.(map[string]interface{})["path"] = "." - - directoryConfig := map[string]interface{}{ - "path": dir, - "contents": []interface{}{artifactEntry}, - } - - vendirConfig["directories"] = append(vendirConfig["directories"].([]interface{}), directoryConfig) - - yamlData, err := yaml.Marshal(&vendirConfig) - - if err != nil { - return errors.Wrap(err, "unable to generate vendir config") - } - - vendirConfigFile, err := os.Create(filepath.Join(tempDir, "vendir.yml")) - - if err != nil { - return errors.Wrap(err, "unable to create vendir config file") - } - - defer vendirConfigFile.Close() - - _, err = vendirConfigFile.Write(yamlData) - - if err != nil { - return errors.Wrap(err, "unable to write vendir config file") - } - - syncOptions := vendirsync.NewSyncOptions(confUI) - - syncOptions.Directories = nil - syncOptions.Files = []string{filepath.Join(tempDir, "vendir.yml")} - - // Note that Chdir here actually changes the process working directory. - - syncOptions.LockFile = filepath.Join(tempDir, "lock-file") - syncOptions.Locked = false - syncOptions.Chdir = tempDir - syncOptions.AllowAllSymlinkDestinations = false - - if err = syncOptions.Run(); err != nil { - fmt.Println(string(yamlData)) - - return errors.Wrap(err, "failed to prepare image files for publishing") - } - } - - // Restore working directory as was changed. - - os.Chdir((workingDirectory)) - - rootDirectory = filepath.Join(tempDir, "files") - includePaths = []string{rootDirectory} - } - - // Now publish workshop directory contents as OCI image artifact. - - fmt.Printf("Publishing workshop files to %q.\n", image) - - pushOptions := imgpkgcmd.NewPushOptions(confUI) - - pushOptions.ImageFlags.Image = image - pushOptions.FileFlags.Files = append(pushOptions.FileFlags.Files, includePaths...) - pushOptions.FileFlags.ExcludedFilePaths = append(pushOptions.FileFlags.ExcludedFilePaths, excludePaths...) - - pushOptions.RegistryFlags = o.RegistryFlags - - err = pushOptions.Run() - - if err != nil { - return errors.Wrap(err, "unable to push image artifact for workshop") + config := workshops.WorkshopPublishConfig{ + Image: o.Image, + Repository: o.Repository, + WorkshopFile: o.WorkshopFile, + ExportWorkshop: o.ExportWorkshop, + WorkshopVersion: o.WorkshopVersion, + RegistryFlags: o.RegistryFlags, + DataValuesFlags: o.DataValuesFlags, } - // We add a newline to output for better readability. - fmt.Println() - - // Export modified workshop definition file. - - exportWorkshop := o.ExportWorkshop - - if exportWorkshop != "" { - // Insert workshop version property if not specified. - - _, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") - - if !found && o.WorkshopVersion != "latest" { - unstructured.SetNestedField(workshop.Object, o.WorkshopVersion, "spec", "version") - } - - // Remove the publish section as will not be accurate after publising. + m := workshops.NewWorkshopManager() - unstructured.RemoveNestedField(workshop.Object, "spec", "publish") - - workshopFileData, err = yaml.Marshal(&workshop.Object) - - if err != nil { - return errors.Wrap(err, "couldn't convert workshop definition back to YAML") - } - - if !filepath.IsAbs(exportWorkshop) { - exportWorkshop = filepath.Join(workingDirectory, exportWorkshop) - } - - exportWorkshopFile, err := os.Create(exportWorkshop) - - if err != nil { - return errors.Wrap(err, "unable to create exported workshop definition file") - } - - defer exportWorkshopFile.Close() - - _, err = exportWorkshopFile.Write(workshopFileData) - - if err != nil { - return errors.Wrap(err, "unable to write exported workshop definition file") - } - } - - return nil + return m.Publish(directory, &config) } + func (p *ProjectInfo) NewWorkshopPublishCmd() *cobra.Command { var o FilesPublishOptions var c = &cobra.Command{ - Args: cobra.MaximumNArgs(1), + Args: func(cmd *cobra.Command, args []string) error { + if len(args) > 1 { + return utils.CmdError(cmd, "too many arguments", "[PATH]") + } + return nil + }, Use: "publish [PATH]", Short: "Publish workshop files to repository", RunE: func(cmd *cobra.Command, args []string) error { return o.Run(args) }, + Example: workshopPublishExample, } c.Flags().StringVar( @@ -427,35 +218,3 @@ func (p *ProjectInfo) NewWorkshopPublishCmd() *cobra.Command { return c } - -func processWorkshopDefinition(yamlData []byte, dataValueFlags yttcmd.DataValuesFlags) ([]byte, error) { - templatingOptions := yttcmd.NewOptions() - - templatingOptions.IgnoreUnknownComments = true - - templatingOptions.DataValuesFlags = dataValueFlags - - var filesToProcess []*files.File - - mainInputFile := files.MustNewFileFromSource(files.NewBytesSource("workshop.yaml", yamlData)) - - filesToProcess = append(filesToProcess, mainInputFile) - - logUI := yttcmdui.NewCustomWriterTTY(false, log.Writer(), log.Writer()) - - output := templatingOptions.RunWithFiles(yttcmd.Input{Files: filesToProcess}, logUI) - - if output.Err != nil { - return []byte{}, fmt.Errorf("execution of ytt failed: %s", output.Err) - } - - if len(output.DocSet.Items) == 0 { - return []byte{}, nil - } - - var buf bytes.Buffer - - yamlmeta.NewYAMLPrinter(&buf).Print(output.DocSet.Items[0]) - - return buf.Bytes(), nil -} diff --git a/client-programs/pkg/docker/workshop_manager.go b/client-programs/pkg/docker/workshop_manager.go new file mode 100644 index 000000000..2f7a2783f --- /dev/null +++ b/client-programs/pkg/docker/workshop_manager.go @@ -0,0 +1,1007 @@ +package docker + +import ( + "bytes" + "context" + "fmt" + "io" + "os" + "path" + "path/filepath" + "runtime" + "slices" + "strings" + "sync" + "text/template" + + yttcmd "carvel.dev/ytt/pkg/cmd/template" + composeloader "github.com/compose-spec/compose-go/v2/loader" + composetypes "github.com/compose-spec/compose-go/v2/types" + "github.com/docker/cli/cli/command" + "github.com/docker/cli/cli/flags" + "github.com/docker/compose/v5/pkg/api" + "github.com/docker/compose/v5/pkg/compose" + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/client" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" + "github.com/educates/educates-training-platform/client-programs/pkg/workshops" + "github.com/pkg/errors" + "go.yaml.in/yaml/v2" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "sigs.k8s.io/kind/pkg/cluster" + "sigs.k8s.io/kind/pkg/cmd" +) + +const containerScript = `exec bash -s << "EOF" +mkdir -p /opt/eduk8s/config +cat > /opt/eduk8s/config/workshop.yaml << "EOS" +{{ .WorkshopConfig -}} +EOS +{{ if .Assets -}} +cat > /opt/eduk8s/config/vendir-assets-01.yaml << "EOS" +apiVersion: vendir.k14s.io/v1alpha1 +kind: Config +directories: +- path: /opt/assets/files + contents: + - directory: + path: /opt/eduk8s/mnt/assets + path: . +EOS +{{ else -}} +{{ range $k, $v := .VendirFilesConfig -}} +{{ $off := inc $k -}} +cat > /opt/eduk8s/config/vendir-assets-{{ printf "%02d" $off }}.yaml << "EOS" +{{ $v -}} +EOS +{{ end -}} +{{ end -}} +{{ if .VendirPackagesConfig -}} +cat > /opt/eduk8s/config/vendir-packages.yaml << "EOS" +{{ .VendirPackagesConfig -}} +EOS +{{ end -}} +{{ if .KubeConfig -}} +mkdir -p /opt/kubeconfig +cat > /opt/kubeconfig/config << "EOS" +{{ .KubeConfig -}} +EOS +{{ end -}} +exec start-container +EOF +` + +type DockerWorkshopsManager struct { + Statuses map[string]DockerWorkshopDetails + StatusesMutex sync.Mutex + composeService api.Compose + composeServiceMu sync.Mutex +} + +func NewDockerWorkshopsManager() DockerWorkshopsManager { + return DockerWorkshopsManager{ + Statuses: map[string]DockerWorkshopDetails{}, + StatusesMutex: sync.Mutex{}, + } +} + +type DockerWorkshopDetails struct { + Name string `json:"name"` + Url string `json:"url,omitempty"` + Source string `json:"source,omitempty"` + Status string `json:"status"` +} + +type DockerWorkshopDeployConfig struct { + Path string + Host string + Port uint + LocalRepository string + DisableOpenBrowser bool + ImageRepository string + ImageVersion string + Cluster string + KubeConfig string + Assets string + WorkshopFile string + WorkshopImage string + WorkshopVersion string + DataValuesFlags yttcmd.DataValuesFlags +} + + +func (m *DockerWorkshopsManager) WorkshopStatus(name string) (DockerWorkshopDetails, bool) { + workshops, err := m.ListWorkhops() + + if err != nil { + return DockerWorkshopDetails{}, false + } + + for _, workshop := range workshops { + if workshop.Name == name { + return workshop, true + } + } + + return DockerWorkshopDetails{}, false +} + +func (m *DockerWorkshopsManager) SetWorkshopStatus(name string, url string, source string, status string) { + m.StatusesMutex.Lock() + + m.Statuses[name] = DockerWorkshopDetails{ + Name: name, + Url: url, + Source: source, + Status: status, + } + + m.StatusesMutex.Unlock() +} + +func (m *DockerWorkshopsManager) ClearWorkshopStatus(name string) { + m.StatusesMutex.Lock() + + delete(m.Statuses, name) + + m.StatusesMutex.Unlock() +} + +func (m *DockerWorkshopsManager) ListWorkhops() ([]DockerWorkshopDetails, error) { + setOfWorkshops := map[string]DockerWorkshopDetails{} + workshopsList := []DockerWorkshopDetails{} + + ctx := context.Background() + + cli, err := client.NewClientWithOpts(client.FromEnv) + + if err != nil { + return nil, errors.Wrap(err, "unable to create docker client") + } + + containers, err := cli.ContainerList(ctx, container.ListOptions{}) + + if err != nil { + return nil, errors.Wrap(err, "unable to list containers") + } + + m.StatusesMutex.Lock() + + for _, details := range m.Statuses { + if details.Status == "Starting" { + setOfWorkshops[details.Name] = details + } + } + + defer m.StatusesMutex.Unlock() + + for _, container := range containers { + url, found := container.Labels["training.educates.dev/url"] + source := container.Labels["training.educates.dev/source"] + instance := container.Labels["training.educates.dev/session"] + + details, statusFound := m.Statuses[instance] + + status := "Running" + + if statusFound { + status = details.Status + } + + if found && url != "" && len(container.Names) != 0 { + setOfWorkshops[instance] = DockerWorkshopDetails{ + Name: instance, + Url: url, + Source: source, + Status: status, + } + } + } + + for _, details := range setOfWorkshops { + workshopsList = append(workshopsList, details) + } + + return workshopsList, nil +} + +// GetComposeService returns a ComposeService instance, initializing it if necessary. +// It uses a singleton pattern to reuse the same service instance across operations. +func (m *DockerWorkshopsManager) GetComposeService(stdout io.Writer, stderr io.Writer) (api.Compose, error) { + m.composeServiceMu.Lock() + defer m.composeServiceMu.Unlock() + + if m.composeService != nil { + return m.composeService, nil + } + + dockerCLI, err := command.NewDockerCli() + if err != nil { + return nil, errors.Wrap(err, "unable to create docker CLI") + } + + err = dockerCLI.Initialize(&flags.ClientOptions{}) + if err != nil { + return nil, errors.Wrap(err, "unable to initialize docker CLI") + } + + // Create ComposeService with options for I/O redirection and non-interactive mode + service, err := compose.NewComposeService( + dockerCLI, + compose.WithOutputStream(stdout), + compose.WithErrorStream(stderr), + compose.WithPrompt(compose.AlwaysOkPrompt()), + compose.WithMaxConcurrency(4), + ) + if err != nil { + return nil, errors.Wrap(err, "unable to create compose service") + } + + m.composeService = service + return service, nil +} + + +func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployConfig, stdout io.Writer, stderr io.Writer) (string, error) { + var err error + + // If path not provided assume the current working directory. When loading + // the workshop will then expect the workshop definition to reside in the + // resources/workshop.yaml file under the directory, the same as if a + // directory path was provided explicitly. + + if o.Path == "" { + o.Path = "." + } + + // Load the workshop definition. The path can be a HTTP/HTTPS URL for a + // local file system path for a directory or file. + + var workshop *unstructured.Unstructured + + if workshop, err = workshops.LoadWorkshopDefinition("", o.Path, workshops.DefaultPortalName, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + return "", err + } + + name := workshop.GetName() + + m.SetWorkshopStatus(name, "", o.Path, "Starting") + + defer m.ClearWorkshopStatus(name) + + originalName := workshop.GetAnnotations()["training.educates.dev/workshop"] + + configFileDir := utils.GetEducatesHomeDir() + composeConfigDir := path.Join(configFileDir, "compose", name) + + err = os.MkdirAll(composeConfigDir, os.ModePerm) + + if err != nil { + return name, errors.Wrapf(err, "unable to create workshops compose directory") + } + + ctx := context.Background() + + cli, err := client.NewClientWithOpts(client.FromEnv) + + if err != nil { + return name, errors.Wrap(err, "unable to create docker client") + } + + _, err = cli.ContainerInspect(ctx, name) + + if err == nil { + return name, errors.New("this workshop is already running") + } + + registryNetwork := false + + if o.LocalRepository == "localhost:5001" { + o.LocalRepository = "registry.docker.local:5000" + } + + var registryIP string + + registryInfo, err := cli.ContainerInspect(ctx, "educates-registry") + + if err == nil { + educatesNetwork, exists := registryInfo.NetworkSettings.Networks["educates"] + + if !exists { + return name, errors.New("registry is not attached to educates network") + } + + registryNetwork = true + registryIP = educatesNetwork.IPAddress + } else { + o.LocalRepository = "" + } + + var kubeConfigData string + + if o.KubeConfig != "" { + kubeConfigBytes, err := os.ReadFile(o.KubeConfig) + + if err != nil { + return name, errors.Wrap(err, "unable to read kubeconfig file") + } + + kubeConfigData = string(kubeConfigBytes) + } + + if o.Cluster != "" { + kubeConfigData, err = generateClusterKubeconfig(o.Cluster) + + if err != nil { + return name, err + } + } + + var workshopConfigData string + var vendirFilesConfigData []string + var vendirPackagesConfigData string + var workshopImageName string + + var workshopPortsConfig []composetypes.ServicePortConfig + var workshopVolumesConfig []composetypes.ServiceVolumeConfig + + var workshopEnvironment []string + var workshopLabels map[string]string + var workshopExtraHosts map[string]string + + var workshopComposeProject *composetypes.Project + + if workshopConfigData, err = generateWorkshopConfig(workshop); err != nil { + return name, err + } + + if vendirFilesConfigData, err = generateVendirFilesConfig(workshop, originalName, o.LocalRepository, o.WorkshopVersion); err != nil { + return name, err + } + + if vendirPackagesConfigData, err = generateVendirPackagesConfig(workshop, originalName, o.LocalRepository, o.WorkshopVersion); err != nil { + return name, err + } + + if workshopImageName, err = generateWorkshopImageName(workshop, o.LocalRepository, o.ImageRepository, o.ImageVersion, o.WorkshopImage, o.WorkshopVersion); err != nil { + return name, err + } + + if workshopPortsConfig, err = composetypes.ParsePortConfig(fmt.Sprintf("%s:%d:10081", o.Host, o.Port)); err != nil { + return name, errors.Wrap(err, "unable to generate workshop ports config") + } + + if workshopVolumesConfig, err = generateWorkshopVolumeMounts(workshop, o.Assets); err != nil { + return name, err + } + + if workshopEnvironment, err = generateWorkshopEnvironment(workshop, o.LocalRepository, o.Host, o.Port); err != nil { + return name, err + } + + if workshopLabels, err = generateWorkshopLabels(workshop, o.Host, o.Port); err != nil { + return name, err + } + + if registryIP != "" { + if workshopExtraHosts, err = generateWorkshopExtraHosts(workshop, registryIP); err != nil { + return name, err + } + } + + if workshopComposeProject, err = extractWorkshopComposeConfig(workshop); err != nil { + return name, err + } + + type TemplateInputs struct { + WorkshopConfig string + VendirFilesConfig []string + VendirPackagesConfig string + KubeConfig string + Assets string + } + + inputs := TemplateInputs{ + WorkshopConfig: workshopConfigData, + VendirFilesConfig: vendirFilesConfigData, + VendirPackagesConfig: vendirPackagesConfigData, + KubeConfig: kubeConfigData, + Assets: o.Assets, + } + + funcMap := template.FuncMap{ + "inc": func(i int) int { + return i + 1 + }, + } + + containerScriptTemplate, err := template.New("entrypoint").Funcs(funcMap).Parse(containerScript) + + if err != nil { + return name, errors.Wrap(err, "not able to parse container script template") + } + + var containerScriptData bytes.Buffer + + err = containerScriptTemplate.Execute(&containerScriptData, inputs) + + if err != nil { + return name, errors.Wrap(err, "not able to generate container script") + } + + networks := map[string]*composetypes.ServiceNetworkConfig{ + "default": {}, + } + + if registryNetwork { + networks["educates"] = &composetypes.ServiceNetworkConfig{} + } + + var extraHostsList composetypes.HostsList + if len(workshopExtraHosts) > 0 { + extraHostsList = make(composetypes.HostsList, len(workshopExtraHosts)) + for hostname, ip := range workshopExtraHosts { + extraHostsList[hostname] = []string{ip} + } + } + + workshopServiceConfig := composetypes.ServiceConfig{ + Name: "workshop", + Image: workshopImageName, + Command: composetypes.ShellCommand([]string{"bash", "-c", containerScriptData.String()}), + User: "1001:0", + Ports: workshopPortsConfig, + Volumes: workshopVolumesConfig, + Environment: composetypes.NewMappingWithEquals(workshopEnvironment), + Labels: composetypes.Labels(workshopLabels), + ExtraHosts: extraHostsList, + DependsOn: composetypes.DependsOnConfig{}, + Networks: networks, + } + + if o.Cluster != "" { + workshopServiceConfig.Networks["kind"] = &composetypes.ServiceNetworkConfig{} + } + + dockerEnabled, found, _ := unstructured.NestedBool(workshop.Object, "spec", "session", "applications", "docker", "enabled") + + if found && dockerEnabled { + extraServices, _, _ := unstructured.NestedMap(workshop.Object, "spec", "session", "applications", "docker", "compose") + + socketEnabledDefault := true + + if len(extraServices) != 0 { + socketEnabledDefault = false + } + + socketEnabled, found, _ := unstructured.NestedBool(workshop.Object, "spec", "session", "applications", "docker", "socket", "enabled") + + if !found { + socketEnabled = socketEnabledDefault + } + + if socketEnabled { + workshopServiceConfig.GroupAdd = []string{"docker"} + } + } + + workshopServices := composetypes.Services{ + "workshop": workshopServiceConfig, + } + + composeConfig := composetypes.Project{ + Name: originalName, + Services: workshopServices, + Networks: composetypes.Networks{ + "educates": composetypes.NetworkConfig{Name: "educates", External: true}, + }, + Volumes: composetypes.Volumes{ + "workshop": composetypes.VolumeConfig{}, + }, + } + + if workshopComposeProject != nil { + for serviceName, extraService := range workshopComposeProject.Services { + extraService.Ports = []composetypes.ServicePortConfig{} + + composeConfig.Services[serviceName] = extraService + + workshopServiceConfig.DependsOn[serviceName] = composetypes.ServiceDependency{ + Condition: composetypes.ServiceConditionStarted, + } + } + + for volumeName, extraVolume := range workshopComposeProject.Volumes { + if volumeName != "workshop" { + composeConfig.Volumes[volumeName] = extraVolume + } + } + } + + if o.Cluster != "" { + composeConfig.Networks["kind"] = composetypes.NetworkConfig{Name: "kind", External: true} + } + + composeConfigBytes, err := yaml.Marshal(&composeConfig) + + if err != nil { + return name, errors.Wrap(err, "failed to generate compose config") + } + + composeConfigFilePath := path.Join(composeConfigDir, "docker-compose.yaml") + + composeConfigFile, err := os.OpenFile(composeConfigFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) + + if err != nil { + return name, errors.Wrapf(err, "unable to create workshop config file %s", composeConfigFilePath) + } + + if _, err = composeConfigFile.Write(composeConfigBytes); err != nil { + return name, errors.Wrapf(err, "unable to write workshop config file %s", composeConfigFilePath) + } + + if err := composeConfigFile.Close(); err != nil { + return name, errors.Wrapf(err, "unable to close workshop config file %s", composeConfigFilePath) + } + + // Get ComposeService instance + service, err := m.GetComposeService(stdout, stderr) + if err != nil { + return name, errors.Wrap(err, "unable to get compose service") + } + + // Load the project from the compose file + project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ + ConfigPaths: []string{composeConfigFilePath}, + ProjectName: name, + }) + if err != nil { + return name, errors.Wrap(err, "failed to load project") + } + + // Start the services using SDK + err = service.Up(ctx, project, api.UpOptions{ + Create: api.CreateOptions{ + Recreate: api.RecreateDiverged, + RecreateDependencies: api.RecreateDiverged, + RemoveOrphans: false, + }, + Start: api.StartOptions{}, + }) + if err != nil { + return name, errors.Wrap(err, "unable to start workshop") + } + + return name, nil +} + +func (m *DockerWorkshopsManager) DeleteWorkshop(name string, stdout io.Writer, stderr io.Writer) error { + m.SetWorkshopStatus(name, "", "", "Stopping") + + defer m.ClearWorkshopStatus(name) + + ctx := context.Background() + + // Get ComposeService instance + service, err := m.GetComposeService(stdout, stderr) + if err != nil { + return errors.Wrap(err, "unable to get compose service") + } + + // Load the project to get the project name + configFileDir := utils.GetEducatesHomeDir() + composeConfigDir := path.Join(configFileDir, "compose", name) + composeConfigFilePath := path.Join(composeConfigDir, "docker-compose.yaml") + + // Try to load project, but if file doesn't exist, just use the name + project, err := service.LoadProject(ctx, api.ProjectLoadOptions{ + ConfigPaths: []string{composeConfigFilePath}, + ProjectName: name, + }) + if err != nil { + // If project can't be loaded, still try to remove by name + project = nil + } + + projectName := name + if project != nil { + projectName = project.Name + } + + // Stop and remove services using SDK + err = service.Down(ctx, projectName, api.DownOptions{ + RemoveOrphans: true, + Volumes: true, + }) + if err != nil { + return errors.Wrap(err, "unable to stop workshop") + } + + cli, err2 := client.NewClientWithOpts(client.FromEnv) + + if err2 != nil { + return errors.Wrap(err2, "unable to create docker client") + } + + err2 = cli.VolumeRemove(ctx, fmt.Sprintf("%s_workshop", name), false) + + if err2 != nil { + return errors.Wrap(err2, "unable to delete workshop volume") + } + + workshopConfigDir := path.Join(configFileDir, "workshops", name) + + os.RemoveAll(workshopConfigDir) + os.RemoveAll(composeConfigDir) + + return nil +} + + +func generateWorkshopConfig(workshop *unstructured.Unstructured) (string, error) { + workshopTitle, _, _ := unstructured.NestedFieldNoCopy(workshop.Object, "spec", "title") + workshopDescription, _, _ := unstructured.NestedFieldNoCopy(workshop.Object, "spec", "description") + applicationsConfig, _, _ := unstructured.NestedFieldNoCopy(workshop.Object, "spec", "session", "applications") + ingressesConfig, _, _ := unstructured.NestedSlice(workshop.Object, "spec", "session", "ingresses") + dashboardsConfig, _, _ := unstructured.NestedSlice(workshop.Object, "spec", "session", "dashboards") + + workshopConfig := map[string]interface{}{ + "spec": map[string]interface{}{ + "title": workshopTitle, + "description": workshopDescription, + "session": map[string]interface{}{ + "applications": applicationsConfig, + "ingresses": ingressesConfig, + "dashboards": dashboardsConfig, + }, + }, + } + + workshopConfigData, err := yaml.Marshal(&workshopConfig) + + if err != nil { + return "", errors.Wrap(err, "failed to generate workshop config") + } + + return string(workshopConfigData), nil +} + +func generateVendirFilesConfig(workshop *unstructured.Unstructured, name string, localRepository string, version string) ([]string, error) { + var vendirConfigs []string + + workshopVersion, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") + + if !found { + workshopVersion = version + } + + filesItems, found, _ := unstructured.NestedSlice(workshop.Object, "spec", "workshop", "files") + + if found && len(filesItems) != 0 { + for _, filesItem := range filesItems { + directoriesConfig := []map[string]interface{}{} + + tmpPath, found := filesItem.(map[string]interface{})["path"] + + var filesItemPath string + + if found { + filesItemPath = tmpPath.(string) + } else { + filesItemPath = "." + } + + filesItemPath = filepath.Clean(path.Join("/opt/assets/files", filesItemPath)) + + filesItem.(map[string]interface{})["path"] = "." + + directoriesConfig = append(directoriesConfig, map[string]interface{}{ + "path": filesItemPath, + "contents": []interface{}{filesItem}, + }) + + vendirConfig := map[string]interface{}{ + "apiVersion": "vendir.k14s.io/v1alpha1", + "kind": "Config", + "directories": directoriesConfig, + } + + vendirConfigBytes, err := yaml.Marshal(&vendirConfig) + + if err != nil { + return []string{}, errors.Wrap(err, "failed to generate vendir config") + } + + vendirConfigString := string(vendirConfigBytes) + + vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(image_repository)", localRepository) + vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(workshop_name)", name) + vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(workshop_version)", workshopVersion) + vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(platform_arch)", runtime.GOARCH) + + vendirConfigs = append(vendirConfigs, vendirConfigString) + } + } + + return vendirConfigs, nil +} + +func generateVendirPackagesConfig(workshop *unstructured.Unstructured, name string, localRepository string, version string) (string, error) { + var vendirConfigString string + + workshopVersion, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") + + if !found { + workshopVersion = version + } + + packagesItems, found, _ := unstructured.NestedSlice(workshop.Object, "spec", "workshop", "packages") + + if found && len(packagesItems) != 0 { + directoriesConfig := []map[string]interface{}{} + + for _, packagesItem := range packagesItems { + tmpPackagesItem := packagesItem.(map[string]interface{}) + + tmpName, found := tmpPackagesItem["name"] + + if !found { + continue + } + + packagesItemPath := filepath.Clean(path.Join("/opt/packages", tmpName.(string))) + + tmpPackagesFilesItem := tmpPackagesItem["files"] + + packagesFilesItem := tmpPackagesFilesItem.([]interface{}) + + for _, tmpEntry := range packagesFilesItem { + entry := tmpEntry.(map[string]interface{}) + + _, found = entry["path"] + + if !found { + entry["path"] = "." + } + } + + directoriesConfig = append(directoriesConfig, map[string]interface{}{ + "path": packagesItemPath, + "contents": packagesFilesItem, + }) + + } + + vendirConfig := map[string]interface{}{ + "apiVersion": "vendir.k14s.io/v1alpha1", + "kind": "Config", + "directories": directoriesConfig, + } + + vendirConfigBytes, err := yaml.Marshal(&vendirConfig) + + if err != nil { + return "", errors.Wrap(err, "failed to generate vendir config") + } + + vendirConfigString = string(vendirConfigBytes) + + vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(image_repository)", localRepository) + vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(workshop_name)", name) + vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(workshop_version)", workshopVersion) + } + + return vendirConfigString, nil +} + +func generateWorkshopImageName(workshop *unstructured.Unstructured, localRepository string, imageRepository string, baseImageVersion string, workshopImage string, workshopVersion string) (string, error) { + _, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") + + if found { + workshopVersion, _, _ = unstructured.NestedString(workshop.Object, "spec", "version") + } + + image, found, err := unstructured.NestedString(workshop.Object, "spec", "workshop", "image") + + if err != nil { + return "", errors.Wrapf(err, "unable to parse workshop definition") + } + + if !found || image == "" { + image = "base-environment:*" + } + + defaultImageVersion := strings.TrimSpace(baseImageVersion) + + if workshopImage != "" { + image = workshopImage + } else { + if defaultImageVersion == "latest" { + image = strings.ReplaceAll(image, "base-environment:*", fmt.Sprintf("localhost:5001/educates-base-environment:%s", defaultImageVersion)) + image = strings.ReplaceAll(image, "jdk8-environment:*", fmt.Sprintf("localhost:5001/educates-jdk8-environment:%s", defaultImageVersion)) + image = strings.ReplaceAll(image, "jdk11-environment:*", fmt.Sprintf("localhost:5001/educates-jdk11-environment:%s", defaultImageVersion)) + image = strings.ReplaceAll(image, "jdk17-environment:*", fmt.Sprintf("localhost:5001/educates-jdk17-environment:%s", defaultImageVersion)) + image = strings.ReplaceAll(image, "jdk21-environment:*", fmt.Sprintf("localhost:5001/educates-jdk21-environment:%s", defaultImageVersion)) + image = strings.ReplaceAll(image, "conda-environment:*", fmt.Sprintf("localhost:5001/educates-conda-environment:%s", defaultImageVersion)) + } else { + image = strings.ReplaceAll(image, "base-environment:*", fmt.Sprintf("%s/educates-base-environment:%s", imageRepository, defaultImageVersion)) + image = strings.ReplaceAll(image, "jdk8-environment:*", fmt.Sprintf("%s/educates-jdk8-environment:%s", imageRepository, defaultImageVersion)) + image = strings.ReplaceAll(image, "jdk11-environment:*", fmt.Sprintf("%s/educates-jdk11-environment:%s", imageRepository, defaultImageVersion)) + image = strings.ReplaceAll(image, "jdk17-environment:*", fmt.Sprintf("%s/educates-jdk17-environment:%s", imageRepository, defaultImageVersion)) + image = strings.ReplaceAll(image, "jdk21-environment:*", fmt.Sprintf("%s/educates-jdk21-environment:%s", imageRepository, defaultImageVersion)) + image = strings.ReplaceAll(image, "conda-environment:*", fmt.Sprintf("%s/educates-conda-environment:%s", imageRepository, defaultImageVersion)) + } + } + + image = strings.ReplaceAll(image, "$(image_repository)", localRepository) + image = strings.ReplaceAll(image, "$(workshop_version)", workshopVersion) + + return image, nil +} + +func generateWorkshopVolumeMounts(workshop *unstructured.Unstructured, assets string) ([]composetypes.ServiceVolumeConfig, error) { + filesMounts := []composetypes.ServiceVolumeConfig{ + { + Type: "volume", + Source: "workshop", + Target: "/home/eduk8s", + }, + } + + if assets != "" { + assets = filepath.Clean(assets) + assets, err := filepath.Abs(assets) + + if err != nil { + return []composetypes.ServiceVolumeConfig{}, errors.Wrap(err, "can't resolve local workshop assets path") + } + + filesMounts = append(filesMounts, composetypes.ServiceVolumeConfig{ + Type: "bind", + Source: assets, + Target: "/opt/eduk8s/mnt/assets", + ReadOnly: true, + }) + } + + dockerEnabled, found, _ := unstructured.NestedBool(workshop.Object, "spec", "session", "applications", "docker", "enabled") + + if found && dockerEnabled { + extraServices, _, _ := unstructured.NestedMap(workshop.Object, "spec", "session", "applications", "docker", "compose") + + socketEnabledDefault := true + + if len(extraServices) != 0 { + socketEnabledDefault = false + } + + socketEnabled, found, _ := unstructured.NestedBool(workshop.Object, "spec", "session", "applications", "docker", "socket", "enabled") + + if !found { + socketEnabled = socketEnabledDefault + } + + if socketEnabled { + if runtime.GOOS == "linux" { + filesMounts = append(filesMounts, composetypes.ServiceVolumeConfig{ + Type: "bind", + Source: "/var/run/docker.sock", + Target: "/var/run/docker/docker.sock", + ReadOnly: true, + }) + } else { + filesMounts = append(filesMounts, composetypes.ServiceVolumeConfig{ + Type: "bind", + Source: "/var/run/docker.sock.raw", + Target: "/var/run/docker/docker.sock", + ReadOnly: true, + }) + } + } + } + + return filesMounts, nil +} + +func generateWorkshopEnvironment(workshop *unstructured.Unstructured, localRepository string, host string, port uint) ([]string, error) { + domain := fmt.Sprintf("%s.nip.io", strings.ReplaceAll(host, ".", "-")) + + return []string{ + fmt.Sprintf("WORKSHOP_NAME=%s", workshop.GetName()), + "SESSION_NAME=workshop", + fmt.Sprintf("SESSION_URL=http://workshop.%s:%d", domain, port), + "INGRESS_PROTOCOL=http", + fmt.Sprintf("INGRESS_DOMAIN=%s", domain), + fmt.Sprintf("INGRESS_PORT_SUFFIX=:%d", port), + fmt.Sprintf("IMAGE_REPOSITORY=%s", localRepository), + }, nil +} + +func generateWorkshopLabels(workshop *unstructured.Unstructured, host string, port uint) (map[string]string, error) { + labels := workshop.GetAnnotations() + + domain := fmt.Sprintf("%s.nip.io", strings.ReplaceAll(host, ".", "-")) + + labels["training.educates.dev/url"] = fmt.Sprintf("http://workshop.%s:%d", domain, port) + labels["training.educates.dev/session"] = workshop.GetName() + + return labels, nil +} + +func generateWorkshopExtraHosts(workshop *unstructured.Unstructured, registryIP string) (map[string]string, error) { + hosts := map[string]string{} + + if registryIP != "" { + hosts["registry.docker.local"] = registryIP + } + + return hosts, nil +} + +func extractWorkshopComposeConfig(workshop *unstructured.Unstructured) (*composetypes.Project, error) { + composeConfigObj, found, _ := unstructured.NestedMap(workshop.Object, "spec", "session", "applications", "docker", "compose") + + if found { + composeConfigObjBytes, err := yaml.Marshal(&composeConfigObj) + + if err != nil { + return nil, errors.Wrap(err, "unable to parse workshop docker compose config") + } + + configFiles := composetypes.ConfigFile{ + Content: composeConfigObjBytes, + } + + composeConfigDetails := composetypes.ConfigDetails{ + ConfigFiles: []composetypes.ConfigFile{configFiles}, + } + + return composeloader.LoadWithContext(context.Background(), composeConfigDetails, func(options *composeloader.Options) { + options.SkipConsistencyCheck = true + options.SkipNormalization = true + options.ResolvePaths = false + options.SkipValidation = true + }) + } + + return nil, nil +} + +func generateClusterKubeconfig(name string) (string, error) { + provider := cluster.NewProvider( + cluster.ProviderWithLogger(cmd.NewLogger()), + ) + + clusters, err := provider.List() + + if err != nil { + return "", errors.Wrap(err, "unable to get list of clusters") + } + + if !slices.Contains(clusters, name) { + return "", errors.Errorf("cluster %s doesn't exist", name) + } + + file, err := os.CreateTemp("", "kubeconfig-") + + if err != nil { + return "", errors.Wrap(err, "unable to generate kubeconfig file") + } + + defer os.Remove(file.Name()) + + err = provider.ExportKubeConfig(name, file.Name(), true) + + if err != nil { + return "", errors.Wrap(err, "unable to generate kubeconfig file") + } + + kubeConfigData, err := os.ReadFile(file.Name()) + + if err != nil { + return "", errors.Wrap(err, "unable to generate kubeconfig file") + } + + return string(kubeConfigData), nil +} + diff --git a/client-programs/pkg/tunnel/tunnel.go b/client-programs/pkg/tunnel/tunnel.go new file mode 100644 index 000000000..20eb7e033 --- /dev/null +++ b/client-programs/pkg/tunnel/tunnel.go @@ -0,0 +1,104 @@ +package tunnel + +import ( + "fmt" + "net/http" + "net/url" + "os" + + "github.com/gorilla/websocket" + "github.com/pkg/errors" +) + +type session struct { + ws *websocket.Conn + errChan chan error +} + +type Tunnel struct { + Url string +} + +func NewTunnel(url string) *Tunnel { + return &Tunnel{ + Url: url, + } +} + +func (t *Tunnel) Start() error { + dest, err := url.Parse(t.Url) + + if err != nil { + return errors.Wrap(err, "unable to parse websocket URL") + } + + originURL := *dest + + origin := originURL.String() + + headers := make(http.Header) + headers.Add("Origin", origin) + + dialer := websocket.Dialer{} + + ws, _, err := dialer.Dial(origin, headers) + + if err != nil { + return errors.Wrap(err, "unable to connect to websocket URL") + } + + sess := &session{ + ws: ws, + errChan: make(chan error), + } + + go sess.readInput() + go sess.readRemote() + + os.Stderr.WriteString(fmt.Sprintf("%s\n", <-sess.errChan)) + + return nil +} + +func (s *session) readInput() { + in := os.Stdin + + const BUF_SIZE = 16384 + bufOut := make([]byte, BUF_SIZE) + + for { + var n int + var err error + + if n, err = in.Read(bufOut); err != nil || n == 0 { + break + } + + if err = s.ws.WriteMessage(websocket.BinaryMessage, bufOut[0:n]); err != nil { + break + } + } +} + +func (s *session) readRemote() { + out := os.Stdout + + for { + msgType, buf, err := s.ws.ReadMessage() + + if err != nil { + s.errChan <- err + return + } + + switch msgType { + case websocket.BinaryMessage: + if _, err = out.Write(buf); err != nil { + return + } + default: + s.errChan <- fmt.Errorf("unexpected websocket frame type: %d", msgType) + return + } + } +} diff --git a/client-programs/pkg/utils/cmd_error.go b/client-programs/pkg/utils/cmd_error.go new file mode 100644 index 000000000..f02156e73 --- /dev/null +++ b/client-programs/pkg/utils/cmd_error.go @@ -0,0 +1,23 @@ +package utils + +import ( + "fmt" + + "github.com/spf13/cobra" +) + +func CmdError(cmd *cobra.Command, errorMessage string, additionalMessage string) error { + return cmdError(cmd, errorMessage, additionalMessage, false) +} + +func CmdErrorFullUsage(cmd *cobra.Command, errorMessage string, additionalMessage string) error { + return cmdError(cmd, errorMessage, additionalMessage, true) +} + +func cmdError(cmd *cobra.Command, errorMessage string, additionalMessage string, fullUsage bool) error { + if fullUsage { + return fmt.Errorf("%s\n\n%s", errorMessage, cmd.UsageString()) + } + + return fmt.Errorf("%s\n\n%s %s\nRun '%s --help' for details.", errorMessage, cmd.CommandPath(), additionalMessage, cmd.CommandPath()) +} diff --git a/client-programs/pkg/workshops/constants.go b/client-programs/pkg/workshops/constants.go new file mode 100644 index 000000000..00d2d05e9 --- /dev/null +++ b/client-programs/pkg/workshops/constants.go @@ -0,0 +1,3 @@ +package workshops + +const DefaultPortalName = "educates-cli" diff --git a/client-programs/pkg/workshops/definition.go b/client-programs/pkg/workshops/definition.go new file mode 100644 index 000000000..611dc3534 --- /dev/null +++ b/client-programs/pkg/workshops/definition.go @@ -0,0 +1,232 @@ +package workshops + +import ( + "bytes" + "context" + "crypto/sha1" + "fmt" + "io" + "log" + "net/http" + "net/url" + "os" + "path/filepath" + + yttcmd "carvel.dev/ytt/pkg/cmd/template" + yttcmdui "carvel.dev/ytt/pkg/cmd/ui" + "carvel.dev/ytt/pkg/files" + "carvel.dev/ytt/pkg/yamlmeta" + "github.com/pkg/errors" + "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/dynamic" +) + + +func LoadWorkshopDefinition(name string, path string, portal string, workshopFile string, workshopVersion string, dataValueFlags yttcmd.DataValuesFlags) (*unstructured.Unstructured, error) { + // Parse the workshop location so we can determine if it is a local file + // or accessible using a HTTP/HTTPS URL. + + var urlInfo *url.URL + var err error + + if urlInfo, err = url.Parse(path); err != nil { + return nil, errors.Wrap(err, "unable to parse workshop location") + } + + // Check if file system path first (not HTTP/HTTPS) and if so normalize + // the path. If it the path references a directory, then extend the path + // so we look for the workshop file within that directory. + + if urlInfo.Scheme != "http" && urlInfo.Scheme != "https" { + path = filepath.Clean(path) + + if path, err = filepath.Abs(path); err != nil { + return nil, errors.Wrap(err, "couldn't convert workshop location to absolute path") + } + + if !filepath.IsAbs(workshopFile) { + fileInfo, err := os.Stat(path) + + if err != nil { + return nil, errors.Wrap(err, "couldn't test if workshop location is a directory") + } + + if fileInfo.IsDir() { + path = filepath.Join(path, workshopFile) + } + } else { + path = workshopFile + } + } + + // Read in the workshop definition as raw data ready for parsing. + + var workshopData []byte + + if urlInfo.Scheme != "http" && urlInfo.Scheme != "https" { + if workshopData, err = os.ReadFile(path); err != nil { + return nil, errors.Wrap(err, "couldn't read workshop definition data file") + } + } else { + var client http.Client + + resp, err := client.Get(path) + + if err != nil { + return nil, errors.Wrap(err, "couldn't download workshop definition from host") + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, errors.New("failed to download workshop definition from host") + } + + workshopData, err = io.ReadAll(resp.Body) + + if err != nil { + return nil, errors.Wrap(err, "failed to read workshop definition from host") + } + } + + // Process the workshop YAML data in case it contains ytt templating. + + if workshopData, err = ProcessWorkshopDefinition(workshopData, dataValueFlags); err != nil { + return nil, errors.Wrap(err, "unable to process workshop definition as template") + } + + // Parse the workshop definition. + + decoder := serializer.NewCodecFactory(scheme.Scheme).UniversalDecoder() + + workshop := &unstructured.Unstructured{} + + err = runtime.DecodeInto(decoder, workshopData, workshop) + + if err != nil { + return nil, errors.Wrap(err, "couldn't parse workshop definition") + } + + // Verify the type of resource definition. + + if workshop.GetAPIVersion() != "training.educates.dev/v1beta1" || workshop.GetKind() != "Workshop" { + return nil, errors.New("invalid type for workshop definition") + } + + // Add annotations recording details about original workshop location. + + annotations := workshop.GetAnnotations() + + if annotations == nil { + annotations = map[string]string{} + } + + annotations["training.educates.dev/workshop"] = workshop.GetName() + + if urlInfo.Scheme != "http" && urlInfo.Scheme != "https" { + annotations["training.educates.dev/source"] = fmt.Sprintf("file://%s", path) + } else { + annotations["training.educates.dev/source"] = path + } + + workshop.SetAnnotations(annotations) + + // Update the name for the workshop such that it incorporates a hash of + // the workshop location. + + if name == "" { + name = GenerateWorkshopName(path, workshop, portal) + } + + workshop.SetName(name) + + // Insert workshop version property if not specified. + + _, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") + + if !found && workshopVersion != "latest" { + unstructured.SetNestedField(workshop.Object, workshopVersion, "spec", "version") + } + + // Remove the publish section as will not be accurate after publising. + + unstructured.RemoveNestedField(workshop.Object, "spec", "publish") + + return workshop, nil +} + +func GenerateWorkshopName(path string, workshop *unstructured.Unstructured, portal string) string { + name := workshop.GetName() + + h := sha1.New() + + io.WriteString(h, path) + + hv := fmt.Sprintf("%x", h.Sum(nil)) + + name = fmt.Sprintf("%s--%s-%s", portal, name, hv[len(hv)-7:]) + + return name +} + +func GetWorkshopResource() schema.GroupVersionResource { + return schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshops"} +} + +func UpdateWorkshopResource(client dynamic.Interface, workshop *unstructured.Unstructured) error { + workshopsClient := client.Resource(GetWorkshopResource()) + + // _, err := workshopsClient.Apply(context.TODO(), workshop.GetName(), workshop, metav1.ApplyOptions{FieldManager: workshops.DefaultPortalName, Force: true}) + + workshopBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, workshop) + + if err != nil { + return errors.Wrapf(err, "unable to update workshop definition in cluster %q", workshop.GetName()) + } + + _, err = workshopsClient.Patch(context.TODO(), workshop.GetName(), types.ApplyPatchType, workshopBytes, metav1.ApplyOptions{FieldManager: DefaultPortalName, Force: true}.ToPatchOptions()) + + if err != nil { + return errors.Wrapf(err, "unable to update workshop definition in cluster %q", workshop.GetName()) + } + + return nil +} + +func ProcessWorkshopDefinition(yamlData []byte, dataValueFlags yttcmd.DataValuesFlags) ([]byte, error) { + templatingOptions := yttcmd.NewOptions() + + templatingOptions.IgnoreUnknownComments = true + + templatingOptions.DataValuesFlags = dataValueFlags + + var filesToProcess []*files.File + + mainInputFile := files.MustNewFileFromSource(files.NewBytesSource("workshop.yaml", yamlData)) + + filesToProcess = append(filesToProcess, mainInputFile) + + logUI := yttcmdui.NewCustomWriterTTY(false, log.Writer(), log.Writer()) + + output := templatingOptions.RunWithFiles(yttcmd.Input{Files: filesToProcess}, logUI) + + if output.Err != nil { + return []byte{}, fmt.Errorf("execution of ytt failed: %s", output.Err) + } + + if len(output.DocSet.Items) == 0 { + return []byte{}, nil + } + + var buf bytes.Buffer + + yamlmeta.NewYAMLPrinter(&buf).Print(output.DocSet.Items[0]) + + return buf.Bytes(), nil +} diff --git a/client-programs/pkg/workshops/manager.go b/client-programs/pkg/workshops/manager.go new file mode 100644 index 000000000..bafacb7eb --- /dev/null +++ b/client-programs/pkg/workshops/manager.go @@ -0,0 +1,372 @@ +package workshops + +import ( + "fmt" + "os" + "path/filepath" + "regexp" + "strings" + + "carvel.dev/imgpkg/pkg/imgpkg/cmd" + imgpkgcmd "carvel.dev/imgpkg/pkg/imgpkg/cmd" + vendirsync "carvel.dev/vendir/pkg/vendir/cmd" + yttcmd "carvel.dev/ytt/pkg/cmd/template" + + "github.com/cppforlife/go-cli-ui/ui" + "github.com/educates/educates-training-platform/client-programs/pkg/templates" + "github.com/pkg/errors" + "go.yaml.in/yaml/v2" + "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/serializer" +) + +type WorkshopManager struct { + +} + +type WorkshopNewConfig struct { + Template string + Name string + Title string + Description string + Image string +} + +type WorkshopExportConfig struct { + Repository string + WorkshopFile string + WorkshopVersion string + DataValuesFlags yttcmd.DataValuesFlags +} + +type WorkshopPublishConfig struct { + Image string + Repository string + WorkshopFile string + ExportWorkshop string + WorkshopVersion string + RegistryFlags imgpkgcmd.RegistryFlags + DataValuesFlags yttcmd.DataValuesFlags +} + +func NewWorkshopManager() *WorkshopManager { + return &WorkshopManager{} +} + +func (m *WorkshopManager) NewWorkshop(directory string,o *WorkshopNewConfig) error { + name := o.Name + + if name == "" { + name = filepath.Base(directory) + } + + if match, _ := regexp.MatchString("^[a-z0-9-]+$", name); !match { + return errors.Errorf("invalid workshop name %q", name) + } + + parameters := map[string]string{ + "WorkshopName": name, + "WorkshopTitle": o.Title, + "WorkshopDescription": o.Description, + "WorkshopImage": o.Image, + } + + template := templates.InternalTemplate(o.Template) + + return template.Apply(directory, parameters) +} + +func (m *WorkshopManager) Export(directory string,o *WorkshopExportConfig) error { + // If image name hasn't been supplied read workshop definition file and + // try to work out image name to Export workshop as. + + rootDirectory := directory + workshopFilePath := o.WorkshopFile + + if !filepath.IsAbs(workshopFilePath) { + workshopFilePath = filepath.Join(rootDirectory, workshopFilePath) + } + + workshopFileData, err := os.ReadFile(workshopFilePath) + + if err != nil { + return errors.Wrapf(err, "cannot open workshop definition %q", workshopFilePath) + } + + // Process the workshop YAML data for ytt templating and data variables. + + if workshopFileData, err = ProcessWorkshopDefinition(workshopFileData, o.DataValuesFlags); err != nil { + return errors.Wrap(err, "unable to process workshop definition as template") + } + + workshopFileData = []byte(strings.ReplaceAll(string(workshopFileData), "$(image_repository)", o.Repository)) + workshopFileData = []byte(strings.ReplaceAll(string(workshopFileData), "$(workshop_version)", o.WorkshopVersion)) + + decoder := serializer.NewCodecFactory(scheme.Scheme).UniversalDecoder() + + workshop := &unstructured.Unstructured{} + + err = runtime.DecodeInto(decoder, workshopFileData, workshop) + + if err != nil { + return errors.Wrap(err, "couldn't parse workshop definition") + } + + if workshop.GetAPIVersion() != "training.educates.dev/v1beta1" || workshop.GetKind() != "Workshop" { + return errors.New("invalid type for workshop definition") + } + + // Insert workshop version property if not specified. + + _, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") + + if !found && o.WorkshopVersion != "latest" { + unstructured.SetNestedField(workshop.Object, o.WorkshopVersion, "spec", "version") + } + + // Remove the publish section as will not be accurate after publising. + + unstructured.RemoveNestedField(workshop.Object, "spec", "publish") + + // Export modified workshop definition file. + + workshopFileData, err = yaml.Marshal(&workshop.Object) + + if err != nil { + return errors.Wrap(err, "couldn't convert workshop definition back to YAML") + } + + fmt.Print(string(workshopFileData)) + + return nil +} + +func (m *WorkshopManager) Publish(directory string,o *WorkshopPublishConfig) error { + // If image name hasn't been supplied read workshop definition file and + // try to work out image name to publish workshop as. + + rootDirectory := directory + workshopFilePath := o.WorkshopFile + + workingDirectory, err := os.Getwd() + + if err != nil { + return errors.Wrap(err, "cannot determine current working directory") + } + + includePaths := []string{directory} + excludePaths := []string{".git"} + + if !filepath.IsAbs(workshopFilePath) { + workshopFilePath = filepath.Join(rootDirectory, workshopFilePath) + } + + workshopFileData, err := os.ReadFile(workshopFilePath) + + if err != nil { + return errors.Wrapf(err, "cannot open workshop definition %q", workshopFilePath) + } + + // Process the workshop YAML data for ytt templating and data variables. + + if workshopFileData, err = ProcessWorkshopDefinition(workshopFileData, o.DataValuesFlags); err != nil { + return errors.Wrap(err, "unable to process workshop definition as template") + } + + workshopFileData = []byte(strings.ReplaceAll(string(workshopFileData), "$(image_repository)", o.Repository)) + workshopFileData = []byte(strings.ReplaceAll(string(workshopFileData), "$(workshop_version)", o.WorkshopVersion)) + + decoder := serializer.NewCodecFactory(scheme.Scheme).UniversalDecoder() + + workshop := &unstructured.Unstructured{} + + err = runtime.DecodeInto(decoder, workshopFileData, workshop) + + if err != nil { + return errors.Wrap(err, "couldn't parse workshop definition") + } + + fmt.Printf("Processing workshop with name %q.\n", workshop.GetName()) + + if workshop.GetAPIVersion() != "training.educates.dev/v1beta1" || workshop.GetKind() != "Workshop" { + return errors.New("invalid type for workshop definition") + } + + image := o.Image + + if image == "" { + image, _, _ = unstructured.NestedString(workshop.Object, "spec", "publish", "image") + } + + if image == "" { + return errors.Errorf("cannot find image name for publishing workshop %q", workshopFilePath) + } + + // Extract vendir snippet describing subset of files to package up as the + // workshop image. + + confUI := ui.NewConfUI(ui.NewNoopLogger()) + + uiFlags := cmd.UIFlags{ + Color: true, + JSON: false, + NonInteractive: true, + } + + uiFlags.ConfigureUI(confUI) + + defer confUI.Flush() + + if fileArtifacts, found, _ := unstructured.NestedSlice(workshop.Object, "spec", "publish", "files"); found && len(fileArtifacts) != 0 { + tempDir, err := os.MkdirTemp("", "educates-imgpkg") + + if err != nil { + return errors.Wrapf(err, "unable to create temporary working directory") + } + + defer os.RemoveAll(tempDir) + + for _, artifactEntry := range fileArtifacts { + vendirConfig := map[string]interface{}{ + "apiVersion": "vendir.k14s.io/v1alpha1", + "kind": "Config", + "directories": []interface{}{}, + } + + dir := filepath.Join(tempDir, "files") + + if filePath, found := artifactEntry.(map[string]interface{})["path"].(string); found { + dir = filepath.Join(tempDir, "files", filepath.Clean(filePath)) + } + + if directoryConfig, found := artifactEntry.(map[string]interface{})["directory"]; found { + if directoryPath, found := directoryConfig.(map[string]interface{})["path"].(string); found { + if !filepath.IsAbs(directoryPath) { + directoryConfig.(map[string]interface{})["path"] = filepath.Join(directory, directoryPath) + } + } + } + + artifactEntry.(map[string]interface{})["path"] = "." + + directoryConfig := map[string]interface{}{ + "path": dir, + "contents": []interface{}{artifactEntry}, + } + + vendirConfig["directories"] = append(vendirConfig["directories"].([]interface{}), directoryConfig) + + yamlData, err := yaml.Marshal(&vendirConfig) + + if err != nil { + return errors.Wrap(err, "unable to generate vendir config") + } + + vendirConfigFile, err := os.Create(filepath.Join(tempDir, "vendir.yml")) + + if err != nil { + return errors.Wrap(err, "unable to create vendir config file") + } + + defer vendirConfigFile.Close() + + _, err = vendirConfigFile.Write(yamlData) + + if err != nil { + return errors.Wrap(err, "unable to write vendir config file") + } + + syncOptions := vendirsync.NewSyncOptions(confUI) + + syncOptions.Directories = nil + syncOptions.Files = []string{filepath.Join(tempDir, "vendir.yml")} + + // Note that Chdir here actually changes the process working directory. + + syncOptions.LockFile = filepath.Join(tempDir, "lock-file") + syncOptions.Locked = false + syncOptions.Chdir = tempDir + syncOptions.AllowAllSymlinkDestinations = false + + if err = syncOptions.Run(); err != nil { + fmt.Println(string(yamlData)) + + return errors.Wrap(err, "failed to prepare image files for publishing") + } + } + + // Restore working directory as was changed. + + os.Chdir((workingDirectory)) + + rootDirectory = filepath.Join(tempDir, "files") + includePaths = []string{rootDirectory} + } + + // Now publish workshop directory contents as OCI image artifact. + + fmt.Printf("Publishing workshop files to %q.\n", image) + + pushOptions := imgpkgcmd.NewPushOptions(confUI) + + pushOptions.ImageFlags.Image = image + pushOptions.FileFlags.Files = append(pushOptions.FileFlags.Files, includePaths...) + pushOptions.FileFlags.ExcludedFilePaths = append(pushOptions.FileFlags.ExcludedFilePaths, excludePaths...) + + pushOptions.RegistryFlags = o.RegistryFlags + + err = pushOptions.Run() + + if err != nil { + return errors.Wrap(err, "unable to push image artifact for workshop") + } + + // We add a newline to output for better readability. + fmt.Println() + + // Export modified workshop definition file. + + exportWorkshop := o.ExportWorkshop + + if exportWorkshop != "" { + // Insert workshop version property if not specified. + + _, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") + + if !found && o.WorkshopVersion != "latest" { + unstructured.SetNestedField(workshop.Object, o.WorkshopVersion, "spec", "version") + } + + // Remove the publish section as will not be accurate after publising. + + unstructured.RemoveNestedField(workshop.Object, "spec", "publish") + + workshopFileData, err = yaml.Marshal(&workshop.Object) + + if err != nil { + return errors.Wrap(err, "couldn't convert workshop definition back to YAML") + } + + if !filepath.IsAbs(exportWorkshop) { + exportWorkshop = filepath.Join(workingDirectory, exportWorkshop) + } + + exportWorkshopFile, err := os.Create(exportWorkshop) + + if err != nil { + return errors.Wrap(err, "unable to create exported workshop definition file") + } + + defer exportWorkshopFile.Close() + + _, err = exportWorkshopFile.Write(workshopFileData) + + if err != nil { + return errors.Wrap(err, "unable to write exported workshop definition file") + } + } + + return nil +} diff --git a/go.work.sum b/go.work.sum index 4913345cd..aded10bdd 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,22 +1,11 @@ bitbucket.org/bertimus9/systemstat v0.5.0/go.mod h1:EkUWPp8lKFPMXP8vnbpT5JDI0W/sTiLZAvN8ONWErHY= carvel.dev/imgpkg v0.44.0 h1:5f7LZKn8MALx2xldwNXm5TD6vn9NDQuHJs8Nag2Fr0Y= -carvel.dev/imgpkg v0.46.1 h1:UOYaPllQJRsbzSl61IiNvmDZA5z4951i/KaSROAC1W0= -carvel.dev/imgpkg v0.46.1/go.mod h1:Q1E+7tpoiPbVNjb7HSmLZP7E1j0w6mWFzDarOXW1HiI= carvel.dev/kapp v0.59.2/go.mod h1:HAeURGw65eT00APPvnOQ8uDx5yvdrro2vtH5VYF1Zz0= -carvel.dev/kapp v0.64.2 h1:dJhtWVOkvPPgcS0f5A4OtOlrGie9gHvabtZBvB/h0+M= -carvel.dev/kapp v0.64.2/go.mod h1:5t0pWQzyoY9SzPVqrqgYhTlzgsuyMy+bvFdmrvtbDJw= -carvel.dev/kbld v0.46.0 h1:khSHTH3yiEE8imE9K245ZT67ZToixa1nC1938Oje1O4= -carvel.dev/kbld v0.46.0/go.mod h1:wmUYbnw0di759Id26P6dtRW59cBHy4UT9/FJgthiJ0I= -carvel.dev/vendir v0.44.0 h1:vfq5KgGbbLlxHrE0prY7gZgiEQpjwo4lS2akCaVkcxA= -carvel.dev/vendir v0.44.0/go.mod h1:gslrJ0HPiy8gtJYsQZHzIVuGfOG0nfDKDupEm7uBWVQ= carvel.dev/ytt v0.47.0/go.mod h1:Xarf0th61vX6VY07l3KBSi3uaMCQ2UyPPiCPiaVpHME= -carvel.dev/ytt v0.52.1 h1:I9rCwIunzClas2MH5nVGtCK5ujZdiGaqAfGol/wiRKQ= -carvel.dev/ytt v0.52.1/go.mod h1:lzkMguCvSVvxT2My9RG3gRMgTws97NpNXufKZ6iiP5E= cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8= cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo= cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= -cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= -cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= +cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= cloud.google.com/go v0.53.0/go.mod h1:fp/UouUEsRkN6ryDKNW/Upv/JBKnv6WDthjR6+vze6M= @@ -81,8 +70,6 @@ cloud.google.com/go/compute v1.27.4/go.mod h1:7JZS+h21ERAGHOy5qb7+EPyXlQwzshzrx1 cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= cloud.google.com/go/compute/metadata v0.6.0 h1:A6hENjEsCDtC1k8byVsgwvVcioamEHvZ4j01OwKxG9I= cloud.google.com/go/compute/metadata v0.6.0/go.mod h1:FjyFAW1MW0C203CEOMDTu3Dk1FlqW3Rga40jzHL4hfg= -cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= -cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= cloud.google.com/go/contactcenterinsights v1.11.1/go.mod h1:FeNP3Kg8iteKM80lMwSk3zZZKVxr+PGnAId6soKuXwE= cloud.google.com/go/container v1.26.1/go.mod h1:5smONjPRUxeEpDG7bMKWfDL4sauswqEtnBK1/KKpR04= cloud.google.com/go/containeranalysis v0.11.1/go.mod h1:rYlUOM7nem1OJMKwE1SadufX0JP3wnXj844EtZAwWLY= @@ -182,9 +169,15 @@ cloud.google.com/go/vpcaccess v1.7.2/go.mod h1:mmg/MnRHv+3e8FJUjeSibVFvQF1cCy2Ms cloud.google.com/go/webrisk v1.9.2/go.mod h1:pY9kfDgAqxUpDBOrG4w8deLfhvJmejKB0qd/5uQIPBc= cloud.google.com/go/websecurityscanner v1.6.2/go.mod h1:7YgjuU5tun7Eg2kpKgGnDuEOXWIrh8x8lWrJT4zfmas= cloud.google.com/go/workflows v1.12.1/go.mod h1:5A95OhD/edtOhQd/O741NSfIMezNTbCwLM1P1tBRGHM= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +github.com/AlecAivazis/survey/v2 v2.3.7/go.mod h1:xUTIdE4KCOIjsBAE1JYsUPoCqYdZ1reCfTwbto0Fduo= github.com/Azure/azure-sdk-for-go v46.4.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= github.com/Azure/azure-sdk-for-go v55.0.0+incompatible h1:L4/vUGbg1Xkw5L20LZD+hJI5I+ibWSytqQ68lTCfLwY= github.com/Azure/azure-sdk-for-go v55.0.0+incompatible/go.mod h1:9XXNKU+eRnpl9moKnB4QOLf1HestfXbmab5FXxiDBjc= +github.com/Azure/azure-sdk-for-go/sdk/azcore v1.18.2/go.mod h1:QyVsSSN64v5TGltphKLQ2sQxe4OBQg0J1eKRcVBnfgE= +github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.11.0/go.mod h1:okZ+ZURbArNdlJ+ptXoyHNuOETzOl1Oww19rm8I2WLA= +github.com/Azure/azure-sdk-for-go/sdk/internal v1.11.2/go.mod h1:XtLgD3ZD34DAaVIIAyG3objl5DynM3CQ/vMcbBNJZGI= +github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.5.0/go.mod h1:PXe2h+LKcWTX9afWdZoHyODqR4fBa5boUM/8uJfZ0Jo= github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= github.com/Azure/go-autorest/autorest v0.11.6/go.mod h1:V6p3pKZx1KKkJubbxnDWrzNhEIfOy/pTGasLqzHIPHs= github.com/Azure/go-autorest/autorest v0.11.18 h1:90Y4srNYrwOtAgVo3ndrQkTYn6kf1Eg/AjTFJ8Is2aM= @@ -199,23 +192,28 @@ github.com/Azure/go-autorest/autorest/azure/auth v0.5.11/go.mod h1:84w/uV8E37feW github.com/Azure/go-autorest/autorest/azure/cli v0.4.1 h1:jwcD1wURu0+hKceV04MubZmKLzwEYOCz6q4aOtVZ+Ng= github.com/Azure/go-autorest/autorest/azure/cli v0.4.1/go.mod h1:JfDgiIO1/RPu6z42AdQTyjOoCM2MFhLqSBDvMEkDgcg= github.com/Azure/go-autorest/logger v0.2.0/go.mod h1:T9E3cAhj2VqvPOtCYAvby9aBXkZmbF5NWuPV8+WeEW8= +github.com/AzureAD/microsoft-authentication-library-for-go v1.4.2/go.mod h1:wP83P5OoQ5p6ip3ScPr0BAq0BvuPAvacpEuSzyouqAI= github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/BurntSushi/toml v1.3.2/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= -github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= -github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/GoogleCloudPlatform/k8s-cloud-provider v1.18.1-0.20220218231025-f11817397a1b/go.mod h1:FNj4KYEAAHfYu68kRYolGoxkaJn+6mdEsaM12VTwuI0= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.25.0/go.mod h1:obipzmGjfSjam60XLwGfqUkJsfiheAl+TUjG+4yzyPM= github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.29.0/go.mod h1:Cz6ft6Dkn3Et6l2v2a9/RpN7epQ1GtDlO6lj8bEcOvw= +github.com/GoogleCloudPlatform/opentelemetry-operations-go/detectors/gcp v1.30.0/go.mod h1:P4WPRUkOhJC13W//jWpyfJNDAIpvRbAUIYLX/4jtlE0= github.com/JeffAshton/win_pdh v0.0.0-20161109143554-76bb4ee9f0ab/go.mod h1:3VYc5hodBMJ5+l/7J4xAyMeuM2PNuepvHlGs8yilUCA= +github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= +github.com/Microsoft/go-winio v0.4.21/go.mod h1:JPGBdM1cNvN/6ISo+n8V5iA4v8pBzdOpzfwIujj1a84= github.com/Microsoft/hcsshim v0.8.25/go.mod h1:4zegtUJth7lAvFyc6cH2gGQ5B3OFQim01nnU2M8jKDg= github.com/Microsoft/hnslib v0.0.8/go.mod h1:EYveQJlhKh2obmEIRB3uKN6dBd9pj1frPsrTGFppKuk= github.com/Microsoft/hnslib v0.1.1/go.mod h1:DRQR4IjLae6WHYVhW7uqe44hmFUiNhmaWA+jwMbz5tM= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= github.com/NYTimes/gziphandler v1.1.1/go.mod h1:n/CVRwUEOgIxrgPvAQhUUr9oeUtvrhMomdKFjzJNB0c= +github.com/ProtonMail/go-crypto v1.3.0/go.mod h1:9whxjD8Rbs29b4XWbB8irEcE8KHMqaR2e7GWU1R+/PE= github.com/PuerkitoBio/purell v1.0.0/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20160726150825-5bd2802263f2/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE= +github.com/StackExchange/wmi v0.0.0-20190523213315-cbe66965904d/go.mod h1:3eOhrUMpNV+6aFIbp5/iudMxNCF27Vw2OZgy4xEx0Fg= +github.com/agext/levenshtein v1.2.3/go.mod h1:JEDfjyjHDjOF/1e4FlBE/PkbqA9OfWu2ki2W0IB5558= github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= @@ -225,6 +223,8 @@ github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3 github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= 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/apparentlymart/go-cidr v1.0.1/go.mod h1:EBcsNrHc3zQeuaeCeCtQruQm+n9/YjEn/vI25Lg7Gwc= +github.com/apparentlymart/go-textseg/v15 v15.0.0/go.mod h1:K8XmNZdhEBkdlyDdvbmmsvpAG721bKi0joRfFdHIWJ4= github.com/armon/circbuf v0.0.0-20190214190532-5111143e8da2/go.mod h1:3U/XgcO3hCbHZ8TKRvWD2dDTCfh9M9ya+I9JpbB7O8o= 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= @@ -232,77 +232,51 @@ github.com/aws/aws-lambda-go v1.41.0/go.mod h1:jwFe2KmMsHmffA1X2R09hH6lFzJQxzI8q github.com/aws/aws-sdk-go-v2 v1.7.1/go.mod h1:L5LuPC1ZgDr2xQS7AmIec/Jlc7O/Y1u2KxJyNVab250= github.com/aws/aws-sdk-go-v2 v1.36.1 h1:iTDl5U6oAhkNPba0e1t1hrwAo02ZMqbrGq4k5JBWM5E= github.com/aws/aws-sdk-go-v2 v1.36.1/go.mod h1:5PMILGVKiW32oDzjj6RU52yrNrDPUHcbZQYr1sM7qmM= -github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk= -github.com/aws/aws-sdk-go-v2 v1.39.6/go.mod h1:c9pm7VwuW0UPxAEYGyTmyurVcNrbF6Rt/wixFqDhcjE= +github.com/aws/aws-sdk-go-v2/aws/protocol/eventstream v1.7.0/go.mod h1:/mXlTIVG9jbxkqDnr5UQNQxW1HRYxeGklkM9vAFeabg= github.com/aws/aws-sdk-go-v2/config v1.5.0/go.mod h1:RWlPOAW3E3tbtNAqTwvSW54Of/yP3oiZXMI0xfUdjyA= github.com/aws/aws-sdk-go-v2/config v1.29.6 h1:fqgqEKK5HaZVWLQoLiC9Q+xDlSp+1LYidp6ybGE2OGg= github.com/aws/aws-sdk-go-v2/config v1.29.6/go.mod h1:Ft+WLODzDQmCTHDvqAH1JfC2xxbZ0MxpZAcJqmE1LTQ= -github.com/aws/aws-sdk-go-v2/config v1.31.19 h1:qdUtOw4JhZr2YcKO3g0ho/IcFXfXrrb8xlX05Y6EvSw= -github.com/aws/aws-sdk-go-v2/config v1.31.19/go.mod h1:tMJ8bur01t8eEm0atLadkIIFA154OJ4JCKZeQ+o+R7k= github.com/aws/aws-sdk-go-v2/credentials v1.3.1/go.mod h1:r0n73xwsIVagq8RsxmZbGSRQFj9As3je72C2WzUIToc= github.com/aws/aws-sdk-go-v2/credentials v1.17.59 h1:9btwmrt//Q6JcSdgJOLI98sdr5p7tssS9yAsGe8aKP4= github.com/aws/aws-sdk-go-v2/credentials v1.17.59/go.mod h1:NM8fM6ovI3zak23UISdWidyZuI1ghNe2xjzUZAyT+08= -github.com/aws/aws-sdk-go-v2/credentials v1.18.23 h1:IQILcxVgMO2BVLaJ2aAv21dKWvE1MduNrbvuK43XL2Q= -github.com/aws/aws-sdk-go-v2/credentials v1.18.23/go.mod h1:JRodHszhVdh5TPUknxDzJzrMiznG+M+FfR3WSWKgCI8= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.3.0/go.mod h1:2LAuqPx1I6jNfaGDucWfA2zqQCYCOMCDHiCOciALyNw= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28 h1:KwsodFKVQTlI5EyhRSugALzsV6mG/SGrdjlMXSZSdso= github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.16.28/go.mod h1:EY3APf9MzygVhKuPXAc5H+MkGb8k/DOSQjWS0LgkKqI= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13 h1:T1brd5dR3/fzNFAQch/iBKeX07/ffu/cLu+q+RuzEWk= -github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.13/go.mod h1:Peg/GBAQ6JDt+RoBf4meB1wylmAipb7Kg2ZFakZTlwk= +github.com/aws/aws-sdk-go-v2/feature/s3/manager v1.17.10/go.mod h1:3HKuexPDcwLWPaqpW2UR/9n8N/u/3CKcGAzSs8p8u8g= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32 h1:BjUcr3X3K0wZPGFg2bxOWW3VPN8rkE3/61zhP+IHviA= github.com/aws/aws-sdk-go-v2/internal/configsources v1.3.32/go.mod h1:80+OGC/bgzzFFTUmcuwD0lb4YutwQeKLFpmt6hoWapU= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13 h1:a+8/MLcWlIxo1lF9xaGt3J/u3yOZx+CdSveSNwjhD40= -github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.13/go.mod h1:oGnKwIYZ4XttyU2JWxFrwvhF6YKiK/9/wmE3v3Iu9K8= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32 h1:m1GeXHVMJsRsUAqG6HjZWx9dj7F5TR+cF1bjyfYyBd4= github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.6.32/go.mod h1:IitoQxGfaKdVLNg0hD8/DXmAqNy0H4K2H2Sf91ti8sI= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13 h1:HBSI2kDkMdWz4ZM7FjwE7e/pWDEZ+nR95x8Ztet1ooY= -github.com/aws/aws-sdk-go-v2/internal/endpoints/v2 v2.7.13/go.mod h1:YE94ZoDArI7awZqJzBAZ3PDD2zSfuP7w6P2knOzIn8M= github.com/aws/aws-sdk-go-v2/internal/ini v1.1.1/go.mod h1:Zy8smImhTdOETZqfyn01iNOe0CNggVbPjCajyaz6Gvg= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2 h1:Pg9URiobXy85kgFev3og2CuOZ8JZUBENF+dcgWBaYNk= github.com/aws/aws-sdk-go-v2/internal/ini v1.8.2/go.mod h1:FbtygfRFze9usAadmnGJNc8KsP346kEe+y2/oyhGAGc= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4 h1:WKuaxf++XKWlHWu9ECbMlha8WOEGm0OUEZqm4K/Gcfk= -github.com/aws/aws-sdk-go-v2/internal/ini v1.8.4/go.mod h1:ZWy7j6v1vWGmPReu0iSGvRiise4YI5SkR3OHKTZ6Wuc= +github.com/aws/aws-sdk-go-v2/internal/v4a v1.4.4/go.mod h1:SPBBhkJxjcrzJBc+qY85e83MQ2q3qdra8fghhkkyrJg= github.com/aws/aws-sdk-go-v2/service/ecr v1.4.1/go.mod h1:FglZcyeiBqcbvyinl+n14aT/EWC7S1MIH+Gan2iizt0= github.com/aws/aws-sdk-go-v2/service/ecr v1.40.3 h1:a+210FCU/pR5hhKRaskRfX/ogcyyzFBrehcTk5DTAyU= github.com/aws/aws-sdk-go-v2/service/ecr v1.40.3/go.mod h1:dtD3a4sjUjVL86e0NUvaqdGvds5ED6itUiZPDaT+Gh8= -github.com/aws/aws-sdk-go-v2/service/ecr v1.51.3 h1:+0AhrMCsfRxzlojjbJBOOBO1Ka5t1VsF28g+eHYbyEI= -github.com/aws/aws-sdk-go-v2/service/ecr v1.51.3/go.mod h1:1NVD1KuMjH2GqnPwMotPndQaT/MreKkWpjkF12d6oKU= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.4.1/go.mod h1:eD5Eo4drVP2FLTw0G+SMIPWNWvQRGGTtIZR2XeAagoA= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.31.2 h1:E6/Myrj9HgLF22medmDrKmbpm4ULsa+cIBNx3phirBk= github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.31.2/go.mod h1:OQ8NALFcchBJ/qruak6zKUQodovnTKKaReTuCkc5/9Y= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.3 h1:2Mfho1EDuk815vcGZbiGsOY6mMGPMCsJTx2dWZdWudI= -github.com/aws/aws-sdk-go-v2/service/ecrpublic v1.38.3/go.mod h1:x7gU4CAyAz4BsM9hlRkhHiYw2GIr1QCmN45uwQw9l/E= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2 h1:D4oz8/CzT9bAEYtVhSBmFj2dNOtaHOtMKc2vHBwYizA= github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.12.2/go.mod h1:Za3IHqTQ+yNcRHxu1OFucBh0ACZT4j4VQFF0BqpZcLY= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3 h1:x2Ibm/Af8Fi+BH+Hsn9TXGdT+hKbDd5XOTZxTMxDk7o= -github.com/aws/aws-sdk-go-v2/service/internal/accept-encoding v1.13.3/go.mod h1:IW1jwyrQgMdhisceG8fQLmQIydcT/jWY21rFhzgaKwo= +github.com/aws/aws-sdk-go-v2/service/internal/checksum v1.8.4/go.mod h1:b17At0o8inygF+c6FOD3rNyYZufPw62o9XJbSfQPgbo= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.2.1/go.mod h1:zceowr5Z1Nh2WVP8bf/3ikB41IZW59E4yIYbg+pC6mw= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13 h1:SYVGSFQHlchIcy6e7x12bsrxClCXSP5et8cqVhL8cuw= github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.12.13/go.mod h1:kizuDaLX37bG5WZaoxGPQR/LNFXpxp0vsUnqfkWXfNE= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13 h1:kDqdFvMY4AtKoACfzIGD8A0+hbT41KTKF//gq7jITfM= -github.com/aws/aws-sdk-go-v2/service/internal/presigned-url v1.13.13/go.mod h1:lmKuogqSU3HzQCwZ9ZtcqOc5XGMqtDK7OIc2+DxiUEg= +github.com/aws/aws-sdk-go-v2/service/internal/s3shared v1.19.4/go.mod h1:DnbBOv4FlIXHj2/xmrUQYtawRFC9L9ZmQPz+DBc6X5I= +github.com/aws/aws-sdk-go-v2/service/s3 v1.87.1/go.mod h1:w5PC+6GHLkvMJKasYGVloB3TduOtROEMqm15HSuIbw4= github.com/aws/aws-sdk-go-v2/service/sso v1.3.1/go.mod h1:J3A3RGUvuCZjvSuZEcOpHDnzZP/sKbhDWV2T1EOzFIM= github.com/aws/aws-sdk-go-v2/service/sso v1.24.15 h1:/eE3DogBjYlvlbhd2ssWyeuovWunHLxfgw3s/OJa4GQ= github.com/aws/aws-sdk-go-v2/service/sso v1.24.15/go.mod h1:2PCJYpi7EKeA5SkStAmZlF6fi0uUABuhtF8ILHjGc3Y= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.2 h1:/p6MxkbQoCzaGQT3WO0JwG0FlQyG9RD8VmdmoKc5xqU= -github.com/aws/aws-sdk-go-v2/service/sso v1.30.2/go.mod h1:fKvyjJcz63iL/ftA6RaM8sRCtN4r4zl4tjL3qw5ec7k= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14 h1:M/zwXiL2iXUrHputuXgmO94TVNmcenPHxgLXLutodKE= github.com/aws/aws-sdk-go-v2/service/ssooidc v1.28.14/go.mod h1:RVwIw3y/IqxC2YEXSIkAzRDdEU1iRabDPaYjpGCbCGQ= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.6 h1:0dES42T2dhICCbVB3JSTTn7+Bz93wfJEK1b7jksZIyQ= -github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.6/go.mod h1:klO+ejMvYsB4QATfEOIXk8WAEwN4N0aBfJpvC+5SZBo= github.com/aws/aws-sdk-go-v2/service/sts v1.6.0/go.mod h1:q7o0j7d7HrJk/vr9uUt3BVRASvcU7gYZB9PUgPiByXg= github.com/aws/aws-sdk-go-v2/service/sts v1.33.14 h1:TzeR06UCMUq+KA3bDkujxK1GVGy+G8qQN/QVYzGLkQE= github.com/aws/aws-sdk-go-v2/service/sts v1.33.14/go.mod h1:dspXf/oYWGWo6DEvj98wpaTeqt5+DMidZD0A9BYTizc= -github.com/aws/aws-sdk-go-v2/service/sts v1.40.1 h1:5sbIM57lHLaEaNWdIx23JH30LNBsSDkjN/QXGcRLAFc= -github.com/aws/aws-sdk-go-v2/service/sts v1.40.1/go.mod h1:E19xDjpzPZC7LS2knI9E6BaRFDK43Eul7vd6rSq2HWk= github.com/aws/smithy-go v1.6.0/go.mod h1:SObp3lf9smib00L/v3U2eAKG8FyQ7iLrJnQiAmR5n+E= github.com/aws/smithy-go v1.22.2 h1:6D9hW43xKFrRx/tXXfAlIZc4JI+yQe6snnWcQyxSyLQ= github.com/aws/smithy-go v1.22.2/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= -github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= -github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220517224237-e6f29200ae04/go.mod h1:Z+bXnIbhKJYSvxNwsNnwde7pDKxuqlEZCbUBoTwAqf0= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.11.0 h1:GOPttfOAf5qAgx7r6b+zCWZrvCsfKffkL4H6mSYx1kA= -github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.11.0/go.mod h1:a2HN6+p7k0JLDO8514sMr0l4cnrR52z4sWoZ/Uc82ho= github.com/bazelbuild/bazelisk v1.13.2/go.mod h1:jVD8/E7hMAXgWKCljEz8hOV0PZ+nFBgCpjIOJ6Xyzus= github.com/bazelbuild/rules_go v0.34.0/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= @@ -312,13 +286,15 @@ github.com/bmatcuk/doublestar v1.2.1 h1:eetYiv8DDYOZcBADY+pRvRytf3Dlz1FhnpvL2FsC github.com/bmatcuk/doublestar v1.2.1/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= -github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= -github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= github.com/census-instrumentation/opencensus-proto v0.4.1/go.mod h1:4T9NM4+4Vw91VeyqjLS6ao50K5bOcLKN6Q42XnYaRYw= github.com/cespare/xxhash/v2 v2.1.2/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/chai2010/gettext-go v1.0.2/go.mod h1:y+wnP2cHYaVj19NZhYKAwEMH2CI1gNHeQQ+5AjwawxA= +github.com/checkpoint-restore/checkpointctl v1.4.0/go.mod h1:ynQ52zQBazgcTZuxpwTFzRinIcAf0haDTC1X1LA/FKA= github.com/checkpoint-restore/go-criu/v5 v5.3.0/go.mod h1:E/eQpaFtUKGOOSEBZgmKAcn+zUUwWxqcaKZlF54wK8E= +github.com/checkpoint-restore/go-criu/v7 v7.2.0/go.mod h1:u0LCWLg0w4yqqu14aXhiB4YD3a1qd8EcCEg7vda5dwo= github.com/cheggaaa/pb/v3 v3.1.4/go.mod h1:6wVjILNBaXMs8c21qRiaUM8BR82erfgau1DQ4iUXmSA= github.com/chrismellard/docker-credential-acr-env v0.0.0-20220327082430-c57b701bfc08 h1:9Qh4lJ/KMr5iS1zfZ8I97+3MDpiKjl+0lZVUNBhdvRs= github.com/chrismellard/docker-credential-acr-env v0.0.0-20220327082430-c57b701bfc08/go.mod h1:MAuu1uDJNOS3T3ui0qmKdPUwm59+bO19BbTph2wZafE= @@ -326,10 +302,8 @@ github.com/chzyer/logex v1.2.1/go.mod h1:JLbx6lG2kDbNRFnfkgvh4eRJRPX1QCoOIWomwys github.com/chzyer/readline v1.5.1/go.mod h1:Eh+b79XXUwfKfcPLepksvw2tcLE/Ct21YObkaSkeBlk= github.com/chzyer/test v1.0.0/go.mod h1:2JlltgoNkt4TW/z9V/IzDdFaMTM2JPIi26O1pF38GC8= github.com/cilium/ebpf v0.9.1/go.mod h1:+OhNOIXx/Fnu1IE8bJz2dzOA+VSfyTfdNUVdlQnxUFY= -github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfatpWHKCs= -github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= -github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= -github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/cilium/ebpf v0.16.0/go.mod h1:L7u2Blt2jMM/vLAVgjxluxtBKlz3/GWjB0dMOEngfwE= +github.com/cloudflare/circl v1.6.0/go.mod h1:uddAzsPgqdMAYatqJ0lsjX1oECcQLIlRpzZh3pJrofs= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= github.com/cncf/udpa/go v0.0.0-20200629203442-efcf912fb354/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= github.com/cncf/udpa/go v0.0.0-20201120205902-5459f2c99403/go.mod h1:WmhPx2Nbnhtbo57+VJT5O0JRkEi1Wbu0z5j0R8u5Hbk= @@ -339,26 +313,35 @@ github.com/cncf/xds/go v0.0.0-20240318125728-8a4994d93e50/go.mod h1:5e1+Vvlzido6 github.com/cncf/xds/go v0.0.0-20240905190251-b4127c9b8d78/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20241223141626-cff3c89139a3/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1IgJhy4+A9GOqVhqLpfZi+vwmdNXUehLA8= +github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= github.com/compose-spec/compose-go/v2 v2.9.1 h1:8UwI+ujNU+9Ffkf/YgAm/qM9/eU7Jn8nHzWG721W4rs= github.com/compose-spec/compose-go/v2 v2.9.1/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE= github.com/container-storage-interface/spec v1.8.0/go.mod h1:ROLik+GhPslwwWRNFF1KasPzroNARibH2rfz1rkg4H0= github.com/container-storage-interface/spec v1.9.0/go.mod h1:ZfDu+3ZRyeVqxZM0Ds19MVLkN2d1XJ5MAfi1L3VjlT0= +github.com/containerd/accelerated-container-image v1.3.0/go.mod h1:EvKVWor6ZQNUyYp0MZm5hw4k21ropuz7EegM+m/Jb/Q= +github.com/containerd/btrfs/v2 v2.0.0/go.mod h1:swkD/7j9HApWpzl8OHfrHNxppPd9l44DFZdF94BUj9k= github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHqJDGwsa1mIw= github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig= github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc= github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= -github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= -github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= -github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= -github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/fuse-overlayfs-snapshotter/v2 v2.1.6/go.mod h1:Mau9LZ7ZnyKCIgcNT7sMG5fjaZ9YCOHU5RuolUikhBQ= +github.com/containerd/go-cni v1.1.13/go.mod h1:nTieub0XDRmvCZ9VI/SBG6PyqT95N4FIhxsauF1vSBI= +github.com/containerd/go-runc v1.1.0/go.mod h1:xJv2hFF7GvHtTJd9JqTS2UVxMkULUYw4JN5XAUZqH5U= +github.com/containerd/imgcrypt/v2 v2.0.1/go.mod h1:/qIJL8nxzdzMA2n5iYyyuIY36KfoVQWmgTWdfVtyebM= +github.com/containerd/nri v0.10.0/go.mod h1:5VyvLa/4uL8FjyO8nis1UjbCutXDpngil17KvBSL6BU= +github.com/containerd/otelttrpc v0.1.0/go.mod h1:XhoA2VvaGPW1clB2ULwrBZfXVuEWuyOd2NUD1IM0yTg= +github.com/containerd/stargz-snapshotter v0.17.0 h1:djNS4KU8ztFhLdEDZ1bsfzOiYuVHT6TgSU5qwRk+cNc= +github.com/containerd/stargz-snapshotter v0.17.0/go.mod h1:ySEul1ck7jCE4jqsuFCo8FFLrHU20UWQeI9g7mdsanI= github.com/containerd/stargz-snapshotter/estargz v0.14.3/go.mod h1:KY//uOCIkSuNAHhJogcZtrNHdKrA99/FCCRjE3HD36o= -github.com/containerd/stargz-snapshotter/estargz v0.18.1 h1:cy2/lpgBXDA3cDKSyEfNOFMA/c10O1axL69EU7iirO8= -github.com/containerd/stargz-snapshotter/estargz v0.18.1/go.mod h1:ALIEqa7B6oVDsrF37GkGN20SuvG/pIMm7FwP7ZmRb0Q= github.com/containerd/ttrpc v1.2.2/go.mod h1:sIT6l32Ph/H9cvnJsfXM5drIVzTr5A2flTf1G5tYZak= github.com/containerd/ttrpc v1.2.5/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= github.com/containerd/ttrpc v1.2.6/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= github.com/containerd/typeurl/v2 v2.2.2/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= +github.com/containerd/zfs/v2 v2.0.0-rc.0/go.mod h1:g36g/XCEGDRxUXIFdM3oWAEvmTvhfz/eKWElqg4Secw= +github.com/containernetworking/cni v1.3.0/go.mod h1:Bs8glZjjFfGPHMw6hQu82RUgEPNGEaBb9KS5KtNMnJ4= +github.com/containernetworking/plugins v1.9.0/go.mod h1:JG3BxoJifxxHBhG3hFyxyhid7JgRVBu/wtooGEvWf1c= +github.com/containers/ocicrypt v1.2.1/go.mod h1:aD0AAqfMp0MtwqWgHM1bUwe1anx0VazI108CRrSKINQ= github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+BawD4= github.com/coredns/corefile-migration v1.0.21/go.mod h1:XnhgULOEouimnzgn0t4WPuFDN2/PJQcTxdWKC5eXNGE= github.com/coredns/corefile-migration v1.0.24/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= @@ -369,14 +352,10 @@ github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSV github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/cppforlife/cobrautil v0.0.0-20221021151949-d60711905d65/go.mod h1:2w+qxVu2KSGW78Ex/XaIqfh/OvBgjEsmN53S4T8vEyA= github.com/cppforlife/go-cli-ui v0.0.0-20220425131040-94f26b16bc14/go.mod h1:AlgTssDlstr4mf92TR4DPITLfl5+7wEY4cKStCmeeto= -github.com/cppforlife/go-cli-ui v0.0.0-20250603184554-47874c9078ad h1:PaYhzcFC4VCmlBNWLshK0VxWJyb5J+AdnrwR6hnfe+A= -github.com/cppforlife/go-cli-ui v0.0.0-20250603184554-47874c9078ad/go.mod h1:xZhzUOhCF76o47bEulESCNzmvP4xbwRFUSpN62Zu9FI= 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/cpuguy83/go-md2man/v2 v2.0.5/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= -github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= -github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM= github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= @@ -388,25 +367,22 @@ github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2 github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= github.com/dimchansky/utfbom v1.1.0/go.mod h1:rO41eb7gLfo8SF1jd9F8HplJm1Fewwi4mQvIirEdv+8= +github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM= github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli v29.0.0+incompatible h1:KgsN2RUFMNM8wChxryicn4p46BdQWpXOA1XLGBGPGAw= -github.com/docker/cli v29.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQtHZEcfE= +github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v24.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U= github.com/docker/docker v27.5.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v28.0.2+incompatible h1:9BILleFwug5FSSqWBgVevgL3ewDJfWWWyZVqlDMttE8= github.com/docker/docker v28.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= -github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= -github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= -github.com/docker/docker-credential-helpers v0.9.4 h1:76ItO69/AP/V4yT9V4uuuItG0B1N8hvt0T0c0NN/DzI= -github.com/docker/docker-credential-helpers v0.9.4/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= -github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= -github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= +github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= @@ -418,8 +394,6 @@ github.com/emicklei/go-restful v2.16.0+incompatible/go.mod h1:otzb+WCGbkyDHkqmQm github.com/emicklei/go-restful/v3 v3.8.0/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/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= -github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= @@ -430,7 +404,9 @@ github.com/envoyproxy/go-control-plane v0.11.1/go.mod h1:uhMcXKCQMEJHiAb0w+YGefQ github.com/envoyproxy/go-control-plane v0.12.0/go.mod h1:ZBTaoJ23lqITozF0M6G4/IragXCQKCnYbmlmtHvwRG0= github.com/envoyproxy/go-control-plane v0.13.0/go.mod h1:GRaKG3dwvFoTg4nj7aXdZnvMg4d7nvT/wl9WgVXn3Q8= github.com/envoyproxy/go-control-plane v0.13.4/go.mod h1:kDfuBlDVsSj2MjrLEtRWtHlsWIFcGyB2RMO44Dc5GZA= +github.com/envoyproxy/go-control-plane v0.13.5-0.20251024222203-75eaa193e329/go.mod h1:Alz8LEClvR7xKsrq3qzoc4N0guvVNSS8KmSChGYr9hs= github.com/envoyproxy/go-control-plane/envoy v1.32.4/go.mod h1:Gzjc5k8JcJswLjAx1Zm+wSYE20UrLtt7JZMWiWQXQEw= +github.com/envoyproxy/go-control-plane/envoy v1.35.0/go.mod h1:09qwbGVuSWWAyN5t/b3iyVfz5+z8QWGrzkoqm/8SbEs= github.com/envoyproxy/go-control-plane/ratelimit v0.1.0/go.mod h1:Wk+tMFAFbCXaJPzVVHnPgRKdUdwW/KdbRt94AzgRee4= github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= github.com/envoyproxy/protoc-gen-validate v1.0.2/go.mod h1:GpiZQP3dDbg4JouG/NNS7QWXpgx6x8QiMKdmN72jogE= @@ -446,16 +422,12 @@ github.com/exponent-io/jsonpath v0.0.0-20151013193312-d6023ce2651d/go.mod h1:ZZM github.com/exponent-io/jsonpath v0.0.0-20210407135951-1de76d718b3f/go.mod h1:OSYXu++VVOHnXeitef/D8n/6y4QV8uLHSFXX4NeXMGc= github.com/fatih/camelcase v1.0.0/go.mod h1:yN2Sb0lFhZJUdVvtELVWefmrXpuZESvPmqwoZc+/fpc= github.com/fatih/color v1.15.0/go.mod h1:0h5ZqXfHYED7Bhv2ZJamyIOUej9KtShiJESRwBDUSsw= +github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= github.com/felixge/httpsnoop v1.0.1/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/form3tech-oss/jwt-go v3.2.2+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= github.com/form3tech-oss/jwt-go v3.2.3+incompatible h1:7ZaBxOI7TMoYBfyA3cQHErNNyAWIKUMIwqxEtgHOs5c= github.com/form3tech-oss/jwt-go v3.2.3+incompatible/go.mod h1:pbq4aXjuKjdthFRnoDwaVPLA+WlJuPGy+QneDUgJi2k= -github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= -github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= -github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/fxamacker/cbor/v2 v2.6.0/go.mod h1:pxXPTn3joSm21Gbwsv0w9OSA2y1HFR9qXEeXQVeNoDQ= -github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= -github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= github.com/getkin/kin-openapi v0.76.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/getkin/kin-openapi v0.81.0/go.mod h1:660oXbgy5JFMKreazJaQTw7o+X00qeSyhcnluiMv+Xg= github.com/ghodss/yaml v0.0.0-20150909031657-73d445a93680/go.mod h1:4dBDuWmgqj2HViK6kFavaiC9ZROes6MMH2rRYeMEF04= @@ -463,7 +435,9 @@ github.com/ghodss/yaml v1.0.0 h1:wQHKEahhL6wmXdzwWG11gIVCkOv05bNOh+Rxn0yngAk= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20191125211704-12ad95a8df72/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= github.com/go-gl/glfw/v3.3/glfw v0.0.0-20200222043503-6f7a984d4dc4/go.mod h1:tQ2UAYgL5IevRw8kRxooKSPJfGvJ9fJQFa0TUsXzTg8= +github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= github.com/go-jose/go-jose/v4 v4.1.2/go.mod h1:22cg9HWM1pOlnRiY+9cQYJ9XHmya1bYW8OeDM6Ku6Oo= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= github.com/go-kit/log v0.2.1/go.mod h1:NwTd00d/i8cPZ3xOwwiv2PO5MOcx78fFErGNcVmBjv0= github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs= github.com/go-logr/logr v0.1.0/go.mod h1:ixOQHD9gLJUVQQ2ZOR7zLEifBX6tGkNJF4QyIY7sIas= @@ -473,19 +447,16 @@ github.com/go-logr/logr v1.2.3/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbV github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.1 h1:pKouT5E8xu9zeFC39JXRDukb6JFQPXM5p5I91188VAQ= github.com/go-logr/logr v1.4.1/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= github.com/go-logr/zapr v1.2.3 h1:a9vnzlIBPQBBkeaR9IuMUfmVOrQlkoC4YfPoFkX3T7A= github.com/go-logr/zapr v1.2.3/go.mod h1:eIauM6P8qSvTw5o2ez6UEAfGjQKrxQTl5EoK+Qa2oG4= github.com/go-logr/zapr v1.2.4/go.mod h1:FyHWQIzQORZ0QVE1BtVHv3cKtNLuXsbNLtpuhNapBOA= github.com/go-logr/zapr v1.3.0/go.mod h1:YKepepNBd1u/oyhd/yQmtjVXmm9uML4IXUgMOwR8/Gg= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-openapi/jsonpointer v0.0.0-20160704185906-46af16f9f7b1/go.mod h1:+35s3my2LFTysnkMfxsJBAMHj/DoqoB9knIWoYG/Vk0= github.com/go-openapi/jsonpointer v0.19.2/go.mod h1:3akKfEdA7DF1sugOqz1dVQHBcuDBPKZGEoHC/NkiQRg= github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.5/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg= github.com/go-openapi/jsonpointer v0.19.6/go.mod h1:osyAmYz/mB/C3I+WsTTSgw1ONzaLJoLCyoi6/zppojs= -github.com/go-openapi/jsonpointer v0.22.1 h1:sHYI1He3b9NqJ4wXLoJDKmUmHkWy/L7rtEo92JUxBNk= -github.com/go-openapi/jsonpointer v0.22.1/go.mod h1:pQT9OsLkfz1yWoMgYFy4x3U5GY5nUlsOn1qSBH5MkCM= github.com/go-openapi/jsonreference v0.0.0-20160704190145-13c6e3589ad9/go.mod h1:W3Z9FmVs9qj+KR4zFKmDPGiLdk1D9Rlm7cyMvf57TTg= github.com/go-openapi/jsonreference v0.19.2/go.mod h1:jMjeRr2HHw6nAVajTXJ4eiUwohSTlpa0o73RUL1owJc= github.com/go-openapi/jsonreference v0.19.3/go.mod h1:rjx6GuL8TTa9VaixXglHmQmIL98+wF9xc8zWvFonSJ8= @@ -493,8 +464,6 @@ github.com/go-openapi/jsonreference v0.19.5/go.mod h1:RdybgQwPxbL4UEjuAruzK1x3nE github.com/go-openapi/jsonreference v0.20.1/go.mod h1:Bl1zwGIM8/wsvqjsOQLJ/SH+En5Ap4rVB5KVcIDZG2k= 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/jsonreference v0.21.3 h1:96Dn+MRPa0nYAR8DR1E03SblB5FJvh7W6krPI0Z7qMc= -github.com/go-openapi/jsonreference v0.21.3/go.mod h1:RqkUP0MrLf37HqxZxrIAtTWW4ZJIK1VzduhXYBEeGc4= github.com/go-openapi/spec v0.0.0-20160808142527-6aced65f8501/go.mod h1:J8+jY1nAiCcj+friV/PDoE1/3eeccG9LYBs0tYvLOWc= github.com/go-openapi/spec v0.19.3/go.mod h1:FpwSN1ksY1eteniUU7X0N/BgJ7a4WvBFVA8Lj9mJglo= github.com/go-openapi/swag v0.0.0-20160704191624-1d0bd113de87/go.mod h1:DXUve3Dpr1UfpPtxFw+EFuQ41HhCWZfha5jSVRG7C7I= @@ -502,36 +471,8 @@ github.com/go-openapi/swag v0.19.2/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh github.com/go-openapi/swag v0.19.5/go.mod h1:POnQmlKehdgb5mhVOsnJFsivZCEZ/vjK9gh66Z9tfKk= github.com/go-openapi/swag v0.19.14/go.mod h1:QYRuS/SOXUCsnplDa677K7+DxSOj6IPNl/eQntq43wQ= github.com/go-openapi/swag v0.22.3/go.mod h1:UzaqsxGiab7freDnrUUra0MwWfN/q7tE4j+VcZ0yl14= -github.com/go-openapi/swag v0.25.1 h1:6uwVsx+/OuvFVPqfQmOOPsqTcm5/GkBhNwLqIR916n8= -github.com/go-openapi/swag v0.25.1/go.mod h1:bzONdGlT0fkStgGPd3bhZf1MnuPkf2YAys6h+jZipOo= -github.com/go-openapi/swag/cmdutils v0.25.1 h1:nDke3nAFDArAa631aitksFGj2omusks88GF1VwdYqPY= -github.com/go-openapi/swag/cmdutils v0.25.1/go.mod h1:pdae/AFo6WxLl5L0rq87eRzVPm/XRHM3MoYgRMvG4A0= -github.com/go-openapi/swag/conv v0.25.1 h1:+9o8YUg6QuqqBM5X6rYL/p1dpWeZRhoIt9x7CCP+he0= -github.com/go-openapi/swag/conv v0.25.1/go.mod h1:Z1mFEGPfyIKPu0806khI3zF+/EUXde+fdeksUl2NiDs= -github.com/go-openapi/swag/fileutils v0.25.1 h1:rSRXapjQequt7kqalKXdcpIegIShhTPXx7yw0kek2uU= -github.com/go-openapi/swag/fileutils v0.25.1/go.mod h1:+NXtt5xNZZqmpIpjqcujqojGFek9/w55b3ecmOdtg8M= -github.com/go-openapi/swag/jsonname v0.25.1 h1:Sgx+qbwa4ej6AomWC6pEfXrA6uP2RkaNjA9BR8a1RJU= -github.com/go-openapi/swag/jsonname v0.25.1/go.mod h1:71Tekow6UOLBD3wS7XhdT98g5J5GR13NOTQ9/6Q11Zo= -github.com/go-openapi/swag/jsonutils v0.25.1 h1:AihLHaD0brrkJoMqEZOBNzTLnk81Kg9cWr+SPtxtgl8= -github.com/go-openapi/swag/jsonutils v0.25.1/go.mod h1:JpEkAjxQXpiaHmRO04N1zE4qbUEg3b7Udll7AMGTNOo= -github.com/go-openapi/swag/jsonutils/fixtures_test v0.25.1/go.mod h1:kjmweouyPwRUEYMSrbAidoLMGeJ5p6zdHi9BgZiqmsg= -github.com/go-openapi/swag/loading v0.25.1 h1:6OruqzjWoJyanZOim58iG2vj934TysYVptyaoXS24kw= -github.com/go-openapi/swag/loading v0.25.1/go.mod h1:xoIe2EG32NOYYbqxvXgPzne989bWvSNoWoyQVWEZicc= -github.com/go-openapi/swag/mangling v0.25.1 h1:XzILnLzhZPZNtmxKaz/2xIGPQsBsvmCjrJOWGNz/ync= -github.com/go-openapi/swag/mangling v0.25.1/go.mod h1:CdiMQ6pnfAgyQGSOIYnZkXvqhnnwOn997uXZMAd/7mQ= -github.com/go-openapi/swag/netutils v0.25.1 h1:2wFLYahe40tDUHfKT1GRC4rfa5T1B4GWZ+msEFA4Fl4= -github.com/go-openapi/swag/netutils v0.25.1/go.mod h1:CAkkvqnUJX8NV96tNhEQvKz8SQo2KF0f7LleiJwIeRE= -github.com/go-openapi/swag/stringutils v0.25.1 h1:Xasqgjvk30eUe8VKdmyzKtjkVjeiXx1Iz0zDfMNpPbw= -github.com/go-openapi/swag/stringutils v0.25.1/go.mod h1:JLdSAq5169HaiDUbTvArA2yQxmgn4D6h4A+4HqVvAYg= -github.com/go-openapi/swag/typeutils v0.25.1 h1:rD/9HsEQieewNt6/k+JBwkxuAHktFtH3I3ysiFZqukA= -github.com/go-openapi/swag/typeutils v0.25.1/go.mod h1:9McMC/oCdS4BKwk2shEB7x17P6HmMmA6dQRtAkSnNb8= -github.com/go-openapi/swag/yamlutils v0.25.1 h1:mry5ez8joJwzvMbaTGLhw8pXUnhDK91oSJLDPF1bmGk= -github.com/go-openapi/swag/yamlutils v0.25.1/go.mod h1:cm9ywbzncy3y6uPm/97ysW8+wZ09qsks+9RS8fLWKqg= -github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= github.com/go-task/slim-sprig v0.0.0-20210107165309-348f09dbbbc0/go.mod h1:fyg7847qk6SyHyPtNmDHnmrv/HOrqktSC+C9fM+CJOE= github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572/go.mod h1:9Pwr4B2jHnOSGXyyzV8ROjYa2ojvAY6HCGYYfMoC3Ls= -github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= -github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gobuffalo/flect v0.2.3/go.mod h1:vmkQwuZYhN5Pc4ljYQZzP+1sq+NEkK+lh20jmEmX3jc= github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= @@ -554,7 +495,6 @@ github.com/golang/mock v1.5.0/go.mod h1:CWnOUgYIOo4TcNZ0wHX3YZCqsaM1I1Jvs6v3mP3K github.com/golang/mock v1.6.0/go.mod h1:p6yTPP+5HYm5mzsMV8JkE6ZKdX+/wYM6Hr+LicevLPs= github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= -github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= github.com/golang/protobuf v1.3.5/go.mod h1:6O5/vntMXwX2lRkT1hjjk0nAC1IDOTvTlVgjlRvqsdk= github.com/golang/protobuf v1.4.3/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= @@ -575,14 +515,12 @@ github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= github.com/google/cel-go v0.24.1 h1:jsBCtxG8mM5wiUJDSGUqU0K7Mtr3w7Eyv00rw4DiZxI= github.com/google/cel-go v0.24.1/go.mod h1:Hdf9TqOaTNSFQA1ybQaRqATVoK7m/zcf7IMhGXP5zI8= -github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= -github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= +github.com/google/certtostore v1.0.6/go.mod h1:2N0ZPLkGvQWhYvXaiBGq02r71fnSLfq78VKIWQHr1wo= +github.com/google/deck v0.0.0-20230104221208-105ad94aa8ae/go.mod h1:DoDv8G58DuLNZF0KysYn0bA/6ZWhmRW3fZE2VnGEH0w= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= github.com/google/gnostic v0.5.7-v3refs/go.mod h1:73MKFl6jIHelAJNaBGFzt3SPtZULs9dYrGFt8OiIsHQ= 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/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= -github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= github.com/google/go-cmp v0.4.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.1/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= @@ -591,11 +529,11 @@ github.com/google/go-cmp v0.5.4/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/ github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.8/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-containerregistry v0.16.1/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= github.com/google/go-containerregistry v0.17.0/go.mod h1:u0qB2l7mvtWVR5kNcbFIhFY1hLbf8eeGapA+vbFDCtQ= github.com/google/go-containerregistry v0.20.2 h1:B1wPJ1SN/S7pB+ZAimcciVD+r+yV/l/DSArMxlbwseo= -github.com/google/go-containerregistry v0.20.6 h1:cvWX87UxxLgaH76b4hIvya6Dzz9qHB31qAwjAohdSTU= -github.com/google/go-containerregistry v0.20.6/go.mod h1:T0x8MuoAoKX/873bkeSfLD2FAkwCDf9/HZgsFJ02E2Y= +github.com/google/go-dap v0.12.0/go.mod h1:tNjCASCm5cqePi/RVXXWEVqtnNLV1KTWtYOqu6rZNzc= github.com/google/go-pkcs11 v0.2.1-0.20230907215043-c6f79328ddf9/go.mod h1:6eQoGcuNJpa7jnd5pMGdkSaQpNDYvPlXWMcjXXThLlY= github.com/google/gofuzz v0.0.0-20161122191042-44d81051d367/go.mod h1:HP5RmnzzSNb993RKQDq4+1A4ia9nllfqcQFTQJedwGI= github.com/google/gofuzz v1.1.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= @@ -632,27 +570,34 @@ github.com/googleapis/gax-go/v2 v2.14.1/go.mod h1:Hb/NubMaVM88SrNkvl8X/o8XWwDJEP github.com/googleapis/gnostic v0.0.0-20170729233727-0c5108395e2d/go.mod h1:sJBsCZ4ayReDTBIg8b9dl28c5xFWyhBTVRp3pOg5EKY= github.com/gorilla/mux v1.8.0/go.mod h1:DVbg23sWSpFRCP0SfiEN6jmj59UnW/n46BH5rLB71So= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= -github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= -github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= +github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= 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.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.24.0/go.mod h1:qztMSjm835F2bXf+5HKAPIS5qsmQDqZna/PgVt4rWtI= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= -github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= +github.com/hanwen/go-fuse/v2 v2.8.0/go.mod h1:yE6D2PqWwm3CbYRxFXV9xUd8Md5d6NG0WBs5spCswmI= +github.com/hashicorp/go-cleanhttp v0.5.2/go.mod h1:kO/YDlP8L1346E6Sodw+PrpBSV4/SoxCXGY6BqNFT48= +github.com/hashicorp/go-cty-funcs v0.0.0-20250818135842-6aab67130928/go.mod h1:YC9ASYt9Z9sQEAtzCe+yaAzi3E7wcxfRphDXtwZoWC0= +github.com/hashicorp/go-immutable-radix/v2 v2.1.0/go.mod h1:hgdqLXA4f6NIjRVisM1TJ9aOJVNRqKZj+xDGF6m7PBw= +github.com/hashicorp/go-retryablehttp v0.7.8/go.mod h1:rjiScheydd+CxvumBsIrFKlx3iS0jrZ7LvzFGFmuKbw= github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM= +github.com/hiddeco/sshsig v0.2.0/go.mod h1:nJc98aGgiH6Yql2doqH4CTBVHexQA40Q+hMMLHP4EqE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/ianlancetaylor/demangle v0.0.0-20240312041847-bd984b5ce465/go.mod h1:gx7rwoVhcfuVKG5uya9Hs3Sxj7EIvldVofAWIUtGouw= +github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH0dnCYA= +github.com/intel/goresctrl v0.10.0/go.mod h1:1S8GDqL46GuKb525bxNhIEEkhf4rhVcbSf9DuKhp7mw= github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg= github.com/ishidawataru/sctp v0.0.0-20250521072954-ae8eb7fa7995/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= @@ -660,7 +605,7 @@ github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPG github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= github.com/jonboulle/clockwork v0.2.2/go.mod h1:Pkfl5aHPm1nk2H9h0bjmnJD/BcgbGXUBGnn1kMkgxc8= github.com/jonboulle/clockwork v0.4.0/go.mod h1:xgRqUGwRcjKCO1vbZUEtSLrqKoPSsUpK7fnezOII0kc= -github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= +github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y= github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4= github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.8/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= @@ -669,6 +614,7 @@ github.com/jstemmer/go-junit-report v0.9.1/go.mod h1:Brl9GWCQeLvo8nXZwPNNblvFj/X github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM= github.com/k14s/semver/v4 v4.0.1-0.20210701191048-266d47ac6115/go.mod h1:mGrnmO5qnhJIaSiwMo05cvRL6Ww9ccYbTgNFcm6RHZQ= github.com/karrick/godirwalk v1.17.0/go.mod h1:j4mkqPuvaLI8mp1DroR3P6ad7cyYd4c1qeJ3RV7ULlk= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= github.com/keybase/go-keychain v0.0.1/go.mod h1:PdEILRW3i9D8JcdM+FmY6RwkHGnhHxXwkPPMeUgOK1k= github.com/kisielk/errcheck v1.2.0/go.mod h1:/BMXB+zMLi60iA8Vv6Ksmxu/1UDYcXs4uQLJ+jE2L00= github.com/kisielk/errcheck v1.6.3/go.mod h1:nXw/i/MfnvRHqXa7XXmQMUB0oNFGuBrNI8d8NLy0LPw= @@ -678,9 +624,7 @@ github.com/klauspost/compress v1.16.5/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQs github.com/klauspost/compress v1.17.2/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE= 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.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= -github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= -github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/knqyf263/go-plugin v0.9.0/go.mod h1:2z5lCO1/pez6qGo8CvCxSlBFSEat4MEp1DrnA+f7w8Q= github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= @@ -689,6 +633,7 @@ github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wn github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= +github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -698,28 +643,30 @@ github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJ github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= -github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byFGLdw= -github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/matttproud/golang_protobuf_extensions v1.0.2-0.20181231171920-c182affec369/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0/go.mod h1:QUyp042oQthUoa9bqDv0ER0wrtXnBruoNd7aNjkbP+k= github.com/maxbrunsfeld/counterfeiter/v6 v6.7.0/go.mod h1:RVP6/F85JyxTrbJxWIdKU2vlSvK48iCMnMXRkSz7xtg= github.com/maxbrunsfeld/counterfeiter/v6 v6.8.1/go.mod h1:eyp4DdUJAKkr9tvxR3jWhw2mDK7CWABMG5r9uyaKC7I= github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2/go.mod h1:VzB2VoMh1Y32/QqDfg9ZJYHj99oM4LiGtqPZydTiQSQ= +github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= +github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= +github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= +github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= +github.com/mistifyio/go-zfs/v3 v3.0.1/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k= +github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/ipvs v1.1.0/go.mod h1:4VJMWuf098bsUMmZEiD4Tjk/O7mOn3l1PTD3s4OoYAs= github.com/moby/moby v27.1.1+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= -github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/policy-helpers v0.0.0-20251105011237-bcaa71c99f14/go.mod h1:HJfK0E8dR+Jpk5anJ3oADg2dRSom1gJK17sqEiiMS7w= +github.com/moby/profiles/seccomp v0.1.0/go.mod h1:Kqk57vxH6/wuOc5bmqRiSXJ6iEz8Pvo3LQRkv0ytFWs= +github.com/moby/sys/mount v0.3.4/go.mod h1:KcQJMbQdJHPlq5lcYT+/CjatWM4PuxKe+XLSVS4J6Os= github.com/moby/sys/mountinfo v0.6.2/go.mod h1:IJb6JQeOklcdMU9F5xQ8ZALD+CUr5VlGpwtX+VE0rpI= -github.com/moby/sys/mountinfo v0.7.2/go.mod h1:1YOa8w8Ih7uW0wALDUgT1dTTSBrZ+HiBLGws92L2RU4= -github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= -github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/sys/reexec v0.1.0/go.mod h1:EqjBg8F3X7iZe5pU6nRZnYCMUTXoxsjiIfHup5wYIN8= github.com/moby/term v0.0.0-20221205130635-1aeaba878587/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= github.com/modern-go/reflect2 v0.0.0-20180320133207-05fbef0ca5da/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= -github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= -github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/mohae/deepcopy v0.0.0-20170603005431-491d3605edfb/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/mohae/deepcopy v0.0.0-20170929034955-c48cc78d4826/go.mod h1:TaXosZuwdSHYgviHp1DAtfrULt5eUgsSMsZf+YrPgl8= github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod h1:Pm3mSP3c5uWn86xMLZ5Sa7JB9GsEZySvHYXCTK4E9q4= @@ -732,28 +679,25 @@ github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9k github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= github.com/onsi/ginkgo/v2 v2.1.4/go.mod h1:um6tUpWM/cxCK3/FK8BXqEiUMUwRgSM4JXG47RKZmLU= github.com/onsi/ginkgo/v2 v2.20.1/go.mod h1:lG9ey2Z29hR41WMVthyJBGUBcBhGOtoPF2VFMvBXFCI= -github.com/onsi/ginkgo/v2 v2.23.0/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= github.com/onsi/gomega v0.0.0-20170829124025-dcabb60a477c/go.mod h1:C1qb7wdrVGGVU+Z6iS04AVkA3Q65CEZX59MT0QO5uiA= github.com/onsi/gomega v1.17.0/go.mod h1:HnhC7FXeEQY45zxNK3PPoIUhzk/80Xly9PcubAlGdZY= github.com/onsi/gomega v1.18.1/go.mod h1:0q+aL8jAiMXy9hbwj2mr5GziHiwhAIQpFmmtT5hitRs= github.com/onsi/gomega v1.19.0/go.mod h1:LY+I3pBVzYsTBU1AnDwOSxaYi9WoWiqgwooUqq9yPro= github.com/onsi/gomega v1.27.10/go.mod h1:RsS8tutOdbdgzbPtzzATp12yT7kM5I5aElG3evPbQ0M= github.com/onsi/gomega v1.34.2/go.mod h1:v1xfxRgk0KIsG+QOdm7p8UosrOzPYRo60fd3B/1Dukc= -github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= github.com/opencontainers/cgroups v0.0.1/go.mod h1:s8lktyhlGUqM7OSRL5P7eAW6Wb+kWPNvt4qvVfzA5vs= github.com/opencontainers/image-spec v1.1.0-rc3/go.mod h1:X4pATf0uXsnn3g5aiGIsVnJBR4mxhKzfwmvK/B2NTm8= github.com/opencontainers/runc v1.1.12/go.mod h1:S+lQwSfncpBha7XTy/5lBwWgm5+y5Ma/O44Ekby9FK8= github.com/opencontainers/runc v1.2.1/go.mod h1:/PXzF0h531HTMsYQnmxXkBD7YaGShm/2zcRB79dksUc= github.com/opencontainers/runtime-spec v1.0.3-0.20220909204839-494a5a6aca78/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/runtime-spec v1.2.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= +github.com/opencontainers/runtime-tools v0.9.1-0.20251114084447-edf4cb3d2116/go.mod h1:DKDEfzxvRkoQ6n9TGhxQgg2IM1lY4aM0eaQP4e3oElw= github.com/opencontainers/selinux v1.11.0/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/opencontainers/selinux v1.11.1/go.mod h1:E5dMC3VPuVvVHDYmi78qvhJp8+M586T4DlDRYpFkyec= github.com/openshift/build-machinery-go v0.0.0-20230824093055-6a18da01283c/go.mod h1:b1BuldmJlbA/xYtdZvKi+7j5YGB44qJUJDZ9zwiNCfE= github.com/openshift/build-machinery-go v0.0.0-20240613134303-8359781da660/go.mod h1:8jcm8UPtg2mCAsxfqKil1xrmRMI3a+XU2TZ9fF8A7TE= github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11 h1:eTNDkNRNV5lZvUbVM9Nop0lBcljSnA8rZX6yQPZ0ZnU= github.com/openshift/crd-schema-checker v0.0.0-20240404194209-35a9033b1d11/go.mod h1:EmVJt97N+pfWFsli/ipXTBZqSG5F5KGQhm3c3IsGq1o= -github.com/openshift/crd-schema-checker v0.0.0-20250905140724-c313b6407231 h1:8lSGufji9rfiyDxtUl7A4uOyeeP4x0UOOXcsDBFfkGI= -github.com/openshift/crd-schema-checker v0.0.0-20250905140724-c313b6407231/go.mod h1:sTxJ4ZFW9r9fEdbW2v0yMRi6NcyTbx0fII4p83IQ+L8= github.com/openshift/generic-admission-server v1.14.1-0.20231020105858-8dcc3c9b298f/go.mod h1:/CLsleDcQ6AFTGKJe9VL3Y4rB9DqX3fQwQv47q2/ZJc= github.com/openshift/generic-admission-server v1.14.1-0.20240926143655-a882ebf9df19/go.mod h1:eNpBvr/3zce6zLOeCtBw48xbCp8SLAmQqu/rb7vFE9Y= github.com/otiai10/copy v1.2.0 h1:HvG945u96iNadPoG2/Ja2+AUJeW5YuFQMixq9yirC+k= @@ -762,11 +706,15 @@ github.com/otiai10/curr v0.0.0-20150429015615-9b4961190c95/go.mod h1:9qAhocn7zKJ github.com/otiai10/curr v1.0.0/go.mod h1:LskTG5wDwr8Rs+nNQ+1LlxRjAtTZZjtJW4rMXl6j4vs= github.com/otiai10/mint v1.3.0/go.mod h1:F5AjcsTsWUqX+Na9fpHb52P8pcRX2CI6A3ctIT91xUo= github.com/otiai10/mint v1.3.1/go.mod h1:/yxELlJQ0ufhjUwhshSj+wFjZ78CnZ48/1wtmBH1OTc= +github.com/package-url/packageurl-go v0.1.1/go.mod h1:uQd4a7Rh3ZsVg5j0lNyAfyxIeGde9yrlhjF78GzeW0c= github.com/pelletier/go-toml v1.9.3/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= +github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/petermattis/goid v0.0.0-20240813172612-4fcff4a6cae7/go.mod h1:pxMtw7cyUw6B2bRH0ZBANSPg+AoSud1I1iyJHI69jH4= +github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= github.com/pkg/diff v0.0.0-20210226163009-20ebb0f2a09e/go.mod h1:pJLUxLENpZxwdsKMEsNbx1VGcRFpLqf3715MtcvvzbA= +github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pkg/sftp v1.10.1/go.mod h1:lYOWFsE0bwd1+KfKJaKeuokY15vzFx25BLbzYYoAxZI= -github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1:t/avpk3KcrXxUnYOhZhMXJlSEyie6gQbtLq5NM3loB8= github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/pquerna/cachecontrol v0.1.0/go.mod h1:NrUG3Z7Rdu85UNR3vm7SOsl1nFIeSiQnrHV5K9mBcUI= github.com/prometheus/client_golang v1.14.0/go.mod h1:8vpkKitgIVNcqrRBWh1C4TIUQgYNtG/XQE4E/Zae36Y= @@ -775,43 +723,36 @@ github.com/prometheus/client_golang v1.15.1/go.mod h1:e9yaBhRPU2pPNsZwE+JdQl0KEt github.com/prometheus/client_golang v1.18.0/go.mod h1:T+GXkCk5wSJyOqMIzVgvvjFDlkOQntgjkJWKrN5txjA= 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_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= -github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= github.com/prometheus/client_model v0.3.0/go.mod h1:LDGWKZIo7rky3hgvBe+caln+Dr3dPggB5dvjtD7w9+w= github.com/prometheus/client_model v0.5.0/go.mod h1:dTiFglRmd66nLR9Pv9f0mZi7B7fk5Pm3gvsjB5tr+kI= -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.37.0/go.mod h1:phzohg0JFMnBEFGxTDbfu3QyL5GI8gTQJFhYO5B3mfA= github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= github.com/prometheus/common v0.45.0/go.mod h1:YJmSTw9BoKxJplESWWxlbyttQR4uaEcGyv9MZjVOJsY= github.com/prometheus/common v0.55.0 h1:KEi6DK7lXW/m7Ig5i47x0vRzuBsHuvJdi5ee6Y3G1dc= github.com/prometheus/common v0.55.0/go.mod h1:2SECS4xJG1kd8XF9IcM1gMX6510RAEL65zxzNImwdc8= -github.com/prometheus/common v0.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyAEN8= -github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko= github.com/prometheus/procfs v0.8.0/go.mod h1:z7EfXMXOkbkqb9IINtpCn86r/to3BnA0uaxHdg830/4= github.com/prometheus/procfs v0.9.0 h1:wzCHvIvM5SxWqYvwgVL7yJY8Lz3PKn49KQtpgMYJfhI= github.com/prometheus/procfs v0.9.0/go.mod h1:+pB4zwohETzFnmlpe6yd2lSc+0/46IYZRB/chUwxUZY= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= -github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= -github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= +github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= -github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= -github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= github.com/russross/blackfriday v1.6.0 h1:KqfZb0pUVN2lYqZUYRddxF4OR8ZMURnJIG5Y3VRLtww= github.com/russross/blackfriday v1.6.0/go.mod h1:ti0ldHuxg49ri4ksnFxlkCfN+hvslNlmVHqNRXXJNAY= github.com/santhosh-tekuri/jsonschema/v5 v5.3.1/go.mod h1:uToXkOrWAZ6/Oc07xWQrPOhJotwFIyu2bBVN41fcDUY= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.1 h1:PKK9DyHxif4LZo+uQSgXNqs0jj5+xZwwfKHgph2lxBw= -github.com/santhosh-tekuri/jsonschema/v6 v6.0.1/go.mod h1:JXeL+ps8p7/KNMjDQk3TCwPpBy0wYklyWTfbkIzdIFU= +github.com/sasha-s/go-deadlock v0.3.5/go.mod h1:bugP6EGbdGYObIlx7pUZtWqlvo8k9H6vCBBsiChJQ5U= github.com/seccomp/libseccomp-golang v0.10.0/go.mod h1:JA8cRccbGaA1s33RQf7Y1+q9gHmZX1yB/z9WDN1C6fg= +github.com/serialx/hashring v0.0.0-20200727003509-22c0c7ab6b1b/go.mod h1:/yeG0My1xr/u+HZrFQ1tOQQQQrOawfyMUH13ai5brBc= github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/sirupsen/logrus v1.7.0/go.mod h1:yWOB1SBYBC5VeMP7gHvWumXLIWorT60ONWic61uBYv0= github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/sirupsen/logrus v1.9.1/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/smallstep/pkcs7 v0.1.1/go.mod h1:dL6j5AIz9GHjVEBTXtW+QliALcgM19RtXaTeyxI+AfA= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= @@ -823,69 +764,63 @@ github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= -github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= +github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= +github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= -github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= -github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= -github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= -github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= -github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= -github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= -github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= +github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= +github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= +github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= +github.com/tonistiigi/go-actions-cache v0.0.0-20250626083717-378c5ed1ddd9/go.mod h1:cD0SB2270BYw6HYKriFn4H6NRLhGj6ytf48YTpsm8LY= +github.com/tonistiigi/go-archvariant v1.0.0/go.mod h1:TxFmO5VS6vMq2kvs3ht04iPXtu2rUT/erOnGFYfk5Ho= +github.com/tonistiigi/jaeger-ui-rest v0.0.0-20250408171107-3dd17559e117/go.mod h1:3Ez1Paeg+0Ghu3KwpEGC1HgZ4CHDlg+Ez/5Baeomk54= github.com/urfave/cli v1.22.12/go.mod h1:sSBEIC79qR6OvcmsD4U3KABeOTxDqQtdDnaFuUN30b8= github.com/urfave/cli v1.22.15/go.mod h1:wSan1hmo5zeyLGBjRJbzRTNk8gwoYa2B9n4q9dmRIc0= github.com/urfave/cli v1.22.16/go.mod h1:EeJR6BKodywf4zciqrdw6hpCPk68JO9z5LazXZMn5Po= +github.com/urfave/cli v1.22.17/go.mod h1:b0ht0aqgH/6pBYzzxURyrM4xXNgsoT/n2ZzwQiEhNVo= +github.com/urfave/cli/v2 v2.27.7/go.mod h1:CyNAG/xg+iAOg0N4MPGZqVmv2rCoP267496AOXUZjA4= github.com/vbatts/tar-split v0.11.3/go.mod h1:9QlHN18E+fEH7RdG+QAJJcuya3rqT7eXSTY7wGrAokY= github.com/vbatts/tar-split v0.11.6 h1:4SjTW5+PU11n6fZenf2IPoV8/tz3AaYHMWjf23envGs= github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkFDFN6TCBEI= -github.com/vbatts/tar-split v0.12.2 h1:w/Y6tjxpeiFMR47yzZPlPj/FcPLpXbTUi/9H7d3CPa4= -github.com/vbatts/tar-split v0.12.2/go.mod h1:eF6B6i6ftWQcDqEn3/iGFRFRo8cBIMSJVOpnNdfTMFA= github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.3.1-0.20250206174618-62fb240731fa/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= -github.com/vito/go-interact v1.0.2 h1:viJuANio3WH9utUG4rKbJC9V3JR5JgYNS+i0efeA+GU= -github.com/vito/go-interact v1.0.2/go.mod h1:s+y0jK9Z2etBYt5ZM6+DhpOsE5C7NNGC3jrJvW0BBpc= github.com/vmware-tanzu/carvel-imgpkg v0.36.0 h1:ha5a3WUPaqpGlP+QRkKBA9WyT85vUPh7+57x94Cmj58= github.com/vmware-tanzu/carvel-imgpkg v0.36.0/go.mod h1:8HeIt+froyx7iRjyZ/4py2wFMPXEFNyWUNUTQgAjD8M= github.com/vmware-tanzu/carvel-imgpkg v0.38.2/go.mod h1:v9BcO1qfXwwIQFw2zmksdUkx8eI1e+/a0Md3xG2BzDE= github.com/xeipuuv/gojsonpointer v0.0.0-20180127040702-4e3ac2762d5f h1:J9EGpcZtP0E/raorCMxlFGSTBrsSlaDGf3jU/qvAE2c= -github.com/xhit/go-str2duration/v2 v2.1.0 h1:lxklc02Drh6ynqX+DdPyp5pCKLUQpRT8bp8Ydu2Bstc= -github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtXVyJfNt1+BlmyAsU= github.com/xiang90/probing v0.0.0-20221125231312-a49e3df8f510/go.mod h1:UETIi67q53MR2AWcXfiuqkDkRtnGDLqkBTpCHuJHxtU= github.com/xlab/treeprint v1.2.0/go.mod h1:gj5Gd3gPdKtR1ikdDK6fnFLdmIS0X30kTTuNd/WEJu0= +github.com/xrash/smetrics v0.0.0-20240521201337-686a1a2994c1/go.mod h1:Ohn+xnUBiLI6FVj/9LpzZWtj1/D6lUovWYBkxHVV3aM= github.com/yuin/goldmark v1.1.25/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.1.32/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= +github.com/zclconf/go-cty v1.17.0/go.mod h1:wqFzcImaLTI6A5HfsRwB0nj5n0MRZFwmey8YoFPPs3U= +github.com/zeebo/errs v1.3.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= github.com/zeebo/errs v1.4.0/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= go.etcd.io/bbolt v1.3.8/go.mod h1:N9Mkw9X8x5fupy0IKsmuqVtoGDyxsaDlbk4Rd05IAQw= go.etcd.io/bbolt v1.3.11/go.mod h1:dksAq7YMXoljX0xu6VF5DMZGbhYYoLUalEiSySYAS4I= go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM= +go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.8/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k= go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI= -go.etcd.io/etcd/api/v3 v3.6.4/go.mod h1:eFhhvfR8Px1P6SEuLT600v+vrhdDTdcfMzmnxVXXSbk= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.8/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4= go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U= -go.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v2 v2.305.10/go.mod h1:m3CKZi69HzilhVqtPDcjhSGp+kA1OmbNn0qamH80xjA= go.etcd.io/etcd/client/v2 v2.305.16/go.mod h1:h9YxWCzcdvZENbfzBTFCnoNumr2ax3F19sKMqHFmXHE= go.etcd.io/etcd/client/v3 v3.5.8/go.mod h1:idZYIPVkttBJBiRigkB5EM0MmEyx8jcl18zCV3F5noc= go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc= -go.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo= go.etcd.io/etcd/pkg/v3 v3.5.10/go.mod h1:TKTuCKKcF1zxmfKWDkfz5qqYaE3JncKKZPFf8c1nFUs= go.etcd.io/etcd/pkg/v3 v3.5.16/go.mod h1:+lutCZHG5MBBFI/U4eYT5yL7sJfnexsoM20Y0t2uNuY= go.etcd.io/etcd/pkg/v3 v3.6.4/go.mod h1:kKcYWP8gHuBRcteyv6MXWSN0+bVMnfgqiHueIZnKMtE= @@ -900,25 +835,22 @@ go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.5/go.mod h1:5pWMHQbX5EPX2/62yrJeAkowc+lfs/XD7Uxpq3pI6kk= go.opencensus.io v0.23.0/go.mod h1:XItmlyltB5F7CS4xOC1DcqMoFqwtC6OG2xF7mCv7P7E= -go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= go.opentelemetry.io/contrib v0.20.0 h1:ubFQUn0VCZ0gPwIoJfBJVpeBlyRMxu8Mm/huKWYd9p0= go.opentelemetry.io/contrib v0.20.0/go.mod h1:G/EtFaa6qaN7+LxqfIAT3GiZa7Wv5DTBUzl5H4LY0Kc= go.opentelemetry.io/contrib/detectors/gcp v1.34.0/go.mod h1:cV4BMFcscUR/ckqLkbfQmF0PRsq8w/lMGzdbCSveBHo= go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPxAxnjc2pQTxWNkwfstZ+6H2k= +go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts= go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.42.0/go.mod h1:XiglO+8SPMqM3Mqh5/rtxR1VHc63o8tb38QrU6tm4mU= go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.44.0/go.mod h1:uq8DrRaen3suIWTpdR/JNHCGpurSvMv9D5Nr5CU5TXc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0/go.mod h1:h8TWwRAhQpOd0aM5nYsRD8+flnkj+526GEIVlarH7eY= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ= +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/contrib/instrumentation/net/http/otelhttp v0.20.0/go.mod h1:2AboqHi0CiIZU0qwhtUfCYD1GeUzvvIXWNkhDt7ZMG4= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.35.1/go.mod h1:9NiG9I2aHTKkcxqCILhjtyNA1QEiCjdBACv4IvrFQ+c= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0 h1:yd02MEjBdJkG3uabWP9apV+OuWRIXGDuJEUJbOHmCFU= go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.58.0/go.mod h1:umTcuxiv1n/s/S6/c2AT/g2CQ7u5C59sHDNmfSwgz7Q= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0 h1:RbKq8BG0FI8OiXhBfcRtqqHcZcka+gU3cskNuf05R18= -go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.63.0/go.mod h1:h06DGIukJOevXaj/xrNjhi/2098RZzcLTbc0jDAUbsg= go.opentelemetry.io/otel v0.20.0/go.mod h1:Y3ugLH2oa81t5QO+Lty+zXf8zC9L26ax4Nzoxm/dooo= go.opentelemetry.io/otel v1.10.0/go.mod h1:NbvWjCthWHKBEUMpf0/v8ZRZlni86PpGFEMA9pnQSnQ= go.opentelemetry.io/otel v1.19.0 h1:MuS/TNf4/j4IXsZuJegVzI1cwut7Qc00344rgH7p8bs= @@ -927,19 +859,18 @@ go.opentelemetry.io/otel v1.33.0 h1:/FerN9bax5LoK51X/sI0SVYrjSE0/yUL7DpxW4K3FWw= go.opentelemetry.io/otel v1.33.0/go.mod h1:SUUkR6csvUQl+yjReHu5uM3EtVV7MBm5FHKRlNx4I8I= go.opentelemetry.io/otel v1.38.0 h1:RkfdswUDRimDg0m2Az18RKOsnI8UDzppJAtj01/Ymk8= go.opentelemetry.io/otel v1.38.0/go.mod h1:zcmtmQ1+YmQM9wrNsTGV/q/uyusom3P8RxwExxkZhjM= +go.opentelemetry.io/otel/exporters/jaeger v1.17.0/go.mod h1:nPCqOnEH9rNLKqH/+rrUjiMzHJdV1BlpKcTwRTyKkKI= go.opentelemetry.io/otel/exporters/otlp v0.20.0/go.mod h1:YIieizyaN77rtLJra0buKiNBOm9XQfkPEKBeuhoMwAM= go.opentelemetry.io/otel/exporters/otlp/internal/retry v1.10.0/go.mod h1:78XhIg8Ht9vR4tbLNUhXsiOnE2HOuSeKAiAcoVQEpOY= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.10.0/go.mod h1:Krqnjl22jUJ0HgMzw5eveuCvFDXY4nSYb4F8t5gdrag= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0/go.mod h1:IPtUMKL4O3tH5y+iXVyAXqpAwMuzC1IrxVS81rummfE= go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.33.0/go.mod h1:cpgtDBaqD/6ok/UG0jT15/uKjAY8mRA53diogHBg3UI= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0 h1:GqRJVj7UmLjCVyVJ3ZFLdPRmhDUp2zFmQe3RHIOsw24= -go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.38.0/go.mod h1:ri3aaHSmCTVYu2AWv44YMauwAQc0aqI9gHKIcSbI1pU= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.10.0/go.mod h1:OfUCyyIiDvNXHWpcWgbF+MWvqPZiNa3YDEnivcnYsV0= go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0/go.mod h1:0+KuTDyKL4gjKCF75pHOX4wuzYDUZYfAQdSu43o+Z2I= 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/exporters/otlp/otlptrace/otlptracegrpc v1.38.0 h1:lwI4Dc5leUqENgGuQImwLo4WnuXFPetmPpkLi2IrX54= -go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.38.0/go.mod h1:Kz/oCE7z5wuyhPxsXDuaPteSWqjSBD5YaSdbxZYGbGk= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0/go.mod h1:f3bYiqNqhoPxkvI2LrXqQVC546K7BuRDL/kKuxkujhA= +go.opentelemetry.io/otel/exporters/stdout/stdouttrace v1.31.0/go.mod h1:fcwWuDuaObkkChiDlhEpSq9+X1C0omv+s5mBtToAQ64= go.opentelemetry.io/otel/metric v0.20.0/go.mod h1:598I5tYlH1vzBjn+BTuhzTCSb/9debfNp6R3s7Pr1eU= go.opentelemetry.io/otel/metric v0.31.0/go.mod h1:ohmwj9KTSIeBnDBm/ZwH2PSZxZzoOaG2xZeekTRzL5A= go.opentelemetry.io/otel/metric v1.19.0 h1:aTzpGtV0ar9wlV4Sna9sdJyII5jTVJEvKETPiOKwvpE= @@ -958,6 +889,7 @@ go.opentelemetry.io/otel/sdk v1.38.0 h1:l48sr5YbNf2hpCUj/FoGhW9yDkl+Ma+LrVl8qaM5 go.opentelemetry.io/otel/sdk v1.38.0/go.mod h1:ghmNdGlVemJI3+ZB5iDEuk4bWA3GkTpW+DOoZMYBVVg= go.opentelemetry.io/otel/sdk/export/metric v0.20.0/go.mod h1:h7RBNMsDJ5pmI1zExLi+bJK+Dr8NQCh0qGhm1KDnNlE= go.opentelemetry.io/otel/sdk/metric v0.20.0/go.mod h1:knxiS8Xd4E/N+ZqKmUPf3gTTZ4/0TjTXukfxjzSTpHE= +go.opentelemetry.io/otel/sdk/metric v1.38.0 h1:aSH66iL0aZqo//xXzQLYozmWrXxyFkBJ6qT5wthqPoM= go.opentelemetry.io/otel/sdk/metric v1.38.0/go.mod h1:dg9PBnW9XdQ1Hd6ZnRz689CbtrUp0wMMs9iPcgT9EZA= go.opentelemetry.io/otel/trace v0.20.0/go.mod h1:6GjCW8zgDjwGHGa6GkyeB8+/5vjT16gUEi0Nf1iBdgw= go.opentelemetry.io/otel/trace v1.10.0/go.mod h1:Sij3YYczqAdz+EhmGhE6TpTxUO5/F/AzrK+kxfGqySM= @@ -971,8 +903,6 @@ go.opentelemetry.io/proto/otlp v0.7.0/go.mod h1:PqfVotwruBrMGOCsRd/89rSnXhoiJIqe go.opentelemetry.io/proto/otlp v0.19.0/go.mod h1:H7XAot3MsfNsj7EXtrA2q5xSNQ10UqI405h3+duxN4U= go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= go.opentelemetry.io/proto/otlp v1.4.0/go.mod h1:PPBWZIP98o2ElSqI35IHfu7hIhSwvc5N38Jw8pXuGFY= -go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= -go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.starlark.net v0.0.0-20230525235612-a134d8f9ddca/go.mod h1:jxU+3+j+71eXOW14274+SmmuW82qJzl6iZSeqEtTGds= go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= @@ -983,10 +913,6 @@ go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= -go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= -go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= -go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= -go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1004,8 +930,6 @@ golang.org/x/exp v0.0.0-20191227195350-da58074b4299/go.mod h1:2RIsYlXP63K8oxa1u0 golang.org/x/exp v0.0.0-20200119233911-0405dc783f0a/go.mod h1:2RIsYlXP63K8oxa1u096TMicItID8zy7Y6sNkU49FU4= golang.org/x/exp v0.0.0-20200207192155-f17229e696bd/go.mod h1:J/WKrq2StrnmMY6+EHIKF9dgMWnmCNThgcyBT1FY9mM= golang.org/x/exp v0.0.0-20200224162631-6cc2880d07d6/go.mod h1:3jZMyOhIsHpP37uCMkUooju7aAi5cS1Q23tOzKc+0MU= -golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546 h1:mgKeJMpvi0yx/sU5GsxQ7p6s2wtOnGAHZWCHUM4KGzY= -golang.org/x/exp v0.0.0-20251023183803-a4bb9ffd2546/go.mod h1:j/pmGrbnkbPtQfxEe5D0VQhZC6qKbfKifgD0oM7sR70= golang.org/x/lint v0.0.0-20191125180803-fdd1cda4f05f/go.mod h1:5qLYkcX4OjUUV8bRuDixDT3tpyyb+LUpUlRWLxfhWrs= golang.org/x/lint v0.0.0-20200130185559-910be7a94367/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= golang.org/x/lint v0.0.0-20200302205851-738671d3881b/go.mod h1:3xt1FjdF8hUf6vQPIChWIBhFzV8gjjsPE/fR3IyQdNY= @@ -1026,9 +950,7 @@ golang.org/x/mod v0.20.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= golang.org/x/mod v0.21.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= -golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190827160401-ba9fcec4b297/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1069,8 +991,8 @@ golang.org/x/net v0.30.0/go.mod h1:2wGyMJ5iFasEhkwi13ChkO/t1ECNC4X4eBKkVFyYFlU= golang.org/x/net v0.32.0/go.mod h1:CwU0IoeOlnQQWJ6ioyFrfRuomB8GKF6KbYXZVyeXNfs= 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/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= -golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= +golang.org/x/net v0.46.0/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= +golang.org/x/net v0.46.1-0.20251013234738-63d1a5100f82/go.mod h1:Q9BGdFy1y4nkUwiLvT5qtyhAnEHgnQ/zd8PfU6nc210= golang.org/x/oauth2 v0.0.0-20191202225959-858c2ad4c8b6/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= golang.org/x/oauth2 v0.0.0-20200902213428-5d25da1a8d43/go.mod h1:KelEdhl1UZF7XfJ4dDtk6s++YSgaE7mD/BuKKDLBl4A= @@ -1084,8 +1006,7 @@ golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE golang.org/x/oauth2 v0.13.0/go.mod h1:/JMhi4ZRXAf4HG9LiNmxvk+45+96RUlVThiH8FzNBn0= 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/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= -golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/oauth2 v0.32.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20200625203802-6e8e738ad208/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201207232520-09787c993a3a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= @@ -1099,6 +1020,7 @@ golang.org/x/sync v0.8.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.9.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= golang.org/x/sync v0.11.0 h1:GGz8+XQP4FvTTrjZPzNKTMFtSXH80RAzG+5ghFPgK9w= golang.org/x/sync v0.11.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/sync v0.17.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20170830134202-bb24a47a89ea/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -1125,6 +1047,7 @@ golang.org/x/sys v0.0.0-20201201145000-ef89a241ccb3/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210104204734-6f8348627aad/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210112080510-489259a85091/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210119212857-b64e53b001e4/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210124154548-22da62e12c0c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210220050731-9a76102bfb43/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210305230114-8fe3ee5dd75b/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210315160823-c6e025ad8005/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -1138,7 +1061,7 @@ golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220325203850-36772127a21f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220906165534-d0df966e6959/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.10.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.13.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.14.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= @@ -1152,15 +1075,16 @@ golang.org/x/sys v0.25.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.26.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.29.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.35.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= +golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= 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.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= -golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.0.0-20160726164857-2910a502d2bf/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.0.0-20170915032832-14c0d48ead0c/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= @@ -1172,13 +1096,10 @@ golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.17.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.18.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= golang.org/x/text v0.19.0/go.mod h1:BuEKDfySbSR4drPmRPG/7iBdf8hvFMuRexcpahXilzY= -golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= -golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/time v0.0.0-20191024005414-555d28b269f0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.0.0-20210723032227-1f47c861a9ac/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= -golang.org/x/time v0.14.0 h1:MRx4UaLrDotUKUdCIqzPC48t1Y9hANFKIRpNx+Te8PI= -golang.org/x/time v0.14.0/go.mod h1:eL/Oa2bBBK0TkX57Fyni+NgnyQQN4LitPmob2Hjnqw4= golang.org/x/tools v0.0.0-20181011042414-1f849cf54d09/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20181030221726-6c7e314b6563/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20190206041539-40960b6deb8e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -1222,13 +1143,14 @@ golang.org/x/tools v0.1.2/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.1.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= golang.org/x/tools v0.4.0/go.mod h1:UE5sM2OK9E/d67R0ANs2xJizIymRP5gJU295PvKXxjQ= golang.org/x/tools v0.9.1/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.11.0/go.mod h1:anzJrxPjNtfgiYQYirP2CPGzGLxrH2u2QBhn6Bf3qY8= golang.org/x/tools v0.12.0/go.mod h1:Sc0INKfu04TlqNoRA1hgpFZbhYXHPr4V5DzpSBTPqQM= golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58= golang.org/x/tools v0.16.1/go.mod h1:kYVVN6I1mBNoB1OX+noeBjbRk4IUEPa7JJ+TJMEooJ0= golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/tools v0.24.0/go.mod h1:YhNqVBIfWHdzvTLs0d8LCuMhkKUgSUKldakyV7W/WDQ= golang.org/x/tools v0.29.0/go.mod h1:KMQVMRsVxU6nHCFXrBPhDB8XncLNLM0lIy/F14RP588= -golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= +golang.org/x/tools v0.37.0/go.mod h1:MBN5QPQtLMHVdvsbtarmTNukZDdgwdwlO5qGacAzF0w= golang.org/x/tools/go/expect v0.1.1-deprecated/go.mod h1:eihoPOH+FgIqa3FpoTwguz/bVUSGBlGQU67vpBeOrBY= golang.org/x/tools/go/packages/packagestest v0.1.1-deprecated/go.mod h1:RVAQXBGNv1ib0J382/DPCRS/BPnsGebyM1Gj5VSDpG8= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= @@ -1236,7 +1158,6 @@ gomodules.xyz/jsonpatch/v2 v2.2.0/go.mod h1:WXp+iVDkoLQqPudfQ9GBlwB2eZ5DKOnjQZCY gomodules.xyz/jsonpatch/v2 v2.3.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= gonum.org/v1/gonum v0.0.0-20190331200053-3d26580ed485/go.mod h1:2ltnJ7xHfj0zHS40VVPYEAAMTa3ZGguvHGBSJeRWqE0= -gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E= gonum.org/v1/netlib v0.0.0-20190313105609-8cb42192e0e0/go.mod h1:wa6Ws7BG/ESfp6dHfk7C6KdzKA7wR7u/rKwOGE66zvw= gonum.org/v1/netlib v0.0.0-20190331212654-76723241ea4e/go.mod h1:kS+toOQn6AQKjmKJ7gzohV1XkqsFehRA2FbsbkopSuQ= google.golang.org/api v0.14.0/go.mod h1:iLdEw5Ide6rF15KTC1Kkl0iskquN2gFfn9o9XIsbkAI= @@ -1310,6 +1231,7 @@ google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7 h1: google.golang.org/genproto/googleapis/api v0.0.0-20240826202546-f6391c0de4c7/go.mod h1:OCdP9MfskevB/rbYvHTsXTtKC+3bHWajPdoKgjcYkfo= google.golang.org/genproto/googleapis/api v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:qpvKtACPCQhAdu3PyQgV4l3LMXZEtft7y8QcarRsp9I= google.golang.org/genproto/googleapis/api v0.0.0-20241209162323-e6fa225c2576/go.mod h1:1R3kvZ1dtP3+4p4d3G8uJ8rFk/fWlScl38vanWACI08= +google.golang.org/genproto/googleapis/api v0.0.0-20251022142026-3a174f9686a8/go.mod h1:fDMmzKV90WSg1NbozdqrE64fkuTv6mlq2zxo9ad+3yo= google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba h1:B14OtaXuMaCQsl2deSvNkyPKIzq3BjfxQp8d00QyWx4= google.golang.org/genproto/googleapis/api v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:G5IanEx8/PgI9w6CFcYQf7jMtHQhZruvfM1i3qOqk5U= google.golang.org/genproto/googleapis/bytestream v0.0.0-20231030173426-d783a09b4405/go.mod h1:GRUCuLdzVqZte8+Dl/D4N25yLzcGqqWaYkeVOwulFqw= @@ -1319,6 +1241,8 @@ google.golang.org/genproto/googleapis/rpc v0.0.0-20240826202546-f6391c0de4c7/go. google.golang.org/genproto/googleapis/rpc v0.0.0-20240903143218-8af14fe29dc1/go.mod h1:UqMtugtsSgubUsoxbuAoiCXvqvErP7Gf0so0mK9tHxU= google.golang.org/genproto/googleapis/rpc v0.0.0-20241206012308-a4fef0638583/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= google.golang.org/genproto/googleapis/rpc v0.0.0-20241209162323-e6fa225c2576/go.mod h1:5uTbfoYQed2U9p3KIj2/Zzm02PYhndfdmML0qC3q3FU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251022142026-3a174f9686a8/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20251103181224-f26f9409b101/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba h1:UKgtfRM7Yh93Sya0Fo8ZzhDP4qBckrrxEr2oF5UIVb8= google.golang.org/genproto/googleapis/rpc v0.0.0-20251111163417-95abcf5c77ba/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= @@ -1344,6 +1268,7 @@ google.golang.org/grpc v1.67.1/go.mod h1:1gLDyUQU7CTLJI90u3nXZ9ekeghjeM7pTDZlqFN google.golang.org/grpc v1.68.1/go.mod h1:+q1XYFJjShcqn0QZHvCyeR4CXPA+llXIeUIfIe00waw= google.golang.org/grpc v1.76.0 h1:UnVkv1+uMLYXoIz6o7chp59WfQUYA2ex/BXQ9rHZu7A= google.golang.org/grpc v1.76.0/go.mod h1:Ju12QI8M6iQJtbcsV+awF5a4hfJMLi4X0JLo94ULZ6c= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= @@ -1359,11 +1284,6 @@ google.golang.org/protobuf v1.34.2/go.mod h1:qYOHts0dSfpeUzUFpOMr/WGzszTmLH+DiWn google.golang.org/protobuf v1.35.2/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= 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/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= -google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= -gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= -gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= -gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= @@ -1377,31 +1297,19 @@ gotest.tools/v3 v3.0.3/go.mod h1:Z7Lb0S5l+klDB31fvDQX8ss/FlKDxtlFlw3Oa8Ymbl8= honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= honnef.co/go/tools v0.0.1-2020.1.3/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= honnef.co/go/tools v0.0.1-2020.1.4/go.mod h1:X/FiERA/W4tHapMX5mGpAtMSVEeEUOyHaw9vFzvIQ3k= -k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= -k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= k8s.io/apiextensions-apiserver v0.25.0/go.mod h1:3pAjZiN4zw7R8aZC5gR0y3/vCkGlAjCazcg1me8iB/E= k8s.io/apiextensions-apiserver v0.27.7/go.mod h1:x0p+b5a955lfPz9gaDeBy43obM12s+N9dNHK6+dUL+g= k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h7yLdRForMZwc= k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0= k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw= k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto= -k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= -k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= -k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= -k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= k8s.io/apiserver v0.25.6/go.mod h1:IEp2B2/FvQ8GmdspscUoUS0iFF/GGc6NVrJ/cTM4OaA= k8s.io/apiserver v0.27.7/go.mod h1:OrLG9RwCOerutAlo8QJW5EHzUG9Dad7k6rgcDUNSO/w= k8s.io/apiserver v0.29.0/go.mod h1:31n78PsRKPmfpee7/l9NYEv67u6hOL6AfcE761HapDM= k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4= k8s.io/apiserver v0.32.1 h1:oo0OozRos66WFq87Zc5tclUX2r0mymoVHRq8JmR7Aak= k8s.io/apiserver v0.32.1/go.mod h1:UcB9tWjBY7aryeI5zAgzVJB/6k7E97bkr1RgqDz0jPw= -k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA= -k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0= -k8s.io/cli-runtime v0.34.2 h1:cct1GEuWc3IyVT8MSCoIWzRGw9HJ/C5rgP32H60H6aE= -k8s.io/cli-runtime v0.34.2/go.mod h1:X13tsrYexYUCIq8MarCBy8lrm0k0weFPTpcaNo7lms4= -k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= -k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= k8s.io/code-generator v0.25.6/go.mod h1:aDxzxJynLKQkaa117y0FFcgZ5jG8+GobxZ2JUntmvKk= k8s.io/code-generator v0.27.7/go.mod h1:w1YF/xQcTg+d9Ag+04xuRqER+q8rDnJ70ynLql8/RLA= @@ -1411,12 +1319,8 @@ k8s.io/code-generator v0.34.1/go.mod h1:DeWjekbDnJWRwpw3s0Jat87c+e0TgkxoR4ar608y k8s.io/component-base v0.28.6 h1:G4T8VrcQ7xZou3by/fY5NU5mfxOBlWaivS2lPrEltAo= k8s.io/component-base v0.28.6/go.mod h1:Dg62OOG3ALu2P4nAG00UdsuHoNLQJ5VsUZKQlLDcS+E= k8s.io/component-base v0.29.0/go.mod h1:sADonFTQ9Zc9yFLghpDpmNXEdHyQmFIGbiuZbqAXQ1M= -k8s.io/component-base v0.34.2 h1:HQRqK9x2sSAsd8+R4xxRirlTjowsg6fWCPwWYeSvogQ= -k8s.io/component-base v0.34.2/go.mod h1:9xw2FHJavUHBFpiGkZoKuYZ5pdtLKe97DEByaA+hHbM= k8s.io/component-helpers v0.29.0/go.mod h1:j2coxVfmzTOXWSE6sta0MTgNSr572Dcx68F6DD+8fWc= -k8s.io/component-helpers v0.34.2 h1:RIUGDdU+QFzeVKLZ9f05sXTNAtJrRJ3bnbMLrogCrvM= -k8s.io/component-helpers v0.34.2/go.mod h1:pLi+GByuRTeFjjcezln8gHL7LcT6HImkwVQ3A2SQaEE= -k8s.io/controller-manager v0.33.5/go.mod h1:KuQeAlf4vI2+qj5fwPVLaDlbtrTBA/8L/LqQvI74Ow0= +k8s.io/cri-api v0.34.1/go.mod h1:4qVUjidMg7/Z9YGZpqIDygbkPWkg3mkS1PvOx/kpHTE= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20210813121822-485abfe95c7c/go.mod h1:FiNAH4ZV3gBg2Kwh89tzAEV2be7d5xI0vBa/VySYy3E= @@ -1442,13 +1346,7 @@ k8s.io/kube-aggregator v0.22.17/go.mod h1:J557nueFVurHA1JiDrxT1HlgygNQ+2exsTVUXi k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= -k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= -k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= -k8s.io/kubectl v0.34.2 h1:+fWGrVlDONMUmmQLDaGkQ9i91oszjjRAa94cr37hzqA= -k8s.io/kubectl v0.34.2/go.mod h1:X2KTOdtZZNrTWmUD4oHApJ836pevSl+zvC5sI6oO2YQ= k8s.io/kubernetes v1.31.2 h1:VNSu4O7Xn5FFRsh9ePXyEPg6ucR21fOftarSdi053Gs= -k8s.io/kubernetes v1.34.2 h1:WQdDvYJazkmkwSncgNwGvVtaCt4TYXIU3wSMRgvp3MI= -k8s.io/kubernetes v1.34.2/go.mod h1:m6pZk6a179pRo2wsTiCPORJ86iOEQmfIzUvtyEF8BwA= k8s.io/metrics v0.29.0/go.mod h1:UCuTT4dC/x/x6ODSk87IWIZQnuAfcwxOjb1gjWJdjMA= k8s.io/metrics v0.30.3/go.mod h1:W06L2nXRhOwPkFYDJYWdEIS3u6JcJy3ebIPYbndRs6A= k8s.io/metrics v0.32.3/go.mod h1:9R1Wk5cb+qJpCQon9h52mgkVCcFeYxcY+YkumfwHVCU= @@ -1459,8 +1357,8 @@ k8s.io/system-validators v1.10.2/go.mod h1:awfSS706v9R12VC7u7K89FKfqVy44G+E0L1A0 k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= -k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= +kernel.org/pub/linux/libs/security/libcap/cap v1.2.76/go.mod h1:7V2BQeHnVAQwhCnCPJ977giCeGDiywVewWF+8vkpPlc= +kernel.org/pub/linux/libs/security/libcap/psx v1.2.76/go.mod h1:+l6Ee2F59XiJ2I6WR5ObpC1utCQJZ/VLsEbQCD8RG24= modernc.org/cc v1.0.0/go.mod h1:1Sk4//wdnYJiUIxnW8ddKpaOJCF37yAdqYnkxUpaYxw= modernc.org/golex v1.0.0/go.mod h1:b/QX9oBD/LhixY6NDh+IdGv17hgB+51fET1i2kPSmvk= modernc.org/mathutil v1.0.0/go.mod h1:wU0vUrJsVWBZ4P6e7xtFJEhFSNsfRLJ8H458uRjg03k= @@ -1476,18 +1374,10 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0/go.mod h1:VHVDI/ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0/go.mod h1:z7+wmGM2dfIiLRfrC6jb5kV2Mq/sK1ZP303cxzkV5Y4= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= sigs.k8s.io/controller-runtime v0.15.3/go.mod h1:kp4jckA4vTx281S/0Yk2LFEEQe67mjg+ev/yknv47Ds= -sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= -sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= sigs.k8s.io/controller-tools v0.7.0/go.mod h1:bpBAo0VcSDDLuWt47evLhMLPxRPxMDInTEH/YbdeMK0= sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz4CVE26eOSDAeYCpfDnC2kdKMY= -sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= -sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= -sigs.k8s.io/kind v0.30.0 h1:2Xi1KFEfSMm0XDcvKnUt15ZfgRPCT0OnCBbpgh8DztY= -sigs.k8s.io/kind v0.30.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= sigs.k8s.io/knftables v0.0.14/go.mod h1:f/5ZLKYEUPUhVjUCg6l80ACdL7CIIyeL0DxfgojGRTk= sigs.k8s.io/knftables v0.0.17/go.mod h1:f/5ZLKYEUPUhVjUCg6l80ACdL7CIIyeL0DxfgojGRTk= sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= @@ -1503,9 +1393,7 @@ sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:w sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= -sigs.k8s.io/structured-merge-diff/v6 v6.3.0/go.mod h1:M3W8sfWvn2HhQDIbGWj3S099YozAsymCo/wrT5ohRUE= +sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= -sigs.k8s.io/yaml v1.6.0 h1:G8fkbMSAFqgEFgh4b1wmtzDnioxFCUgTZhlbj5P9QYs= -sigs.k8s.io/yaml v1.6.0/go.mod h1:796bPqUfzR/0jLAl6XjHl3Ck7MiyVv8dbTdyT3/pMf4= +tags.cncf.io/container-device-interface/specs-go v1.1.0/go.mod h1:u86hoFWqnh3hWz3esofRFKbI261bUlvUfLKGrDhJkgQ= From 54583fc0bc9bd008448e42509b9d2069548c9090 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Mon, 19 Jan 2026 17:37:21 +0100 Subject: [PATCH 03/24] Refactored project_xxx commands --- .../pkg/cmd/project_docs_open_cmd.go | 23 ++------------- client-programs/pkg/config/project.go | 5 ++++ client-programs/pkg/utils/browser.go | 28 +++++++++++++++++++ 3 files changed, 36 insertions(+), 20 deletions(-) create mode 100644 client-programs/pkg/config/project.go create mode 100644 client-programs/pkg/utils/browser.go diff --git a/client-programs/pkg/cmd/project_docs_open_cmd.go b/client-programs/pkg/cmd/project_docs_open_cmd.go index 7de7198a4..6567516a7 100644 --- a/client-programs/pkg/cmd/project_docs_open_cmd.go +++ b/client-programs/pkg/cmd/project_docs_open_cmd.go @@ -1,10 +1,8 @@ package cmd import ( - "fmt" - "os/exec" - "runtime" - + "github.com/educates/educates-training-platform/client-programs/pkg/config" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/spf13/cobra" ) @@ -17,22 +15,7 @@ func (p *ProjectInfo) NewProjectDocsOpenCmd() *cobra.Command { Use: "open", Short: "Open browser on project documentation", RunE: func(_ *cobra.Command, _ []string) error { - var err error - - const url = "https://docs.educates.dev/" - - switch runtime.GOOS { - case "linux": - err = exec.Command("xdg-open", url).Start() - case "windows": - err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() - case "darwin": - err = exec.Command("open", url).Start() - default: - err = fmt.Errorf("unsupported platform") - } - - return err + return utils.OpenBrowser(config.PROJECT_DOCS_URL) }, } diff --git a/client-programs/pkg/config/project.go b/client-programs/pkg/config/project.go new file mode 100644 index 000000000..992201109 --- /dev/null +++ b/client-programs/pkg/config/project.go @@ -0,0 +1,5 @@ +package config + +const ( + PROJECT_DOCS_URL = "https://docs.educates.dev/" +) diff --git a/client-programs/pkg/utils/browser.go b/client-programs/pkg/utils/browser.go new file mode 100644 index 000000000..c16dc2621 --- /dev/null +++ b/client-programs/pkg/utils/browser.go @@ -0,0 +1,28 @@ +package utils + +import ( + "fmt" + "os/exec" + "runtime" +) + +func OpenBrowser(url string) error { + var err error + + if url == "" { + return fmt.Errorf("url is required") + } + + switch runtime.GOOS { + case "linux": + err = exec.Command("xdg-open", url).Start() + case "windows": + err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() + case "darwin": + err = exec.Command("open", url).Start() + default: + err = fmt.Errorf("unsupported platform") + } + + return err +} From 159a7e50f57eeb8f9df683c4d716c32a5adf9001 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Mon, 19 Jan 2026 18:00:04 +0100 Subject: [PATCH 04/24] Refactored commands admin_diagnostics_xx and admin_lookup_xx --- client-programs/pkg/cluster/cluster.go | 2 +- .../pkg/cmd/admin_diagnostics_analyze_cmd.go | 8 +- .../pkg/cmd/admin_diagnostics_collect_cmd.go | 19 ++- .../pkg/cmd/admin_lookup_kubeconfig_cmd.go | 113 +++++------------- client-programs/pkg/lookup/lookup.go | 93 ++++++++++++++ 5 files changed, 148 insertions(+), 87 deletions(-) create mode 100644 client-programs/pkg/lookup/lookup.go diff --git a/client-programs/pkg/cluster/cluster.go b/client-programs/pkg/cluster/cluster.go index bf4b80d89..00f5c402f 100644 --- a/client-programs/pkg/cluster/cluster.go +++ b/client-programs/pkg/cluster/cluster.go @@ -50,7 +50,7 @@ func GetConfig(kubeconfigPath string, context string) (*rest.Config, error) { if kubeconfigPath != "" { if _, err := os.Stat(kubeconfigPath); os.IsNotExist(err) { // If kubeconfig is provided but not available, fail - return nil, errors.Wrap(err, "kubeconfig file does not exist") + return nil, fmt.Errorf("kubeconfig file does not exist: %s", kubeconfigPath) } } diff --git a/client-programs/pkg/cmd/admin_diagnostics_analyze_cmd.go b/client-programs/pkg/cmd/admin_diagnostics_analyze_cmd.go index 62dc7d21a..2198451a2 100644 --- a/client-programs/pkg/cmd/admin_diagnostics_analyze_cmd.go +++ b/client-programs/pkg/cmd/admin_diagnostics_analyze_cmd.go @@ -11,6 +11,11 @@ type AdminDiagnosticsAnalyzeOptions struct { Dir string } +var adminDiagnosticsAnalyzeExample = ` + # Analyze diagnostic information for current Educates cluster in current directory + educates admin diagnostics analyze --file ./diagnostics.tar.gz +` + func (o *AdminDiagnosticsAnalyzeOptions) Run() error { // clusterConfig := cluster.NewClusterConfig(o.Kubeconfig, "") @@ -31,6 +36,7 @@ func (p *ProjectInfo) NewAdminDiagnosticsAnalyzeCmd() *cobra.Command { Use: "analyze", Short: "Analyze diagnostic information for an Educates cluster", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: adminDiagnosticsAnalyzeExample, } c.Flags().StringVar( @@ -47,7 +53,7 @@ func (p *ProjectInfo) NewAdminDiagnosticsAnalyzeCmd() *cobra.Command { "Path to the directory where the diagnostics files are located", ) - // c.MarkFlagRequired("dest") + //c.MarkFlagRequired("file") return c } diff --git a/client-programs/pkg/cmd/admin_diagnostics_collect_cmd.go b/client-programs/pkg/cmd/admin_diagnostics_collect_cmd.go index 98f202175..7f626cb59 100644 --- a/client-programs/pkg/cmd/admin_diagnostics_collect_cmd.go +++ b/client-programs/pkg/cmd/admin_diagnostics_collect_cmd.go @@ -4,10 +4,10 @@ import ( "os" "path/filepath" - "github.com/mitchellh/go-homedir" - "github.com/spf13/cobra" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/diagnostics" + "github.com/mitchellh/go-homedir" + "github.com/spf13/cobra" ) type AdminDiagnosticsCollectOptions struct { @@ -16,6 +16,20 @@ type AdminDiagnosticsCollectOptions struct { Verbose bool } +var adminDiagnosticsCollectExample = ` + # Collect diagnostic information for current Educates cluster in current directory + educates admin diagnostics collect + + # Collect diagnostic information ffor current Educates cluster in current directory with verbose output + educates admin diagnostics collect --verbose + + # Collect diagnostic information for an Educates cluster and save to a specific directory + educates admin diagnostics collect --dest ./diagnostics + + # Collect diagnostic information for a specific Educates Cluster in current directory + educates admin diagnostics collect --kubeconfig /path/to/kubeconfig --context my-cluster +` + func (o *AdminDiagnosticsCollectOptions) Run() error { clusterConfig := cluster.NewClusterConfig(o.Kubeconfig, o.Context) @@ -36,6 +50,7 @@ func (p *ProjectInfo) NewAdminDiagnosticsCollectCmd() *cobra.Command { Use: "collect", Short: "Collect diagnostic information for an Educates cluster", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: adminDiagnosticsCollectExample, } c.Flags().StringVar( diff --git a/client-programs/pkg/cmd/admin_lookup_kubeconfig_cmd.go b/client-programs/pkg/cmd/admin_lookup_kubeconfig_cmd.go index 3f9c5eace..ec2b5bbf9 100644 --- a/client-programs/pkg/cmd/admin_lookup_kubeconfig_cmd.go +++ b/client-programs/pkg/cmd/admin_lookup_kubeconfig_cmd.go @@ -1,15 +1,12 @@ package cmd import ( - "context" - "encoding/base64" "fmt" - "io/ioutil" + "os" + "github.com/educates/educates-training-platform/client-programs/pkg/lookup" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/cluster" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type LookupConfigOptions struct { @@ -17,85 +14,16 @@ type LookupConfigOptions struct { OutputPath string } -func (o *LookupConfigOptions) Run() error { - var err error +const adminLookupKubeconfigExample = ` + # Fetch kubeconfig for lookup service remote access + educates admin lookup kubeconfig - clusterConfig, err := cluster.NewClusterConfigIfAvailable(o.Kubeconfig, o.Context) - if err != nil { - return err - } - - client, err := clusterConfig.GetClient() - - if err != nil { - return err - } - - // We need to fetch the secret called "remote-access-token" from the - // "educates" namespace. This contains a Kubernetes access token secret - // giving access to just the Educates custom resources. - - secretsClient := client.CoreV1().Secrets("educates") - - secret, err := secretsClient.Get(context.TODO(), "remote-access-token", metav1.GetOptions{}) - - if err != nil { - return errors.Wrapf(err, "unable to fetch remote-access secret") - } - - // Within the secret are data fields for "ca.crt" and "token". We need to - // extract these and use them to create a kubeconfig file. Note that there - // is no "server" property in the secret, so when constructing the kubeconfig - // we need to use the server from the same cluster as we are requesting the - // secret from. - - caCrt := secret.Data["ca.crt"] - token := secret.Data["token"] + # Fetch kubeconfig for lookup service remote access and save to a specific file + educates admin lookup kubeconfig --output ./lookup-kubeconfig.yaml - // Get the server from the client for Kubernetes cluster access. - - serverScheme := client.CoreV1().RESTClient().Get().URL().Scheme - serverHost := client.CoreV1().RESTClient().Get().URL().Host - - serverUrl := fmt.Sprintf("%s://%s", serverScheme, serverHost) - - // Construct the kubeconfig file. We need to base64 encode the ca.crt file - // as it is a binary file. - - kubeconfig := fmt.Sprintf(`apiVersion: v1 -kind: Config -clusters: -- name: training-platform - cluster: - server: %s - certificate-authority-data: %s -contexts: -- name: training-platform - context: - cluster: training-platform - user: remote-access -current-context: training-platform -users: -- name: remote-access - user: - token: %s -`, serverUrl, base64.StdEncoding.EncodeToString(caCrt), token) - - // Write out the kubeconfig to the output path if provided, otherwise - // print it to stdout. - - if o.OutputPath != "" { - err = ioutil.WriteFile(o.OutputPath, []byte(kubeconfig), 0644) - - if err != nil { - return errors.Wrapf(err, "unable to write kubeconfig to %s", o.OutputPath) - } - } else { - fmt.Print(kubeconfig) - } - - return nil -} + # Fetch kubeconfig for lookup service remote access for a specific cluster + educates admin lookup kubeconfig --kubeconfig /path/to/kubeconfig --context my-cluster +` func (p *ProjectInfo) NewAdminLookupKubeconfigCmd() *cobra.Command { var o LookupConfigOptions @@ -105,8 +33,27 @@ func (p *ProjectInfo) NewAdminLookupKubeconfigCmd() *cobra.Command { Use: "kubeconfig", Short: "Fetch kubeconfig for lookup service remote access", RunE: func(cmd *cobra.Command, _ []string) error { - return o.Run() + config := lookup.LookupConfig{ + Kubeconfig: o.Kubeconfig, + Context: o.Context, + } + kubeconfig, err := lookup.NewLookupService().RemoteAccessKubeconfig(&config) + if err != nil { + return err + } + if o.OutputPath != "" { + err = os.WriteFile(o.OutputPath, []byte(kubeconfig), 0644) + + if err != nil { + return errors.Wrapf(err, "unable to write kubeconfig to %s", o.OutputPath) + } + } else { + fmt.Print(kubeconfig) + } + + return nil }, + Example: adminLookupKubeconfigExample, } c.Flags().StringVar( diff --git a/client-programs/pkg/lookup/lookup.go b/client-programs/pkg/lookup/lookup.go new file mode 100644 index 000000000..54236a8ce --- /dev/null +++ b/client-programs/pkg/lookup/lookup.go @@ -0,0 +1,93 @@ +package lookup + +import ( + "context" + "encoding/base64" + "fmt" + + "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/pkg/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + + +type LookupConfig struct { + Kubeconfig string + Context string +} + +type LookupService struct { +} + +func NewLookupService() *LookupService { + return &LookupService{} +} + +func (o *LookupService) RemoteAccessKubeconfig(config *LookupConfig) (string, error) { + var err error + + clusterConfig, err := cluster.NewClusterConfigIfAvailable(config.Kubeconfig, config.Context) + if err != nil { + return "", err + } + + client, err := clusterConfig.GetClient() + + if err != nil { + return "", err + } + + // We need to fetch the secret called "remote-access-token" from the + // "educates" namespace. This contains a Kubernetes access token secret + // giving access to just the Educates custom resources. + + secretsClient := client.CoreV1().Secrets("educates") + + secret, err := secretsClient.Get(context.TODO(), "remote-access-token", metav1.GetOptions{}) + + if err != nil { + return "", errors.Wrapf(err, "unable to fetch remote-access secret") + } + + // Within the secret are data fields for "ca.crt" and "token". We need to + // extract these and use them to create a kubeconfig file. Note that there + // is no "server" property in the secret, so when constructing the kubeconfig + // we need to use the server from the same cluster as we are requesting the + // secret from. + + caCrt := secret.Data["ca.crt"] + token := secret.Data["token"] + + // Get the server from the client for Kubernetes cluster access. + + serverScheme := client.CoreV1().RESTClient().Get().URL().Scheme + serverHost := client.CoreV1().RESTClient().Get().URL().Host + + serverUrl := fmt.Sprintf("%s://%s", serverScheme, serverHost) + + // Construct the kubeconfig file. We need to base64 encode the ca.crt file + // as it is a binary file. + + kubeconfig := fmt.Sprintf(`apiVersion: v1 +kind: Config +clusters: +- name: training-platform + cluster: + server: %s + certificate-authority-data: %s +contexts: +- name: training-platform + context: + cluster: training-platform + user: remote-access +current-context: training-platform +users: +- name: remote-access + user: + token: %s +`, serverUrl, base64.StdEncoding.EncodeToString(caCrt), token) + + // Write out the kubeconfig to the output path if provided, otherwise + // print it to stdout. + return kubeconfig, nil +} From f5c7622d4cd443cba365b43772571ed826f3effc Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Mon, 19 Jan 2026 18:03:36 +0100 Subject: [PATCH 05/24] Refactored admin_platform_xx command --- client-programs/pkg/cmd/admin_diagnostics_analyze_cmd.go | 2 +- client-programs/pkg/cmd/admin_diagnostics_collect_cmd.go | 2 +- client-programs/pkg/cmd/admin_platform_config_cmd.go | 4 ++-- client-programs/pkg/cmd/admin_platform_delete_cmd.go | 9 +++++++++ client-programs/pkg/cmd/admin_platform_deploy_cmd.go | 2 +- client-programs/pkg/cmd/admin_platform_values_cmd.go | 4 ++-- client-programs/pkg/cmd/workshop_export_cmd.go | 2 +- client-programs/pkg/cmd/workshop_new_cmd.go | 2 +- client-programs/pkg/cmd/workshop_publish_cmd.go | 2 +- 9 files changed, 19 insertions(+), 10 deletions(-) diff --git a/client-programs/pkg/cmd/admin_diagnostics_analyze_cmd.go b/client-programs/pkg/cmd/admin_diagnostics_analyze_cmd.go index 2198451a2..e4a34e85d 100644 --- a/client-programs/pkg/cmd/admin_diagnostics_analyze_cmd.go +++ b/client-programs/pkg/cmd/admin_diagnostics_analyze_cmd.go @@ -11,7 +11,7 @@ type AdminDiagnosticsAnalyzeOptions struct { Dir string } -var adminDiagnosticsAnalyzeExample = ` +const adminDiagnosticsAnalyzeExample = ` # Analyze diagnostic information for current Educates cluster in current directory educates admin diagnostics analyze --file ./diagnostics.tar.gz ` diff --git a/client-programs/pkg/cmd/admin_diagnostics_collect_cmd.go b/client-programs/pkg/cmd/admin_diagnostics_collect_cmd.go index 7f626cb59..cc29a780a 100644 --- a/client-programs/pkg/cmd/admin_diagnostics_collect_cmd.go +++ b/client-programs/pkg/cmd/admin_diagnostics_collect_cmd.go @@ -16,7 +16,7 @@ type AdminDiagnosticsCollectOptions struct { Verbose bool } -var adminDiagnosticsCollectExample = ` +const adminDiagnosticsCollectExample = ` # Collect diagnostic information for current Educates cluster in current directory educates admin diagnostics collect diff --git a/client-programs/pkg/cmd/admin_platform_config_cmd.go b/client-programs/pkg/cmd/admin_platform_config_cmd.go index 5e7d2b490..b02a5c1bd 100644 --- a/client-programs/pkg/cmd/admin_platform_config_cmd.go +++ b/client-programs/pkg/cmd/admin_platform_config_cmd.go @@ -9,7 +9,7 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/installer" ) -var ( +const ( adminPlatformConfigExample = ` # Show configuration config for local deployment educates admin platform config --local-config @@ -18,7 +18,7 @@ var ( educates admin platform config --config config.yaml # Get configuration used to deploy to the current cluster - educates admin platform config --from-cluster + educates admin platform config --from-cluster educates admin platform config --from-cluster --kubeconfig /path/to/kubeconfig --context my-cluster # Get configuration config with different domain (to make copies of the config) diff --git a/client-programs/pkg/cmd/admin_platform_delete_cmd.go b/client-programs/pkg/cmd/admin_platform_delete_cmd.go index 6dfdb03a3..a141d2404 100644 --- a/client-programs/pkg/cmd/admin_platform_delete_cmd.go +++ b/client-programs/pkg/cmd/admin_platform_delete_cmd.go @@ -16,6 +16,14 @@ type PlatformDeleteOptions struct { Verbose bool } +const adminPlatformDeleteExample = ` + # Delete Educates and related cluster services from your cluster + educates admin platform delete + + # Delete Educates and related cluster services from your cluster for a specific cluster + educates admin platform delete --kubeconfig /path/to/kubeconfig --context my-cluster +` + func (o *PlatformDeleteOptions) Run() error { fullConfig := config.NewDefaultInstallationConfig() @@ -44,6 +52,7 @@ func (p *ProjectInfo) NewAdminPlatformDeleteCmd() *cobra.Command { RunE: func(cmd *cobra.Command, _ []string) error { return o.Run() }, + Example: adminPlatformDeleteExample, } c.Flags().StringVar( diff --git a/client-programs/pkg/cmd/admin_platform_deploy_cmd.go b/client-programs/pkg/cmd/admin_platform_deploy_cmd.go index 51d4bf15f..11cf47503 100644 --- a/client-programs/pkg/cmd/admin_platform_deploy_cmd.go +++ b/client-programs/pkg/cmd/admin_platform_deploy_cmd.go @@ -12,7 +12,7 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/secrets" ) -var ( +const ( adminPlatformDeployExample = ` # Deploy educates platform educates admin platform deploy --config config.yaml diff --git a/client-programs/pkg/cmd/admin_platform_values_cmd.go b/client-programs/pkg/cmd/admin_platform_values_cmd.go index 95f57bde5..20ebe5c1b 100644 --- a/client-programs/pkg/cmd/admin_platform_values_cmd.go +++ b/client-programs/pkg/cmd/admin_platform_values_cmd.go @@ -10,7 +10,7 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/installer" ) -var ( +const ( adminPlatformValuesExample = ` # Show configuration values for local deployment educates admin platform values --local-config @@ -19,7 +19,7 @@ var ( educates admin platform values --config config.yaml # Get configuration used to deploy to the current cluster - educates admin platform values --from-cluster + educates admin platform values --from-cluster educates admin platform values --from-cluster --kubeconfig /path/to/kubeconfig --context my-cluster # Get configuration values using locally built educates package (version latest does the same and skips image resolution) diff --git a/client-programs/pkg/cmd/workshop_export_cmd.go b/client-programs/pkg/cmd/workshop_export_cmd.go index 7efb647a0..aa905892c 100644 --- a/client-programs/pkg/cmd/workshop_export_cmd.go +++ b/client-programs/pkg/cmd/workshop_export_cmd.go @@ -11,7 +11,7 @@ import ( "github.com/spf13/cobra" ) -var workshopExportExample = ` +const workshopExportExample = ` # Export workshop resource definition in current directory educates workshop export diff --git a/client-programs/pkg/cmd/workshop_new_cmd.go b/client-programs/pkg/cmd/workshop_new_cmd.go index 81ae17bc1..0feb8a651 100644 --- a/client-programs/pkg/cmd/workshop_new_cmd.go +++ b/client-programs/pkg/cmd/workshop_new_cmd.go @@ -20,7 +20,7 @@ type WorkshopNewOptions struct { Image string } -var workshopNewExample = ` +const workshopNewExample = ` # Create workshop files from template in my-workshop directory educates workshop new my-workshop diff --git a/client-programs/pkg/cmd/workshop_publish_cmd.go b/client-programs/pkg/cmd/workshop_publish_cmd.go index 58bc76b2c..f5a1ff014 100644 --- a/client-programs/pkg/cmd/workshop_publish_cmd.go +++ b/client-programs/pkg/cmd/workshop_publish_cmd.go @@ -23,7 +23,7 @@ type FilesPublishOptions struct { DataValuesFlags yttcmd.DataValuesFlags } -var workshopPublishExample = ` +const workshopPublishExample = ` # Publish workshop files to repository in current directory educates workshop publish From d86b65fa29d55c41d09b5b0eb1bc5fcf3e0941cd Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Mon, 19 Jan 2026 19:13:36 +0100 Subject: [PATCH 06/24] Refactored cluster_portal_xxx command --- .../pkg/cmd/cluster_portal_create_cmd.go | 166 ++-------- .../pkg/cmd/cluster_portal_delete_cmd.go | 38 ++- .../pkg/cmd/cluster_portal_list_cmd.go | 50 +-- .../pkg/cmd/cluster_portal_open_cmd.go | 84 ++--- .../pkg/cmd/cluster_portal_password_cmd.go | 51 +-- .../pkg/cmd/cluster_session_extend_cmd.go | 7 +- .../pkg/cmd/cluster_session_list_cmd.go | 5 +- .../pkg/cmd/cluster_session_status_cmd.go | 7 +- .../pkg/cmd/cluster_session_terminate_cmd.go | 7 +- .../pkg/cmd/cluster_workshop_delete_cmd.go | 7 +- .../pkg/cmd/cluster_workshop_deploy_cmd.go | 9 +- .../pkg/cmd/cluster_workshop_list_cmd.go | 5 +- .../pkg/cmd/cluster_workshop_request_cmd.go | 9 +- .../pkg/cmd/cluster_workshop_serve_cmd.go | 5 +- .../pkg/cmd/cluster_workshop_update_cmd.go | 9 +- .../pkg/cmd/docker_workshop_delete_cmd.go | 5 +- .../pkg/cmd/docker_workshop_deploy_cmd.go | 3 +- .../pkg/cmd/docker_workshop_logs.go | 3 +- .../pkg/cmd/docker_workshop_open_cmd.go | 3 +- .../pkg/cmd/project_docs_open_cmd.go | 4 +- client-programs/pkg/config/project.go | 5 - client-programs/pkg/constants/project.go | 9 + .../pkg/docker/workshop_manager.go | 3 +- client-programs/pkg/portal/manager.go | 308 ++++++++++++++++++ client-programs/pkg/portal/password.go | 20 ++ client-programs/pkg/secrets/secrets.go | 5 +- client-programs/pkg/workshops/constants.go | 3 - client-programs/pkg/workshops/definition.go | 3 +- 28 files changed, 513 insertions(+), 320 deletions(-) delete mode 100644 client-programs/pkg/config/project.go create mode 100644 client-programs/pkg/constants/project.go create mode 100644 client-programs/pkg/portal/manager.go create mode 100644 client-programs/pkg/portal/password.go delete mode 100644 client-programs/pkg/workshops/constants.go diff --git a/client-programs/pkg/cmd/cluster_portal_create_cmd.go b/client-programs/pkg/cmd/cluster_portal_create_cmd.go index 393191c1c..584195632 100644 --- a/client-programs/pkg/cmd/cluster_portal_create_cmd.go +++ b/client-programs/pkg/cmd/cluster_portal_create_cmd.go @@ -1,16 +1,11 @@ package cmd import ( - "context" - "strings" - "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/portal" "github.com/pkg/errors" "github.com/spf13/cobra" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/client-go/dynamic" ) type ClusterConfigViewOptions struct { @@ -25,13 +20,26 @@ type ClusterConfigViewOptions struct { Labels []string } +const clusterPortalCreateExample = ` +# Create TrainingPortal in Educates cluster with default name +educates cluster portal create + +# Create TrainingPortal in Educates cluster with specific name +educates cluster portal create --portal=my-portal + +# Create TrainingPortal in Educates cluster with specific name and capacity and theme +educates cluster portal create --portal=my-portal --capacity=10 --theme-name=my-theme + +# Create given TrainingPortal in given Educates cluster +educates cluster portal create --portal=my-portal --kubeconfig ~/.kube/config --context=my-context +` + func (o *ClusterConfigViewOptions) Run(isPasswordSet bool) error { var err error // Ensure have portal name. - if o.Portal == "" { - o.Portal = "educates-cli" + o.Portal = constants.DefaultPortalName } clusterConfig, err := cluster.NewClusterConfigIfAvailable(o.Kubeconfig, o.Context) @@ -46,9 +54,21 @@ func (o *ClusterConfigViewOptions) Run(isPasswordSet bool) error { return errors.Wrapf(err, "unable to create Kubernetes client") } - // Update the training portal, creating it if necessary. + config := portal.TrainingPortalCreateConfig{ + Portal: o.Portal, + Hostname: o.Hostname, + Repository: o.Repository, + Capacity: o.Capacity, + Password: o.Password, + IsPasswordSet: isPasswordSet, + ThemeName: o.ThemeName, + CookieDomain: o.CookieDomain, + Labels: o.Labels, + } + + manager := portal.NewPortalManager(dynamicClient) - err = createTrainingPortal(dynamicClient, o.Portal, o.Hostname, o.Repository, o.Capacity, o.Password, isPasswordSet, o.ThemeName, o.CookieDomain, o.Labels) + err = manager.CreateTrainingPortal(&config) if err != nil { return err @@ -69,6 +89,7 @@ func (p *ProjectInfo) NewClusterPortalCreateCmd() *cobra.Command { return o.Run(isPasswordSet) }, + Example: clusterPortalCreateExample, } c.Flags().StringVar( @@ -87,7 +108,7 @@ func (p *ProjectInfo) NewClusterPortalCreateCmd() *cobra.Command { &o.Portal, "portal", "p", - "educates-cli", + constants.DefaultPortalName, "name to be used for training portal and workshop name prefixes", ) c.Flags().StringVar( @@ -105,7 +126,7 @@ func (p *ProjectInfo) NewClusterPortalCreateCmd() *cobra.Command { c.Flags().UintVar( &o.Capacity, "capacity", - 5, + constants.DefaultPortalCapacity, "maximum number of current sessions for the training portal", ) c.Flags().StringVar( @@ -136,122 +157,3 @@ func (p *ProjectInfo) NewClusterPortalCreateCmd() *cobra.Command { return c } - -func createTrainingPortal(client dynamic.Interface, portal string, hostname string, registry string, capacity uint, password string, isPasswordSet bool, themeName string, cookieDomain string, labels []string) error { - trainingPortalClient := client.Resource(trainingPortalResource) - - _, err := trainingPortalClient.Get(context.TODO(), portal, metav1.GetOptions{}) - - if err != nil { - if !k8serrors.IsNotFound(err) { - return errors.Wrap(err, "unable to query training portal") - } - } else { - return errors.New("training portal already exists") - } - - trainingPortal := &unstructured.Unstructured{} - - if !isPasswordSet { - password = randomPassword(12) - } - - type LabelDetails struct { - Name string `json:"name"` - Value string `json:"value"` - } - - var labelOverrides []LabelDetails - - for _, value := range labels { - parts := strings.SplitN(value, "=", 2) - labelOverrides = append(labelOverrides, LabelDetails{ - Name: parts[0], - Value: parts[1], - }) - } - - type RegistryDetails struct { - Host string `json:"host"` - Namespace string `json:"namespace"` - } - - registryHost := "" - registryNamespace := "" - - if registry != "" { - parts := strings.SplitN(registry, "/", 2) - - registryHost = parts[0] - - if len(parts) > 1 { - registryNamespace = parts[1] - } - - } - - trainingPortal.SetUnstructuredContent(map[string]interface{}{ - "apiVersion": "training.educates.dev/v1beta1", - "kind": "TrainingPortal", - "metadata": map[string]interface{}{ - "name": portal, - }, - "spec": map[string]interface{}{ - "portal": map[string]interface{}{ - "password": password, - "registration": struct { - Type string `json:"type"` - }{ - Type: "anonymous", - }, - "updates": struct { - Workshop bool `json:"workshop"` - }{ - Workshop: true, - }, - "sessions": struct { - Maximum int64 `json:"maximum"` - }{ - Maximum: int64(capacity), - }, - "workshop": map[string]interface{}{ - "defaults": struct { - Reserved int `json:"reserved"` - Registry RegistryDetails `json:"registry"` - }{ - Reserved: 0, - Registry: RegistryDetails{ - Host: registryHost, - Namespace: registryNamespace, - }, - }, - }, - "ingress": struct { - Hostname string `json:"hostname"` - }{ - Hostname: hostname, - }, - "theme": struct { - Name string `json:"name"` - }{ - Name: themeName, - }, - "cookies": struct { - Domain string `json:"domain"` - }{ - Domain: cookieDomain, - }, - "labels": labelOverrides, - }, - "workshops": []interface{}{}, - }, - }) - - _, err = trainingPortalClient.Create(context.TODO(), trainingPortal, metav1.CreateOptions{FieldManager: "educates-cli"}) - - if err != nil { - return errors.Wrapf(err, "unable to create training portal %q in cluster", portal) - } - - return nil -} diff --git a/client-programs/pkg/cmd/cluster_portal_delete_cmd.go b/client-programs/pkg/cmd/cluster_portal_delete_cmd.go index dc94d06cb..a0f95b80c 100644 --- a/client-programs/pkg/cmd/cluster_portal_delete_cmd.go +++ b/client-programs/pkg/cmd/cluster_portal_delete_cmd.go @@ -1,13 +1,11 @@ package cmd import ( - "context" - + "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/portal" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/cluster" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) type ClusterPortalDeleteOptions struct { @@ -15,13 +13,24 @@ type ClusterPortalDeleteOptions struct { Portal string } +const clusterPortalDeleteExample = ` +# Delete TrainingPortal from Educates cluster with default name +educates cluster portal delete + +# Delete TrainingPortal from Educates cluster with specific name +educates cluster portal delete --portal=my-portal + +# Delete given TrainingPortal from given Educates cluster +educates cluster portal delete --portal=my-portal --kubeconfig ~/.kube/config --context=my-context +` + func (o *ClusterPortalDeleteOptions) Run() error { var err error // Ensure have portal name. if o.Portal == "" { - o.Portal = "educates-cli" + o.Portal = constants.DefaultPortalName } clusterConfig, err := cluster.NewClusterConfigIfAvailable(o.Kubeconfig, o.Context) @@ -36,18 +45,16 @@ func (o *ClusterPortalDeleteOptions) Run() error { return errors.Wrapf(err, "unable to create Kubernetes client") } - trainingPortalClient := dynamicClient.Resource(trainingPortalResource) - - _, err = trainingPortalClient.Get(context.TODO(), o.Portal, metav1.GetOptions{}) - - if k8serrors.IsNotFound(err) { - return errors.New("no portal found") + config := portal.TrainingPortalDeleteConfig{ + Portal: o.Portal, } - err = trainingPortalClient.Delete(context.TODO(), o.Portal, metav1.DeleteOptions{}) + manager := portal.NewPortalManager(dynamicClient) + + err = manager.DeleteTrainingPortal(&config) if err != nil { - return errors.Wrap(err, "unable to delete portal") + return err } return nil @@ -61,6 +68,7 @@ func (p *ProjectInfo) NewClusterPortalDeleteCmd() *cobra.Command { Use: "delete", Short: "Delete portal from Kubernetes", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: clusterPortalDeleteExample, } c.Flags().StringVar( @@ -79,7 +87,7 @@ func (p *ProjectInfo) NewClusterPortalDeleteCmd() *cobra.Command { &o.Portal, "portal", "p", - "educates-cli", + constants.DefaultPortalName, "name to be used for training portal and workshop name prefixes", ) diff --git a/client-programs/pkg/cmd/cluster_portal_list_cmd.go b/client-programs/pkg/cmd/cluster_portal_list_cmd.go index 70d777e82..99bcea36d 100644 --- a/client-programs/pkg/cmd/cluster_portal_list_cmd.go +++ b/client-programs/pkg/cmd/cluster_portal_list_cmd.go @@ -1,23 +1,26 @@ package cmd import ( - "context" "fmt" - "os" - "text/tabwriter" + "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/portal" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/cluster" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) type ClusterPortalListOptions struct { KubeconfigOptions } +const clusterPortalListExample = ` +# List TrainingPortals deployed to Educates cluster +educates cluster portal list + +# List TrainingPortals deployed to Educaets cluster and save to file +educates cluster portal list --kubeconfig ~/.kube/config --context=my-context +` + func (o *ClusterPortalListOptions) Run() error { var err error @@ -33,37 +36,15 @@ func (o *ClusterPortalListOptions) Run() error { return errors.Wrapf(err, "unable to create Kubernetes client") } - trainingPortalClient := dynamicClient.Resource(trainingPortalResource) + manager := portal.NewPortalManager(dynamicClient) - trainingPortals, err := trainingPortalClient.List(context.TODO(), metav1.ListOptions{}) + list, err := manager.ListTrainingPortals(nil) - if k8serrors.IsNotFound(err) { - fmt.Println("No portals found.") - return nil + if err != nil { + return err } - w := new(tabwriter.Writer) - w.Init(os.Stdout, 8, 8, 3, ' ', 0) - - defer w.Flush() - - fmt.Fprintf(w, "%s\t%s\t%s\n", "NAME", "CAPACITY", "URL") - - for _, item := range trainingPortals.Items { - name := item.GetName() - - sessionsMaximum, propertyExists, err := unstructured.NestedInt64(item.Object, "spec", "portal", "sessions", "maximum") - - var capacity string - - if err == nil && propertyExists { - capacity = fmt.Sprintf("%d", sessionsMaximum) - } - - url, _, _ := unstructured.NestedString(item.Object, "status", "educates", "url") - - fmt.Fprintf(w, "%s\t%s\t%s\n", name, capacity, url) - } + fmt.Print(list) return nil } @@ -76,6 +57,7 @@ func (p *ProjectInfo) NewClusterPortalListCmd() *cobra.Command { Use: "list", Short: "List portals deployed to Kubernetes", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: clusterPortalListExample, } c.Flags().StringVar( diff --git a/client-programs/pkg/cmd/cluster_portal_open_cmd.go b/client-programs/pkg/cmd/cluster_portal_open_cmd.go index 82b0321c0..6464a838c 100644 --- a/client-programs/pkg/cmd/cluster_portal_open_cmd.go +++ b/client-programs/pkg/cmd/cluster_portal_open_cmd.go @@ -1,21 +1,17 @@ package cmd import ( - "context" "fmt" "io" "net/http" - "net/url" - "os/exec" - "runtime" "time" + "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/portal" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/cluster" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) type ClusterPortalOpenOptions struct { @@ -24,13 +20,26 @@ type ClusterPortalOpenOptions struct { Portal string } +const clusterPortalOpenExample = ` +# Open TrainingPortal in Educates cluster with default name +educates cluster portal open + +# Open TrainingPortal in Educates cluster with specific name +educates cluster portal open --portal=my-portal + +# Open admin interface of specific TrainingPortal +educates cluster portal open --portal=my-portal --admin + +# Open given TrainingPortal in given Educates cluster +educates cluster portal open --portal=my-portal --kubeconfig ~/.kube/config --context=my-context +` + func (o *ClusterPortalOpenOptions) Run() error { var err error // Ensure have portal name. - if o.Portal == "" { - o.Portal = "educates-cli" + o.Portal = constants.DefaultPortalName } clusterConfig, err := cluster.NewClusterConfigIfAvailable(o.Kubeconfig, o.Context) @@ -45,37 +54,20 @@ func (o *ClusterPortalOpenOptions) Run() error { return errors.Wrapf(err, "unable to create Kubernetes client") } - trainingPortalClient := dynamicClient.Resource(trainingPortalResource) - - trainingPortal, err := trainingPortalClient.Get(context.TODO(), o.Portal, metav1.GetOptions{}) - - if k8serrors.IsNotFound(err) { - return errors.New("no workshops deployed") + config := portal.TrainingPortalOpenConfig{ + Portal: o.Portal, + Admin: o.Admin, } - targetUrl, found, _ := unstructured.NestedString(trainingPortal.Object, "status", "educates", "url") - - if !found { - return errors.New("workshops not available") - } + manager := portal.NewPortalManager(dynamicClient) - rootUrl := targetUrl + targetUrl, err := manager.GetTrainingPortalBrowserUrl(&config) - if o.Admin { - targetUrl = targetUrl + "/admin" - } else { - password, _, _ := unstructured.NestedString(trainingPortal.Object, "spec", "portal", "password") - - if password != "" { - values := url.Values{} - values.Add("redirect_url", "/") - values.Add("password", password) - - targetUrl = fmt.Sprintf("%s/workshops/access/?%s", targetUrl, values.Encode()) - } + if err != nil { + return err } - fmt.Printf("Training portal %q.\n", trainingPortal.GetName()) + fmt.Printf("Training portal %q.\n", o.Portal) fmt.Print("Checking training portal is ready.\n") @@ -89,7 +81,7 @@ func (o *ClusterPortalOpenOptions) Run() error { time.Sleep(time.Second) - resp, err := http.Get(rootUrl) + resp, err := http.Get(targetUrl) if err != nil || resp.StatusCode == 503 { continue @@ -105,22 +97,7 @@ func (o *ClusterPortalOpenOptions) Run() error { fmt.Printf("Opening training portal %s.\n", targetUrl) - switch runtime.GOOS { - case "linux": - err = exec.Command("xdg-open", targetUrl).Start() - case "windows": - err = exec.Command("rundll32", "url.dll,FileProtocolHandler", targetUrl).Start() - case "darwin": - err = exec.Command("open", targetUrl).Start() - default: - err = fmt.Errorf("unsupported platform") - } - - if err != nil { - return errors.Wrap(err, "unable to open web browser") - } - - return nil + return utils.OpenBrowser(targetUrl) } func (p *ProjectInfo) NewClusterPortalOpenCmd() *cobra.Command { @@ -131,6 +108,7 @@ func (p *ProjectInfo) NewClusterPortalOpenCmd() *cobra.Command { Use: "open", Short: "Browse portal in Kubernetes", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: clusterPortalOpenExample, } c.Flags().StringVar( @@ -155,7 +133,7 @@ func (p *ProjectInfo) NewClusterPortalOpenCmd() *cobra.Command { &o.Portal, "portal", "p", - "educates-cli", + constants.DefaultPortalName, "name to be used for training portal and workshop name prefixes", ) diff --git a/client-programs/pkg/cmd/cluster_portal_password_cmd.go b/client-programs/pkg/cmd/cluster_portal_password_cmd.go index 964627957..19ff5788d 100644 --- a/client-programs/pkg/cmd/cluster_portal_password_cmd.go +++ b/client-programs/pkg/cmd/cluster_portal_password_cmd.go @@ -1,17 +1,13 @@ package cmd import ( - "context" "fmt" - "os" - "text/tabwriter" + "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/portal" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/cluster" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) type ClusterPortalPasswordOptions struct { @@ -26,7 +22,7 @@ func (o *ClusterPortalPasswordOptions) Run() error { // Ensure have portal name. if o.Portal == "" { - o.Portal = "educates-cli" + o.Portal = constants.DefaultPortalName } clusterConfig, err := cluster.NewClusterConfigIfAvailable(o.Kubeconfig, o.Context) @@ -41,40 +37,21 @@ func (o *ClusterPortalPasswordOptions) Run() error { return errors.Wrapf(err, "unable to create Kubernetes client") } - trainingPortalClient := dynamicClient.Resource(trainingPortalResource) - - trainingPortal, err := trainingPortalClient.Get(context.TODO(), o.Portal, metav1.GetOptions{}) - - if k8serrors.IsNotFound(err) { - return errors.New("no workshops deployed") + config := portal.TrainingPortalPasswordConfig{ + Portal: o.Portal, + Admin: o.Admin, } - if o.Admin { - username, found, err := unstructured.NestedString(trainingPortal.Object, "status", "educates", "credentials", "admin", "username") - - if err != nil || !found { - return errors.New("unable to access credentials") - } - - password, found, err := unstructured.NestedString(trainingPortal.Object, "status", "educates", "credentials", "admin", "password") + manager := portal.NewPortalManager(dynamicClient) - if err != nil || !found { - return errors.New("unable to access credentials") - } + password, err := manager.GetTrainingPortalPassword(&config) - w := new(tabwriter.Writer) - w.Init(os.Stdout, 8, 8, 3, ' ', 0) - - defer w.Flush() - - fmt.Fprintf(w, "%s\t%s\n", "USERNAME", "PASSWORD") - fmt.Fprintf(w, "%s\t%s\n", username, password) - } else { - password, _, _ := unstructured.NestedString(trainingPortal.Object, "spec", "portal", "password") - - fmt.Println(password) + if err != nil { + return err } + fmt.Print(password) + return nil } @@ -110,7 +87,7 @@ func (p *ProjectInfo) NewClusterPortalPasswordCmd() *cobra.Command { &o.Portal, "portal", "p", - "educates-cli", + constants.DefaultPortalName, "name to be used for training portal and workshop name prefixes", ) diff --git a/client-programs/pkg/cmd/cluster_session_extend_cmd.go b/client-programs/pkg/cmd/cluster_session_extend_cmd.go index 921d6da84..84ae3aed7 100644 --- a/client-programs/pkg/cmd/cluster_session_extend_cmd.go +++ b/client-programs/pkg/cmd/cluster_session_extend_cmd.go @@ -3,10 +3,11 @@ package cmd import ( "fmt" - "github.com/pkg/errors" - "github.com/spf13/cobra" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/educatesrestapi" + "github.com/pkg/errors" + "github.com/spf13/cobra" ) type ClusterSessionExtendOptions struct { @@ -75,7 +76,7 @@ func (p *ProjectInfo) NewClusterSessionExtendCmd() *cobra.Command { &o.Portal, "portal", "p", - "educates-cli", + constants.DefaultPortalName, "name of the training portal", ) diff --git a/client-programs/pkg/cmd/cluster_session_list_cmd.go b/client-programs/pkg/cmd/cluster_session_list_cmd.go index d34831446..2c2cd51ea 100644 --- a/client-programs/pkg/cmd/cluster_session_list_cmd.go +++ b/client-programs/pkg/cmd/cluster_session_list_cmd.go @@ -6,9 +6,10 @@ import ( "os" "text/tabwriter" + "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/cluster" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -121,7 +122,7 @@ func (p *ProjectInfo) NewClusterSessionListCmd() *cobra.Command { &o.Portal, "portal", "p", - "educates-cli", + constants.DefaultPortalName, "name of the training portal", ) c.Flags().StringVarP( diff --git a/client-programs/pkg/cmd/cluster_session_status_cmd.go b/client-programs/pkg/cmd/cluster_session_status_cmd.go index 29d00cd5b..869bae1e0 100644 --- a/client-programs/pkg/cmd/cluster_session_status_cmd.go +++ b/client-programs/pkg/cmd/cluster_session_status_cmd.go @@ -3,10 +3,11 @@ package cmd import ( "fmt" - "github.com/pkg/errors" - "github.com/spf13/cobra" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/educatesrestapi" + "github.com/pkg/errors" + "github.com/spf13/cobra" ) type ClusterSessionStatusOptions struct { @@ -71,7 +72,7 @@ func (p *ProjectInfo) NewClusterSessionStatusCmd() *cobra.Command { &o.Portal, "portal", "p", - "educates-cli", + constants.DefaultPortalName, "name of the training portal", ) diff --git a/client-programs/pkg/cmd/cluster_session_terminate_cmd.go b/client-programs/pkg/cmd/cluster_session_terminate_cmd.go index cb07e6fe1..3fe57a1c6 100644 --- a/client-programs/pkg/cmd/cluster_session_terminate_cmd.go +++ b/client-programs/pkg/cmd/cluster_session_terminate_cmd.go @@ -3,10 +3,11 @@ package cmd import ( "fmt" - "github.com/pkg/errors" - "github.com/spf13/cobra" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/educatesrestapi" + "github.com/pkg/errors" + "github.com/spf13/cobra" ) type ClusterSessionTerminateOptions struct { @@ -72,7 +73,7 @@ func (p *ProjectInfo) NewClusterSessionTerminateCmd() *cobra.Command { &o.Portal, "portal", "p", - "educates-cli", + constants.DefaultPortalName, "name of the training portal", ) diff --git a/client-programs/pkg/cmd/cluster_workshop_delete_cmd.go b/client-programs/pkg/cmd/cluster_workshop_delete_cmd.go index 8bbe26e2c..f4d5a066f 100644 --- a/client-programs/pkg/cmd/cluster_workshop_delete_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_delete_cmd.go @@ -5,6 +5,7 @@ import ( yttcmd "carvel.dev/ytt/pkg/cmd/template" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/pkg/errors" "github.com/spf13/cobra" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -32,7 +33,7 @@ func (o *ClusterWorkshopDeleteOptions) Run() error { // Ensure have portal name. if o.Portal == "" { - o.Portal = "educates-cli" + o.Portal = constants.DefaultPortalName } if name == "" { @@ -129,7 +130,7 @@ func (p *ProjectInfo) NewClusterWorkshopDeleteCmd() *cobra.Command { &o.Portal, "portal", "p", - "educates-cli", + constants.DefaultPortalName, "name to be used for training portal and workshop name prefixes", ) @@ -223,7 +224,7 @@ func deleteWorkshopResource(client dynamic.Interface, name string, alias string, unstructured.SetNestedSlice(trainingPortal.Object, updatedWorkshops, "spec", "workshops") - _, err = trainingPortalClient.Update(context.TODO(), trainingPortal, metav1.UpdateOptions{FieldManager: "educates-cli"}) + _, err = trainingPortalClient.Update(context.TODO(), trainingPortal, metav1.UpdateOptions{FieldManager: constants.DefaultPortalName}) if err != nil { return errors.Wrapf(err, "unable to update training portal %q in cluster", portal) diff --git a/client-programs/pkg/cmd/cluster_workshop_deploy_cmd.go b/client-programs/pkg/cmd/cluster_workshop_deploy_cmd.go index f14e5f2b4..cb89f2383 100644 --- a/client-programs/pkg/cmd/cluster_workshop_deploy_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_deploy_cmd.go @@ -16,6 +16,7 @@ import ( yttcmd "carvel.dev/ytt/pkg/cmd/template" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/pkg/errors" "github.com/spf13/cobra" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -57,7 +58,7 @@ func (o *ClusterWorkshopDeployOptions) Run() error { // Ensure have portal name. if o.Portal == "" { - o.Portal = "educates-cli" + o.Portal = constants.DefaultPortalName } // If path not provided assume the current working directory. When loading @@ -158,7 +159,7 @@ func (p *ProjectInfo) NewClusterWorkshopDeployCmd() *cobra.Command { &o.Portal, "portal", "p", - "educates-cli", + constants.DefaultPortalName, "name to be used for training portal and workshop name prefixes", ) c.Flags().UintVar( @@ -549,10 +550,10 @@ func deployWorkshopResource(client dynamic.Interface, workshop *unstructured.Uns if trainingPortalExists { fmt.Printf("Updating existing training portal %q.\n", trainingPortal.GetName()) - _, err = trainingPortalClient.Update(context.TODO(), trainingPortal, metav1.UpdateOptions{FieldManager: "educates-cli"}) + _, err = trainingPortalClient.Update(context.TODO(), trainingPortal, metav1.UpdateOptions{FieldManager: constants.DefaultPortalName}) } else { fmt.Printf("Creating new training portal %q.\n", trainingPortal.GetName()) - _, err = trainingPortalClient.Create(context.TODO(), trainingPortal, metav1.CreateOptions{FieldManager: "educates-cli"}) + _, err = trainingPortalClient.Create(context.TODO(), trainingPortal, metav1.CreateOptions{FieldManager: constants.DefaultPortalName}) } if err != nil { diff --git a/client-programs/pkg/cmd/cluster_workshop_list_cmd.go b/client-programs/pkg/cmd/cluster_workshop_list_cmd.go index b2228615f..f4d9094f9 100644 --- a/client-programs/pkg/cmd/cluster_workshop_list_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_list_cmd.go @@ -7,6 +7,7 @@ import ( "text/tabwriter" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/pkg/errors" "github.com/spf13/cobra" k8serrors "k8s.io/apimachinery/pkg/api/errors" @@ -25,7 +26,7 @@ func (o *ClusterWorkshopsListOptions) Run() error { // Ensure have portal name. if o.Portal == "" { - o.Portal = "educates-cli" + o.Portal = constants.DefaultPortalName } clusterConfig, err := cluster.NewClusterConfigIfAvailable(o.Kubeconfig, o.Context) @@ -128,7 +129,7 @@ func (p *ProjectInfo) NewClusterWorkshopListCmd() *cobra.Command { &o.Portal, "portal", "p", - "educates-cli", + constants.DefaultPortalName, "name to be used for training portal and workshop name prefixes", ) diff --git a/client-programs/pkg/cmd/cluster_workshop_request_cmd.go b/client-programs/pkg/cmd/cluster_workshop_request_cmd.go index 38efb9d1a..94db7fb3e 100644 --- a/client-programs/pkg/cmd/cluster_workshop_request_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_request_cmd.go @@ -9,11 +9,12 @@ import ( "strings" yttcmd "carvel.dev/ytt/pkg/cmd/template" + "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/educatesrestapi" "github.com/joho/godotenv" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/cluster" - "github.com/educates/educates-training-platform/client-programs/pkg/educatesrestapi" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -89,7 +90,7 @@ func (o *ClusterWorkshopRequestOptions) Run() error { // Ensure have portal name. if o.Portal == "" { - o.Portal = "educates-cli" + o.Portal = constants.DefaultPortalName } if name == "" { @@ -178,7 +179,7 @@ func (p *ProjectInfo) NewClusterWorkshopRequestCmd() *cobra.Command { &o.Portal, "portal", "p", - "educates-cli", + constants.DefaultPortalName, "name to be used for training portal and workshop name prefixes", ) c.Flags().StringArrayVarP( diff --git a/client-programs/pkg/cmd/cluster_workshop_serve_cmd.go b/client-programs/pkg/cmd/cluster_workshop_serve_cmd.go index 5bfdf020c..927f92e60 100644 --- a/client-programs/pkg/cmd/cluster_workshop_serve_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_serve_cmd.go @@ -13,6 +13,7 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/renderer" "github.com/educates/educates-training-platform/client-programs/pkg/utils" ) @@ -139,7 +140,7 @@ func (o *ClusterWorkshopServeOptions) Run() error { // Ensure have portal name. if portal == "" { - portal = "educates-cli" + portal = constants.DefaultPortalName } // Calculate workshop root and name. @@ -273,7 +274,7 @@ func (p *ProjectInfo) NewClusterWorkshopServeCmd() *cobra.Command { &o.Portal, "portal", "p", - "educates-cli", + constants.DefaultPortalName, "name of the training portal to lookup the workshop", ) c.Flags().StringVar( diff --git a/client-programs/pkg/cmd/cluster_workshop_update_cmd.go b/client-programs/pkg/cmd/cluster_workshop_update_cmd.go index c3075ded8..dcf94706d 100644 --- a/client-programs/pkg/cmd/cluster_workshop_update_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_update_cmd.go @@ -12,6 +12,7 @@ import ( yttcmd "carvel.dev/ytt/pkg/cmd/template" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/workshops" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -43,7 +44,7 @@ func (o *ClusterWorkshopUpdateOptions) Run() error { // Ensure have portal name. if o.Portal == "" { - o.Portal = "educates-cli" + o.Portal = constants.DefaultPortalName } // If path not provided assume the current working directory. When loading @@ -129,7 +130,7 @@ func (p *ProjectInfo) NewClusterWorkshopUpdateCmd() *cobra.Command { &o.Portal, "portal", "p", - "educates-cli", + constants.DefaultPortalName, "name to be used for training portal and workshop name prefixes", ) @@ -340,7 +341,7 @@ var workshopResource = schema.GroupVersionResource{Group: "training.educates.dev func updateWorkshopResource(client dynamic.Interface, workshop *unstructured.Unstructured) error { workshopsClient := client.Resource(workshopResource) - // _, err := workshopsClient.Apply(context.TODO(), workshop.GetName(), workshop, metav1.ApplyOptions{FieldManager: "educates-cli", Force: true}) + // _, err := workshopsClient.Apply(context.TODO(), workshop.GetName(), workshop, metav1.ApplyOptions{FieldManager: constants.DefaultPortalName, Force: true}) workshopBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, workshop) @@ -348,7 +349,7 @@ func updateWorkshopResource(client dynamic.Interface, workshop *unstructured.Uns return errors.Wrapf(err, "unable to update workshop definition in cluster %q", workshop.GetName()) } - _, err = workshopsClient.Patch(context.TODO(), workshop.GetName(), types.ApplyPatchType, workshopBytes, metav1.ApplyOptions{FieldManager: "educates-cli", Force: true}.ToPatchOptions()) + _, err = workshopsClient.Patch(context.TODO(), workshop.GetName(), types.ApplyPatchType, workshopBytes, metav1.ApplyOptions{FieldManager: constants.DefaultPortalName, Force: true}.ToPatchOptions()) if err != nil { return errors.Wrapf(err, "unable to update workshop definition in cluster %q", workshop.GetName()) diff --git a/client-programs/pkg/cmd/docker_workshop_delete_cmd.go b/client-programs/pkg/cmd/docker_workshop_delete_cmd.go index 068072b9b..aa2c13e73 100644 --- a/client-programs/pkg/cmd/docker_workshop_delete_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_delete_cmd.go @@ -10,9 +10,10 @@ import ( yttcmd "carvel.dev/ytt/pkg/cmd/template" "github.com/docker/docker/client" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/utils" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) @@ -95,7 +96,7 @@ func (o *DockerWorkshopDeleteOptions) Run(cmd *cobra.Command) error { var workshop *unstructured.Unstructured - if workshop, err = loadWorkshopDefinition(o.Name, path, "educates-cli", o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + if workshop, err = loadWorkshopDefinition(o.Name, path, constants.DefaultPortalName, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { return err } diff --git a/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go b/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go index 06c974085..f02db0026 100644 --- a/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go @@ -19,6 +19,7 @@ import ( composeloader "github.com/compose-spec/compose-go/v2/loader" composetypes "github.com/compose-spec/compose-go/v2/types" "github.com/docker/docker/client" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -102,7 +103,7 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployOptions, var workshop *unstructured.Unstructured - if workshop, err = loadWorkshopDefinition("", o.Path, "educates-cli", o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + if workshop, err = loadWorkshopDefinition("", o.Path, constants.DefaultPortalName, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { return "", err } diff --git a/client-programs/pkg/cmd/docker_workshop_logs.go b/client-programs/pkg/cmd/docker_workshop_logs.go index 841d17932..811579830 100644 --- a/client-programs/pkg/cmd/docker_workshop_logs.go +++ b/client-programs/pkg/cmd/docker_workshop_logs.go @@ -4,6 +4,7 @@ import ( "os/exec" yttcmd "carvel.dev/ytt/pkg/cmd/template" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/pkg/errors" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -40,7 +41,7 @@ func (o *DockerWorkshopLogsOptions) Run(cmd *cobra.Command) error { var workshop *unstructured.Unstructured - if workshop, err = loadWorkshopDefinition(o.Name, path, "educates-cli", o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + if workshop, err = loadWorkshopDefinition(o.Name, path, constants.DefaultPortalName, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { return err } diff --git a/client-programs/pkg/cmd/docker_workshop_open_cmd.go b/client-programs/pkg/cmd/docker_workshop_open_cmd.go index 6bd78d2e4..c04a78d85 100644 --- a/client-programs/pkg/cmd/docker_workshop_open_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_open_cmd.go @@ -11,6 +11,7 @@ import ( yttcmd "carvel.dev/ytt/pkg/cmd/template" "github.com/docker/docker/client" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/pkg/errors" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -46,7 +47,7 @@ func (o *DockerWorkshopOpenOptions) Run() error { var workshop *unstructured.Unstructured - if workshop, err = loadWorkshopDefinition(o.Name, path, "educates-cli", o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + if workshop, err = loadWorkshopDefinition(o.Name, path, constants.DefaultPortalName, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { return err } diff --git a/client-programs/pkg/cmd/project_docs_open_cmd.go b/client-programs/pkg/cmd/project_docs_open_cmd.go index 6567516a7..45702d226 100644 --- a/client-programs/pkg/cmd/project_docs_open_cmd.go +++ b/client-programs/pkg/cmd/project_docs_open_cmd.go @@ -1,7 +1,7 @@ package cmd import ( - "github.com/educates/educates-training-platform/client-programs/pkg/config" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/spf13/cobra" ) @@ -15,7 +15,7 @@ func (p *ProjectInfo) NewProjectDocsOpenCmd() *cobra.Command { Use: "open", Short: "Open browser on project documentation", RunE: func(_ *cobra.Command, _ []string) error { - return utils.OpenBrowser(config.PROJECT_DOCS_URL) + return utils.OpenBrowser(constants.PROJECT_DOCS_URL) }, } diff --git a/client-programs/pkg/config/project.go b/client-programs/pkg/config/project.go deleted file mode 100644 index 992201109..000000000 --- a/client-programs/pkg/config/project.go +++ /dev/null @@ -1,5 +0,0 @@ -package config - -const ( - PROJECT_DOCS_URL = "https://docs.educates.dev/" -) diff --git a/client-programs/pkg/constants/project.go b/client-programs/pkg/constants/project.go new file mode 100644 index 000000000..c07797160 --- /dev/null +++ b/client-programs/pkg/constants/project.go @@ -0,0 +1,9 @@ +package constants + +const ( + PROJECT_DOCS_URL = "https://docs.educates.dev/" + + + DefaultPortalName = "educates-cli" + DefaultPortalCapacity = 5 +) diff --git a/client-programs/pkg/docker/workshop_manager.go b/client-programs/pkg/docker/workshop_manager.go index 2f7a2783f..2847a0ac7 100644 --- a/client-programs/pkg/docker/workshop_manager.go +++ b/client-programs/pkg/docker/workshop_manager.go @@ -23,6 +23,7 @@ import ( "github.com/docker/compose/v5/pkg/compose" "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/educates/educates-training-platform/client-programs/pkg/workshops" "github.com/pkg/errors" @@ -259,7 +260,7 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployConfig, s var workshop *unstructured.Unstructured - if workshop, err = workshops.LoadWorkshopDefinition("", o.Path, workshops.DefaultPortalName, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + if workshop, err = workshops.LoadWorkshopDefinition("", o.Path, constants.DefaultPortalName, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { return "", err } diff --git a/client-programs/pkg/portal/manager.go b/client-programs/pkg/portal/manager.go new file mode 100644 index 000000000..4af744a0c --- /dev/null +++ b/client-programs/pkg/portal/manager.go @@ -0,0 +1,308 @@ +package portal + +import ( + "context" + "fmt" + "net/url" + "strings" + "text/tabwriter" + + "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/pkg/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" +) + +var TrainingPortalResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "trainingportals"} + +type PortalManager struct { + client dynamic.Interface +} + +type TrainingPortalCreateConfig struct { + Portal string + Hostname string + Repository string + Capacity uint + Password string + IsPasswordSet bool + ThemeName string + CookieDomain string + Labels []string +} + +type TrainingPortalDeleteConfig struct { + Portal string +} + +type TrainingPortalListConfig struct { +} + +type TrainingPortalOpenConfig struct { + Portal string + Admin bool +} + +type TrainingPortalPasswordConfig struct { + Portal string + Admin bool +} + +func NewPortalManager(client dynamic.Interface) *PortalManager { + return &PortalManager{client: client} +} + +func (m *PortalManager) CreateTrainingPortal(cfg *TrainingPortalCreateConfig) error { + trainingPortalClient := m.client.Resource(TrainingPortalResource) + + _, err := trainingPortalClient.Get(context.TODO(), cfg.Portal, metav1.GetOptions{}) + + if err != nil { + if !k8serrors.IsNotFound(err) { + return errors.Wrap(err, "unable to query training portal") + } + } else { + return errors.New("training portal already exists") + } + + trainingPortal := &unstructured.Unstructured{} + + if !cfg.IsPasswordSet { + cfg.Password = RandomPassword(12) + } + + type LabelDetails struct { + Name string `json:"name"` + Value string `json:"value"` + } + + var labelOverrides []LabelDetails + + for _, value := range cfg.Labels { + parts := strings.SplitN(value, "=", 2) + labelOverrides = append(labelOverrides, LabelDetails{ + Name: parts[0], + Value: parts[1], + }) + } + + type RegistryDetails struct { + Host string `json:"host"` + Namespace string `json:"namespace"` + } + + registryHost := "" + registryNamespace := "" + + if cfg.Repository != "" { + parts := strings.SplitN(cfg.Repository, "/", 2) + + registryHost = parts[0] + + if len(parts) > 1 { + registryNamespace = parts[1] + } + + } + + trainingPortal.SetUnstructuredContent(map[string]interface{}{ + "apiVersion": "training.educates.dev/v1beta1", + "kind": "TrainingPortal", + "metadata": map[string]interface{}{ + "name": cfg.Portal, + }, + "spec": map[string]interface{}{ + "portal": map[string]interface{}{ + "password": cfg.Password, + "registration": struct { + Type string `json:"type"` + }{ + Type: "anonymous", + }, + "updates": struct { + Workshop bool `json:"workshop"` + }{ + Workshop: true, + }, + "sessions": struct { + Maximum int64 `json:"maximum"` + }{ + Maximum: int64(cfg.Capacity), + }, + "workshop": map[string]interface{}{ + "defaults": struct { + Reserved int `json:"reserved"` + Registry RegistryDetails `json:"registry"` + }{ + Reserved: 0, + Registry: RegistryDetails{ + Host: registryHost, + Namespace: registryNamespace, + }, + }, + }, + "ingress": struct { + Hostname string `json:"hostname"` + }{ + Hostname: cfg.Hostname, + }, + "theme": struct { + Name string `json:"name"` + }{ + Name: cfg.ThemeName, + }, + "cookies": struct { + Domain string `json:"domain"` + }{ + Domain: cfg.CookieDomain, + }, + "labels": labelOverrides, + }, + "workshops": []interface{}{}, + }, + }) + + _, err = trainingPortalClient.Create(context.TODO(), trainingPortal, metav1.CreateOptions{FieldManager: constants.DefaultPortalName}) + + if err != nil { + return errors.Wrapf(err, "unable to create training portal %q in cluster", cfg.Portal) + } + + return nil +} + +func (m *PortalManager) DeleteTrainingPortal(cfg *TrainingPortalDeleteConfig) error { + trainingPortalClient := m.client.Resource(TrainingPortalResource) + + _, err := trainingPortalClient.Get(context.TODO(), cfg.Portal, metav1.GetOptions{}) + + if k8serrors.IsNotFound(err) { + return errors.New("no portal found") + } + + err = trainingPortalClient.Delete(context.TODO(), cfg.Portal, metav1.DeleteOptions{}) + + if err != nil { + return errors.Wrap(err, "unable to delete portal") + } + + return nil +} + + +func (m *PortalManager) ListTrainingPortals(cfg *TrainingPortalListConfig) (string, error) { + trainingPortalClient := m.client.Resource(TrainingPortalResource) + + trainingPortals, err := trainingPortalClient.List(context.TODO(), metav1.ListOptions{}) + + if k8serrors.IsNotFound(err) { + fmt.Println("No portals found.") + return "", nil + } + + var buf strings.Builder + w := new(tabwriter.Writer) + + // Initialize tabwriter to write to 'buf' instead of 'os.Stdout' + w.Init(&buf, 8, 8, 3, ' ', 0) + + fmt.Fprintf(w, "%s\t%s\t%s\n", "NAME", "CAPACITY", "URL") + + for _, item := range trainingPortals.Items { + name := item.GetName() + + sessionsMaximum, propertyExists, err := unstructured.NestedInt64(item.Object, "spec", "portal", "sessions", "maximum") + + var capacity string + + if err == nil && propertyExists { + capacity = fmt.Sprintf("%d", sessionsMaximum) + } + + url, _, _ := unstructured.NestedString(item.Object, "status", "educates", "url") + + fmt.Fprintf(w, "%s\t%s\t%s\n", name, capacity, url) + } + + // Important: Flush ensures all data is written from tabwriter to the builder + w.Flush() + + return buf.String(), nil +} + +func (m *PortalManager) GetTrainingPortalBrowserUrl(cfg *TrainingPortalOpenConfig) (string, error) { + trainingPortalClient := m.client.Resource(TrainingPortalResource) + + trainingPortal, err := trainingPortalClient.Get(context.TODO(), cfg.Portal, metav1.GetOptions{}) + + if k8serrors.IsNotFound(err) { + return "", errors.New("no workshops deployed") + } + + targetUrl, found, _ := unstructured.NestedString(trainingPortal.Object, "status", "educates", "url") + + if !found { + return "", errors.New("workshops not available") + } + + if cfg.Admin { + targetUrl = targetUrl + "/admin" + } else { + password, _, _ := unstructured.NestedString(trainingPortal.Object, "spec", "portal", "password") + + if password != "" { + values := url.Values{} + values.Add("redirect_url", "/") + values.Add("password", password) + + targetUrl = fmt.Sprintf("%s/workshops/access/?%s", targetUrl, values.Encode()) + } + } + + return targetUrl, nil +} + +func (m *PortalManager) GetTrainingPortalPassword(cfg *TrainingPortalPasswordConfig) (string, error) { + trainingPortalClient := m.client.Resource(TrainingPortalResource) + + trainingPortal, err := trainingPortalClient.Get(context.TODO(), cfg.Portal, metav1.GetOptions{}) + + if k8serrors.IsNotFound(err) { + return "", errors.New("no workshops deployed") + } + + if cfg.Admin { + username, found, err := unstructured.NestedString(trainingPortal.Object, "status", "educates", "credentials", "admin", "username") + + if err != nil || !found { + return "", errors.New("unable to access credentials") + } + + password, found, err := unstructured.NestedString(trainingPortal.Object, "status", "educates", "credentials", "admin", "password") + + if err != nil || !found { + return "", errors.New("unable to access credentials") + } + + var buf strings.Builder + w := new(tabwriter.Writer) + + // Initialize tabwriter to write to 'buf' instead of 'os.Stdout' + w.Init(&buf, 8, 8, 3, ' ', 0) + + fmt.Fprintf(w, "%s\t%s\n", "USERNAME", "PASSWORD") + fmt.Fprintf(w, "%s\t%s\n", username, password) + + // Important: Flush ensures all data is written from tabwriter to the builder + w.Flush() + + return buf.String(), nil + } else { + password, _, _ := unstructured.NestedString(trainingPortal.Object, "spec", "portal", "password") + + return password, nil + } +} diff --git a/client-programs/pkg/portal/password.go b/client-programs/pkg/portal/password.go new file mode 100644 index 000000000..601322c8c --- /dev/null +++ b/client-programs/pkg/portal/password.go @@ -0,0 +1,20 @@ +package portal + +import ( + "hash/maphash" + "math/rand" + "strings" +) + +func RandomPassword(length int) string { + rand.New(rand.NewSource(int64(new(maphash.Hash).Sum64()))) + + chars := []rune("!#%+23456789:=?@ABCDEFGHJKLMNPRSTUVWXYZabcdefghijkmnopqrstuvwxyz") + + var b strings.Builder + + for i := 0; i < length; i++ { + b.WriteRune(chars[rand.Intn(len(chars))]) + } + return b.String() +} diff --git a/client-programs/pkg/secrets/secrets.go b/client-programs/pkg/secrets/secrets.go index 568091f6d..f0b8bad81 100644 --- a/client-programs/pkg/secrets/secrets.go +++ b/client-programs/pkg/secrets/secrets.go @@ -6,8 +6,9 @@ import ( "path" "strings" - "github.com/pkg/errors" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/utils" + "github.com/pkg/errors" apiv1 "k8s.io/api/core/v1" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -169,7 +170,7 @@ func SyncLocalCachedSecretsToCluster(client *kubernetes.Clientset) error { patch = applycorev1.Secret(name, secretsNS).WithType(secretObj.Type).WithData(secretObj.Data) } - _, err = secretsClient.Apply(context.TODO(), patch, metav1.ApplyOptions{FieldManager: "educates-cli", Force: true}) + _, err = secretsClient.Apply(context.TODO(), patch, metav1.ApplyOptions{FieldManager: constants.DefaultPortalName, Force: true}) if err != nil { return errors.Wrapf(err, "unable to update secret in cluster %q", name) diff --git a/client-programs/pkg/workshops/constants.go b/client-programs/pkg/workshops/constants.go deleted file mode 100644 index 00d2d05e9..000000000 --- a/client-programs/pkg/workshops/constants.go +++ /dev/null @@ -1,3 +0,0 @@ -package workshops - -const DefaultPortalName = "educates-cli" diff --git a/client-programs/pkg/workshops/definition.go b/client-programs/pkg/workshops/definition.go index 611dc3534..ab667774e 100644 --- a/client-programs/pkg/workshops/definition.go +++ b/client-programs/pkg/workshops/definition.go @@ -16,6 +16,7 @@ import ( yttcmdui "carvel.dev/ytt/pkg/cmd/ui" "carvel.dev/ytt/pkg/files" "carvel.dev/ytt/pkg/yamlmeta" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/pkg/errors" "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -190,7 +191,7 @@ func UpdateWorkshopResource(client dynamic.Interface, workshop *unstructured.Uns return errors.Wrapf(err, "unable to update workshop definition in cluster %q", workshop.GetName()) } - _, err = workshopsClient.Patch(context.TODO(), workshop.GetName(), types.ApplyPatchType, workshopBytes, metav1.ApplyOptions{FieldManager: DefaultPortalName, Force: true}.ToPatchOptions()) + _, err = workshopsClient.Patch(context.TODO(), workshop.GetName(), types.ApplyPatchType, workshopBytes, metav1.ApplyOptions{FieldManager: constants.DefaultPortalName, Force: true}.ToPatchOptions()) if err != nil { return errors.Wrapf(err, "unable to update workshop definition in cluster %q", workshop.GetName()) From fa736206cd6cb6d9adcbc54f29be4a0437dbd905 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Mon, 19 Jan 2026 19:16:30 +0100 Subject: [PATCH 07/24] Refactored cluster_portal_xxx command --- .../pkg/cmd/cluster_portal_password_cmd.go | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/client-programs/pkg/cmd/cluster_portal_password_cmd.go b/client-programs/pkg/cmd/cluster_portal_password_cmd.go index 19ff5788d..9081b863e 100644 --- a/client-programs/pkg/cmd/cluster_portal_password_cmd.go +++ b/client-programs/pkg/cmd/cluster_portal_password_cmd.go @@ -16,6 +16,20 @@ type ClusterPortalPasswordOptions struct { Portal string } +const clusterPortalPasswordExample = ` +# View accesspassword for TrainingPortal in Educates cluster with default name +educates cluster portal password + +# View access password for TrainingPortal in Educates cluster with specific name +educates cluster portal password --portal=my-portal + +# View admin password for TrainingPortal in Educates cluster with default name +educates cluster portal password --admin + +# View access password for given TrainingPortal in given Educates cluster +educates cluster portal password --portal=my-portal --kubeconfig ~/.kube/config --context=my-context --admin +` + func (o *ClusterPortalPasswordOptions) Run() error { var err error @@ -63,6 +77,7 @@ func (p *ProjectInfo) NewClusterPortalPasswordCmd() *cobra.Command { Use: "password", Short: "View portal credentials in Kubernetes", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: clusterPortalPasswordExample, } c.Flags().StringVar( From 518c3cac4edab60fd4a438439b1ed8b15fc6cd8e Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Mon, 19 Jan 2026 20:14:47 +0100 Subject: [PATCH 08/24] Refactored cluster_session_xx command --- .../pkg/cmd/cluster_portal_list_cmd.go | 2 +- .../pkg/cmd/cluster_portal_password_cmd.go | 2 +- .../pkg/cmd/cluster_session_extend_cmd.go | 47 +++-- .../pkg/cmd/cluster_session_list_cmd.go | 79 ++------ .../pkg/cmd/cluster_session_status_cmd.go | 47 +++-- .../pkg/cmd/cluster_session_terminate_cmd.go | 47 +++-- .../educates/resources/sessions/manager.go | 183 ++++++++++++++++++ client-programs/pkg/portal/manager.go | 9 +- 8 files changed, 293 insertions(+), 123 deletions(-) create mode 100644 client-programs/pkg/educates/resources/sessions/manager.go diff --git a/client-programs/pkg/cmd/cluster_portal_list_cmd.go b/client-programs/pkg/cmd/cluster_portal_list_cmd.go index 99bcea36d..bd6319a93 100644 --- a/client-programs/pkg/cmd/cluster_portal_list_cmd.go +++ b/client-programs/pkg/cmd/cluster_portal_list_cmd.go @@ -44,7 +44,7 @@ func (o *ClusterPortalListOptions) Run() error { return err } - fmt.Print(list) + fmt.Println(list) return nil } diff --git a/client-programs/pkg/cmd/cluster_portal_password_cmd.go b/client-programs/pkg/cmd/cluster_portal_password_cmd.go index 9081b863e..2523a7db5 100644 --- a/client-programs/pkg/cmd/cluster_portal_password_cmd.go +++ b/client-programs/pkg/cmd/cluster_portal_password_cmd.go @@ -64,7 +64,7 @@ func (o *ClusterPortalPasswordOptions) Run() error { return err } - fmt.Print(password) + fmt.Println(password) return nil } diff --git a/client-programs/pkg/cmd/cluster_session_extend_cmd.go b/client-programs/pkg/cmd/cluster_session_extend_cmd.go index 84ae3aed7..5f60b729f 100644 --- a/client-programs/pkg/cmd/cluster_session_extend_cmd.go +++ b/client-programs/pkg/cmd/cluster_session_extend_cmd.go @@ -5,8 +5,8 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" - "github.com/educates/educates-training-platform/client-programs/pkg/educatesrestapi" - "github.com/pkg/errors" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/sessions" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/spf13/cobra" ) @@ -16,6 +16,17 @@ type ClusterSessionExtendOptions struct { Name string } +const clusterSessionExtendExample = ` +# Extend duration of session "my-session" in Kubernetes +educates cluster session extend my-session SESSION_NAME + +# Extend duration of session "my-session" in Kubernetes using a specific portal +educates cluster session extend my-session SESSION_NAME --portal=my-portal + +# Extend duration of session "my-session" in Kubernetes using a specific portal and context +educates cluster session extend my-session SESSION_NAME --portal=my-portal --kubeconfig ~/.kube/config --context=my-context +` + func (o *ClusterSessionExtendOptions) Run() error { var err error @@ -25,27 +36,17 @@ func (o *ClusterSessionExtendOptions) Run() error { return err } - catalogApiRequester := educatesrestapi.NewWorkshopsCatalogRequester( - clusterConfig, - o.Portal, - ) - logout, err := catalogApiRequester.Login() - defer logout() - if err != nil { - return errors.Wrap(err, "failed to login to training portal") - } - - details, err := catalogApiRequester.ExtendWorkshopSession(o.Name) + manager := sessions.NewSessionManager() + result, err := manager.ExtendSession(sessions.ExtendSessionConfig{ + ClusterConfig: clusterConfig, + Portal: o.Portal, + Name: o.Name, + }) if err != nil { return err } - fmt.Println("Started:", details.Started) - fmt.Println("Expires:", details.Expires) - fmt.Println("Expiring:", details.Expiring) - fmt.Println("Countdown:", details.Countdown) - fmt.Println("Extendable:", details.Extendable) - fmt.Println("Status:", details.Status) + fmt.Println(result) return nil } @@ -54,10 +55,16 @@ func (p *ProjectInfo) NewClusterSessionExtendCmd() *cobra.Command { var o ClusterSessionExtendOptions var c = &cobra.Command{ - Args: cobra.ExactArgs(1), + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return utils.CmdError(cmd, "session name is required", "NAME") + } + return nil + }, Use: "extend NAME", Short: "Extend duration of session in Kubernetes", RunE: func(_ *cobra.Command, args []string) error { o.Name = args[0]; return o.Run() }, + Example: clusterSessionExtendExample, } c.Flags().StringVar( diff --git a/client-programs/pkg/cmd/cluster_session_list_cmd.go b/client-programs/pkg/cmd/cluster_session_list_cmd.go index 2c2cd51ea..99ce16911 100644 --- a/client-programs/pkg/cmd/cluster_session_list_cmd.go +++ b/client-programs/pkg/cmd/cluster_session_list_cmd.go @@ -1,19 +1,13 @@ package cmd import ( - "context" "fmt" - "os" - "text/tabwriter" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/sessions" "github.com/pkg/errors" "github.com/spf13/cobra" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" ) type ClusterSessionListOptions struct { @@ -22,7 +16,16 @@ type ClusterSessionListOptions struct { Environment string } -var workshopSessionResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshopsessions"} +const clusterSessionListExample = ` +# List active Educates sessions in default Educates portal and cluster +educates cluster session list + +# List active Educates sessions using a specific portal +educates cluster session list --portal=my-portal + +# List active Educates sessions in Kubernetes using a specific portal and context +educates cluster session list --portal=my-portal --kubeconfig ~/.kube/config --context=my-context +` func (o *ClusterSessionListOptions) Run() error { var err error @@ -39,59 +42,18 @@ func (o *ClusterSessionListOptions) Run() error { return errors.Wrapf(err, "unable to create Kubernetes client") } - workshopSessionClient := dynamicClient.Resource(workshopSessionResource) - - workshopSessions, err := workshopSessionClient.List(context.TODO(), metav1.ListOptions{}) - - if k8serrors.IsNotFound(err) { - fmt.Println("No sessions found.") - return nil - } - - var sessions []unstructured.Unstructured + manager := sessions.NewSessionManager() - for _, item := range workshopSessions.Items { - labels := item.GetLabels() - - portal, ok := labels["training.educates.dev/portal.name"] - - if ok && portal == o.Portal { - if o.Environment != "" { - environment, ok := labels["training.educates.dev/environment.name"] - - if ok && environment == o.Environment { - sessions = append(sessions, item) - } - } else { - sessions = append(sessions, item) - } - - } - } - - if len(sessions) == 0 { - fmt.Println("No sessions found.") - return nil + list, err := manager.ListSessions(sessions.ListSessionsConfig{ + Client: dynamicClient, + Portal: o.Portal, + Environment: o.Environment, + }) + if err != nil { + return err } - w := new(tabwriter.Writer) - w.Init(os.Stdout, 8, 8, 3, ' ', 0) - - defer w.Flush() - - fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "NAME", "PORTAL", "ENVIRONMENT", "STATUS") - - for _, item := range sessions { - name := item.GetName() - labels := item.GetLabels() - - portal := labels["training.educates.dev/portal.name"] - environment := labels["training.educates.dev/environment.name"] - - status, _, _ := unstructured.NestedString(item.Object, "status", "educates", "phase") - - fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", name, portal, environment, status) - } + fmt.Println(list) return nil } @@ -104,6 +66,7 @@ func (p *ProjectInfo) NewClusterSessionListCmd() *cobra.Command { Use: "list", Short: "List active sessions in Kubernetes", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: clusterSessionListExample, } c.Flags().StringVar( diff --git a/client-programs/pkg/cmd/cluster_session_status_cmd.go b/client-programs/pkg/cmd/cluster_session_status_cmd.go index 869bae1e0..eeccd9365 100644 --- a/client-programs/pkg/cmd/cluster_session_status_cmd.go +++ b/client-programs/pkg/cmd/cluster_session_status_cmd.go @@ -5,8 +5,8 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" - "github.com/educates/educates-training-platform/client-programs/pkg/educatesrestapi" - "github.com/pkg/errors" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/sessions" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/spf13/cobra" ) @@ -16,32 +16,33 @@ type ClusterSessionStatusOptions struct { Name string } +const clusterSessionStatusExample = ` +# Get status of Educates session "my-session" in default Educates portal +educates cluster session status my-session + +# Get status of Educates session "my-session" using a specific portal +educates cluster session status my-session --portal=my-portal + +# Get status of Educates session "my-session" using a specific portal and context +educates cluster session status my-session --portal=my-portal --kubeconfig ~/.kube/config --context=my-context +` + func (o *ClusterSessionStatusOptions) Run() error { var err error clusterConfig := cluster.NewClusterConfig(o.Kubeconfig, o.Context) - catalogApiRequester := educatesrestapi.NewWorkshopsCatalogRequester( - clusterConfig, - o.Portal, - ) - logout, err := catalogApiRequester.Login() - defer logout() - if err != nil { - return errors.Wrap(err, "failed to login to training portal") - } - - details, err := catalogApiRequester.GetWorkshopSession(o.Name) + manager := sessions.NewSessionManager() + result, err := manager.SessionStatus(sessions.SessionStatusConfig{ + ClusterConfig: clusterConfig, + Portal: o.Portal, + Name: o.Name, + }) if err != nil { return err } - fmt.Println("Started:", details.Started) - fmt.Println("Expires:", details.Expires) - fmt.Println("Expiring:", details.Expiring) - fmt.Println("Countdown:", details.Countdown) - fmt.Println("Extendable:", details.Extendable) - fmt.Println("Status:", details.Status) + fmt.Println(result) return nil } @@ -50,10 +51,16 @@ func (p *ProjectInfo) NewClusterSessionStatusCmd() *cobra.Command { var o ClusterSessionStatusOptions var c = &cobra.Command{ - Args: cobra.ExactArgs(1), + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return utils.CmdError(cmd, "session name is required", "NAME") + } + return nil + }, Use: "status NAME", Short: "Output status of session in Kubernetes", RunE: func(_ *cobra.Command, args []string) error { o.Name = args[0]; return o.Run() }, + Example: clusterSessionStatusExample, } c.Flags().StringVar( diff --git a/client-programs/pkg/cmd/cluster_session_terminate_cmd.go b/client-programs/pkg/cmd/cluster_session_terminate_cmd.go index 3fe57a1c6..c3950e0e0 100644 --- a/client-programs/pkg/cmd/cluster_session_terminate_cmd.go +++ b/client-programs/pkg/cmd/cluster_session_terminate_cmd.go @@ -5,8 +5,8 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" - "github.com/educates/educates-training-platform/client-programs/pkg/educatesrestapi" - "github.com/pkg/errors" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/sessions" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/spf13/cobra" ) @@ -16,32 +16,33 @@ type ClusterSessionTerminateOptions struct { Name string } +const clusterSessionTerminateExample = ` +# Terminate running Educatessession "my-session" in default Educates portal +educates cluster session terminate my-session + +# Terminate running Educates session "my-session" using a specific portal +educates cluster session terminate my-session --portal=my-portal + +# Terminate running Educates session "my-session" using a specific portal and context +educates cluster session terminate my-session --portal=my-portal --kubeconfig ~/.kube/config --context=my-context +` + func (o *ClusterSessionTerminateOptions) Run() error { var err error clusterConfig := cluster.NewClusterConfig(o.Kubeconfig, o.Context) - catalogApiRequester := educatesrestapi.NewWorkshopsCatalogRequester( - clusterConfig, - o.Portal, - ) - logout, err := catalogApiRequester.Login() - defer logout() - if err != nil { - return errors.Wrap(err, "failed to login to training portal") - } - - details, err := catalogApiRequester.TerminateWorkshopSession(o.Name) + manager := sessions.NewSessionManager() + result, err := manager.TerminateSession(sessions.TerminateSessionConfig{ + ClusterConfig: clusterConfig, + Portal: o.Portal, + Name: o.Name, + }) if err != nil { return err } - fmt.Println("Started:", details.Started) - fmt.Println("Expires:", details.Expires) - fmt.Println("Expiring:", details.Expiring) - fmt.Println("Countdown:", details.Countdown) - fmt.Println("Extendable:", details.Extendable) - fmt.Println("Status:", details.Status) + fmt.Println(result) return nil } @@ -50,11 +51,17 @@ func (p *ProjectInfo) NewClusterSessionTerminateCmd() *cobra.Command { var o ClusterSessionTerminateOptions var c = &cobra.Command{ - Args: cobra.ExactArgs(1), + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return utils.CmdError(cmd, "session name is required", "NAME") + } + return nil + }, Use: "delete NAME", Aliases: []string{"terminate"}, Short: "Terminate running session in Kubernetes", RunE: func(_ *cobra.Command, args []string) error { o.Name = args[0]; return o.Run() }, + Example: clusterSessionTerminateExample, } c.Flags().StringVar( diff --git a/client-programs/pkg/educates/resources/sessions/manager.go b/client-programs/pkg/educates/resources/sessions/manager.go new file mode 100644 index 000000000..c4280979a --- /dev/null +++ b/client-programs/pkg/educates/resources/sessions/manager.go @@ -0,0 +1,183 @@ +package sessions + +import ( + "context" + "fmt" + "strings" + "text/tabwriter" + + "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/educatesrestapi" + "github.com/pkg/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/client-go/dynamic" +) + +var workshopSessionResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshopsessions"} + + +type SessionManager struct { +} + +func NewSessionManager() *SessionManager { + return &SessionManager{} +} + + +type ListSessionsConfig struct { + Client dynamic.Interface + Portal string + Environment string +} + +type ExtendSessionConfig struct { + ClusterConfig *cluster.ClusterConfig + Portal string + Name string +} + +type SessionStatusConfig struct { + ClusterConfig *cluster.ClusterConfig + Portal string + Name string +} + +type TerminateSessionConfig struct { + ClusterConfig *cluster.ClusterConfig + Portal string + Name string +} + +func (m *SessionManager) ListSessions(cfg ListSessionsConfig) (string, error) { + workshopSessionClient := cfg.Client.Resource(workshopSessionResource) + + workshopSessions, err := workshopSessionClient.List(context.TODO(), metav1.ListOptions{}) + + if k8serrors.IsNotFound(err) { + return "No sessions found.", nil + } + + var sessions []unstructured.Unstructured + + for _, item := range workshopSessions.Items { + labels := item.GetLabels() + + portal, ok := labels["training.educates.dev/portal.name"] + + if ok && portal == cfg.Portal { + if cfg.Environment != "" { + environment, ok := labels["training.educates.dev/environment.name"] + + if ok && environment == cfg.Environment { + sessions = append(sessions, item) + } + } else { + sessions = append(sessions, item) + } + + } + } + + if len(sessions) == 0 { + return "No sessions found.", nil + } + + var buf strings.Builder + w := new(tabwriter.Writer) + w.Init(&buf, 8, 8, 3, ' ', 0) + + fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "NAME", "PORTAL", "ENVIRONMENT", "STATUS") + + for i, item := range sessions { + name := item.GetName() + labels := item.GetLabels() + + portal := labels["training.educates.dev/portal.name"] + environment := labels["training.educates.dev/environment.name"] + + status, _, _ := unstructured.NestedString(item.Object, "status", "educates", "phase") + + fmt.Fprintf(w, "%s\t%s\t%s\t%s", name, portal, environment, status) + if i < len(sessions) - 1 { + fmt.Fprintf(w, "\n") + } + } + + w.Flush() + + return buf.String(), nil +} + +func (m *SessionManager) ExtendSession(cfg ExtendSessionConfig) (string, error) { + catalogApiRequester := educatesrestapi.NewWorkshopsCatalogRequester( + cfg.ClusterConfig, + cfg.Portal, + ) + logout, err := catalogApiRequester.Login() + defer logout() + if err != nil { + return "", errors.Wrap(err, "failed to login to training portal") + } + + details, err := catalogApiRequester.ExtendWorkshopSession(cfg.Name) + if err != nil { + return "", err + } + + return printStatus(details), nil +} + +func (m *SessionManager) SessionStatus(cfg SessionStatusConfig) (string, error) { + catalogApiRequester := educatesrestapi.NewWorkshopsCatalogRequester( + cfg.ClusterConfig, + cfg.Portal, + ) + logout, err := catalogApiRequester.Login() + defer logout() + if err != nil { + return "", errors.Wrap(err, "failed to login to training portal") + } + + details, err := catalogApiRequester.GetWorkshopSession(cfg.Name) + if err != nil { + return "", err + } + + return printStatus(details), nil +} + +func (m *SessionManager) TerminateSession(cfg TerminateSessionConfig) (string, error) { + catalogApiRequester := educatesrestapi.NewWorkshopsCatalogRequester( + cfg.ClusterConfig, + cfg.Portal, + ) + logout, err := catalogApiRequester.Login() + defer logout() + if err != nil { + return "", errors.Wrap(err, "failed to login to training portal") + } + + details, err := catalogApiRequester.TerminateWorkshopSession(cfg.Name) + if err != nil { + return "", err + } + + return printStatus(details), nil + +} + +func printStatus(details *educatesrestapi.WorkshopSessionDetails) string { + var buf strings.Builder + + fmt.Fprintf(&buf, "Started: %s\n", details.Started) + fmt.Fprintf(&buf, "Expires: %s\n", details.Expires) + fmt.Fprintf(&buf, "Expiring: %t\n", details.Expiring) + fmt.Fprintf(&buf, "Countdown: %d\n", details.Countdown) + fmt.Fprintf(&buf, "Extendable: %t\n", details.Extendable) + fmt.Fprintf(&buf, "Status: %s", details.Status) + + return buf.String() +} diff --git a/client-programs/pkg/portal/manager.go b/client-programs/pkg/portal/manager.go index 4af744a0c..286c9f524 100644 --- a/client-programs/pkg/portal/manager.go +++ b/client-programs/pkg/portal/manager.go @@ -211,7 +211,7 @@ func (m *PortalManager) ListTrainingPortals(cfg *TrainingPortalListConfig) (stri fmt.Fprintf(w, "%s\t%s\t%s\n", "NAME", "CAPACITY", "URL") - for _, item := range trainingPortals.Items { + for i, item := range trainingPortals.Items { name := item.GetName() sessionsMaximum, propertyExists, err := unstructured.NestedInt64(item.Object, "spec", "portal", "sessions", "maximum") @@ -224,7 +224,10 @@ func (m *PortalManager) ListTrainingPortals(cfg *TrainingPortalListConfig) (stri url, _, _ := unstructured.NestedString(item.Object, "status", "educates", "url") - fmt.Fprintf(w, "%s\t%s\t%s\n", name, capacity, url) + fmt.Fprintf(w, "%s\t%s\t%s", name, capacity, url) + if i < len(trainingPortals.Items) - 1 { + fmt.Fprintf(w, "\n") + } } // Important: Flush ensures all data is written from tabwriter to the builder @@ -294,7 +297,7 @@ func (m *PortalManager) GetTrainingPortalPassword(cfg *TrainingPortalPasswordCon w.Init(&buf, 8, 8, 3, ' ', 0) fmt.Fprintf(w, "%s\t%s\n", "USERNAME", "PASSWORD") - fmt.Fprintf(w, "%s\t%s\n", username, password) + fmt.Fprintf(w, "%s\t%s", username, password) // Important: Flush ensures all data is written from tabwriter to the builder w.Flush() From 41aa9679867b6af966e92cd50502d40e1bd2b2a5 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Tue, 20 Jan 2026 13:33:00 +0100 Subject: [PATCH 09/24] Refactored cluster_workshop_xx commands --- .../pkg/cmd/cluster_workshop_delete_cmd.go | 87 +-- .../pkg/cmd/cluster_workshop_deploy_cmd.go | 436 ++--------- .../pkg/cmd/cluster_workshop_list_cmd.go | 81 +- .../pkg/cmd/cluster_workshop_request_cmd.go | 48 +- .../pkg/cmd/cluster_workshop_serve_cmd.go | 131 ++-- .../pkg/cmd/cluster_workshop_update_cmd.go | 214 +----- .../pkg/cmd/docker_workshop_delete_cmd.go | 11 +- .../pkg/cmd/docker_workshop_deploy_cmd.go | 11 +- .../pkg/cmd/docker_workshop_logs.go | 11 +- .../pkg/cmd/docker_workshop_open_cmd.go | 11 +- .../educates/resources/workshops/manager.go | 693 ++++++++++++++++++ client-programs/pkg/portal/manager.go | 3 +- client-programs/pkg/renderer/hugo.go | 98 ++- .../pkg/{portal => utils}/password.go | 2 +- 14 files changed, 1037 insertions(+), 800 deletions(-) create mode 100644 client-programs/pkg/educates/resources/workshops/manager.go rename client-programs/pkg/{portal => utils}/password.go (96%) diff --git a/client-programs/pkg/cmd/cluster_workshop_delete_cmd.go b/client-programs/pkg/cmd/cluster_workshop_delete_cmd.go index f4d5a066f..9daf5f8ce 100644 --- a/client-programs/pkg/cmd/cluster_workshop_delete_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_delete_cmd.go @@ -1,19 +1,30 @@ package cmd import ( - "context" - yttcmd "carvel.dev/ytt/pkg/cmd/template" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" "github.com/pkg/errors" "github.com/spf13/cobra" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/client-go/dynamic" ) +const ( + clusterWorkshopDeleteExample = ` + # Delete Educates workshop from cluster in current workshop directory and using default workshop file + educates cluster workshop delete + + # Delete Educates workshop from cluster from specific portal + educates cluster workshop delete --portal my-portal + + # Delete Educates workshop from cluster defined with custom path and workshop file + educates cluster workshop delete --path ./workshop --workshop-file custom-workshop.yaml + + # Delete Educates workshop from alternate cluster + educates cluster workshop delete --kubeconfig ~/.kube/config --context=my-context +` +) type ClusterWorkshopDeleteOptions struct { KubeconfigOptions Name string @@ -53,7 +64,16 @@ func (o *ClusterWorkshopDeleteOptions) Run() error { var workshop *unstructured.Unstructured - if workshop, err = loadWorkshopDefinition(o.Name, path, o.Portal, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + definitionConfig := workshops.WorkshopDefinitionConfig{ + Name: o.Name, + Path: path, + Portal: o.Portal, + WorkshopFile: o.WorkshopFile, + WorkshopVersion: o.WorkshopVersion, + DataValueFlags: o.DataValuesFlags, + } + + if workshop, err = workshops.LoadWorkshopDefinition(&definitionConfig); err != nil { return err } @@ -72,9 +92,15 @@ func (o *ClusterWorkshopDeleteOptions) Run() error { return errors.Wrapf(err, "unable to create Kubernetes client") } - // Delete the deployed workshop from the Kubernetes cluster. + manager := workshops.NewWorkshopManager(dynamicClient) - err = deleteWorkshopResource(dynamicClient, name, o.Alias, o.Portal) + // Delete the deployed workshop from the Kubernetes cluster. + deleteConfig := workshops.DeleteWorkshopResourceConfig{ + Name: name, + Alias: o.Alias, + Portal: o.Portal, + } + err = manager.DeleteWorkshopResource(&deleteConfig) if err != nil { return err @@ -91,6 +117,7 @@ func (p *ProjectInfo) NewClusterWorkshopDeleteCmd() *cobra.Command { Use: "delete", Short: "Delete workshop from Kubernetes", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: clusterWorkshopDeleteExample, } c.Flags().StringVarP( @@ -188,47 +215,3 @@ func (p *ProjectInfo) NewClusterWorkshopDeleteCmd() *cobra.Command { return c } - -func deleteWorkshopResource(client dynamic.Interface, name string, alias string, portal string) error { - trainingPortalClient := client.Resource(trainingPortalResource) - - trainingPortal, err := trainingPortalClient.Get(context.TODO(), portal, metav1.GetOptions{}) - - if k8serrors.IsNotFound(err) { - return nil - } - - workshops, _, err := unstructured.NestedSlice(trainingPortal.Object, "spec", "workshops") - - if err != nil { - return errors.Wrap(err, "unable to retrieve workshops from training portal") - } - - var found = false - - var updatedWorkshops []interface{} - - for _, item := range workshops { - object := item.(map[string]interface{}) - - if object["name"] != name || object["alias"] != alias { - updatedWorkshops = append(updatedWorkshops, object) - } else { - found = true - } - } - - if !found { - return nil - } - - unstructured.SetNestedSlice(trainingPortal.Object, updatedWorkshops, "spec", "workshops") - - _, err = trainingPortalClient.Update(context.TODO(), trainingPortal, metav1.UpdateOptions{FieldManager: constants.DefaultPortalName}) - - if err != nil { - return errors.Wrapf(err, "unable to update training portal %q in cluster", portal) - } - - return nil -} diff --git a/client-programs/pkg/cmd/cluster_workshop_deploy_cmd.go b/client-programs/pkg/cmd/cluster_workshop_deploy_cmd.go index cb89f2383..e728ee812 100644 --- a/client-programs/pkg/cmd/cluster_workshop_deploy_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_deploy_cmd.go @@ -1,29 +1,28 @@ package cmd import ( - "context" - "encoding/json" "fmt" - "hash/maphash" - "io" - "math/rand" - "net/http" - "net/url" - "os/exec" - "runtime" - "strings" - "time" yttcmd "carvel.dev/ytt/pkg/cmd/template" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" "github.com/pkg/errors" "github.com/spf13/cobra" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/client-go/dynamic" +) + +const ( + clusterWorkshopDeployExample = ` + # Deploy Educates workshop to cluster in current workshop directory and using default workshop file + educates cluster workshop deploy + + # Deploy Educates workshop to cluster with custom workshop name and alias and custom workshop settings + educates cluster workshop deploy --name my-workshop --alias my-workshop -initial 10 -reserved 5 -expires 1h -overtime 10m -deadline 2h -orphaned 10m -overdue 10s + + # Deploy Educates workshop to cluster with custom workshop file + educates cluster workshop deploy --path ./workshop --workshop-file custom-workshop.yaml +` ) type ClusterWorkshopDeployOptions struct { @@ -75,7 +74,16 @@ func (o *ClusterWorkshopDeployOptions) Run() error { var workshop *unstructured.Unstructured - if workshop, err = loadWorkshopDefinition(o.Name, path, o.Portal, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + loadConfig := workshops.WorkshopDefinitionConfig{ + Name: o.Name, + Path: path, + Portal: o.Portal, + WorkshopFile: o.WorkshopFile, + WorkshopVersion: o.WorkshopVersion, + DataValueFlags: o.DataValuesFlags, + } + + if workshop, err = workshops.LoadWorkshopDefinition(&loadConfig); err != nil { return err } @@ -91,9 +99,14 @@ func (o *ClusterWorkshopDeployOptions) Run() error { return errors.Wrapf(err, "unable to create Kubernetes client") } + manager := workshops.NewWorkshopManager(dynamicClient) + // Update the workshop resource in the Kubernetes cluster. + updateConfig := workshops.UpdateWorkshopResourceConfig{ + Workshop: workshop, + } - err = updateWorkshopResource(dynamicClient, workshop) + err = manager.UpdateWorkshopResource(&updateConfig) if err != nil { return err @@ -103,7 +116,33 @@ func (o *ClusterWorkshopDeployOptions) Run() error { // Update the training portal, creating it if necessary. - err = deployWorkshopResource(dynamicClient, workshop, o.Alias, o.Portal, o.Capacity, o.Reserved, o.Initial, o.Expires, o.Overtime, o.Deadline, o.Orphaned, o.Overdue, o.Refresh, o.Repository, o.Environ, o.Labels, o.OpenBrowser) + deployConfig := workshops.DeployWorkshopConfig{ + Workshop: workshop, + Alias: o.Alias, + Portal: o.Portal, + Capacity: o.Capacity, + Reserved: o.Reserved, + Initial: o.Initial, + Expires: o.Expires, + Overtime: o.Overtime, + Deadline: o.Deadline, + Orphaned: o.Orphaned, + Overdue: o.Overdue, + Refresh: o.Refresh, + Registry: o.Repository, + Environ: o.Environ, + Labels: o.Labels, + OpenBrowser: o.OpenBrowser, + } + err = manager.DeployWorkshopResource(&deployConfig) + + // TODO: Move open browser logic to separate function and extract logic here + // if o.OpenBrowser { + // err = manager.OpenBrowser(&deployConfig) + // if err != nil { + // return err + // } + // } if err != nil { return err @@ -120,6 +159,7 @@ func (p *ProjectInfo) NewClusterWorkshopDeployCmd() *cobra.Command { Use: "deploy", Short: "Deploy workshop to Kubernetes", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: clusterWorkshopDeployExample, } c.Flags().StringVarP( @@ -299,363 +339,3 @@ func (p *ProjectInfo) NewClusterWorkshopDeployCmd() *cobra.Command { return c } - -var trainingPortalResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "trainingportals"} - -func deployWorkshopResource(client dynamic.Interface, workshop *unstructured.Unstructured, alias string, portal string, capacity uint, reserved uint, initial uint, expires string, overtime string, deadline string, orphaned string, overdue string, refresh string, registry string, environ []string, labels []string, openBrowser bool) error { - trainingPortalClient := client.Resource(trainingPortalResource) - - trainingPortal, err := trainingPortalClient.Get(context.TODO(), portal, metav1.GetOptions{}) - - var trainingPortalExists = true - - if k8serrors.IsNotFound(err) { - trainingPortalExists = false - - trainingPortal = &unstructured.Unstructured{} - - trainingPortal.SetUnstructuredContent(map[string]interface{}{ - "apiVersion": "training.educates.dev/v1beta1", - "kind": "TrainingPortal", - "metadata": map[string]interface{}{ - "name": portal, - }, - "spec": map[string]interface{}{ - "portal": map[string]interface{}{ - "password": randomPassword(12), - "registration": struct { - Type string `json:"type"` - }{ - Type: "anonymous", - }, - "updates": struct { - Workshop bool `json:"workshop"` - }{ - Workshop: true, - }, - "sessions": struct { - Maximum int64 `json:"maximum"` - }{ - Maximum: 5, - }, - "workshop": map[string]interface{}{ - "defaults": struct { - Reserved int `json:"reserved"` - }{ - Reserved: 0, - }, - }, - }, - "workshops": []interface{}{}, - }, - }) - } - - workshops, _, err := unstructured.NestedSlice(trainingPortal.Object, "spec", "workshops") - - if err != nil { - return errors.Wrap(err, "unable to retrieve workshops from training portal") - } - - var updatedWorkshops []interface{} - - if expires == "" { - duration, propertyExists, err := unstructured.NestedString(workshop.Object, "spec", "duration") - - if err != nil || !propertyExists { - expires = "60m" - } else { - expires = duration - } - } - - type EnvironDetails struct { - Name string `json:"name"` - Value string `json:"value"` - } - - var environVariables []EnvironDetails - - for _, value := range environ { - parts := strings.SplitN(value, "=", 2) - environVariables = append(environVariables, EnvironDetails{ - Name: parts[0], - Value: parts[1], - }) - } - - type LabelDetails struct { - Name string `json:"name"` - Value string `json:"value"` - } - - var labelOverrides []LabelDetails - - for _, value := range labels { - parts := strings.SplitN(value, "=", 2) - labelOverrides = append(labelOverrides, LabelDetails{ - Name: parts[0], - Value: parts[1], - }) - } - - var foundWorkshop = false - - for _, item := range workshops { - object := item.(map[string]interface{}) - - updatedWorkshops = append(updatedWorkshops, object) - - if object["name"] == workshop.GetName() && object["alias"] == alias { - foundWorkshop = true - - object["reserved"] = int64(reserved) - object["initial"] = int64(initial) - - if capacity != 0 { - object["capacity"] = int64(capacity) - } else { - delete(object, "capacity") - } - - if expires != "" { - object["expires"] = expires - } else { - delete(object, "expires") - } - - if overtime != "" { - object["overtime"] = overtime - } else { - delete(object, "overtime") - } - - if deadline != "" { - object["deadline"] = deadline - } else { - delete(object, "deadline") - } - - if orphaned != "" { - object["orphaned"] = orphaned - } else { - delete(object, "orphaned") - } - - if overdue != "" { - object["overdue"] = overdue - } else { - delete(object, "overdue") - } - - if refresh != "" { - object["refresh"] = refresh - } else { - delete(object, "refresh") - } - - var tmpEnvironVariables []interface{} - - for _, item := range environVariables { - tmpEnvironVariables = append(tmpEnvironVariables, map[string]interface{}{ - "name": item.Name, - "value": item.Value, - }) - } - - object["env"] = tmpEnvironVariables - - var tmpLabelOverrides []interface{} - - for _, item := range labelOverrides { - tmpLabelOverrides = append(tmpLabelOverrides, map[string]interface{}{ - "name": item.Name, - "value": item.Value, - }) - } - - object["labels"] = tmpLabelOverrides - } - } - - type RegistryDetails struct { - Host string `json:"host"` - Namespace string `json:"namespace,omitempty"` - } - - type WorkshopDetails struct { - Name string `json:"name"` - Alias string `json:"alias"` - Capacity int64 `json:"capacity,omitempty"` - Initial int64 `json:"initial"` - Reserved int64 `json:"reserved"` - Expires string `json:"expires,omitempty"` - Overtime string `json:"overtime,omitempty"` - Deadline string `json:"deadline,omitempty"` - Orphaned string `json:"orphaned,omitempty"` - Overdue string `json:"overdue,omitempty"` - Refresh string `json:"refresh,omitempty"` - Registry *RegistryDetails `json:"registry,omitempty"` - Environ []EnvironDetails `json:"env"` - Labels []LabelDetails `json:"labels"` - } - - if !foundWorkshop { - workshopDetails := WorkshopDetails{ - Name: workshop.GetName(), - Alias: alias, - Initial: int64(initial), - Reserved: int64(reserved), - Expires: expires, - Overtime: overtime, - Deadline: deadline, - Orphaned: orphaned, - Overdue: overdue, - Refresh: refresh, - Environ: environVariables, - Labels: labelOverrides, - } - - if capacity != 0 { - workshopDetails.Capacity = int64(capacity) - } - - if registry != "" { - parts := strings.SplitN(registry, "/", 2) - - host := parts[0] - var namespace string - - if len(parts) > 1 { - namespace = parts[1] - } - - registryDetails := RegistryDetails{ - Host: host, - Namespace: namespace, - } - - workshopDetails.Registry = ®istryDetails - } - - var workshopDetailsMap map[string]interface{} - - data, _ := json.Marshal(workshopDetails) - json.Unmarshal(data, &workshopDetailsMap) - - updatedWorkshops = append(updatedWorkshops, workshopDetailsMap) - } - - unstructured.SetNestedSlice(trainingPortal.Object, updatedWorkshops, "spec", "workshops") - - if trainingPortalExists { - fmt.Printf("Updating existing training portal %q.\n", trainingPortal.GetName()) - _, err = trainingPortalClient.Update(context.TODO(), trainingPortal, metav1.UpdateOptions{FieldManager: constants.DefaultPortalName}) - } else { - fmt.Printf("Creating new training portal %q.\n", trainingPortal.GetName()) - _, err = trainingPortalClient.Create(context.TODO(), trainingPortal, metav1.CreateOptions{FieldManager: constants.DefaultPortalName}) - } - - if err != nil { - return errors.Wrapf(err, "unable to update training portal %q in cluster", portal) - } - - fmt.Print("Workshop added to training portal.\n") - - if openBrowser { - // Need to refetch training portal because if was just created the URL - // for access may not have been set yet. - - var targetUrl string - - fmt.Print("Checking training portal is ready.\n") - - spinner := func(iteration int) string { - spinners := `|/-\` - return string(spinners[iteration%len(spinners)]) - } - - for i := 1; i < 60; i++ { - fmt.Printf("\r[%s] Waiting...", spinner(i)) - - time.Sleep(time.Second) - - trainingPortal, err = trainingPortalClient.Get(context.TODO(), portal, metav1.GetOptions{}) - - if err != nil { - return errors.Wrapf(err, "unable to fetch training portal %q in cluster", portal) - } - - var found bool - - targetUrl, found, _ = unstructured.NestedString(trainingPortal.Object, "status", "educates", "url") - - if found { - break - } - } - - rootUrl := targetUrl - - password, _, _ := unstructured.NestedString(trainingPortal.Object, "spec", "portal", "password") - - if password != "" { - values := url.Values{} - values.Add("redirect_url", "/") - values.Add("password", password) - - targetUrl = fmt.Sprintf("%s/workshops/access/?%s", targetUrl, values.Encode()) - } - - for i := 1; i < 300; i++ { - fmt.Printf("\r[%s] Waiting...", spinner(i)) - - time.Sleep(time.Second) - - resp, err := http.Get(rootUrl) - - if err != nil || resp.StatusCode == 503 { - continue - } - - defer resp.Body.Close() - io.ReadAll(resp.Body) - - break - } - - fmt.Print("\r \r") - - fmt.Printf("Opening training portal %s.\n", targetUrl) - - switch runtime.GOOS { - case "linux": - err = exec.Command("xdg-open", targetUrl).Start() - case "windows": - err = exec.Command("rundll32", "url.dll,FileProtocolHandler", targetUrl).Start() - case "darwin": - err = exec.Command("open", targetUrl).Start() - default: - err = fmt.Errorf("unsupported platform") - } - - if err != nil { - return errors.Wrap(err, "unable to open web browser") - } - } - - return nil -} - -func randomPassword(length int) string { - rand.New(rand.NewSource(int64(new(maphash.Hash).Sum64()))) - - chars := []rune("!#%+23456789:=?@ABCDEFGHJKLMNPRSTUVWXYZabcdefghijkmnopqrstuvwxyz") - - var b strings.Builder - - for i := 0; i < length; i++ { - b.WriteRune(chars[rand.Intn(len(chars))]) - } - return b.String() -} diff --git a/client-programs/pkg/cmd/cluster_workshop_list_cmd.go b/client-programs/pkg/cmd/cluster_workshop_list_cmd.go index f4d9094f9..4300f123a 100644 --- a/client-programs/pkg/cmd/cluster_workshop_list_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_list_cmd.go @@ -1,18 +1,26 @@ package cmd import ( - "context" "fmt" - "os" - "text/tabwriter" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" "github.com/pkg/errors" "github.com/spf13/cobra" - k8serrors "k8s.io/apimachinery/pkg/api/errors" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" +) + +const ( + clusterWorkshopListExample = ` + # List Educates workshops deployed to Kubernetes cluster + educates cluster workshop list + + # List Educates workshops deployed to Kubernetes cluster with a specific portal + educates cluster workshop list --portal=my-portal + + # List Educates workshops deployed to alternateKubernetes cluster + educates cluster workshop list --kubeconfig ~/.kube/config --context=my-context +` ) type ClusterWorkshopsListOptions struct { @@ -40,65 +48,17 @@ func (o *ClusterWorkshopsListOptions) Run() error { return errors.Wrapf(err, "unable to create Kubernetes client") } - trainingPortalClient := dynamicClient.Resource(trainingPortalResource) - - trainingPortal, err := trainingPortalClient.Get(context.TODO(), o.Portal, metav1.GetOptions{}) - - if k8serrors.IsNotFound(err) { - fmt.Println("No workshops found.") - return nil - } - - sessionsMaximum, sessionsMaximumExists, _ := unstructured.NestedInt64(trainingPortal.Object, "spec", "portal", "sessions", "maximum") + manager := workshops.NewWorkshopManager(dynamicClient) - workshops, _, err := unstructured.NestedSlice(trainingPortal.Object, "spec", "workshops") + list, err := manager.ListWorkshopResources(&workshops.ListWorkshopResourcesConfig{ + Portal: o.Portal, + }) if err != nil { - return errors.Wrap(err, "unable to retrieve workshops from training portal") - } - - if len(workshops) == 0 { - fmt.Println("No workshops found.") - return nil + return err } - w := new(tabwriter.Writer) - w.Init(os.Stdout, 8, 8, 3, ' ', 0) - - defer w.Flush() - - fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "NAME", "ALIAS", "CAPACITY", "SOURCE") - - workshopsClient := dynamicClient.Resource(workshopResource) - - for _, item := range workshops { - object := item.(map[string]interface{}) - name := object["name"].(string) - - var capacityField string - - capacity, capacityExists := object["capacity"] - - if capacityExists { - capacityField = fmt.Sprintf("%d", capacity) - } else if sessionsMaximumExists { - capacityField = fmt.Sprintf("%d", sessionsMaximum) - } - - workshop, err := workshopsClient.Get(context.TODO(), name, metav1.GetOptions{}) - - source := "" - - if err == nil { - annotations := workshop.GetAnnotations() - - if val, ok := annotations["training.educates.dev/source"]; ok { - source = val - } - } - - fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", object["name"], object["alias"], capacityField, source) - } + fmt.Println(list) return nil } @@ -111,6 +71,7 @@ func (p *ProjectInfo) NewClusterWorkshopListCmd() *cobra.Command { Use: "list", Short: "List workshops deployed to Kubernetes", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: clusterWorkshopListExample, } c.Flags().StringVar( diff --git a/client-programs/pkg/cmd/cluster_workshop_request_cmd.go b/client-programs/pkg/cmd/cluster_workshop_request_cmd.go index 94db7fb3e..360db579f 100644 --- a/client-programs/pkg/cmd/cluster_workshop_request_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_request_cmd.go @@ -4,14 +4,14 @@ import ( "context" "fmt" "os" - "os/exec" - "runtime" "strings" yttcmd "carvel.dev/ytt/pkg/cmd/template" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" "github.com/educates/educates-training-platform/client-programs/pkg/educatesrestapi" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/joho/godotenv" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -20,6 +20,19 @@ import ( "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) +const ( + clusterWorkshopRequestExample = ` + # Request Educates workshop in cluster in current workshop directory and using default workshop file + educates cluster workshop request + + # Request Educates workshop in cluster with custom workshop file + educates cluster workshop request --path ./workshop --workshop-file custom-workshop.yaml + + # Request Educates workshop but don't open the browser + educates cluster workshop request --no-browser +` +) + type ClusterWorkshopRequestOptions struct { KubeconfigOptions Name string @@ -110,7 +123,16 @@ func (o *ClusterWorkshopRequestOptions) Run() error { var workshop *unstructured.Unstructured - if workshop, err = loadWorkshopDefinition(o.Name, path, o.Portal, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + definitionConfig := workshops.WorkshopDefinitionConfig{ + Name: o.Name, + Path: path, + Portal: o.Portal, + WorkshopFile: o.WorkshopFile, + WorkshopVersion: o.WorkshopVersion, + DataValueFlags: o.DataValuesFlags, + } + + if workshop, err = workshops.LoadWorkshopDefinition(&definitionConfig); err != nil { return err } @@ -147,6 +169,7 @@ func (p *ProjectInfo) NewClusterWorkshopRequestCmd() *cobra.Command { Use: "request", Short: "Request workshop in Kubernetes", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: clusterWorkshopRequestExample, } c.Flags().StringVarP( @@ -300,7 +323,7 @@ func ensurePortalHasWorkshop(clusterConfig *cluster.ClusterConfig, name string, return errors.Wrapf(err, "unable to create Kubernetes client") } - trainingPortalClient := client.Resource(trainingPortalResource) + trainingPortalClient := client.Resource(workshops.TrainingPortalResource) trainingPortal, err := trainingPortalClient.Get(context.TODO(), portal, metav1.GetOptions{}) @@ -379,20 +402,5 @@ func requestWorkshop(clusterConfig *cluster.ClusterConfig, workshopName string, fmt.Printf("Opening workshop URL %s.\n", workshopUrl) - switch runtime.GOOS { - case "linux": - err = exec.Command("xdg-open", workshopUrl).Start() - case "windows": - err = exec.Command("rundll32", "url.dll,FileProtocolHandler", workshopUrl).Start() - case "darwin": - err = exec.Command("open", workshopUrl).Start() - default: - err = fmt.Errorf("unsupported platform") - } - - if err != nil { - return errors.Wrap(err, "unable to open web browser on workshop") - } - - return nil + return utils.OpenBrowser(workshopUrl) } diff --git a/client-programs/pkg/cmd/cluster_workshop_serve_cmd.go b/client-programs/pkg/cmd/cluster_workshop_serve_cmd.go index 927f92e60..71d26c04d 100644 --- a/client-programs/pkg/cmd/cluster_workshop_serve_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_serve_cmd.go @@ -2,9 +2,7 @@ package cmd import ( "fmt" - "io/ioutil" "os" - "path" "path/filepath" yttcmd "carvel.dev/ytt/pkg/cmd/template" @@ -14,10 +12,24 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" "github.com/educates/educates-training-platform/client-programs/pkg/renderer" - "github.com/educates/educates-training-platform/client-programs/pkg/utils" ) +const ( + clusterWorkshopServeExample = ` + # Serve Educates workshop from local system in current workshop directory and using default workshop file + educates cluster workshop serve + + # Serve Educates workshop from local system with custom workshop file + educates cluster workshop serve --path ./workshop --workshop-file custom-workshop.yaml + + # Serve Educates workshop from local system with custom workshop file and activate live reload mode + educates cluster workshop serve --path ./workshop --workshop-file custom-workshop.yaml --patch-workshop +` +) + +// TODO: Move this function somewhere and see how to update the code that does this same functionality in many workshop related commands func calculateWorkshopRoot(path string) (string, error) { var err error @@ -42,22 +54,6 @@ func calculateWorkshopRoot(path string) (string, error) { return path, nil } -// func calculateWorkshopName(name string, path string, portal string, workshopFile string, workshopVersion string, dataValuesFlags yttcmd.DataValuesFlags) (string, error) { -// var err error - -// if name == "" { -// var workshop *unstructured.Unstructured - -// if workshop, err = loadWorkshopDefinition(name, path, portal, workshopFile, workshopVersion, dataValuesFlags); err != nil { -// return "", err -// } - -// name = workshop.GetName() -// } - -// return name, nil -// } - type ClusterWorkshopServeOptions struct { KubeconfigOptions Name string @@ -78,51 +74,6 @@ type ClusterWorkshopServeOptions struct { DataValuesFlags yttcmd.DataValuesFlags } -func generateAccessToken(refresh bool) (string, error) { - configFileDir := utils.GetEducatesHomeDir() - accessTokenFile := path.Join(configFileDir, "live-reload-token.dat") - - err := os.MkdirAll(configFileDir, os.ModePerm) - - if err != nil { - return "", errors.Wrapf(err, "unable to create config directory") - } - - var accessToken string - - if refresh { - accessToken = randomPassword(32) - - err := ioutil.WriteFile(accessTokenFile, []byte(accessToken), 0644) - - if err != nil { - return "", err - } - } else { - if _, err := os.Stat(accessTokenFile); err == nil { - accessTokenBytes, err := ioutil.ReadFile(accessTokenFile) - - if err != nil { - return "", err - } - - accessToken = string(accessTokenBytes) - } else if os.IsNotExist(err) { - accessToken = randomPassword(32) - - err = ioutil.WriteFile(accessTokenFile, []byte(accessToken), 0644) - - if err != nil { - return "", err - } - } else { - return "", err - } - } - - return accessToken, nil -} - func (o *ClusterWorkshopServeOptions) Run() error { var err error @@ -138,20 +89,26 @@ func (o *ClusterWorkshopServeOptions) Run() error { } // Ensure have portal name. - if portal == "" { portal = constants.DefaultPortalName } // Calculate workshop root and name. - if path, err = calculateWorkshopRoot(path); err != nil { return err } var workshop *unstructured.Unstructured - if workshop, err = loadWorkshopDefinition(name, path, portal, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + definitionConfig := workshops.WorkshopDefinitionConfig{ + Name: name, + Path: path, + Portal: portal, + WorkshopFile: o.WorkshopFile, + WorkshopVersion: o.WorkshopVersion, + DataValueFlags: o.DataValuesFlags, + } + if workshop, err = workshops.LoadWorkshopDefinition(&definitionConfig); err != nil { return err } @@ -160,9 +117,8 @@ func (o *ClusterWorkshopServeOptions) Run() error { } // If going to patch hosted workshop, ensure we have an access token. - if o.PatchWorkshop && token == "" { - token, err = generateAccessToken(o.RefreshToken) + token, err = renderer.GenerateAccessToken(o.RefreshToken) if err != nil { return errors.Wrap(err, "error generating access token") @@ -170,7 +126,6 @@ func (o *ClusterWorkshopServeOptions) Run() error { } // If patching hosted workshop create an apply the updated configuration. - if o.PatchWorkshop { patchedWorkshop := workshop.DeepCopyObject().(*unstructured.Unstructured) @@ -202,9 +157,11 @@ func (o *ClusterWorkshopServeOptions) Run() error { return errors.Wrapf(err, "unable to create Kubernetes client") } + manager := workshops.NewWorkshopManager(dynamicClient) // Update the workshop resource in the Kubernetes cluster. - - err = updateWorkshopResource(dynamicClient, patchedWorkshop) + err = manager.UpdateWorkshopResource(&workshops.UpdateWorkshopResourceConfig{ + Workshop: patchedWorkshop, + }) if err != nil { return err @@ -215,23 +172,38 @@ func (o *ClusterWorkshopServeOptions) Run() error { var cleanupFunc = func() { // Do our best to revert workshop configuration and ignore errors. - clusterConfig := cluster.NewClusterConfig(o.Kubeconfig, o.Context) dynamicClient, err := clusterConfig.GetDynamicClient() if err == nil { // Update the workshop resource in the Kubernetes cluster. - - updateWorkshopResource(dynamicClient, workshop) - - fmt.Printf("Restored workshop %q.\n", workshop.GetName()) + manager := workshops.NewWorkshopManager(dynamicClient) + err = manager.UpdateWorkshopResource(&workshops.UpdateWorkshopResourceConfig{ + Workshop: workshop, + }) + if err != nil { + fmt.Printf("Error restoring workshop %q: %v\n", workshop.GetName(), err) + } else { + fmt.Printf("Restored workshop %q.\n", workshop.GetName()) + } } } // Run the proxy server and Hugo server. - - return renderer.RunHugoServer(path, o.Kubeconfig, o.Context, name, portal, o.LocalHost, o.LocalPort, o.HugoPort, token, o.Files, cleanupFunc) + return renderer.RunHugoServer(&renderer.RunHugoServerConfig{ + WorkshopRoot: path, + Kubeconfig: o.Kubeconfig, + Context: o.Context, + Workshop: name, + Portal: portal, + LocalHost: o.LocalHost, + LocalPort: o.LocalPort, + HugoPort: o.HugoPort, + Token: token, + Files: o.Files, + CleanupFunc: cleanupFunc, + }) } func (p *ProjectInfo) NewClusterWorkshopServeCmd() *cobra.Command { @@ -242,6 +214,7 @@ func (p *ProjectInfo) NewClusterWorkshopServeCmd() *cobra.Command { Use: "serve", Short: "Serve workshop from local system", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: clusterWorkshopServeExample, } c.Flags().StringVarP( diff --git a/client-programs/pkg/cmd/cluster_workshop_update_cmd.go b/client-programs/pkg/cmd/cluster_workshop_update_cmd.go index dcf94706d..15795fdbd 100644 --- a/client-programs/pkg/cmd/cluster_workshop_update_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_update_cmd.go @@ -1,29 +1,25 @@ package cmd import ( - "context" - "crypto/sha1" "fmt" - "io" - "net/http" - "net/url" - "os" - "path/filepath" yttcmd "carvel.dev/ytt/pkg/cmd/template" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" - "github.com/educates/educates-training-platform/client-programs/pkg/workshops" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" "github.com/pkg/errors" "github.com/spf13/cobra" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/dynamic" - "k8s.io/kubectl/pkg/scheme" +) + +const ( + clusterWorkshopUpdateExample = ` + # Update Educates workshop in Kubernetes cluster in current workshop directory and using default workshop file + educates cluster workshop update + + # Update Educates workshop in Kubernetes cluster with custom workshop file + educates cluster workshop update --path ./workshop --workshop-file custom-workshop.yaml +` ) type ClusterWorkshopUpdateOptions struct { @@ -61,7 +57,16 @@ func (o *ClusterWorkshopUpdateOptions) Run() error { var workshop *unstructured.Unstructured - if workshop, err = loadWorkshopDefinition(o.Name, path, o.Portal, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + definitionConfig := workshops.WorkshopDefinitionConfig{ + Name: o.Name, + Path: o.Path, + Portal: o.Portal, + WorkshopFile: o.WorkshopFile, + WorkshopVersion: o.WorkshopVersion, + DataValueFlags: o.DataValuesFlags, + } + + if workshop, err = workshops.LoadWorkshopDefinition(&definitionConfig); err != nil { return err } @@ -77,9 +82,14 @@ func (o *ClusterWorkshopUpdateOptions) Run() error { return errors.Wrapf(err, "unable to create Kubernetes client") } + manager := workshops.NewWorkshopManager(dynamicClient) + // Update the workshop resource in the Kubernetes cluster. + updateConfig := workshops.UpdateWorkshopResourceConfig{ + Workshop: workshop, + } - err = updateWorkshopResource(dynamicClient, workshop) + err = manager.UpdateWorkshopResource(&updateConfig) if err != nil { return err @@ -98,6 +108,7 @@ func (p *ProjectInfo) NewClusterWorkshopUpdateCmd() *cobra.Command { Use: "update", Short: "Update workshop in Kubernetes", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: clusterWorkshopUpdateExample, } c.Flags().StringVarP( @@ -188,172 +199,3 @@ func (p *ProjectInfo) NewClusterWorkshopUpdateCmd() *cobra.Command { return c } - -func loadWorkshopDefinition(name string, path string, portal string, workshopFile string, workshopVersion string, dataValueFlags yttcmd.DataValuesFlags) (*unstructured.Unstructured, error) { - // Parse the workshop location so we can determine if it is a local file - // or accessible using a HTTP/HTTPS URL. - - var urlInfo *url.URL - var err error - - if urlInfo, err = url.Parse(path); err != nil { - return nil, errors.Wrap(err, "unable to parse workshop location") - } - - // Check if file system path first (not HTTP/HTTPS) and if so normalize - // the path. If it the path references a directory, then extend the path - // so we look for the workshop file within that directory. - - if urlInfo.Scheme != "http" && urlInfo.Scheme != "https" { - path = filepath.Clean(path) - - if path, err = filepath.Abs(path); err != nil { - return nil, errors.Wrap(err, "couldn't convert workshop location to absolute path") - } - - if !filepath.IsAbs(workshopFile) { - fileInfo, err := os.Stat(path) - - if err != nil { - return nil, errors.Wrap(err, "couldn't test if workshop location is a directory") - } - - if fileInfo.IsDir() { - path = filepath.Join(path, workshopFile) - } - } else { - path = workshopFile - } - } - - // Read in the workshop definition as raw data ready for parsing. - - var workshopData []byte - - if urlInfo.Scheme != "http" && urlInfo.Scheme != "https" { - if workshopData, err = os.ReadFile(path); err != nil { - return nil, errors.Wrap(err, "couldn't read workshop definition data file") - } - } else { - var client http.Client - - resp, err := client.Get(path) - - if err != nil { - return nil, errors.Wrap(err, "couldn't download workshop definition from host") - } - - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, errors.New("failed to download workshop definition from host") - } - - workshopData, err = io.ReadAll(resp.Body) - - if err != nil { - return nil, errors.Wrap(err, "failed to read workshop definition from host") - } - } - - // Process the workshop YAML data in case it contains ytt templating. - - if workshopData, err = workshops.ProcessWorkshopDefinition(workshopData, dataValueFlags); err != nil { - return nil, errors.Wrap(err, "unable to process workshop definition as template") - } - - // Parse the workshop definition. - - decoder := serializer.NewCodecFactory(scheme.Scheme).UniversalDecoder() - - workshop := &unstructured.Unstructured{} - - err = runtime.DecodeInto(decoder, workshopData, workshop) - - if err != nil { - return nil, errors.Wrap(err, "couldn't parse workshop definition") - } - - // Verify the type of resource definition. - - if workshop.GetAPIVersion() != "training.educates.dev/v1beta1" || workshop.GetKind() != "Workshop" { - return nil, errors.New("invalid type for workshop definition") - } - - // Add annotations recording details about original workshop location. - - annotations := workshop.GetAnnotations() - - if annotations == nil { - annotations = map[string]string{} - } - - annotations["training.educates.dev/workshop"] = workshop.GetName() - - if urlInfo.Scheme != "http" && urlInfo.Scheme != "https" { - annotations["training.educates.dev/source"] = fmt.Sprintf("file://%s", path) - } else { - annotations["training.educates.dev/source"] = path - } - - workshop.SetAnnotations(annotations) - - // Update the name for the workshop such that it incorporates a hash of - // the workshop location. - - if name == "" { - name = generateWorkshopName(path, workshop, portal) - } - - workshop.SetName(name) - - // Insert workshop version property if not specified. - - _, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") - - if !found && workshopVersion != "latest" { - unstructured.SetNestedField(workshop.Object, workshopVersion, "spec", "version") - } - - // Remove the publish section as will not be accurate after publising. - - unstructured.RemoveNestedField(workshop.Object, "spec", "publish") - - return workshop, nil -} - -func generateWorkshopName(path string, workshop *unstructured.Unstructured, portal string) string { - name := workshop.GetName() - - h := sha1.New() - - io.WriteString(h, path) - - hv := fmt.Sprintf("%x", h.Sum(nil)) - - name = fmt.Sprintf("%s--%s-%s", portal, name, hv[len(hv)-7:]) - - return name -} - -var workshopResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshops"} - -func updateWorkshopResource(client dynamic.Interface, workshop *unstructured.Unstructured) error { - workshopsClient := client.Resource(workshopResource) - - // _, err := workshopsClient.Apply(context.TODO(), workshop.GetName(), workshop, metav1.ApplyOptions{FieldManager: constants.DefaultPortalName, Force: true}) - - workshopBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, workshop) - - if err != nil { - return errors.Wrapf(err, "unable to update workshop definition in cluster %q", workshop.GetName()) - } - - _, err = workshopsClient.Patch(context.TODO(), workshop.GetName(), types.ApplyPatchType, workshopBytes, metav1.ApplyOptions{FieldManager: constants.DefaultPortalName, Force: true}.ToPatchOptions()) - - if err != nil { - return errors.Wrapf(err, "unable to update workshop definition in cluster %q", workshop.GetName()) - } - - return nil -} diff --git a/client-programs/pkg/cmd/docker_workshop_delete_cmd.go b/client-programs/pkg/cmd/docker_workshop_delete_cmd.go index aa2c13e73..e524f4e4e 100644 --- a/client-programs/pkg/cmd/docker_workshop_delete_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_delete_cmd.go @@ -11,6 +11,7 @@ import ( yttcmd "carvel.dev/ytt/pkg/cmd/template" "github.com/docker/docker/client" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -96,7 +97,15 @@ func (o *DockerWorkshopDeleteOptions) Run(cmd *cobra.Command) error { var workshop *unstructured.Unstructured - if workshop, err = loadWorkshopDefinition(o.Name, path, constants.DefaultPortalName, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + definitionConfig := workshops.WorkshopDefinitionConfig{ + Name: o.Name, + Path: path, + Portal: constants.DefaultPortalName, + WorkshopFile: o.WorkshopFile, + WorkshopVersion: o.WorkshopVersion, + DataValueFlags: o.DataValuesFlags, + } + if workshop, err = workshops.LoadWorkshopDefinition(&definitionConfig); err != nil { return err } diff --git a/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go b/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go index f02db0026..c98a09cd9 100644 --- a/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go @@ -20,6 +20,7 @@ import ( composetypes "github.com/compose-spec/compose-go/v2/types" "github.com/docker/docker/client" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -103,7 +104,15 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployOptions, var workshop *unstructured.Unstructured - if workshop, err = loadWorkshopDefinition("", o.Path, constants.DefaultPortalName, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + definitionConfig := workshops.WorkshopDefinitionConfig{ + Name: "", + Path: o.Path, + Portal: constants.DefaultPortalName, + WorkshopFile: o.WorkshopFile, + WorkshopVersion: o.WorkshopVersion, + DataValueFlags: o.DataValuesFlags, + } + if workshop, err = workshops.LoadWorkshopDefinition(&definitionConfig); err != nil { return "", err } diff --git a/client-programs/pkg/cmd/docker_workshop_logs.go b/client-programs/pkg/cmd/docker_workshop_logs.go index 811579830..67f819a1d 100644 --- a/client-programs/pkg/cmd/docker_workshop_logs.go +++ b/client-programs/pkg/cmd/docker_workshop_logs.go @@ -5,6 +5,7 @@ import ( yttcmd "carvel.dev/ytt/pkg/cmd/template" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" "github.com/pkg/errors" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -41,7 +42,15 @@ func (o *DockerWorkshopLogsOptions) Run(cmd *cobra.Command) error { var workshop *unstructured.Unstructured - if workshop, err = loadWorkshopDefinition(o.Name, path, constants.DefaultPortalName, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + definitionConfig := workshops.WorkshopDefinitionConfig{ + Name: o.Name, + Path: path, + Portal: constants.DefaultPortalName, + WorkshopFile: o.WorkshopFile, + WorkshopVersion: o.WorkshopVersion, + DataValueFlags: o.DataValuesFlags, + } + if workshop, err = workshops.LoadWorkshopDefinition(&definitionConfig); err != nil { return err } diff --git a/client-programs/pkg/cmd/docker_workshop_open_cmd.go b/client-programs/pkg/cmd/docker_workshop_open_cmd.go index c04a78d85..a63c3ff53 100644 --- a/client-programs/pkg/cmd/docker_workshop_open_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_open_cmd.go @@ -12,6 +12,7 @@ import ( yttcmd "carvel.dev/ytt/pkg/cmd/template" "github.com/docker/docker/client" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" "github.com/pkg/errors" "github.com/spf13/cobra" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -47,7 +48,15 @@ func (o *DockerWorkshopOpenOptions) Run() error { var workshop *unstructured.Unstructured - if workshop, err = loadWorkshopDefinition(o.Name, path, constants.DefaultPortalName, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + definitionConfig := workshops.WorkshopDefinitionConfig{ + Name: o.Name, + Path: path, + Portal: constants.DefaultPortalName, + WorkshopFile: o.WorkshopFile, + WorkshopVersion: o.WorkshopVersion, + DataValueFlags: o.DataValuesFlags, + } + if workshop, err = workshops.LoadWorkshopDefinition(&definitionConfig); err != nil { return err } diff --git a/client-programs/pkg/educates/resources/workshops/manager.go b/client-programs/pkg/educates/resources/workshops/manager.go new file mode 100644 index 000000000..62c65282f --- /dev/null +++ b/client-programs/pkg/educates/resources/workshops/manager.go @@ -0,0 +1,693 @@ +package workshops + +import ( + "context" + "crypto/sha1" + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "os" + "path/filepath" + "strings" + "text/tabwriter" + "time" + + yttcmd "carvel.dev/ytt/pkg/cmd/template" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" + "github.com/educates/educates-training-platform/client-programs/pkg/workshops" + "github.com/pkg/errors" + k8serrors "k8s.io/apimachinery/pkg/api/errors" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" + "k8s.io/apimachinery/pkg/runtime" + "k8s.io/apimachinery/pkg/runtime/schema" + "k8s.io/apimachinery/pkg/runtime/serializer" + "k8s.io/apimachinery/pkg/types" + "k8s.io/client-go/dynamic" + "k8s.io/kubectl/pkg/scheme" +) + +type WorkshopManager struct { + Client dynamic.Interface +} + +func NewWorkshopManager(client dynamic.Interface) *WorkshopManager { + return &WorkshopManager{Client: client} +} + +type DeployWorkshopConfig struct { + Workshop *unstructured.Unstructured + Alias string + Portal string + Capacity uint + Reserved uint + Initial uint + Expires string + Overtime string + Deadline string + Orphaned string + Overdue string + Refresh string + Registry string + Environ []string + Labels []string + OpenBrowser bool +} + +type UpdateWorkshopResourceConfig struct { + Workshop *unstructured.Unstructured +} + +type WorkshopDefinitionConfig struct { + Name string + Path string + Portal string + WorkshopFile string + WorkshopVersion string + DataValueFlags yttcmd.DataValuesFlags +} + +type ListWorkshopResourcesConfig struct { + Portal string +} + +type DeleteWorkshopResourceConfig struct { + Name string + Alias string + Portal string +} + +var TrainingPortalResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "trainingportals"} +var WorkshopResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshops"} + + +func (m *WorkshopManager) DeployWorkshopResource(o *DeployWorkshopConfig) error { + trainingPortalClient := m.Client.Resource(TrainingPortalResource) + + trainingPortal, err := trainingPortalClient.Get(context.TODO(), o.Portal, metav1.GetOptions{}) + + var trainingPortalExists = true + + if k8serrors.IsNotFound(err) { + trainingPortalExists = false + + trainingPortal = &unstructured.Unstructured{} + + trainingPortal.SetUnstructuredContent(map[string]interface{}{ + "apiVersion": "training.educates.dev/v1beta1", + "kind": "TrainingPortal", + "metadata": map[string]interface{}{ + "name": o.Portal, + }, + "spec": map[string]interface{}{ + "portal": map[string]interface{}{ + "password": utils.RandomPassword(12), + "registration": struct { + Type string `json:"type"` + }{ + Type: "anonymous", + }, + "updates": struct { + Workshop bool `json:"workshop"` + }{ + Workshop: true, + }, + "sessions": struct { + Maximum int64 `json:"maximum"` + }{ + Maximum: 5, + }, + "workshop": map[string]interface{}{ + "defaults": struct { + Reserved int `json:"reserved"` + }{ + Reserved: 0, + }, + }, + }, + "workshops": []interface{}{}, + }, + }) + } + + workshops, _, err := unstructured.NestedSlice(trainingPortal.Object, "spec", "workshops") + + if err != nil { + return errors.Wrap(err, "unable to retrieve workshops from training portal") + } + + var updatedWorkshops []interface{} + + if o.Expires == "" { + duration, propertyExists, err := unstructured.NestedString(o.Workshop.Object, "spec", "duration") + + if err != nil || !propertyExists { + o.Expires = "60m" + } else { + o.Expires = duration + } + } + + type EnvironDetails struct { + Name string `json:"name"` + Value string `json:"value"` + } + + var environVariables []EnvironDetails + + for _, value := range o.Environ { + parts := strings.SplitN(value, "=", 2) + environVariables = append(environVariables, EnvironDetails{ + Name: parts[0], + Value: parts[1], + }) + } + + type LabelDetails struct { + Name string `json:"name"` + Value string `json:"value"` + } + + var labelOverrides []LabelDetails + + for _, value := range o.Labels { + parts := strings.SplitN(value, "=", 2) + labelOverrides = append(labelOverrides, LabelDetails{ + Name: parts[0], + Value: parts[1], + }) + } + + var foundWorkshop = false + + for _, item := range workshops { + object := item.(map[string]interface{}) + + updatedWorkshops = append(updatedWorkshops, object) + + if object["name"] == o.Workshop.GetName() && object["alias"] == o.Alias { + foundWorkshop = true + + object["reserved"] = int64(o.Reserved) + object["initial"] = int64(o.Initial) + + if o.Capacity != 0 { + object["capacity"] = int64(o.Capacity) + } else { + delete(object, "capacity") + } + + if o.Expires != "" { + object["expires"] = o.Expires + } else { + delete(object, "expires") + } + + if o.Overtime != "" { + object["overtime"] = o.Overtime + } else { + delete(object, "overtime") + } + + if o.Deadline != "" { + object["deadline"] = o.Deadline + } else { + delete(object, "deadline") + } + + if o.Orphaned != "" { + object["orphaned"] = o.Orphaned + } else { + delete(object, "orphaned") + } + + if o.Overdue != "" { + object["overdue"] = o.Overdue + } else { + delete(object, "overdue") + } + + if o.Refresh != "" { + object["refresh"] = o.Refresh + } else { + delete(object, "refresh") + } + + var tmpEnvironVariables []interface{} + + for _, item := range environVariables { + tmpEnvironVariables = append(tmpEnvironVariables, map[string]interface{}{ + "name": item.Name, + "value": item.Value, + }) + } + + object["env"] = tmpEnvironVariables + + var tmpLabelOverrides []interface{} + + for _, item := range labelOverrides { + tmpLabelOverrides = append(tmpLabelOverrides, map[string]interface{}{ + "name": item.Name, + "value": item.Value, + }) + } + + object["labels"] = tmpLabelOverrides + } + } + + type RegistryDetails struct { + Host string `json:"host"` + Namespace string `json:"namespace,omitempty"` + } + + type WorkshopDetails struct { + Name string `json:"name"` + Alias string `json:"alias"` + Capacity int64 `json:"capacity,omitempty"` + Initial int64 `json:"initial"` + Reserved int64 `json:"reserved"` + Expires string `json:"expires,omitempty"` + Overtime string `json:"overtime,omitempty"` + Deadline string `json:"deadline,omitempty"` + Orphaned string `json:"orphaned,omitempty"` + Overdue string `json:"overdue,omitempty"` + Refresh string `json:"refresh,omitempty"` + Registry *RegistryDetails `json:"registry,omitempty"` + Environ []EnvironDetails `json:"env"` + Labels []LabelDetails `json:"labels"` + } + + if !foundWorkshop { + workshopDetails := WorkshopDetails{ + Name: o.Workshop.GetName(), + Alias: o.Alias, + Initial: int64(o.Initial), + Reserved: int64(o.Reserved), + Expires: o.Expires, + Overtime: o.Overtime, + Deadline: o.Deadline, + Orphaned: o.Orphaned, + Overdue: o.Overdue, + Refresh: o.Refresh, + Environ: environVariables, + Labels: labelOverrides, + } + + if o.Capacity != 0 { + workshopDetails.Capacity = int64(o.Capacity) + } + + if o.Registry != "" { + parts := strings.SplitN(o.Registry, "/", 2) + + host := parts[0] + var namespace string + + if len(parts) > 1 { + namespace = parts[1] + } + + registryDetails := RegistryDetails{ + Host: host, + Namespace: namespace, + } + + workshopDetails.Registry = ®istryDetails + } + + var workshopDetailsMap map[string]interface{} + + data, _ := json.Marshal(workshopDetails) + json.Unmarshal(data, &workshopDetailsMap) + + updatedWorkshops = append(updatedWorkshops, workshopDetailsMap) + } + + unstructured.SetNestedSlice(trainingPortal.Object, updatedWorkshops, "spec", "workshops") + + if trainingPortalExists { + fmt.Printf("Updating existing training portal %q.\n", trainingPortal.GetName()) + _, err = trainingPortalClient.Update(context.TODO(), trainingPortal, metav1.UpdateOptions{FieldManager: constants.DefaultPortalName}) + } else { + fmt.Printf("Creating new training portal %q.\n", trainingPortal.GetName()) + _, err = trainingPortalClient.Create(context.TODO(), trainingPortal, metav1.CreateOptions{FieldManager: constants.DefaultPortalName}) + } + + if err != nil { + return errors.Wrapf(err, "unable to update training portal %q in cluster", o.Portal) + } + + fmt.Print("Workshop added to training portal.\n") + + if o.OpenBrowser { + // Need to refetch training portal because if was just created the URL + // for access may not have been set yet. + + var targetUrl string + + fmt.Print("Checking training portal is ready.\n") + + spinner := func(iteration int) string { + spinners := `|/-\` + return string(spinners[iteration%len(spinners)]) + } + + for i := 1; i < 60; i++ { + fmt.Printf("\r[%s] Waiting...", spinner(i)) + + time.Sleep(time.Second) + + trainingPortal, err = trainingPortalClient.Get(context.TODO(), o.Portal, metav1.GetOptions{}) + + if err != nil { + return errors.Wrapf(err, "unable to fetch training portal %q in cluster", o.Portal) + } + + var found bool + + targetUrl, found, _ = unstructured.NestedString(trainingPortal.Object, "status", "educates", "url") + + if found { + break + } + } + + rootUrl := targetUrl + + password, _, _ := unstructured.NestedString(trainingPortal.Object, "spec", "portal", "password") + + if password != "" { + values := url.Values{} + values.Add("redirect_url", "/") + values.Add("password", password) + + targetUrl = fmt.Sprintf("%s/workshops/access/?%s", targetUrl, values.Encode()) + } + + for i := 1; i < 300; i++ { + fmt.Printf("\r[%s] Waiting...", spinner(i)) + + time.Sleep(time.Second) + + resp, err := http.Get(rootUrl) + + if err != nil || resp.StatusCode == 503 { + continue + } + + defer resp.Body.Close() + io.ReadAll(resp.Body) + + break + } + + fmt.Print("\r \r") + + fmt.Printf("Opening training portal %s.\n", targetUrl) + + return utils.OpenBrowser(targetUrl) + } + + return nil +} + + +func (m *WorkshopManager) UpdateWorkshopResource(o *UpdateWorkshopResourceConfig) error { + workshopsClient := m.Client.Resource(WorkshopResource) + + // _, err := workshopsClient.Apply(context.TODO(), workshop.GetName(), workshop, metav1.ApplyOptions{FieldManager: constants.DefaultPortalName, Force: true}) + + workshopBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, o.Workshop) + + if err != nil { + return errors.Wrapf(err, "unable to update workshop definition in cluster %q", o.Workshop.GetName()) + } + + _, err = workshopsClient.Patch(context.TODO(), o.Workshop.GetName(), types.ApplyPatchType, workshopBytes, metav1.ApplyOptions{FieldManager: constants.DefaultPortalName, Force: true}.ToPatchOptions()) + + if err != nil { + return errors.Wrapf(err, "unable to update workshop definition in cluster %q", o.Workshop.GetName()) + } + + return nil +} + +func (m *WorkshopManager) ListWorkshopResources(o *ListWorkshopResourcesConfig) (string, error) { + trainingPortalClient := m.Client.Resource(TrainingPortalResource) + + trainingPortal, err := trainingPortalClient.Get(context.TODO(), o.Portal, metav1.GetOptions{}) + + if k8serrors.IsNotFound(err) { + return "No workshops found.", nil + } + + sessionsMaximum, sessionsMaximumExists, _ := unstructured.NestedInt64(trainingPortal.Object, "spec", "portal", "sessions", "maximum") + + workshops, _, err := unstructured.NestedSlice(trainingPortal.Object, "spec", "workshops") + + if err != nil { + return "", errors.Wrap(err, "unable to retrieve workshops from training portal") + } + + if len(workshops) == 0 { + return "No workshops found.", nil + } + + var buf strings.Builder + w := new(tabwriter.Writer) + w.Init(&buf, 8, 8, 3, ' ', 0) + + fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "NAME", "ALIAS", "CAPACITY", "SOURCE") + + workshopsClient := m.Client.Resource(WorkshopResource) + + for _, item := range workshops { + object := item.(map[string]interface{}) + name := object["name"].(string) + + var capacityField string + + capacity, capacityExists := object["capacity"] + + if capacityExists { + capacityField = fmt.Sprintf("%d", capacity) + } else if sessionsMaximumExists { + capacityField = fmt.Sprintf("%d", sessionsMaximum) + } + + workshop, err := workshopsClient.Get(context.TODO(), name, metav1.GetOptions{}) + + source := "" + + if err == nil { + annotations := workshop.GetAnnotations() + + if val, ok := annotations["training.educates.dev/source"]; ok { + source = val + } + } + + fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", object["name"], object["alias"], capacityField, source) + } + + w.Flush() + + return buf.String(), nil +} + +func (m *WorkshopManager) DeleteWorkshopResource(o *DeleteWorkshopResourceConfig) error { + trainingPortalClient := m.Client.Resource(TrainingPortalResource) + + trainingPortal, err := trainingPortalClient.Get(context.TODO(), o.Portal, metav1.GetOptions{}) + + if k8serrors.IsNotFound(err) { + return nil + } + + workshops, _, err := unstructured.NestedSlice(trainingPortal.Object, "spec", "workshops") + + if err != nil { + return errors.Wrap(err, "unable to retrieve workshops from training portal") + } + + var found = false + + var updatedWorkshops []interface{} + + for _, item := range workshops { + object := item.(map[string]interface{}) + + if object["name"] != o.Name || object["alias"] != o.Alias { + updatedWorkshops = append(updatedWorkshops, object) + } else { + found = true + } + } + + if !found { + return nil + } + + unstructured.SetNestedSlice(trainingPortal.Object, updatedWorkshops, "spec", "workshops") + + _, err = trainingPortalClient.Update(context.TODO(), trainingPortal, metav1.UpdateOptions{FieldManager: constants.DefaultPortalName}) + + if err != nil { + return errors.Wrapf(err, "unable to update training portal %q in cluster", o.Portal) + } + + return nil +} + +func LoadWorkshopDefinition(o *WorkshopDefinitionConfig) (*unstructured.Unstructured, error) { + // Parse the workshop location so we can determine if it is a local file + // or accessible using a HTTP/HTTPS URL. + + var urlInfo *url.URL + var err error + + if urlInfo, err = url.Parse(o.Path); err != nil { + return nil, errors.Wrap(err, "unable to parse workshop location") + } + + // Check if file system path first (not HTTP/HTTPS) and if so normalize + // the path. If it the path references a directory, then extend the path + // so we look for the workshop file within that directory. + + if urlInfo.Scheme != "http" && urlInfo.Scheme != "https" { + o.Path = filepath.Clean(o.Path) + + if o.Path, err = filepath.Abs(o.Path); err != nil { + return nil, errors.Wrap(err, "couldn't convert workshop location to absolute path") + } + + if !filepath.IsAbs(o.WorkshopFile) { + fileInfo, err := os.Stat(o.Path) + + if err != nil { + return nil, errors.Wrap(err, "couldn't test if workshop location is a directory") + } + + if fileInfo.IsDir() { + o.Path = filepath.Join(o.Path, o.WorkshopFile) + } + } else { + o.Path = o.WorkshopFile + } + } + + // Read in the workshop definition as raw data ready for parsing. + + var workshopData []byte + + if urlInfo.Scheme != "http" && urlInfo.Scheme != "https" { + if workshopData, err = os.ReadFile(o.Path); err != nil { + return nil, errors.Wrap(err, "couldn't read workshop definition data file") + } + } else { + var client http.Client + + resp, err := client.Get(o.Path) + + if err != nil { + return nil, errors.Wrap(err, "couldn't download workshop definition from host") + } + + defer resp.Body.Close() + + if resp.StatusCode != http.StatusOK { + return nil, errors.New("failed to download workshop definition from host") + } + + workshopData, err = io.ReadAll(resp.Body) + + if err != nil { + return nil, errors.Wrap(err, "failed to read workshop definition from host") + } + } + + // Process the workshop YAML data in case it contains ytt templating. + + if workshopData, err = workshops.ProcessWorkshopDefinition(workshopData, o.DataValueFlags); err != nil { + return nil, errors.Wrap(err, "unable to process workshop definition as template") + } + + // Parse the workshop definition. + + decoder := serializer.NewCodecFactory(scheme.Scheme).UniversalDecoder() + + workshop := &unstructured.Unstructured{} + + err = runtime.DecodeInto(decoder, workshopData, workshop) + + if err != nil { + return nil, errors.Wrap(err, "couldn't parse workshop definition") + } + + // Verify the type of resource definition. + + if workshop.GetAPIVersion() != "training.educates.dev/v1beta1" || workshop.GetKind() != "Workshop" { + return nil, errors.New("invalid type for workshop definition") + } + + // Add annotations recording details about original workshop location. + + annotations := workshop.GetAnnotations() + + if annotations == nil { + annotations = map[string]string{} + } + + annotations["training.educates.dev/workshop"] = workshop.GetName() + + if urlInfo.Scheme != "http" && urlInfo.Scheme != "https" { + annotations["training.educates.dev/source"] = fmt.Sprintf("file://%s", o.Path) + } else { + annotations["training.educates.dev/source"] = o.Path + } + + workshop.SetAnnotations(annotations) + + // Update the name for the workshop such that it incorporates a hash of + // the workshop location. + + if o.Name == "" { + o.Name = generateWorkshopName(o.Path, workshop, o.Portal) + } + + workshop.SetName(o.Name) + + // Insert workshop version property if not specified. + + _, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") + + if !found && o.WorkshopVersion != "latest" { + unstructured.SetNestedField(workshop.Object, o.WorkshopVersion, "spec", "version") + } + + // Remove the publish section as will not be accurate after publising. + + unstructured.RemoveNestedField(workshop.Object, "spec", "publish") + + return workshop, nil +} + + +func generateWorkshopName(path string, workshop *unstructured.Unstructured, portal string) string { + name := workshop.GetName() + + h := sha1.New() + + io.WriteString(h, path) + + hv := fmt.Sprintf("%x", h.Sum(nil)) + + name = fmt.Sprintf("%s--%s-%s", portal, name, hv[len(hv)-7:]) + + return name +} diff --git a/client-programs/pkg/portal/manager.go b/client-programs/pkg/portal/manager.go index 286c9f524..4412f9b1f 100644 --- a/client-programs/pkg/portal/manager.go +++ b/client-programs/pkg/portal/manager.go @@ -8,6 +8,7 @@ import ( "text/tabwriter" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -71,7 +72,7 @@ func (m *PortalManager) CreateTrainingPortal(cfg *TrainingPortalCreateConfig) er trainingPortal := &unstructured.Unstructured{} if !cfg.IsPasswordSet { - cfg.Password = RandomPassword(12) + cfg.Password = utils.RandomPassword(12) } type LabelDetails struct { diff --git a/client-programs/pkg/renderer/hugo.go b/client-programs/pkg/renderer/hugo.go index f3b65d5bb..03cc56186 100644 --- a/client-programs/pkg/renderer/hugo.go +++ b/client-programs/pkg/renderer/hugo.go @@ -23,8 +23,9 @@ import ( "syscall" "time" - "github.com/pkg/errors" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" + "github.com/pkg/errors" "gopkg.in/yaml.v2" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -61,7 +62,7 @@ func copyFiles(fs embed.FS, src string, dst string) error { return errors.Wrapf(err, "unable to open source file %q", srcFile) } - err = ioutil.WriteFile(dstFile, input, 0644) + err = os.WriteFile(dstFile, input, 0644) if err != nil { return errors.Wrapf(err, "unable to create source file %q", dstFile) @@ -104,6 +105,20 @@ type WorkshopConfig struct { Params []WorkshopParamsConfig `yaml:"params,omitempty"` } +type RunHugoServerConfig struct { + WorkshopRoot string + Kubeconfig string + Context string + Workshop string + Portal string + LocalHost string + LocalPort int + HugoPort int + Token string + Files bool + CleanupFunc ServerCleanupFunc +} + var workshopSessionResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshopsessions"} func fetchWorkshopSessionAndValidate(kubeconfig string, kubeContext string, workshop string, portal string, session string) (string, string, error) { @@ -180,7 +195,7 @@ func fetchSessionVariables(sessionURL string, password string) (map[string]strin return params, errors.New("unexpected failure querying workshop session config") } - resBody, err := ioutil.ReadAll(res.Body) + resBody, err := io.ReadAll(res.Body) if err != nil { return params, errors.Wrapf(err, "failed to read workshop session config") @@ -375,7 +390,7 @@ func startHugoServer(workshopDir string, tempDir string, port int, sessionURL st } func populateTemporaryDirectory() (string, error) { - tempDir, err := ioutil.TempDir("", "educates") + tempDir, err := os.MkdirTemp("", "educates") if err != nil { return "", errors.Wrapf(err, "unable to create hugo files directory") @@ -392,11 +407,11 @@ func populateTemporaryDirectory() (string, error) { type ServerCleanupFunc func() -func RunHugoServer(workshopRoot string, kubeconfig string, context string, workshop string, portal string, localHost string, localPort int, hugoPort int, token string, files bool, cleanupFunc ServerCleanupFunc) error { +func RunHugoServer(o *RunHugoServerConfig) error { var err error var tempDir string - workshopDir := filepath.Join(workshopRoot, "workshop") + workshopDir := filepath.Join(o.WorkshopRoot, "workshop") // First create directory to hold unpacked files for Hugo to use. @@ -417,8 +432,8 @@ func RunHugoServer(workshopRoot string, kubeconfig string, context string, works os.RemoveAll(tempDir) - if cleanupFunc != nil { - cleanupFunc() + if o.CleanupFunc != nil { + o.CleanupFunc() } os.Exit(1) @@ -434,10 +449,10 @@ func RunHugoServer(workshopRoot string, kubeconfig string, context string, works proxyHandler := func(w http.ResponseWriter, r *http.Request) { // If an access token is provided validate it. - if token != "" { + if o.Token != "" { accessToken := r.Header.Get("X-Access-Token") - if accessToken != token { + if accessToken != o.Token { w.WriteHeader(http.StatusForbidden) w.Write([]byte("403 - Invalid access token")) @@ -463,7 +478,7 @@ func RunHugoServer(workshopRoot string, kubeconfig string, context string, works if sessionName != lastSessionName { // First validate that can access workshop session. - sessionURL, password, err := fetchWorkshopSessionAndValidate(kubeconfig, context, workshop, portal, sessionName) + sessionURL, password, err := fetchWorkshopSessionAndValidate(o.Kubeconfig, o.Context, o.Workshop, o.Portal, sessionName) if err != nil { fmt.Println("Error validating workshop session:", err) @@ -512,7 +527,7 @@ func RunHugoServer(workshopRoot string, kubeconfig string, context string, works if !hugoStarted { fmt.Println("Starting Hugo server") - go startHugoServer(workshopDir, tempDir, hugoPort, sessionURL) + go startHugoServer(workshopDir, tempDir, o.HugoPort, sessionURL) time.Sleep(4 * time.Second) @@ -528,7 +543,7 @@ func RunHugoServer(workshopRoot string, kubeconfig string, context string, works serverDetailsLock.Unlock() - hugoServerURL := fmt.Sprintf("http://localhost:%d", hugoPort) + hugoServerURL := fmt.Sprintf("http://localhost:%d", o.HugoPort) target, _ := url.Parse(hugoServerURL) proxy := httputil.NewSingleHostReverseProxy(target) @@ -543,10 +558,10 @@ func RunHugoServer(workshopRoot string, kubeconfig string, context string, works http.HandleFunc("/workshop/content/", proxyHandler) filesHandler := func(w http.ResponseWriter, r *http.Request) { - if token != "" { + if o.Token != "" { accessToken := r.URL.Query().Get("token") - if accessToken != token { + if accessToken != o.Token { w.WriteHeader(http.StatusForbidden) w.Write([]byte("403 - Invalid access token")) @@ -558,7 +573,7 @@ func RunHugoServer(workshopRoot string, kubeconfig string, context string, works tw := tar.NewWriter(w) - filepath.Walk(workshopRoot, func(file string, fi os.FileInfo, err error) error { + filepath.Walk(o.WorkshopRoot, func(file string, fi os.FileInfo, err error) error { if err != nil { return err } @@ -568,7 +583,7 @@ func RunHugoServer(workshopRoot string, kubeconfig string, context string, works return err } - header.Name, err = filepath.Rel(workshopRoot, filepath.ToSlash(file)) + header.Name, err = filepath.Rel(o.WorkshopRoot, filepath.ToSlash(file)) if err != nil { return err @@ -596,11 +611,11 @@ func RunHugoServer(workshopRoot string, kubeconfig string, context string, works }) } - if files { + if o.Files { http.HandleFunc("/workshop/files.tar", filesHandler) } - portString := fmt.Sprintf("%s:%d", localHost, localPort) + portString := fmt.Sprintf("%s:%d", o.LocalHost, o.LocalPort) fmt.Println("Proxy listening on:", portString) @@ -608,3 +623,48 @@ func RunHugoServer(workshopRoot string, kubeconfig string, context string, works return nil } + +func GenerateAccessToken(refresh bool) (string, error) { + configFileDir := utils.GetEducatesHomeDir() + accessTokenFile := path.Join(configFileDir, "live-reload-token.dat") + + err := os.MkdirAll(configFileDir, os.ModePerm) + + if err != nil { + return "", errors.Wrapf(err, "unable to create config directory") + } + + var accessToken string + + if refresh { + accessToken = utils.RandomPassword(32) + + err := os.WriteFile(accessTokenFile, []byte(accessToken), 0644) + + if err != nil { + return "", err + } + } else { + if _, err := os.Stat(accessTokenFile); err == nil { + accessTokenBytes, err := ioutil.ReadFile(accessTokenFile) + + if err != nil { + return "", err + } + + accessToken = string(accessTokenBytes) + } else if os.IsNotExist(err) { + accessToken = utils.RandomPassword(32) + + err = os.WriteFile(accessTokenFile, []byte(accessToken), 0644) + + if err != nil { + return "", err + } + } else { + return "", err + } + } + + return accessToken, nil +} diff --git a/client-programs/pkg/portal/password.go b/client-programs/pkg/utils/password.go similarity index 96% rename from client-programs/pkg/portal/password.go rename to client-programs/pkg/utils/password.go index 601322c8c..a6e18605b 100644 --- a/client-programs/pkg/portal/password.go +++ b/client-programs/pkg/utils/password.go @@ -1,4 +1,4 @@ -package portal +package utils import ( "hash/maphash" From d2598b8c42116317f6a3d5eb8a2487f1abfe8262 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Tue, 20 Jan 2026 14:03:52 +0100 Subject: [PATCH 10/24] Refactoring moves and cluster_workshop_xx command --- .../pkg/cmd/cluster_portal_create_cmd.go | 2 +- .../pkg/cmd/cluster_portal_delete_cmd.go | 2 +- .../pkg/cmd/cluster_portal_list_cmd.go | 2 +- .../pkg/cmd/cluster_portal_open_cmd.go | 2 +- .../pkg/cmd/cluster_portal_password_cmd.go | 2 +- .../pkg/cmd/cluster_workshop_request_cmd.go | 5 +- .../pkg/cmd/workshop_export_cmd.go | 2 +- client-programs/pkg/cmd/workshop_new_cmd.go | 47 +--- .../pkg/cmd/workshop_publish_cmd.go | 2 +- .../pkg/diagnostics/diagnostics.go | 21 +- client-programs/pkg/diagnostics/fetcher.go | 15 +- .../pkg/docker/workshop_manager.go | 12 +- .../{ => educates/local}/workshops/manager.go | 17 +- .../resources}/portal/manager.go | 14 +- .../educates/resources/sessions/manager.go | 9 +- .../educates/resources/workshops/manager.go | 59 ++++- .../restapi}/catalog.go | 8 +- .../restapi}/types.go | 2 +- .../pkg/educates/types/educates.go | 14 ++ client-programs/pkg/renderer/hugo.go | 6 +- client-programs/pkg/workshops/definition.go | 233 ------------------ 21 files changed, 135 insertions(+), 341 deletions(-) rename client-programs/pkg/{ => educates/local}/workshops/manager.go (93%) rename client-programs/pkg/{ => educates/resources}/portal/manager.go (93%) rename client-programs/pkg/{educatesrestapi => educates/restapi}/catalog.go (98%) rename client-programs/pkg/{educatesrestapi => educates/restapi}/types.go (98%) create mode 100644 client-programs/pkg/educates/types/educates.go delete mode 100644 client-programs/pkg/workshops/definition.go diff --git a/client-programs/pkg/cmd/cluster_portal_create_cmd.go b/client-programs/pkg/cmd/cluster_portal_create_cmd.go index 584195632..61c54f468 100644 --- a/client-programs/pkg/cmd/cluster_portal_create_cmd.go +++ b/client-programs/pkg/cmd/cluster_portal_create_cmd.go @@ -3,7 +3,7 @@ package cmd import ( "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" - "github.com/educates/educates-training-platform/client-programs/pkg/portal" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/portal" "github.com/pkg/errors" "github.com/spf13/cobra" ) diff --git a/client-programs/pkg/cmd/cluster_portal_delete_cmd.go b/client-programs/pkg/cmd/cluster_portal_delete_cmd.go index a0f95b80c..24f503dcb 100644 --- a/client-programs/pkg/cmd/cluster_portal_delete_cmd.go +++ b/client-programs/pkg/cmd/cluster_portal_delete_cmd.go @@ -3,7 +3,7 @@ package cmd import ( "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" - "github.com/educates/educates-training-platform/client-programs/pkg/portal" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/portal" "github.com/pkg/errors" "github.com/spf13/cobra" ) diff --git a/client-programs/pkg/cmd/cluster_portal_list_cmd.go b/client-programs/pkg/cmd/cluster_portal_list_cmd.go index bd6319a93..94a4e139d 100644 --- a/client-programs/pkg/cmd/cluster_portal_list_cmd.go +++ b/client-programs/pkg/cmd/cluster_portal_list_cmd.go @@ -4,7 +4,7 @@ import ( "fmt" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" - "github.com/educates/educates-training-platform/client-programs/pkg/portal" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/portal" "github.com/pkg/errors" "github.com/spf13/cobra" ) diff --git a/client-programs/pkg/cmd/cluster_portal_open_cmd.go b/client-programs/pkg/cmd/cluster_portal_open_cmd.go index 6464a838c..baf5cee04 100644 --- a/client-programs/pkg/cmd/cluster_portal_open_cmd.go +++ b/client-programs/pkg/cmd/cluster_portal_open_cmd.go @@ -8,7 +8,7 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" - "github.com/educates/educates-training-platform/client-programs/pkg/portal" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/portal" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" "github.com/spf13/cobra" diff --git a/client-programs/pkg/cmd/cluster_portal_password_cmd.go b/client-programs/pkg/cmd/cluster_portal_password_cmd.go index 2523a7db5..10f51f1b5 100644 --- a/client-programs/pkg/cmd/cluster_portal_password_cmd.go +++ b/client-programs/pkg/cmd/cluster_portal_password_cmd.go @@ -5,7 +5,7 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" - "github.com/educates/educates-training-platform/client-programs/pkg/portal" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/portal" "github.com/pkg/errors" "github.com/spf13/cobra" ) diff --git a/client-programs/pkg/cmd/cluster_workshop_request_cmd.go b/client-programs/pkg/cmd/cluster_workshop_request_cmd.go index 360db579f..9e1127147 100644 --- a/client-programs/pkg/cmd/cluster_workshop_request_cmd.go +++ b/client-programs/pkg/cmd/cluster_workshop_request_cmd.go @@ -10,7 +10,8 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" - "github.com/educates/educates-training-platform/client-programs/pkg/educatesrestapi" + educatesrestapi "github.com/educates/educates-training-platform/client-programs/pkg/educates/restapi" + educatesTypes "github.com/educates/educates-training-platform/client-programs/pkg/educates/types" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/joho/godotenv" "github.com/pkg/errors" @@ -323,7 +324,7 @@ func ensurePortalHasWorkshop(clusterConfig *cluster.ClusterConfig, name string, return errors.Wrapf(err, "unable to create Kubernetes client") } - trainingPortalClient := client.Resource(workshops.TrainingPortalResource) + trainingPortalClient := client.Resource(educatesTypes.TrainingPortalResource) trainingPortal, err := trainingPortalClient.Get(context.TODO(), portal, metav1.GetOptions{}) diff --git a/client-programs/pkg/cmd/workshop_export_cmd.go b/client-programs/pkg/cmd/workshop_export_cmd.go index aa905892c..d6ca0e5f9 100644 --- a/client-programs/pkg/cmd/workshop_export_cmd.go +++ b/client-programs/pkg/cmd/workshop_export_cmd.go @@ -5,8 +5,8 @@ import ( "path/filepath" yttcmd "carvel.dev/ytt/pkg/cmd/template" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/local/workshops" "github.com/educates/educates-training-platform/client-programs/pkg/utils" - "github.com/educates/educates-training-platform/client-programs/pkg/workshops" "github.com/pkg/errors" "github.com/spf13/cobra" ) diff --git a/client-programs/pkg/cmd/workshop_new_cmd.go b/client-programs/pkg/cmd/workshop_new_cmd.go index 0feb8a651..99f7d6f4a 100644 --- a/client-programs/pkg/cmd/workshop_new_cmd.go +++ b/client-programs/pkg/cmd/workshop_new_cmd.go @@ -1,14 +1,9 @@ package cmd import ( - "os" - "path/filepath" - "regexp" - - "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/templates" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/local/workshops" "github.com/educates/educates-training-platform/client-programs/pkg/utils" ) @@ -46,38 +41,14 @@ func (p *ProjectInfo) NewWorkshopNewCmd() *cobra.Command { Use: "new PATH", Short: "Create workshop files from template", RunE: func(_ *cobra.Command, args []string) error { - var err error - - directory := filepath.Clean(args[0]) - - if directory, err = filepath.Abs(directory); err != nil { - return errors.Wrapf(err, "could not convert path name %q to absolute path", directory) - } - - if _, err = os.Stat(directory); err == nil { - return errors.Errorf("target path name %q already exists", directory) - } - - name := o.Name - - if name == "" { - name = filepath.Base(directory) - } - - if match, _ := regexp.MatchString("^[a-z0-9-]+$", name); !match { - return errors.Errorf("invalid workshop name %q", name) - } - - parameters := map[string]string{ - "WorkshopName": name, - "WorkshopTitle": o.Title, - "WorkshopDescription": o.Description, - "WorkshopImage": o.Image, - } - - template := templates.InternalTemplate(o.Template) - - return template.Apply(directory, parameters) + manager := workshops.NewWorkshopManager() + return manager.NewWorkshop(args[0], &workshops.WorkshopNewConfig{ + Template: o.Template, + Name: o.Name, + Title: o.Title, + Description: o.Description, + Image: o.Image, + }) }, Example: workshopNewExample, } diff --git a/client-programs/pkg/cmd/workshop_publish_cmd.go b/client-programs/pkg/cmd/workshop_publish_cmd.go index f5a1ff014..85cdd78d9 100644 --- a/client-programs/pkg/cmd/workshop_publish_cmd.go +++ b/client-programs/pkg/cmd/workshop_publish_cmd.go @@ -7,8 +7,8 @@ import ( imgpkgcmd "carvel.dev/imgpkg/pkg/imgpkg/cmd" yttcmd "carvel.dev/ytt/pkg/cmd/template" + "github.com/educates/educates-training-platform/client-programs/pkg/educates/local/workshops" "github.com/educates/educates-training-platform/client-programs/pkg/utils" - "github.com/educates/educates-training-platform/client-programs/pkg/workshops" "github.com/pkg/errors" "github.com/spf13/cobra" ) diff --git a/client-programs/pkg/diagnostics/diagnostics.go b/client-programs/pkg/diagnostics/diagnostics.go index 3b875d667..4bfb39d2f 100644 --- a/client-programs/pkg/diagnostics/diagnostics.go +++ b/client-programs/pkg/diagnostics/diagnostics.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + educatesTypes "github.com/educates/educates-training-platform/client-programs/pkg/educates/types" ) type ClusterDiagnostics struct { @@ -35,22 +36,22 @@ func (c *ClusterDiagnostics) Run() error { clusterDiagnosticsFetcher := &ClusterDiagnosticsFetcher{c.clusterConfig, tempDir, c.verbose} // Fetch all Educates training related resources - if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(trainingportalResource, "training-portals.yaml"); err != nil { + if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(educatesTypes.TrainingPortalResource, "training-portals.yaml"); err != nil { fmt.Println("Error fetching training portals: ", err) } - if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(workshopResource, "workshops.yaml"); err != nil { + if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(educatesTypes.WorkshopResource, "workshops.yaml"); err != nil { fmt.Println("Error fetching workshops: ", err) } - if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(workshopsessionsResource, "workshop-sessions.yaml"); err != nil { + if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(educatesTypes.WorkshopsessionsResource, "workshop-sessions.yaml"); err != nil { fmt.Println("Error fetching workshop sessions: ", err) } - if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(workshoprequestsResource, "workshop-requests.yaml"); err != nil { + if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(educatesTypes.WorkshoprequestsResource, "workshop-requests.yaml"); err != nil { fmt.Println("Error fetching workshop requests: ", err) } - if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(workshopenvironmentsResource, "workshop-environments.yaml"); err != nil { + if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(educatesTypes.WorkshopenvironmentsResource, "workshop-environments.yaml"); err != nil { fmt.Println("Error fetching workshop environments: ", err) } - if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(workshopallocationsResource, "workshop-allocations.yaml"); err != nil { + if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(educatesTypes.WorkshopallocationsResource, "workshop-allocations.yaml"); err != nil { fmt.Println("Error fetching workshop allocations: ", err) } @@ -60,16 +61,16 @@ func (c *ClusterDiagnostics) Run() error { } // Fetch all Educates secrets related resources - if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(secretcopierResource, "secret-copiers.yaml"); err != nil { + if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(educatesTypes.SecretcopierResource, "secret-copiers.yaml"); err != nil { fmt.Println("Error fetching secret copiers: ", err) } - if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(secretinjectorsResource, "secret-injectors.yaml"); err != nil { + if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(educatesTypes.SecretinjectorsResource, "secret-injectors.yaml"); err != nil { fmt.Println("Error fetching secret injectors: ", err) } - if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(secretexportersResource, "secret-exporters.yaml"); err != nil { + if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(educatesTypes.SecretexportersResource, "secret-exporters.yaml"); err != nil { fmt.Println("Error fetching secret injectors: ", err) } - if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(secretimportersResource, "secret-importers.yaml"); err != nil { + if err = clusterDiagnosticsFetcher.fetchDynamicallyResources(educatesTypes.SecretimportersResource, "secret-importers.yaml"); err != nil { fmt.Println("Error fetching secret injectors: ", err) } diff --git a/client-programs/pkg/diagnostics/fetcher.go b/client-programs/pkg/diagnostics/fetcher.go index 9e000e865..abf74483a 100644 --- a/client-programs/pkg/diagnostics/fetcher.go +++ b/client-programs/pkg/diagnostics/fetcher.go @@ -10,7 +10,8 @@ import ( "strings" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" - "github.com/educates/educates-training-platform/client-programs/pkg/educatesrestapi" + educatesrestapi "github.com/educates/educates-training-platform/client-programs/pkg/educates/restapi" + educatesTypes "github.com/educates/educates-training-platform/client-programs/pkg/educates/types" "github.com/pkg/errors" v1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -19,16 +20,6 @@ import ( "k8s.io/cli-runtime/pkg/printers" ) -var workshopResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshops"} -var trainingportalResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "trainingportals"} -var workshopsessionsResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshopsessions"} -var workshoprequestsResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshoprequests"} -var workshopenvironmentsResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshopenvironments"} -var workshopallocationsResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshopallocations"} -var secretcopierResource = schema.GroupVersionResource{Group: "secrets.educates.dev", Version: "v1beta1", Resource: "secretcopiers"} -var secretinjectorsResource = schema.GroupVersionResource{Group: "secrets.educates.dev", Version: "v1beta1", Resource: "secretinjectors"} -var secretexportersResource = schema.GroupVersionResource{Group: "secrets.educates.dev", Version: "v1beta1", Resource: "secretexporters"} -var secretimportersResource = schema.GroupVersionResource{Group: "secrets.educates.dev", Version: "v1beta1", Resource: "secretimporters"} type ClusterDiagnosticsFetcher struct { clusterConfig *cluster.ClusterConfig @@ -216,7 +207,7 @@ func (c *ClusterDiagnosticsFetcher) fetchTrainingPortalDetailsAtRest(fileNamePat if err != nil { return err } - dynClient := dynamicClient.Resource(trainingportalResource) + dynClient := dynamicClient.Resource(educatesTypes.TrainingPortalResource) trainingPortals, err := dynClient.List(context.TODO(), metav1.ListOptions{}) if err != nil { return err diff --git a/client-programs/pkg/docker/workshop_manager.go b/client-programs/pkg/docker/workshop_manager.go index 2847a0ac7..63f8cdb6d 100644 --- a/client-programs/pkg/docker/workshop_manager.go +++ b/client-programs/pkg/docker/workshop_manager.go @@ -24,8 +24,8 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/client" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + eduk8sWorkshops "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" "github.com/educates/educates-training-platform/client-programs/pkg/utils" - "github.com/educates/educates-training-platform/client-programs/pkg/workshops" "github.com/pkg/errors" "go.yaml.in/yaml/v2" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" @@ -260,7 +260,15 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployConfig, s var workshop *unstructured.Unstructured - if workshop, err = workshops.LoadWorkshopDefinition("", o.Path, constants.DefaultPortalName, o.WorkshopFile, o.WorkshopVersion, o.DataValuesFlags); err != nil { + definitionConfig := eduk8sWorkshops.WorkshopDefinitionConfig{ + Name: "", + Path: o.Path, + Portal: constants.DefaultPortalName, + WorkshopFile: o.WorkshopFile, + WorkshopVersion: o.WorkshopVersion, + DataValueFlags: o.DataValuesFlags, + } + if workshop, err = eduk8sWorkshops.LoadWorkshopDefinition(&definitionConfig); err != nil { return "", err } diff --git a/client-programs/pkg/workshops/manager.go b/client-programs/pkg/educates/local/workshops/manager.go similarity index 93% rename from client-programs/pkg/workshops/manager.go rename to client-programs/pkg/educates/local/workshops/manager.go index bafacb7eb..fcdc00e36 100644 --- a/client-programs/pkg/workshops/manager.go +++ b/client-programs/pkg/educates/local/workshops/manager.go @@ -13,6 +13,7 @@ import ( yttcmd "carvel.dev/ytt/pkg/cmd/template" "github.com/cppforlife/go-cli-ui/ui" + eduk8sWorkshops "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" "github.com/educates/educates-training-platform/client-programs/pkg/templates" "github.com/pkg/errors" "go.yaml.in/yaml/v2" @@ -56,6 +57,18 @@ func NewWorkshopManager() *WorkshopManager { } func (m *WorkshopManager) NewWorkshop(directory string,o *WorkshopNewConfig) error { + var err error + + directory = filepath.Clean(directory) + + if directory, err = filepath.Abs(directory); err != nil { + return errors.Wrapf(err, "could not convert path name %q to absolute path", directory) + } + + if _, err = os.Stat(directory); err == nil { + return errors.Errorf("target path name %q already exists", directory) + } + name := o.Name if name == "" { @@ -97,7 +110,7 @@ func (m *WorkshopManager) Export(directory string,o *WorkshopExportConfig) error // Process the workshop YAML data for ytt templating and data variables. - if workshopFileData, err = ProcessWorkshopDefinition(workshopFileData, o.DataValuesFlags); err != nil { + if workshopFileData, err = eduk8sWorkshops.ProcessWorkshopDefinition(workshopFileData, o.DataValuesFlags); err != nil { return errors.Wrap(err, "unable to process workshop definition as template") } @@ -171,7 +184,7 @@ func (m *WorkshopManager) Publish(directory string,o *WorkshopPublishConfig) err // Process the workshop YAML data for ytt templating and data variables. - if workshopFileData, err = ProcessWorkshopDefinition(workshopFileData, o.DataValuesFlags); err != nil { + if workshopFileData, err = eduk8sWorkshops.ProcessWorkshopDefinition(workshopFileData, o.DataValuesFlags); err != nil { return errors.Wrap(err, "unable to process workshop definition as template") } diff --git a/client-programs/pkg/portal/manager.go b/client-programs/pkg/educates/resources/portal/manager.go similarity index 93% rename from client-programs/pkg/portal/manager.go rename to client-programs/pkg/educates/resources/portal/manager.go index 4412f9b1f..a9fefb85f 100644 --- a/client-programs/pkg/portal/manager.go +++ b/client-programs/pkg/educates/resources/portal/manager.go @@ -8,17 +8,15 @@ import ( "text/tabwriter" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + educatesTypes "github.com/educates/educates-training-platform/client-programs/pkg/educates/types" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" ) -var TrainingPortalResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "trainingportals"} - type PortalManager struct { client dynamic.Interface } @@ -57,7 +55,7 @@ func NewPortalManager(client dynamic.Interface) *PortalManager { } func (m *PortalManager) CreateTrainingPortal(cfg *TrainingPortalCreateConfig) error { - trainingPortalClient := m.client.Resource(TrainingPortalResource) + trainingPortalClient := m.client.Resource(educatesTypes.TrainingPortalResource) _, err := trainingPortalClient.Get(context.TODO(), cfg.Portal, metav1.GetOptions{}) @@ -176,7 +174,7 @@ func (m *PortalManager) CreateTrainingPortal(cfg *TrainingPortalCreateConfig) er } func (m *PortalManager) DeleteTrainingPortal(cfg *TrainingPortalDeleteConfig) error { - trainingPortalClient := m.client.Resource(TrainingPortalResource) + trainingPortalClient := m.client.Resource(educatesTypes.TrainingPortalResource) _, err := trainingPortalClient.Get(context.TODO(), cfg.Portal, metav1.GetOptions{}) @@ -195,7 +193,7 @@ func (m *PortalManager) DeleteTrainingPortal(cfg *TrainingPortalDeleteConfig) er func (m *PortalManager) ListTrainingPortals(cfg *TrainingPortalListConfig) (string, error) { - trainingPortalClient := m.client.Resource(TrainingPortalResource) + trainingPortalClient := m.client.Resource(educatesTypes.TrainingPortalResource) trainingPortals, err := trainingPortalClient.List(context.TODO(), metav1.ListOptions{}) @@ -238,7 +236,7 @@ func (m *PortalManager) ListTrainingPortals(cfg *TrainingPortalListConfig) (stri } func (m *PortalManager) GetTrainingPortalBrowserUrl(cfg *TrainingPortalOpenConfig) (string, error) { - trainingPortalClient := m.client.Resource(TrainingPortalResource) + trainingPortalClient := m.client.Resource(educatesTypes.TrainingPortalResource) trainingPortal, err := trainingPortalClient.Get(context.TODO(), cfg.Portal, metav1.GetOptions{}) @@ -270,7 +268,7 @@ func (m *PortalManager) GetTrainingPortalBrowserUrl(cfg *TrainingPortalOpenConfi } func (m *PortalManager) GetTrainingPortalPassword(cfg *TrainingPortalPasswordConfig) (string, error) { - trainingPortalClient := m.client.Resource(TrainingPortalResource) + trainingPortalClient := m.client.Resource(educatesTypes.TrainingPortalResource) trainingPortal, err := trainingPortalClient.Get(context.TODO(), cfg.Portal, metav1.GetOptions{}) diff --git a/client-programs/pkg/educates/resources/sessions/manager.go b/client-programs/pkg/educates/resources/sessions/manager.go index c4280979a..0a3d46c4b 100644 --- a/client-programs/pkg/educates/resources/sessions/manager.go +++ b/client-programs/pkg/educates/resources/sessions/manager.go @@ -7,18 +7,15 @@ import ( "text/tabwriter" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" - "github.com/educates/educates-training-platform/client-programs/pkg/educatesrestapi" + educatesrestapi "github.com/educates/educates-training-platform/client-programs/pkg/educates/restapi" + educatesTypes "github.com/educates/educates-training-platform/client-programs/pkg/educates/types" "github.com/pkg/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/client-go/dynamic" ) -var workshopSessionResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshopsessions"} - - type SessionManager struct { } @@ -52,7 +49,7 @@ type TerminateSessionConfig struct { } func (m *SessionManager) ListSessions(cfg ListSessionsConfig) (string, error) { - workshopSessionClient := cfg.Client.Resource(workshopSessionResource) + workshopSessionClient := cfg.Client.Resource(educatesTypes.WorkshopsessionsResource) workshopSessions, err := workshopSessionClient.List(context.TODO(), metav1.ListOptions{}) diff --git a/client-programs/pkg/educates/resources/workshops/manager.go b/client-programs/pkg/educates/resources/workshops/manager.go index 62c65282f..e0834315c 100644 --- a/client-programs/pkg/educates/resources/workshops/manager.go +++ b/client-programs/pkg/educates/resources/workshops/manager.go @@ -1,11 +1,13 @@ package workshops import ( + "bytes" "context" "crypto/sha1" "encoding/json" "fmt" "io" + "log" "net/http" "net/url" "os" @@ -15,15 +17,19 @@ import ( "time" yttcmd "carvel.dev/ytt/pkg/cmd/template" + yttcmdui "carvel.dev/ytt/pkg/cmd/ui" + "carvel.dev/ytt/pkg/files" + "carvel.dev/ytt/pkg/yamlmeta" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + educatesTypes "github.com/educates/educates-training-platform/client-programs/pkg/educates/types" "github.com/educates/educates-training-platform/client-programs/pkg/utils" - "github.com/educates/educates-training-platform/client-programs/pkg/workshops" + + // "github.com/educates/educates-training-platform/client-programs/pkg/workshops" "github.com/pkg/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" "k8s.io/apimachinery/pkg/runtime/serializer" "k8s.io/apimachinery/pkg/types" "k8s.io/client-go/dynamic" @@ -80,12 +86,8 @@ type DeleteWorkshopResourceConfig struct { Portal string } -var TrainingPortalResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "trainingportals"} -var WorkshopResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshops"} - - func (m *WorkshopManager) DeployWorkshopResource(o *DeployWorkshopConfig) error { - trainingPortalClient := m.Client.Resource(TrainingPortalResource) + trainingPortalClient := m.Client.Resource(educatesTypes.TrainingPortalResource) trainingPortal, err := trainingPortalClient.Get(context.TODO(), o.Portal, metav1.GetOptions{}) @@ -418,7 +420,7 @@ func (m *WorkshopManager) DeployWorkshopResource(o *DeployWorkshopConfig) error func (m *WorkshopManager) UpdateWorkshopResource(o *UpdateWorkshopResourceConfig) error { - workshopsClient := m.Client.Resource(WorkshopResource) + workshopsClient := m.Client.Resource(educatesTypes.WorkshopResource) // _, err := workshopsClient.Apply(context.TODO(), workshop.GetName(), workshop, metav1.ApplyOptions{FieldManager: constants.DefaultPortalName, Force: true}) @@ -438,7 +440,7 @@ func (m *WorkshopManager) UpdateWorkshopResource(o *UpdateWorkshopResourceConfig } func (m *WorkshopManager) ListWorkshopResources(o *ListWorkshopResourcesConfig) (string, error) { - trainingPortalClient := m.Client.Resource(TrainingPortalResource) + trainingPortalClient := m.Client.Resource(educatesTypes.TrainingPortalResource) trainingPortal, err := trainingPortalClient.Get(context.TODO(), o.Portal, metav1.GetOptions{}) @@ -464,7 +466,7 @@ func (m *WorkshopManager) ListWorkshopResources(o *ListWorkshopResourcesConfig) fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "NAME", "ALIAS", "CAPACITY", "SOURCE") - workshopsClient := m.Client.Resource(WorkshopResource) + workshopsClient := m.Client.Resource(educatesTypes.WorkshopResource) for _, item := range workshops { object := item.(map[string]interface{}) @@ -501,7 +503,7 @@ func (m *WorkshopManager) ListWorkshopResources(o *ListWorkshopResourcesConfig) } func (m *WorkshopManager) DeleteWorkshopResource(o *DeleteWorkshopResourceConfig) error { - trainingPortalClient := m.Client.Resource(TrainingPortalResource) + trainingPortalClient := m.Client.Resource(educatesTypes.TrainingPortalResource) trainingPortal, err := trainingPortalClient.Get(context.TODO(), o.Portal, metav1.GetOptions{}) @@ -613,7 +615,7 @@ func LoadWorkshopDefinition(o *WorkshopDefinitionConfig) (*unstructured.Unstruct // Process the workshop YAML data in case it contains ytt templating. - if workshopData, err = workshops.ProcessWorkshopDefinition(workshopData, o.DataValueFlags); err != nil { + if workshopData, err = ProcessWorkshopDefinition(workshopData, o.DataValueFlags); err != nil { return nil, errors.Wrap(err, "unable to process workshop definition as template") } @@ -677,6 +679,39 @@ func LoadWorkshopDefinition(o *WorkshopDefinitionConfig) (*unstructured.Unstruct return workshop, nil } +func ProcessWorkshopDefinition(yamlData []byte, dataValueFlags yttcmd.DataValuesFlags) ([]byte, error) { + templatingOptions := yttcmd.NewOptions() + + templatingOptions.IgnoreUnknownComments = true + + templatingOptions.DataValuesFlags = dataValueFlags + + var filesToProcess []*files.File + + mainInputFile := files.MustNewFileFromSource(files.NewBytesSource("workshop.yaml", yamlData)) + + filesToProcess = append(filesToProcess, mainInputFile) + + logUI := yttcmdui.NewCustomWriterTTY(false, log.Writer(), log.Writer()) + + output := templatingOptions.RunWithFiles(yttcmd.Input{Files: filesToProcess}, logUI) + + if output.Err != nil { + return []byte{}, fmt.Errorf("execution of ytt failed: %s", output.Err) + } + + if len(output.DocSet.Items) == 0 { + return []byte{}, nil + } + + var buf bytes.Buffer + + yamlmeta.NewYAMLPrinter(&buf).Print(output.DocSet.Items[0]) + + return buf.Bytes(), nil +} + + func generateWorkshopName(path string, workshop *unstructured.Unstructured, portal string) string { name := workshop.GetName() diff --git a/client-programs/pkg/educatesrestapi/catalog.go b/client-programs/pkg/educates/restapi/catalog.go similarity index 98% rename from client-programs/pkg/educatesrestapi/catalog.go rename to client-programs/pkg/educates/restapi/catalog.go index 745b3bd85..23664cf58 100644 --- a/client-programs/pkg/educatesrestapi/catalog.go +++ b/client-programs/pkg/educates/restapi/catalog.go @@ -1,4 +1,4 @@ -package educatesrestapi +package restapi import ( "bytes" @@ -12,12 +12,12 @@ import ( "strings" "time" - "github.com/pkg/errors" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + educatesTypes "github.com/educates/educates-training-platform/client-programs/pkg/educates/types" + "github.com/pkg/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" ) type AuthDetails struct { @@ -300,7 +300,7 @@ func (c *WorkshopsCatalogRequester) Login() (func(), error) { return nil, errors.Wrapf(err, "unable to create Kubernetes client") } - trainingPortalClient := dynamicClient.Resource(schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "trainingportals"}) + trainingPortalClient := dynamicClient.Resource(educatesTypes.TrainingPortalResource) var trainingPortal *unstructured.Unstructured var executions = 0 diff --git a/client-programs/pkg/educatesrestapi/types.go b/client-programs/pkg/educates/restapi/types.go similarity index 98% rename from client-programs/pkg/educatesrestapi/types.go rename to client-programs/pkg/educates/restapi/types.go index 1f7fb4cac..44c5c9f62 100644 --- a/client-programs/pkg/educatesrestapi/types.go +++ b/client-programs/pkg/educates/restapi/types.go @@ -1,4 +1,4 @@ -package educatesrestapi +package restapi // WorkshopCatalog // -------------------------------------------- diff --git a/client-programs/pkg/educates/types/educates.go b/client-programs/pkg/educates/types/educates.go new file mode 100644 index 000000000..88272d1fc --- /dev/null +++ b/client-programs/pkg/educates/types/educates.go @@ -0,0 +1,14 @@ +package types + +import "k8s.io/apimachinery/pkg/runtime/schema" + +var TrainingPortalResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "trainingportals"} +var WorkshopResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshops"} +var WorkshopsessionsResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshopsessions"} +var WorkshoprequestsResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshoprequests"} +var WorkshopenvironmentsResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshopenvironments"} +var WorkshopallocationsResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshopallocations"} +var SecretcopierResource = schema.GroupVersionResource{Group: "secrets.educates.dev", Version: "v1beta1", Resource: "secretcopiers"} +var SecretinjectorsResource = schema.GroupVersionResource{Group: "secrets.educates.dev", Version: "v1beta1", Resource: "secretinjectors"} +var SecretexportersResource = schema.GroupVersionResource{Group: "secrets.educates.dev", Version: "v1beta1", Resource: "secretexporters"} +var SecretimportersResource = schema.GroupVersionResource{Group: "secrets.educates.dev", Version: "v1beta1", Resource: "secretimporters"} diff --git a/client-programs/pkg/renderer/hugo.go b/client-programs/pkg/renderer/hugo.go index 03cc56186..19572b371 100644 --- a/client-programs/pkg/renderer/hugo.go +++ b/client-programs/pkg/renderer/hugo.go @@ -24,13 +24,13 @@ import ( "time" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + educatesTypes "github.com/educates/educates-training-platform/client-programs/pkg/educates/types" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" "gopkg.in/yaml.v2" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime/schema" ) //go:embed all:files/* @@ -119,8 +119,6 @@ type RunHugoServerConfig struct { CleanupFunc ServerCleanupFunc } -var workshopSessionResource = schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshopsessions"} - func fetchWorkshopSessionAndValidate(kubeconfig string, kubeContext string, workshop string, portal string, session string) (string, string, error) { // Returns session URL, config password and error. @@ -134,7 +132,7 @@ func fetchWorkshopSessionAndValidate(kubeconfig string, kubeContext string, work return "", "", errors.Wrapf(err, "unable to create Kubernetes client") } - workshopSessionClient := dynamicClient.Resource(workshopSessionResource) + workshopSessionClient := dynamicClient.Resource(educatesTypes.WorkshopsessionsResource) workshopSession, err := workshopSessionClient.Get(context.TODO(), session, metav1.GetOptions{}) diff --git a/client-programs/pkg/workshops/definition.go b/client-programs/pkg/workshops/definition.go deleted file mode 100644 index ab667774e..000000000 --- a/client-programs/pkg/workshops/definition.go +++ /dev/null @@ -1,233 +0,0 @@ -package workshops - -import ( - "bytes" - "context" - "crypto/sha1" - "fmt" - "io" - "log" - "net/http" - "net/url" - "os" - "path/filepath" - - yttcmd "carvel.dev/ytt/pkg/cmd/template" - yttcmdui "carvel.dev/ytt/pkg/cmd/ui" - "carvel.dev/ytt/pkg/files" - "carvel.dev/ytt/pkg/yamlmeta" - "github.com/educates/educates-training-platform/client-programs/pkg/constants" - "github.com/pkg/errors" - "k8s.io/apimachinery/pkg/apis/meta/internalversion/scheme" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "k8s.io/apimachinery/pkg/runtime" - "k8s.io/apimachinery/pkg/runtime/schema" - "k8s.io/apimachinery/pkg/runtime/serializer" - "k8s.io/apimachinery/pkg/types" - "k8s.io/client-go/dynamic" -) - - -func LoadWorkshopDefinition(name string, path string, portal string, workshopFile string, workshopVersion string, dataValueFlags yttcmd.DataValuesFlags) (*unstructured.Unstructured, error) { - // Parse the workshop location so we can determine if it is a local file - // or accessible using a HTTP/HTTPS URL. - - var urlInfo *url.URL - var err error - - if urlInfo, err = url.Parse(path); err != nil { - return nil, errors.Wrap(err, "unable to parse workshop location") - } - - // Check if file system path first (not HTTP/HTTPS) and if so normalize - // the path. If it the path references a directory, then extend the path - // so we look for the workshop file within that directory. - - if urlInfo.Scheme != "http" && urlInfo.Scheme != "https" { - path = filepath.Clean(path) - - if path, err = filepath.Abs(path); err != nil { - return nil, errors.Wrap(err, "couldn't convert workshop location to absolute path") - } - - if !filepath.IsAbs(workshopFile) { - fileInfo, err := os.Stat(path) - - if err != nil { - return nil, errors.Wrap(err, "couldn't test if workshop location is a directory") - } - - if fileInfo.IsDir() { - path = filepath.Join(path, workshopFile) - } - } else { - path = workshopFile - } - } - - // Read in the workshop definition as raw data ready for parsing. - - var workshopData []byte - - if urlInfo.Scheme != "http" && urlInfo.Scheme != "https" { - if workshopData, err = os.ReadFile(path); err != nil { - return nil, errors.Wrap(err, "couldn't read workshop definition data file") - } - } else { - var client http.Client - - resp, err := client.Get(path) - - if err != nil { - return nil, errors.Wrap(err, "couldn't download workshop definition from host") - } - - defer resp.Body.Close() - - if resp.StatusCode != http.StatusOK { - return nil, errors.New("failed to download workshop definition from host") - } - - workshopData, err = io.ReadAll(resp.Body) - - if err != nil { - return nil, errors.Wrap(err, "failed to read workshop definition from host") - } - } - - // Process the workshop YAML data in case it contains ytt templating. - - if workshopData, err = ProcessWorkshopDefinition(workshopData, dataValueFlags); err != nil { - return nil, errors.Wrap(err, "unable to process workshop definition as template") - } - - // Parse the workshop definition. - - decoder := serializer.NewCodecFactory(scheme.Scheme).UniversalDecoder() - - workshop := &unstructured.Unstructured{} - - err = runtime.DecodeInto(decoder, workshopData, workshop) - - if err != nil { - return nil, errors.Wrap(err, "couldn't parse workshop definition") - } - - // Verify the type of resource definition. - - if workshop.GetAPIVersion() != "training.educates.dev/v1beta1" || workshop.GetKind() != "Workshop" { - return nil, errors.New("invalid type for workshop definition") - } - - // Add annotations recording details about original workshop location. - - annotations := workshop.GetAnnotations() - - if annotations == nil { - annotations = map[string]string{} - } - - annotations["training.educates.dev/workshop"] = workshop.GetName() - - if urlInfo.Scheme != "http" && urlInfo.Scheme != "https" { - annotations["training.educates.dev/source"] = fmt.Sprintf("file://%s", path) - } else { - annotations["training.educates.dev/source"] = path - } - - workshop.SetAnnotations(annotations) - - // Update the name for the workshop such that it incorporates a hash of - // the workshop location. - - if name == "" { - name = GenerateWorkshopName(path, workshop, portal) - } - - workshop.SetName(name) - - // Insert workshop version property if not specified. - - _, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") - - if !found && workshopVersion != "latest" { - unstructured.SetNestedField(workshop.Object, workshopVersion, "spec", "version") - } - - // Remove the publish section as will not be accurate after publising. - - unstructured.RemoveNestedField(workshop.Object, "spec", "publish") - - return workshop, nil -} - -func GenerateWorkshopName(path string, workshop *unstructured.Unstructured, portal string) string { - name := workshop.GetName() - - h := sha1.New() - - io.WriteString(h, path) - - hv := fmt.Sprintf("%x", h.Sum(nil)) - - name = fmt.Sprintf("%s--%s-%s", portal, name, hv[len(hv)-7:]) - - return name -} - -func GetWorkshopResource() schema.GroupVersionResource { - return schema.GroupVersionResource{Group: "training.educates.dev", Version: "v1beta1", Resource: "workshops"} -} - -func UpdateWorkshopResource(client dynamic.Interface, workshop *unstructured.Unstructured) error { - workshopsClient := client.Resource(GetWorkshopResource()) - - // _, err := workshopsClient.Apply(context.TODO(), workshop.GetName(), workshop, metav1.ApplyOptions{FieldManager: workshops.DefaultPortalName, Force: true}) - - workshopBytes, err := runtime.Encode(unstructured.UnstructuredJSONScheme, workshop) - - if err != nil { - return errors.Wrapf(err, "unable to update workshop definition in cluster %q", workshop.GetName()) - } - - _, err = workshopsClient.Patch(context.TODO(), workshop.GetName(), types.ApplyPatchType, workshopBytes, metav1.ApplyOptions{FieldManager: constants.DefaultPortalName, Force: true}.ToPatchOptions()) - - if err != nil { - return errors.Wrapf(err, "unable to update workshop definition in cluster %q", workshop.GetName()) - } - - return nil -} - -func ProcessWorkshopDefinition(yamlData []byte, dataValueFlags yttcmd.DataValuesFlags) ([]byte, error) { - templatingOptions := yttcmd.NewOptions() - - templatingOptions.IgnoreUnknownComments = true - - templatingOptions.DataValuesFlags = dataValueFlags - - var filesToProcess []*files.File - - mainInputFile := files.MustNewFileFromSource(files.NewBytesSource("workshop.yaml", yamlData)) - - filesToProcess = append(filesToProcess, mainInputFile) - - logUI := yttcmdui.NewCustomWriterTTY(false, log.Writer(), log.Writer()) - - output := templatingOptions.RunWithFiles(yttcmd.Input{Files: filesToProcess}, logUI) - - if output.Err != nil { - return []byte{}, fmt.Errorf("execution of ytt failed: %s", output.Err) - } - - if len(output.DocSet.Items) == 0 { - return []byte{}, nil - } - - var buf bytes.Buffer - - yamlmeta.NewYAMLPrinter(&buf).Print(output.DocSet.Items[0]) - - return buf.Bytes(), nil -} From 5034b93edb6ca17f30e38c5d5ad85d046c05edc7 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Tue, 20 Jan 2026 17:52:58 +0100 Subject: [PATCH 11/24] Refactored docker_xxx commands --- client-programs/go.mod | 36 +- client-programs/go.sum | 221 +++-- .../pkg/cmd/docker_extension_backend_cmd.go | 235 +---- .../pkg/cmd/docker_workshop_delete_cmd.go | 91 +- .../pkg/cmd/docker_workshop_deploy_cmd.go | 815 +----------------- .../pkg/cmd/docker_workshop_list_cmd.go | 130 +-- .../pkg/cmd/docker_workshop_logs.go | 23 +- .../pkg/cmd/docker_workshop_open_cmd.go | 46 +- .../pkg/docker/extension_backend.go | 103 +++ .../pkg/docker/extension_backend_api.go | 145 ++++ .../pkg/docker/workshop_manager.go | 4 +- .../educates/resources/workshops/manager.go | 1 - go.work.sum | 47 +- 13 files changed, 575 insertions(+), 1322 deletions(-) create mode 100644 client-programs/pkg/docker/extension_backend.go create mode 100644 client-programs/pkg/docker/extension_backend_api.go diff --git a/client-programs/go.mod b/client-programs/go.mod index 1d88773a5..2bc00347c 100644 --- a/client-programs/go.mod +++ b/client-programs/go.mod @@ -2,12 +2,6 @@ module github.com/educates/educates-training-platform/client-programs go 1.24.10 -// replace cloud.google.com/go/compute/metadata => cloud.google.com/go/compute/metadata v0.2.3 - -// replace github.com/google/cel-go => github.com/google/cel-go v0.22.1 - -// replace github.com/docker/docker => github.com/docker/docker v27.5.1+incompatible - require ( carvel.dev/imgpkg v0.46.1 carvel.dev/kapp v0.64.2 @@ -17,9 +11,7 @@ require ( github.com/adrg/xdg v0.5.3 github.com/compose-spec/compose-go/v2 v2.10.0 github.com/cppforlife/go-cli-ui v0.0.0-20250603184554-47874c9078ad - // Every time we update below version, we need to update Docker Desktop client to match the required version - // or else downgrade CLI support via export DOCKER_API_VERSION=1.xx - // Version compabitility: https://github.com/moby/moby/blob/master/docs/api/version-history.md + // Docker packages must be kept aligned with docker/compose v5.0.1 requirements. This still relies on docker/docker v28.5.2 github.com/docker/docker v28.5.2+incompatible github.com/docker/go-connections v0.6.0 github.com/go-logr/logr v1.4.3 @@ -34,7 +26,6 @@ require ( k8s.io/apimachinery v0.34.2 k8s.io/cli-runtime v0.34.2 k8s.io/client-go v0.34.2 - k8s.io/controller-manager v0.33.5 // indirect k8s.io/klog/v2 v2.130.1 k8s.io/kubectl v0.34.2 sigs.k8s.io/controller-runtime v0.22.4 @@ -43,14 +34,15 @@ require ( ) require ( - github.com/docker/cli v29.0.0+incompatible + // Keep docker/cli aligned with docker/docker - see comment above + github.com/docker/cli v28.5.2+incompatible + // This still relies on docker/docker v28.5.2 so we need to align docker/docker and docker/cli to the same version github.com/docker/compose/v5 v5.0.1 go.yaml.in/yaml/v2 v2.4.3 ) require ( al.essio.dev/pkg/shellescape v1.6.0 // indirect - cel.dev/expr v0.25.1 // indirect cloud.google.com/go/compute/metadata v0.9.0 // indirect github.com/Azure/azure-sdk-for-go v68.0.0+incompatible // indirect github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c // indirect @@ -68,7 +60,6 @@ require ( github.com/Microsoft/go-winio v0.6.2 // indirect github.com/VividCortex/ewma v1.2.0 // indirect github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d // indirect - github.com/antlr4-go/antlr/v4 v4.13.1 // indirect github.com/aws/aws-sdk-go-v2 v1.39.6 // indirect github.com/aws/aws-sdk-go-v2/config v1.31.19 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.18.23 // indirect @@ -86,7 +77,6 @@ require ( github.com/aws/smithy-go v1.23.2 // indirect github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.11.0 // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/blang/semver/v4 v4.0.0 // indirect github.com/bmatcuk/doublestar v1.3.4 // indirect github.com/buger/goterm v1.0.4 // indirect github.com/carvel-dev/semver/v4 v4.0.1-0.20240402203627-beb83fbf25e4 // indirect @@ -114,8 +104,11 @@ require ( github.com/dimchansky/utfbom v1.1.1 // indirect github.com/distribution/reference v0.6.0 // indirect github.com/docker/buildx v0.30.1 // indirect + github.com/docker/cli-docs-tool v0.11.0 // indirect github.com/docker/distribution v2.8.3+incompatible // indirect github.com/docker/docker-credential-helpers v0.9.4 // indirect + github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c // indirect + github.com/docker/go-metrics v0.0.1 // indirect github.com/docker/go-units v0.5.0 // indirect github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 // indirect github.com/emicklei/go-restful/v3 v3.13.0 // indirect @@ -146,7 +139,6 @@ require ( github.com/golang-jwt/jwt/v4 v4.5.2 // indirect github.com/golang-jwt/jwt/v5 v5.3.0 // indirect github.com/golang/protobuf v1.5.4 // indirect - github.com/google/cel-go v0.26.1 // indirect github.com/google/gnostic-models v0.7.0 // indirect github.com/google/go-cmp v0.7.0 // indirect github.com/google/go-containerregistry v0.20.6 // indirect @@ -154,6 +146,7 @@ require ( github.com/google/go-querystring v1.1.0 // indirect github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 // indirect github.com/google/uuid v1.6.0 // indirect + github.com/gorilla/mux v1.8.1 // indirect github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 // indirect github.com/hashicorp/errwrap v1.1.0 // indirect github.com/hashicorp/go-multierror v1.1.1 // indirect @@ -172,14 +165,13 @@ require ( github.com/mattn/go-isatty v0.0.20 // indirect github.com/mattn/go-runewidth v0.0.19 // indirect github.com/mattn/go-shellwords v1.0.12 // indirect + github.com/miekg/pkcs11 v1.1.1 // indirect github.com/mitchellh/go-wordwrap v1.0.1 // indirect github.com/mitchellh/hashstructure/v2 v2.0.2 // indirect github.com/moby/buildkit v0.26.3 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/go-archive v0.1.0 // indirect github.com/moby/locker v1.0.1 // indirect - github.com/moby/moby/api v1.52.0 // indirect - github.com/moby/moby/client v0.2.1 // indirect github.com/moby/patternmatcher v0.6.0 // indirect github.com/moby/spdystream v0.5.0 // indirect github.com/moby/sys/atomicwriter v0.1.0 // indirect @@ -197,7 +189,6 @@ require ( github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f // indirect github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/image-spec v1.1.1 // indirect - github.com/openshift/crd-schema-checker v0.0.0-20250905140724-c313b6407231 // indirect github.com/otiai10/copy v1.14.1 // indirect github.com/otiai10/mint v1.6.3 // indirect github.com/pelletier/go-toml v1.9.5 // indirect @@ -214,8 +205,8 @@ require ( github.com/sirupsen/logrus v1.9.3 // indirect github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 // indirect github.com/spf13/pflag v1.0.10 // indirect - github.com/stoewer/go-strcase v1.3.1 // indirect github.com/stretchr/testify v1.11.1 // indirect + github.com/theupdateframework/notary v0.7.0 // indirect github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 // indirect github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 // indirect github.com/tonistiigi/fsutil v0.0.0-20250605211040-586307ad452f // indirect @@ -259,16 +250,9 @@ require ( gopkg.in/evanphx/json-patch.v4 v4.13.0 // indirect gopkg.in/inf.v0 v0.9.1 // indirect gopkg.in/ini.v1 v1.67.0 // indirect - gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect - k8s.io/apiextensions-apiserver v0.34.1 // indirect - k8s.io/apiserver v0.34.1 // indirect - k8s.io/component-base v0.34.2 // indirect - k8s.io/component-helpers v0.34.2 // indirect k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912 // indirect - k8s.io/kubernetes v1.34.2 // indirect k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 // indirect - sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 // indirect sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 // indirect sigs.k8s.io/randfill v1.0.0 // indirect sigs.k8s.io/structured-merge-diff/v6 v6.3.0 // indirect diff --git a/client-programs/go.sum b/client-programs/go.sum index fe2370772..e6f46cf93 100644 --- a/client-programs/go.sum +++ b/client-programs/go.sum @@ -10,8 +10,6 @@ carvel.dev/vendir v0.44.0 h1:vfq5KgGbbLlxHrE0prY7gZgiEQpjwo4lS2akCaVkcxA= carvel.dev/vendir v0.44.0/go.mod h1:gslrJ0HPiy8gtJYsQZHzIVuGfOG0nfDKDupEm7uBWVQ= carvel.dev/ytt v0.52.1 h1:I9rCwIunzClas2MH5nVGtCK5ujZdiGaqAfGol/wiRKQ= carvel.dev/ytt v0.52.1/go.mod h1:lzkMguCvSVvxT2My9RG3gRMgTws97NpNXufKZ6iiP5E= -cel.dev/expr v0.25.1 h1:1KrZg61W6TWSxuNZ37Xy49ps13NUovb66QLprthtwi4= -cel.dev/expr v0.25.1/go.mod h1:hrXvqGP6G6gyx8UAHSHJ5RGk//1Oj5nXQ2NI02Nrsg4= cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs= cloud.google.com/go/compute/metadata v0.9.0/go.mod h1:E0bWwX5wTnLPedCKqk3pJmVgCBSM6qQI1yTBdEb3C10= github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= @@ -46,6 +44,7 @@ github.com/Azure/go-autorest/logger v0.2.2/go.mod h1:I5fg9K52o+iuydlWfa9T5K6WFos github.com/Azure/go-autorest/tracing v0.6.0/go.mod h1:+vhtPC754Xsa23ID7GlGsrdKBpUA79WCAKPPZVC2DeU= github.com/Azure/go-autorest/tracing v0.6.1 h1:YUMSrC/CeD1ZnnXcNYU4a/fzsO35u2Fsful9L/2nyR0= github.com/Azure/go-autorest/tracing v0.6.1/go.mod h1:/3EgjbsjraOqiicERAeu3m7/z0x1TzjQGAwDrJrXGkc= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/BurntSushi/toml v1.5.0 h1:W5quZX/G/csjUnuI8SUYlsHs9M38FC7znL0lIO+DvMg= github.com/BurntSushi/toml v1.5.0/go.mod h1:ukJfTF/6rtPPRCnwkur4qwRxa8vTRFBF0uk2lLoLwho= github.com/DefangLabs/secret-detector v0.0.0-20250403165618-22662109213e h1:rd4bOvKmDIx0WeTv9Qz+hghsgyjikFiPrseXHlKepO0= @@ -56,16 +55,18 @@ github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERo github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= github.com/Microsoft/hcsshim v0.14.0-rc.1 h1:qAPXKwGOkVn8LlqgBN8GS0bxZ83hOJpcjxzmlQKxKsQ= github.com/Microsoft/hcsshim v0.14.0-rc.1/go.mod h1:hTKFGbnDtQb1wHiOWv4v0eN+7boSWAHyK/tNAaYZL0c= +github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d h1:hi6J4K6DKrR4/ljxn6SF6nURyu785wKMuQcjt7H3VCQ= +github.com/Shopify/logrus-bugsnag v0.0.0-20170309145241-6dbc35f2c30d/go.mod h1:HI8ITrYtUY+O+ZhtlqUnD8+KwNPOyugEhfP9fdUIaEQ= github.com/VividCortex/ewma v1.2.0 h1:f58SaIzcDXrSy3kWaHNvuJgJ3Nmz59Zji6XoJR/q1ow= github.com/VividCortex/ewma v1.2.0/go.mod h1:nz4BbCtbLyFDeC9SUHbtcT5644juEuWfUAUnGx7j5l4= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d h1:licZJFw2RwpHMqeKTCYkitsPqHNxTmd4SNR5r94FGM8= github.com/acarl005/stripansi v0.0.0-20180116102854-5a71ef0e047d/go.mod h1:asat636LX7Bqt5lYEZ27JNDcqxfjdBQuJ/MM4CN/Lzo= github.com/adrg/xdg v0.5.3 h1:xRnxJXne7+oWDatRhR1JLnvuccuIeCoBu2rtuLqQB78= github.com/adrg/xdg v0.5.3/go.mod h1:nlTsY+NNiCBGCK2tpm09vRqfVzrc2fLmXGpBLF0zlTQ= +github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092 h1:aM1rlcoLz8y5B2r4tTLMiVTrMtpfY0O8EScKJxaSaEc= github.com/anchore/go-struct-converter v0.0.0-20221118182256-c68fdcfa2092/go.mod h1:rYqSE9HbjzpHTI74vwPvae4ZVYZd1lue2ta6xHPdblA= -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/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= github.com/aws/aws-sdk-go-v2 v1.39.6 h1:2JrPCVgWJm7bm83BDwY5z8ietmeJUbh3O2ACnn+Xsqk= @@ -100,14 +101,24 @@ github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.11.0 h1:GOPttfOAf5qAgx7r6b+zCWZrvCsfKffkL4H6mSYx1kA= github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.11.0/go.mod h1:a2HN6+p7k0JLDO8514sMr0l4cnrR52z4sWoZ/Uc82ho= +github.com/beorn7/perks v0.0.0-20150223135152-b965b613227f/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24TaqPxmxbtue+5NUziq4I4S80YR8gNf3Q= +github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= 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= -github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= +github.com/bitly/go-hostpool v0.1.0/go.mod h1:4gOCgp6+NZnVqlKyZ/iBZFTAJKembaVENUpMkpg42fw= +github.com/bitly/go-simplejson v0.5.0/go.mod h1:cXHtHw4XUPsvGaxgjIAn8PhEWG9NfngEKAMDJEczWVA= github.com/bmatcuk/doublestar v1.3.4 h1:gPypJ5xD31uhX6Tf54sDPUOBXTqKH4c9aPY66CyQrS0= github.com/bmatcuk/doublestar v1.3.4/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= +github.com/bmizerany/assert v0.0.0-20160611221934-b7ed37b82869/go.mod h1:Ekp36dRnpXw/yCqJaO+ZrUyxD+3VXMFFr56k5XYrpB4= github.com/buger/goterm v1.0.4 h1:Z9YvGmOih81P0FbVtEYTFF6YsSgxSUKEhf/f9bTMXbY= github.com/buger/goterm v1.0.4/go.mod h1:HiFWV3xnkolgrBV3mY8m0X0Pumt4zg4QhbdOzQtB8tE= +github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0 h1:s7+5BfS4WFJoVF9pnB8kBk03S7pZXRdKamnV0FOl5Sc= +github.com/bugsnag/bugsnag-go v1.0.5-0.20150529004307-13fd6b8acda0/go.mod h1:2oa8nejYd4cQ/b0hMIopN0lCRxU0bueqREvZLWFrtK8= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b h1:otBG+dV+YK+Soembjv71DPz3uX/V/6MMlSyD9JBQ6kQ= +github.com/bugsnag/osext v0.0.0-20130617224835-0dd3f918b21b/go.mod h1:obH5gd0BsqsP2LwDJ9aOkm/6J86V6lyAXCoQWGw3K50= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0 h1:nvj0OLI3YqYXer/kZD8Ri1aaunCxIEsOst1BVJswV0o= +github.com/bugsnag/panicwrap v0.0.0-20151223152923-e2c28503fcd0/go.mod h1:D/8v3kj0zr8ZAKg1AQ6crr+5VwKN5eIywRkfhyM/+dE= github.com/carvel-dev/semver/v4 v4.0.1-0.20240402203627-beb83fbf25e4 h1:F4rZiMGZyC66j9VB7doVOE4tFHF1yNEihQlOuht4jmM= github.com/carvel-dev/semver/v4 v4.0.1-0.20240402203627-beb83fbf25e4/go.mod h1:4cFTBLAr/U11ykiEEQMccu4uJ1i0GS+atJmeETHCFtI= github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= @@ -125,6 +136,8 @@ github.com/clipperhouse/stringish v0.1.1 h1:+NSqMOr3GR6k1FdRhhnXrLfztGzuG+VuFDfa github.com/clipperhouse/stringish v0.1.1/go.mod h1:v/WhFtE1q0ovMta2+m+UbpZ+2/HEXNWYXQgCt4hdOzA= github.com/clipperhouse/uax29/v2 v2.3.0 h1:SNdx9DVUqMoBuBoW3iLOj4FQv3dN5mDtuqwuhIGpJy4= github.com/clipperhouse/uax29/v2 v2.3.0/go.mod h1:Wn1g7MK6OoeDT0vL+Q0SQLDz/KpfsVRgg6W7ihQeh4g= +github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e85keuznYcH5rqI438v41pKcBl4ZxQ= +github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= github.com/compose-spec/compose-go/v2 v2.10.0 h1:K2C5LQ3KXvkYpy5N/SG6kIYB90iiAirA9btoTh/gB0Y= @@ -159,10 +172,6 @@ github.com/containerd/ttrpc v1.2.7 h1:qIrroQvuOL9HQ1X6KHe2ohc7p+HP/0VE6XPU7elJRq github.com/containerd/ttrpc v1.2.7/go.mod h1:YCXHsb32f+Sq5/72xHubdiJRQY9inL4a4ZQrAbN1q9o= github.com/containerd/typeurl/v2 v2.2.3 h1:yNA/94zxWdvYACdYO8zofhrTVuQY73fFU1y++dYSw40= github.com/containerd/typeurl/v2 v2.2.3/go.mod h1:95ljDnPfD3bAbDJRugOiShd/DlAAsxGtUBhJxIn7SCk= -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.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo= -github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU= github.com/cppforlife/cobrautil v0.0.0-20221130162803-acdfead391ef h1:de10GNLe45JTMghl2qf9WH17H/BjGShK41X3vKAsPJA= github.com/cppforlife/cobrautil v0.0.0-20221130162803-acdfead391ef/go.mod h1:2w+qxVu2KSGW78Ex/XaIqfh/OvBgjEsmN53S4T8vEyA= github.com/cppforlife/color v1.9.1-0.20200716202919-6706ac40b835 h1:mYQweUIBD+TBRjIeQnJmXr0GSVMpI6O0takyb/aaOgo= @@ -172,12 +181,14 @@ github.com/cppforlife/go-cli-ui v0.0.0-20250603184554-47874c9078ad/go.mod h1:xZh github.com/cppforlife/go-patch v0.0.0-20240118020416-2147782e467b h1:+8LQctLhaj+63L/37l8IK/5Q3odN6RzWlglonUwrKok= github.com/cppforlife/go-patch v0.0.0-20240118020416-2147782e467b/go.mod h1:67a7aIi94FHDZdoeGSJRRFDp66l9MhaAG1yGxpUoFD8= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= +github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E= github.com/creack/pty v1.1.24 h1:bJrF4RRfyJnbTJqzRLHzcGaZK1NeM5kTC9jGgovnR1s= github.com/creack/pty v1.1.24/go.mod h1:08sCNb52WyoAwi2QDyzUCTgcvVFhUzewun7wtTfvcwE= 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/denisenkom/go-mssqldb v0.0.0-20191128021309-1d7a30a10f73/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= github.com/dimchansky/utfbom v1.1.1 h1:vV6w1AhK4VMnhBno/TPVCoK9U/LP0PkLCS9tbxHdi/U= github.com/dimchansky/utfbom v1.1.1/go.mod h1:SxdoEBH5qIqFocHMyGOXVAybYJdr71b1Q/j0mACtrfE= github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= @@ -186,26 +197,37 @@ github.com/dlclark/regexp2 v1.11.0 h1:G/nrcoOa7ZXlpoa/91N3X7mM3r8eIlMBBJZvsz/mxK github.com/dlclark/regexp2 v1.11.0/go.mod h1:DHkYz0B9wPfa6wondMfaivmHpzrQ3v9q8cnmRbL6yW8= github.com/docker/buildx v0.30.1 h1:3vthfaTQOLt5QfN2nl7rKuPLUvx69nL5ZikFIXp//c8= github.com/docker/buildx v0.30.1/go.mod h1:8nwT0V6UNYNo9rXq6WO/BQd9KrJ0JYcY/QX6x0y1Oro= -github.com/docker/cli v29.0.0+incompatible h1:KgsN2RUFMNM8wChxryicn4p46BdQWpXOA1XLGBGPGAw= -github.com/docker/cli v29.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli v28.5.2+incompatible h1:XmG99IHcBmIAoC1PPg9eLBZPlTrNUAijsHLm8PjhBlg= +github.com/docker/cli v28.5.2+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= +github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQtHZEcfE= +github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q= github.com/docker/compose/v5 v5.0.1 h1:5yCjDJbwUqcuI+6WNFHNWz2/3vyBDsNnfe8LlFjyxEc= github.com/docker/compose/v5 v5.0.1/go.mod h1:vuKBtnRuvsVIlYHzdPkF3SToljqR+pFJseO5lDBuF18= +github.com/docker/distribution v2.7.1+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/distribution v2.8.3+incompatible h1:AtKxIZ36LoNK51+Z6RpzLpddBirtxJnzDrHLEKxTAYk= github.com/docker/distribution v2.8.3+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v28.5.2+incompatible h1:DBX0Y0zAjZbSrm1uzOkdr1onVghKaftjlSWt4AFexzM= github.com/docker/docker v28.5.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.9.4 h1:76ItO69/AP/V4yT9V4uuuItG0B1N8hvt0T0c0NN/DzI= github.com/docker/docker-credential-helpers v0.9.4/go.mod h1:v1S+hepowrQXITkEfw6o4+BMbGot02wiKpzWhGUZK6c= +github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c h1:lzqkGL9b3znc+ZUgi7FlLnqjQhcXxkNM/quxIjBVMD0= +github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= +github.com/docker/go-connections v0.4.0/go.mod h1:Gbd7IOopHjR8Iph03tsViu4nIes5XhDvyHbTtUxmeec= github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-metrics v0.0.0-20180209012529-399ea8c73916/go.mod h1:/u0gXw0Gay3ceNrsHubL3BtdOL2fHf93USgMTe0W5dI= github.com/docker/go-metrics v0.0.1 h1:AgB/0SvBxihN0X8OR4SjsblXkbMvalQ8cjmtKQ2rQV8= github.com/docker/go-metrics v0.0.1/go.mod h1:cG1hvH2utMXtqgqqYE9plW6lDxS3/5ayHzueweSI3Vw= github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7 h1:UhxFibDNY/bfvqU5CAUmr9zpesgbU6SWc8/B4mflAE4= +github.com/docker/libtrust v0.0.0-20160708172513-aabc10ec26b7/go.mod h1:cyGadeNEkKy96OOhEzfZl+yxihPEzKnqJwvfuSUqbZE= +github.com/dvsekhvalnov/jose2go v0.0.0-20170216131308-f21a8cedbbae/go.mod h1:7BvyPhdbLxMXIYTFPLsyJRFMsKmOZnQmzh6Gb+uquuM= github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203 h1:XBBHcIb256gUJtLmY22n99HaZTz+r2Z51xUPi01m3wg= github.com/eiannone/keyboard v0.0.0-20220611211555-0d226195f203/go.mod h1:E1jcSv8FaEny+OP/5k9UxZVw9YFWGj7eI4KR/iOBqCg= github.com/emicklei/go-restful/v3 v3.13.0 h1:C4Bl2xDndpU6nJ4bc1jXd+uTmYPVUwkD6bFY/oTyCes= github.com/emicklei/go-restful/v3 v3.13.0/go.mod h1:6n3XBCmQQb25CM2LCACGz8ukIrRry+4bhvbpWn3mrbc= +github.com/erikstmartin/go-testdb v0.0.0-20160219214506-8d10e4a1bae5/go.mod h1:a2zkGnVExMxdzMo3M0Hi/3sEU+cWnZpSni0O6/Yb/P0= github.com/evanphx/json-patch/v5 v5.9.11 h1:/8HVnzMq13/3x9TPvjG08wUGqBTmZBsCWzjTM0wiaDU= github.com/evanphx/json-patch/v5 v5.9.11/go.mod h1:3j+LviiESTElxA4p3EMKAB9HXj3/XEtnUf6OZxqIQTM= github.com/fatih/color v1.18.0 h1:S8gINlzdQ840/4pfAwic/ZE0djQEH3wM94VfqLTZcOM= @@ -214,12 +236,16 @@ github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2 github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/fsnotify/fsevents v0.2.0 h1:BRlvlqjvNTfogHfeBOFvSC9N0Ddy+wzQCQukyoD7o/c= github.com/fsnotify/fsevents v0.2.0/go.mod h1:B3eEk39i4hz8y1zaWS/wPrAP4O6wkIl7HQwKBr1qH/w= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k= github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/fvbommel/sortorder v1.1.0 h1:fUmoe+HLsBTctBDoaBwpQo5N+nrCp8g/BjKb/6ZQmYw= github.com/fvbommel/sortorder v1.1.0/go.mod h1:uk88iVf1ovNn1iLfgUVU2F9o5eO30ui720w+kxuqRs0= github.com/fxamacker/cbor/v2 v2.9.0 h1:NpKPmjDBgUfBms6tr6JZkTHtfFGcMKsw3eGcmD/sapM= github.com/fxamacker/cbor/v2 v2.9.0/go.mod h1:vM4b+DJCtHn+zz7h3FFp/hDAI9WNWCsZj23V5ytsSxQ= +github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as= +github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE= +github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= @@ -257,12 +283,17 @@ github.com/go-openapi/swag/yamlutils v0.25.1 h1:mry5ez8joJwzvMbaTGLhw8pXUnhDK91o github.com/go-openapi/swag/yamlutils v0.25.1/go.mod h1:cm9ywbzncy3y6uPm/97ysW8+wZ09qsks+9RS8fLWKqg= github.com/go-openapi/testify/v2 v2.0.2 h1:X999g3jeLcoY8qctY/c/Z8iBHTbwLz7R2WXd6Ub6wls= github.com/go-openapi/testify/v2 v2.0.2/go.mod h1:HCPmvFFnheKK2BuwSA0TbbdxJ3I16pjwMkYkP4Ywn54= +github.com/go-sql-driver/mysql v1.3.0 h1:pgwjLi/dvffoP9aabwkT3AKpXQM93QARkjFhDDqC1UE= +github.com/go-sql-driver/mysql v1.3.0/go.mod h1:zAC/RDZ24gD3HViQzih4MyKcchzm+sOG5ZlKdlhCg5w= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= 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= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/gofrs/flock v0.13.0 h1:95JolYOvGMqeH31+FC7D2+uULf6mG61mEZ/A8dRYMzw= github.com/gofrs/flock v0.13.0/go.mod h1:jxeyy9R1auM5S6JYDBhDt+E2TCo7DkratH4Pgi8P+Z0= +github.com/gogo/protobuf v1.0.0/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= +github.com/gogo/protobuf v1.1.1/go.mod h1:r8qH/GZQm5c6nD/R0oafs1akxWv10x8SbQlK7atdtwQ= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v4 v4.0.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= @@ -272,16 +303,20 @@ github.com/golang-jwt/jwt/v4 v4.5.2 h1:YtQM7lnr8iZ+j5q71MGKkNw9Mn7AjHM68uc9g5fXe github.com/golang-jwt/jwt/v4 v4.5.2/go.mod h1:m21LjoU+eqJr34lmDMbreY2eSTRJ1cv77w39/MY0Ch0= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= +github.com/golang-sql/civil v0.0.0-20190719163853-cb61b32ac6fe/go.mod h1:8vg3r2VgvsThLBIFL93Qb5yWzgyZWhEmBwUJWevAkK0= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8 h1:f+oWsMOmNPc8JmEHVZIycC7hBoQxHH9pNKQORJNozsQ= github.com/golang/groupcache v0.0.0-20241129210726-2c02b8208cf8/go.mod h1:wcDNUvekVysuuOpQKo3191zZyTpiI6se1N1ULghS0sw= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.1/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.4/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= 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.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= -github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= -github.com/google/cel-go v0.26.1 h1:iPbVVEdkhTX++hpe3lzSk7D3G3QSYqLGoHOcEio+UXQ= -github.com/google/cel-go v0.26.1/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= +github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93 h1:jc2UWq7CbdszqeH6qu1ougXMIUBfSy8Pbh/anURYbGI= +github.com/google/certificate-transparency-go v1.0.10-0.20180222191210-5ab67e519c93/go.mod h1:QeJfpSbVSfYc7RgB3gJFj9cbuQMMchQxrWXz8Ruopmg= github.com/google/gnostic-models v0.7.0 h1:qwTtogB15McXDaNqTZdzPJRHvaVJlAl+HVQnLmJEJxo= github.com/google/gnostic-models v0.7.0/go.mod h1:whL5G0m6dmc5cPxKc5bdKdEN3UjI7OUGxBlw57miDrQ= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -300,14 +335,15 @@ github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510 h1:El6M4kTTCOh6aBiKaU github.com/google/shlex v0.0.0-20191202100458-e7afc7fbc510/go.mod h1:pupxD2MaaD3pAXIBCelhxNneeOaAeabZDe5s4K6zSpQ= github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.7.0/go.mod h1:1lud6UwP+6orDFRuTfBEV8e9/aOM/c4fVVCaMa2zaAs= github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674 h1:JeSE6pjso5THxAzdVpqr6/geYxZytqFMBCOtn/ujyeo= github.com/gorilla/websocket v1.5.4-0.20250319132907-e064f32e3674/go.mod h1:r4w70xmWCQKmi1ONH4KIaBptdivuRPyosB9RmPlGEwA= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= -github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgfV/d3M/q6VIi02HzZEHgUlZvzk= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3 h1:NmZ1PKzSTQbuGHw9DGPFomqkkLWMC+vZCkfs+FHv1Vg= github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.3/go.mod h1:zQrxl1YP88HQlA6i9c63DSVPFklWpGX4OWAc9bFuaH4= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed h1:5upAirOpQc1Q53c0bnx2ufif5kANL7bfZWcc6VJWJd8= +github.com/hailocab/go-hostpool v0.0.0-20160125115350-e80d13ce29ed/go.mod h1:tMWxXQ9wFIaZeTI9F+hmhFiGpFmhOHzyShyFUhRm0H4= github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I= github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4= @@ -315,18 +351,31 @@ github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+l github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM= github.com/hashicorp/go-version v1.8.0 h1:KAkNb1HAiZd1ukkxDFGmokVZe1Xy9HG6NUp+bPle2i4= github.com/hashicorp/go-version v1.8.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= github.com/in-toto/in-toto-golang v0.9.0 h1:tHny7ac4KgtsfrG6ybU8gVOZux2H8jN05AXJ9EBM1XU= github.com/in-toto/in-toto-golang v0.9.0/go.mod h1:xsBVrVsHNsB61++S6Dy2vWosKhuA3lUTQd+eF9HdeMo= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8= github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw= github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf h1:FtEj8sfIcaaBfAKrE1Cwb61YDtYq9JxChK1c7AKce7s= github.com/inhies/go-bytesize v0.0.0-20220417184213-4913239db9cf/go.mod h1:yrqSXGoD/4EKfF26AOGzscPOgTTJcyAwM2rpixWT+t4= +github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8 h1:CZkYfurY6KGhVtlalI4QwQ6T0Cu6iuY3e0x5RLu96WE= +github.com/jinzhu/gorm v0.0.0-20170222002820-5409931a1bb8/go.mod h1:Vla75njaFJ8clLU1W44h34PjIkijhjHIYnZxMqCdxqo= +github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d h1:jRQLvyVGL+iVtDElaEIDdKwpPqUIZJfzkNLV34htpEc= +github.com/jinzhu/inflection v0.0.0-20170102125226-1c35d901db3d/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= +github.com/jinzhu/now v1.1.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4= github.com/jonboulle/clockwork v0.5.0 h1:Hyh9A8u51kptdkR+cqRpT1EebBwTn1oK9YfGYbdFz6I= github.com/jonboulle/clockwork v0.5.0/go.mod h1:3mZlmanh0g2NDKO5TWZVJAfofYk64M7XN3SzBPjZF60= +github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= +github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM= github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo= +github.com/juju/loggo v0.0.0-20190526231331-6e530bcce5d8/go.mod h1:vgyd7OREkbtVEN/8IXZe5Ooef3LQePvuBm9UWj6ZL8U= +github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= github.com/k14s/difflib v0.0.0-20240118055029-596a7a5585c3 h1:q2ikACDbDDbyUcN9JkDcNMGhIx1EBRkctAsPZMr35qM= github.com/k14s/difflib v0.0.0-20240118055029-596a7a5585c3/go.mod h1:B0xN2MiNBGWOWi9CcfAo9LBI8IU4J1utlbOIJCsmKr4= github.com/k14s/starlark-go v0.0.0-20200720175618-3a5c849cc368 h1:4bcRTTSx+LKSxMWibIwzHnDNmaN1x52oEpvnjCy+8vk= @@ -337,16 +386,26 @@ github.com/kisielk/errcheck v1.5.0/go.mod h1:pFxgyoBC7bSaBwPgfKdkLd5X25qrDl4LWUI github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= github.com/klauspost/compress v1.18.1 h1:bcSGx7UbpBqMChDtsF28Lw6v/G94LPrrbMbdC3JH2co= github.com/klauspost/compress v1.18.1/go.mod h1:ZQFFVG+MdnR0P+l6wpXgIL4NTtwiKIdBnrBd8Nrxr+0= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= 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/pty v1.1.8 h1:AkaSdXYQOWeaO3neb8EM634ahkXXe3jYbVh/F9lq+GI= github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +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/lib/pq v0.0.0-20150723085316-0dad96c0b94f/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de h1:9TO3cAIGXtEhnIaL+V+BEER86oLrvS+kWobKpbJuye0= github.com/liggitt/tabwriter v0.0.0-20181228230101-89fcab3d43de/go.mod h1:zAbeS9B/r2mtpb6U+EI2rYA5OAXxsYw6wTamcNW+zcE= +github.com/magiconair/properties v1.5.3/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/magiconair/properties v1.8.9 h1:nWcCbLq1N2v/cpNsy5WvQ37Fb+YElfq20WJ/a8RkpQM= +github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mattn/go-colorable v0.1.4/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE= github.com/mattn/go-colorable v0.1.14 h1:9A9LHSqF/7dyVVX6g0U9cwm9pG3kP9gSzcuIPHPsaIE= github.com/mattn/go-colorable v0.1.14/go.mod h1:6LmQG8QLFO4G5z1gPvYEzlUgJ2wF+stgPZH1UqBm1s8= @@ -358,12 +417,20 @@ github.com/mattn/go-runewidth v0.0.19 h1:v++JhqYnZuu5jSKrk9RbgF5v4CGUjqRfBm05byF github.com/mattn/go-runewidth v0.0.19/go.mod h1:XBkDxAl56ILZc9knddidhrOlY5R/pDhgLpndooCuJAs= github.com/mattn/go-shellwords v1.0.12 h1:M2zGm7EW6UQJvDeQxo4T51eKPurbeFbe8WtebGE2xrk= github.com/mattn/go-shellwords v1.0.12/go.mod h1:EZzvwXDESEeg03EKmM+RmDnNOPKG4lLtQsUlTZDWQ8Y= +github.com/mattn/go-sqlite3 v1.6.0/go.mod h1:FPy6KqzDD04eiIsT53CuJW3U88zkxoIYsOqkbpncsNc= +github.com/matttproud/golang_protobuf_extensions v1.0.1/go.mod h1:D8He9yQNgCq6Z5Ld7szi9bcBfOoFv/3dc6xSMkL2PC0= +github.com/miekg/pkcs11 v1.0.2/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= +github.com/miekg/pkcs11 v1.1.1 h1:Ugu9pdy6vAYku5DEpVWVFPYnzV+bxB+iRdbuFSu7TvU= +github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= 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/mitchellh/go-wordwrap v1.0.1 h1:TLuKupo69TCn6TQSyGxwI1EblZZEsQ0vMlAFQflz0v0= github.com/mitchellh/go-wordwrap v1.0.1/go.mod h1:R62XHJLzvMFRBbcrT7m7WgmE1eOyTSsCt+hzestvNj0= github.com/mitchellh/hashstructure/v2 v2.0.2 h1:vGKWl0YJqUNxE8d+h8f6NJLcCJrgbhC4NcD46KavDd4= github.com/mitchellh/hashstructure/v2 v2.0.2/go.mod h1:MG3aRVU/N29oo/V/IhBX8GR/zz4kQkprJgF2EVszyDE= +github.com/mitchellh/mapstructure v0.0.0-20150613213606-2caf8efc9366/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/mitchellh/mapstructure v1.5.0 h1:jeMsZIYE/09sWLaz43PL7Gy6RuMjD2eJVyuac5Z2hdY= +github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/buildkit v0.26.3 h1:D+ruZVAk/3ipRq5XRxBH9/DIFpRjSlTtMbghT5gQP9g= github.com/moby/buildkit v0.26.3/go.mod h1:4T4wJzQS4kYWIfFRjsbJry4QoxDBjK+UGOEOs1izL7w= github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= @@ -372,10 +439,6 @@ github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= github.com/moby/locker v1.0.1 h1:fOXqR41zeveg4fFODix+1Ch4mj/gT0NE1XJbp/epuBg= github.com/moby/locker v1.0.1/go.mod h1:S7SDdo5zpBK84bzzVlKr2V0hz+7x9hWbYC/kq7oQppc= -github.com/moby/moby/api v1.52.0 h1:00BtlJY4MXkkt84WhUZPRqt5TvPbgig2FZvTbe3igYg= -github.com/moby/moby/api v1.52.0/go.mod h1:8mb+ReTlisw4pS6BRzCMts5M49W5M7bKt1cJy/YbAqc= -github.com/moby/moby/client v0.2.1 h1:1Grh1552mvv6i+sYOdY+xKKVTvzJegcVMhuXocyDz/k= -github.com/moby/moby/client v0.2.1/go.mod h1:O+/tw5d4a1Ha/ZA/tPxIZJapJRUS6LNZ1wiVRxYHyUE= github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= github.com/moby/spdystream v0.5.0 h1:7r0J1Si3QO/kjRitvSLVVFUjxMEb/YLj6S9FF62JBCU= @@ -401,6 +464,8 @@ github.com/moby/term v0.5.2/go.mod h1:d3djjFCrjnB+fl8NJux+EJzu0msscUP+f8it8hPkFL 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= +github.com/modern-go/reflect2 v0.0.0-20180701023420-4b7aa43c6742/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= +github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3RllmbCylyMrvgv0= github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee h1:W5t00kpgFdJifH4BDsTlE89Zl93FEloxaWZfGcifgq8= github.com/modern-go/reflect2 v1.0.3-0.20250322232337-35a7c28c31ee/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= @@ -408,26 +473,34 @@ github.com/morikuni/aec v1.1.0 h1:vBBl0pUnvi/Je71dsRrhMBtreIqNMYErSAbEeb8jrXQ= github.com/morikuni/aec v1.1.0/go.mod h1:xDRgiq/iw5l+zkao76YTKzKttOp2cwPEne25HDkJnBw= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq1c1nUAm88MOHcQC9l5mIlSMApZMrHA= github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= +github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/nxadm/tail v1.4.8 h1:nPr65rt6Y5JFSKQO7qToXr7pePgD6Gwiw05lkbyAQTE= github.com/nxadm/tail v1.4.8/go.mod h1:+ncqLTQzXmGhMZNUePPaPqPvBxHAIsmXswZKocGu+AU= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.12.0/go.mod h1:oUhWkIvk5aDxtKvDDuw8gItl8pKl42LzjC9KZE0HfGg= github.com/onsi/ginkgo v1.16.4 h1:29JGrr5oVBm5ulCWet69zQkzWipVXIol6ygQUe/EzNc= github.com/onsi/ginkgo v1.16.4/go.mod h1:dX+/inL/fNMqNlz0e9LfyB9TswhZpCVdJM/Z6Vvnwo0= github.com/onsi/ginkgo/v2 v2.23.0 h1:FA1xjp8ieYDzlgS5ABTpdUDB7wtngggONc8a7ku2NqQ= github.com/onsi/ginkgo/v2 v2.23.0/go.mod h1:zXTP6xIp3U8aVuXN8ENK9IXRaTjFnpVB9mGmaSRvxnM= +github.com/onsi/gomega v1.7.1/go.mod h1:XdKZgCCFLUoM/7CFJVPcG8C1xQ1AJ0vpAezJrB7JYyY= +github.com/onsi/gomega v1.9.0/go.mod h1:Ho0h+IUsWyvy1OpqCwxlQ/21gkhVunqlU8fDGcoTdcA= github.com/onsi/gomega v1.36.2 h1:koNYke6TVk6ZmnyHrCXba/T/MoLBXFjeC1PtvYgw0A8= github.com/onsi/gomega v1.36.2/go.mod h1:DdwyADRjrc825LhMEkD76cHR5+pUnjhUN8GlHlRPHzY= +github.com/opencontainers/go-digest v0.0.0-20170106003457-a6d0ee40d420/go.mod h1:cMLVZDEM3+U2I4VmLI6N8jQYUd2OVphdqWwCJHrFt2s= 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.0.1/go.mod h1:BtxoFyWECRxE4U/7sNtV5W15zMzWCbyJoFRP3s7yZA0= github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= github.com/opencontainers/runtime-spec v1.3.0 h1:YZupQUdctfhpZy3TM39nN9Ika5CBWT5diQ8ibYCRkxg= github.com/opencontainers/runtime-spec v1.3.0/go.mod h1:jwyrGlmzljRJv/Fgzds9SsS/C5hL+LL3ko9hs6T5lQ0= github.com/opencontainers/selinux v1.12.0 h1:6n5JV4Cf+4y0KNXW48TLj5DwfXpvWlxXplUkdTrmPb8= github.com/opencontainers/selinux v1.12.0/go.mod h1:BTPX+bjVbWGXw7ZZWUbdENt8w0htPSrlgOOysQaU62U= -github.com/openshift/crd-schema-checker v0.0.0-20250905140724-c313b6407231 h1:8lSGufji9rfiyDxtUl7A4uOyeeP4x0UOOXcsDBFfkGI= -github.com/openshift/crd-schema-checker v0.0.0-20250905140724-c313b6407231/go.mod h1:sTxJ4ZFW9r9fEdbW2v0yMRi6NcyTbx0fII4p83IQ+L8= +github.com/opentracing/opentracing-go v1.1.0 h1:pWlfV3Bxv7k65HYwkikxat0+s3pV4bsqf19k25Ur8rU= +github.com/opentracing/opentracing-go v1.1.0/go.mod h1:UkNAQd3GIcIGf0SeVgPpRdFStlNbqXla1AfSYxPUl2o= github.com/otiai10/copy v1.14.1 h1:5/7E6qsUMBaH5AnQ0sSLzzTg1oTECmcCmT6lvF45Na8= github.com/otiai10/copy v1.14.1/go.mod h1:oQwrEDDOci3IM8dJF0d8+jnbfPDllW6vUjNc3DoZm9I= github.com/otiai10/mint v1.6.3 h1:87qsV/aw1F5as1eH1zS/yqHY85ANKVMgkDrf9rcxbQs= @@ -436,6 +509,8 @@ github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3v github.com/pelletier/go-toml v1.9.5/go.mod h1:u1nR/EPcESfeI/szUZKdtJ0xRNbUoANCkoOuaOx1Y+c= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5 h1:Ii+DKncOVM8Cu1Hc+ETb5K+23HdAMvESYE3ZJ5b5cMI= github.com/phayes/freeport v0.0.0-20220201140144-74d24b5ae9f5/go.mod h1:iIss55rKnNBTvrwdmkUpLnDpZoAHvWaiq5+iMmen4AE= +github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10 h1:GFCKgmp0tecUJ0sJuv4pzYCqS9+RGSn52M3FUwPs+uo= @@ -443,12 +518,26 @@ github.com/planetscale/vtprotobuf v0.6.1-0.20240319094008-0393e58bdf10/go.mod h1 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 v0.9.0-pre1.0.20180209125602-c332b6f63c06/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v0.9.1/go.mod h1:7SWBe2y4D6OKWSNQJUaRYU/AaXPKyh/dDVn+NZz0KFw= +github.com/prometheus/client_golang v1.0.0/go.mod h1:db9x61etRT2tGnBNRi70OPL5FsnadC4Ky3P0J6CfImo= +github.com/prometheus/client_golang v1.1.0/go.mod h1:I1FGZT9+L76gKKOs5djB6ezCbFQP1xR9D75/vuwEF3g= github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o= github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg= +github.com/prometheus/client_model v0.0.0-20171117100541-99fa1f4be8e5/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20180712105110-5c3871d89910/go.mod h1:MbSGuTsp3dbXC40dX6PRTWyKYBIrTGTE9sqQNg2J8bo= +github.com/prometheus/client_model v0.0.0-20190129233127-fd36f4220a90/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= 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.0.0-20180110214958-89604d197083/go.mod h1:daVV7qP5qjZbuso7PdcryaAu0sAZbrN9i7WWcTMWvro= +github.com/prometheus/common v0.4.1/go.mod h1:TNfzLD0ON7rHzMJeJkieUDPYmFC7Snx/y86RQel1bk4= +github.com/prometheus/common v0.6.0/go.mod h1:eBmuwkDJBwy6iBfxCBob6t6dR6ENT/y+J+Zk0j9GMYc= github.com/prometheus/common v0.67.2 h1:PcBAckGFTIHt2+L3I33uNRTlKTplNzFctXcWhPyAEN8= github.com/prometheus/common v0.67.2/go.mod h1:63W3KZb1JOKgcjlIr64WW/LvFGAqKPj0atm+knVGEko= +github.com/prometheus/procfs v0.0.0-20180125133057-cb4147076ac7/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.0-20181005140218-185b4288413d/go.mod h1:c3At6R/oaqEKCNdg8wHV1ftS6bRYblBhIjjI8uT2IGk= +github.com/prometheus/procfs v0.0.2/go.mod h1:TjEm7ze935MbeOT/UhFTIMYKhuLP4wbCsTZCD3I8kEA= +github.com/prometheus/procfs v0.0.3/go.mod h1:4A/X28fw3Fc593LaREMrKMqOKvUAntwMDaekg4FpcdQ= github.com/prometheus/procfs v0.19.2 h1:zUMhqEW66Ex7OXIiDkll3tl9a1ZdilUOd/F6ZXw4Vws= github.com/prometheus/procfs v0.19.2/go.mod h1:M0aotyiemPhBCM0z5w87kL22CxfcH05ZpYlu+b4J7mw= github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= @@ -461,32 +550,54 @@ github.com/secure-systems-lab/go-securesystemslib v0.9.1 h1:nZZaNz4DiERIQguNy0cL github.com/secure-systems-lab/go-securesystemslib v0.9.1/go.mod h1:np53YzT0zXGMv6x4iEWc9Z59uR+x+ndLwCLqPYpLXVU= github.com/shibumi/go-pathspec v1.3.0 h1:QUyMZhFo0Md5B8zV8x2tesohbb5kfbpTi9rBnKh5dkI= github.com/shibumi/go-pathspec v1.3.0/go.mod h1:Xutfslp817l2I1cZvgcfeMQJG5QnU2lh5tVaaMCl3jE= +github.com/sirupsen/logrus v1.0.6/go.mod h1:pMByvHTf9Beacp5x1UXfOR9xyW/9antXMhjMPG0dEzc= +github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966 h1:JIAuq3EEf9cgbU6AtGPK4CTG3Zf6CKMNqf0MHTggAUA= github.com/skratchdot/open-golang v0.0.0-20200116055534-eef842397966/go.mod h1:sUM3LWHvSMaG192sy56D9F7CNvL7jUJVXoqM1QKLnog= github.com/spdx/tools-golang v0.5.5 h1:61c0KLfAcNqAjlg6UNMdkwpMernhw3zVRwDZ2x9XOmk= github.com/spdx/tools-golang v0.5.5/go.mod h1:MVIsXx8ZZzaRWNQpUDhC4Dud34edUYJYecciXgrw5vE= +github.com/spf13/afero v1.6.0 h1:xoax2sJ2DT8S8xA2paPFjDCScCNeWsg75VG0DLRreiY= +github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= +github.com/spf13/cast v0.0.0-20150508191742-4d07383ffe94/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg= +github.com/spf13/cast v1.3.1 h1:nFm6S0SMdyzrzcmThSipiEubIDy8WEXKNZ0UOgiRpng= +github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.1/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= github.com/spf13/cobra v1.10.2 h1:DMTTonx5m65Ic0GOoRY2c16WCbHxOOw6xxezuLaBpcU= github.com/spf13/cobra v1.10.2/go.mod h1:7C1pvHqHw5A4vrJfjNwvOdzYu0Gml16OCs2GRiTUUS4= +github.com/spf13/jwalterweatherman v0.0.0-20141219030609-3d60171a6431/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/jwalterweatherman v1.1.0 h1:ue6voC5bR5F8YxI5S67j9i582FU4Qvo2bmqnqMYADFk= +github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= +github.com/spf13/pflag v1.0.0/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= github.com/spf13/pflag v1.0.9/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= github.com/spf13/pflag v1.0.10 h1:4EBh2KAYBwaONj6b2Ye1GiHfwjqyROoF4RwYO+vPwFk= github.com/spf13/pflag v1.0.10/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= -github.com/stoewer/go-strcase v1.3.1 h1:iS0MdW+kVTxgMoE1LAZyMiYJFKlOzLooE4MxjirtkAs= -github.com/stoewer/go-strcase v1.3.1/go.mod h1:fAH5hQ5pehh+j3nZfvwdk2RgEgQjAoM8wodgtPmh1xo= +github.com/spf13/viper v0.0.0-20150530192845-be5ff3e4840c/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM= +github.com/spf13/viper v1.8.1 h1:Kq1fyeebqsBfbjZj4EL7gj2IO0mMaiyjYUWcUsl2O44= +github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= 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= 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.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +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= github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/subosito/gotenv v1.2.0 h1:Slr1R9HxAlEKefgq5jn9U+DnETlIUa6HfgEzj0g5d7s= +github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= +github.com/theupdateframework/notary v0.7.0 h1:QyagRZ7wlSpjT5N2qQAh/pN+DVqgekv4DzbAiAiEL3c= +github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw= github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375 h1:QB54BJwA6x8QU9nHY3xJSZR2kX9bgpZekRKGkLTmEXA= github.com/tilt-dev/fsnotify v1.4.8-0.20220602155310-fff9c274a375/go.mod h1:xRroudyp5iVtxKqZCrA6n2TLFRBf8bmnjr1UD4x+z7g= github.com/tonistiigi/dchapes-mode v0.0.0-20250318174251-73d941a28323 h1:r0p7fK56l8WPequOaR3i9LBqfPtEdXIQbUTzT55iqT4= @@ -512,12 +623,6 @@ github.com/xhit/go-str2duration/v2 v2.1.0/go.mod h1:ohY8p+0f07DiV6Em5LKB0s2YpLtX github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= -go.etcd.io/etcd/api/v3 v3.6.4 h1:7F6N7toCKcV72QmoUKa23yYLiiljMrT4xCeBL9BmXdo= -go.etcd.io/etcd/api/v3 v3.6.4/go.mod h1:eFhhvfR8Px1P6SEuLT600v+vrhdDTdcfMzmnxVXXSbk= -go.etcd.io/etcd/client/pkg/v3 v3.6.4 h1:9HBYrjppeOfFjBjaMTRxT3R7xT0GLK8EJMVC4xg6ok0= -go.etcd.io/etcd/client/pkg/v3 v3.6.4/go.mod h1:sbdzr2cl3HzVmxNw//PH7aLGVtY4QySjQFuaCgcRFAI= -go.etcd.io/etcd/client/v3 v3.6.4 h1:YOMrCfMhRzY8NgtzUsHl8hC2EBSnuqbR3dh84Uryl7A= -go.etcd.io/etcd/client/v3 v3.6.4/go.mod h1:jaNNHCyg2FdALyKWnd7hxZXZxZANb0+KGY+YQaEMISo= go.opencensus.io v0.24.0 h1:y73uSU6J157QMP2kn2r30vwW1A2W2WFwSCGnAVxeaD0= go.opencensus.io v0.24.0/go.mod h1:vNK8G9p7aAivkbmorf4v+7Hgx+Zs0yY+0fOtgBfjQKo= go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= @@ -554,19 +659,19 @@ go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= go.uber.org/mock v0.6.0 h1:hyF9dfmbgIX5EfOdasqLsWD6xqpNZlXblLB/Dbnwv3Y= go.uber.org/mock v0.6.0/go.mod h1:KiVJ4BqZJaMj4svdfmHM0AUx4NJYO8ZNpPnZn1Z+BBU= -go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= -go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= -go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= -go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= go.yaml.in/yaml/v2 v2.4.3 h1:6gvOSjQoTB3vt1l+CU+tSyi/HOjfOjRLJ4YwYZGwRO0= go.yaml.in/yaml/v2 v2.4.3/go.mod h1:zSxWcmIDjOzPXpjlTTbAsKokqkDNAVtZO0WOMiT90s8= go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go.yaml.in/yaml/v4 v4.0.0-rc.3 h1:3h1fjsh1CTAPjW7q/EMe+C8shx5d8ctzZTrLcs/j8Go= go.yaml.in/yaml/v4 v4.0.0-rc.3/go.mod h1:aZqd9kCMsGL7AuUv/m/PvWLdg5sjJsZ4oHDEnfPPfY0= +golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/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-20200302210943-78000ba7a073/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201117144127-c1f2f97bffc9/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= @@ -580,7 +685,10 @@ golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91 golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190613194153-d28f0bde5980/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= 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= @@ -593,6 +701,9 @@ golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= golang.org/x/oauth2 v0.33.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= 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= @@ -600,11 +711,16 @@ golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.19.0 h1:vV+1eWNmZ5geRlYjzm2adRgW2/mcpevXNg50YZtPCE4= golang.org/x/sync v0.19.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/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-20190801041406-cbf593c0f2f3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191002063906-3421d5a6bb1c/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191120155948-bd437916bb0e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210331175145-43e1dd70ce54/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= @@ -622,6 +738,7 @@ golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/sys v0.39.0 h1:CvCKL8MeisomCi6qNZ+wbb0DN9E5AATixKsvNtMoMFk= golang.org/x/sys v0.39.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= @@ -658,21 +775,35 @@ google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217 h1: google.golang.org/genproto/googleapis/api v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:+rXWjjaukWZun3mLfjmVnQi18E1AsFbDN9QdJ5YXLto= google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217 h1:gRkg/vSppuSQoDjxyiGfN4Upv/h/DQmIR10ZU8dh4Ww= google.golang.org/genproto/googleapis/rpc v0.0.0-20251202230838-ff82c1b0f217/go.mod h1:7i2o+ce6H/6BluujYR+kqX3GKH+dChPTQU19wjRPiGk= +google.golang.org/grpc v1.0.5/go.mod h1:yo6s7OP7yaDglbqo1J04qKzAhqBH6lvTonzMVmEdcZw= google.golang.org/grpc v1.77.0 h1:wVVY6/8cGA6vvffn+wWK5ToddbgdU3d8MNENr4evgXM= google.golang.org/grpc v1.77.0/go.mod h1:z0BY1iVj0q8E1uSQCjL9cppRj+gnZjzDnzV0dHhrNig= google.golang.org/protobuf v1.36.10 h1:AYd7cD/uASjIL6Q9LiTjz8JLcrh/88q5UObnmY3aOOE= google.golang.org/protobuf v1.36.10/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/airbrake/gobrake.v2 v2.0.9/go.mod h1:/h5ZAUhDkGaJfjzjKLSjv6zCL6O0LLBxU4K+aSYdM/U= +gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= +gopkg.in/cenkalti/backoff.v2 v2.2.1 h1:eJ9UAg01/HIHG987TwxvnzK2MgxXq97YY6rYDpY9aII= +gopkg.in/cenkalti/backoff.v2 v2.2.1/go.mod h1:S0QdOvT2AlerfSBkp0O+dk+bbIMaNbEmVk876gPCthU= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20200227125254-8fa46927fb4f/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= gopkg.in/evanphx/json-patch.v4 v4.13.0 h1:czT3CmqEaQ1aanPc5SdlgQrrEIb8w/wwCvWWnfEbYzo= gopkg.in/evanphx/json-patch.v4 v4.13.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= +gopkg.in/gemnasium/logrus-airbrake-hook.v2 v2.1.2/go.mod h1:Xk6kEKp8OKb+X14hQBKWaSkCsqBpgog8nAV2xsGOxlo= gopkg.in/inf.v0 v0.9.1 h1:73M5CoZyi3ZLMOyDlQh031Cx6N9NDJ2Vvfl76EDAgDc= gopkg.in/inf.v0 v0.9.1/go.mod h1:cWUDdTG/fYaXco+Dcufb5Vnc6Gp2YChqWtbxRZE0mXw= gopkg.in/ini.v1 v1.67.0 h1:Dgnx+6+nfE+IfzjUEISNeydPJh9AXNNsWbGP9KzCsOA= gopkg.in/ini.v1 v1.67.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= +gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1 h1:d4KQkxAaAiRY2h5Zqis161Pv91A37uZyJOx73duwUwM= +gopkg.in/rethinkdb/rethinkdb-go.v6 v6.2.1/go.mod h1:WbjuEoo1oadwzQ4apSDU+JTvmllEHtsNHS6y7vFc7iw= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= +gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v2 v2.2.4/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= @@ -682,36 +813,20 @@ gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA= k8s.io/api v0.34.2 h1:fsSUNZhV+bnL6Aqrp6O7lMTy6o5x2C4XLjnh//8SLYY= k8s.io/api v0.34.2/go.mod h1:MMBPaWlED2a8w4RSeanD76f7opUoypY8TFYkSM+3XHw= -k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= -k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= k8s.io/apimachinery v0.34.2 h1:zQ12Uk3eMHPxrsbUJgNF8bTauTVR2WgqJsTmwTE/NW4= k8s.io/apimachinery v0.34.2/go.mod h1:/GwIlEcWuTX9zKIg2mbw0LRFIsXwrfoVxn+ef0X13lw= -k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA= -k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0= k8s.io/cli-runtime v0.34.2 h1:cct1GEuWc3IyVT8MSCoIWzRGw9HJ/C5rgP32H60H6aE= k8s.io/cli-runtime v0.34.2/go.mod h1:X13tsrYexYUCIq8MarCBy8lrm0k0weFPTpcaNo7lms4= k8s.io/client-go v0.34.2 h1:Co6XiknN+uUZqiddlfAjT68184/37PS4QAzYvQvDR8M= k8s.io/client-go v0.34.2/go.mod h1:2VYDl1XXJsdcAxw7BenFslRQX28Dxz91U9MWKjX97fE= -k8s.io/component-base v0.34.2 h1:HQRqK9x2sSAsd8+R4xxRirlTjowsg6fWCPwWYeSvogQ= -k8s.io/component-base v0.34.2/go.mod h1:9xw2FHJavUHBFpiGkZoKuYZ5pdtLKe97DEByaA+hHbM= -k8s.io/component-helpers v0.34.2 h1:RIUGDdU+QFzeVKLZ9f05sXTNAtJrRJ3bnbMLrogCrvM= -k8s.io/component-helpers v0.34.2/go.mod h1:pLi+GByuRTeFjjcezln8gHL7LcT6HImkwVQ3A2SQaEE= -k8s.io/controller-manager v0.33.5 h1:abmssknXnhOhW533583v2SYQObD5RhYiSL7Za1rezGM= -k8s.io/controller-manager v0.33.5/go.mod h1:KuQeAlf4vI2+qj5fwPVLaDlbtrTBA/8L/LqQvI74Ow0= 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-20250910181357-589584f1c912 h1:Y3gxNAuB0OBLImH611+UDZcmKS3g6CthxToOb37KgwE= k8s.io/kube-openapi v0.0.0-20250910181357-589584f1c912/go.mod h1:kdmbQkyfwUagLfXIad1y2TdrjPFWp2Q89B3qkRwf/pQ= k8s.io/kubectl v0.34.2 h1:+fWGrVlDONMUmmQLDaGkQ9i91oszjjRAa94cr37hzqA= k8s.io/kubectl v0.34.2/go.mod h1:X2KTOdtZZNrTWmUD4oHApJ836pevSl+zvC5sI6oO2YQ= -k8s.io/kubernetes v1.34.2 h1:WQdDvYJazkmkwSncgNwGvVtaCt4TYXIU3wSMRgvp3MI= -k8s.io/kubernetes v1.34.2/go.mod h1:m6pZk6a179pRo2wsTiCPORJ86iOEQmfIzUvtyEF8BwA= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4 h1:SjGebBtkBqHFOli+05xYbK8YF1Dzkbzn+gDM4X9T4Ck= k8s.io/utils v0.0.0-20251002143259-bc988d571ff4/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= -pgregory.net/rapid v1.2.0 h1:keKAYRcjm+e1F0oAuU5F5+YPAWcyxNNRK2wud503Gnk= -pgregory.net/rapid v1.2.0/go.mod h1:PY5XlDGj0+V1FCq0o192FdRhpKHGTRIWBgqjDBTrq04= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0 h1:hSfpvjjTQXQY2Fol2CS0QHMNs/WI1MOSGzCm1KhM5ec= -sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.34.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327UfMq9A= sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= diff --git a/client-programs/pkg/cmd/docker_extension_backend_cmd.go b/client-programs/pkg/cmd/docker_extension_backend_cmd.go index bc1e28d6e..19d42d188 100644 --- a/client-programs/pkg/cmd/docker_extension_backend_cmd.go +++ b/client-programs/pkg/cmd/docker_extension_backend_cmd.go @@ -1,234 +1,19 @@ package cmd import ( - "context" - "encoding/json" - "fmt" - "net" - "net/http" - "os" - "os/signal" - "regexp" - "strconv" - "strings" - "syscall" - - "github.com/pkg/errors" + "github.com/educates/educates-training-platform/client-programs/pkg/docker" "github.com/spf13/cobra" ) +const dockerExtensionBackendExample = ` +# Start the backend server on a Unix socket +docker extension backend --socket /run/guest-services/backend.sock +` + type DockerExtensionBackendOptions struct { Socket string } -type DockerWorkshopsBackend struct { - Manager DockerWorkshopsManager - ImageRepository string - ImageVersion string -} - -func NewDockerWorkshopsBackend(version string, imageRepository string) DockerWorkshopsBackend { - return DockerWorkshopsBackend{ - Manager: NewDockerWorkshopsManager(), - ImageRepository: imageRepository, - ImageVersion: version, - } -} - -func (b *DockerWorkshopsBackend) ListWorkhops(w http.ResponseWriter, r *http.Request) { - workshops, err := b.Manager.ListWorkhops() - - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - jsonData, err := json.Marshal(workshops) - - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - - w.WriteHeader(http.StatusOK) - w.Write(jsonData) -} - -func (b *DockerWorkshopsBackend) DeployWorkshop(w http.ResponseWriter, r *http.Request) { - queryParams := r.URL.Query() - - url := queryParams.Get("url") - - if url == "" { - http.Error(w, "workshop definition url required", http.StatusBadRequest) - return - } - - portString := queryParams.Get("port") - - if portString == "" { - portString = "10081" - } - - port, err := strconv.Atoi(portString) - - if err != nil || port <= 0 { - http.Error(w, "invalid workshop port supplied", http.StatusBadRequest) - return - } - - o := DockerWorkshopDeployOptions{ - Path: url, - Host: "127.0.0.1", - Port: uint(port), - LocalRepository: "localhost:5001", - DisableOpenBrowser: false, - ImageRepository: b.ImageRepository, - ImageVersion: b.ImageVersion, - Cluster: "", - KubeConfig: "", - Assets: "", - } - - name, err := b.Manager.DeployWorkshop(&o, os.Stdout, os.Stderr) - - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - sessionUrl := fmt.Sprintf("http://workshop.%s.nip.io:%d", strings.ReplaceAll(o.Host, ".", "-"), o.Port) - - workshop := DockerWorkshopDetails{ - Name: name, - Url: sessionUrl, - Source: url, - Status: "Started", - } - - jsonData, err := json.Marshal(workshop) - - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - - w.WriteHeader(http.StatusOK) - w.Write(jsonData) -} - -func (b *DockerWorkshopsBackend) DeleteWorkshop(w http.ResponseWriter, r *http.Request) { - queryParams := r.URL.Query() - - name := queryParams.Get("name") - - if name == "" { - http.Error(w, "workshop session name required", http.StatusBadRequest) - return - } - - err := b.Manager.DeleteWorkshop(name, os.Stdout, os.Stderr) - - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - workshop := DockerWorkshopDetails{ - Name: name, - Status: "Stopped", - } - - jsonData, err := json.Marshal(workshop) - - if err != nil { - http.Error(w, err.Error(), http.StatusInternalServerError) - return - } - - w.Header().Set("Content-Type", "application/json") - - w.WriteHeader(http.StatusOK) - w.Write(jsonData) -} - -func (o *DockerExtensionBackendOptions) Run(p *ProjectInfo) error { - if o.Socket == "" { - return errors.New("invalid socket for HTTP server") - } - - router := http.NewServeMux() - - versionHandler := func(w http.ResponseWriter, r *http.Request) { - fmt.Fprintln(w, p.Version) - } - - router.HandleFunc("/version", versionHandler) - - backend := NewDockerWorkshopsBackend(p.Version, p.ImageRepository) - - router.HandleFunc("/workshop/list", backend.ListWorkhops) - router.HandleFunc("/workshop/deploy", backend.DeployWorkshop) - router.HandleFunc("/workshop/delete", backend.DeleteWorkshop) - - server := http.Server{ - Handler: router, - } - - // The socket string can either be of the form host:nnn, or it can be a file - // system path (absolute or relative). In the first case we start up a - // normal HTTP server accepting connections over an INET socket connection. - // In the second case connections will be accepted over a UNIX socket. - - inetRegexPattern := `^([a-zA-Z0-9.-]+):(\d+)$` - - match, err := regexp.MatchString(inetRegexPattern, o.Socket) - - if err != nil { - return errors.Wrap(err, "failed to perform regex match on socket") - } - - var listener net.Listener - - if match { - listener, err = net.Listen("tcp", o.Socket) - - if err != nil { - return errors.Wrap(err, "unable to create INET HTTP server socket") - } - } else { - listener, err = net.Listen("unix", o.Socket) - - if err != nil { - return errors.Wrap(err, "unable to create UNIX HTTP server socket") - } - - defer os.Remove(o.Socket) - } - - defer listener.Close() - - go func() { - server.Serve(listener) - }() - - sigChan := make(chan os.Signal, 1) - signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) - <-sigChan - - err = server.Shutdown(context.TODO()) - - if err != nil { - return errors.Wrap(err, "failed to shutdown HTTP server") - } - - return nil -} - func (p *ProjectInfo) NewDockerExtensionBackendCmd() *cobra.Command { var o DockerExtensionBackendOptions @@ -236,7 +21,13 @@ func (p *ProjectInfo) NewDockerExtensionBackendCmd() *cobra.Command { Args: cobra.NoArgs, Use: "backend", Short: "Docker desktop extension backend", - RunE: func(_ *cobra.Command, _ []string) error { return o.Run(p) }, + RunE: func(_ *cobra.Command, _ []string) error { + dockerExtensionBackend := docker.NewDockerExtensionBackend(p.Version, p.ImageRepository) + return dockerExtensionBackend.Run(&docker.DockerExtensionBackendConfig{ + Socket: o.Socket, + }) + }, + Example: dockerExtensionBackendExample, } c.Flags().StringVar( diff --git a/client-programs/pkg/cmd/docker_workshop_delete_cmd.go b/client-programs/pkg/cmd/docker_workshop_delete_cmd.go index e524f4e4e..48fff07ca 100644 --- a/client-programs/pkg/cmd/docker_workshop_delete_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_delete_cmd.go @@ -1,23 +1,24 @@ package cmd import ( - "context" - "fmt" - "io" - "os" - "os/exec" - "path" - yttcmd "carvel.dev/ytt/pkg/cmd/template" - "github.com/docker/docker/client" "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/docker" "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" - "github.com/educates/educates-training-platform/client-programs/pkg/utils" - "github.com/pkg/errors" "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) +const dockerWorkshopDeleteExample = ` + # Delete Educates workshop from Docker in current workshop directory and using default workshop file + educates docker workshop delete + + # Delete Educates workshop from Docker from specific portal + educates docker workshop delete --portal my-portal + + # Delete Educates workshop from Docker defined with custom path and workshop file + educates docker workshop delete --path ./workshop --workshop-file custom-workshop.yaml +` + type DockerWorkshopDeleteOptions struct { Name string Path string @@ -26,58 +27,7 @@ type DockerWorkshopDeleteOptions struct { DataValuesFlags yttcmd.DataValuesFlags } -func (m *DockerWorkshopsManager) DeleteWorkshop(name string, stdout io.Writer, stderr io.Writer) error { - m.SetWorkshopStatus(name, "", "", "Stopping") - - defer m.ClearWorkshopStatus(name) - - dockerCommand := exec.Command( - "docker", - "compose", - "--project-name", - name, - "rm", - "--stop", - "--force", - "--volumes", - ) - - dockerCommand.Stdout = stdout - dockerCommand.Stderr = stderr - - err := dockerCommand.Run() - - if err != nil { - return errors.Wrap(err, "unable to stop workshop") - } - - ctx := context.Background() - - cli, err := client.NewClientWithOpts(client.FromEnv) - - if err != nil { - return errors.Wrap(err, "unable to create docker client") - } - - err = cli.VolumeRemove(ctx, fmt.Sprintf("%s_workshop", name), false) - - if err != nil { - return errors.Wrap(err, "unable to delete workshop volume") - } - - configFileDir := utils.GetEducatesHomeDir() - workshopConfigDir := path.Join(configFileDir, "workshops", name) - composeConfigDir := path.Join(configFileDir, "compose", name) - - os.RemoveAll(workshopConfigDir) - os.RemoveAll(composeConfigDir) - - return nil -} - func (o *DockerWorkshopDeleteOptions) Run(cmd *cobra.Command) error { - var err error - var name = o.Name if name == "" { @@ -87,32 +37,28 @@ func (o *DockerWorkshopDeleteOptions) Run(cmd *cobra.Command) error { // the workshop will then expect the workshop definition to reside in the // resources/workshop.yaml file under the directory, the same as if a // directory path was provided explicitly. - if path == "" { path = "." } // Load the workshop definition. The path can be a HTTP/HTTPS URL for a // local file system path for a directory or file. - - var workshop *unstructured.Unstructured - - definitionConfig := workshops.WorkshopDefinitionConfig{ + workshop, err := workshops.LoadWorkshopDefinition(&workshops.WorkshopDefinitionConfig{ Name: o.Name, Path: path, Portal: constants.DefaultPortalName, WorkshopFile: o.WorkshopFile, WorkshopVersion: o.WorkshopVersion, DataValueFlags: o.DataValuesFlags, - } - if workshop, err = workshops.LoadWorkshopDefinition(&definitionConfig); err != nil { + }) + if err != nil { return err } name = workshop.GetName() } - dockerWorkshopsManager := NewDockerWorkshopsManager() + dockerWorkshopsManager := docker.NewDockerWorkshopsManager() return dockerWorkshopsManager.DeleteWorkshop(name, cmd.OutOrStdout(), cmd.OutOrStderr()) } @@ -125,6 +71,7 @@ func (p *ProjectInfo) NewDockerWorkshopDeleteCmd() *cobra.Command { Use: "delete", Short: "Delete workshop from Docker", RunE: func(cmd *cobra.Command, _ []string) error { return o.Run(cmd) }, + Example: dockerWorkshopDeleteExample, } c.Flags().StringVarP( @@ -134,6 +81,8 @@ func (p *ProjectInfo) NewDockerWorkshopDeleteCmd() *cobra.Command { "", "name to be used for the workshop definition, generated if not set", ) + + // TODO: Move "." to a constant c.Flags().StringVarP( &o.Path, "file", @@ -142,6 +91,7 @@ func (p *ProjectInfo) NewDockerWorkshopDeleteCmd() *cobra.Command { "path to local workshop directory, definition file, or URL for workshop definition file", ) + // TODO: Move "resources/workshop.yaml" to a constant c.Flags().StringVar( &o.WorkshopFile, "workshop-file", @@ -149,6 +99,7 @@ func (p *ProjectInfo) NewDockerWorkshopDeleteCmd() *cobra.Command { "location of the workshop definition file", ) + // TODO: Move "latest" to a constant c.Flags().StringVar( &o.WorkshopVersion, "workshop-version", diff --git a/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go b/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go index c98a09cd9..aaff367fb 100644 --- a/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go @@ -1,36 +1,41 @@ package cmd import ( - "bytes" - "context" "fmt" "io" "net/http" - "os" - "os/exec" - "path" - "path/filepath" - "runtime" "strings" - "text/template" "time" yttcmd "carvel.dev/ytt/pkg/cmd/template" - composeloader "github.com/compose-spec/compose-go/v2/loader" - composetypes "github.com/compose-spec/compose-go/v2/types" - "github.com/docker/docker/client" - "github.com/educates/educates-training-platform/client-programs/pkg/constants" - "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" + "github.com/educates/educates-training-platform/client-programs/pkg/docker" "github.com/educates/educates-training-platform/client-programs/pkg/utils" - "github.com/pkg/errors" "github.com/spf13/cobra" - "golang.org/x/exp/slices" - "gopkg.in/yaml.v2" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" - "sigs.k8s.io/kind/pkg/cluster" - "sigs.k8s.io/kind/pkg/cmd" ) +const dockerWorkshopDeployExample = ` + # Deploy Educates workshop to Docker in current workshop directory and using default workshop file + educates docker workshop deploy + + # Deploy Educates workshop to Docker from specific path and using custom workshop file + educates docker workshop deploy --path ./workshop --workshop-file custom-workshop.yaml + + # Deploy Educates workshop to Docker with custom host and port + educates docker workshop deploy --host 192.168.1.100 --port 10081 + + # Deploy Educates workshop to Docker with custom local repository + educates docker workshop deploy --local-repository localhost:5001 + + # Deploy Educates workshop adding to the session kubeconfig to specified Kind cluster + educates docker workshop deploy --cluster my-cluster + + # Deploy Educates workshop adding to the specified kubeconfig to the session + educates docker workshop deploy --kubeconfig /path/to/kubeconfig + + # Deploy Educates workshop in current folder without opening the browser + educates docker workshop deploy --disable-open-browser +` + type DockerWorkshopDeployOptions struct { Path string Host string @@ -48,393 +53,26 @@ type DockerWorkshopDeployOptions struct { DataValuesFlags yttcmd.DataValuesFlags } -const containerScript = `exec bash -s << "EOF" -mkdir -p /opt/eduk8s/config -cat > /opt/eduk8s/config/workshop.yaml << "EOS" -{{ .WorkshopConfig -}} -EOS -{{ if .Assets -}} -cat > /opt/eduk8s/config/vendir-assets-01.yaml << "EOS" -apiVersion: vendir.k14s.io/v1alpha1 -kind: Config -directories: -- path: /opt/assets/files - contents: - - directory: - path: /opt/eduk8s/mnt/assets - path: . -EOS -{{ else -}} -{{ range $k, $v := .VendirFilesConfig -}} -{{ $off := inc $k -}} -cat > /opt/eduk8s/config/vendir-assets-{{ printf "%02d" $off }}.yaml << "EOS" -{{ $v -}} -EOS -{{ end -}} -{{ end -}} -{{ if .VendirPackagesConfig -}} -cat > /opt/eduk8s/config/vendir-packages.yaml << "EOS" -{{ .VendirPackagesConfig -}} -EOS -{{ end -}} -{{ if .KubeConfig -}} -mkdir -p /opt/kubeconfig -cat > /opt/kubeconfig/config << "EOS" -{{ .KubeConfig -}} -EOS -{{ end -}} -exec start-container -EOF -` - -func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployOptions, stdout io.Writer, stderr io.Writer) (string, error) { - var err error - - // If path not provided assume the current working directory. When loading - // the workshop will then expect the workshop definition to reside in the - // resources/workshop.yaml file under the directory, the same as if a - // directory path was provided explicitly. - - if o.Path == "" { - o.Path = "." - } - - // Load the workshop definition. The path can be a HTTP/HTTPS URL for a - // local file system path for a directory or file. - - var workshop *unstructured.Unstructured +func (o *DockerWorkshopDeployOptions) Run(cmd *cobra.Command) error { + dockerWorkshopsManager := docker.NewDockerWorkshopsManager() - definitionConfig := workshops.WorkshopDefinitionConfig{ - Name: "", + config := docker.DockerWorkshopDeployConfig{ Path: o.Path, - Portal: constants.DefaultPortalName, - WorkshopFile: o.WorkshopFile, - WorkshopVersion: o.WorkshopVersion, - DataValueFlags: o.DataValuesFlags, - } - if workshop, err = workshops.LoadWorkshopDefinition(&definitionConfig); err != nil { - return "", err - } - - name := workshop.GetName() - - m.SetWorkshopStatus(name, "", o.Path, "Starting") - - defer m.ClearWorkshopStatus(name) - - originalName := workshop.GetAnnotations()["training.educates.dev/workshop"] - - configFileDir := utils.GetEducatesHomeDir() - composeConfigDir := path.Join(configFileDir, "compose", name) - - err = os.MkdirAll(composeConfigDir, os.ModePerm) - - if err != nil { - return name, errors.Wrapf(err, "unable to create workshops compose directory") - } - - ctx := context.Background() - - cli, err := client.NewClientWithOpts(client.FromEnv) - - if err != nil { - return name, errors.Wrap(err, "unable to create docker client") - } - - _, err = cli.ContainerInspect(ctx, name) - - if err == nil { - return name, errors.New("this workshop is already running") - } - - registryNetwork := false - - if o.LocalRepository == "localhost:5001" { - o.LocalRepository = "registry.docker.local:5000" - } - - var registryIP string - - registryInfo, err := cli.ContainerInspect(ctx, "educates-registry") - - if err == nil { - educatesNetwork, exists := registryInfo.NetworkSettings.Networks["educates"] - - if !exists { - return name, errors.New("registry is not attached to educates network") - } - - registryNetwork = true - registryIP = educatesNetwork.IPAddress - } else { - o.LocalRepository = "" - } - - var kubeConfigData string - - if o.KubeConfig != "" { - kubeConfigBytes, err := os.ReadFile(o.KubeConfig) - - if err != nil { - return name, errors.Wrap(err, "unable to read kubeconfig file") - } - - kubeConfigData = string(kubeConfigBytes) - } - - if o.Cluster != "" { - kubeConfigData, err = generateClusterKubeconfig(o.Cluster) - - if err != nil { - return name, err - } - } - - var workshopConfigData string - var vendirFilesConfigData []string - var vendirPackagesConfigData string - var workshopImageName string - - var workshopPortsConfig []composetypes.ServicePortConfig - var workshopVolumesConfig []composetypes.ServiceVolumeConfig - - var workshopEnvironment []string - var workshopLabels map[string]string - var workshopExtraHosts map[string]string - - var workshopComposeProject *composetypes.Project - - if workshopConfigData, err = generateWorkshopConfig(workshop); err != nil { - return name, err - } - - if vendirFilesConfigData, err = generateVendirFilesConfig(workshop, originalName, o.LocalRepository, o.WorkshopVersion); err != nil { - return name, err - } - - if vendirPackagesConfigData, err = generateVendirPackagesConfig(workshop, originalName, o.LocalRepository, o.WorkshopVersion); err != nil { - return name, err - } - - if workshopImageName, err = generateWorkshopImageName(workshop, o.LocalRepository, o.ImageRepository, o.ImageVersion, o.WorkshopImage, o.WorkshopVersion); err != nil { - return name, err - } - - if workshopPortsConfig, err = composetypes.ParsePortConfig(fmt.Sprintf("%s:%d:10081", o.Host, o.Port)); err != nil { - return name, errors.Wrap(err, "unable to generate workshop ports config") - } - - if workshopVolumesConfig, err = generateWorkshopVolumeMounts(workshop, o.Assets); err != nil { - return name, err - } - - if workshopEnvironment, err = generateWorkshopEnvironment(workshop, o.LocalRepository, o.Host, o.Port); err != nil { - return name, err - } - - if workshopLabels, err = generateWorkshopLabels(workshop, o.Host, o.Port); err != nil { - return name, err - } - - if registryIP != "" { - if workshopExtraHosts, err = generateWorkshopExtraHosts(workshop, registryIP); err != nil { - return name, err - } - } - - if workshopComposeProject, err = extractWorkshopComposeConfig(workshop); err != nil { - return name, err - } - - type TemplateInputs struct { - WorkshopConfig string - VendirFilesConfig []string - VendirPackagesConfig string - KubeConfig string - Assets string - } - - inputs := TemplateInputs{ - WorkshopConfig: workshopConfigData, - VendirFilesConfig: vendirFilesConfigData, - VendirPackagesConfig: vendirPackagesConfigData, - KubeConfig: kubeConfigData, - Assets: o.Assets, - } - - funcMap := template.FuncMap{ - "inc": func(i int) int { - return i + 1 - }, - } - - containerScriptTemplate, err := template.New("entrypoint").Funcs(funcMap).Parse(containerScript) - - if err != nil { - return name, errors.Wrap(err, "not able to parse container script template") - } - - var containerScriptData bytes.Buffer - - err = containerScriptTemplate.Execute(&containerScriptData, inputs) - - if err != nil { - return name, errors.Wrap(err, "not able to generate container script") - } - - networks := map[string]*composetypes.ServiceNetworkConfig{ - "default": {}, - } - - if registryNetwork { - networks["educates"] = &composetypes.ServiceNetworkConfig{} - } - - var extraHostsList composetypes.HostsList - if len(workshopExtraHosts) > 0 { - extraHostsList = make(composetypes.HostsList, len(workshopExtraHosts)) - for hostname, ip := range workshopExtraHosts { - extraHostsList[hostname] = []string{ip} - } - } - - workshopServiceConfig := composetypes.ServiceConfig{ - Name: "workshop", - Image: workshopImageName, - Command: composetypes.ShellCommand([]string{"bash", "-c", containerScriptData.String()}), - User: "1001:0", - Ports: workshopPortsConfig, - Volumes: workshopVolumesConfig, - Environment: composetypes.NewMappingWithEquals(workshopEnvironment), - Labels: composetypes.Labels(workshopLabels), - ExtraHosts: extraHostsList, - DependsOn: composetypes.DependsOnConfig{}, - Networks: networks, - } - - if o.Cluster != "" { - workshopServiceConfig.Networks["kind"] = &composetypes.ServiceNetworkConfig{} - } - - dockerEnabled, found, _ := unstructured.NestedBool(workshop.Object, "spec", "session", "applications", "docker", "enabled") - - if found && dockerEnabled { - extraServices, _, _ := unstructured.NestedMap(workshop.Object, "spec", "session", "applications", "docker", "compose") - - socketEnabledDefault := true - - if len(extraServices) != 0 { - socketEnabledDefault = false - } - - socketEnabled, found, _ := unstructured.NestedBool(workshop.Object, "spec", "session", "applications", "docker", "socket", "enabled") - - if !found { - socketEnabled = socketEnabledDefault - } - - if socketEnabled { - workshopServiceConfig.GroupAdd = []string{"docker"} - } - } - - workshopServices := composetypes.Services{ - "workshop": workshopServiceConfig, - } - - composeConfig := composetypes.Project{ - Name: originalName, - Services: workshopServices, - Networks: composetypes.Networks{ - "educates": composetypes.NetworkConfig{Name: "educates", External: true}, - }, - Volumes: composetypes.Volumes{ - "workshop": composetypes.VolumeConfig{}, - }, - } - - if workshopComposeProject != nil { - for serviceName, extraService := range workshopComposeProject.Services { - extraService.Ports = []composetypes.ServicePortConfig{} - - composeConfig.Services[serviceName] = extraService - - workshopServiceConfig.DependsOn[serviceName] = composetypes.ServiceDependency{ - Condition: composetypes.ServiceConditionStarted, - } - } - - for volumeName, extraVolume := range workshopComposeProject.Volumes { - if volumeName != "workshop" { - composeConfig.Volumes[volumeName] = extraVolume - } - } - } - - if o.Cluster != "" { - composeConfig.Networks["kind"] = composetypes.NetworkConfig{Name: "kind", External: true} - } - - composeConfigBytes, err := yaml.Marshal(&composeConfig) - - if err != nil { - return name, errors.Wrap(err, "failed to generate compose config") + Host: o.Host, + Port: o.Port, + LocalRepository: o.LocalRepository, + DisableOpenBrowser: o.DisableOpenBrowser, + ImageRepository: o.ImageRepository, + ImageVersion: o.ImageVersion, } - - composeConfigFilePath := path.Join(composeConfigDir, "docker-compose.yaml") - - composeConfigFile, err := os.OpenFile(composeConfigFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) - - if err != nil { - return name, errors.Wrapf(err, "unable to create workshop config file %s", composeConfigFilePath) - } - - if _, err = composeConfigFile.Write(composeConfigBytes); err != nil { - return name, errors.Wrapf(err, "unable to write workshop config file %s", composeConfigFilePath) - } - - if err := composeConfigFile.Close(); err != nil { - return name, errors.Wrapf(err, "unable to close workshop config file %s", composeConfigFilePath) - } - - dockerCommand := exec.Command( - "docker", - "compose", - "--project-directory", - composeConfigDir, - "--file", - composeConfigFilePath, - "--project-name", - name, - "up", - "--detach", - "--renew-anon-volumes", - ) - - dockerCommand.Stdout = stdout - dockerCommand.Stderr = stderr - - err = dockerCommand.Run() - - if err != nil { - return name, errors.Wrap(err, "unable to start workshop") - } - - return name, nil -} - -func (o *DockerWorkshopDeployOptions) Run(cmd *cobra.Command) error { - dockerWorkshopsManager := NewDockerWorkshopsManager() - - _, err := dockerWorkshopsManager.DeployWorkshop(o, cmd.OutOrStdout(), cmd.OutOrStderr()) + _, err := dockerWorkshopsManager.DeployWorkshop(&config, cmd.OutOrStdout(), cmd.OutOrStderr()) if err != nil { return err } - // XXX Need a better way of handling very long startup times for container + // TODO: XXX Need a better way of handling very long startup times for container // due to workshop content or package downloads. - url := fmt.Sprintf("http://workshop.%s.nip.io:%d", strings.ReplaceAll(o.Host, ".", "-"), o.Port) if !o.DisableOpenBrowser { @@ -453,20 +91,7 @@ func (o *DockerWorkshopDeployOptions) Run(cmd *cobra.Command) error { break } - switch runtime.GOOS { - case "linux": - err = exec.Command("xdg-open", url).Start() - case "windows": - err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() - case "darwin": - err = exec.Command("open", url).Start() - default: - err = fmt.Errorf("unsupported platform") - } - - if err != nil { - return errors.Wrap(err, "unable to open web browser") - } + return utils.OpenBrowser(url) } return nil @@ -480,6 +105,7 @@ func (p *ProjectInfo) NewDockerWorkshopDeployCmd() *cobra.Command { Use: "deploy", Short: "Deploy workshop to Docker", RunE: func(cmd *cobra.Command, _ []string) error { return o.Run(cmd) }, + Example: dockerWorkshopDeployExample, } c.Flags().StringVarP( @@ -605,370 +231,3 @@ func (p *ProjectInfo) NewDockerWorkshopDeployCmd() *cobra.Command { return c } - -func generateWorkshopConfig(workshop *unstructured.Unstructured) (string, error) { - workshopTitle, _, _ := unstructured.NestedFieldNoCopy(workshop.Object, "spec", "title") - workshopDescription, _, _ := unstructured.NestedFieldNoCopy(workshop.Object, "spec", "description") - applicationsConfig, _, _ := unstructured.NestedFieldNoCopy(workshop.Object, "spec", "session", "applications") - ingressesConfig, _, _ := unstructured.NestedSlice(workshop.Object, "spec", "session", "ingresses") - dashboardsConfig, _, _ := unstructured.NestedSlice(workshop.Object, "spec", "session", "dashboards") - - workshopConfig := map[string]interface{}{ - "spec": map[string]interface{}{ - "title": workshopTitle, - "description": workshopDescription, - "session": map[string]interface{}{ - "applications": applicationsConfig, - "ingresses": ingressesConfig, - "dashboards": dashboardsConfig, - }, - }, - } - - workshopConfigData, err := yaml.Marshal(&workshopConfig) - - if err != nil { - return "", errors.Wrap(err, "failed to generate workshop config") - } - - return string(workshopConfigData), nil -} - -func generateVendirFilesConfig(workshop *unstructured.Unstructured, name string, localRepository string, version string) ([]string, error) { - var vendirConfigs []string - - workshopVersion, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") - - if !found { - workshopVersion = version - } - - filesItems, found, _ := unstructured.NestedSlice(workshop.Object, "spec", "workshop", "files") - - if found && len(filesItems) != 0 { - for _, filesItem := range filesItems { - directoriesConfig := []map[string]interface{}{} - - tmpPath, found := filesItem.(map[string]interface{})["path"] - - var filesItemPath string - - if found { - filesItemPath = tmpPath.(string) - } else { - filesItemPath = "." - } - - filesItemPath = filepath.Clean(path.Join("/opt/assets/files", filesItemPath)) - - filesItem.(map[string]interface{})["path"] = "." - - directoriesConfig = append(directoriesConfig, map[string]interface{}{ - "path": filesItemPath, - "contents": []interface{}{filesItem}, - }) - - vendirConfig := map[string]interface{}{ - "apiVersion": "vendir.k14s.io/v1alpha1", - "kind": "Config", - "directories": directoriesConfig, - } - - vendirConfigBytes, err := yaml.Marshal(&vendirConfig) - - if err != nil { - return []string{}, errors.Wrap(err, "failed to generate vendir config") - } - - vendirConfigString := string(vendirConfigBytes) - - vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(image_repository)", localRepository) - vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(workshop_name)", name) - vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(workshop_version)", workshopVersion) - vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(platform_arch)", runtime.GOARCH) - - vendirConfigs = append(vendirConfigs, vendirConfigString) - } - } - - return vendirConfigs, nil -} - -func generateVendirPackagesConfig(workshop *unstructured.Unstructured, name string, localRepository string, version string) (string, error) { - var vendirConfigString string - - workshopVersion, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") - - if !found { - workshopVersion = version - } - - packagesItems, found, _ := unstructured.NestedSlice(workshop.Object, "spec", "workshop", "packages") - - if found && len(packagesItems) != 0 { - directoriesConfig := []map[string]interface{}{} - - for _, packagesItem := range packagesItems { - tmpPackagesItem := packagesItem.(map[string]interface{}) - - tmpName, found := tmpPackagesItem["name"] - - if !found { - continue - } - - packagesItemPath := filepath.Clean(path.Join("/opt/packages", tmpName.(string))) - - tmpPackagesFilesItem := tmpPackagesItem["files"] - - packagesFilesItem := tmpPackagesFilesItem.([]interface{}) - - for _, tmpEntry := range packagesFilesItem { - entry := tmpEntry.(map[string]interface{}) - - _, found = entry["path"] - - if !found { - entry["path"] = "." - } - } - - directoriesConfig = append(directoriesConfig, map[string]interface{}{ - "path": packagesItemPath, - "contents": packagesFilesItem, - }) - - } - - vendirConfig := map[string]interface{}{ - "apiVersion": "vendir.k14s.io/v1alpha1", - "kind": "Config", - "directories": directoriesConfig, - } - - vendirConfigBytes, err := yaml.Marshal(&vendirConfig) - - if err != nil { - return "", errors.Wrap(err, "failed to generate vendir config") - } - - vendirConfigString = string(vendirConfigBytes) - - vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(image_repository)", localRepository) - vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(workshop_name)", name) - vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(workshop_version)", workshopVersion) - } - - return vendirConfigString, nil -} - -func generateWorkshopImageName(workshop *unstructured.Unstructured, localRepository string, imageRepository string, baseImageVersion string, workshopImage string, workshopVersion string) (string, error) { - _, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") - - if found { - workshopVersion, _, _ = unstructured.NestedString(workshop.Object, "spec", "version") - } - - image, found, err := unstructured.NestedString(workshop.Object, "spec", "workshop", "image") - - if err != nil { - return "", errors.Wrapf(err, "unable to parse workshop definition") - } - - if !found || image == "" { - image = "base-environment:*" - } - - defaultImageVersion := strings.TrimSpace(baseImageVersion) - - if workshopImage != "" { - image = workshopImage - } else { - if defaultImageVersion == "latest" { - image = strings.ReplaceAll(image, "base-environment:*", fmt.Sprintf("localhost:5001/educates-base-environment:%s", defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk8-environment:*", fmt.Sprintf("localhost:5001/educates-jdk8-environment:%s", defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk11-environment:*", fmt.Sprintf("localhost:5001/educates-jdk11-environment:%s", defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk17-environment:*", fmt.Sprintf("localhost:5001/educates-jdk17-environment:%s", defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk21-environment:*", fmt.Sprintf("localhost:5001/educates-jdk21-environment:%s", defaultImageVersion)) - image = strings.ReplaceAll(image, "conda-environment:*", fmt.Sprintf("localhost:5001/educates-conda-environment:%s", defaultImageVersion)) - } else { - image = strings.ReplaceAll(image, "base-environment:*", fmt.Sprintf("%s/educates-base-environment:%s", imageRepository, defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk8-environment:*", fmt.Sprintf("%s/educates-jdk8-environment:%s", imageRepository, defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk11-environment:*", fmt.Sprintf("%s/educates-jdk11-environment:%s", imageRepository, defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk17-environment:*", fmt.Sprintf("%s/educates-jdk17-environment:%s", imageRepository, defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk21-environment:*", fmt.Sprintf("%s/educates-jdk21-environment:%s", imageRepository, defaultImageVersion)) - image = strings.ReplaceAll(image, "conda-environment:*", fmt.Sprintf("%s/educates-conda-environment:%s", imageRepository, defaultImageVersion)) - } - } - - image = strings.ReplaceAll(image, "$(image_repository)", localRepository) - image = strings.ReplaceAll(image, "$(workshop_version)", workshopVersion) - - return image, nil -} - -func generateWorkshopVolumeMounts(workshop *unstructured.Unstructured, assets string) ([]composetypes.ServiceVolumeConfig, error) { - filesMounts := []composetypes.ServiceVolumeConfig{ - { - Type: "volume", - Source: "workshop", - Target: "/home/eduk8s", - }, - } - - if assets != "" { - assets = filepath.Clean(assets) - assets, err := filepath.Abs(assets) - - if err != nil { - return []composetypes.ServiceVolumeConfig{}, errors.Wrap(err, "can't resolve local workshop assets path") - } - - filesMounts = append(filesMounts, composetypes.ServiceVolumeConfig{ - Type: "bind", - Source: assets, - Target: "/opt/eduk8s/mnt/assets", - ReadOnly: true, - }) - } - - dockerEnabled, found, _ := unstructured.NestedBool(workshop.Object, "spec", "session", "applications", "docker", "enabled") - - if found && dockerEnabled { - extraServices, _, _ := unstructured.NestedMap(workshop.Object, "spec", "session", "applications", "docker", "compose") - - socketEnabledDefault := true - - if len(extraServices) != 0 { - socketEnabledDefault = false - } - - socketEnabled, found, _ := unstructured.NestedBool(workshop.Object, "spec", "session", "applications", "docker", "socket", "enabled") - - if !found { - socketEnabled = socketEnabledDefault - } - - if socketEnabled { - if runtime.GOOS == "linux" { - filesMounts = append(filesMounts, composetypes.ServiceVolumeConfig{ - Type: "bind", - Source: "/var/run/docker.sock", - Target: "/var/run/docker/docker.sock", - ReadOnly: true, - }) - } else { - filesMounts = append(filesMounts, composetypes.ServiceVolumeConfig{ - Type: "bind", - Source: "/var/run/docker.sock.raw", - Target: "/var/run/docker/docker.sock", - ReadOnly: true, - }) - } - } - } - - return filesMounts, nil -} - -func generateWorkshopEnvironment(workshop *unstructured.Unstructured, localRepository string, host string, port uint) ([]string, error) { - domain := fmt.Sprintf("%s.nip.io", strings.ReplaceAll(host, ".", "-")) - - return []string{ - fmt.Sprintf("WORKSHOP_NAME=%s", workshop.GetName()), - "SESSION_NAME=workshop", - fmt.Sprintf("SESSION_URL=http://workshop.%s:%d", domain, port), - "INGRESS_PROTOCOL=http", - fmt.Sprintf("INGRESS_DOMAIN=%s", domain), - fmt.Sprintf("INGRESS_PORT_SUFFIX=:%d", port), - fmt.Sprintf("IMAGE_REPOSITORY=%s", localRepository), - }, nil -} - -func generateWorkshopLabels(workshop *unstructured.Unstructured, host string, port uint) (map[string]string, error) { - labels := workshop.GetAnnotations() - - domain := fmt.Sprintf("%s.nip.io", strings.ReplaceAll(host, ".", "-")) - - labels["training.educates.dev/url"] = fmt.Sprintf("http://workshop.%s:%d", domain, port) - labels["training.educates.dev/session"] = workshop.GetName() - - return labels, nil -} - -func generateWorkshopExtraHosts(workshop *unstructured.Unstructured, registryIP string) (map[string]string, error) { - hosts := map[string]string{} - - if registryIP != "" { - hosts["registry.docker.local"] = registryIP - } - - return hosts, nil -} - -func extractWorkshopComposeConfig(workshop *unstructured.Unstructured) (*composetypes.Project, error) { - composeConfigObj, found, _ := unstructured.NestedMap(workshop.Object, "spec", "session", "applications", "docker", "compose") - - if found { - composeConfigObjBytes, err := yaml.Marshal(&composeConfigObj) - - if err != nil { - return nil, errors.Wrap(err, "unable to parse workshop docker compose config") - } - - configFiles := composetypes.ConfigFile{ - Content: composeConfigObjBytes, - } - - composeConfigDetails := composetypes.ConfigDetails{ - ConfigFiles: []composetypes.ConfigFile{configFiles}, - } - - return composeloader.LoadWithContext(context.Background(), composeConfigDetails, func(options *composeloader.Options) { - options.SkipConsistencyCheck = true - options.SkipNormalization = true - options.ResolvePaths = false - options.SkipValidation = true - }) - } - - return nil, nil -} - -func generateClusterKubeconfig(name string) (string, error) { - provider := cluster.NewProvider( - cluster.ProviderWithLogger(cmd.NewLogger()), - ) - - clusters, err := provider.List() - - if err != nil { - return "", errors.Wrap(err, "unable to get list of clusters") - } - - if !slices.Contains(clusters, name) { - return "", errors.Errorf("cluster %s doesn't exist", name) - } - - file, err := os.CreateTemp("", "kubeconfig-") - - if err != nil { - return "", errors.Wrap(err, "unable to generate kubeconfig file") - } - - defer os.Remove(file.Name()) - - err = provider.ExportKubeConfig(name, file.Name(), true) - - if err != nil { - return "", errors.Wrap(err, "unable to generate kubeconfig file") - } - - kubeConfigData, err := os.ReadFile(file.Name()) - - if err != nil { - return "", errors.Wrap(err, "unable to generate kubeconfig file") - } - - return string(kubeConfigData), nil -} diff --git a/client-programs/pkg/cmd/docker_workshop_list_cmd.go b/client-programs/pkg/cmd/docker_workshop_list_cmd.go index 38069ac39..77f618b32 100644 --- a/client-programs/pkg/cmd/docker_workshop_list_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_list_cmd.go @@ -1,32 +1,35 @@ package cmd import ( - "context" "fmt" "os" - "sync" "text/tabwriter" - "github.com/docker/docker/api/types/container" - "github.com/docker/docker/client" + "github.com/educates/educates-training-platform/client-programs/pkg/docker" "github.com/pkg/errors" "github.com/spf13/cobra" ) +const dockerWorkshopListExample = ` + # List Educates workshops deployed to Docker + educates docker workshop list +` + func (p *ProjectInfo) NewDockerWorkshopListCmd() *cobra.Command { var c = &cobra.Command{ Args: cobra.NoArgs, Use: "list", Short: "List workshops deployed to Docker", RunE: func(_ *cobra.Command, _ []string) error { - dockerWorkshopsManager := NewDockerWorkshopsManager() + dockerWorkshopsManager := docker.NewDockerWorkshopsManager() - workshops, err := dockerWorkshopsManager.ListWorkhops() + workshops, err := dockerWorkshopsManager.ListWorkshops() if err != nil { return errors.Wrap(err, "cannot display list of workshops") } + // TODO: Move this to a helper function w := new(tabwriter.Writer) w.Init(os.Stdout, 8, 8, 3, ' ', 0) @@ -40,121 +43,8 @@ func (p *ProjectInfo) NewDockerWorkshopListCmd() *cobra.Command { return nil }, + Example: dockerWorkshopListExample, } return c } - -type DockerWorkshopsManager struct { - Statuses map[string]DockerWorkshopDetails - StatusesMutex sync.Mutex -} - -func NewDockerWorkshopsManager() DockerWorkshopsManager { - return DockerWorkshopsManager{ - Statuses: map[string]DockerWorkshopDetails{}, - StatusesMutex: sync.Mutex{}, - } -} - -type DockerWorkshopDetails struct { - Name string `json:"name"` - Url string `json:"url,omitempty"` - Source string `json:"source,omitempty"` - Status string `json:"status"` -} - -func (m *DockerWorkshopsManager) WorkshopStatus(name string) (DockerWorkshopDetails, bool) { - workshops, err := m.ListWorkhops() - - if err != nil { - return DockerWorkshopDetails{}, false - } - - for _, workshop := range workshops { - if workshop.Name == name { - return workshop, true - } - } - - return DockerWorkshopDetails{}, false -} - -func (m *DockerWorkshopsManager) SetWorkshopStatus(name string, url string, source string, status string) { - m.StatusesMutex.Lock() - - m.Statuses[name] = DockerWorkshopDetails{ - Name: name, - Url: url, - Source: source, - Status: status, - } - - m.StatusesMutex.Unlock() -} - -func (m *DockerWorkshopsManager) ClearWorkshopStatus(name string) { - m.StatusesMutex.Lock() - - delete(m.Statuses, name) - - m.StatusesMutex.Unlock() -} - -func (m *DockerWorkshopsManager) ListWorkhops() ([]DockerWorkshopDetails, error) { - setOfWorkshops := map[string]DockerWorkshopDetails{} - workshopsList := []DockerWorkshopDetails{} - - ctx := context.Background() - - cli, err := client.NewClientWithOpts(client.FromEnv) - - if err != nil { - return nil, errors.Wrap(err, "unable to create docker client") - } - - containers, err := cli.ContainerList(ctx, container.ListOptions{}) - - if err != nil { - return nil, errors.Wrap(err, "unable to list containers") - } - - m.StatusesMutex.Lock() - - for _, details := range m.Statuses { - if details.Status == "Starting" { - setOfWorkshops[details.Name] = details - } - } - - defer m.StatusesMutex.Unlock() - - for _, container := range containers { - url, found := container.Labels["training.educates.dev/url"] - source := container.Labels["training.educates.dev/source"] - instance := container.Labels["training.educates.dev/session"] - - details, statusFound := m.Statuses[instance] - - status := "Running" - - if statusFound { - status = details.Status - } - - if found && url != "" && len(container.Names) != 0 { - setOfWorkshops[instance] = DockerWorkshopDetails{ - Name: instance, - Url: url, - Source: source, - Status: status, - } - } - } - - for _, details := range setOfWorkshops { - workshopsList = append(workshopsList, details) - } - - return workshopsList, nil -} diff --git a/client-programs/pkg/cmd/docker_workshop_logs.go b/client-programs/pkg/cmd/docker_workshop_logs.go index 67f819a1d..8330d20e0 100644 --- a/client-programs/pkg/cmd/docker_workshop_logs.go +++ b/client-programs/pkg/cmd/docker_workshop_logs.go @@ -8,9 +8,22 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" "github.com/pkg/errors" "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) +const dockerWorkshopLogsExample = ` + # Display logs for Educates workshop in current workshop directory + educates docker workshop logs + + # Display logs for Educates workshop with provided name + educates docker workshop logs --name my-workshop + + # Display logs for Educates workshop from specific path and using custom workshop file + educates docker workshop logs --path ./workshop --workshop-file custom-workshop.yaml + + # Display logs for Educates workshop in current folder and follow the logs + educates docker workshop logs --follow +` + type DockerWorkshopLogsOptions struct { Name string Path string @@ -32,16 +45,12 @@ func (o *DockerWorkshopLogsOptions) Run(cmd *cobra.Command) error { // the workshop will then expect the workshop definition to reside in the // resources/workshop.yaml file under the directory, the same as if a // directory path was provided explicitly. - if path == "" { path = "." } // Load the workshop definition. The path can be a HTTP/HTTPS URL for a // local file system path for a directory or file. - - var workshop *unstructured.Unstructured - definitionConfig := workshops.WorkshopDefinitionConfig{ Name: o.Name, Path: path, @@ -50,7 +59,8 @@ func (o *DockerWorkshopLogsOptions) Run(cmd *cobra.Command) error { WorkshopVersion: o.WorkshopVersion, DataValueFlags: o.DataValuesFlags, } - if workshop, err = workshops.LoadWorkshopDefinition(&definitionConfig); err != nil { + workshop, err := workshops.LoadWorkshopDefinition(&definitionConfig) + if err != nil { return err } @@ -90,6 +100,7 @@ func (p *ProjectInfo) NewDockerWorkshopLogsCmd() *cobra.Command { Use: "logs", Short: "Display logs for workshop", RunE: func(cmd *cobra.Command, _ []string) error { return o.Run(cmd) }, + Example: dockerWorkshopLogsExample, } c.Flags().StringVarP( diff --git a/client-programs/pkg/cmd/docker_workshop_open_cmd.go b/client-programs/pkg/cmd/docker_workshop_open_cmd.go index a63c3ff53..0f7361f97 100644 --- a/client-programs/pkg/cmd/docker_workshop_open_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_open_cmd.go @@ -2,22 +2,30 @@ package cmd import ( "context" - "fmt" "io" "net/http" - "os/exec" - "runtime" "time" yttcmd "carvel.dev/ytt/pkg/cmd/template" "github.com/docker/docker/client" "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" "github.com/spf13/cobra" - "k8s.io/apimachinery/pkg/apis/meta/v1/unstructured" ) +const dockerWorkshopOpenExample = ` + # Open Educates workshop in browser in current workshop directory + educates docker workshop open + + # Open Educates workshop in browser with provided name + educates docker workshop open --name my-workshop + + # Open Educates workshop in browser from specific path and using custom workshop file + educates docker workshop open --path ./workshop --workshop-file custom-workshop.yaml +` + type DockerWorkshopOpenOptions struct { Name string Path string @@ -27,8 +35,6 @@ type DockerWorkshopOpenOptions struct { } func (o *DockerWorkshopOpenOptions) Run() error { - var err error - var name = o.Name if name == "" { @@ -38,16 +44,12 @@ func (o *DockerWorkshopOpenOptions) Run() error { // the workshop will then expect the workshop definition to reside in the // resources/workshop.yaml file under the directory, the same as if a // directory path was provided explicitly. - if path == "" { path = "." } // Load the workshop definition. The path can be a HTTP/HTTPS URL for a // local file system path for a directory or file. - - var workshop *unstructured.Unstructured - definitionConfig := workshops.WorkshopDefinitionConfig{ Name: o.Name, Path: path, @@ -56,7 +58,8 @@ func (o *DockerWorkshopOpenOptions) Run() error { WorkshopVersion: o.WorkshopVersion, DataValueFlags: o.DataValuesFlags, } - if workshop, err = workshops.LoadWorkshopDefinition(&definitionConfig); err != nil { + workshop, err := workshops.LoadWorkshopDefinition(&definitionConfig) + if err != nil { return err } @@ -85,9 +88,8 @@ func (o *DockerWorkshopOpenOptions) Run() error { return errors.New("can't determine URL for workshop") } - // XXX Need a better way of handling very long startup times for container + // TODO: XXX Need a better way of handling very long startup times for container // due to workshop content or package downloads. - for i := 1; i < 120; i++ { time.Sleep(time.Second) @@ -103,22 +105,7 @@ func (o *DockerWorkshopOpenOptions) Run() error { break } - switch runtime.GOOS { - case "linux": - err = exec.Command("xdg-open", url).Start() - case "windows": - err = exec.Command("rundll32", "url.dll,FileProtocolHandler", url).Start() - case "darwin": - err = exec.Command("open", url).Start() - default: - err = fmt.Errorf("unsupported platform") - } - - if err != nil { - return errors.Wrap(err, "unable to open web browser") - } - - return nil + return utils.OpenBrowser(url) } func (p *ProjectInfo) NewDockerWorkshopOpenCmd() *cobra.Command { @@ -129,6 +116,7 @@ func (p *ProjectInfo) NewDockerWorkshopOpenCmd() *cobra.Command { Use: "open", Short: "Open workshop in browser", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: dockerWorkshopOpenExample, } c.Flags().StringVarP( diff --git a/client-programs/pkg/docker/extension_backend.go b/client-programs/pkg/docker/extension_backend.go new file mode 100644 index 000000000..c4b4cb797 --- /dev/null +++ b/client-programs/pkg/docker/extension_backend.go @@ -0,0 +1,103 @@ +package docker + +import ( + "context" + "fmt" + "net" + "net/http" + "os" + "os/signal" + "regexp" + "syscall" + + "github.com/pkg/errors" +) + +type DockerExtensionBackendConfig struct { + Socket string +} + +type DockerExtensionBackend struct { + Api *DockerWorkshopsApi +} + +func NewDockerExtensionBackend(version string, imageRepository string) DockerExtensionBackend { + return DockerExtensionBackend{ + Api: &DockerWorkshopsApi{ + Manager: NewDockerWorkshopsManager(), + ImageRepository: imageRepository, + ImageVersion: version, + }, + } +} + +func (b *DockerExtensionBackend) Run(config *DockerExtensionBackendConfig) error { + if config.Socket == "" { + return errors.New("invalid socket for HTTP server") + } + + router := http.NewServeMux() + + versionHandler := func(w http.ResponseWriter, r *http.Request) { + fmt.Fprintln(w, b.Api.ImageVersion) + } + + router.HandleFunc("/version", versionHandler) + + router.HandleFunc("/workshop/list", b.Api.ListWorkhops) + router.HandleFunc("/workshop/deploy", b.Api.DeployWorkshop) + router.HandleFunc("/workshop/delete", b.Api.DeleteWorkshop) + + server := http.Server{ + Handler: router, + } + + // The socket string can either be of the form host:nnn, or it can be a file + // system path (absolute or relative). In the first case we start up a + // normal HTTP server accepting connections over an INET socket connection. + // In the second case connections will be accepted over a UNIX socket. + + inetRegexPattern := `^([a-zA-Z0-9.-]+):(\d+)$` + + match, err := regexp.MatchString(inetRegexPattern, config.Socket) + + if err != nil { + return errors.Wrap(err, "failed to perform regex match on socket") + } + + var listener net.Listener + + if match { + listener, err = net.Listen("tcp", config.Socket) + + if err != nil { + return errors.Wrap(err, "unable to create INET HTTP server socket") + } + } else { + listener, err = net.Listen("unix", config.Socket) + + if err != nil { + return errors.Wrap(err, "unable to create UNIX HTTP server socket") + } + + defer os.Remove(config.Socket) + } + + defer listener.Close() + + go func() { + server.Serve(listener) + }() + + sigChan := make(chan os.Signal, 1) + signal.Notify(sigChan, os.Interrupt, syscall.SIGTERM) + <-sigChan + + err = server.Shutdown(context.TODO()) + + if err != nil { + return errors.Wrap(err, "failed to shutdown HTTP server") + } + + return nil +} diff --git a/client-programs/pkg/docker/extension_backend_api.go b/client-programs/pkg/docker/extension_backend_api.go new file mode 100644 index 000000000..f4bbbb81e --- /dev/null +++ b/client-programs/pkg/docker/extension_backend_api.go @@ -0,0 +1,145 @@ +package docker + +import ( + "encoding/json" + "fmt" + "net/http" + "os" + "strconv" + "strings" +) + +type DockerWorkshopsApi struct { + Manager DockerWorkshopsManager + ImageRepository string + ImageVersion string +} + +// func NewDockerWorkshopsApi(version string, imageRepository string) *DockerWorkshopsApi { +// return &DockerWorkshopsApi{ +// Manager: NewDockerWorkshopsManager(), +// ImageRepository: imageRepository, +// ImageVersion: version, +// } +// } + +func (b *DockerWorkshopsApi) ListWorkhops(w http.ResponseWriter, r *http.Request) { + workshops, err := b.Manager.ListWorkshops() + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + jsonData, err := json.Marshal(workshops) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + + w.WriteHeader(http.StatusOK) + w.Write(jsonData) +} + +func (b *DockerWorkshopsApi) DeployWorkshop(w http.ResponseWriter, r *http.Request) { + queryParams := r.URL.Query() + + url := queryParams.Get("url") + + if url == "" { + http.Error(w, "workshop definition url required", http.StatusBadRequest) + return + } + + portString := queryParams.Get("port") + + if portString == "" { + portString = "10081" + } + + port, err := strconv.Atoi(portString) + + if err != nil || port <= 0 { + http.Error(w, "invalid workshop port supplied", http.StatusBadRequest) + return + } + + o := DockerWorkshopDeployConfig{ + Path: url, + Host: "127.0.0.1", + Port: uint(port), + LocalRepository: "localhost:5001", + DisableOpenBrowser: false, + ImageRepository: b.ImageRepository, + ImageVersion: b.ImageVersion, + Cluster: "", + KubeConfig: "", + Assets: "", + } + + name, err := b.Manager.DeployWorkshop(&o, os.Stdout, os.Stderr) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + sessionUrl := fmt.Sprintf("http://workshop.%s.nip.io:%d", strings.ReplaceAll(o.Host, ".", "-"), o.Port) + + workshop := DockerWorkshopDetails{ + Name: name, + Url: sessionUrl, + Source: url, + Status: "Started", + } + + jsonData, err := json.Marshal(workshop) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + + w.WriteHeader(http.StatusOK) + w.Write(jsonData) +} + +func (b *DockerWorkshopsApi) DeleteWorkshop(w http.ResponseWriter, r *http.Request) { + queryParams := r.URL.Query() + + name := queryParams.Get("name") + + if name == "" { + http.Error(w, "workshop session name required", http.StatusBadRequest) + return + } + + err := b.Manager.DeleteWorkshop(name, os.Stdout, os.Stderr) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + workshop := DockerWorkshopDetails{ + Name: name, + Status: "Stopped", + } + + jsonData, err := json.Marshal(workshop) + + if err != nil { + http.Error(w, err.Error(), http.StatusInternalServerError) + return + } + + w.Header().Set("Content-Type", "application/json") + + w.WriteHeader(http.StatusOK) + w.Write(jsonData) +} diff --git a/client-programs/pkg/docker/workshop_manager.go b/client-programs/pkg/docker/workshop_manager.go index 63f8cdb6d..04b166257 100644 --- a/client-programs/pkg/docker/workshop_manager.go +++ b/client-programs/pkg/docker/workshop_manager.go @@ -112,7 +112,7 @@ type DockerWorkshopDeployConfig struct { func (m *DockerWorkshopsManager) WorkshopStatus(name string) (DockerWorkshopDetails, bool) { - workshops, err := m.ListWorkhops() + workshops, err := m.ListWorkshops() if err != nil { return DockerWorkshopDetails{}, false @@ -148,7 +148,7 @@ func (m *DockerWorkshopsManager) ClearWorkshopStatus(name string) { m.StatusesMutex.Unlock() } -func (m *DockerWorkshopsManager) ListWorkhops() ([]DockerWorkshopDetails, error) { +func (m *DockerWorkshopsManager) ListWorkshops() ([]DockerWorkshopDetails, error) { setOfWorkshops := map[string]DockerWorkshopDetails{} workshopsList := []DockerWorkshopDetails{} diff --git a/client-programs/pkg/educates/resources/workshops/manager.go b/client-programs/pkg/educates/resources/workshops/manager.go index e0834315c..0c421c6c4 100644 --- a/client-programs/pkg/educates/resources/workshops/manager.go +++ b/client-programs/pkg/educates/resources/workshops/manager.go @@ -584,7 +584,6 @@ func LoadWorkshopDefinition(o *WorkshopDefinitionConfig) (*unstructured.Unstruct } // Read in the workshop definition as raw data ready for parsing. - var workshopData []byte if urlInfo.Scheme != "http" && urlInfo.Scheme != "https" { diff --git a/go.work.sum b/go.work.sum index aded10bdd..3f5618305 100644 --- a/go.work.sum +++ b/go.work.sum @@ -5,6 +5,7 @@ carvel.dev/ytt v0.47.0/go.mod h1:Xarf0th61vX6VY07l3KBSi3uaMCQ2UyPPiCPiaVpHME= cel.dev/expr v0.16.1/go.mod h1:AsGA5zb3WruAEQeQng1RZdGEXmBj0jvMWh6l5SnNuC8= cel.dev/expr v0.18.0 h1:CJ6drgk+Hf96lkLikr4rFf19WrU0BOWEihyZnI2TAzo= cel.dev/expr v0.18.0/go.mod h1:MrpN08Q+lEBs+bGYdLxxHkZoUSsCp0nSKTs0nTymJgw= +cel.dev/expr v0.24.0 h1:56OvJKSH3hDGL0ml5uSxZmz3/3Pq4tJ+fb1unVLAFcY= cel.dev/expr v0.24.0/go.mod h1:hLPLo1W4QUmuYdA72RBX06QTs6MXw941piREPl3Yfiw= cloud.google.com/go v0.50.0/go.mod h1:r9sluTvynVuxRIOHXQEHMFffphuXHOMZMycpNR5e6To= cloud.google.com/go v0.52.0/go.mod h1:pXajvRH/6o3+F9jDHZWQ5PbGhn+o8w9qiu/CffaVdO4= @@ -280,8 +281,9 @@ github.com/awslabs/amazon-ecr-credential-helper/ecr-login v0.0.0-20220517224237- github.com/bazelbuild/bazelisk v1.13.2/go.mod h1:jVD8/E7hMAXgWKCljEz8hOV0PZ+nFBgCpjIOJ6Xyzus= github.com/bazelbuild/rules_go v0.34.0/go.mod h1:MC23Dc/wkXEyk3Wpq6lCqz0ZAYOZDw2DR5y3N1q2i7M= github.com/bketelsen/crypt v0.0.4/go.mod h1:aI6NrJ0pMGgvZKL1iVgXLnfIFJtfV+bKCoqOes/6LfM= -github.com/blang/semver v3.5.1+incompatible h1:cQNTCjp13qL8KC3Nbxr/y2Bqb63oX6wdnnjpJbkM4JQ= github.com/blang/semver v3.5.1+incompatible/go.mod h1:kRBLl5iJ+tD4TcOOxsy/0fnwebNt5EWlYSAyrTnjyyk= +github.com/blang/semver/v4 v4.0.0 h1:1PFHFE6yCCTv8C1TeyNNarDzntLi7wMI5i/pzqYIsAM= +github.com/blang/semver/v4 v4.0.0/go.mod h1:IbckMUScFkM3pff0VJDNKRiT6TG/YpiHIM2yvyW5YoQ= github.com/bmatcuk/doublestar v1.2.1 h1:eetYiv8DDYOZcBADY+pRvRytf3Dlz1FhnpvL2FsClBc= github.com/bmatcuk/doublestar v1.2.1/go.mod h1:wiQtGV+rzVYxB7WIlirSN++5HPtPlXEo9MEoZQC/PmE= github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= @@ -348,8 +350,11 @@ github.com/coredns/corefile-migration v1.0.24/go.mod h1:56DPqONc3njpVPsdilEnfijC github.com/coredns/corefile-migration v1.0.26/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-oidc v2.3.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= +github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= github.com/coreos/go-systemd/v22 v22.4.0/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc= +github.com/coreos/go-systemd/v22 v22.6.0 h1:aGVa/v8B7hpb0TKl0MWoAavPDmHvobFe5R5zn0bCJWo= +github.com/coreos/go-systemd/v22 v22.6.0/go.mod h1:iG+pp635Fo7ZmV/j14KUcmEyWF+0X7Lua8rrTWzYgWU= github.com/cppforlife/cobrautil v0.0.0-20221021151949-d60711905d65/go.mod h1:2w+qxVu2KSGW78Ex/XaIqfh/OvBgjEsmN53S4T8vEyA= github.com/cppforlife/go-cli-ui v0.0.0-20220425131040-94f26b16bc14/go.mod h1:AlgTssDlstr4mf92TR4DPITLfl5+7wEY4cKStCmeeto= github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o= @@ -371,8 +376,6 @@ github.com/distribution/reference v0.5.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5 github.com/docker/cli v24.0.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= github.com/docker/cli v27.5.0+incompatible h1:aMphQkcGtpHixwwhAXJT1rrK/detk2JIvDaFkLctbGM= github.com/docker/cli v27.5.0+incompatible/go.mod h1:JLrzqnKDaYBop7H2jaqPtU4hHvMKP+vjCwu2uszcLI8= -github.com/docker/cli-docs-tool v0.11.0 h1:7d8QARFb7QEobizqxmEM7fOteZEHwH/zWgHQtHZEcfE= -github.com/docker/cli-docs-tool v0.11.0/go.mod h1:ma8BKiisUo8D6W05XEYIh3oa1UbgrZhi1nowyKFJa8Q= github.com/docker/distribution v2.8.2+incompatible/go.mod h1:J2gT2udsDAN96Uj4KfcMRqY0/ypR+oyYUYmja8H+y+w= github.com/docker/docker v24.0.0+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker v27.5.0+incompatible h1:um++2NcQtGRTz5eEgO6aJimo6/JxrTXC941hd05JO6U= @@ -381,7 +384,6 @@ github.com/docker/docker v28.0.2+incompatible h1:9BILleFwug5FSSqWBgVevgL3ewDJfWW github.com/docker/docker v28.0.2+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= github.com/docker/docker-credential-helpers v0.6.3/go.mod h1:WRaJzqw3CTB9bk10avuGsjVBZsD05qeibJ1/TYlvc0Y= github.com/docker/docker-credential-helpers v0.7.0/go.mod h1:rETQfLdHNT3foU5kuNkFR1R1V12OJRRO5lzt2D1b5X0= -github.com/docker/go v1.5.1-1.0.20160303222718-d30aec9fd63c/go.mod h1:CADgU4DSXK5QUlFslkQu2yW2TKzFZcXq/leZfM0UH5Q= github.com/docker/go-events v0.0.0-20190806004212-e31b211e4f1c/go.mod h1:Uw6UezgYA44ePAFQYUehOuCzmy5zmg/+nl2ZfMWGkpA= github.com/docopt/docopt-go v0.0.0-20180111231733-ee0de3bc6815/go.mod h1:WwZ+bS3ebgob9U8Nd0kOddGdZWjyMGR8Wziv+TBNwSE= github.com/dustin/go-humanize v1.0.0/go.mod h1:HtrtbFcZ19U5GC7JDqmcUSB87Iq5E25KnS6fMYU6eOk= @@ -502,6 +504,8 @@ github.com/golang/protobuf v1.5.1/go.mod h1:DopwsBzvsk0Fs44TXzsVbJyPhcCPeIwnvohx github.com/golang/protobuf v1.5.2/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= github.com/golang/snappy v0.0.4/go.mod h1:/XxbfmMg8lxefKM7IXC3fBNl/7bRcc72aCRzEWrmP2Q= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/google/btree v1.1.3 h1:CVpQJjYgC4VbzxeGVHfvZrv1ctoYCAI8vbl07Fcxlyg= +github.com/google/btree v1.1.3/go.mod h1:qOPhT0dTNdNzV6Z/lhRX0YXUafgPLFUh+gZMl761Gm4= github.com/google/cadvisor v0.49.0/go.mod h1:s6Fqwb2KiWG6leCegVhw4KW40tf9f7m+SF1aXiE8Wsk= github.com/google/cadvisor v0.49.2/go.mod h1:s6Fqwb2KiWG6leCegVhw4KW40tf9f7m+SF1aXiE8Wsk= github.com/google/cadvisor v0.51.0/go.mod h1:czGE/c/P/i0QFpVNKTFrIEzord9Y10YfpwuaSWXELc0= @@ -515,6 +519,7 @@ github.com/google/cel-go v0.23.2 h1:UdEe3CvQh3Nv+E/j9r1Y//WO0K0cSyD7/y0bzyLIMI4= github.com/google/cel-go v0.23.2/go.mod h1:52Pb6QsDbC5kvgxvZhiL9QX1oZEkcUF/ZqaPx1J5Wwo= github.com/google/cel-go v0.24.1 h1:jsBCtxG8mM5wiUJDSGUqU0K7Mtr3w7Eyv00rw4DiZxI= github.com/google/cel-go v0.24.1/go.mod h1:Hdf9TqOaTNSFQA1ybQaRqATVoK7m/zcf7IMhGXP5zI8= +github.com/google/cel-go v0.26.0/go.mod h1:A9O8OU9rdvrK5MQyrqfIxo1a0u4g3sF8KB6PUIaryMM= github.com/google/certtostore v1.0.6/go.mod h1:2N0ZPLkGvQWhYvXaiBGq02r71fnSLfq78VKIWQHr1wo= github.com/google/deck v0.0.0-20230104221208-105ad94aa8ae/go.mod h1:DoDv8G58DuLNZF0KysYn0bA/6ZWhmRW3fZE2VnGEH0w= github.com/google/gnostic v0.5.7-v3refs h1:FhTMOKj2VhjpouxvWJAV1TL304uMlb9zcDqkl6cEI54= @@ -575,7 +580,10 @@ github.com/gregjones/httpcache v0.0.0-20190611155906-901d90724c79/go.mod h1:Fecb github.com/grpc-ecosystem/go-grpc-middleware v1.3.0/go.mod h1:z0ButlSOZa5vEBq9m2m2hlwIgKw+rp3sdCBRoJY+30Y= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.0.1/go.mod h1:lXGCsh6c22WGtjr+qGHj1otzZpV/1kwTMAqkwZsnWRU= github.com/grpc-ecosystem/go-grpc-middleware/providers/prometheus v1.1.0/go.mod h1:hM2alZsMUni80N33RBe6J0e423LB+odMj7d3EMP9l20= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.3.0/go.mod h1:qOchhhIlmRcqk/O9uCo/puJlyo07YINaIqdZfZG3Jkc= +github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0 h1:Ovs26xHkKqVztRpIrF/92BcuyuQ/YW4NSIpoGtfXNho= +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.7.0/go.mod h1:hgWBS7lorOAVIJEQMi4ZsPv9hVvWI6+ch50m39Pf2Ks= @@ -590,7 +598,6 @@ github.com/hashicorp/go-version v1.2.1/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09 github.com/hashicorp/go-version v1.6.0 h1:feTTfFNnjP967rlCxM/I9g701jU+RN74YKx2mOkIeek= github.com/hashicorp/go-version v1.6.0/go.mod h1:fltr4n8CU8Ke44wwGCBoEymUuxUHl09ZGVZPK5anwXA= github.com/hashicorp/golang-lru/v2 v2.0.7/go.mod h1:QeFd9opnmA6QUJc5vARoKUSoFhyfM2/ZepoAG6RGpeM= -github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= github.com/hashicorp/hcl/v2 v2.24.0/go.mod h1:oGoO1FIQYfn/AgyOhlg9qLC6/nOJPX3qGbkZpYAcqfM= github.com/hiddeco/sshsig v0.2.0/go.mod h1:nJc98aGgiH6Yql2doqH4CTBVHexQA40Q+hMMLHP4EqE= github.com/ianlancetaylor/demangle v0.0.0-20181102032728-5e5cf60278f6/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= @@ -633,7 +640,6 @@ github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wn github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= github.com/magiconair/properties v1.8.5/go.mod h1:y3VJvCyxH9uVvJTWEGAELF3aiYNyPKd5NZ3oSwXrF60= -github.com/magiconair/properties v1.8.9/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= github.com/mailru/easyjson v0.0.0-20160728113105-d5b7844b561a/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190614124828-94de47d64c63/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN94OJF2b5HbByQZoLdCWB1Yqtg26g4irojpc= @@ -652,12 +658,10 @@ github.com/maxbrunsfeld/counterfeiter/v6 v6.11.2/go.mod h1:VzB2VoMh1Y32/QqDfg9ZJ github.com/mdlayher/socket v0.5.1/go.mod h1:TjPLHI1UgwEv5J1B5q0zTZq12A/6H7nKmtTanQE37IQ= github.com/mdlayher/vsock v1.2.1/go.mod h1:NRfCibel++DgeMD8z/hP+PPTjlNJsdPOmxcnENvE+SE= github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE= -github.com/miekg/pkcs11 v1.1.1/go.mod h1:XsNlhZGX73bx86s2hdc/FuaLm2CPZJemRLMA+WTFxgs= github.com/mistifyio/go-zfs v2.1.2-0.20190413222219-f784269be439+incompatible/go.mod h1:8AuVvqP/mXw1px98n46wfvcGfQ4ci2FwoAjKYxuo3Z4= github.com/mistifyio/go-zfs/v3 v3.0.1/go.mod h1:CzVgeB0RvF2EGzQnytKVvVSDwmKJXxkOTUGbNrTja/k= github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg= github.com/mitchellh/mapstructure v1.4.1/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= -github.com/mitchellh/mapstructure v1.5.0/go.mod h1:bFUtVrKA4DC2yAKiSyO/QUcy7e+RRV2QTWOzhPopBRo= github.com/moby/ipvs v1.1.0/go.mod h1:4VJMWuf098bsUMmZEiD4Tjk/O7mOn3l1PTD3s4OoYAs= github.com/moby/moby v27.1.1+incompatible/go.mod h1:fDXVQ6+S340veQPv35CzDahGBmHsiclFwfEygB/TWMc= github.com/moby/policy-helpers v0.0.0-20251105011237-bcaa71c99f14/go.mod h1:HJfK0E8dR+Jpk5anJ3oADg2dRSom1gJK17sqEiiMS7w= @@ -673,7 +677,6 @@ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= @@ -755,27 +758,23 @@ github.com/sirupsen/logrus v1.9.1/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVs github.com/smallstep/pkcs7 v0.1.1/go.mod h1:dL6j5AIz9GHjVEBTXtW+QliALcgM19RtXaTeyxI+AfA= github.com/soheilhy/cmux v0.1.5/go.mod h1:T7TcVDs9LWfQgPlPsdngu6I6QIoyIFZDDC6sNE1GqG0= github.com/spf13/afero v1.2.2/go.mod h1:9ZxEEn6pIJ8Rxe320qSDBk6AsU0r9pR7Q4OcevTdifk= -github.com/spf13/afero v1.6.0/go.mod h1:Ai8FlHk4v/PARR026UzYexafAt9roJ7LcLMAmO6Z93I= -github.com/spf13/cast v1.3.1/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= github.com/spf13/cobra v1.2.0/go.mod h1:ExllRjgxM/piMAM+3tAZvg8fsklGAf3tPfi+i8t68Nk= github.com/spf13/cobra v1.7.0/go.mod h1:uLxZILRyS/50WlhOIKD7W6V5bgeIt+4sICxh6uRMrb0= github.com/spf13/cobra v1.8.1/go.mod h1:wHxEcudfqmLYa8iTfL+OuZPbBZkmvliBWKIezN3kD9Y= github.com/spf13/cobra v1.10.1 h1:lJeBwCfmrnXthfAupyUTzJ/J4Nc1RsHC/mSRU2dll/s= github.com/spf13/cobra v1.10.1/go.mod h1:7SmJGaTHFVBY0jW4NXGluQoLvhqFQM+6XSKD+P4XaB0= -github.com/spf13/jwalterweatherman v1.1.0/go.mod h1:aNWZUN0dPAAO/Ljvb5BEdw96iTZ0EXowPYD95IqWIGo= github.com/spf13/pflag v0.0.0-20170130214245-9ff6c6923cff/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= -github.com/spf13/viper v1.8.1/go.mod h1:o0Pch8wJ9BVSWGQMbra6iw0oQ5oktSIBaujf1rJH9Ns= github.com/spiffe/go-spiffe/v2 v2.5.0/go.mod h1:P+NxobPc6wXhVtINNtFjNWGBTreew1GBUCwT2wPmb7g= github.com/spiffe/go-spiffe/v2 v2.6.0/go.mod h1:gm2SeUoMZEtpnzPNs2Csc0D/gX33k1xIx7lEzqblHEs= github.com/stefanberger/go-pkcs11uri v0.0.0-20230803200340-78284954bff6/go.mod h1:39R/xuhNgVhi+K0/zst4TLrJrVmbm6LVgl4A0+ZFS5M= github.com/stoewer/go-strcase v1.2.0/go.mod h1:IBiWB2sKIp3wVVQ3Y035++gc+knqhUQag1KpM8ahLw8= +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/stretchr/testify v0.0.0-20151208002404-e3a8ff8ce365/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= -github.com/subosito/gotenv v1.2.0/go.mod h1:N0PQaV/YGNqwC0u51sEeR/aUtSLEXKX9iv69rRypqCw= github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG7XYTFWNJGYcbNJQlaLq0fg1yr4J4t/NcTQtrfww= github.com/tchap/go-patricia/v2 v2.3.3/go.mod h1:VZRHKAb53DLaG+nA9EaYYiaEx6YztwDlLElMsnSHD4k= github.com/tetratelabs/wazero v1.9.0/go.mod h1:TSbcXCfFP0L2FGkRPxHphadXPjo1T6W+CseNNY7EkjM= -github.com/theupdateframework/notary v0.7.0/go.mod h1:c9DRxcmhHmVLDay4/2fUYdISnHqbFDGRSlXPO0AhYWw= github.com/tmc/grpc-websocket-proxy v0.0.0-20220101234140-673ab2c3ae75/go.mod h1:KO6IkyS8Y3j8OdNO85qEYBsRPuteD+YciPomcXdrMnk= github.com/tonistiigi/go-actions-cache v0.0.0-20250626083717-378c5ed1ddd9/go.mod h1:cD0SB2270BYw6HYKriFn4H6NRLhGj6ytf48YTpsm8LY= github.com/tonistiigi/go-archvariant v1.0.0/go.mod h1:TxFmO5VS6vMq2kvs3ht04iPXtu2rUT/erOnGFYfk5Ho= @@ -812,14 +811,17 @@ go.etcd.io/bbolt v1.4.2/go.mod h1:Is8rSHO/b4f3XigBC0lL0+4FwAQv3HXEEIgFMuKHceM= go.etcd.io/bbolt v1.4.3/go.mod h1:tKQlpPaYCVFctUIgFKFnAlvbmB3tpy1vkTnDWohtc0E= go.etcd.io/etcd/api/v3 v3.5.0/go.mod h1:cbVKeC6lCfl7j/8jBhAK6aIYO9XOjdptoxU/nLQcPvs= go.etcd.io/etcd/api/v3 v3.5.8/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k= +go.etcd.io/etcd/api/v3 v3.5.9/go.mod h1:uyAal843mC8uUVSLWz6eHa/d971iDGnCRpmKd2Z+X8k= go.etcd.io/etcd/api/v3 v3.5.10/go.mod h1:TidfmT4Uycad3NM/o25fG3J07odo4GBB9hoxaodFCtI= go.etcd.io/etcd/client/pkg/v3 v3.5.0/go.mod h1:IJHfcCEKxYu1Os13ZdwCwIUTUVGYTSAM3YSwc9/Ac1g= go.etcd.io/etcd/client/pkg/v3 v3.5.8/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4= +go.etcd.io/etcd/client/pkg/v3 v3.5.9/go.mod h1:y+CzeSmkMpWN2Jyu1npecjB9BBnABxGM4pN8cGuJeL4= go.etcd.io/etcd/client/pkg/v3 v3.5.10/go.mod h1:DYivfIviIuQ8+/lCq4vcxuseg2P2XbHygkKwFo9fc8U= go.etcd.io/etcd/client/v2 v2.305.0/go.mod h1:h9puh54ZTgAKtEbut2oe9P4L/oqKCVB6xsXlzd7alYQ= go.etcd.io/etcd/client/v2 v2.305.10/go.mod h1:m3CKZi69HzilhVqtPDcjhSGp+kA1OmbNn0qamH80xjA= go.etcd.io/etcd/client/v2 v2.305.16/go.mod h1:h9YxWCzcdvZENbfzBTFCnoNumr2ax3F19sKMqHFmXHE= go.etcd.io/etcd/client/v3 v3.5.8/go.mod h1:idZYIPVkttBJBiRigkB5EM0MmEyx8jcl18zCV3F5noc= +go.etcd.io/etcd/client/v3 v3.5.9/go.mod h1:i/Eo5LrZ5IKqpbtpPDuaUnDOUv471oDg8cjQaUr2MbA= go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7P1VkOKc= go.etcd.io/etcd/pkg/v3 v3.5.10/go.mod h1:TKTuCKKcF1zxmfKWDkfz5qqYaE3JncKKZPFf8c1nFUs= go.etcd.io/etcd/pkg/v3 v3.5.16/go.mod h1:+lutCZHG5MBBFI/U4eYT5yL7sJfnexsoM20Y0t2uNuY= @@ -909,10 +911,14 @@ go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= go.uber.org/atomic v1.10.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0= go.uber.org/multierr v1.6.0 h1:y6IPFStTAIT5Ytl7/XYmHvzXQ7S3g/IeZW9hyZ5thw4= go.uber.org/multierr v1.6.0/go.mod h1:cdWPpRnG4AhwMwsgIHip0KRBQjJy5kYEpYjJxpXp9iU= +go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0= +go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y= go.uber.org/zap v1.17.0/go.mod h1:MXVU+bhUf/A7Xi2HNOnopQOrmycQ5Ih87HtOu4q5SSo= go.uber.org/zap v1.21.0/go.mod h1:wjWOCqI0f2ZZrJF/UufIOkiC8ii6tm1iqIsLo76RfJw= go.uber.org/zap v1.24.0/go.mod h1:2kMP+WWQ8aoFoedH3T2sq6iJ2yDWpHbP0f6MQbS9Gkg= go.uber.org/zap v1.26.0/go.mod h1:dtElttAiwGvoJ/vj4IwHBS/gXsEu/pZ50mUIRWuG0so= +go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8= +go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E= golang.org/x/crypto v0.0.0-20190611184440-5c40567a22f8/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20201002170205-7f63de1d35b0/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= @@ -1303,6 +1309,8 @@ k8s.io/apiextensions-apiserver v0.29.0/go.mod h1:TKmpy3bTS0mr9pylH0nOt/QzQRrW7/h k8s.io/apiextensions-apiserver v0.31.2 h1:W8EwUb8+WXBLu56ser5IudT2cOho0gAKeTOnywBLxd0= k8s.io/apiextensions-apiserver v0.32.1 h1:hjkALhRUeCariC8DiVmb5jj0VjIc1N0DREP32+6UXZw= k8s.io/apiextensions-apiserver v0.32.1/go.mod h1:sxWIGuGiYov7Io1fAS2X06NjMIk5CbRHc2StSmbaQto= +k8s.io/apiextensions-apiserver v0.34.1 h1:NNPBva8FNAPt1iSVwIE0FsdrVriRXMsaWFMqJbII2CI= +k8s.io/apiextensions-apiserver v0.34.1/go.mod h1:hP9Rld3zF5Ay2Of3BeEpLAToP+l4s5UlxiHfqRaRcMc= k8s.io/apimachinery v0.24.3/go.mod h1:82Bi4sCzVBdpYjyI4jY6aHX+YCUchUIrZrXKedjd2UM= k8s.io/apiserver v0.25.6/go.mod h1:IEp2B2/FvQ8GmdspscUoUS0iFF/GGc6NVrJ/cTM4OaA= k8s.io/apiserver v0.27.7/go.mod h1:OrLG9RwCOerutAlo8QJW5EHzUG9Dad7k6rgcDUNSO/w= @@ -1310,6 +1318,8 @@ k8s.io/apiserver v0.29.0/go.mod h1:31n78PsRKPmfpee7/l9NYEv67u6hOL6AfcE761HapDM= k8s.io/apiserver v0.31.2 h1:VUzOEUGRCDi6kX1OyQ801m4A7AUPglpsmGvdsekmcI4= k8s.io/apiserver v0.32.1 h1:oo0OozRos66WFq87Zc5tclUX2r0mymoVHRq8JmR7Aak= k8s.io/apiserver v0.32.1/go.mod h1:UcB9tWjBY7aryeI5zAgzVJB/6k7E97bkr1RgqDz0jPw= +k8s.io/apiserver v0.34.1 h1:U3JBGdgANK3dfFcyknWde1G6X1F4bg7PXuvlqt8lITA= +k8s.io/apiserver v0.34.1/go.mod h1:eOOc9nrVqlBI1AFCvVzsob0OxtPZUCPiUJL45JOTBG0= k8s.io/code-generator v0.17.2/go.mod h1:DVmfPQgxQENqDIzVR2ddLXMH34qeszkKSdH/N+s+38s= k8s.io/code-generator v0.25.6/go.mod h1:aDxzxJynLKQkaa117y0FFcgZ5jG8+GobxZ2JUntmvKk= k8s.io/code-generator v0.27.7/go.mod h1:w1YF/xQcTg+d9Ag+04xuRqER+q8rDnJ70ynLql8/RLA= @@ -1319,7 +1329,11 @@ k8s.io/code-generator v0.34.1/go.mod h1:DeWjekbDnJWRwpw3s0Jat87c+e0TgkxoR4ar608y k8s.io/component-base v0.28.6 h1:G4T8VrcQ7xZou3by/fY5NU5mfxOBlWaivS2lPrEltAo= k8s.io/component-base v0.28.6/go.mod h1:Dg62OOG3ALu2P4nAG00UdsuHoNLQJ5VsUZKQlLDcS+E= k8s.io/component-base v0.29.0/go.mod h1:sADonFTQ9Zc9yFLghpDpmNXEdHyQmFIGbiuZbqAXQ1M= +k8s.io/component-base v0.34.2 h1:HQRqK9x2sSAsd8+R4xxRirlTjowsg6fWCPwWYeSvogQ= +k8s.io/component-base v0.34.2/go.mod h1:9xw2FHJavUHBFpiGkZoKuYZ5pdtLKe97DEByaA+hHbM= k8s.io/component-helpers v0.29.0/go.mod h1:j2coxVfmzTOXWSE6sta0MTgNSr572Dcx68F6DD+8fWc= +k8s.io/component-helpers v0.34.2 h1:RIUGDdU+QFzeVKLZ9f05sXTNAtJrRJ3bnbMLrogCrvM= +k8s.io/component-helpers v0.34.2/go.mod h1:pLi+GByuRTeFjjcezln8gHL7LcT6HImkwVQ3A2SQaEE= k8s.io/cri-api v0.34.1/go.mod h1:4qVUjidMg7/Z9YGZpqIDygbkPWkg3mkS1PvOx/kpHTE= k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= k8s.io/gengo v0.0.0-20190822140433-26a664648505/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0= @@ -1347,6 +1361,7 @@ k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKf k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= k8s.io/kube-openapi v0.0.0-20231010175941-2dd684a91f00/go.mod h1:AsvuZPBlUDVuCdzJ87iajxtXuR9oktsTctW/R9wwouA= k8s.io/kubernetes v1.31.2 h1:VNSu4O7Xn5FFRsh9ePXyEPg6ucR21fOftarSdi053Gs= +k8s.io/kubernetes v1.31.7/go.mod h1:9xmT2buyTYj8TRKwRae7FcuY8k5+xlxv7VivvO0KKfs= k8s.io/metrics v0.29.0/go.mod h1:UCuTT4dC/x/x6ODSk87IWIZQnuAfcwxOjb1gjWJdjMA= k8s.io/metrics v0.30.3/go.mod h1:W06L2nXRhOwPkFYDJYWdEIS3u6JcJy3ebIPYbndRs6A= k8s.io/metrics v0.32.3/go.mod h1:9R1Wk5cb+qJpCQon9h52mgkVCcFeYxcY+YkumfwHVCU= @@ -1374,6 +1389,7 @@ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.28.0/go.mod h1:VHVDI/ sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.29.0/go.mod h1:z7+wmGM2dfIiLRfrC6jb5kV2Mq/sK1ZP303cxzkV5Y4= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0 h1:CPT0ExVicCzcpeN4baWEV2ko2Z/AsiZgEdwgcfwLgMo= sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.0/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= +sigs.k8s.io/apiserver-network-proxy/konnectivity-client v0.31.2/go.mod h1:Ve9uj1L+deCXFrPOk1LpFXqTg7LCFzFso6PA48q/XZw= sigs.k8s.io/controller-runtime v0.13.1/go.mod h1:Zbz+el8Yg31jubvAEyglRZGdLAjplZl+PgtYNI6WNTI= sigs.k8s.io/controller-runtime v0.15.3/go.mod h1:kp4jckA4vTx281S/0Yk2LFEEQe67mjg+ev/yknv47Ds= sigs.k8s.io/controller-tools v0.7.0/go.mod h1:bpBAo0VcSDDLuWt47evLhMLPxRPxMDInTEH/YbdeMK0= @@ -1393,6 +1409,7 @@ sigs.k8s.io/structured-merge-diff v0.0.0-20190525122527-15d366b2352e/go.mod h1:w sigs.k8s.io/structured-merge-diff/v4 v4.0.2/go.mod h1:bJZC9H9iH24zzfZ/41RGcq60oK1F7G282QMXDPYydCw= sigs.k8s.io/structured-merge-diff/v4 v4.2.1/go.mod h1:j/nl6xW8vLS49O8YvXW1ocPhZawJtm+Yrr7PPRQ0Vg4= sigs.k8s.io/structured-merge-diff/v4 v4.2.3/go.mod h1:qjx8mGObPmV2aSZepjQjbmb2ihdVs8cGKBraizNC69E= +sigs.k8s.io/structured-merge-diff/v4 v4.4.2/go.mod h1:N8f93tFZh9U6vpxwRArLiikrE5/2tiu1w1AGfACIGE4= sigs.k8s.io/structured-merge-diff/v4 v4.6.0/go.mod h1:dDy58f92j70zLsuZVuUX5Wp9vtxXpaZnkPGWeqDfCps= sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o= sigs.k8s.io/yaml v1.2.0/go.mod h1:yfXDCHCao9+ENCvLSE62v9VSji2MKu5jeNfTrofGhJc= From afd4698ca3d00c688c31ca9126f2bcef7c6805dc Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Tue, 20 Jan 2026 18:26:01 +0100 Subject: [PATCH 12/24] Refactored local_xxx commands --- .../pkg/cmd/local_cluster_create_cmd.go | 2 +- .../pkg/cmd/local_cluster_delete_cmd.go | 9 + .../pkg/cmd/local_cluster_start_cmd.go | 6 + .../pkg/cmd/local_cluster_status_cmd.go | 6 + .../pkg/cmd/local_cluster_stop_cmd.go | 6 + .../pkg/cmd/local_config_edit_cmd.go | 120 ++--- .../pkg/cmd/local_config_reset_cmd.go | 27 +- .../pkg/cmd/local_config_view_cmd.go | 4 +- .../pkg/cmd/local_mirror_delete_cmd.go | 2 +- .../pkg/cmd/local_mirror_deploy_cmd.go | 6 +- .../pkg/cmd/local_registry_delete_cmd.go | 6 + .../pkg/cmd/local_registry_deploy_cmd.go | 13 +- .../pkg/cmd/local_registry_prune_cmd.go | 6 + .../pkg/cmd/local_resolver_delete_cmd.go | 6 + .../pkg/cmd/local_resolver_deploy_cmd.go | 12 + .../pkg/cmd/local_resolver_update_cmd.go | 12 + .../pkg/cmd/local_secrets_add_ca_cmd.go | 141 ++++++ .../pkg/cmd/local_secrets_add_cmd.go | 441 ------------------ .../pkg/cmd/local_secrets_add_cmd_group.go | 35 ++ .../pkg/cmd/local_secrets_add_generic_cmd.go | 56 +++ .../pkg/cmd/local_secrets_add_registry_cmd.go | 154 ++++++ .../pkg/cmd/local_secrets_add_tls_cmd.go | 155 ++++++ .../pkg/cmd/local_secrets_export_cmd.go | 45 +- .../pkg/cmd/local_secrets_import_cmd.go | 8 +- .../pkg/cmd/local_secrets_list_cmd.go | 60 ++- .../pkg/cmd/local_secrets_remove_cmd.go | 55 ++- .../pkg/cmd/local_secrets_sync_cmd.go | 13 +- 27 files changed, 839 insertions(+), 567 deletions(-) create mode 100644 client-programs/pkg/cmd/local_secrets_add_ca_cmd.go delete mode 100644 client-programs/pkg/cmd/local_secrets_add_cmd.go create mode 100644 client-programs/pkg/cmd/local_secrets_add_cmd_group.go create mode 100644 client-programs/pkg/cmd/local_secrets_add_generic_cmd.go create mode 100644 client-programs/pkg/cmd/local_secrets_add_registry_cmd.go create mode 100644 client-programs/pkg/cmd/local_secrets_add_tls_cmd.go diff --git a/client-programs/pkg/cmd/local_cluster_create_cmd.go b/client-programs/pkg/cmd/local_cluster_create_cmd.go index 534ee9ec4..dd06da2ee 100644 --- a/client-programs/pkg/cmd/local_cluster_create_cmd.go +++ b/client-programs/pkg/cmd/local_cluster_create_cmd.go @@ -22,7 +22,7 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/utils" ) -var ( +const ( localClusterCreateExample = ` # Create local educates cluster (no configuration, uses nip.io wildcard domain and Kind as provider config defaults) educates local cluster create diff --git a/client-programs/pkg/cmd/local_cluster_delete_cmd.go b/client-programs/pkg/cmd/local_cluster_delete_cmd.go index ab9a7303b..c32779aaa 100644 --- a/client-programs/pkg/cmd/local_cluster_delete_cmd.go +++ b/client-programs/pkg/cmd/local_cluster_delete_cmd.go @@ -8,6 +8,14 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/resolver" ) +const localClusterDeleteExample = ` + # Delete the local Kubernetes cluster + educates local cluster delete + + # Delete the local Kubernetes cluster and all components (registry, mirrors and resolver) + educates local cluster delete --all +` + type LocalClusterDeleteOptions struct { Kubeconfig string AllComponents bool @@ -34,6 +42,7 @@ func (p *ProjectInfo) NewLocalClusterDeleteCmd() *cobra.Command { Use: "delete", Short: "Deletes the local Kubernetes cluster", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: localClusterDeleteExample, } c.Flags().BoolVar( diff --git a/client-programs/pkg/cmd/local_cluster_start_cmd.go b/client-programs/pkg/cmd/local_cluster_start_cmd.go index c7f7a2ac6..a9c990df3 100644 --- a/client-programs/pkg/cmd/local_cluster_start_cmd.go +++ b/client-programs/pkg/cmd/local_cluster_start_cmd.go @@ -6,6 +6,11 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/cluster" ) +const localClusterStartExample = ` + # Start the local Kubernetes cluster + educates local cluster start +` + func (p *ProjectInfo) NewLocalClusterStartCmd() *cobra.Command { var c = &cobra.Command{ Args: cobra.NoArgs, @@ -16,6 +21,7 @@ func (p *ProjectInfo) NewLocalClusterStartCmd() *cobra.Command { return c.StartCluster() }, + Example: localClusterStartExample, } return c diff --git a/client-programs/pkg/cmd/local_cluster_status_cmd.go b/client-programs/pkg/cmd/local_cluster_status_cmd.go index 912edec6a..c62b86feb 100644 --- a/client-programs/pkg/cmd/local_cluster_status_cmd.go +++ b/client-programs/pkg/cmd/local_cluster_status_cmd.go @@ -6,6 +6,11 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/cluster" ) +const localClusterStatusExample = ` + # Get status of the local Kubernetes cluster + educates local cluster status +` + func (p *ProjectInfo) NewLocalClusterStatusCmd() *cobra.Command { var c = &cobra.Command{ Args: cobra.NoArgs, @@ -16,6 +21,7 @@ func (p *ProjectInfo) NewLocalClusterStatusCmd() *cobra.Command { return c.ClusterStatus() }, + Example: localClusterStatusExample, } return c diff --git a/client-programs/pkg/cmd/local_cluster_stop_cmd.go b/client-programs/pkg/cmd/local_cluster_stop_cmd.go index c7685dc0b..f7b419aaa 100644 --- a/client-programs/pkg/cmd/local_cluster_stop_cmd.go +++ b/client-programs/pkg/cmd/local_cluster_stop_cmd.go @@ -6,6 +6,11 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/cluster" ) +const localClusterStopExample = ` + # Stop the local Kubernetes cluster + educates local cluster stop +` + func (p *ProjectInfo) NewLocalClusterStopCmd() *cobra.Command { var c = &cobra.Command{ Args: cobra.NoArgs, @@ -16,6 +21,7 @@ func (p *ProjectInfo) NewLocalClusterStopCmd() *cobra.Command { return c.StopCluster() }, + Example: localClusterStopExample, } return c diff --git a/client-programs/pkg/cmd/local_config_edit_cmd.go b/client-programs/pkg/cmd/local_config_edit_cmd.go index 2b76ce1b6..9a5222c7a 100644 --- a/client-programs/pkg/cmd/local_config_edit_cmd.go +++ b/client-programs/pkg/cmd/local_config_edit_cmd.go @@ -6,88 +6,102 @@ import ( "os/exec" "path" - "github.com/pkg/errors" - "github.com/spf13/cobra" "github.com/educates/educates-training-platform/client-programs/pkg/config" "github.com/educates/educates-training-platform/client-programs/pkg/utils" + "github.com/pkg/errors" + "github.com/spf13/cobra" ) -func (p *ProjectInfo) NewLocalConfigEditCmd() *cobra.Command { - var c = &cobra.Command{ - Args: cobra.NoArgs, - Use: "edit", - Short: "Edit local configuration", - RunE: func(_ *cobra.Command, _ []string) error { - err := os.MkdirAll(utils.GetEducatesHomeDir(), os.ModePerm) +const localConfigEditExample = ` + # Edit the local configuration + educates local config edit +` + +type LocalConfigEditOptions struct {} + +func (o *LocalConfigEditOptions) Run() error { + err := os.MkdirAll(utils.GetEducatesHomeDir(), os.ModePerm) + + if err != nil { + return errors.Wrapf(err, "unable to create configuration directory %q", utils.GetEducatesHomeDir()) + } + + valuesFilePath := path.Join(utils.GetEducatesHomeDir(), "values.yaml") + tmpValuesFilePath := fmt.Sprintf("%s.%d", valuesFilePath, os.Getpid()) - if err != nil { - return errors.Wrapf(err, "unable to create configuration directory %q", utils.GetEducatesHomeDir()) - } + tmpValuesFile, err := os.OpenFile(tmpValuesFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) - valuesFilePath := path.Join(utils.GetEducatesHomeDir(), "values.yaml") - tmpValuesFilePath := fmt.Sprintf("%s.%d", valuesFilePath, os.Getpid()) + if err != nil { + return errors.Wrapf(err, "unable to create local configuration file %q", tmpValuesFilePath) + } - tmpValuesFile, err := os.OpenFile(tmpValuesFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) + valuesFileData, err := os.ReadFile(valuesFilePath) - if err != nil { - return errors.Wrapf(err, "unable to create local configuration file %q", tmpValuesFilePath) - } + if err == nil && len(valuesFileData) != 0 { + tmpValuesFile.Write(valuesFileData) + } - valuesFileData, err := os.ReadFile(valuesFilePath) + tmpValuesFile.Close() - if err == nil && len(valuesFileData) != 0 { - tmpValuesFile.Write(valuesFileData) - } + defer os.Remove(tmpValuesFilePath) - tmpValuesFile.Close() + editor := "vi" - defer os.Remove(tmpValuesFilePath) + if s := os.Getenv("EDITOR"); s != "" { + editor = s + } - editor := "vi" + editorPath, err := exec.LookPath(editor) - if s := os.Getenv("EDITOR"); s != "" { - editor = s - } + if err != nil { + return errors.Wrapf(err, "unable to determine path for editor %q", editor) - editorPath, err := exec.LookPath(editor) + } - if err != nil { - return errors.Wrapf(err, "unable to determine path for editor %q", editor) + cmd := exec.Command(editorPath, tmpValuesFilePath) - } + cmd.Stdin = os.Stdin + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr - cmd := exec.Command(editorPath, tmpValuesFilePath) + err = cmd.Start() - cmd.Stdin = os.Stdin - cmd.Stdout = os.Stdout - cmd.Stderr = os.Stderr + if err != nil { + return errors.Wrapf(err, "cannot execute editor on configuration") + } - err = cmd.Start() + err = cmd.Wait() - if err != nil { - return errors.Wrapf(err, "cannot execute editor on configuration") - } + if err != nil { + return errors.Wrapf(err, "editing of values configuration failed") + } - err = cmd.Wait() + _, err = config.NewInstallationConfigFromFile(tmpValuesFilePath) - if err != nil { - return errors.Wrapf(err, "editing of values configuration failed") - } + if err != nil { + return errors.Wrapf(err, "error in values configuration file") + } - _, err = config.NewInstallationConfigFromFile(tmpValuesFilePath) + err = os.Rename(tmpValuesFilePath, valuesFilePath) - if err != nil { - return errors.Wrapf(err, "error in values configuration file") - } + if err != nil { + return errors.Wrapf(err, "unable to update default configuration") + } - err = os.Rename(tmpValuesFilePath, valuesFilePath) + return nil +} - if err != nil { - return errors.Wrapf(err, "unable to update default configuration") - } +func (p *ProjectInfo) NewLocalConfigEditCmd() *cobra.Command { + var o LocalConfigEditOptions - return nil + var c = &cobra.Command{ + Args: cobra.NoArgs, + Use: "edit", + Short: "Edit local configuration", + RunE: func(_ *cobra.Command, _ []string) error { + return o.Run() }, + Example: localConfigEditExample, } return c diff --git a/client-programs/pkg/cmd/local_config_reset_cmd.go b/client-programs/pkg/cmd/local_config_reset_cmd.go index 450b5ef8e..c7ab95b05 100644 --- a/client-programs/pkg/cmd/local_config_reset_cmd.go +++ b/client-programs/pkg/cmd/local_config_reset_cmd.go @@ -4,22 +4,37 @@ import ( "os" "path" - "github.com/spf13/cobra" "github.com/educates/educates-training-platform/client-programs/pkg/utils" + "github.com/spf13/cobra" ) +const localConfigResetExample = ` + # Reset the local configuration + educates local config reset +` + +type LocalConfigResetOptions struct {} + +func (o *LocalConfigResetOptions) Run() error { + // TODO: Move "values.yaml" to a constant + valuesFile := path.Join(utils.GetEducatesHomeDir(), "values.yaml") + + os.Remove(valuesFile) + + return nil +} + func (p *ProjectInfo) NewLocalConfigResetCmd() *cobra.Command { + var o LocalConfigResetOptions + var c = &cobra.Command{ Args: cobra.NoArgs, Use: "reset", Short: "Reset local configuration", RunE: func(_ *cobra.Command, _ []string) error { - valuesFile := path.Join(utils.GetEducatesHomeDir(), "values.yaml") - - os.Remove(valuesFile) - - return nil + return o.Run() }, + Example: localConfigResetExample, } return c diff --git a/client-programs/pkg/cmd/local_config_view_cmd.go b/client-programs/pkg/cmd/local_config_view_cmd.go index cbdb22240..1f45dd743 100644 --- a/client-programs/pkg/cmd/local_config_view_cmd.go +++ b/client-programs/pkg/cmd/local_config_view_cmd.go @@ -3,13 +3,13 @@ package cmd import ( "fmt" + "github.com/educates/educates-training-platform/client-programs/pkg/config" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/config" "gopkg.in/yaml.v2" ) -var ( +const ( localConfigViewExample = ` # View local educates cluster configuration by default. Uses nip.io wildcard domain and Kind as provider config defaults educates local config view --config NULL diff --git a/client-programs/pkg/cmd/local_mirror_delete_cmd.go b/client-programs/pkg/cmd/local_mirror_delete_cmd.go index 8ecb53fea..0cc7565b7 100644 --- a/client-programs/pkg/cmd/local_mirror_delete_cmd.go +++ b/client-programs/pkg/cmd/local_mirror_delete_cmd.go @@ -7,7 +7,7 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/registry" ) -var ( +const ( localMirrorDeleteExample = ` # Delete a local image registry mirror educates local mirror delete mymirror diff --git a/client-programs/pkg/cmd/local_mirror_deploy_cmd.go b/client-programs/pkg/cmd/local_mirror_deploy_cmd.go index baf03678c..f86b8a82c 100644 --- a/client-programs/pkg/cmd/local_mirror_deploy_cmd.go +++ b/client-programs/pkg/cmd/local_mirror_deploy_cmd.go @@ -8,17 +8,17 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/registry" ) -var ( +const ( localMirrorDeployExample = ` # Mirror DockerHub anonymously (may be subject to rate limits): educates local mirror deploy docker.io # Mirror DockerHub with credentials (recommended to avoid throttling): educates local mirror deploy docker.io --username --password - + # Mirror a private registry: educates local mirror deploy myprivateregistry.com --username --password - + # Mirror a registry with a different remote URL: educates local mirror deploy mymirror --url registry.example.com ` diff --git a/client-programs/pkg/cmd/local_registry_delete_cmd.go b/client-programs/pkg/cmd/local_registry_delete_cmd.go index 36f7e04c2..602702dfb 100644 --- a/client-programs/pkg/cmd/local_registry_delete_cmd.go +++ b/client-programs/pkg/cmd/local_registry_delete_cmd.go @@ -6,12 +6,18 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/registry" ) +const localRegistryDeleteExample = ` + # Delete the local image registry + educates local registry delete +` + func (p *ProjectInfo) NewLocalRegistryDeleteCmd() *cobra.Command { var c = &cobra.Command{ Args: cobra.NoArgs, Use: "delete", Short: "Deletes the local image registry", RunE: func(_ *cobra.Command, _ []string) error { return registry.DeleteRegistry() }, + Example: localRegistryDeleteExample, } return c diff --git a/client-programs/pkg/cmd/local_registry_deploy_cmd.go b/client-programs/pkg/cmd/local_registry_deploy_cmd.go index e6c7d7346..238d25456 100644 --- a/client-programs/pkg/cmd/local_registry_deploy_cmd.go +++ b/client-programs/pkg/cmd/local_registry_deploy_cmd.go @@ -11,6 +11,17 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/utils" ) +const localRegistryDeployExample = ` + # Deploy the local image registry + educates local registry deploy + + # Deploy the local image registry with a custom bind IP + educates local registry deploy --bind-ip 192.168.1.100 + + # Deploy the local image registry with a custom kubeconfig + educates local registry deploy --kubeconfig /path/to/kubeconfig --context my-context +` + type LocalRegistryDeployOptions struct { KubeconfigOptions BindIP string @@ -26,7 +37,6 @@ func (o *LocalRegistryDeployOptions) Run() error { // This will fail if you do not have a Kubernetes cluster, but we can still // deploy just the image registry alone without Kubernetes. If a Kubernetes // cluster is created later, then the registry service will be added then. - clusterConfig, err := cluster.NewClusterConfigIfAvailable(o.Kubeconfig, o.Context) if err != nil { @@ -65,6 +75,7 @@ func (p *ProjectInfo) NewLocalRegistryDeployCmd() *cobra.Command { return o.Run() }, + Example: localRegistryDeployExample, } c.Flags().StringVar( diff --git a/client-programs/pkg/cmd/local_registry_prune_cmd.go b/client-programs/pkg/cmd/local_registry_prune_cmd.go index 3becc460c..5f645f0f1 100644 --- a/client-programs/pkg/cmd/local_registry_prune_cmd.go +++ b/client-programs/pkg/cmd/local_registry_prune_cmd.go @@ -7,6 +7,11 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/registry" ) +const localRegistryPruneExample = ` + # Prune the local image registry + educates local registry prune +` + type LocalRegistryPruneOptions struct { } @@ -28,6 +33,7 @@ func (p *ProjectInfo) NewLocalRegistryPruneCmd() *cobra.Command { Use: "prune", Short: "Prunes the local image registry (deletes any untagged image)", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: localRegistryPruneExample, } return c diff --git a/client-programs/pkg/cmd/local_resolver_delete_cmd.go b/client-programs/pkg/cmd/local_resolver_delete_cmd.go index ac00c1d37..51a04c96d 100644 --- a/client-programs/pkg/cmd/local_resolver_delete_cmd.go +++ b/client-programs/pkg/cmd/local_resolver_delete_cmd.go @@ -6,12 +6,18 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/resolver" ) +const localResolverDeleteExample = ` + # Delete the local DNS resolver + educates local resolver delete +` + func (p *ProjectInfo) NewLocalResolverDeleteCmd() *cobra.Command { var c = &cobra.Command{ Args: cobra.NoArgs, Use: "delete", Short: "Deletes the local DNS resolver", RunE: func(_ *cobra.Command, _ []string) error { return resolver.DeleteResolver() }, + Example: localResolverDeleteExample, } return c diff --git a/client-programs/pkg/cmd/local_resolver_deploy_cmd.go b/client-programs/pkg/cmd/local_resolver_deploy_cmd.go index aaf643e7b..b18fd8053 100644 --- a/client-programs/pkg/cmd/local_resolver_deploy_cmd.go +++ b/client-programs/pkg/cmd/local_resolver_deploy_cmd.go @@ -7,6 +7,17 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/resolver" ) +const localResolverDeployExample = ` + # Deploy the local DNS resolver + educates local resolver deploy + + # Deploy the local DNS resolver with a custom config + educates local resolver deploy --config /path/to/config.yaml + + # Deploy the local DNS resolver with a custom domain + educates local resolver deploy --domain test.educates.io +` + type LocalResolverDeployOptions struct { Config string Domain string @@ -41,6 +52,7 @@ func (p *ProjectInfo) NewLocalResolverDeployCmd() *cobra.Command { Use: "deploy", Short: "Deploys a local DNS resolver", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: localResolverDeployExample, } c.Flags().StringVar( diff --git a/client-programs/pkg/cmd/local_resolver_update_cmd.go b/client-programs/pkg/cmd/local_resolver_update_cmd.go index 93c0648bc..82a4cf7a0 100644 --- a/client-programs/pkg/cmd/local_resolver_update_cmd.go +++ b/client-programs/pkg/cmd/local_resolver_update_cmd.go @@ -7,6 +7,17 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/resolver" ) +const localResolverUpdateExample = ` + # Update the local DNS resolver + educates local resolver update + + # Update the local DNS resolver with a custom config + educates local resolver update --config /path/to/config.yaml + + # Update the local DNS resolver with a custom domain + educates local resolver update --domain test.educates.io +` + type LocalResolverUpdateOptions struct { Config string Domain string @@ -37,6 +48,7 @@ func (p *ProjectInfo) NewLocalResolverUpdateCmd() *cobra.Command { Use: "update", Short: "Updates the local DNS resolver", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: localResolverUpdateExample, } c.Flags().StringVar( diff --git a/client-programs/pkg/cmd/local_secrets_add_ca_cmd.go b/client-programs/pkg/cmd/local_secrets_add_ca_cmd.go new file mode 100644 index 000000000..0810931cb --- /dev/null +++ b/client-programs/pkg/cmd/local_secrets_add_ca_cmd.go @@ -0,0 +1,141 @@ +package cmd + +import ( + "encoding/json" + "os" + "path" + "regexp" + + "github.com/educates/educates-training-platform/client-programs/pkg/utils" + "github.com/pkg/errors" + "github.com/spf13/cobra" + apiv1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/yaml" +) + +const localSecretsAddCaExample = ` + # Create a CA secret + educates local secrets add ca my-ca + + # Create a CA secret with a custom domain + educates local secrets add ca my-ca --domain my-domain.com + + # Create a CA secret with a custom certificate file + educates local secrets add ca my-ca --cert /path/to/ca.crt +` + +type LocalSecretsAddCaOptions struct { + CertFile string + IngressDomain string +} + +func (o *LocalSecretsAddCaOptions) Run(name string) error { + var err error + var matched bool + + if matched, err = regexp.MatchString("^[a-z0-9]([.a-z0-9-]+)?[a-z0-9]$", name); err != nil { + return errors.Wrapf(err, "regex match on secret name failed") + } + + if !matched { + return errors.New("invalid secret name") + } + + var certificateFileData []byte + + if o.CertFile != "" { + certificateFileData, err = os.ReadFile(o.CertFile) + + if err != nil { + return errors.Wrapf(err, "failed to read certificate file %s", o.CertFile) + } + } + + secret := &apiv1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Annotations: map[string]string{}, + }, + // Type: "kubernetes.io/tls", + Data: map[string][]byte{ + "ca.crt": certificateFileData, + }, + } + + if o.IngressDomain != "" { + secret.ObjectMeta.Annotations["training.educates.dev/domain"] = o.IngressDomain + } + + secretData, err := json.MarshalIndent(&secret, "", " ") + + if err != nil { + return errors.Wrap(err, "failed to generate secret data") + } + + secretData, err = yaml.JSONToYAML(secretData) + + if err != nil { + return errors.Wrap(err, "failed to generate YAML data") + } + + secretsCacheDir := path.Join(utils.GetEducatesHomeDir(), "secrets") + + err = os.MkdirAll(secretsCacheDir, os.ModePerm) + + if err != nil { + return errors.Wrapf(err, "unable to create secrets cache directory") + } + + secretFilePath := path.Join(secretsCacheDir, name+".yaml") + + secretFile, err := os.OpenFile(secretFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) + + if err != nil { + return errors.Wrapf(err, "unable to create secret file %s", secretFilePath) + } + + if _, err := secretFile.Write(secretData); err != nil { + return errors.Wrapf(err, "unable to write secret file %s", secretFilePath) + } + + if err := secretFile.Close(); err != nil { + return errors.Wrapf(err, "unable to close secret file %s", secretFilePath) + } + + return nil +} + +func (p *ProjectInfo) NewLocalSecretsAddCaCmd() *cobra.Command { + var o LocalSecretsAddCaOptions + + var c = &cobra.Command{ + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return utils.CmdError(cmd, "name is required", "NAME") + } + return nil + }, + Use: "ca NAME", + Short: "Create a CA secret", + RunE: func(_ *cobra.Command, args []string) error { return o.Run(args[0]) }, + Example: localSecretsAddCaExample, + } + + c.Flags().StringVar( + &o.CertFile, + "cert", + "", + "path to PEM encoded CA certificate", + ) + c.Flags().StringVar( + &o.IngressDomain, + "domain", + "", + "wildcard ingress domain matching certificate", + ) + + c.MarkFlagsRequiredTogether("cert") + + return c +} diff --git a/client-programs/pkg/cmd/local_secrets_add_cmd.go b/client-programs/pkg/cmd/local_secrets_add_cmd.go deleted file mode 100644 index eadd56184..000000000 --- a/client-programs/pkg/cmd/local_secrets_add_cmd.go +++ /dev/null @@ -1,441 +0,0 @@ -package cmd - -import ( - "encoding/base64" - "encoding/json" - "fmt" - "os" - "path" - "regexp" - - "github.com/pkg/errors" - "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/utils" - apiv1 "k8s.io/api/core/v1" - metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/kubectl/pkg/util/templates" - "sigs.k8s.io/yaml" -) - -func (p *ProjectInfo) NewLocalSecretsAddCmdGroup() *cobra.Command { - var c = &cobra.Command{ - Args: cobra.NoArgs, - Use: "add", - Short: "Add secret to the cache", - } - - // Use a command group as it allows us to dictate the order in which they - // are displayed in the help message, as otherwise they are displayed in - // sort order. - - commandGroups := templates.CommandGroups{ - { - Message: "Available Commands:", - Commands: []*cobra.Command{ - p.NewLocalSecretsAddCaCmd(), - p.NewLocalSecretsAddDockerRegistryCmd(), - // NewLocalSecretsAddGenericCmd(), - p.NewLocalSecretsAddTlsCmd(), - }, - }, - } - - commandGroups.Add(c) - - templates.ActsAsRootCommand(c, []string{"--help"}, commandGroups...) - - return c -} - -type LocalSecretsAddTlsOptions struct { - CertFile string - KeyFile string - IngressDomain string -} - -func (o *LocalSecretsAddTlsOptions) Run(name string) error { - var err error - var matched bool - - if matched, err = regexp.MatchString("^[a-z0-9]([.a-z0-9-]+)?[a-z0-9]$", name); err != nil { - return errors.Wrapf(err, "regex match on secret name failed") - } - - if !matched { - return errors.New("invalid secret name") - } - - var certificateFileData []byte - var certificateKeyFileData []byte - - if o.CertFile != "" { - certificateFileData, err = os.ReadFile(o.CertFile) - - if err != nil { - return errors.Wrapf(err, "failed to read certificate file %s", o.CertFile) - } - } - - if o.KeyFile != "" { - certificateKeyFileData, err = os.ReadFile(o.KeyFile) - - if err != nil { - return errors.Wrapf(err, "failed to read certificate key file %s", o.KeyFile) - } - } - - secret := &apiv1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Annotations: map[string]string{}, - }, - Type: "kubernetes.io/tls", - Data: map[string][]byte{ - "tls.crt": certificateFileData, - "tls.key": certificateKeyFileData, - }, - } - - if o.IngressDomain != "" { - secret.ObjectMeta.Annotations["training.educates.dev/domain"] = o.IngressDomain - } - - secretData, err := json.MarshalIndent(&secret, "", " ") - - if err != nil { - return errors.Wrap(err, "failed to generate secret data") - } - - secretData, err = yaml.JSONToYAML(secretData) - - if err != nil { - return errors.Wrap(err, "failed to generate YAML data") - } - - secretsCacheDir := path.Join(utils.GetEducatesHomeDir(), "secrets") - - err = os.MkdirAll(secretsCacheDir, os.ModePerm) - - if err != nil { - return errors.Wrapf(err, "unable to create secrets cache directory") - } - - secretFilePath := path.Join(secretsCacheDir, name+".yaml") - - secretFile, err := os.OpenFile(secretFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) - - if err != nil { - return errors.Wrapf(err, "unable to create secret file %s", secretFilePath) - } - - if _, err := secretFile.Write(secretData); err != nil { - return errors.Wrapf(err, "unable to write secret file %s", secretFilePath) - } - - if err := secretFile.Close(); err != nil { - return errors.Wrapf(err, "unable to close secret file %s", secretFilePath) - } - - return nil -} - -func (p *ProjectInfo) NewLocalSecretsAddTlsCmd() *cobra.Command { - var o LocalSecretsAddTlsOptions - - var c = &cobra.Command{ - Args: cobra.ExactArgs(1), - Use: "tls NAME", - Short: "Create a TLS secret", - RunE: func(_ *cobra.Command, args []string) error { return o.Run(args[0]) }, - } - - c.Flags().StringVar( - &o.CertFile, - "cert", - "", - "path to PEM encoded public key certificate", - ) - c.Flags().StringVar( - &o.KeyFile, - "key", - "", - "path to private key associated with given certificate", - ) - c.Flags().StringVar( - &o.IngressDomain, - "domain", - "", - "wildcard ingress domain matching certificate", - ) - - c.MarkFlagsRequiredTogether("cert", "key") - - return c -} - -type LocalSecretsAddCaOptions struct { - CertFile string - IngressDomain string -} - -func (o *LocalSecretsAddCaOptions) Run(name string) error { - var err error - var matched bool - - if matched, err = regexp.MatchString("^[a-z0-9]([.a-z0-9-]+)?[a-z0-9]$", name); err != nil { - return errors.Wrapf(err, "regex match on secret name failed") - } - - if !matched { - return errors.New("invalid secret name") - } - - var certificateFileData []byte - - if o.CertFile != "" { - certificateFileData, err = os.ReadFile(o.CertFile) - - if err != nil { - return errors.Wrapf(err, "failed to read certificate file %s", o.CertFile) - } - } - - secret := &apiv1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - Annotations: map[string]string{}, - }, - // Type: "kubernetes.io/tls", - Data: map[string][]byte{ - "ca.crt": certificateFileData, - }, - } - - if o.IngressDomain != "" { - secret.ObjectMeta.Annotations["training.educates.dev/domain"] = o.IngressDomain - } - - secretData, err := json.MarshalIndent(&secret, "", " ") - - if err != nil { - return errors.Wrap(err, "failed to generate secret data") - } - - secretData, err = yaml.JSONToYAML(secretData) - - if err != nil { - return errors.Wrap(err, "failed to generate YAML data") - } - - secretsCacheDir := path.Join(utils.GetEducatesHomeDir(), "secrets") - - err = os.MkdirAll(secretsCacheDir, os.ModePerm) - - if err != nil { - return errors.Wrapf(err, "unable to create secrets cache directory") - } - - secretFilePath := path.Join(secretsCacheDir, name+".yaml") - - secretFile, err := os.OpenFile(secretFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) - - if err != nil { - return errors.Wrapf(err, "unable to create secret file %s", secretFilePath) - } - - if _, err := secretFile.Write(secretData); err != nil { - return errors.Wrapf(err, "unable to write secret file %s", secretFilePath) - } - - if err := secretFile.Close(); err != nil { - return errors.Wrapf(err, "unable to close secret file %s", secretFilePath) - } - - return nil -} - -func (p *ProjectInfo) NewLocalSecretsAddCaCmd() *cobra.Command { - var o LocalSecretsAddCaOptions - - var c = &cobra.Command{ - Args: cobra.ExactArgs(1), - Use: "ca NAME", - Short: "Create a CA secret", - RunE: func(_ *cobra.Command, args []string) error { return o.Run(args[0]) }, - } - - c.Flags().StringVar( - &o.CertFile, - "cert", - "", - "path to PEM encoded CA certificate", - ) - c.Flags().StringVar( - &o.IngressDomain, - "domain", - "", - "wildcard ingress domain matching certificate", - ) - - c.MarkFlagsRequiredTogether("cert") - - return c -} - -type LocalSecretsAddDockerRegistryOptions struct { - Server string - Username string - Password string - Email string -} - -func (o *LocalSecretsAddDockerRegistryOptions) Run(name string) error { - var err error - var matched bool - - if matched, err = regexp.MatchString("^[a-z0-9]([.a-z0-9-]+)?[a-z0-9]$", name); err != nil { - return errors.Wrapf(err, "regex match on secret name failed") - } - - if !matched { - return errors.New("invalid secret name") - } - - authString := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", o.Username, o.Password))) - - dockerConfig := map[string]interface{}{ - "auths": map[string]interface{}{ - o.Server: map[string]string{ - "username": o.Username, - "password": o.Password, - "email": o.Email, - "auth": authString, - }, - }, - } - - dockerConfigData, _ := json.Marshal(dockerConfig) - - secret := &apiv1.Secret{ - ObjectMeta: metav1.ObjectMeta{ - Name: name, - }, - Type: "kubernetes.io/dockerconfigjson", - Data: map[string][]byte{ - ".dockerconfigjson": dockerConfigData, - }, - } - - secretData, err := json.MarshalIndent(&secret, "", " ") - - if err != nil { - return errors.Wrap(err, "failed to generate secret data") - } - - secretData, err = yaml.JSONToYAML(secretData) - - if err != nil { - return errors.Wrap(err, "failed to generate YAML data") - } - - secretsCacheDir := path.Join(utils.GetEducatesHomeDir(), "secrets") - - err = os.MkdirAll(secretsCacheDir, os.ModePerm) - - if err != nil { - return errors.Wrapf(err, "unable to create secrets cache directory") - } - - secretFilePath := path.Join(secretsCacheDir, name+".yaml") - - secretFile, err := os.OpenFile(secretFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) - - if err != nil { - return errors.Wrapf(err, "unable to create secret file %s", secretFile) - } - - if _, err = secretFile.Write(secretData); err != nil { - return errors.Wrapf(err, "unable to write secret file %s", secretFile) - } - - if err := secretFile.Close(); err != nil { - return errors.Wrapf(err, "unable to close secret file %s", secretFile) - } - - return nil -} - -func (p *ProjectInfo) NewLocalSecretsAddDockerRegistryCmd() *cobra.Command { - var o LocalSecretsAddDockerRegistryOptions - - var c = &cobra.Command{ - Args: cobra.ExactArgs(1), - Use: "docker-registry NAME", - Short: "Create a secret for use with a Docker registry", - RunE: func(_ *cobra.Command, args []string) error { return o.Run(args[0]) }, - } - - c.Flags().StringVar( - &o.Server, - "docker-server", - "https://index.docker.io/v1/", - "server location for docker registry", - ) - c.Flags().StringVar( - &o.Username, - "docker-username", - "", - "username for docker registry authentication", - ) - c.Flags().StringVar( - &o.Password, - "docker-password", - "", - "password for docker registry authentication", - ) - c.Flags().StringVar( - &o.Email, - "docker-email", - "", - "email for docker registry", - ) - - c.MarkFlagsRequiredTogether("docker-username", "docker-password", "docker-email") - - return c -} - -type LocalSecretsAddGenericOptions struct { - FileSources []string - LiteralSources []string -} - -func (o *LocalSecretsAddGenericOptions) Run(name string) error { - return nil -} - -func (p *ProjectInfo) NewLocalSecretsAddGenericCmd() *cobra.Command { - var o LocalSecretsAddGenericOptions - - var c = &cobra.Command{ - Args: cobra.ExactArgs(1), - Use: "generic NAME", - Short: "Create a secret from a local file, directory, or literal value", - RunE: func(_ *cobra.Command, args []string) error { return o.Run(args[0]) }, - } - - c.Flags().StringArrayVar( - &o.FileSources, - "from-file", - []string{}, - "Key files can be specified using their file path, in which case a default name will be given to them, or optionally with a name and file path, in which case the given name will be used. Specifying a directory will iterate each named file in the directory that is avalid secret key.", - ) - c.Flags().StringArrayVar( - &o.LiteralSources, - "from-literal", - []string{}, - "Specify a key and literal value to insert in secret (i.e. mykey=somevalue)", - ) - - return c -} diff --git a/client-programs/pkg/cmd/local_secrets_add_cmd_group.go b/client-programs/pkg/cmd/local_secrets_add_cmd_group.go new file mode 100644 index 000000000..1dd845c28 --- /dev/null +++ b/client-programs/pkg/cmd/local_secrets_add_cmd_group.go @@ -0,0 +1,35 @@ +package cmd + +import ( + "github.com/spf13/cobra" + "k8s.io/kubectl/pkg/util/templates" +) + +func (p *ProjectInfo) NewLocalSecretsAddCmdGroup() *cobra.Command { + var c = &cobra.Command{ + Args: cobra.NoArgs, + Use: "add", + Short: "Add secret to the cache", + } + + // Use a command group as it allows us to dictate the order in which they + // are displayed in the help message, as otherwise they are displayed in + // sort order. + commandGroups := templates.CommandGroups{ + { + Message: "Available Commands:", + Commands: []*cobra.Command{ + p.NewLocalSecretsAddCaCmd(), + p.NewLocalSecretsAddDockerRegistryCmd(), + // NewLocalSecretsAddGenericCmd(), + p.NewLocalSecretsAddTlsCmd(), + }, + }, + } + + commandGroups.Add(c) + + templates.ActsAsRootCommand(c, []string{"--help"}, commandGroups...) + + return c +} diff --git a/client-programs/pkg/cmd/local_secrets_add_generic_cmd.go b/client-programs/pkg/cmd/local_secrets_add_generic_cmd.go new file mode 100644 index 000000000..5f6e1ee3e --- /dev/null +++ b/client-programs/pkg/cmd/local_secrets_add_generic_cmd.go @@ -0,0 +1,56 @@ +package cmd + +import ( + "github.com/educates/educates-training-platform/client-programs/pkg/utils" + "github.com/spf13/cobra" +) + +const localSecretsAddGenericExample = ` + # Create a secret from a local file + educates local secrets add generic my-secret --from-file /path/to/file + + # Create a secret from a local directory + educates local secrets add generic my-secret --from-literal key=value +` + +type LocalSecretsAddGenericOptions struct { + FileSources []string + LiteralSources []string +} + +func (o *LocalSecretsAddGenericOptions) Run(name string) error { + return nil +} + +func (p *ProjectInfo) NewLocalSecretsAddGenericCmd() *cobra.Command { + var o LocalSecretsAddGenericOptions + + var c = &cobra.Command{ + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return utils.CmdError(cmd, "name is required", "NAME") + } + return nil + }, + + Use: "generic NAME", + Short: "Create a secret from a local file, directory, or literal value", + RunE: func(_ *cobra.Command, args []string) error { return o.Run(args[0]) }, + Example: localSecretsAddGenericExample, + } + + c.Flags().StringArrayVar( + &o.FileSources, + "from-file", + []string{}, + "Key files can be specified using their file path, in which case a default name will be given to them, or optionally with a name and file path, in which case the given name will be used. Specifying a directory will iterate each named file in the directory that is avalid secret key.", + ) + c.Flags().StringArrayVar( + &o.LiteralSources, + "from-literal", + []string{}, + "Specify a key and literal value to insert in secret (i.e. mykey=somevalue)", + ) + + return c +} diff --git a/client-programs/pkg/cmd/local_secrets_add_registry_cmd.go b/client-programs/pkg/cmd/local_secrets_add_registry_cmd.go new file mode 100644 index 000000000..f6589b281 --- /dev/null +++ b/client-programs/pkg/cmd/local_secrets_add_registry_cmd.go @@ -0,0 +1,154 @@ +package cmd + +import ( + "encoding/base64" + "encoding/json" + "fmt" + "os" + "path" + "regexp" + + "github.com/educates/educates-training-platform/client-programs/pkg/utils" + "github.com/pkg/errors" + "github.com/spf13/cobra" + apiv1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/yaml" +) + +const localSecretsAddDockerRegistryExample = ` + # Create a secret for use with Docker hub + educates local secrets add docker-registry my-registry --docker-username my-username --docker-password my-password --docker-email my-email + + # Create a secret for use with GitHub Container Registry + educates local secrets add docker-registry my-registry --docker-server https://ghcr.io --docker-username my-username --docker-password my-password --docker-email my-email +` + +type LocalSecretsAddDockerRegistryOptions struct { + Server string + Username string + Password string + Email string +} + +func (o *LocalSecretsAddDockerRegistryOptions) Run(name string) error { + var err error + var matched bool + + if matched, err = regexp.MatchString("^[a-z0-9]([.a-z0-9-]+)?[a-z0-9]$", name); err != nil { + return errors.Wrapf(err, "regex match on secret name failed") + } + + if !matched { + return errors.New("invalid secret name") + } + + authString := base64.StdEncoding.EncodeToString([]byte(fmt.Sprintf("%s:%s", o.Username, o.Password))) + + dockerConfig := map[string]interface{}{ + "auths": map[string]interface{}{ + o.Server: map[string]string{ + "username": o.Username, + "password": o.Password, + "email": o.Email, + "auth": authString, + }, + }, + } + + dockerConfigData, _ := json.Marshal(dockerConfig) + + secret := &apiv1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + }, + Type: "kubernetes.io/dockerconfigjson", + Data: map[string][]byte{ + ".dockerconfigjson": dockerConfigData, + }, + } + + secretData, err := json.MarshalIndent(&secret, "", " ") + + if err != nil { + return errors.Wrap(err, "failed to generate secret data") + } + + secretData, err = yaml.JSONToYAML(secretData) + + if err != nil { + return errors.Wrap(err, "failed to generate YAML data") + } + + secretsCacheDir := path.Join(utils.GetEducatesHomeDir(), "secrets") + + err = os.MkdirAll(secretsCacheDir, os.ModePerm) + + if err != nil { + return errors.Wrapf(err, "unable to create secrets cache directory") + } + + secretFilePath := path.Join(secretsCacheDir, name+".yaml") + + secretFile, err := os.OpenFile(secretFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) + + if err != nil { + return errors.Wrapf(err, "unable to create secret file %s", secretFilePath) + } + + if _, err = secretFile.Write(secretData); err != nil { + return errors.Wrapf(err, "unable to write secret file %s", secretFilePath) + } + + if err := secretFile.Close(); err != nil { + return errors.Wrapf(err, "unable to close secret file %s", secretFilePath) + } + + return nil +} + +func (p *ProjectInfo) NewLocalSecretsAddDockerRegistryCmd() *cobra.Command { + var o LocalSecretsAddDockerRegistryOptions + + var c = &cobra.Command{ + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return utils.CmdError(cmd, "name is required", "NAME") + } + return nil + }, + Use: "docker-registry NAME", + Short: "Create a secret for use with a Docker registry", + RunE: func(_ *cobra.Command, args []string) error { return o.Run(args[0]) }, + Example: localSecretsAddDockerRegistryExample, + } + + c.Flags().StringVar( + &o.Server, + "docker-server", + "https://index.docker.io/v1/", + "server location for docker registry", + ) + c.Flags().StringVar( + &o.Username, + "docker-username", + "", + "username for docker registry authentication", + ) + c.Flags().StringVar( + &o.Password, + "docker-password", + "", + "password for docker registry authentication", + ) + c.Flags().StringVar( + &o.Email, + "docker-email", + "", + "email for docker registry", + ) + + c.MarkFlagsRequiredTogether("docker-username", "docker-password", "docker-email") + + return c +} diff --git a/client-programs/pkg/cmd/local_secrets_add_tls_cmd.go b/client-programs/pkg/cmd/local_secrets_add_tls_cmd.go new file mode 100644 index 000000000..01be3588a --- /dev/null +++ b/client-programs/pkg/cmd/local_secrets_add_tls_cmd.go @@ -0,0 +1,155 @@ +package cmd + +import ( + "encoding/json" + "os" + "path" + "regexp" + + "github.com/educates/educates-training-platform/client-programs/pkg/utils" + "github.com/pkg/errors" + "github.com/spf13/cobra" + apiv1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + "sigs.k8s.io/yaml" +) + +const localSecretsAddTlsExample = ` + # Create a TLS secret + educates local secrets add tls my-tls --cert /path/to/cert.pem --key /path/to/key.pem + + # Create a TLS secret with a custom domain + educates local secrets add tls my-tls --cert /path/to/cert.pem --key /path/to/key.pem --domain my-domain.com +` + +type LocalSecretsAddTlsOptions struct { + CertFile string + KeyFile string + IngressDomain string +} + +func (o *LocalSecretsAddTlsOptions) Run(name string) error { + var err error + var matched bool + + if matched, err = regexp.MatchString("^[a-z0-9]([.a-z0-9-]+)?[a-z0-9]$", name); err != nil { + return errors.Wrapf(err, "regex match on secret name failed") + } + + if !matched { + return errors.New("invalid secret name") + } + + var certificateFileData []byte + var certificateKeyFileData []byte + + if o.CertFile != "" { + certificateFileData, err = os.ReadFile(o.CertFile) + + if err != nil { + return errors.Wrapf(err, "failed to read certificate file %s", o.CertFile) + } + } + + if o.KeyFile != "" { + certificateKeyFileData, err = os.ReadFile(o.KeyFile) + + if err != nil { + return errors.Wrapf(err, "failed to read certificate key file %s", o.KeyFile) + } + } + + secret := &apiv1.Secret{ + ObjectMeta: metav1.ObjectMeta{ + Name: name, + Annotations: map[string]string{}, + }, + Type: "kubernetes.io/tls", + Data: map[string][]byte{ + "tls.crt": certificateFileData, + "tls.key": certificateKeyFileData, + }, + } + + if o.IngressDomain != "" { + secret.ObjectMeta.Annotations["training.educates.dev/domain"] = o.IngressDomain + } + + secretData, err := json.MarshalIndent(&secret, "", " ") + + if err != nil { + return errors.Wrap(err, "failed to generate secret data") + } + + secretData, err = yaml.JSONToYAML(secretData) + + if err != nil { + return errors.Wrap(err, "failed to generate YAML data") + } + + secretsCacheDir := path.Join(utils.GetEducatesHomeDir(), "secrets") + + err = os.MkdirAll(secretsCacheDir, os.ModePerm) + + if err != nil { + return errors.Wrapf(err, "unable to create secrets cache directory") + } + + secretFilePath := path.Join(secretsCacheDir, name+".yaml") + + secretFile, err := os.OpenFile(secretFilePath, os.O_RDWR|os.O_CREATE|os.O_TRUNC, os.ModePerm) + + if err != nil { + return errors.Wrapf(err, "unable to create secret file %s", secretFilePath) + } + + if _, err := secretFile.Write(secretData); err != nil { + return errors.Wrapf(err, "unable to write secret file %s", secretFilePath) + } + + if err := secretFile.Close(); err != nil { + return errors.Wrapf(err, "unable to close secret file %s", secretFilePath) + } + + return nil +} + +func (p *ProjectInfo) NewLocalSecretsAddTlsCmd() *cobra.Command { + var o LocalSecretsAddTlsOptions + + var c = &cobra.Command{ + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return utils.CmdError(cmd, "name is required", "NAME") + } + return nil + }, + Use: "tls NAME", + Short: "Create a TLS secret", + RunE: func(_ *cobra.Command, args []string) error { return o.Run(args[0]) }, + Example: localSecretsAddTlsExample, + } + + c.Flags().StringVar( + &o.CertFile, + "cert", + "", + "path to PEM encoded public key certificate", + ) + c.Flags().StringVar( + &o.KeyFile, + "key", + "", + "path to private key associated with given certificate", + ) + c.Flags().StringVar( + &o.IngressDomain, + "domain", + "", + "wildcard ingress domain matching certificate", + ) + + c.MarkFlagsRequiredTogether("cert", "key") + + return c +} diff --git a/client-programs/pkg/cmd/local_secrets_export_cmd.go b/client-programs/pkg/cmd/local_secrets_export_cmd.go index a6f237265..147cdd409 100644 --- a/client-programs/pkg/cmd/local_secrets_export_cmd.go +++ b/client-programs/pkg/cmd/local_secrets_export_cmd.go @@ -10,27 +10,44 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/utils" ) +const localSecretsExportExample = ` + # Export all secrets in the cache + educates local secrets export + + # Export multiple secrets from the cache + educates local secrets export my-secret-1 my-secret-2 +` + +type LocalSecretsExportOptions struct {} + +func (o *LocalSecretsExportOptions) Run(args []string) error { + secretsCacheDir := path.Join(utils.GetEducatesHomeDir(), "secrets") + + err := os.MkdirAll(secretsCacheDir, os.ModePerm) + + if err != nil { + return errors.Wrapf(err, "unable to create secrets cache directory") + } + + err = utils.PrintYamlFilesInDir(secretsCacheDir, args) + if err != nil { + return errors.Wrapf(err, "unable to read secrets cache directory") + } + + return nil +} + func (p *ProjectInfo) NewLocalSecretsExportCmd() *cobra.Command { + var o LocalSecretsExportOptions + var c = &cobra.Command{ Args: cobra.ArbitraryArgs, Use: "export [NAME]", Short: "Export secrets in the cache", RunE: func(_ *cobra.Command, args []string) error { - secretsCacheDir := path.Join(utils.GetEducatesHomeDir(), "secrets") - - err := os.MkdirAll(secretsCacheDir, os.ModePerm) - - if err != nil { - return errors.Wrapf(err, "unable to create secrets cache directory") - } - - err = utils.PrintYamlFilesInDir(secretsCacheDir, args) - if err != nil { - return errors.Wrapf(err, "unable to read secrets cache directory") - } - - return nil + return o.Run(args) }, + Example: localSecretsExportExample, } return c diff --git a/client-programs/pkg/cmd/local_secrets_import_cmd.go b/client-programs/pkg/cmd/local_secrets_import_cmd.go index e42a0d4b6..583e8ce0d 100644 --- a/client-programs/pkg/cmd/local_secrets_import_cmd.go +++ b/client-programs/pkg/cmd/local_secrets_import_cmd.go @@ -7,9 +7,9 @@ import ( "regexp" "syscall" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/utils" apiv1 "k8s.io/api/core/v1" "k8s.io/apimachinery/pkg/runtime" "k8s.io/apimachinery/pkg/runtime/serializer" @@ -17,6 +17,11 @@ import ( "sigs.k8s.io/yaml" ) +const localSecretsImportExample = ` + # Import secrets from a file + educates local secrets import --file /path/to/secrets.yaml +` + type LocalSecretsImportOptions struct { File string } @@ -113,6 +118,7 @@ func (p *ProjectInfo) NewLocalSecretsImportCmd() *cobra.Command { Use: "import", Short: "Import secrets to the cache", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: localSecretsImportExample, } c.Flags().StringVarP( diff --git a/client-programs/pkg/cmd/local_secrets_list_cmd.go b/client-programs/pkg/cmd/local_secrets_list_cmd.go index f910ebe25..ef6bed897 100644 --- a/client-programs/pkg/cmd/local_secrets_list_cmd.go +++ b/client-programs/pkg/cmd/local_secrets_list_cmd.go @@ -6,40 +6,54 @@ import ( "path" "strings" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/utils" ) -func (p *ProjectInfo) NewLocalSecretsListCmd() *cobra.Command { - var c = &cobra.Command{ - Args: cobra.NoArgs, - Use: "list", - Short: "List secrets in the cache", - RunE: func(_ *cobra.Command, _ []string) error { - secretsCacheDir := path.Join(utils.GetEducatesHomeDir(), "secrets") +const localSecretsListExample = ` + # List all secrets in the cache + educates local secrets list +` + +type LocalSecretsListOptions struct {} + +func (o *LocalSecretsListOptions) Run() error { + secretsCacheDir := path.Join(utils.GetEducatesHomeDir(), "secrets") + + err := os.MkdirAll(secretsCacheDir, os.ModePerm) - err := os.MkdirAll(secretsCacheDir, os.ModePerm) + if err != nil { + return errors.Wrapf(err, "unable to create secrets cache directory") + } + + files, err := os.ReadDir(secretsCacheDir) - if err != nil { - return errors.Wrapf(err, "unable to create secrets cache directory") - } + if err != nil { + return errors.Wrapf(err, "unable to read secrets cache directory") + } - files, err := os.ReadDir(secretsCacheDir) + for _, f := range files { + if strings.HasSuffix(f.Name(), ".yaml") { + name := strings.TrimSuffix(f.Name(), ".yaml") + fmt.Println(name) + } + } - if err != nil { - return errors.Wrapf(err, "unable to read secrets cache directory") - } + return nil +} - for _, f := range files { - if strings.HasSuffix(f.Name(), ".yaml") { - name := strings.TrimSuffix(f.Name(), ".yaml") - fmt.Println(name) - } - } +func (p *ProjectInfo) NewLocalSecretsListCmd() *cobra.Command { + var o LocalSecretsListOptions - return nil + var c = &cobra.Command{ + Args: cobra.NoArgs, + Use: "list", + Short: "List secrets in the cache", + RunE: func(_ *cobra.Command, _ []string) error { + return o.Run() }, + Example: localSecretsListExample, } return c diff --git a/client-programs/pkg/cmd/local_secrets_remove_cmd.go b/client-programs/pkg/cmd/local_secrets_remove_cmd.go index b0953eed8..7e928f26e 100644 --- a/client-programs/pkg/cmd/local_secrets_remove_cmd.go +++ b/client-programs/pkg/cmd/local_secrets_remove_cmd.go @@ -5,38 +5,55 @@ import ( "path" "regexp" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" "github.com/spf13/cobra" - "github.com/educates/educates-training-platform/client-programs/pkg/utils" ) -func (p *ProjectInfo) NewLocalSecretsRemoveCmd() *cobra.Command { - var c = &cobra.Command{ - Args: cobra.ExactArgs(1), - Use: "remove NAME", - Short: "Remove secret from the cache", - RunE: func(_ *cobra.Command, args []string) error { - name := args[0] +const localSecretsRemoveExample = ` + # Remove a secret from the cache + educates local secrets remove my-secret +` - var err error - var matched bool +type LocalSecretsRemoveOptions struct {} - if matched, err = regexp.MatchString("^[a-z0-9]([.a-z0-9-]+)?[a-z0-9]$", name); err != nil { - return errors.Wrapf(err, "regex match on secret name failed") - } +func (o *LocalSecretsRemoveOptions) Run(name string) error { + var err error + var matched bool - if !matched { - return errors.Errorf("invalid secret name %q", name) - } + if matched, err = regexp.MatchString("^[a-z0-9]([.a-z0-9-]+)?[a-z0-9]$", name); err != nil { + return errors.Wrapf(err, "regex match on secret name failed") + } + + if !matched { + return errors.Errorf("invalid secret name %q", name) + } + + secretsCacheDir := path.Join(utils.GetEducatesHomeDir(), "secrets") + + secretFilePath := path.Join(secretsCacheDir, name+".yaml") - secretsCacheDir := path.Join(utils.GetEducatesHomeDir(), "secrets") + os.Remove(secretFilePath) - secretFilePath := path.Join(secretsCacheDir, name+".yaml") + return nil +} - os.Remove(secretFilePath) +func (p *ProjectInfo) NewLocalSecretsRemoveCmd() *cobra.Command { + var o LocalSecretsRemoveOptions + var c = &cobra.Command{ + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return utils.CmdError(cmd, "name is required", "NAME") + } return nil }, + Use: "remove NAME", + Short: "Remove secret from the cache", + RunE: func(_ *cobra.Command, args []string) error { + return o.Run(args[0]) + }, + Example: localSecretsRemoveExample, } return c diff --git a/client-programs/pkg/cmd/local_secrets_sync_cmd.go b/client-programs/pkg/cmd/local_secrets_sync_cmd.go index 2151e0fcf..305ce3701 100644 --- a/client-programs/pkg/cmd/local_secrets_sync_cmd.go +++ b/client-programs/pkg/cmd/local_secrets_sync_cmd.go @@ -1,12 +1,20 @@ package cmd import ( - "github.com/pkg/errors" - "github.com/spf13/cobra" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/secrets" + "github.com/pkg/errors" + "github.com/spf13/cobra" ) +const localSecretsSyncExample = ` + # Sync secrets to the cluster + educates local secrets sync + + # Sync secrets to the cluster using a custom kubeconfig file and context + educates local secrets sync --kubeconfig /path/to/kubeconfig --context my-context +` + type LocalSecretsSyncOptions struct { KubeconfigOptions } @@ -35,6 +43,7 @@ func (p *ProjectInfo) NewLocalSecretsSyncCmd() *cobra.Command { Use: "sync", Short: "Copy secrets to cluster", RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Example: localSecretsSyncExample, } c.Flags().StringVar( From c56390699dcda3d7805d1820b6bcc349e1f95421 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Wed, 21 Jan 2026 11:55:24 +0100 Subject: [PATCH 13/24] New local_mirror_list command --- .../pkg/cmd/local_mirror_cmd_group.go | 3 +- .../pkg/cmd/local_mirror_list_cmd.go | 47 +++++++++++++++++++ client-programs/pkg/registry/registry.go | 43 +++++++++++++++++ client-programs/pkg/utils/docker.go | 18 +++++++ 4 files changed, 110 insertions(+), 1 deletion(-) create mode 100644 client-programs/pkg/cmd/local_mirror_list_cmd.go create mode 100644 client-programs/pkg/utils/docker.go diff --git a/client-programs/pkg/cmd/local_mirror_cmd_group.go b/client-programs/pkg/cmd/local_mirror_cmd_group.go index ad3588914..031dc30e7 100644 --- a/client-programs/pkg/cmd/local_mirror_cmd_group.go +++ b/client-programs/pkg/cmd/local_mirror_cmd_group.go @@ -8,19 +8,20 @@ import ( func (p *ProjectInfo) NewLocalMirrorCmdGroup() *cobra.Command { var c = &cobra.Command{ Use: "mirror", + Aliases: []string{"mirrors"}, Short: "Manage local image registry mirrors", } // Use a command group as it allows us to dictate the order in which they // are displayed in the help message, as otherwise they are displayed in // sort order. - commandGroups := templates.CommandGroups{ { Message: "Available Commands:", Commands: []*cobra.Command{ p.NewLocalMirrorDeployCmd(), p.NewLocalMirrorDeleteCmd(), + p.NewLocalMirrorListCmd(), }, }, } diff --git a/client-programs/pkg/cmd/local_mirror_list_cmd.go b/client-programs/pkg/cmd/local_mirror_list_cmd.go new file mode 100644 index 000000000..64d442b55 --- /dev/null +++ b/client-programs/pkg/cmd/local_mirror_list_cmd.go @@ -0,0 +1,47 @@ +package cmd + +import ( + "fmt" + + "github.com/pkg/errors" + "github.com/spf13/cobra" + + "github.com/educates/educates-training-platform/client-programs/pkg/registry" +) + +const ( + localMirroListExample = ` + # List all local image registry mirrors + educates local mirror list +` +) + +type LocalMirrorListOptions struct {} + +func (o *LocalMirrorListOptions) Run() error { + list, err := registry.ListRegistryMirrors() + + if err != nil { + return errors.Wrap(err, "failed to deploy registry mirror") + } + + fmt.Println(list) + + return nil +} + +func (p *ProjectInfo) NewLocalMirrorListCmd() *cobra.Command { + var o LocalMirrorListOptions + + var c = &cobra.Command{ + Args: cobra.NoArgs, + Use: "list", + Short: "List all local image registry mirrors", + RunE: func(_ *cobra.Command, _ []string) error { + return o.Run() + }, + Example: localMirroListExample, + } + + return c +} diff --git a/client-programs/pkg/registry/registry.go b/client-programs/pkg/registry/registry.go index 2c6d3dde3..7e8002e2b 100644 --- a/client-programs/pkg/registry/registry.go +++ b/client-programs/pkg/registry/registry.go @@ -11,6 +11,7 @@ import ( "os" "path" "strings" + "text/tabwriter" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" @@ -19,6 +20,7 @@ import ( "github.com/docker/docker/client" "github.com/docker/go-connections/nat" "github.com/educates/educates-training-platform/client-programs/pkg/config" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" yaml "gopkg.in/yaml.v2" v1 "k8s.io/api/core/v1" @@ -736,6 +738,47 @@ func PruneRegistry() error { return nil } + +/** + * This function is used to list all local image registry mirrors. + */ +func ListRegistryMirrors() (string, error) { + ctx := context.Background() + + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + return "", errors.Wrap(err, "unable to create docker client") + } + + mirrors, err := cli.ContainerList(ctx, container.ListOptions{Filters: filters.NewArgs(filters.Arg("label", "role="+EducatesMirrorRoleLabel), filters.Arg("label", "app="+EducatesAppLabel))}) + if err != nil { + return "", errors.Wrap(err, "unable to list registry mirrors") + } + + var buf strings.Builder + w := new(tabwriter.Writer) + + // Initialize tabwriter to write to 'buf' instead of 'os.Stdout' + w.Init(&buf, 8, 8, 3, ' ', 0) + + fmt.Fprintf(w, "%s\n", "NAME") + + for i, item := range mirrors { + // TODO: Add the right way to get the container information + name := utils.GetContainerName(item) + + fmt.Fprintf(w, "%s", name) + if i < len(mirrors) - 1 { + fmt.Fprintf(w, "\n") + } + } + + // Important: Flush ensures all data is written from tabwriter to the builder + w.Flush() + + return buf.String(), nil +} + /** * This function is used to get the container name of a registry mirror. */ diff --git a/client-programs/pkg/utils/docker.go b/client-programs/pkg/utils/docker.go new file mode 100644 index 000000000..57368f645 --- /dev/null +++ b/client-programs/pkg/utils/docker.go @@ -0,0 +1,18 @@ +package utils + +import ( + "strings" + + "github.com/docker/docker/api/types/container" +) + +func GetContainerName(container container.Summary) string { + name := "unknown" + + if len(container.Names) > 0 { + // Get the first name and strip the leading slash "/" + name = strings.TrimPrefix(container.Names[0], "/") + } + + return name +} From bf7b60706978977ce684b6474666b6c5a558c663 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Wed, 21 Jan 2026 12:26:23 +0100 Subject: [PATCH 14/24] Refactored constants --- client-programs/pkg/cluster/kindcluster.go | 21 +++--- .../pkg/config/installationconfig.go | 5 +- client-programs/pkg/constants/installer.go | 10 +++ client-programs/pkg/constants/names.go | 14 ++++ client-programs/pkg/constants/project.go | 7 +- .../pkg/diagnostics/diagnostics.go | 5 +- client-programs/pkg/diagnostics/fetcher.go | 3 +- .../pkg/docker/workshop_manager.go | 8 +- client-programs/pkg/installer/installer.go | 34 ++++----- client-programs/pkg/registry/registry.go | 75 ++++++++----------- client-programs/pkg/resolver/resolver.go | 20 ++--- client-programs/pkg/utils/dirs.go | 3 +- 12 files changed, 112 insertions(+), 93 deletions(-) create mode 100644 client-programs/pkg/constants/installer.go create mode 100644 client-programs/pkg/constants/names.go diff --git a/client-programs/pkg/cluster/kindcluster.go b/client-programs/pkg/cluster/kindcluster.go index c40114959..6e09b7451 100644 --- a/client-programs/pkg/cluster/kindcluster.go +++ b/client-programs/pkg/cluster/kindcluster.go @@ -19,6 +19,7 @@ import ( "sigs.k8s.io/kind/pkg/cmd" "github.com/educates/educates-training-platform/client-programs/pkg/config" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/utils" ) @@ -53,7 +54,7 @@ func (o *KindClusterConfig) ClusterExists() (bool, error) { return false, errors.Wrap(err, "unable to get list of clusters") } - if slices.Contains(clusters, "educates") { + if slices.Contains(clusters, constants.EducatesClusterName) { return true, errors.New("cluster for Educates already exists") } @@ -89,7 +90,7 @@ func (o *KindClusterConfig) CreateCluster(config *config.InstallationConfig, ima return errors.Wrapf(err, "unable to create config directory") } - kindConfigPath := filepath.Join(configFileDir, "educates-cluster-config.yaml") + kindConfigPath := filepath.Join(configFileDir, fmt.Sprintf("%s-cluster-config.yaml", constants.EducatesClusterName)) err = os.WriteFile(kindConfigPath, clusterConfigData.Bytes(), 0644) if err != nil { return errors.Wrap(err, "failed to write cluster config to file") @@ -98,7 +99,7 @@ func (o *KindClusterConfig) CreateCluster(config *config.InstallationConfig, ima fmt.Println("Cluster config used is saved to: ", kindConfigPath) if err := o.provider.Create( - "educates", + constants.EducatesClusterName, cluster.CreateWithRawConfig(clusterConfigData.Bytes()), cluster.CreateWithNodeImage(image), cluster.CreateWithWaitForReady(time.Duration(time.Duration(60)*time.Second)), @@ -122,7 +123,7 @@ func (o *KindClusterConfig) DeleteCluster() error { fmt.Println("Deleting cluster educates ...") - if err := o.provider.Delete("educates", o.Config.Kubeconfig); err != nil { + if err := o.provider.Delete(constants.EducatesClusterName, o.Config.Kubeconfig); err != nil { return errors.Wrapf(err, "failed to delete cluster") } @@ -145,7 +146,7 @@ func (o *KindClusterConfig) StopCluster() error { return errors.Wrap(err, "unable to create docker client") } - _, err = cli.ContainerInspect(ctx, "educates-control-plane") + _, err = cli.ContainerInspect(ctx, constants.EducatesControlPlaneContainer) if err != nil { return errors.Wrap(err, "no container for Educates cluster") @@ -155,13 +156,13 @@ func (o *KindClusterConfig) StopCluster() error { timeout := 30 - if err := cli.ContainerStop(ctx, "educates-control-plane", container.StopOptions{Timeout: &timeout}); err != nil { + if err := cli.ContainerStop(ctx, constants.EducatesControlPlaneContainer, container.StopOptions{Timeout: &timeout}); err != nil { return errors.Wrapf(err, "failed to stop cluster") } // timeout := time.Duration(30) * time.Second - // if err := cli.ContainerStop(ctx, "educates-control-plane", &timeout); err != nil { + // if err := cli.ContainerStop(ctx, EducatesControlPlaneContainer, &timeout); err != nil { // return errors.Wrapf(err, "failed to stop cluster") // } @@ -184,7 +185,7 @@ func (o *KindClusterConfig) StartCluster() error { return errors.Wrap(err, "unable to create docker client") } - _, err = cli.ContainerInspect(ctx, "educates-control-plane") + _, err = cli.ContainerInspect(ctx, constants.EducatesControlPlaneContainer) if err != nil { return errors.Wrap(err, "no container for Educates cluster") @@ -192,7 +193,7 @@ func (o *KindClusterConfig) StartCluster() error { fmt.Println("Starting cluster educates ...") - if err := cli.ContainerStart(ctx, "educates-control-plane", container.StartOptions{}); err != nil { + if err := cli.ContainerStart(ctx, constants.EducatesControlPlaneContainer, container.StartOptions{}); err != nil { return errors.Wrapf(err, "failed to start cluster") } @@ -215,7 +216,7 @@ func (o *KindClusterConfig) ClusterStatus() error { return errors.Wrap(err, "unable to create docker client") } - containerJSON, err := cli.ContainerInspect(ctx, "educates-control-plane") + containerJSON, err := cli.ContainerInspect(ctx, constants.EducatesControlPlaneContainer) if err != nil { return errors.Wrap(err, "no container for Educates cluster") diff --git a/client-programs/pkg/config/installationconfig.go b/client-programs/pkg/config/installationconfig.go index 862b06153..181d17c33 100644 --- a/client-programs/pkg/config/installationconfig.go +++ b/client-programs/pkg/config/installationconfig.go @@ -4,6 +4,7 @@ import ( "os" "path" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/secrets" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" @@ -425,12 +426,12 @@ func ConfigForLocalClusters(configFile string, domain string, local bool) (fullC if local { // This augments the installation config with the secrets that are cached locally if secretName := secrets.LocalCachedSecretForIngressDomain(fullConfig.ClusterIngress.Domain); secretName != "" { - fullConfig.ClusterIngress.TLSCertificateRef.Namespace = "educates-secrets" + fullConfig.ClusterIngress.TLSCertificateRef.Namespace = constants.EducatesSecretsNamespace fullConfig.ClusterIngress.TLSCertificateRef.Name = secretName } if secretName := secrets.LocalCachedSecretForCertificateAuthority(fullConfig.ClusterIngress.Domain); secretName != "" { - fullConfig.ClusterIngress.CACertificateRef.Namespace = "educates-secrets" + fullConfig.ClusterIngress.CACertificateRef.Namespace = constants.EducatesSecretsNamespace fullConfig.ClusterIngress.CACertificateRef.Name = secretName } } diff --git a/client-programs/pkg/constants/installer.go b/client-programs/pkg/constants/installer.go new file mode 100644 index 000000000..62e196771 --- /dev/null +++ b/client-programs/pkg/constants/installer.go @@ -0,0 +1,10 @@ +package constants + +const ( + EducatesInstallerString = "educates-installer" + EducatesInstallerAppString = "label:installer=educates-installer.app" + EducatesConfigNamespace = "educates" + EducatesConfigConfigMapName = "educates-config" + EducatesProcessedValuesKey = "educates-processed-values.yaml" + EducatesOriginalConfigKey = "educates-original-config.yaml" +) diff --git a/client-programs/pkg/constants/names.go b/client-programs/pkg/constants/names.go new file mode 100644 index 000000000..2f41b0215 --- /dev/null +++ b/client-programs/pkg/constants/names.go @@ -0,0 +1,14 @@ +package constants + +const ( + EducatesClusterName = "educates" + RegistryImageV3 = "docker.io/library/registry:3" + RegistryConfigTargetPath = "/etc/distribution/config.yml" + EducatesNetworkName = "educates" + EducatesRegistryContainer = "educates-registry" + EducatesControlPlaneContainer = "educates-control-plane" + EducatesRegistryRoleLabel = "registry" + EducatesMirrorRoleLabel = "mirror" + EducatesAppLabel = "educates" + EducatesResolverContainerName = "educates-resolver" +) diff --git a/client-programs/pkg/constants/project.go b/client-programs/pkg/constants/project.go index c07797160..22e8faf97 100644 --- a/client-programs/pkg/constants/project.go +++ b/client-programs/pkg/constants/project.go @@ -1,9 +1,14 @@ package constants const ( - PROJECT_DOCS_URL = "https://docs.educates.dev/" + EducatesHomeDirName = "educates" + PROJECT_DOCS_URL = "https://docs.educates.dev/" DefaultPortalName = "educates-cli" DefaultPortalCapacity = 5 + + EducatesNamespace = "educates" + EducatesNamespaceLabelMetadataName = "educates" + EducatesSecretsNamespace = "educates-secrets" ) diff --git a/client-programs/pkg/diagnostics/diagnostics.go b/client-programs/pkg/diagnostics/diagnostics.go index 4bfb39d2f..888768c45 100644 --- a/client-programs/pkg/diagnostics/diagnostics.go +++ b/client-programs/pkg/diagnostics/diagnostics.go @@ -8,6 +8,7 @@ import ( "strings" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" educatesTypes "github.com/educates/educates-training-platform/client-programs/pkg/educates/types" ) @@ -75,10 +76,10 @@ func (c *ClusterDiagnostics) Run() error { } // fetch logs for the session-manager, secret-manager deploymentments - if err = clusterDiagnosticsFetcher.fetchLogsForDeployment("deployment=session-manager", "educates", "session-manager.log"); err != nil { + if err = clusterDiagnosticsFetcher.fetchLogsForDeployment("deployment=session-manager", constants.EducatesNamespace, "session-manager.log"); err != nil { fmt.Println("Error fetching logs for session-manager: ", err) } - if err = clusterDiagnosticsFetcher.fetchLogsForDeployment("deployment=secrets-manager", "educates", "secrets-manager.log"); err != nil { + if err = clusterDiagnosticsFetcher.fetchLogsForDeployment("deployment=secrets-manager", constants.EducatesNamespace, "secrets-manager.log"); err != nil { fmt.Println("Error fetching logs for secrets-manager: ", err) } // dump logs for all training-portal deployments diff --git a/client-programs/pkg/diagnostics/fetcher.go b/client-programs/pkg/diagnostics/fetcher.go index abf74483a..9662eb05e 100644 --- a/client-programs/pkg/diagnostics/fetcher.go +++ b/client-programs/pkg/diagnostics/fetcher.go @@ -10,6 +10,7 @@ import ( "strings" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" educatesrestapi "github.com/educates/educates-training-platform/client-programs/pkg/educates/restapi" educatesTypes "github.com/educates/educates-training-platform/client-programs/pkg/educates/types" "github.com/pkg/errors" @@ -83,7 +84,7 @@ func (c *ClusterDiagnosticsFetcher) getEducatesNamespacesEvents(fileName string) y := printers.YAMLPrinter{} for _, namespace := range namespaces.Items { - if !strings.HasPrefix(namespace.Labels["kubernetes.io/metadata.name"], "educates") { + if !strings.HasPrefix(namespace.Labels["kubernetes.io/metadata.name"], constants.EducatesNamespaceLabelMetadataName) { continue } events, err := client.CoreV1().Events(namespace.Name).List(context.TODO(), metav1.ListOptions{ diff --git a/client-programs/pkg/docker/workshop_manager.go b/client-programs/pkg/docker/workshop_manager.go index 04b166257..1df55c416 100644 --- a/client-programs/pkg/docker/workshop_manager.go +++ b/client-programs/pkg/docker/workshop_manager.go @@ -311,10 +311,10 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployConfig, s var registryIP string - registryInfo, err := cli.ContainerInspect(ctx, "educates-registry") + registryInfo, err := cli.ContainerInspect(ctx, constants.EducatesRegistryContainer) if err == nil { - educatesNetwork, exists := registryInfo.NetworkSettings.Networks["educates"] + educatesNetwork, exists := registryInfo.NetworkSettings.Networks[constants.EducatesNetworkName] if !exists { return name, errors.New("registry is not attached to educates network") @@ -443,7 +443,7 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployConfig, s } if registryNetwork { - networks["educates"] = &composetypes.ServiceNetworkConfig{} + networks[constants.EducatesNetworkName] = &composetypes.ServiceNetworkConfig{} } var extraHostsList composetypes.HostsList @@ -502,7 +502,7 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployConfig, s Name: originalName, Services: workshopServices, Networks: composetypes.Networks{ - "educates": composetypes.NetworkConfig{Name: "educates", External: true}, + "educates": composetypes.NetworkConfig{Name: constants.EducatesNetworkName, External: true}, }, Volumes: composetypes.Volumes{ "workshop": composetypes.VolumeConfig{}, diff --git a/client-programs/pkg/installer/installer.go b/client-programs/pkg/installer/installer.go index 8fc6e6c0a..413391592 100644 --- a/client-programs/pkg/installer/installer.go +++ b/client-programs/pkg/installer/installer.go @@ -9,6 +9,7 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/config" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/logger" "github.com/educates/educates-training-platform/client-programs/pkg/utils" @@ -32,13 +33,6 @@ import ( metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" ) -const EducatesInstallerString = "educates-installer" -const EducatesInstallerAppString = "label:installer=educates-installer.app" -const educatesConfigNamespace = "educates" -const educatesConfigConfigMapName = "educates-config" -const processedValuesKey = "educates-processed-values.yaml" -const originalConfigKey = "educates-original-config.yaml" - // We use a NullWriter to suppress the output of some commands, like kbld type NullWriter int @@ -57,7 +51,7 @@ func (inst *Installer) DryRun(version string, packageRepository string, fullConf } // Create a temporary directory - tempDir, err := os.MkdirTemp("", EducatesInstallerString) + tempDir, err := os.MkdirTemp("", constants.EducatesInstallerString) if err != nil { return err } @@ -111,7 +105,7 @@ func (inst *Installer) Run(version string, packageRepository string, fullConfig } // Create a temporary directory - tempDir, err := os.MkdirTemp("", EducatesInstallerString) + tempDir, err := os.MkdirTemp("", constants.EducatesInstallerString) if err != nil { return err } @@ -179,15 +173,15 @@ func (inst *Installer) GetValuesFromCluster(kubeconfig string, kubeContext strin return "", errors.Wrapf(err, "unable to create Kubernetes client") } - configMapClient := client.CoreV1().ConfigMaps(educatesConfigNamespace) + configMapClient := client.CoreV1().ConfigMaps(constants.EducatesConfigNamespace) - values, err := configMapClient.Get(context.TODO(), educatesConfigConfigMapName, metav1.GetOptions{}) + values, err := configMapClient.Get(context.TODO(), constants.EducatesConfigConfigMapName, metav1.GetOptions{}) if err != nil { return "", errors.Wrap(err, "error querying the cluster") } - valuesData, ok := values.Data[processedValuesKey] + valuesData, ok := values.Data[constants.EducatesProcessedValuesKey] if !ok { return "", errors.New("no platform configuration found") @@ -205,15 +199,15 @@ func (inst *Installer) GetConfigFromCluster(kubeconfig string, kubeContext strin return "", errors.Wrapf(err, "unable to create Kubernetes client") } - configMapClient := client.CoreV1().ConfigMaps(educatesConfigNamespace) + configMapClient := client.CoreV1().ConfigMaps(constants.EducatesConfigNamespace) - values, err := configMapClient.Get(context.TODO(), educatesConfigConfigMapName, metav1.GetOptions{}) + values, err := configMapClient.Get(context.TODO(), constants.EducatesConfigConfigMapName, metav1.GetOptions{}) if err != nil { return "", errors.Wrap(err, "error querying the cluster") } - valuesData, ok := values.Data[originalConfigKey] + valuesData, ok := values.Data[constants.EducatesOriginalConfigKey] if !ok { return "", errors.New("no platform configuration found") @@ -387,8 +381,8 @@ func (inst *Installer) deploy(tempDir string, inputDir string, clusterConfig *cl depsFactory := NewKappDepsFactoryImpl(clusterConfig) deployOptions := app.NewDeployOptions(confUI, depsFactory, logger.NewKappLogger(), nil) - deployOptions.AppFlags.Name = EducatesInstallerAppString - deployOptions.AppFlags.AppNamespace = EducatesInstallerString + deployOptions.AppFlags.Name = constants.EducatesInstallerAppString + deployOptions.AppFlags.AppNamespace = constants.EducatesInstallerString deployOptions.FileFlags.Files = []string{inputDir, filepath.Join(tempDir, "fetch/config/kapp/")} deployOptions.ApplyFlags.ClusterChangeOpts.Wait = true deployOptions.ApplyFlags.ClusterChangeOpts.ApplyIgnored = false @@ -433,8 +427,8 @@ func (inst *Installer) delete(clusterConfig *cluster.ClusterConfig) error { depsFactory := NewKappDepsFactoryImpl(clusterConfig) deleteOptions := app.NewDeleteOptions(confUI, depsFactory, logger.NewKappLogger()) - deleteOptions.AppFlags.Name = EducatesInstallerAppString - deleteOptions.AppFlags.AppNamespace = EducatesInstallerString + deleteOptions.AppFlags.Name = constants.EducatesInstallerAppString + deleteOptions.AppFlags.AppNamespace = constants.EducatesInstallerString deleteOptions.ApplyFlags.ClusterChangeOpts.Wait = true deleteOptions.ApplyFlags.ApplyingChangesOpts.Concurrency = 5 deleteOptions.ApplyFlags.WaitingChangesOpts.CheckInterval = time.Duration(1) * time.Second @@ -449,7 +443,7 @@ func (inst *Installer) delete(clusterConfig *cluster.ClusterConfig) error { } func (inst *Installer) getBundleImageRef(version string, packageRepository string, verbose bool) string { - bundleImageRef := fmt.Sprintf("%s/%s:%s", packageRepository, EducatesInstallerString, version) + bundleImageRef := fmt.Sprintf("%s/%s:%s", packageRepository, constants.EducatesInstallerString, version) if verbose { fmt.Printf("Using installer image: %s\n", bundleImageRef) } diff --git a/client-programs/pkg/registry/registry.go b/client-programs/pkg/registry/registry.go index 7e8002e2b..423d25cba 100644 --- a/client-programs/pkg/registry/registry.go +++ b/client-programs/pkg/registry/registry.go @@ -20,6 +20,7 @@ import ( "github.com/docker/docker/client" "github.com/docker/go-connections/nat" "github.com/educates/educates-training-platform/client-programs/pkg/config" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" yaml "gopkg.in/yaml.v2" @@ -37,16 +38,6 @@ const hostMirrorTomlTemplate = `[host."http://%s:5000"] const hostRegistryTomlTemplate = `[host."http://%s:5000"]` -const ( - RegistryImageV3 = "docker.io/library/registry:3" - RegistryConfigTargetPath = "/etc/distribution/config.yml" - EducatesNetworkName = "educates" - EducatesRegistryContainer = "educates-registry" - EducatesControlPlaneContainer = "educates-control-plane" - EducatesRegistryRoleLabel = "registry" - EducatesMirrorRoleLabel = "mirror" - EducatesAppLabel = "educates" -) /** * This function is used to deploy the registry and link it to the cluster. @@ -61,10 +52,10 @@ func DeployRegistryAndLinkToCluster(bindIP string, client *kubernetes.Clientset) // This is needed to make containerd use the local registry - if err = addRegistryConfigToKindNodes("localhost:5001", fmt.Sprintf(hostRegistryTomlTemplate, EducatesRegistryContainer)); err != nil { + if err = addRegistryConfigToKindNodes("localhost:5001", fmt.Sprintf(hostRegistryTomlTemplate, constants.EducatesRegistryContainer)); err != nil { return errors.Wrap(err, "failed to add registry config to kind nodes") } - if err = addRegistryConfigToKindNodes("registry.default.svc.cluster.local", fmt.Sprintf(hostRegistryTomlTemplate, EducatesRegistryContainer)); err != nil { + if err = addRegistryConfigToKindNodes("registry.default.svc.cluster.local", fmt.Sprintf(hostRegistryTomlTemplate, constants.EducatesRegistryContainer)); err != nil { return errors.Wrap(err, "failed to add registry config to kind nodes") } @@ -104,7 +95,7 @@ func createRegistryContainer(bindIP string) error { return errors.Wrap(err, "unable to create docker client") } - _, err = cli.ContainerInspect(ctx, EducatesRegistryContainer) + _, err = cli.ContainerInspect(ctx, constants.EducatesRegistryContainer) if err == nil { // If we can retrieve a container of required name we assume it is @@ -115,7 +106,7 @@ func createRegistryContainer(bindIP string) error { return nil } - reader, err := cli.ImagePull(ctx, RegistryImageV3, image.PullOptions{}) + reader, err := cli.ImagePull(ctx, constants.RegistryImageV3, image.PullOptions{}) if err != nil { return errors.Wrap(err, "cannot pull registry image") } @@ -123,10 +114,10 @@ func createRegistryContainer(bindIP string) error { defer reader.Close() io.Copy(os.Stdout, reader) - _, err = cli.NetworkInspect(ctx, EducatesNetworkName, network.InspectOptions{}) + _, err = cli.NetworkInspect(ctx, constants.EducatesNetworkName, network.InspectOptions{}) if err != nil { - _, err = cli.NetworkCreate(ctx, EducatesNetworkName, network.CreateOptions{}) + _, err = cli.NetworkCreate(ctx, constants.EducatesNetworkName, network.CreateOptions{}) if err != nil { return errors.Wrap(err, "cannot create educates network") @@ -148,18 +139,18 @@ func createRegistryContainer(bindIP string) error { } labels := map[string]string{ - "app": EducatesAppLabel, - "role": EducatesRegistryRoleLabel, + "app": constants.EducatesAppLabel, + "role": constants.EducatesRegistryRoleLabel, } resp, err := cli.ContainerCreate(ctx, &container.Config{ - Image: RegistryImageV3, + Image: constants.RegistryImageV3, Tty: false, ExposedPorts: nat.PortSet{ "5000/tcp": struct{}{}, }, Labels: labels, - }, hostConfig, nil, nil, EducatesRegistryContainer) + }, hostConfig, nil, nil, constants.EducatesRegistryContainer) if err != nil { return errors.Wrap(err, "cannot create registry container") @@ -169,15 +160,15 @@ func createRegistryContainer(bindIP string) error { return errors.Wrap(err, "unable to start registry") } - cli.NetworkDisconnect(ctx, EducatesNetworkName, EducatesRegistryContainer, false) + cli.NetworkDisconnect(ctx, constants.EducatesNetworkName, constants.EducatesRegistryContainer, false) - err = cli.NetworkConnect(ctx, EducatesNetworkName, EducatesRegistryContainer, &network.EndpointSettings{}) + err = cli.NetworkConnect(ctx, constants.EducatesNetworkName, constants.EducatesRegistryContainer, &network.EndpointSettings{}) if err != nil { return errors.Wrap(err, "unable to connect registry to educates network") } - if err = linkRegistryToClusterNetwork(EducatesRegistryContainer); err != nil { + if err = linkRegistryToClusterNetwork(constants.EducatesRegistryContainer); err != nil { return errors.Wrap(err, "failed to link registry to cluster") } @@ -246,10 +237,10 @@ func createMirrorContainer(mirrorConfig *config.RegistryMirrorConfig) error { envs = append(envs, fmt.Sprintf("REGISTRY_PROXY_PASSWORD=%s", mirrorConfig.Password)) } - _, err = cli.NetworkInspect(ctx, EducatesNetworkName, network.InspectOptions{}) + _, err = cli.NetworkInspect(ctx, constants.EducatesNetworkName, network.InspectOptions{}) if err != nil { - _, err = cli.NetworkCreate(ctx, EducatesNetworkName, network.CreateOptions{}) + _, err = cli.NetworkCreate(ctx, constants.EducatesNetworkName, network.CreateOptions{}) if err != nil { return errors.Wrap(err, "cannot create educates network") @@ -271,13 +262,13 @@ func createMirrorContainer(mirrorConfig *config.RegistryMirrorConfig) error { } labels := map[string]string{ - "app": EducatesAppLabel, - "role": EducatesMirrorRoleLabel, + "app": constants.EducatesAppLabel, + "role": constants.EducatesMirrorRoleLabel, "mirror": mirrorConfig.Mirror, } resp, err := cli.ContainerCreate(ctx, &container.Config{ - Image: RegistryImageV3, + Image: constants.RegistryImageV3, Tty: false, Env: envs, ExposedPorts: nat.PortSet{ @@ -294,9 +285,9 @@ func createMirrorContainer(mirrorConfig *config.RegistryMirrorConfig) error { return errors.Wrap(err, "unable to start local registry mirror") } - cli.NetworkDisconnect(ctx, EducatesNetworkName, mirrorContainerName, false) + cli.NetworkDisconnect(ctx, constants.EducatesNetworkName, mirrorContainerName, false) - err = cli.NetworkConnect(ctx, EducatesNetworkName, mirrorContainerName, &network.EndpointSettings{}) + err = cli.NetworkConnect(ctx, constants.EducatesNetworkName, mirrorContainerName, &network.EndpointSettings{}) if err != nil { return errors.Wrap(err, "unable to connect local registry mirror to educates network") @@ -324,7 +315,7 @@ func addRegistryConfigToKindNodes(repositoryName string, content string) error { return errors.Wrap(err, "unable to create docker client") } - containerID, _ := getContainerInfo(EducatesControlPlaneContainer) + containerID, _ := getContainerInfo(constants.EducatesControlPlaneContainer) registryDir := "/etc/containerd/certs.d/" + repositoryName @@ -379,7 +370,7 @@ func removeRegistryConfigFromKindNodes(repositoryName string) error { return errors.Wrap(err, "unable to create docker client") } - containerID, _ := getContainerInfo(EducatesControlPlaneContainer) + containerID, _ := getContainerInfo(constants.EducatesControlPlaneContainer) if containerID == "" { return nil @@ -486,7 +477,7 @@ func DeleteRegistry() error { return errors.Wrap(err, "unable to create docker client") } - _, err = cli.ContainerInspect(ctx, EducatesRegistryContainer) + _, err = cli.ContainerInspect(ctx, constants.EducatesRegistryContainer) if err != nil { // If we can't retrieve a container of required name we assume it does @@ -497,7 +488,7 @@ func DeleteRegistry() error { timeout := 30 - err = cli.ContainerStop(ctx, EducatesRegistryContainer, container.StopOptions{Timeout: &timeout}) + err = cli.ContainerStop(ctx, constants.EducatesRegistryContainer, container.StopOptions{Timeout: &timeout}) // timeout := time.Duration(30) * time.Second @@ -507,7 +498,7 @@ func DeleteRegistry() error { return errors.Wrap(err, "unable to stop registry container") } - err = cli.ContainerRemove(ctx, EducatesRegistryContainer, container.RemoveOptions{}) + err = cli.ContainerRemove(ctx, constants.EducatesRegistryContainer, container.RemoveOptions{}) if err != nil { return errors.Wrap(err, "unable to delete registry container") @@ -579,8 +570,8 @@ func DeleteRegistryMirrors() error { mirrors, err := cli.ContainerList(ctx, container.ListOptions{ Filters: filters.NewArgs( - filters.Arg("label", "role="+EducatesMirrorRoleLabel), - filters.Arg("label", "app="+EducatesAppLabel), + filters.Arg("label", "role="+constants.EducatesMirrorRoleLabel), + filters.Arg("label", "app="+constants.EducatesAppLabel), ), }) @@ -643,7 +634,7 @@ func UpdateRegistryK8SService(k8sclient *kubernetes.Clientset) error { endpointAppProtocol := "http" endpointProtocol := v1.ProtocolTCP - registryInfo, err := cli.ContainerInspect(ctx, EducatesRegistryContainer) + registryInfo, err := cli.ContainerInspect(ctx, constants.EducatesRegistryContainer) if err != nil { return errors.Wrapf(err, "unable to inspect container for registry") @@ -714,9 +705,9 @@ func PruneRegistry() error { return errors.Wrap(err, "unable to create docker client") } - containerID, _ := getContainerInfo(EducatesRegistryContainer) + containerID, _ := getContainerInfo(constants.EducatesRegistryContainer) - cmdStatement := []string{"registry", "garbage-collect", RegistryConfigTargetPath, "--delete-untagged=true"} + cmdStatement := []string{"registry", "garbage-collect", constants.RegistryConfigTargetPath, "--delete-untagged=true"} optionsCreateExecuteScript := container.ExecOptions{ AttachStdout: false, @@ -750,7 +741,7 @@ func ListRegistryMirrors() (string, error) { return "", errors.Wrap(err, "unable to create docker client") } - mirrors, err := cli.ContainerList(ctx, container.ListOptions{Filters: filters.NewArgs(filters.Arg("label", "role="+EducatesMirrorRoleLabel), filters.Arg("label", "app="+EducatesAppLabel))}) + mirrors, err := cli.ContainerList(ctx, container.ListOptions{Filters: filters.NewArgs(filters.Arg("label", "role="+constants.EducatesMirrorRoleLabel), filters.Arg("label", "app="+constants.EducatesAppLabel))}) if err != nil { return "", errors.Wrap(err, "unable to list registry mirrors") } @@ -783,7 +774,7 @@ func ListRegistryMirrors() (string, error) { * This function is used to get the container name of a registry mirror. */ func registryMirrorContainerName(mirrorConfig *config.RegistryMirrorConfig) string { - return fmt.Sprintf("%s-mirror-%s", EducatesRegistryContainer, mirrorConfig.Mirror) + return fmt.Sprintf("%s-mirror-%s", constants.EducatesRegistryContainer, mirrorConfig.Mirror) } /** diff --git a/client-programs/pkg/resolver/resolver.go b/client-programs/pkg/resolver/resolver.go index d34cd3fbe..b39f093cb 100644 --- a/client-programs/pkg/resolver/resolver.go +++ b/client-programs/pkg/resolver/resolver.go @@ -15,6 +15,7 @@ import ( "github.com/docker/docker/client" "github.com/docker/go-connections/nat" "github.com/educates/educates-training-platform/client-programs/pkg/config" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" ) @@ -33,8 +34,7 @@ address=/{{.IngressDomain}}/{{.TargetAddress}} address=/{{$Domain}}/{{$.TargetAddress}} {{- end }} ` - dnsmasqImage = "ghcr.io/dockur/dnsmasq:2.90" - resolverContainerName = "educates-resolver" + dnsmasqImage = "ghcr.io/dockur/dnsmasq:2.92" ) func DeployResolver(domain string, targetAddress string, extraDomains []string) error { @@ -48,7 +48,7 @@ func DeployResolver(domain string, targetAddress string, extraDomains []string) return errors.Wrap(err, "unable to create docker client") } - _, err = cli.ContainerInspect(ctx, resolverContainerName) + _, err = cli.ContainerInspect(ctx, constants.EducatesResolverContainerName) if err == nil { // If we can retrieve a container of required name we assume it is @@ -105,7 +105,7 @@ func DeployResolver(domain string, targetAddress string, extraDomains []string) ExposedPorts: nat.PortSet{ "53/udp": struct{}{}, }, - }, hostConfig, nil, nil, resolverContainerName) + }, hostConfig, nil, nil, constants.EducatesResolverContainerName) if err != nil { return errors.Wrap(err, "cannot create resolver container") @@ -115,7 +115,7 @@ func DeployResolver(domain string, targetAddress string, extraDomains []string) return errors.Wrap(err, "unable to start resolver") } - fmt.Println("Local DNS resolver running as a Docker container", resolverContainerName) + fmt.Println("Local DNS resolver running as a Docker container", constants.EducatesResolverContainerName) fmt.Println("Local DNS resolver configuration in", configFileName) return nil @@ -132,7 +132,7 @@ func DeleteResolver() error { return errors.Wrap(err, "unable to create docker client") } - _, err = cli.ContainerInspect(ctx, resolverContainerName) + _, err = cli.ContainerInspect(ctx, constants.EducatesResolverContainerName) if err != nil { // If we can't retrieve a container of required name we assume it does @@ -143,13 +143,13 @@ func DeleteResolver() error { timeout := 30 - err = cli.ContainerStop(ctx, resolverContainerName, container.StopOptions{Timeout: &timeout}) + err = cli.ContainerStop(ctx, constants.EducatesResolverContainerName, container.StopOptions{Timeout: &timeout}) if err != nil { return errors.Wrap(err, "unable to stop DNS resolver container") } - err = cli.ContainerRemove(ctx, resolverContainerName, container.RemoveOptions{}) + err = cli.ContainerRemove(ctx, constants.EducatesResolverContainerName, container.RemoveOptions{}) if err != nil { return errors.Wrap(err, "unable to delete DNS resolver container") @@ -168,7 +168,7 @@ func UpdateResolver(domain string, targetAddress string, extraDomains []string) return errors.Wrap(err, "unable to create docker client") } - _, err = cli.ContainerInspect(ctx, resolverContainerName) + _, err = cli.ContainerInspect(ctx, constants.EducatesResolverContainerName) if err != nil { return errors.Wrap(err, "resolver container not found") } @@ -178,7 +178,7 @@ func UpdateResolver(domain string, targetAddress string, extraDomains []string) return err } - err = cli.ContainerRestart(ctx, resolverContainerName, container.StopOptions{}) + err = cli.ContainerRestart(ctx, constants.EducatesResolverContainerName, container.StopOptions{}) if err != nil { return errors.Wrap(err, "failed to restart resolver") } diff --git a/client-programs/pkg/utils/dirs.go b/client-programs/pkg/utils/dirs.go index 2a2ea6e68..ebe6d8836 100644 --- a/client-programs/pkg/utils/dirs.go +++ b/client-programs/pkg/utils/dirs.go @@ -4,8 +4,9 @@ import ( "path" "github.com/adrg/xdg" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" ) func GetEducatesHomeDir() string { - return path.Join(xdg.DataHome, "educates") + return path.Join(xdg.DataHome, constants.EducatesHomeDirName) } From 9dcf09a7589256e947caac0f433b7fff9d02716f Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Wed, 21 Jan 2026 19:54:39 +0100 Subject: [PATCH 15/24] Improved local_mirror_list command --- client-programs/pkg/registry/registry.go | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/client-programs/pkg/registry/registry.go b/client-programs/pkg/registry/registry.go index 423d25cba..9987444b7 100644 --- a/client-programs/pkg/registry/registry.go +++ b/client-programs/pkg/registry/registry.go @@ -265,6 +265,8 @@ func createMirrorContainer(mirrorConfig *config.RegistryMirrorConfig) error { "app": constants.EducatesAppLabel, "role": constants.EducatesMirrorRoleLabel, "mirror": mirrorConfig.Mirror, + "url": mirrorConfig.URL, + "username": mirrorConfig.Username, } resp, err := cli.ContainerCreate(ctx, &container.Config{ @@ -752,13 +754,18 @@ func ListRegistryMirrors() (string, error) { // Initialize tabwriter to write to 'buf' instead of 'os.Stdout' w.Init(&buf, 8, 8, 3, ' ', 0) - fmt.Fprintf(w, "%s\n", "NAME") + fmt.Fprintf(w, "%s\t%s\t%s\n", "NAME", "URL", "USERNAME") for i, item := range mirrors { // TODO: Add the right way to get the container information name := utils.GetContainerName(item) + url := item.Labels["url"] + if url == "" { + url = item.Labels["mirror"] + } + username := item.Labels["username"] - fmt.Fprintf(w, "%s", name) + fmt.Fprintf(w, "%s\t%s\t%s", name, url, username) if i < len(mirrors) - 1 { fmt.Fprintf(w, "\n") } From ad69aab7d08b8aa250319b3d5968ba79a228c41b Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Wed, 21 Jan 2026 20:24:30 +0100 Subject: [PATCH 16/24] Refactor and extract function to print tables --- .../pkg/cmd/docker_workshop_list_cmd.go | 15 ++----- .../pkg/educates/resources/portal/manager.go | 37 +++------------- .../educates/resources/sessions/manager.go | 40 ++++++----------- .../educates/resources/workshops/manager.go | 15 ++----- client-programs/pkg/registry/registry.go | 25 ++--------- client-programs/pkg/utils/printer.go | 44 +++++++++++++++++++ 6 files changed, 74 insertions(+), 102 deletions(-) create mode 100644 client-programs/pkg/utils/printer.go diff --git a/client-programs/pkg/cmd/docker_workshop_list_cmd.go b/client-programs/pkg/cmd/docker_workshop_list_cmd.go index 77f618b32..6e28f0e18 100644 --- a/client-programs/pkg/cmd/docker_workshop_list_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_list_cmd.go @@ -2,10 +2,9 @@ package cmd import ( "fmt" - "os" - "text/tabwriter" "github.com/educates/educates-training-platform/client-programs/pkg/docker" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" "github.com/spf13/cobra" ) @@ -29,17 +28,11 @@ func (p *ProjectInfo) NewDockerWorkshopListCmd() *cobra.Command { return errors.Wrap(err, "cannot display list of workshops") } - // TODO: Move this to a helper function - w := new(tabwriter.Writer) - w.Init(os.Stdout, 8, 8, 3, ' ', 0) - - defer w.Flush() - - fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "NAME", "URL", "SOURCE", "STATUS") - + var data [][]string for _, workshop := range workshops { - fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", workshop.Name, workshop.Url, workshop.Source, workshop.Status) + data = append(data, []string{workshop.Name, workshop.Url, workshop.Source, workshop.Status}) } + fmt.Print(utils.PrintTable([]string{"NAME", "URL", "SOURCE", "STATUS"}, data)) return nil }, diff --git a/client-programs/pkg/educates/resources/portal/manager.go b/client-programs/pkg/educates/resources/portal/manager.go index a9fefb85f..5f5c4372d 100644 --- a/client-programs/pkg/educates/resources/portal/manager.go +++ b/client-programs/pkg/educates/resources/portal/manager.go @@ -5,7 +5,6 @@ import ( "fmt" "net/url" "strings" - "text/tabwriter" "github.com/educates/educates-training-platform/client-programs/pkg/constants" educatesTypes "github.com/educates/educates-training-platform/client-programs/pkg/educates/types" @@ -202,15 +201,8 @@ func (m *PortalManager) ListTrainingPortals(cfg *TrainingPortalListConfig) (stri return "", nil } - var buf strings.Builder - w := new(tabwriter.Writer) - - // Initialize tabwriter to write to 'buf' instead of 'os.Stdout' - w.Init(&buf, 8, 8, 3, ' ', 0) - - fmt.Fprintf(w, "%s\t%s\t%s\n", "NAME", "CAPACITY", "URL") - - for i, item := range trainingPortals.Items { + var data [][]string + for _, item := range trainingPortals.Items { name := item.GetName() sessionsMaximum, propertyExists, err := unstructured.NestedInt64(item.Object, "spec", "portal", "sessions", "maximum") @@ -223,16 +215,9 @@ func (m *PortalManager) ListTrainingPortals(cfg *TrainingPortalListConfig) (stri url, _, _ := unstructured.NestedString(item.Object, "status", "educates", "url") - fmt.Fprintf(w, "%s\t%s\t%s", name, capacity, url) - if i < len(trainingPortals.Items) - 1 { - fmt.Fprintf(w, "\n") - } + data = append(data, []string{name, capacity, url}) } - - // Important: Flush ensures all data is written from tabwriter to the builder - w.Flush() - - return buf.String(), nil + return utils.PrintTable([]string{"NAME", "CAPACITY", "URL"}, data), nil } func (m *PortalManager) GetTrainingPortalBrowserUrl(cfg *TrainingPortalOpenConfig) (string, error) { @@ -289,19 +274,7 @@ func (m *PortalManager) GetTrainingPortalPassword(cfg *TrainingPortalPasswordCon return "", errors.New("unable to access credentials") } - var buf strings.Builder - w := new(tabwriter.Writer) - - // Initialize tabwriter to write to 'buf' instead of 'os.Stdout' - w.Init(&buf, 8, 8, 3, ' ', 0) - - fmt.Fprintf(w, "%s\t%s\n", "USERNAME", "PASSWORD") - fmt.Fprintf(w, "%s\t%s", username, password) - - // Important: Flush ensures all data is written from tabwriter to the builder - w.Flush() - - return buf.String(), nil + return utils.PrintTable([]string{"USERNAME", "PASSWORD"}, [][]string{{username, password}}), nil } else { password, _, _ := unstructured.NestedString(trainingPortal.Object, "spec", "portal", "password") diff --git a/client-programs/pkg/educates/resources/sessions/manager.go b/client-programs/pkg/educates/resources/sessions/manager.go index 0a3d46c4b..136c87040 100644 --- a/client-programs/pkg/educates/resources/sessions/manager.go +++ b/client-programs/pkg/educates/resources/sessions/manager.go @@ -3,12 +3,11 @@ package sessions import ( "context" "fmt" - "strings" - "text/tabwriter" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" educatesrestapi "github.com/educates/educates-training-platform/client-programs/pkg/educates/restapi" educatesTypes "github.com/educates/educates-training-platform/client-programs/pkg/educates/types" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" k8serrors "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" @@ -82,30 +81,19 @@ func (m *SessionManager) ListSessions(cfg ListSessionsConfig) (string, error) { return "No sessions found.", nil } - var buf strings.Builder - w := new(tabwriter.Writer) - w.Init(&buf, 8, 8, 3, ' ', 0) - - fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "NAME", "PORTAL", "ENVIRONMENT", "STATUS") - - for i, item := range sessions { + var data [][]string + for _, item := range sessions { name := item.GetName() labels := item.GetLabels() - portal := labels["training.educates.dev/portal.name"] environment := labels["training.educates.dev/environment.name"] status, _, _ := unstructured.NestedString(item.Object, "status", "educates", "phase") - fmt.Fprintf(w, "%s\t%s\t%s\t%s", name, portal, environment, status) - if i < len(sessions) - 1 { - fmt.Fprintf(w, "\n") - } + data = append(data, []string{name, portal, environment, status}) } - w.Flush() - - return buf.String(), nil + return utils.PrintTable([]string{"NAME", "PORTAL", "ENVIRONMENT", "STATUS"}, data), nil } func (m *SessionManager) ExtendSession(cfg ExtendSessionConfig) (string, error) { @@ -167,14 +155,12 @@ func (m *SessionManager) TerminateSession(cfg TerminateSessionConfig) (string, e } func printStatus(details *educatesrestapi.WorkshopSessionDetails) string { - var buf strings.Builder - - fmt.Fprintf(&buf, "Started: %s\n", details.Started) - fmt.Fprintf(&buf, "Expires: %s\n", details.Expires) - fmt.Fprintf(&buf, "Expiring: %t\n", details.Expiring) - fmt.Fprintf(&buf, "Countdown: %d\n", details.Countdown) - fmt.Fprintf(&buf, "Extendable: %t\n", details.Extendable) - fmt.Fprintf(&buf, "Status: %s", details.Status) - - return buf.String() + return utils.PrintKeyValuesTable([][]string{ + {"Started", details.Started}, + {"Expires", details.Expires}, + {"Expiring", fmt.Sprintf("%t", details.Expiring)}, + {"Countdown", fmt.Sprintf("%d", details.Countdown)}, + {"Extendable", fmt.Sprintf("%t", details.Extendable)}, + {"Status", details.Status}}, + ) } diff --git a/client-programs/pkg/educates/resources/workshops/manager.go b/client-programs/pkg/educates/resources/workshops/manager.go index 0c421c6c4..2761cadc4 100644 --- a/client-programs/pkg/educates/resources/workshops/manager.go +++ b/client-programs/pkg/educates/resources/workshops/manager.go @@ -13,7 +13,6 @@ import ( "os" "path/filepath" "strings" - "text/tabwriter" "time" yttcmd "carvel.dev/ytt/pkg/cmd/template" @@ -460,17 +459,13 @@ func (m *WorkshopManager) ListWorkshopResources(o *ListWorkshopResourcesConfig) return "No workshops found.", nil } - var buf strings.Builder - w := new(tabwriter.Writer) - w.Init(&buf, 8, 8, 3, ' ', 0) - - fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "NAME", "ALIAS", "CAPACITY", "SOURCE") - workshopsClient := m.Client.Resource(educatesTypes.WorkshopResource) + var data [][]string for _, item := range workshops { object := item.(map[string]interface{}) name := object["name"].(string) + alias := object["alias"].(string) var capacityField string @@ -494,12 +489,10 @@ func (m *WorkshopManager) ListWorkshopResources(o *ListWorkshopResourcesConfig) } } - fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", object["name"], object["alias"], capacityField, source) + data = append(data, []string{name, alias, capacityField, source}) } - w.Flush() - - return buf.String(), nil + return utils.PrintTable([]string{"NAME", "ALIAS", "CAPACITY", "SOURCE"}, data), nil } func (m *WorkshopManager) DeleteWorkshopResource(o *DeleteWorkshopResourceConfig) error { diff --git a/client-programs/pkg/registry/registry.go b/client-programs/pkg/registry/registry.go index 9987444b7..43d37239f 100644 --- a/client-programs/pkg/registry/registry.go +++ b/client-programs/pkg/registry/registry.go @@ -11,7 +11,6 @@ import ( "os" "path" "strings" - "text/tabwriter" "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" @@ -748,33 +747,17 @@ func ListRegistryMirrors() (string, error) { return "", errors.Wrap(err, "unable to list registry mirrors") } - var buf strings.Builder - w := new(tabwriter.Writer) - - // Initialize tabwriter to write to 'buf' instead of 'os.Stdout' - w.Init(&buf, 8, 8, 3, ' ', 0) - - fmt.Fprintf(w, "%s\t%s\t%s\n", "NAME", "URL", "USERNAME") - - for i, item := range mirrors { - // TODO: Add the right way to get the container information + var data [][]string + for _, item := range mirrors { name := utils.GetContainerName(item) url := item.Labels["url"] if url == "" { url = item.Labels["mirror"] } username := item.Labels["username"] - - fmt.Fprintf(w, "%s\t%s\t%s", name, url, username) - if i < len(mirrors) - 1 { - fmt.Fprintf(w, "\n") - } + data = append(data, []string{name, url, username}) } - - // Important: Flush ensures all data is written from tabwriter to the builder - w.Flush() - - return buf.String(), nil + return utils.PrintTable([]string{"NAME", "URL", "USERNAME"}, data), nil } /** diff --git a/client-programs/pkg/utils/printer.go b/client-programs/pkg/utils/printer.go new file mode 100644 index 000000000..abe9fc3f1 --- /dev/null +++ b/client-programs/pkg/utils/printer.go @@ -0,0 +1,44 @@ +package utils + +import ( + "fmt" + "strings" + "text/tabwriter" +) + +func PrintTable(headers []string, data [][]string) string { + var buf strings.Builder + w := tabwriter.NewWriter(&buf, 8, 8, 3, ' ', 0) + + // Print headers + fmt.Fprintln(w, strings.Join(headers, "\t")) + + // Print data rows + for i, row := range data { + if i < len(data) - 1 { + fmt.Fprintln(w, strings.Join(row, "\t")) + }else { + fmt.Fprint(w, strings.Join(row, "\t")) + } + } + + w.Flush() + return buf.String() +} + +func PrintKeyValuesTable(data [][]string) string { + var buf strings.Builder + w := tabwriter.NewWriter(&buf, 8, 8, 3, ' ', 0) + + // Print data rows + for i, row := range data { + if i < len(data) - 1 { + fmt.Fprintf(w, "%s:\t%s\n", row[0], row[1]) + }else { + fmt.Fprintf(w, "%s:\t%s", row[0], row[1]) + } + } + + w.Flush() + return buf.String() +} From c58807873d95d4c7316d181345b9ecae9f70b142 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Wed, 21 Jan 2026 20:45:38 +0100 Subject: [PATCH 17/24] Remove one dependency on ytt.ui --- .../educates/resources/workshops/manager.go | 7 +-- client-programs/pkg/logger/StdoutUI.go | 58 +++++++++++++++++++ 2 files changed, 60 insertions(+), 5 deletions(-) create mode 100644 client-programs/pkg/logger/StdoutUI.go diff --git a/client-programs/pkg/educates/resources/workshops/manager.go b/client-programs/pkg/educates/resources/workshops/manager.go index 2761cadc4..858d528c5 100644 --- a/client-programs/pkg/educates/resources/workshops/manager.go +++ b/client-programs/pkg/educates/resources/workshops/manager.go @@ -7,7 +7,6 @@ import ( "encoding/json" "fmt" "io" - "log" "net/http" "net/url" "os" @@ -16,11 +15,11 @@ import ( "time" yttcmd "carvel.dev/ytt/pkg/cmd/template" - yttcmdui "carvel.dev/ytt/pkg/cmd/ui" "carvel.dev/ytt/pkg/files" "carvel.dev/ytt/pkg/yamlmeta" "github.com/educates/educates-training-platform/client-programs/pkg/constants" educatesTypes "github.com/educates/educates-training-platform/client-programs/pkg/educates/types" + "github.com/educates/educates-training-platform/client-programs/pkg/logger" "github.com/educates/educates-training-platform/client-programs/pkg/utils" // "github.com/educates/educates-training-platform/client-programs/pkg/workshops" @@ -684,9 +683,7 @@ func ProcessWorkshopDefinition(yamlData []byte, dataValueFlags yttcmd.DataValues filesToProcess = append(filesToProcess, mainInputFile) - logUI := yttcmdui.NewCustomWriterTTY(false, log.Writer(), log.Writer()) - - output := templatingOptions.RunWithFiles(yttcmd.Input{Files: filesToProcess}, logUI) + output := templatingOptions.RunWithFiles(yttcmd.Input{Files: filesToProcess}, logger.NewStdoutUI()) if output.Err != nil { return []byte{}, fmt.Errorf("execution of ytt failed: %s", output.Err) diff --git a/client-programs/pkg/logger/StdoutUI.go b/client-programs/pkg/logger/StdoutUI.go new file mode 100644 index 000000000..93ebd82b5 --- /dev/null +++ b/client-programs/pkg/logger/StdoutUI.go @@ -0,0 +1,58 @@ +package logger + +import ( + "fmt" + "io" + "os" +) + +// StdoutUI is a local implementation that replaces the ui.UI interface from +// carvel.dev/ytt/pkg/cmd/ui. This allows us to avoid importing that package +// while still being compatible with ytt's RunWithFiles method. +// +// Go's structural typing for interfaces means this type satisfies the ui.UI +// interface as long as it implements the same method signatures: +// - Printf(string, ...interface{}) +// - Debugf(string, ...interface{}) +// - Warnf(str string, args ...interface{}) +// - DebugWriter() io.Writer +type StdoutUI struct { + stdout io.Writer + stderr io.Writer +} + +// NewStdoutUI creates a new StdoutUI with default stdout and stderr writers. +func NewStdoutUI() StdoutUI { + return StdoutUI{os.Stdout, os.Stderr} +} + +// Printf writes formatted output to stdout. +func (y StdoutUI) Printf(str string, args ...interface{}) { + fmt.Fprintf(y.stdout, str, args...) +} + +// Warnf writes formatted warning output to stderr. +func (y StdoutUI) Warnf(str string, args ...interface{}) { + fmt.Fprintf(y.stderr, str, args...) +} + +// Debugf writes formatted debug output to stderr if debug mode is enabled. +func (y StdoutUI) Debugf(str string, args ...interface{}) { + // Do nothing +} + +// DebugWriter returns an io.Writer for debug output. +// Returns stderr if debug mode is enabled, otherwise returns a no-op writer. +func (y StdoutUI) DebugWriter() io.Writer { + // Do nothing + return noopWriter{} +} + +// noopWriter is a writer that discards all data. +type noopWriter struct{} + +var _ io.Writer = noopWriter{} + +func (w noopWriter) Write(data []byte) (int, error) { + return len(data), nil +} From 02cde73906b38ee23ac114639b429a63ae380f67 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Thu, 22 Jan 2026 18:41:34 +0100 Subject: [PATCH 18/24] Refactored registry and mirror --- client-programs/pkg/registry/mirror.go | 279 ++++++++++++++++++++ client-programs/pkg/registry/registry.go | 309 +---------------------- client-programs/pkg/utils/docker.go | 37 +++ 3 files changed, 327 insertions(+), 298 deletions(-) create mode 100644 client-programs/pkg/registry/mirror.go diff --git a/client-programs/pkg/registry/mirror.go b/client-programs/pkg/registry/mirror.go new file mode 100644 index 000000000..1b66a7365 --- /dev/null +++ b/client-programs/pkg/registry/mirror.go @@ -0,0 +1,279 @@ +package registry + +import ( + "context" + _ "embed" + "fmt" + + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/client" + "github.com/docker/go-connections/nat" + "github.com/educates/educates-training-platform/client-programs/pkg/config" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" + "github.com/pkg/errors" +) + +const hostMirrorTomlTemplate = `[host."http://%s:5000"] + capabilities = ["pull", "resolve"] +` + +/** + * This function is used to deploy a registry mirror and link it to the cluster. + * It is used when creating a new local registry mirror. + */ +func DeployMirrorAndLinkToCluster(mirrorConfig *config.RegistryMirrorConfig) error { + err := createMirrorContainer(mirrorConfig) + + if err != nil { + return errors.Wrap(err, "failed to deploy registry mirror "+mirrorConfig.Mirror) + } + + content := fmt.Sprintf(hostMirrorTomlTemplate, registryMirrorContainerName(mirrorConfig)) + err = addRegistryConfigToKindNodes(mirrorConfig.Mirror, content) + + if err != nil { + fmt.Println("Warning: Mirror not added to Kind nodes") + } + + return nil +} + +/** + * This private function only creates the registry mirror container. + */ +func createMirrorContainer(mirrorConfig *config.RegistryMirrorConfig) error { + ctx := context.Background() + + fmt.Printf("Deploying local image registry mirror %s\n", mirrorConfig.Mirror) + + cli, err := client.NewClientWithOpts(client.FromEnv) + + if err != nil { + return errors.Wrap(err, "unable to create docker client") + } + + mirrorContainerName := registryMirrorContainerName(mirrorConfig) + _, err = cli.ContainerInspect(ctx, mirrorContainerName) + + if err == nil { + // If we can retrieve a container of required name we assume it is + // running okay. Technically it could be restarting, stopping or + // have exited and container was not removed, but if that is the case + // then leave it up to the user to sort out. + fmt.Printf("Registry mirror %s already exists\n", mirrorConfig.Mirror) + + return nil + } + + // Prepare environment variables for the registry mirror + envs := []string{} + mirrorURL := mirrorConfig.URL + if mirrorURL == "" { + mirrorURL = mirrorConfig.Mirror + } + envs = append(envs, fmt.Sprintf("REGISTRY_PROXY_REMOTEURL=https://%s", mirrorURL)) + if mirrorConfig.Username != "" { + envs = append(envs, fmt.Sprintf("REGISTRY_PROXY_USERNAME=%s", mirrorConfig.Username)) + } + if mirrorConfig.Password != "" { + envs = append(envs, fmt.Sprintf("REGISTRY_PROXY_PASSWORD=%s", mirrorConfig.Password)) + } + + _, err = cli.NetworkInspect(ctx, constants.EducatesNetworkName, network.InspectOptions{}) + + if err != nil { + _, err = cli.NetworkCreate(ctx, constants.EducatesNetworkName, network.CreateOptions{}) + + if err != nil { + return errors.Wrap(err, "cannot create educates network") + } + } + + hostConfig := &container.HostConfig{ + PortBindings: nat.PortMap{ + "5000/tcp": []nat.PortBinding{ + { + HostIP: "127.0.0.1", + // HostPort: mirrorConfig.Port, + }, + }, + }, + RestartPolicy: container.RestartPolicy{ + Name: "always", + }, + } + + labels := map[string]string{ + "app": constants.EducatesAppLabel, + "role": constants.EducatesMirrorRoleLabel, + "mirror": mirrorConfig.Mirror, + "url": mirrorConfig.URL, + "username": mirrorConfig.Username, + } + + resp, err := cli.ContainerCreate(ctx, &container.Config{ + Image: constants.RegistryImageV3, + Tty: false, + Env: envs, + ExposedPorts: nat.PortSet{ + "5000/tcp": struct{}{}, + }, + Labels: labels, + }, hostConfig, nil, nil, mirrorContainerName) + + if err != nil { + return errors.Wrap(err, "cannot create local registry mirror container") + } + + if err := cli.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil { + return errors.Wrap(err, "unable to start local registry mirror") + } + + cli.NetworkDisconnect(ctx, constants.EducatesNetworkName, mirrorContainerName, false) + + err = cli.NetworkConnect(ctx, constants.EducatesNetworkName, mirrorContainerName, &network.EndpointSettings{}) + + if err != nil { + return errors.Wrap(err, "unable to connect local registry mirror to educates network") + } + + if err = linkRegistryToClusterNetwork(mirrorContainerName); err != nil { + return errors.Wrap(err, "failed to link local registry mirror to cluster") + } + + return nil +} + +/** + * This function is used to delete a local registry mirror and unlink it from the cluster. + * It is used when deleting a local registry mirror. + */ +func DeleteMirrorAndUnlinkFromCluster(mirrorConfig *config.RegistryMirrorConfig) error { + ctx := context.Background() + + fmt.Printf("Deleting local image registry mirror %s\n", mirrorConfig.Mirror) + + cli, err := client.NewClientWithOpts(client.FromEnv) + + if err != nil { + return errors.Wrap(err, "unable to create docker client") + } + + containerName := registryMirrorContainerName(mirrorConfig) + _, err = cli.ContainerInspect(ctx, containerName) + + if err != nil { + // If we can't retrieve a container of required name we assume it does + // not actually exist. + + fmt.Printf("Registry mirror %s does not exist\n", mirrorConfig.Mirror) + return nil + } + + timeout := 30 + + err = cli.ContainerStop(ctx, containerName, container.StopOptions{Timeout: &timeout}) + + if err != nil { + return errors.Wrap(err, "unable to stop registry mirror container "+containerName) + } + + err = cli.ContainerRemove(ctx, containerName, container.RemoveOptions{}) + + if err != nil { + return errors.Wrap(err, "unable to delete registry mirror container "+containerName) + } + + // Remove the registry config from the kind nodes + err = removeRegistryConfigFromKindNodes(mirrorConfig.Mirror) + + if err != nil { + return errors.Wrap(err, "unable to remove registry config from kind nodes") + } + + return nil +} + +func DeleteRegistryMirrors() error { + ctx := context.Background() + + fmt.Println("Deleting local image registry mirrors") + + cli, err := client.NewClientWithOpts(client.FromEnv) + + if err != nil { + return errors.Wrap(err, "unable to create docker client") + } + + mirrors, err := cli.ContainerList(ctx, container.ListOptions{ + Filters: filters.NewArgs( + filters.Arg("label", "role="+constants.EducatesMirrorRoleLabel), + filters.Arg("label", "app="+constants.EducatesAppLabel), + ), + }) + + if err != nil { + return errors.Wrap(err, "unable to list registry mirrors") + } + + for _, mirror := range mirrors { + + timeout := 30 + + err = cli.ContainerStop(ctx, mirror.ID, container.StopOptions{Timeout: &timeout}) + + if err != nil { + return errors.Wrap(err, "unable to stop registry mirror container "+mirror.ID) + } + + err = cli.ContainerRemove(ctx, mirror.ID, container.RemoveOptions{}) + + if err != nil { + return errors.Wrap(err, "unable to delete registry mirror container "+mirror.ID) + } + + } + + return nil +} + +/** + * This function is used to list all local image registry mirrors. + */ +func ListRegistryMirrors() (string, error) { + ctx := context.Background() + + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + return "", errors.Wrap(err, "unable to create docker client") + } + + mirrors, err := cli.ContainerList(ctx, container.ListOptions{Filters: filters.NewArgs(filters.Arg("label", "role="+constants.EducatesMirrorRoleLabel), filters.Arg("label", "app="+constants.EducatesAppLabel))}) + if err != nil { + return "", errors.Wrap(err, "unable to list registry mirrors") + } + + var data [][]string + for _, item := range mirrors { + name := item.Labels["mirror"] + url := item.Labels["url"] + if url == "" { + url = item.Labels["mirror"] + } + username := item.Labels["username"] + status := item.Status + containerName := utils.GetContainerName(item) + data = append(data, []string{name, url, username, status, containerName}) + } + return utils.PrintTable([]string{"NAME", "URL", "USERNAME", "STATUS", "CONTAINER_NAME"}, data), nil +} + +/** + * This function is used to get the container name of a registry mirror. + */ +func registryMirrorContainerName(mirrorConfig *config.RegistryMirrorConfig) string { + return fmt.Sprintf("%s-mirror-%s", constants.EducatesRegistryContainer, mirrorConfig.Mirror) +} diff --git a/client-programs/pkg/registry/registry.go b/client-programs/pkg/registry/registry.go index 43d37239f..81a84314a 100644 --- a/client-programs/pkg/registry/registry.go +++ b/client-programs/pkg/registry/registry.go @@ -10,15 +10,12 @@ import ( "io" "os" "path" - "strings" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" "github.com/docker/go-connections/nat" - "github.com/educates/educates-training-platform/client-programs/pkg/config" "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" @@ -31,10 +28,6 @@ import ( "k8s.io/client-go/kubernetes" ) -const hostMirrorTomlTemplate = `[host."http://%s:5000"] - capabilities = ["pull", "resolve"] -` - const hostRegistryTomlTemplate = `[host."http://%s:5000"]` @@ -174,133 +167,6 @@ func createRegistryContainer(bindIP string) error { return nil } -/** - * This function is used to deploy a registry mirror and link it to the cluster. - * It is used when creating a new local registry mirror. - */ -func DeployMirrorAndLinkToCluster(mirrorConfig *config.RegistryMirrorConfig) error { - err := createMirrorContainer(mirrorConfig) - - if err != nil { - return errors.Wrap(err, "failed to deploy registry mirror "+mirrorConfig.Mirror) - } - - content := fmt.Sprintf(hostMirrorTomlTemplate, registryMirrorContainerName(mirrorConfig)) - err = addRegistryConfigToKindNodes(mirrorConfig.Mirror, content) - - if err != nil { - fmt.Println("Warning: Mirror not added to Kind nodes") - } - - return nil -} - -/** - * This private function only creates the registry mirror container. - */ -func createMirrorContainer(mirrorConfig *config.RegistryMirrorConfig) error { - ctx := context.Background() - - fmt.Printf("Deploying local image registry mirror %s\n", mirrorConfig.Mirror) - - cli, err := client.NewClientWithOpts(client.FromEnv) - - if err != nil { - return errors.Wrap(err, "unable to create docker client") - } - - mirrorContainerName := registryMirrorContainerName(mirrorConfig) - _, err = cli.ContainerInspect(ctx, mirrorContainerName) - - if err == nil { - // If we can retrieve a container of required name we assume it is - // running okay. Technically it could be restarting, stopping or - // have exited and container was not removed, but if that is the case - // then leave it up to the user to sort out. - fmt.Printf("Registry mirror %s already exists\n", mirrorConfig.Mirror) - - return nil - } - - // Prepare environment variables for the registry mirror - envs := []string{} - mirrorURL := mirrorConfig.URL - if mirrorURL == "" { - mirrorURL = mirrorConfig.Mirror - } - envs = append(envs, fmt.Sprintf("REGISTRY_PROXY_REMOTEURL=https://%s", mirrorURL)) - if mirrorConfig.Username != "" { - envs = append(envs, fmt.Sprintf("REGISTRY_PROXY_USERNAME=%s", mirrorConfig.Username)) - } - if mirrorConfig.Password != "" { - envs = append(envs, fmt.Sprintf("REGISTRY_PROXY_PASSWORD=%s", mirrorConfig.Password)) - } - - _, err = cli.NetworkInspect(ctx, constants.EducatesNetworkName, network.InspectOptions{}) - - if err != nil { - _, err = cli.NetworkCreate(ctx, constants.EducatesNetworkName, network.CreateOptions{}) - - if err != nil { - return errors.Wrap(err, "cannot create educates network") - } - } - - hostConfig := &container.HostConfig{ - PortBindings: nat.PortMap{ - "5000/tcp": []nat.PortBinding{ - { - HostIP: "127.0.0.1", - // HostPort: mirrorConfig.Port, - }, - }, - }, - RestartPolicy: container.RestartPolicy{ - Name: "always", - }, - } - - labels := map[string]string{ - "app": constants.EducatesAppLabel, - "role": constants.EducatesMirrorRoleLabel, - "mirror": mirrorConfig.Mirror, - "url": mirrorConfig.URL, - "username": mirrorConfig.Username, - } - - resp, err := cli.ContainerCreate(ctx, &container.Config{ - Image: constants.RegistryImageV3, - Tty: false, - Env: envs, - ExposedPorts: nat.PortSet{ - "5000/tcp": struct{}{}, - }, - Labels: labels, - }, hostConfig, nil, nil, mirrorContainerName) - - if err != nil { - return errors.Wrap(err, "cannot create local registry mirror container") - } - - if err := cli.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil { - return errors.Wrap(err, "unable to start local registry mirror") - } - - cli.NetworkDisconnect(ctx, constants.EducatesNetworkName, mirrorContainerName, false) - - err = cli.NetworkConnect(ctx, constants.EducatesNetworkName, mirrorContainerName, &network.EndpointSettings{}) - - if err != nil { - return errors.Wrap(err, "unable to connect local registry mirror to educates network") - } - - if err = linkRegistryToClusterNetwork(mirrorContainerName); err != nil { - return errors.Wrap(err, "failed to link local registry mirror to cluster") - } - - return nil -} - /** * This function is used to add the registry config to the kind nodes. * It is used when creating a new local registry or registry mirror. @@ -316,7 +182,11 @@ func addRegistryConfigToKindNodes(repositoryName string, content string) error { return errors.Wrap(err, "unable to create docker client") } - containerID, _ := getContainerInfo(constants.EducatesControlPlaneContainer) + containerID, _ := utils.GetContainerInfo(constants.EducatesControlPlaneContainer) + + if containerID == "" { + return errors.New(fmt.Sprintf("%s container not found", constants.EducatesControlPlaneContainer)) + } registryDir := "/etc/containerd/certs.d/" + repositoryName @@ -371,7 +241,7 @@ func removeRegistryConfigFromKindNodes(repositoryName string) error { return errors.Wrap(err, "unable to create docker client") } - containerID, _ := getContainerInfo(constants.EducatesControlPlaneContainer) + containerID, _ := utils.GetContainerInfo(constants.EducatesControlPlaneContainer) if containerID == "" { return nil @@ -508,99 +378,6 @@ func DeleteRegistry() error { return nil } -/** - * This function is used to delete a local registry mirror and unlink it from the cluster. - * It is used when deleting a local registry mirror. - */ -func DeleteMirrorAndUnlinkFromCluster(mirrorConfig *config.RegistryMirrorConfig) error { - ctx := context.Background() - - fmt.Printf("Deleting local image registry mirror %s\n", mirrorConfig.Mirror) - - cli, err := client.NewClientWithOpts(client.FromEnv) - - if err != nil { - return errors.Wrap(err, "unable to create docker client") - } - - containerName := registryMirrorContainerName(mirrorConfig) - _, err = cli.ContainerInspect(ctx, containerName) - - if err != nil { - // If we can't retrieve a container of required name we assume it does - // not actually exist. - - fmt.Printf("Registry mirror %s does not exist\n", mirrorConfig.Mirror) - return nil - } - - timeout := 30 - - err = cli.ContainerStop(ctx, containerName, container.StopOptions{Timeout: &timeout}) - - if err != nil { - return errors.Wrap(err, "unable to stop registry mirror container "+containerName) - } - - err = cli.ContainerRemove(ctx, containerName, container.RemoveOptions{}) - - if err != nil { - return errors.Wrap(err, "unable to delete registry mirror container "+containerName) - } - - // Remove the registry config from the kind nodes - err = removeRegistryConfigFromKindNodes(mirrorConfig.Mirror) - - if err != nil { - return errors.Wrap(err, "unable to remove registry config from kind nodes") - } - - return nil -} - -func DeleteRegistryMirrors() error { - ctx := context.Background() - - fmt.Println("Deleting local image registry mirrors") - - cli, err := client.NewClientWithOpts(client.FromEnv) - - if err != nil { - return errors.Wrap(err, "unable to create docker client") - } - - mirrors, err := cli.ContainerList(ctx, container.ListOptions{ - Filters: filters.NewArgs( - filters.Arg("label", "role="+constants.EducatesMirrorRoleLabel), - filters.Arg("label", "app="+constants.EducatesAppLabel), - ), - }) - - if err != nil { - return errors.Wrap(err, "unable to list registry mirrors") - } - - for _, mirror := range mirrors { - - timeout := 30 - - err = cli.ContainerStop(ctx, mirror.ID, container.StopOptions{Timeout: &timeout}) - - if err != nil { - return errors.Wrap(err, "unable to stop registry mirror container "+mirror.ID) - } - - err = cli.ContainerRemove(ctx, mirror.ID, container.RemoveOptions{}) - - if err != nil { - return errors.Wrap(err, "unable to delete registry mirror container "+mirror.ID) - } - - } - - return nil -} - /** * TODO: Learn whether this is needed or not * This function is used to update the registry k8s service. @@ -706,7 +483,11 @@ func PruneRegistry() error { return errors.Wrap(err, "unable to create docker client") } - containerID, _ := getContainerInfo(constants.EducatesRegistryContainer) + containerID, _ := utils.GetContainerInfo(constants.EducatesRegistryContainer) + + if containerID == "" { + return nil + } cmdStatement := []string{"registry", "garbage-collect", constants.RegistryConfigTargetPath, "--delete-untagged=true"} @@ -731,74 +512,6 @@ func PruneRegistry() error { } -/** - * This function is used to list all local image registry mirrors. - */ -func ListRegistryMirrors() (string, error) { - ctx := context.Background() - - cli, err := client.NewClientWithOpts(client.FromEnv) - if err != nil { - return "", errors.Wrap(err, "unable to create docker client") - } - - mirrors, err := cli.ContainerList(ctx, container.ListOptions{Filters: filters.NewArgs(filters.Arg("label", "role="+constants.EducatesMirrorRoleLabel), filters.Arg("label", "app="+constants.EducatesAppLabel))}) - if err != nil { - return "", errors.Wrap(err, "unable to list registry mirrors") - } - - var data [][]string - for _, item := range mirrors { - name := utils.GetContainerName(item) - url := item.Labels["url"] - if url == "" { - url = item.Labels["mirror"] - } - username := item.Labels["username"] - data = append(data, []string{name, url, username}) - } - return utils.PrintTable([]string{"NAME", "URL", "USERNAME"}, data), nil -} - -/** - * This function is used to get the container name of a registry mirror. - */ -func registryMirrorContainerName(mirrorConfig *config.RegistryMirrorConfig) string { - return fmt.Sprintf("%s-mirror-%s", constants.EducatesRegistryContainer, mirrorConfig.Mirror) -} - -/** - * This function is used to get the container id and status of a container. - * If the container does not exist, it will return an empty string for the container id and status. - */ -func getContainerInfo(containerName string) (containerID string, status string) { - ctx := context.Background() - - cli, err := client.NewClientWithOpts(client.FromEnv) - if err != nil { - panic(err) - } - - filters := filters.NewArgs() - filters.Add( - "name", containerName, - ) - - resp, err := cli.ContainerList(ctx, container.ListOptions{Filters: filters}) - if err != nil { - panic(err) - } - - if len(resp) > 0 { - containerID = resp[0].ID - containerStatus := strings.Split(resp[0].Status, " ") - status = containerStatus[0] //fmt.Println(status[0]) - } else { - fmt.Printf("container '%s' does not exists\n", containerName) - } - - return -} /** * This function is used to tar a file to be copied into a container. diff --git a/client-programs/pkg/utils/docker.go b/client-programs/pkg/utils/docker.go index 57368f645..edb273de4 100644 --- a/client-programs/pkg/utils/docker.go +++ b/client-programs/pkg/utils/docker.go @@ -1,9 +1,13 @@ package utils import ( + "context" + "fmt" "strings" "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/client" ) func GetContainerName(container container.Summary) string { @@ -16,3 +20,36 @@ func GetContainerName(container container.Summary) string { return name } + +/** + * This function is used to get the container id and status of a container. + * If the container does not exist, it will return an empty string for the container id and status. + */ + func GetContainerInfo(containerName string) (containerID string, status string) { + ctx := context.Background() + + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + panic(err) + } + + filters := filters.NewArgs() + filters.Add( + "name", containerName, + ) + + resp, err := cli.ContainerList(ctx, container.ListOptions{Filters: filters}) + if err != nil { + panic(err) + } + + if len(resp) > 0 { + containerID = resp[0].ID + containerStatus := strings.Split(resp[0].Status, " ") + status = containerStatus[0] //fmt.Println(status[0]) + } else { + fmt.Printf("container '%s' does not exists\n", containerName) + } + + return +} From 3f7f5e6ca4a5a0d20fd21f0d3c20299ccd447790 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Thu, 22 Jan 2026 19:19:42 +0100 Subject: [PATCH 19/24] Refactored registry logic to remove duplicate code --- .../pkg/cmd/local_cluster_create_cmd.go | 18 +- .../pkg/cmd/local_cluster_delete_cmd.go | 11 +- .../pkg/cmd/local_mirror_delete_cmd.go | 11 +- .../pkg/cmd/local_mirror_deploy_cmd.go | 3 +- .../pkg/cmd/local_registry_delete_cmd.go | 5 +- .../pkg/cmd/local_registry_deploy_cmd.go | 32 +- .../pkg/cmd/local_registry_prune_cmd.go | 3 +- client-programs/pkg/constants/names.go | 1 + client-programs/pkg/registry/base.go | 289 +++++++++++++ client-programs/pkg/registry/interface.go | 14 + client-programs/pkg/registry/mirror.go | 218 ++++------ client-programs/pkg/registry/registry.go | 382 +++--------------- 12 files changed, 503 insertions(+), 484 deletions(-) create mode 100644 client-programs/pkg/registry/base.go create mode 100644 client-programs/pkg/registry/interface.go diff --git a/client-programs/pkg/cmd/local_cluster_create_cmd.go b/client-programs/pkg/cmd/local_cluster_create_cmd.go index dd06da2ee..64ee4540a 100644 --- a/client-programs/pkg/cmd/local_cluster_create_cmd.go +++ b/client-programs/pkg/cmd/local_cluster_create_cmd.go @@ -98,7 +98,7 @@ func (o *LocalClusterCreateOptions) Run() error { return err } - client, err := clusterConfig.Config.GetClient() + k8sClient, err := clusterConfig.Config.GetClient() if err != nil { return err @@ -107,29 +107,31 @@ func (o *LocalClusterCreateOptions) Run() error { // This creates the educates-secrets namespace if it doesn't exist and creates the // wildcard and CA secrets in there if !o.ClusterOnly { - if err = secrets.SyncLocalCachedSecretsToCluster(client); err != nil { + if err = secrets.SyncLocalCachedSecretsToCluster(k8sClient); err != nil { return err } } - if err = registry.DeployRegistryAndLinkToCluster(o.RegistryBindIP, client); err != nil { + reg := registry.NewRegistry(o.RegistryBindIP, k8sClient) + if err = reg.DeployAndLinkToCluster(); err != nil { return errors.Wrap(err, "failed to deploy registry") } // This is needed for imgpkg pull from locally published workshops - if err = registry.UpdateRegistryK8SService(client); err != nil { + if err = reg.UpdateK8SService(); err != nil { return errors.Wrap(err, "failed to create service for registry") } // This is for hugo livereload (educates serve-workshop) - if err = cluster.CreateLoopbackService(client, fullConfig.ClusterIngress.Domain); err != nil { + if err = cluster.CreateLoopbackService(k8sClient, fullConfig.ClusterIngress.Domain); err != nil { return err } // Create and add registry mirrors defined in config to Kind nodes - for _, mirror := range fullConfig.LocalKindCluster.RegistryMirrors { - if err = registry.DeployMirrorAndLinkToCluster(&mirror); err != nil { - return errors.Wrap(err, "failed to deploy registry mirror "+mirror.Mirror) + for _, mirrorCfg := range fullConfig.LocalKindCluster.RegistryMirrors { + mirror := registry.NewMirror(&mirrorCfg) + if err = mirror.DeployAndLinkToCluster(); err != nil { + return errors.Wrap(err, "failed to deploy registry mirror "+mirrorCfg.Mirror) } } diff --git a/client-programs/pkg/cmd/local_cluster_delete_cmd.go b/client-programs/pkg/cmd/local_cluster_delete_cmd.go index c32779aaa..01dbcbdc6 100644 --- a/client-programs/pkg/cmd/local_cluster_delete_cmd.go +++ b/client-programs/pkg/cmd/local_cluster_delete_cmd.go @@ -25,7 +25,8 @@ func (o *LocalClusterDeleteOptions) Run() error { c := cluster.NewKindClusterConfig("") if o.AllComponents { - registry.DeleteRegistry() + reg := registry.NewRegistry("", nil) + reg.Delete() resolver.DeleteResolver() // Delete all mirrors registry.DeleteRegistryMirrors() @@ -38,10 +39,10 @@ func (p *ProjectInfo) NewLocalClusterDeleteCmd() *cobra.Command { var o LocalClusterDeleteOptions var c = &cobra.Command{ - Args: cobra.NoArgs, - Use: "delete", - Short: "Deletes the local Kubernetes cluster", - RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, + Args: cobra.NoArgs, + Use: "delete", + Short: "Deletes the local Kubernetes cluster", + RunE: func(_ *cobra.Command, _ []string) error { return o.Run() }, Example: localClusterDeleteExample, } diff --git a/client-programs/pkg/cmd/local_mirror_delete_cmd.go b/client-programs/pkg/cmd/local_mirror_delete_cmd.go index 0cc7565b7..24d39a764 100644 --- a/client-programs/pkg/cmd/local_mirror_delete_cmd.go +++ b/client-programs/pkg/cmd/local_mirror_delete_cmd.go @@ -5,6 +5,7 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/config" "github.com/educates/educates-training-platform/client-programs/pkg/registry" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" ) const ( @@ -23,14 +24,20 @@ func (o *LocalMirrorDeleteOptions) Run() error { Mirror: o.MirrorName, } - return registry.DeleteMirrorAndUnlinkFromCluster(mirrorConfig) + mirror := registry.NewMirror(mirrorConfig) + return mirror.DeleteAndUnlinkFromCluster() } func (p *ProjectInfo) NewLocalMirrorDeleteCmd() *cobra.Command { var o LocalMirrorDeleteOptions var c = &cobra.Command{ - Args: cobra.ExactArgs(1), + Args: func(cmd *cobra.Command, args []string) error { + if len(args) != 1 { + return utils.CmdError(cmd, "name is required", "NAME") + } + return nil + }, Use: "delete NAME", Short: "Deletes the local image registry mirror", RunE: func(_ *cobra.Command, args []string) error { o.MirrorName = args[0]; return o.Run() }, diff --git a/client-programs/pkg/cmd/local_mirror_deploy_cmd.go b/client-programs/pkg/cmd/local_mirror_deploy_cmd.go index f86b8a82c..c26c41e27 100644 --- a/client-programs/pkg/cmd/local_mirror_deploy_cmd.go +++ b/client-programs/pkg/cmd/local_mirror_deploy_cmd.go @@ -39,7 +39,8 @@ func (o *LocalMirrorDeployOptions) Run() error { Password: o.Password, } - err := registry.DeployMirrorAndLinkToCluster(mirrorConfig) + mirror := registry.NewMirror(mirrorConfig) + err := mirror.DeployAndLinkToCluster() if err != nil { return errors.Wrap(err, "failed to deploy registry mirror") diff --git a/client-programs/pkg/cmd/local_registry_delete_cmd.go b/client-programs/pkg/cmd/local_registry_delete_cmd.go index 602702dfb..983923b83 100644 --- a/client-programs/pkg/cmd/local_registry_delete_cmd.go +++ b/client-programs/pkg/cmd/local_registry_delete_cmd.go @@ -16,7 +16,10 @@ func (p *ProjectInfo) NewLocalRegistryDeleteCmd() *cobra.Command { Args: cobra.NoArgs, Use: "delete", Short: "Deletes the local image registry", - RunE: func(_ *cobra.Command, _ []string) error { return registry.DeleteRegistry() }, + RunE: func(_ *cobra.Command, _ []string) error { + reg := registry.NewRegistry("", nil) + return reg.Delete() + }, Example: localRegistryDeleteExample, } diff --git a/client-programs/pkg/cmd/local_registry_deploy_cmd.go b/client-programs/pkg/cmd/local_registry_deploy_cmd.go index 238d25456..b8de58046 100644 --- a/client-programs/pkg/cmd/local_registry_deploy_cmd.go +++ b/client-programs/pkg/cmd/local_registry_deploy_cmd.go @@ -28,32 +28,36 @@ type LocalRegistryDeployOptions struct { } func (o *LocalRegistryDeployOptions) Run() error { - err := registry.DeployRegistry(o.BindIP) - - if err != nil { - return errors.Wrap(err, "failed to deploy registry") - } - // This will fail if you do not have a Kubernetes cluster, but we can still // deploy just the image registry alone without Kubernetes. If a Kubernetes // cluster is created later, then the registry service will be added then. clusterConfig, err := cluster.NewClusterConfigIfAvailable(o.Kubeconfig, o.Context) + var client *registry.Registry + if err != nil { fmt.Println("Warning: Kubernetes cluster not available") - return nil + client = registry.NewRegistry(o.BindIP, nil) + } else { + k8sClient, err := clusterConfig.GetClient() + if err != nil { + fmt.Println("Warning: Kubernetes cluster not updated with registry service.") + client = registry.NewRegistry(o.BindIP, nil) + } else { + client = registry.NewRegistry(o.BindIP, k8sClient) + } } - client, err := clusterConfig.GetClient() - + err = client.Deploy() if err != nil { - fmt.Println("Warning: Kubernetes cluster not updated with registry service.") - - return nil + return errors.Wrap(err, "failed to deploy registry") } - if err = registry.UpdateRegistryK8SService(client); err != nil { - return errors.Wrap(err, "failed to create service for registry") + if client != nil { + if err = client.UpdateK8SService(); err != nil { + // Don't fail if we can't update the K8s service, just warn + fmt.Println("Warning: Kubernetes cluster not updated with registry service.") + } } return nil diff --git a/client-programs/pkg/cmd/local_registry_prune_cmd.go b/client-programs/pkg/cmd/local_registry_prune_cmd.go index 5f645f0f1..14af516ad 100644 --- a/client-programs/pkg/cmd/local_registry_prune_cmd.go +++ b/client-programs/pkg/cmd/local_registry_prune_cmd.go @@ -16,7 +16,8 @@ type LocalRegistryPruneOptions struct { } func (o *LocalRegistryPruneOptions) Run() error { - err := registry.PruneRegistry() + reg := registry.NewRegistry("", nil) + err := reg.Prune() if err != nil { return errors.Wrap(err, "failed to prune registry") diff --git a/client-programs/pkg/constants/names.go b/client-programs/pkg/constants/names.go index 2f41b0215..b9553a6b1 100644 --- a/client-programs/pkg/constants/names.go +++ b/client-programs/pkg/constants/names.go @@ -4,6 +4,7 @@ const ( EducatesClusterName = "educates" RegistryImageV3 = "docker.io/library/registry:3" RegistryConfigTargetPath = "/etc/distribution/config.yml" + ClusterNetworkName = "kind" EducatesNetworkName = "educates" EducatesRegistryContainer = "educates-registry" EducatesControlPlaneContainer = "educates-control-plane" diff --git a/client-programs/pkg/registry/base.go b/client-programs/pkg/registry/base.go new file mode 100644 index 000000000..f8e656fc8 --- /dev/null +++ b/client-programs/pkg/registry/base.go @@ -0,0 +1,289 @@ +package registry + +import ( + "archive/tar" + "bytes" + "compress/gzip" + "context" + "fmt" + "io" + "os" + "path" + + "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/image" + "github.com/docker/docker/api/types/network" + "github.com/docker/docker/client" + "github.com/docker/go-connections/nat" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" + "github.com/educates/educates-training-platform/client-programs/pkg/utils" + "github.com/pkg/errors" +) + +// baseContainer contains common configuration and methods for registry and mirror containers. +type baseContainer struct { + containerName string + bindIP string + labels map[string]string + envVars []string + hostPort string +} + +// ensureNetwork creates the specified docker network if it doesn't exist. +func (b *baseContainer) ensureNetwork(cli *client.Client, networkName string) error { + ctx := context.Background() + + _, err := cli.NetworkInspect(ctx, networkName, network.InspectOptions{}) + if err != nil { + _, err = cli.NetworkCreate(ctx, networkName, network.CreateOptions{}) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("cannot create %s network", networkName)) + } + } + return nil +} + +// containerExists checks if the container already exists. +func (b *baseContainer) containerExists(cli *client.Client) (bool, error) { + ctx := context.Background() + _, err := cli.ContainerInspect(ctx, b.containerName) + if err == nil { + return true, nil + } + return false, nil +} + +// pullRegistryImage pulls the registry image. +func (b *baseContainer) pullRegistryImage(cli *client.Client) error { + ctx := context.Background() + + reader, err := cli.ImagePull(ctx, constants.RegistryImageV3, image.PullOptions{}) + if err != nil { + return errors.Wrap(err, "cannot pull registry image") + } + + defer reader.Close() + io.Copy(os.Stdout, reader) + return nil +} + +// createAndStartContainer creates and starts the container with the given configuration. +func (b *baseContainer) createAndStartContainer(cli *client.Client) (string, error) { + ctx := context.Background() + + hostConfig := &container.HostConfig{ + PortBindings: nat.PortMap{ + "5000/tcp": []nat.PortBinding{ + { + HostIP: b.bindIP, + HostPort: b.hostPort, + }, + }, + }, + RestartPolicy: container.RestartPolicy{ + Name: "always", + }, + } + + containerConfig := &container.Config{ + Image: constants.RegistryImageV3, + Tty: false, + ExposedPorts: nat.PortSet{ + "5000/tcp": struct{}{}, + }, + Labels: b.labels, + Env: b.envVars, + } + + resp, err := cli.ContainerCreate(ctx, containerConfig, hostConfig, nil, nil, b.containerName) + if err != nil { + return "", errors.Wrap(err, "cannot create container") + } + + if err := cli.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil { + return "", errors.Wrap(err, "unable to start container") + } + + return resp.ID, nil +} + +// connectToNetwork connects the container to the specified network. +func (b *baseContainer) connectToNetwork(cli *client.Client, networkName string) error { + ctx := context.Background() + + cli.NetworkDisconnect(ctx, networkName, b.containerName, false) + + err := cli.NetworkConnect(ctx, networkName, b.containerName, &network.EndpointSettings{}) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("unable to connect container to %s network", networkName)) + } + + return nil +} + +// linkToNetwork connects the container to the specified network. +func (b *baseContainer) linkToNetwork(cli *client.Client, networkName string) error { + ctx := context.Background() + + fmt.Println("Linking local image registry to cluster") + + cli.NetworkDisconnect(ctx, networkName, b.containerName, false) + + err := cli.NetworkConnect(ctx, networkName, b.containerName, &network.EndpointSettings{}) + if err != nil { + return errors.Wrap(err, fmt.Sprintf("unable to connect container to %s network", networkName)) + } + + return nil +} + +// stopAndRemoveContainer stops and removes the container. +func (b *baseContainer) stopAndRemoveContainer(cli *client.Client) error { + ctx := context.Background() + + exists, _ := b.containerExists(cli) + if !exists { + return nil + } + + timeout := 30 + err := cli.ContainerStop(ctx, b.containerName, container.StopOptions{Timeout: &timeout}) + if err != nil { + return errors.Wrap(err, "unable to stop container") + } + + err = cli.ContainerRemove(ctx, b.containerName, container.RemoveOptions{}) + if err != nil { + return errors.Wrap(err, "unable to delete container") + } + + return nil +} + +// addRegistryConfigToKindNodes adds the registry config to the kind nodes. +// It is used when creating a new local registry or registry mirror. +func addRegistryConfigToKindNodes(repositoryName string, content string) error { + ctx := context.Background() + + fmt.Printf("Adding local image registry config (%s) to Kind nodes\n", repositoryName) + + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + return errors.Wrap(err, "unable to create docker client") + } + + containerID, _ := utils.GetContainerInfo(constants.EducatesControlPlaneContainer) + if containerID == "" { + return errors.New(fmt.Sprintf("%s container not found", constants.EducatesControlPlaneContainer)) + } + + registryDir := "/etc/containerd/certs.d/" + repositoryName + + cmdStatement := []string{"mkdir", "-p", registryDir} + + optionsCreateExecuteScript := container.ExecOptions{ + AttachStdout: true, + AttachStderr: true, + Cmd: cmdStatement, + } + + response, err := cli.ContainerExecCreate(ctx, containerID, optionsCreateExecuteScript) + if err != nil { + return errors.Wrap(err, "unable to create exec command") + } + hijackedResponse, err := cli.ContainerExecAttach(ctx, response.ID, container.ExecAttachOptions{}) + if err != nil { + return errors.Wrap(err, "unable to attach exec command") + } + + hijackedResponse.Close() + + buffer, err := tarFile([]byte(content), path.Join("/etc/containerd/certs.d/"+repositoryName, "hosts.toml"), 0x644) + if err != nil { + return err + } + err = cli.CopyToContainer(context.Background(), + containerID, "/", + buffer, + container.CopyToContainerOptions{ + AllowOverwriteDirWithFile: true, + }) + if err != nil { + return errors.Wrap(err, "unable to copy file to container") + } + + return nil +} + +// removeRegistryConfigFromKindNodes removes the registry config from the kind nodes. +// It is used when deleting a local registry mirror. +func removeRegistryConfigFromKindNodes(repositoryName string) error { + ctx := context.Background() + + fmt.Printf("Removing local image registry config (%s) from Kind nodes\n", repositoryName) + + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + return errors.Wrap(err, "unable to create docker client") + } + + containerID, _ := utils.GetContainerInfo(constants.EducatesControlPlaneContainer) + if containerID == "" { + return nil + } + + registryDir := "/etc/containerd/certs.d/" + repositoryName + + cmdStatement := []string{"rm", "-rf", registryDir} + + optionsCreateExecuteScript := container.ExecOptions{ + AttachStdout: true, + AttachStderr: true, + Cmd: cmdStatement, + } + + response, err := cli.ContainerExecCreate(ctx, containerID, optionsCreateExecuteScript) + if err != nil { + return errors.Wrap(err, "unable to create exec command") + } + + hijackedResponse, err := cli.ContainerExecAttach(ctx, response.ID, container.ExecAttachOptions{}) + if err != nil { + return errors.Wrap(err, "unable to attach exec command") + } + + hijackedResponse.Close() + + return nil +} + +// tarFile creates a tar archive with a single file. +func tarFile(fileContent []byte, basePath string, fileMode int64) (*bytes.Buffer, error) { + buffer := &bytes.Buffer{} + + zr := gzip.NewWriter(buffer) + tw := tar.NewWriter(zr) + + hdr := &tar.Header{ + Name: basePath, + Mode: fileMode, + Size: int64(len(fileContent)), + } + if err := tw.WriteHeader(hdr); err != nil { + return buffer, err + } + if _, err := tw.Write(fileContent); err != nil { + return buffer, err + } + + // produce tar + if err := tw.Close(); err != nil { + return buffer, fmt.Errorf("error closing tar file: %w", err) + } + // produce gzip + if err := zr.Close(); err != nil { + return buffer, fmt.Errorf("error closing gzip file: %w", err) + } + + return buffer, nil +} diff --git a/client-programs/pkg/registry/interface.go b/client-programs/pkg/registry/interface.go new file mode 100644 index 000000000..695992d97 --- /dev/null +++ b/client-programs/pkg/registry/interface.go @@ -0,0 +1,14 @@ +package registry + +// ContainerManager defines the interface for managing registry and mirror containers. +// Both Registry and Mirror types implement this interface. +type ContainerManager interface { + // DeployAndLinkToCluster creates the container and configures the cluster to use it + DeployAndLinkToCluster() error + + // DeleteAndUnlinkFromCluster removes the container and cleans up cluster configuration + DeleteAndUnlinkFromCluster() error + + // Delete removes the container only without cluster configuration cleanup + Delete() error +} diff --git a/client-programs/pkg/registry/mirror.go b/client-programs/pkg/registry/mirror.go index 1b66a7365..fb6936fb6 100644 --- a/client-programs/pkg/registry/mirror.go +++ b/client-programs/pkg/registry/mirror.go @@ -7,9 +7,7 @@ import ( "github.com/docker/docker/api/types/container" "github.com/docker/docker/api/types/filters" - "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" - "github.com/docker/go-connections/nat" "github.com/educates/educates-training-platform/client-programs/pkg/config" "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/utils" @@ -20,55 +18,34 @@ const hostMirrorTomlTemplate = `[host."http://%s:5000"] capabilities = ["pull", "resolve"] ` -/** - * This function is used to deploy a registry mirror and link it to the cluster. - * It is used when creating a new local registry mirror. - */ -func DeployMirrorAndLinkToCluster(mirrorConfig *config.RegistryMirrorConfig) error { - err := createMirrorContainer(mirrorConfig) - - if err != nil { - return errors.Wrap(err, "failed to deploy registry mirror "+mirrorConfig.Mirror) - } - - content := fmt.Sprintf(hostMirrorTomlTemplate, registryMirrorContainerName(mirrorConfig)) - err = addRegistryConfigToKindNodes(mirrorConfig.Mirror, content) - - if err != nil { - fmt.Println("Warning: Mirror not added to Kind nodes") - } - - return nil +// Mirror represents a registry mirror container. +type Mirror struct { + baseContainer + config *config.RegistryMirrorConfig } -/** - * This private function only creates the registry mirror container. - */ -func createMirrorContainer(mirrorConfig *config.RegistryMirrorConfig) error { - ctx := context.Background() - - fmt.Printf("Deploying local image registry mirror %s\n", mirrorConfig.Mirror) - - cli, err := client.NewClientWithOpts(client.FromEnv) - - if err != nil { - return errors.Wrap(err, "unable to create docker client") - } - - mirrorContainerName := registryMirrorContainerName(mirrorConfig) - _, err = cli.ContainerInspect(ctx, mirrorContainerName) - - if err == nil { - // If we can retrieve a container of required name we assume it is - // running okay. Technically it could be restarting, stopping or - // have exited and container was not removed, but if that is the case - // then leave it up to the user to sort out. - fmt.Printf("Registry mirror %s already exists\n", mirrorConfig.Mirror) - - return nil +// NewMirror creates a new Mirror instance. +func NewMirror(mirrorConfig *config.RegistryMirrorConfig) *Mirror { + return &Mirror{ + baseContainer: baseContainer{ + containerName: fmt.Sprintf("%s-mirror-%s", constants.EducatesRegistryContainer, mirrorConfig.Mirror), + bindIP: "127.0.0.1", + hostPort: "", // dynamic port + labels: map[string]string{ + "app": constants.EducatesAppLabel, + "role": constants.EducatesMirrorRoleLabel, + "mirror": mirrorConfig.Mirror, + "url": mirrorConfig.URL, + "username": mirrorConfig.Username, + }, + envVars: buildMirrorEnvVars(mirrorConfig), + }, + config: mirrorConfig, } +} - // Prepare environment variables for the registry mirror +// buildMirrorEnvVars creates the environment variables for a mirror container. +func buildMirrorEnvVars(mirrorConfig *config.RegistryMirrorConfig) []string { envs := []string{} mirrorURL := mirrorConfig.URL if mirrorURL == "" { @@ -81,129 +58,111 @@ func createMirrorContainer(mirrorConfig *config.RegistryMirrorConfig) error { if mirrorConfig.Password != "" { envs = append(envs, fmt.Sprintf("REGISTRY_PROXY_PASSWORD=%s", mirrorConfig.Password)) } + return envs +} - _, err = cli.NetworkInspect(ctx, constants.EducatesNetworkName, network.InspectOptions{}) - +// DeployAndLinkToCluster deploys a registry mirror and links it to the cluster. +func (m *Mirror) DeployAndLinkToCluster() error { + err := m.Deploy() if err != nil { - _, err = cli.NetworkCreate(ctx, constants.EducatesNetworkName, network.CreateOptions{}) - - if err != nil { - return errors.Wrap(err, "cannot create educates network") - } + return errors.Wrap(err, "failed to deploy registry mirror "+m.config.Mirror) } - hostConfig := &container.HostConfig{ - PortBindings: nat.PortMap{ - "5000/tcp": []nat.PortBinding{ - { - HostIP: "127.0.0.1", - // HostPort: mirrorConfig.Port, - }, - }, - }, - RestartPolicy: container.RestartPolicy{ - Name: "always", - }, + content := fmt.Sprintf(hostMirrorTomlTemplate, m.containerName) + err = addRegistryConfigToKindNodes(m.config.Mirror, content) + if err != nil { + fmt.Println("Warning: Mirror not added to Kind nodes") } - labels := map[string]string{ - "app": constants.EducatesAppLabel, - "role": constants.EducatesMirrorRoleLabel, - "mirror": mirrorConfig.Mirror, - "url": mirrorConfig.URL, - "username": mirrorConfig.Username, - } + return nil +} - resp, err := cli.ContainerCreate(ctx, &container.Config{ - Image: constants.RegistryImageV3, - Tty: false, - Env: envs, - ExposedPorts: nat.PortSet{ - "5000/tcp": struct{}{}, - }, - Labels: labels, - }, hostConfig, nil, nil, mirrorContainerName) +// Deploy creates the mirror container. +func (m *Mirror) Deploy() error { + fmt.Printf("Deploying local image registry mirror %s\n", m.config.Mirror) + cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { - return errors.Wrap(err, "cannot create local registry mirror container") + return errors.Wrap(err, "unable to create docker client") } - if err := cli.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil { - return errors.Wrap(err, "unable to start local registry mirror") + exists, _ := m.containerExists(cli) + if exists { + // If we can retrieve a container of required name we assume it is + // running okay. Technically it could be restarting, stopping or + // have exited and container was not removed, but if that is the case + // then leave it up to the user to sort out. + fmt.Printf("Registry mirror %s already exists\n", m.config.Mirror) + return nil } - cli.NetworkDisconnect(ctx, constants.EducatesNetworkName, mirrorContainerName, false) + if err = m.ensureNetwork(cli, constants.EducatesNetworkName); err != nil { + return err + } - err = cli.NetworkConnect(ctx, constants.EducatesNetworkName, mirrorContainerName, &network.EndpointSettings{}) + if _, err = m.createAndStartContainer(cli); err != nil { + return errors.Wrap(err, "cannot create local registry mirror container") + } - if err != nil { - return errors.Wrap(err, "unable to connect local registry mirror to educates network") + if err = m.connectToNetwork(cli, constants.EducatesNetworkName); err != nil { + return errors.Wrap(err, fmt.Sprintf("unable to connect local registry mirror to %s network", constants.EducatesNetworkName)) } - if err = linkRegistryToClusterNetwork(mirrorContainerName); err != nil { - return errors.Wrap(err, "failed to link local registry mirror to cluster") + if err = m.linkToNetwork(cli, constants.ClusterNetworkName); err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to link local registry mirror to %s network", constants.ClusterNetworkName)) } return nil } -/** - * This function is used to delete a local registry mirror and unlink it from the cluster. - * It is used when deleting a local registry mirror. - */ -func DeleteMirrorAndUnlinkFromCluster(mirrorConfig *config.RegistryMirrorConfig) error { - ctx := context.Background() - - fmt.Printf("Deleting local image registry mirror %s\n", mirrorConfig.Mirror) +// DeleteAndUnlinkFromCluster deletes a local registry mirror and unlinks it from the cluster. +func (m *Mirror) DeleteAndUnlinkFromCluster() error { + fmt.Printf("Deleting local image registry mirror %s\n", m.config.Mirror) cli, err := client.NewClientWithOpts(client.FromEnv) - if err != nil { return errors.Wrap(err, "unable to create docker client") } - containerName := registryMirrorContainerName(mirrorConfig) - _, err = cli.ContainerInspect(ctx, containerName) - - if err != nil { - // If we can't retrieve a container of required name we assume it does - // not actually exist. - - fmt.Printf("Registry mirror %s does not exist\n", mirrorConfig.Mirror) + exists, _ := m.containerExists(cli) + if !exists { + fmt.Printf("Registry mirror %s does not exist\n", m.config.Mirror) return nil } - timeout := 30 - - err = cli.ContainerStop(ctx, containerName, container.StopOptions{Timeout: &timeout}) - + err = m.stopAndRemoveContainer(cli) if err != nil { - return errors.Wrap(err, "unable to stop registry mirror container "+containerName) + return err } - err = cli.ContainerRemove(ctx, containerName, container.RemoveOptions{}) - + // Remove the registry config from the kind nodes + err = removeRegistryConfigFromKindNodes(m.config.Mirror) if err != nil { - return errors.Wrap(err, "unable to delete registry mirror container "+containerName) + return errors.Wrap(err, "unable to remove registry config from kind nodes") } - // Remove the registry config from the kind nodes - err = removeRegistryConfigFromKindNodes(mirrorConfig.Mirror) + return nil +} +// Delete removes the mirror container. +func (m *Mirror) Delete() error { + fmt.Printf("Deleting local image registry mirror %s\n", m.config.Mirror) + + cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { - return errors.Wrap(err, "unable to remove registry config from kind nodes") + return errors.Wrap(err, "unable to create docker client") } - return nil + return m.stopAndRemoveContainer(cli) } +// DeleteRegistryMirrors deletes all local image registry mirrors. func DeleteRegistryMirrors() error { ctx := context.Background() fmt.Println("Deleting local image registry mirrors") cli, err := client.NewClientWithOpts(client.FromEnv) - if err != nil { return errors.Wrap(err, "unable to create docker client") } @@ -214,35 +173,28 @@ func DeleteRegistryMirrors() error { filters.Arg("label", "app="+constants.EducatesAppLabel), ), }) - if err != nil { return errors.Wrap(err, "unable to list registry mirrors") } for _, mirror := range mirrors { - timeout := 30 err = cli.ContainerStop(ctx, mirror.ID, container.StopOptions{Timeout: &timeout}) - if err != nil { return errors.Wrap(err, "unable to stop registry mirror container "+mirror.ID) } err = cli.ContainerRemove(ctx, mirror.ID, container.RemoveOptions{}) - if err != nil { return errors.Wrap(err, "unable to delete registry mirror container "+mirror.ID) } - } return nil } -/** - * This function is used to list all local image registry mirrors. - */ +// ListRegistryMirrors lists all local image registry mirrors. func ListRegistryMirrors() (string, error) { ctx := context.Background() @@ -271,9 +223,5 @@ func ListRegistryMirrors() (string, error) { return utils.PrintTable([]string{"NAME", "URL", "USERNAME", "STATUS", "CONTAINER_NAME"}, data), nil } -/** - * This function is used to get the container name of a registry mirror. - */ -func registryMirrorContainerName(mirrorConfig *config.RegistryMirrorConfig) string { - return fmt.Sprintf("%s-mirror-%s", constants.EducatesRegistryContainer, mirrorConfig.Mirror) -} +// Compile-time check that Mirror implements ContainerManager +var _ ContainerManager = (*Mirror)(nil) diff --git a/client-programs/pkg/registry/registry.go b/client-programs/pkg/registry/registry.go index 81a84314a..9ad71baab 100644 --- a/client-programs/pkg/registry/registry.go +++ b/client-programs/pkg/registry/registry.go @@ -1,21 +1,12 @@ package registry import ( - "archive/tar" - "bytes" - "compress/gzip" "context" _ "embed" "fmt" - "io" - "os" - "path" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/image" - "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" - "github.com/docker/go-connections/nat" "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" @@ -30,20 +21,37 @@ import ( const hostRegistryTomlTemplate = `[host."http://%s:5000"]` +// Registry represents the educates image registry container. +type Registry struct { + baseContainer + k8sClient *kubernetes.Clientset +} -/** - * This function is used to deploy the registry and link it to the cluster. - * It is used when creating a new local cluster. - */ -func DeployRegistryAndLinkToCluster(bindIP string, client *kubernetes.Clientset) error { +// NewRegistry creates a new Registry instance. +func NewRegistry(bindIP string, k8sClient *kubernetes.Clientset) *Registry { + return &Registry{ + baseContainer: baseContainer{ + containerName: constants.EducatesRegistryContainer, + bindIP: bindIP, + hostPort: "5001", + labels: map[string]string{ + "app": constants.EducatesAppLabel, + "role": constants.EducatesRegistryRoleLabel, + }, + }, + k8sClient: k8sClient, + } +} - err := createRegistryContainer(bindIP) +// DeployAndLinkToCluster deploys the registry and links it to the cluster. +// It is used when creating a new local cluster. +func (r *Registry) DeployAndLinkToCluster() error { + err := r.Deploy() if err != nil { return errors.Wrap(err, "failed to deploy registry") } // This is needed to make containerd use the local registry - if err = addRegistryConfigToKindNodes("localhost:5001", fmt.Sprintf(hostRegistryTomlTemplate, constants.EducatesRegistryContainer)); err != nil { return errors.Wrap(err, "failed to add registry config to kind nodes") } @@ -52,231 +60,79 @@ func DeployRegistryAndLinkToCluster(bindIP string, client *kubernetes.Clientset) } // This is needed so that kubernetes nodes can pull images from the local registry - if err = documentLocalRegistry(client); err != nil { + if err = r.documentLocalRegistry(); err != nil { return errors.Wrap(err, "failed to document registry config in cluster") } return nil } -/** - * This function is used to deploy a registry. - * It is used when creating a new local registry. - * It will not link the registry to the cluster. - */ -func DeployRegistry(bindIP string) error { - err := createRegistryContainer(bindIP) - if err != nil { - return errors.Wrap(err, "failed to deploy registry") - } - - return nil -} - -/** - * This private function only creates the registry container. - */ -func createRegistryContainer(bindIP string) error { - ctx := context.Background() - +// Deploy creates the registry container without linking to cluster. +// It is used when creating a new local registry standalone. +func (r *Registry) Deploy() error { fmt.Println("Deploying local image registry") cli, err := client.NewClientWithOpts(client.FromEnv) - if err != nil { return errors.Wrap(err, "unable to create docker client") } - _, err = cli.ContainerInspect(ctx, constants.EducatesRegistryContainer) - - if err == nil { + exists, _ := r.containerExists(cli) + if exists { // If we can retrieve a container of required name we assume it is // running okay. Technically it could be restarting, stopping or // have exited and container was not removed, but if that is the case // then leave it up to the user to sort out. - return nil } - reader, err := cli.ImagePull(ctx, constants.RegistryImageV3, image.PullOptions{}) - if err != nil { - return errors.Wrap(err, "cannot pull registry image") - } - - defer reader.Close() - io.Copy(os.Stdout, reader) - - _, err = cli.NetworkInspect(ctx, constants.EducatesNetworkName, network.InspectOptions{}) - - if err != nil { - _, err = cli.NetworkCreate(ctx, constants.EducatesNetworkName, network.CreateOptions{}) - - if err != nil { - return errors.Wrap(err, "cannot create educates network") - } - } - - hostConfig := &container.HostConfig{ - PortBindings: nat.PortMap{ - "5000/tcp": []nat.PortBinding{ - { - HostIP: bindIP, - HostPort: "5001", - }, - }, - }, - RestartPolicy: container.RestartPolicy{ - Name: "always", - }, + if err = r.pullRegistryImage(cli); err != nil { + return err } - labels := map[string]string{ - "app": constants.EducatesAppLabel, - "role": constants.EducatesRegistryRoleLabel, + if err = r.ensureNetwork(cli, constants.EducatesNetworkName); err != nil { + return err } - resp, err := cli.ContainerCreate(ctx, &container.Config{ - Image: constants.RegistryImageV3, - Tty: false, - ExposedPorts: nat.PortSet{ - "5000/tcp": struct{}{}, - }, - Labels: labels, - }, hostConfig, nil, nil, constants.EducatesRegistryContainer) - - if err != nil { + if _, err = r.createAndStartContainer(cli); err != nil { return errors.Wrap(err, "cannot create registry container") } - if err := cli.ContainerStart(ctx, resp.ID, container.StartOptions{}); err != nil { - return errors.Wrap(err, "unable to start registry") + if err = r.connectToNetwork(cli, constants.EducatesNetworkName); err != nil { + return errors.Wrap(err, fmt.Sprintf("unable to connect registry to %s network", constants.EducatesNetworkName)) } - cli.NetworkDisconnect(ctx, constants.EducatesNetworkName, constants.EducatesRegistryContainer, false) - - err = cli.NetworkConnect(ctx, constants.EducatesNetworkName, constants.EducatesRegistryContainer, &network.EndpointSettings{}) - - if err != nil { - return errors.Wrap(err, "unable to connect registry to educates network") - } - - if err = linkRegistryToClusterNetwork(constants.EducatesRegistryContainer); err != nil { - return errors.Wrap(err, "failed to link registry to cluster") + if err = r.linkToNetwork(cli, constants.ClusterNetworkName); err != nil { + return errors.Wrap(err, fmt.Sprintf("failed to link registry to %s network", constants.ClusterNetworkName)) } return nil } -/** - * This function is used to add the registry config to the kind nodes. - * It is used when creating a new local registry or registry mirror. - */ -func addRegistryConfigToKindNodes(repositoryName string, content string) error { - ctx := context.Background() - - fmt.Printf("Adding local image registry config (%s) to Kind nodes\n", repositoryName) - - cli, err := client.NewClientWithOpts(client.FromEnv) - - if err != nil { - return errors.Wrap(err, "unable to create docker client") - } - - containerID, _ := utils.GetContainerInfo(constants.EducatesControlPlaneContainer) - - if containerID == "" { - return errors.New(fmt.Sprintf("%s container not found", constants.EducatesControlPlaneContainer)) - } - - registryDir := "/etc/containerd/certs.d/" + repositoryName - - cmdStatement := []string{"mkdir", "-p", registryDir} - - optionsCreateExecuteScript := container.ExecOptions{ - AttachStdout: true, - AttachStderr: true, - Cmd: cmdStatement, - } - - response, err := cli.ContainerExecCreate(ctx, containerID, optionsCreateExecuteScript) - if err != nil { - return errors.Wrap(err, "unable to create exec command") - } - hijackedResponse, err := cli.ContainerExecAttach(ctx, response.ID, container.ExecAttachOptions{}) - if err != nil { - return errors.Wrap(err, "unable to attach exec command") - } - - hijackedResponse.Close() - - buffer, err := tarFile([]byte(content), path.Join("/etc/containerd/certs.d/"+repositoryName, "hosts.toml"), 0x644) - if err != nil { - return err - } - err = cli.CopyToContainer(context.Background(), - containerID, "/", - buffer, - container.CopyToContainerOptions{ - AllowOverwriteDirWithFile: true, - }) - if err != nil { - return errors.Wrap(err, "unable to copy file to container") - } - - return nil +// DeleteAndUnlinkFromCluster removes the registry and cleans up cluster configuration. +// For the registry, this is the same as Delete since the cluster config is tied to the cluster lifecycle. +func (r *Registry) DeleteAndUnlinkFromCluster() error { + return r.Delete() } -/** - * This function is used to remove the registry config from the kind nodes. - * It is used when deleting a local registry mirror. - */ -func removeRegistryConfigFromKindNodes(repositoryName string) error { - ctx := context.Background() - - fmt.Printf("Removing local image registry config (%s) from Kind nodes\n", repositoryName) +// Delete removes the registry container. +func (r *Registry) Delete() error { + fmt.Println("Deleting local image registry") cli, err := client.NewClientWithOpts(client.FromEnv) - if err != nil { return errors.Wrap(err, "unable to create docker client") } - containerID, _ := utils.GetContainerInfo(constants.EducatesControlPlaneContainer) + return r.stopAndRemoveContainer(cli) +} - if containerID == "" { +// documentLocalRegistry creates the ConfigMap that documents the local registry in the cluster. +func (r *Registry) documentLocalRegistry() error { + if r.k8sClient == nil { return nil } - registryDir := "/etc/containerd/certs.d/" + repositoryName - - cmdStatement := []string{"rm", "-rf", registryDir} - - optionsCreateExecuteScript := container.ExecOptions{ - AttachStdout: true, - AttachStderr: true, - Cmd: cmdStatement, - } - - response, err := cli.ContainerExecCreate(ctx, containerID, optionsCreateExecuteScript) - if err != nil { - return errors.Wrap(err, "unable to create exec command") - } - - hijackedResponse, err := cli.ContainerExecAttach(ctx, response.ID, container.ExecAttachOptions{}) - if err != nil { - return errors.Wrap(err, "unable to attach exec command") - } - - hijackedResponse.Close() - - return nil -} - -/** - * This function is used to document the local registry in the cluster. - * It is used when creating a new local registry. - */ -func documentLocalRegistry(client *kubernetes.Clientset) error { yamlBytes, err := yaml.Marshal(`host: "localhost:5001"`) if err != nil { return err @@ -292,13 +148,13 @@ func documentLocalRegistry(client *kubernetes.Clientset) error { }, } - if _, err := client.CoreV1().ConfigMaps("kube-public").Get(context.TODO(), "local-registry-hosting", metav1.GetOptions{}); k8serrors.IsNotFound(err) { - _, err = client.CoreV1().ConfigMaps("kube-public").Create(context.TODO(), configMap, metav1.CreateOptions{}) + if _, err := r.k8sClient.CoreV1().ConfigMaps("kube-public").Get(context.TODO(), "local-registry-hosting", metav1.GetOptions{}); k8serrors.IsNotFound(err) { + _, err = r.k8sClient.CoreV1().ConfigMaps("kube-public").Create(context.TODO(), configMap, metav1.CreateOptions{}) if err != nil { return errors.Wrap(err, "unable to create local registry hosting config map") } } else { - _, err = client.CoreV1().ConfigMaps("kube-public").Update(context.TODO(), configMap, metav1.UpdateOptions{}) + _, err = r.k8sClient.CoreV1().ConfigMaps("kube-public").Update(context.TODO(), configMap, metav1.UpdateOptions{}) if err != nil { return errors.Wrap(err, "unable to update local registry hosting config map") } @@ -307,87 +163,16 @@ func documentLocalRegistry(client *kubernetes.Clientset) error { return nil } -/** - * This function is used to link the registry to the cluster network, which is the kind network. - * It is used when creating a new local registry or registry mirror containers. - */ -func linkRegistryToClusterNetwork(containerName string) error { - ctx := context.Background() - - fmt.Println("Linking local image registry to cluster") - - cli, err := client.NewClientWithOpts(client.FromEnv) - - if err != nil { - return errors.Wrap(err, "unable to create docker client") +// UpdateK8SService updates the registry k8s service. +// It is used when creating a cluster or a registry in order to update the k8s service to point to the new registry. +func (r *Registry) UpdateK8SService() error { + if r.k8sClient == nil { + return errors.New("kubernetes client is required for UpdateK8SService") } - cli.NetworkDisconnect(ctx, "kind", containerName, false) - - err = cli.NetworkConnect(ctx, "kind", containerName, &network.EndpointSettings{}) - - if err != nil { - return errors.Wrap(err, "unable to connect registry to cluster network") - } - - return nil -} - -/** - * This function is used to delete the local registry. - * It is used when deleting a local registry or deleting all components of the local cluster. - */ -func DeleteRegistry() error { ctx := context.Background() - fmt.Println("Deleting local image registry") - cli, err := client.NewClientWithOpts(client.FromEnv) - - if err != nil { - return errors.Wrap(err, "unable to create docker client") - } - - _, err = cli.ContainerInspect(ctx, constants.EducatesRegistryContainer) - - if err != nil { - // If we can't retrieve a container of required name we assume it does - // not actually exist. - - return nil - } - - timeout := 30 - - err = cli.ContainerStop(ctx, constants.EducatesRegistryContainer, container.StopOptions{Timeout: &timeout}) - - // timeout := time.Duration(30) * time.Second - - // err = cli.ContainerStop(ctx, EducatesRegistryContainer, &timeout) - - if err != nil { - return errors.Wrap(err, "unable to stop registry container") - } - - err = cli.ContainerRemove(ctx, constants.EducatesRegistryContainer, container.RemoveOptions{}) - - if err != nil { - return errors.Wrap(err, "unable to delete registry container") - } - - return nil -} - -/** - * TODO: Learn whether this is needed or not - * This function is used to update the registry k8s service. - * It is used when creating a cluster or a registry in order to update the k8s service to point to the new registry. - */ -func UpdateRegistryK8SService(k8sclient *kubernetes.Clientset) error { - ctx := context.Background() - - cli, err := client.NewClientWithOpts(client.FromEnv) - if err != nil { return errors.Wrap(err, "unable to create docker client") } @@ -413,13 +198,11 @@ func UpdateRegistryK8SService(k8sclient *kubernetes.Clientset) error { endpointProtocol := v1.ProtocolTCP registryInfo, err := cli.ContainerInspect(ctx, constants.EducatesRegistryContainer) - if err != nil { return errors.Wrapf(err, "unable to inspect container for registry") } kindNetwork, exists := registryInfo.NetworkSettings.Networks["kind"] - if !exists { return errors.New("registry is not attached to kind network") } @@ -449,22 +232,20 @@ func UpdateRegistryK8SService(k8sclient *kubernetes.Clientset) error { }, } - endpointSliceClient := k8sclient.DiscoveryV1().EndpointSlices("default") + endpointSliceClient := r.k8sClient.DiscoveryV1().EndpointSlices("default") endpointSliceClient.Delete(context.TODO(), "registry-1", *metav1.NewDeleteOptions(0)) - servicesClient := k8sclient.CoreV1().Services("default") + servicesClient := r.k8sClient.CoreV1().Services("default") servicesClient.Delete(context.TODO(), "registry", *metav1.NewDeleteOptions(0)) _, err = endpointSliceClient.Create(context.TODO(), &endpointSlice, metav1.CreateOptions{}) - if err != nil { return errors.Wrap(err, "unable to create registry headless service endpoint") } _, err = servicesClient.Create(context.TODO(), &service, metav1.CreateOptions{}) - if err != nil { return errors.Wrap(err, "unable to create registry headless service") } @@ -472,19 +253,18 @@ func UpdateRegistryK8SService(k8sclient *kubernetes.Clientset) error { return nil } -func PruneRegistry() error { +// Prune runs garbage collection on the registry. +func (r *Registry) Prune() error { ctx := context.Background() fmt.Println("Pruning local image registry") cli, err := client.NewClientWithOpts(client.FromEnv) - if err != nil { return errors.Wrap(err, "unable to create docker client") } containerID, _ := utils.GetContainerInfo(constants.EducatesRegistryContainer) - if containerID == "" { return nil } @@ -511,37 +291,5 @@ func PruneRegistry() error { return nil } - - -/** - * This function is used to tar a file to be copied into a container. - */ -func tarFile(fileContent []byte, basePath string, fileMode int64) (*bytes.Buffer, error) { - buffer := &bytes.Buffer{} - - zr := gzip.NewWriter(buffer) - tw := tar.NewWriter(zr) - - hdr := &tar.Header{ - Name: basePath, - Mode: fileMode, - Size: int64(len(fileContent)), - } - if err := tw.WriteHeader(hdr); err != nil { - return buffer, err - } - if _, err := tw.Write(fileContent); err != nil { - return buffer, err - } - - // produce tar - if err := tw.Close(); err != nil { - return buffer, fmt.Errorf("error closing tar file: %w", err) - } - // produce gzip - if err := zr.Close(); err != nil { - return buffer, fmt.Errorf("error closing gzip file: %w", err) - } - - return buffer, nil -} +// Compile-time check that Registry implements ContainerManager +var _ ContainerManager = (*Registry)(nil) From 035eb1c2c730f040bf1193dd315b2772a3ace003 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Fri, 23 Jan 2026 19:50:31 +0100 Subject: [PATCH 20/24] Adding selection of kubernetes-version to create cluster. Defaults to 1.34 --- client-programs/go.mod | 3 +- client-programs/go.sum | 4 +-- .../pkg/cmd/local_cluster_create_cmd.go | 36 +++++++++++++++++++ client-programs/pkg/constants/kubernetes.go | 16 +++++++++ go.work.sum | 35 ++---------------- 5 files changed, 58 insertions(+), 36 deletions(-) create mode 100644 client-programs/pkg/constants/kubernetes.go diff --git a/client-programs/go.mod b/client-programs/go.mod index 2bc00347c..6670c6faf 100644 --- a/client-programs/go.mod +++ b/client-programs/go.mod @@ -27,9 +27,10 @@ require ( k8s.io/cli-runtime v0.34.2 k8s.io/client-go v0.34.2 k8s.io/klog/v2 v2.130.1 + // Keep kubectl aligned with DefaultKubernetesVersion in constants/kubernetes.go and k8s.io/api, k8s.io/apimachinery, k8s.io/cli-runtime, k8s.io/client-go, sigs.k8s.io/kind, sigs.k8s.io/controller-runtime k8s.io/kubectl v0.34.2 sigs.k8s.io/controller-runtime v0.22.4 - sigs.k8s.io/kind v0.30.0 + sigs.k8s.io/kind v0.31.0 sigs.k8s.io/yaml v1.6.0 ) diff --git a/client-programs/go.sum b/client-programs/go.sum index e6f46cf93..e14880223 100644 --- a/client-programs/go.sum +++ b/client-programs/go.sum @@ -831,8 +831,8 @@ sigs.k8s.io/controller-runtime v0.22.4 h1:GEjV7KV3TY8e+tJ2LCTxUTanW4z/FmNB7l327U sigs.k8s.io/controller-runtime v0.22.4/go.mod h1:+QX1XUpTXN4mLoblf4tqr5CQcyHPAki2HLXqQMY6vh8= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730 h1:IpInykpT6ceI+QxKBbEflcR5EXP7sU1kvOlxwZh5txg= sigs.k8s.io/json v0.0.0-20250730193827-2d320260d730/go.mod h1:mdzfpAEoE6DHQEN0uh9ZbOCuHbLK5wOm7dK4ctXE9Tg= -sigs.k8s.io/kind v0.30.0 h1:2Xi1KFEfSMm0XDcvKnUt15ZfgRPCT0OnCBbpgh8DztY= -sigs.k8s.io/kind v0.30.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= +sigs.k8s.io/kind v0.31.0 h1:UcT4nzm+YM7YEbqiAKECk+b6dsvc/HRZZu9U0FolL1g= +sigs.k8s.io/kind v0.31.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= 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/v6 v6.3.0 h1:jTijUJbW353oVOd9oTlifJqOGEkUw2jB/fXCbTiQEco= diff --git a/client-programs/pkg/cmd/local_cluster_create_cmd.go b/client-programs/pkg/cmd/local_cluster_create_cmd.go index 6846f347f..0d89d626c 100644 --- a/client-programs/pkg/cmd/local_cluster_create_cmd.go +++ b/client-programs/pkg/cmd/local_cluster_create_cmd.go @@ -15,6 +15,7 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/cluster" "github.com/educates/educates-training-platform/client-programs/pkg/config" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/docker" "github.com/educates/educates-training-platform/client-programs/pkg/installer" "github.com/educates/educates-training-platform/client-programs/pkg/registry" @@ -22,6 +23,23 @@ import ( "github.com/educates/educates-training-platform/client-programs/pkg/utils" ) +// SupportedKubernetesVersions returns a sorted list of supported Kubernetes versions +func SupportedKubernetesVersions() []string { + versions := make([]string, 0, len(constants.KubernetesVersionToKindImage)) + for v := range constants.KubernetesVersionToKindImage { + versions = append(versions, v) + } + // Sort in descending order (newest first) + for i := 0; i < len(versions)-1; i++ { + for j := i + 1; j < len(versions); j++ { + if versions[i] < versions[j] { + versions[i], versions[j] = versions[j], versions[i] + } + } + } + return versions +} + const ( localClusterCreateExample = ` # Create local educates cluster (no configuration, uses nip.io wildcard domain and Kind as provider config defaults) @@ -55,6 +73,7 @@ type LocalClusterCreateOptions struct { Config string Kubeconfig string ClusterImage string + KubernetesVersion string Domain string PackageRepository string Version string @@ -162,6 +181,17 @@ func (p *ProjectInfo) NewLocalClusterCreateCmd() *cobra.Command { } o.RegistryBindIP = ip + // Validate kubernetes-version if provided + if o.KubernetesVersion != "" { + if _, ok := constants.KubernetesVersionToKindImage[o.KubernetesVersion]; !ok { + return fmt.Errorf("unsupported kubernetes version %q, supported versions are: %v", o.KubernetesVersion, SupportedKubernetesVersions()) + } + // If kind-cluster-image is not explicitly set, use the mapped image + if o.ClusterImage == "" { + o.ClusterImage = constants.KubernetesVersionToKindImage[o.KubernetesVersion] + } + } + return o.Run() }, Example: localClusterCreateExample, @@ -185,6 +215,12 @@ func (p *ProjectInfo) NewLocalClusterCreateCmd() *cobra.Command { "", "docker image to use when booting the kind cluster", ) + c.Flags().StringVar( + &o.KubernetesVersion, + "kubernetes-version", + constants.DefaultKubernetesVersion, + fmt.Sprintf("kubernetes version for the kind cluster (supported: %v)", SupportedKubernetesVersions()), + ) c.Flags().StringVar( &o.Domain, "domain", diff --git a/client-programs/pkg/constants/kubernetes.go b/client-programs/pkg/constants/kubernetes.go new file mode 100644 index 000000000..7f3cf9dec --- /dev/null +++ b/client-programs/pkg/constants/kubernetes.go @@ -0,0 +1,16 @@ +package constants + +// KubernetesVersionToKindImage maps Kubernetes versions to their corresponding kind node images +var ( + KubernetesVersionToKindImage = map[string]string{ + "1.35": "kindest/node:v1.35.0@sha256:452d707d4862f52530247495d180205e029056831160e22870e37e3f6c1ac31f", + "1.34": "kindest/node:v1.34.3@sha256:08497ee19eace7b4b5348db5c6a1591d7752b164530a36f855cb0f2bdcbadd48", + "1.33": "kindest/node:v1.33.7@sha256:d26ef333bdb2cbe9862a0f7c3803ecc7b4303d8cea8e814b481b09949d353040", + "1.32": "kindest/node:v1.32.11@sha256:5fc52d52a7b9574015299724bd68f183702956aa4a2116ae75a63cb574b35af8", + "1.31": "kindest/node:v1.31.14@sha256:6f86cf509dbb42767b6e79debc3f2c32e4ee01386f0489b3b2be24b0a55aac2b", + } +) + +const ( + DefaultKubernetesVersion = "1.34" +) diff --git a/go.work.sum b/go.work.sum index d60356f77..7cf4f20ec 100644 --- a/go.work.sum +++ b/go.work.sum @@ -22,7 +22,6 @@ cloud.google.com/go v0.79.0/go.mod h1:3bzgcEeQlzbuEAYu4mrWhKqWjmpprinYgKJLgKHnbb cloud.google.com/go v0.81.0/go.mod h1:mk/AM35KwGk/Nm2YSeZbxXdrNK3KZOYHmLkOqC2V6E0= cloud.google.com/go v0.99.0 h1:y/cM2iqGgGi5D5DQZl6D9STN/3dR/Vx5Mp8s752oJTY= cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk= -cloud.google.com/go v0.110.4 h1:1JYyxKMN9hd5dR2MYTPWkGUgcoxVVhg0LKNKEo0qvmk= cloud.google.com/go v0.110.4/go.mod h1:+EYjdK8e5RME/VY/qLCAtuyALQ9q67dvuum8i+H5xsI= cloud.google.com/go v0.110.8 h1:tyNdfIxjzaWctIiLYOTalaLKZ17SI44SKFW26QbOhME= cloud.google.com/go v0.110.8/go.mod h1:Iz8AkXJf1qmxC3Oxoep8R1T36w8B92yU29PcBhHO5fk= @@ -226,7 +225,6 @@ github.com/alecthomas/kingpin/v2 v2.3.2/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HR github.com/alecthomas/kingpin/v2 v2.4.0/go.mod h1:0gyi0zQnjuFk8xrkNKamJoyUo382HRL7ATRpFZCw6tE= github.com/alecthomas/units v0.0.0-20211218093645-b94a6e3cc137/go.mod h1:OMCwj8VM1Kc9e19TLln2VL61YJF0x1XFtfdL4JdbSyE= github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= -github.com/alecthomas/units v0.0.0-20240927000941-0f3dac36c52b/go.mod h1:fvzegU4vN3H1qMT+8wDmzjAcDONcgo2/SZ/TyfdUOFs= github.com/antihax/optional v1.0.0/go.mod h1:uupD/76wgC+ih3iEmQUL+0Ugr19nfwCT1kdvxnR2qWY= github.com/antlr/antlr4/runtime/Go/antlr v1.4.10/go.mod h1:F7bn7fEU90QkQ3tnmaTx3LTKLEDqnwWODIYppRQ5hnY= github.com/antlr/antlr4/runtime/Go/antlr/v4 v4.0.0-20230305170008-8188dc5388df/go.mod h1:pSwJ0fSY5KhvocuWSx4fz3BA8OrA1bQn+K1Eli3BRwM= @@ -335,7 +333,6 @@ github.com/containerd/cgroups v1.1.0/go.mod h1:6ppBcbh/NOOUU+dMKrykgaBnK9lCIBxHq github.com/containerd/console v1.0.3/go.mod h1:7LqA/THxQ86k76b8c/EMSiaJ3h1eZkMkXar0TQ1gf3U= github.com/containerd/containerd/api v1.7.19/go.mod h1:fwGavl3LNwAV5ilJ0sbrABL44AQxmNjDRcwheXDb6Ig= github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc= -github.com/containerd/containerd/api v1.8.0/go.mod h1:dFv4lt6S20wTu/hMcP4350RL87qPWLVa/OHOwmmdnYc= github.com/containerd/errdefs v0.1.0/go.mod h1:YgWiiHtLmSeBrvpw+UfPijzbLaB77mEG1WwJTDETIV0= github.com/containerd/fuse-overlayfs-snapshotter/v2 v2.1.6/go.mod h1:Mau9LZ7ZnyKCIgcNT7sMG5fjaZ9YCOHU5RuolUikhBQ= github.com/containerd/go-cni v1.1.13/go.mod h1:nTieub0XDRmvCZ9VI/SBG6PyqT95N4FIhxsauF1vSBI= @@ -358,7 +355,6 @@ github.com/coredns/caddy v1.1.1/go.mod h1:A6ntJQlAWuQfFlsd9hvigKbo2WS0VUs2l1e2F+ github.com/coredns/corefile-migration v1.0.21/go.mod h1:XnhgULOEouimnzgn0t4WPuFDN2/PJQcTxdWKC5eXNGE= github.com/coredns/corefile-migration v1.0.24/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= github.com/coredns/corefile-migration v1.0.26/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= -github.com/coredns/corefile-migration v1.0.26/go.mod h1:56DPqONc3njpVPsdilEnfijCwNGC3/kTJLl7i7SPavY= github.com/coreos/go-oidc v2.2.1+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-oidc v2.3.0+incompatible/go.mod h1:CgnwVTmzoESiwO9qyAFEMiHoZ1nMCKZlZ9V6mm3/LKc= github.com/coreos/go-semver v0.3.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= @@ -375,12 +371,10 @@ github.com/cpuguy83/go-md2man/v2 v2.0.7/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6N github.com/cyphar/filepath-securejoin v0.2.4/go.mod h1:aPGpWjXOXUn2NCNjFvBE6aRxGGx79pTxQpKOJNYHHl4= github.com/cyphar/filepath-securejoin v0.3.4/go.mod h1:8s/MCNJREmFK0H02MF6Ihv1nakJe4L/w3WZLHNkvlYM= github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= -github.com/cyphar/filepath-securejoin v0.4.1/go.mod h1:Sdj7gXlvMcPZsbhwhQ33GguGLDGQL7h7bg04C/+u9jI= github.com/danieljoos/wincred v1.2.0/go.mod h1:FzQLLMKBFdvu+osBrnFODiv32YGwCfx0SkRa/eYHgec= github.com/danieljoos/wincred v1.2.1/go.mod h1:uGaFL9fDn3OLTvzCGulzE+SzjEe5NGlh5FdCcyfPwps= github.com/danieljoos/wincred v1.2.2/go.mod h1:w7w4Utbrz8lqeMbDAK0lkNJUv5sAOkFi7nd/ogr0Uh8= github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= -github.com/danieljoos/wincred v1.2.3/go.mod h1:6qqX0WNrS4RzPZ1tnroDzq9kY3fu1KwE7MRLQK4X0bs= github.com/davecgh/go-spew v0.0.0-20151105211317-5215b55f46b2/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/daviddengcn/go-colortext v1.0.0/go.mod h1:zDqEI5NVUop5QPpVJUxE9UO10hRnmkD5G4Pmri9+m4c= github.com/dimchansky/utfbom v1.1.0 h1:FcM3g+nofKgUteL8dm/UpdRXNC9KmADgTpLKsu0TRo4= @@ -496,13 +490,11 @@ github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5x github.com/godbus/dbus/v5 v5.1.0/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA= github.com/gogo/protobuf v1.2.2-0.20190723190241-65acae22fc9d/go.mod h1:SlYgWuQ5SjCEi6WLHjHCa1yvBfUnHcTbrrZtXPKa29o= github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= -github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= github.com/golang/glog v1.1.2/go.mod h1:zR+okUeTbrL6EL3xHUDxZuEtGv04p5shwip1+mL/rLQ= github.com/golang/glog v1.2.0/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/glog v1.2.2/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/glog v1.2.4/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= -github.com/golang/glog v1.2.5/go.mod h1:6AhwSGph0fcJtXVM/PEHPqZlFeoLxhs7/t5UDAwmO+w= github.com/golang/groupcache v0.0.0-20190702054246-869f871628b6/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20191227052852-215e87163ea7/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= github.com/golang/groupcache v0.0.0-20200121045136-8c9f03a8e57e/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= @@ -528,7 +520,6 @@ github.com/google/cadvisor v0.49.0/go.mod h1:s6Fqwb2KiWG6leCegVhw4KW40tf9f7m+SF1 github.com/google/cadvisor v0.49.2/go.mod h1:s6Fqwb2KiWG6leCegVhw4KW40tf9f7m+SF1aXiE8Wsk= github.com/google/cadvisor v0.51.0/go.mod h1:czGE/c/P/i0QFpVNKTFrIEzord9Y10YfpwuaSWXELc0= github.com/google/cadvisor v0.52.1/go.mod h1:OAhPcx1nOm5YwMh/JhpUOMKyv1YKLRtS9KgzWPndHmA= -github.com/google/cadvisor v0.52.1/go.mod h1:OAhPcx1nOm5YwMh/JhpUOMKyv1YKLRtS9KgzWPndHmA= github.com/google/cel-go v0.12.7/go.mod h1:Jk7ljRzLBhkmiAwBoUxB1sZSCVBAzkqPF25olK/iRDw= github.com/google/cel-go v0.17.7/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= github.com/google/cel-go v0.17.8/go.mod h1:HXZKzB0LXqer5lHHgfWAnlYwJaQBDKMjxjulNQzhwhY= @@ -628,7 +619,6 @@ github.com/imdario/mergo v0.3.12/go.mod h1:jmQim1M+e3UYxmgPu/WyfjB3N3VflVyUjjjwH github.com/intel/goresctrl v0.10.0/go.mod h1:1S8GDqL46GuKb525bxNhIEEkhf4rhVcbSf9DuKhp7mw= github.com/ishidawataru/sctp v0.0.0-20230406120618-7ff4192f6ff2/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg= github.com/ishidawataru/sctp v0.0.0-20250521072954-ae8eb7fa7995/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg= -github.com/ishidawataru/sctp v0.0.0-20250521072954-ae8eb7fa7995/go.mod h1:co9pwDoBCm1kGxawmb4sPq0cSIOOWNPT4KnHotMP1Zg= github.com/jessevdk/go-flags v1.4.0/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jessevdk/go-flags v1.6.1/go.mod h1:Mk8T1hIAWpOiJiHa9rJASDK2UGWji0EuPGBnNLMooyc= github.com/jmespath/go-jmespath v0.4.0/go.mod h1:T8mJZnbsbmF+m6zOOFylbeCJqk5+pHWvzYPziyZiYoo= @@ -657,9 +647,7 @@ github.com/knqyf263/go-plugin v0.9.0/go.mod h1:2z5lCO1/pez6qGo8CvCxSlBFSEat4MEp1 github.com/kr/fs v0.1.0/go.mod h1:FFnZGqtBN9Gxj7eW1uZ42v5BccTP0vu6NEaFoC2HwRg= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= github.com/kr/pretty v0.2.1/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= -github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= -github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/libopenstorage/openstorage v1.0.0/go.mod h1:Sp1sIObHjat1BeXhfMqLZ14wnOzEhNx2YQedreMcUyc= github.com/lithammer/dedent v1.1.0/go.mod h1:jrXYCQtgg0nJiN+StA2KgR7w6CiQNv9Fd/Z9BP0jIOc= github.com/magefile/mage v1.14.0/go.mod h1:z5UZb/iS3GoOSn0JgWuiw7dxlurVYTu+/jHXqQg881A= @@ -670,7 +658,6 @@ github.com/mailru/easyjson v0.0.0-20190626092158-b2ccc519800e/go.mod h1:C1wdFJiN github.com/mailru/easyjson v0.7.0/go.mod h1:KAzv3t3aY1NaHWoQz1+4F1ccyAH66Jk7yos7ldAVICs= github.com/mailru/easyjson v0.7.6/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= -github.com/mailru/easyjson v0.7.7/go.mod h1:xzfreul335JAWq5oZzymOObrkdz5UnU4kGfJJLY9Nlc= github.com/mattn/go-isatty v0.0.17/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.19/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= @@ -703,7 +690,6 @@ github.com/monochromegane/go-gitignore v0.0.0-20200626010858-205db1a8cc00/go.mod github.com/mrunalp/fileutils v0.5.1/go.mod h1:M1WthSahJixYnrXQl/DFQuteStB1weuxD2QJNHXfbSQ= github.com/munnerz/goautoneg v0.0.0-20120707110453-a547fc61f48d/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ= github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= -github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno= github.com/onsi/ginkgo v0.0.0-20170829012221-11459a886d9c/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= github.com/onsi/ginkgo v1.14.0/go.mod h1:iSB4RoI2tjJc9BBv4NKIKWKya62Rps+oPG/Lv9klQyY= github.com/onsi/ginkgo/v2 v2.0.0/go.mod h1:vw5CSIxN1JObi/U8gcbwft7ZxR2dgaR70JSE3/PpL4c= @@ -776,7 +762,6 @@ github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPH github.com/prometheus/procfs v0.16.1/go.mod h1:teAbpZRB1iIAJYREa1LsoWUXykVXA1KlTmWl8x/U+Is= github.com/remyoudompheng/bigfft v0.0.0-20170806203942-52369c62f446/go.mod h1:uYEyJGbgTkfkS4+E/PavXkNJcbFIpEtjt2B0KDQ5+9M= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= -github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= github.com/robfig/cron/v3 v3.0.1/go.mod h1:eQICP3HwyT7UooqI/z+Ov+PtYAWygg1TEWWzGIFLtro= github.com/rogpeppe/fastuuid v1.2.0/go.mod h1:jVj6XXZzXRy/MSR5jhDC/2q6DgLz+nrA6LYCDYWNEvQ= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= @@ -827,10 +812,8 @@ github.com/vbatts/tar-split v0.11.6/go.mod h1:dqKNtesIOr2j2Qv3W/cHjnvk9I8+G7oAkF github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE= github.com/vishvananda/netlink v1.3.1-0.20250206174618-62fb240731fa/go.mod h1:i6NetklAujEcC6fK0JPjT8qSwWyO0HLn4UKG+hGqeJs= github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= -github.com/vishvananda/netlink v1.3.1/go.mod h1:ARtKouGSTGchR8aMwmkzC0qiNPrrWO5JS/XMVl45+b4= github.com/vishvananda/netns v0.0.4/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= -github.com/vishvananda/netns v0.0.5/go.mod h1:SpkAiCQRtJ6TvvxPnOSyH3BMl6unz3xZlaprSwhNNJM= github.com/vmware-tanzu/carvel-imgpkg v0.36.0 h1:ha5a3WUPaqpGlP+QRkKBA9WyT85vUPh7+57x94Cmj58= github.com/vmware-tanzu/carvel-imgpkg v0.36.0/go.mod h1:8HeIt+froyx7iRjyZ/4py2wFMPXEFNyWUNUTQgAjD8M= github.com/vmware-tanzu/carvel-imgpkg v0.38.2/go.mod h1:v9BcO1qfXwwIQFw2zmksdUkx8eI1e+/a0Md3xG2BzDE= @@ -865,15 +848,12 @@ go.etcd.io/etcd/client/v3 v3.5.10/go.mod h1:RVeBnDz2PUEZqTpgqwAtUd8nAPf5kjyFyND7 go.etcd.io/etcd/pkg/v3 v3.5.10/go.mod h1:TKTuCKKcF1zxmfKWDkfz5qqYaE3JncKKZPFf8c1nFUs= go.etcd.io/etcd/pkg/v3 v3.5.16/go.mod h1:+lutCZHG5MBBFI/U4eYT5yL7sJfnexsoM20Y0t2uNuY= go.etcd.io/etcd/pkg/v3 v3.6.4/go.mod h1:kKcYWP8gHuBRcteyv6MXWSN0+bVMnfgqiHueIZnKMtE= -go.etcd.io/etcd/pkg/v3 v3.6.4/go.mod h1:kKcYWP8gHuBRcteyv6MXWSN0+bVMnfgqiHueIZnKMtE= go.etcd.io/etcd/raft/v3 v3.5.10/go.mod h1:odD6kr8XQXTy9oQnyMPBOr0TVe+gT0neQhElQ6jbGRc= go.etcd.io/etcd/raft/v3 v3.5.16/go.mod h1:P4UP14AxofMJ/54boWilabqqWoW9eLodl6I5GdGzazI= go.etcd.io/etcd/server/v3 v3.5.10/go.mod h1:gBplPHfs6YI0L+RpGkTQO7buDbHv5HJGG/Bst0/zIPo= go.etcd.io/etcd/server/v3 v3.5.16/go.mod h1:ynhyZZpdDp1Gq49jkUg5mfkDWZwXnn3eIqCqtJnrD/s= go.etcd.io/etcd/server/v3 v3.6.4/go.mod h1:aYCL/h43yiONOv0QIR82kH/2xZ7m+IWYjzRmyQfnCAg= go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo= -go.etcd.io/etcd/server/v3 v3.6.4/go.mod h1:aYCL/h43yiONOv0QIR82kH/2xZ7m+IWYjzRmyQfnCAg= -go.etcd.io/raft/v3 v3.6.0/go.mod h1:nLvLevg6+xrVtHUmVaTcTz603gQPHfh7kUAwV6YpfGo= go.opencensus.io v0.22.2/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.3/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= go.opencensus.io v0.22.4/go.mod h1:yxeiOL68Rb0Xd1ddK5vPZ/oVn4vY4Ynel7k9FzqtOIw= @@ -886,7 +866,6 @@ go.opentelemetry.io/contrib/detectors/gcp v1.36.0/go.mod h1:IbBN8uAIIx734PTonTPx go.opentelemetry.io/contrib/detectors/gcp v1.38.0/go.mod h1:SU+iU7nu5ud4oCb3LQOhIZ3nRLj6FNVrKgtflbaf2ts= go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.42.0/go.mod h1:XiglO+8SPMqM3Mqh5/rtxR1VHc63o8tb38QrU6tm4mU= go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.44.0/go.mod h1:uq8DrRaen3suIWTpdR/JNHCGpurSvMv9D5Nr5CU5TXc= -go.opentelemetry.io/contrib/instrumentation/github.com/emicklei/go-restful/otelrestful v0.44.0/go.mod h1:uq8DrRaen3suIWTpdR/JNHCGpurSvMv9D5Nr5CU5TXc= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.20.0/go.mod h1:oVGt1LRbBOBq1A5BQLlUg9UaU/54aiHw8cgjV3aWZ/E= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.35.0/go.mod h1:h8TWwRAhQpOd0aM5nYsRD8+flnkj+526GEIVlarH7eY= go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ= @@ -1004,7 +983,6 @@ golang.org/x/mod v0.22.0/go.mod h1:6SkKJ3Xj0I0BrPOZoBy3bdMptDDU9oJrpohJ3eWZ1fY= golang.org/x/mod v0.24.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/mod v0.25.0/go.mod h1:IXM97Txy2VM4PJ3gI61r1YEk/gAj6zAHN3AdZt6S9Ww= golang.org/x/mod v0.27.0/go.mod h1:rWI627Fq0DEoudcK+MBkNkCe0EetEaDSwJJkCcjpazc= -golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20170114055629-f2499483f923/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190628185345-da137c7871d7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20190724013045-ca1201d0de80/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= @@ -1137,7 +1115,6 @@ golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/telemetry v0.0.0-20240521205824-bda55230c457/go.mod h1:pRgIJT+bRLFKnoM1ldnzKoxTIn14Yxz928LQRYYgIN0= golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= -golang.org/x/telemetry v0.0.0-20251008203120-078029d740a8/go.mod h1:Pi4ztBfryZoJEkyFTI5/Ocsu2jXyDr6iSdgJiYE/uwE= golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= golang.org/x/term v0.25.0/go.mod h1:RPyXicDX+6vLxogjjRxjgD2TKtmAO6NZBsBRfrOLu7M= golang.org/x/term v0.28.0 h1:/Ts8HFuMR2E6IP/jlo7QVLZHggjKQbhu/7H0LJFr3Gg= @@ -1281,8 +1258,6 @@ google.golang.org/genproto v0.0.0-20210402141018-6c239bbf2bb1/go.mod h1:9lPAdzaE google.golang.org/genproto v0.0.0-20210602131652-f16073e35f0c/go.mod h1:UODoCrxHCcBojKKwX1terBiRUaqAsFqJiF615XL43r0= google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g= google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= -google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98 h1:Z0hjGZePRE0ZBWotvtrwxFNrNE9CUAGtplaDK5NNI/g= -google.golang.org/genproto v0.0.0-20230711160842-782d3b101e98/go.mod h1:S7mY02OqCJTD0E1OiQy1F72PWFB4bZJ87cAtLPYgDR0= google.golang.org/genproto v0.0.0-20230803162519-f966b187b2e5/go.mod h1:oH/ZOT02u4kWEp7oYBGYFFkCdKS/uYR9Z7+0/xuuFp8= google.golang.org/genproto v0.0.0-20231016165738-49dd2c1f3d0b/go.mod h1:CgAqfJo+Xmu0GwA0411Ht3OU3OntXwsGmrmjI8ioGXI= google.golang.org/genproto v0.0.0-20240123012728-ef4313101c80/go.mod h1:cc8bqMqtv9gMOr0zHg2Vzff5ULhhL2IXP4sbcn32Dro= @@ -1353,7 +1328,6 @@ google.golang.org/protobuf v1.36.3 h1:82DV7MYdb8anAVi3qge1wSnMDrnKK7ebr+I0hHRN1B google.golang.org/protobuf v1.36.3/go.mod h1:9fA7Ob0pmnwhb644+1+CVWFRbNajQ6iRojtC/QF5bRE= gopkg.in/gcfg.v1 v1.2.3/go.mod h1:yesOnuUOFQAhST5vPY4nbZsb/huCgGGXlipJsBn0b3o= gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI= -gopkg.in/go-jose/go-jose.v2 v2.6.3/go.mod h1:zzZDPkNNw/c9IE7Z9jr11mBZQhKQTMzoEEIoEdZlFBI= gopkg.in/ini.v1 v1.62.0/go.mod h1:pNLf8WUiyNEtQjuu5G5vTm06TEv9tsIgeAvK8hOrP4k= gopkg.in/natefinch/lumberjack.v2 v2.0.0/go.mod h1:l0ndWWf7gzL7RNwBG7wST/UCcT4T24xpD6X8LsfU/+k= gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc= @@ -1388,7 +1362,6 @@ k8s.io/code-generator v0.27.7/go.mod h1:w1YF/xQcTg+d9Ag+04xuRqER+q8rDnJ70ynLql8/ k8s.io/code-generator v0.30.3/go.mod h1:PFgBiv+miFV7TZYp+RXgROkhA+sWYZ+mtpbMLofMke8= k8s.io/code-generator v0.32.3/go.mod h1:+mbiYID5NLsBuqxjQTygKM/DAdKpAjvBzrJd64NU1G8= k8s.io/code-generator v0.34.1/go.mod h1:DeWjekbDnJWRwpw3s0Jat87c+e0TgkxoR4ar608yqvg= -k8s.io/code-generator v0.34.1/go.mod h1:DeWjekbDnJWRwpw3s0Jat87c+e0TgkxoR4ar608yqvg= k8s.io/component-base v0.28.6 h1:G4T8VrcQ7xZou3by/fY5NU5mfxOBlWaivS2lPrEltAo= k8s.io/component-base v0.28.6/go.mod h1:Dg62OOG3ALu2P4nAG00UdsuHoNLQJ5VsUZKQlLDcS+E= k8s.io/component-base v0.29.0/go.mod h1:sADonFTQ9Zc9yFLghpDpmNXEdHyQmFIGbiuZbqAXQ1M= @@ -1407,7 +1380,6 @@ k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0 k8s.io/gengo/v2 v2.0.0-20240826214909-a7b603a56eb7/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= k8s.io/gengo/v2 v2.0.0-20240911193312-2b36238f13e9/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= -k8s.io/gengo/v2 v2.0.0-20250604051438-85fd79dbfd9f/go.mod h1:EJykeLsmFC60UQbYJezXkEsG2FLrt0GPNkU5iK5GWxU= k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk= k8s.io/klog v1.0.0 h1:Pt+yjF5aB1xDSVbau4VsWe+dQNzA0qv1LlXdC2dF6Q8= k8s.io/klog v1.0.0/go.mod h1:4Bi6QPql/J/LkTDqv7R/cd3hPo4k2DG6Ptcz060Ez5I= @@ -1420,7 +1392,6 @@ k8s.io/kms v0.27.7/go.mod h1:JspOc8g6+cDlZfgW5GqnHS+OV6tAVyg4iXytCrqfNPw= k8s.io/kms v0.30.3/go.mod h1:GrMurD0qk3G4yNgGcsCEmepqf9KyyIrTXYR2lyUOJC4= k8s.io/kms v0.32.3/go.mod h1:Bk2evz/Yvk0oVrvm4MvZbgq8BD34Ksxs2SRHn4/UiOM= k8s.io/kms v0.34.1/go.mod h1:s1CFkLG7w9eaTYvctOxosx88fl4spqmixnNpys0JAtM= -k8s.io/kms v0.34.1/go.mod h1:s1CFkLG7w9eaTYvctOxosx88fl4spqmixnNpys0JAtM= k8s.io/kube-aggregator v0.22.17/go.mod h1:J557nueFVurHA1JiDrxT1HlgygNQ+2exsTVUXiz2T7k= k8s.io/kube-openapi v0.0.0-20191107075043-30be4d16710a/go.mod h1:1TqjTSzOxsLGIKfj0lK8EeCP7K1iUG65v09OM0/WG5E= k8s.io/kube-openapi v0.0.0-20220328201542-3ee0da9b0b42/go.mod h1:Z/45zLw8lUo4wdiUkI+v/ImEGAvu3WatcZl3lPMR4Rk= @@ -1431,11 +1402,9 @@ k8s.io/metrics v0.29.0/go.mod h1:UCuTT4dC/x/x6ODSk87IWIZQnuAfcwxOjb1gjWJdjMA= k8s.io/metrics v0.30.3/go.mod h1:W06L2nXRhOwPkFYDJYWdEIS3u6JcJy3ebIPYbndRs6A= k8s.io/metrics v0.32.3/go.mod h1:9R1Wk5cb+qJpCQon9h52mgkVCcFeYxcY+YkumfwHVCU= k8s.io/metrics v0.34.2/go.mod h1:Ydulln+8uZZctUM8yrUQX4rfq/Ay6UzsuXf24QJ37Vc= -k8s.io/metrics v0.34.2/go.mod h1:Ydulln+8uZZctUM8yrUQX4rfq/Ay6UzsuXf24QJ37Vc= k8s.io/system-validators v1.8.0/go.mod h1:gP1Ky+R9wtrSiFbrpEPwWMeYz9yqyy1S/KOh0Vci7WI= k8s.io/system-validators v1.9.1/go.mod h1:d4UVrxKu52s0BHU984Peb9VpIq4V9sd8xjTBV/waY/I= k8s.io/system-validators v1.10.2/go.mod h1:awfSS706v9R12VC7u7K89FKfqVy44G+E0L1A0FX9Wmw= -k8s.io/system-validators v1.10.2/go.mod h1:awfSS706v9R12VC7u7K89FKfqVy44G+E0L1A0FX9Wmw= k8s.io/utils v0.0.0-20210802155522-efc7438f0176/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20220210201930-3a6ce19ff2f9/go.mod h1:jPW/WVKK9YHAvNhRxK0md/EJ228hCsBRufyofKtW8HA= k8s.io/utils v0.0.0-20230726121419-3b25d923346b/go.mod h1:OLgZIPagt7ERELqWJFomSt595RzquPNLL48iOWgYOg0= @@ -1464,16 +1433,16 @@ sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kind v0.29.0 h1:3TpCsyh908IkXXpcSnsMjWdwdWjIl7o9IMZImZCWFnI= sigs.k8s.io/kind v0.29.0/go.mod h1:ldWQisw2NYyM6k64o/tkZng/1qQW7OlzcN5a8geJX3o= +sigs.k8s.io/kind v0.31.0 h1:UcT4nzm+YM7YEbqiAKECk+b6dsvc/HRZZu9U0FolL1g= +sigs.k8s.io/kind v0.31.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= sigs.k8s.io/knftables v0.0.14/go.mod h1:f/5ZLKYEUPUhVjUCg6l80ACdL7CIIyeL0DxfgojGRTk= sigs.k8s.io/knftables v0.0.17/go.mod h1:f/5ZLKYEUPUhVjUCg6l80ACdL7CIIyeL0DxfgojGRTk= sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= sigs.k8s.io/kustomize/api v0.18.0/go.mod h1:f8isXnX+8b+SGLHQ6yO4JG1rdkZlvhaCf/uZbLVMb0U= sigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM= -sigs.k8s.io/kustomize/api v0.20.1/go.mod h1:t6hUFxO+Ph0VxIk1sKp1WS0dOjbPCtLJ4p8aADLwqjM= sigs.k8s.io/kustomize/kustomize/v5 v5.0.4-0.20230601165947-6ce0bf390ce3/go.mod h1:/d88dHCvoy7d0AKFT0yytezSGZKjsZBVs9YTkBHSGFk= sigs.k8s.io/kustomize/kustomize/v5 v5.5.0/go.mod h1:AeFCmgCrXzmvjWWaeZCyBp6XzG1Y0w1svYus8GhJEOE= sigs.k8s.io/kustomize/kustomize/v5 v5.7.1/go.mod h1:+5/SrBcJ4agx1SJknGuR/c9thwRSKLxnKoI5BzXFaLU= -sigs.k8s.io/kustomize/kustomize/v5 v5.7.1/go.mod h1:+5/SrBcJ4agx1SJknGuR/c9thwRSKLxnKoI5BzXFaLU= sigs.k8s.io/kustomize/kyaml v0.14.3-0.20230601165947-6ce0bf390ce3/go.mod h1:JWP1Fj0VWGHyw3YUPjXSQnRnrwezrZSrApfX5S0nIag= sigs.k8s.io/kustomize/kyaml v0.18.1/go.mod h1:C3L2BFVU1jgcddNBE1TxuVLgS46TjObMwW5FT9FcjYo= sigs.k8s.io/kustomize/kyaml v0.20.1/go.mod h1:0EmkQHRUsJxY8Ug9Niig1pUMSCGHxQ5RklbpV/Ri6po= From 54b6418fd2b7d282a0d454c9e38086ee4621317a Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Wed, 28 Jan 2026 13:12:08 +0100 Subject: [PATCH 21/24] Enhancements to docker workshops, extension and bump go version --- Makefile | 10 +- assets-server/Dockerfile | 15 +- assets-server/go.mod | 2 +- client-programs/Dockerfile | 11 +- client-programs/go.mod | 2 +- .../pkg/cmd/docker_workshop_list_cmd.go | 2 +- .../pkg/docker/workshop_manager.go | 458 +- docker-extension/Dockerfile | 6 +- docker-extension/Makefile | 35 +- docker-extension/ui/package-lock.json | 4412 ++++++----------- docker-extension/ui/package.json | 15 +- docker-extension/ui/src/main.tsx | 6 +- docker-extension/ui/src/views/App.tsx | 6 +- go.work | 2 +- tunnel-manager/go.mod | 2 +- workshop-images/base-environment/Dockerfile | 6 +- 16 files changed, 1938 insertions(+), 3052 deletions(-) diff --git a/Makefile b/Makefile index 544f86a9d..2bfd9f061 100644 --- a/Makefile +++ b/Makefile @@ -157,7 +157,7 @@ endif # Push/Load configuration - can be overridden by PUSH_IMAGES env var or make parameter ifeq ($(PUSH_IMAGES),false) # Load images locally when PUSH_IMAGES is not true (default) -DOCKER_BUILDER = +DOCKER_BUILDER = --builder ${BUILDX_BUILDER} --load MULTIARCH_PLATFORMS = $(DOCKER_PLATFORM) else # Push images to registry when PUSH_IMAGES is true @@ -173,7 +173,7 @@ build-all-images: setup-buildx build-session-manager build-training-portal \ build-conda-environment build-docker-registry \ build-pause-container build-secrets-manager build-tunnel-manager \ build-image-cache build-assets-server build-lookup-service \ - build-cli-image + build-cli-image build-docker-extension build-core-images: setup-buildx build-session-manager build-training-portal \ build-base-environment build-docker-registry build-pause-container \ @@ -343,7 +343,7 @@ push-client-programs: build-client-programs (cd client-programs; GOOS=linux GOARCH=arm64 go build -o bin/educates-linux-arm64 cmd/educates/main.go) imgpkg push -i $(IMAGE_REPOSITORY)/educates-client-programs:$(PACKAGE_VERSION) -f client-programs/bin -build-cli-image: +build-cli-image: build-base-environment docker build --progress plain --platform $(MULTIARCH_PLATFORMS) \ $(DOCKER_BUILDER) \ -t $(IMAGE_REPOSITORY)/educates-cli:$(PACKAGE_VERSION) \ @@ -361,7 +361,7 @@ update-docker-extension : build-docker-extension project-docs/venv : python3 -m venv project-docs/venv project-docs/venv/bin/pip install -r project-docs/requirements.txt - + build-project-docs : project-docs/venv source project-docs/venv/bin/activate && make -C project-docs html @@ -418,4 +418,4 @@ clean-buildx: ## Clean up builder # Multiarch utility targets list-platforms: ## List available platforms for multiarch builds - @echo "Supported platforms: $(MULTIARCH_PLATFORMS)" \ No newline at end of file + @echo "Supported platforms: $(MULTIARCH_PLATFORMS)" diff --git a/assets-server/Dockerfile b/assets-server/Dockerfile index d67b1e023..cfbcd2300 100644 --- a/assets-server/Dockerfile +++ b/assets-server/Dockerfile @@ -1,11 +1,16 @@ -FROM golang:1.19-buster AS builder-image +FROM golang:1.25.6 AS builder-image WORKDIR /app +COPY go.mod go.sum ./ +RUN go mod tidy +RUN go mod download +COPY . . +ARG TARGETOS +ARG TARGETARCH +RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build \ + -o assets-server \ + main.go -COPY . /app/ - -RUN go mod download && \ - go build -o assets-server main.go FROM fedora:42 diff --git a/assets-server/go.mod b/assets-server/go.mod index ba1b0c6ca..db5a10560 100644 --- a/assets-server/go.mod +++ b/assets-server/go.mod @@ -1,6 +1,6 @@ module github.com/educates/educates-training-platform/assets-server -go 1.20 +go 1.25.6 require ( github.com/inconshreveable/mousetrap v1.1.0 // indirect diff --git a/client-programs/Dockerfile b/client-programs/Dockerfile index db8ccfd10..21654245d 100644 --- a/client-programs/Dockerfile +++ b/client-programs/Dockerfile @@ -5,10 +5,7 @@ ARG TAG=latest FROM ${REPOSITORY}/educates-base-environment:${TAG} AS themes-source # Multi-stage build for client-programs -FROM golang:1.24.10-alpine AS builder - -# Install build dependencies -RUN apk add --no-cache git ca-certificates tzdata +FROM golang:1.25.6 AS builder # Set working directory WORKDIR /src @@ -16,6 +13,9 @@ WORKDIR /src # Copy go mod files COPY go.mod go.sum ./ +# Tidy up dependencies +RUN go mod tidy + # Download dependencies RUN go mod download @@ -40,9 +40,8 @@ RUN GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build \ # Runtime stage - minimal image with binaries FROM scratch -# Build for multiple architectures ARG TARGETOS ARG TARGETARCH # Copy binaries from builder COPY --from=builder /src/bin/educates-${TARGETOS}-${TARGETARCH} /educates -ENTRYPOINT ["/educates"] \ No newline at end of file +ENTRYPOINT ["/educates"] diff --git a/client-programs/go.mod b/client-programs/go.mod index 6670c6faf..b399b883f 100644 --- a/client-programs/go.mod +++ b/client-programs/go.mod @@ -1,6 +1,6 @@ module github.com/educates/educates-training-platform/client-programs -go 1.24.10 +go 1.25.6 require ( carvel.dev/imgpkg v0.46.1 diff --git a/client-programs/pkg/cmd/docker_workshop_list_cmd.go b/client-programs/pkg/cmd/docker_workshop_list_cmd.go index 6e28f0e18..7850977ba 100644 --- a/client-programs/pkg/cmd/docker_workshop_list_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_list_cmd.go @@ -32,7 +32,7 @@ func (p *ProjectInfo) NewDockerWorkshopListCmd() *cobra.Command { for _, workshop := range workshops { data = append(data, []string{workshop.Name, workshop.Url, workshop.Source, workshop.Status}) } - fmt.Print(utils.PrintTable([]string{"NAME", "URL", "SOURCE", "STATUS"}, data)) + fmt.Println(utils.PrintTable([]string{"NAME", "URL", "SOURCE", "STATUS"}, data)) return nil }, diff --git a/client-programs/pkg/docker/workshop_manager.go b/client-programs/pkg/docker/workshop_manager.go index 1df55c416..dbb1adfa8 100644 --- a/client-programs/pkg/docker/workshop_manager.go +++ b/client-programs/pkg/docker/workshop_manager.go @@ -22,6 +22,8 @@ import ( "github.com/docker/compose/v5/pkg/api" "github.com/docker/compose/v5/pkg/compose" "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" + "github.com/docker/docker/api/types/volume" "github.com/docker/docker/client" "github.com/educates/educates-training-platform/client-programs/pkg/constants" eduk8sWorkshops "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" @@ -33,6 +35,18 @@ import ( "sigs.k8s.io/kind/pkg/cmd" ) +const ( + // Workshop status constants + WorkshopStatusStarting = "Starting" + WorkshopStatusRunning = "Running" + WorkshopStatusStopping = "Stopping" + + // Workshop label constants + LabelURL = "training.educates.dev/url" + LabelSource = "training.educates.dev/source" + LabelSession = "training.educates.dev/session" +) + const containerScript = `exec bash -s << "EOF" mkdir -p /opt/eduk8s/config cat > /opt/eduk8s/config/workshop.yaml << "EOS" @@ -72,11 +86,19 @@ exec start-container EOF ` +var ( + containerScriptTemplateOnce sync.Once + containerScriptTemplateCached *template.Template + containerScriptTemplateErr error +) + type DockerWorkshopsManager struct { Statuses map[string]DockerWorkshopDetails StatusesMutex sync.Mutex composeService api.Compose composeServiceMu sync.Mutex + dockerClient *client.Client + dockerClientMu sync.RWMutex } func NewDockerWorkshopsManager() DockerWorkshopsManager { @@ -149,47 +171,43 @@ func (m *DockerWorkshopsManager) ClearWorkshopStatus(name string) { } func (m *DockerWorkshopsManager) ListWorkshops() ([]DockerWorkshopDetails, error) { - setOfWorkshops := map[string]DockerWorkshopDetails{} - workshopsList := []DockerWorkshopDetails{} - ctx := context.Background() - cli, err := client.NewClientWithOpts(client.FromEnv) - + cli, err := m.GetDockerClient() if err != nil { - return nil, errors.Wrap(err, "unable to create docker client") + return nil, err } containers, err := cli.ContainerList(ctx, container.ListOptions{}) - if err != nil { return nil, errors.Wrap(err, "unable to list containers") } + // Copy statuses while holding lock briefly m.StatusesMutex.Lock() - + setOfWorkshops := make(map[string]DockerWorkshopDetails, len(m.Statuses)) for _, details := range m.Statuses { - if details.Status == "Starting" { + if details.Status == WorkshopStatusStarting { setOfWorkshops[details.Name] = details } } + statusesCopy := make(map[string]DockerWorkshopDetails, len(m.Statuses)) + for k, v := range m.Statuses { + statusesCopy[k] = v + } + m.StatusesMutex.Unlock() - defer m.StatusesMutex.Unlock() - - for _, container := range containers { - url, found := container.Labels["training.educates.dev/url"] - source := container.Labels["training.educates.dev/source"] - instance := container.Labels["training.educates.dev/session"] - - details, statusFound := m.Statuses[instance] - - status := "Running" + for _, ctr := range containers { + url, found := ctr.Labels[LabelURL] + source := ctr.Labels[LabelSource] + instance := ctr.Labels[LabelSession] - if statusFound { + status := WorkshopStatusRunning + if details, statusFound := statusesCopy[instance]; statusFound { status = details.Status } - if found && url != "" && len(container.Names) != 0 { + if found && url != "" && len(ctr.Names) != 0 { setOfWorkshops[instance] = DockerWorkshopDetails{ Name: instance, Url: url, @@ -199,6 +217,7 @@ func (m *DockerWorkshopsManager) ListWorkshops() ([]DockerWorkshopDetails, error } } + workshopsList := make([]DockerWorkshopDetails, 0, len(setOfWorkshops)) for _, details := range setOfWorkshops { workshopsList = append(workshopsList, details) } @@ -242,6 +261,79 @@ func (m *DockerWorkshopsManager) GetComposeService(stdout io.Writer, stderr io.W return service, nil } +// GetDockerClient returns a Docker client instance, initializing it if necessary. +// It uses a singleton pattern to reuse the same client instance across operations. +func (m *DockerWorkshopsManager) GetDockerClient() (*client.Client, error) { + // Try read lock first for fast path + m.dockerClientMu.RLock() + if m.dockerClient != nil { + defer m.dockerClientMu.RUnlock() + return m.dockerClient, nil + } + m.dockerClientMu.RUnlock() + + // Acquire write lock to initialize + m.dockerClientMu.Lock() + defer m.dockerClientMu.Unlock() + + // Double-check after acquiring write lock + if m.dockerClient != nil { + return m.dockerClient, nil + } + + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + return nil, errors.Wrap(err, "unable to create docker client") + } + + m.dockerClient = cli + return cli, nil +} + +// getContainerScriptTemplate returns the cached container script template. +func getContainerScriptTemplate() (*template.Template, error) { + containerScriptTemplateOnce.Do(func() { + funcMap := template.FuncMap{ + "inc": func(i int) int { return i + 1 }, + } + containerScriptTemplateCached, containerScriptTemplateErr = + template.New("entrypoint").Funcs(funcMap).Parse(containerScript) + }) + return containerScriptTemplateCached, containerScriptTemplateErr +} + + +// isDockerSocketEnabled checks if Docker socket is enabled in the workshop spec. +func isDockerSocketEnabled(workshop *unstructured.Unstructured) bool { + dockerEnabled, found, _ := unstructured.NestedBool( + workshop.Object, "spec", "session", "applications", "docker", "enabled") + if !found || !dockerEnabled { + return false + } + + extraServices, _, _ := unstructured.NestedMap( + workshop.Object, "spec", "session", "applications", "docker", "compose") + + socketEnabledDefault := len(extraServices) == 0 + socketEnabled, found, _ := unstructured.NestedBool( + workshop.Object, "spec", "session", "applications", "docker", "socket", "enabled") + + if !found { + return socketEnabledDefault + } + return socketEnabled +} + +// applyWorkshopVariables replaces workshop-related variables in a string efficiently. +func applyWorkshopVariables(content, name, localRepository, version string) string { + replacer := strings.NewReplacer( + "$(image_repository)", localRepository, + "$(workshop_name)", name, + "$(workshop_version)", version, + "$(platform_arch)", runtime.GOARCH, + ) + return replacer.Replace(content) +} func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployConfig, stdout io.Writer, stderr io.Writer) (string, error) { var err error @@ -274,7 +366,7 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployConfig, s name := workshop.GetName() - m.SetWorkshopStatus(name, "", o.Path, "Starting") + m.SetWorkshopStatus(name, "", o.Path, WorkshopStatusStarting) defer m.ClearWorkshopStatus(name) @@ -291,10 +383,9 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployConfig, s ctx := context.Background() - cli, err := client.NewClientWithOpts(client.FromEnv) - + cli, err := m.GetDockerClient() if err != nil { - return name, errors.Wrap(err, "unable to create docker client") + return name, err } _, err = cli.ContainerInspect(ctx, name) @@ -418,14 +509,7 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployConfig, s Assets: o.Assets, } - funcMap := template.FuncMap{ - "inc": func(i int) int { - return i + 1 - }, - } - - containerScriptTemplate, err := template.New("entrypoint").Funcs(funcMap).Parse(containerScript) - + containerScriptTemplate, err := getContainerScriptTemplate() if err != nil { return name, errors.Wrap(err, "not able to parse container script template") } @@ -472,26 +556,8 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployConfig, s workshopServiceConfig.Networks["kind"] = &composetypes.ServiceNetworkConfig{} } - dockerEnabled, found, _ := unstructured.NestedBool(workshop.Object, "spec", "session", "applications", "docker", "enabled") - - if found && dockerEnabled { - extraServices, _, _ := unstructured.NestedMap(workshop.Object, "spec", "session", "applications", "docker", "compose") - - socketEnabledDefault := true - - if len(extraServices) != 0 { - socketEnabledDefault = false - } - - socketEnabled, found, _ := unstructured.NestedBool(workshop.Object, "spec", "session", "applications", "docker", "socket", "enabled") - - if !found { - socketEnabled = socketEnabledDefault - } - - if socketEnabled { - workshopServiceConfig.GroupAdd = []string{"docker"} - } + if isDockerSocketEnabled(workshop) { + workshopServiceConfig.GroupAdd = []string{"docker"} } workshopServices := composetypes.Services{ @@ -585,7 +651,7 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployConfig, s } func (m *DockerWorkshopsManager) DeleteWorkshop(name string, stdout io.Writer, stderr io.Writer) error { - m.SetWorkshopStatus(name, "", "", "Stopping") + m.SetWorkshopStatus(name, "", "", WorkshopStatusStopping) defer m.ClearWorkshopStatus(name) @@ -626,22 +692,32 @@ func (m *DockerWorkshopsManager) DeleteWorkshop(name string, stdout io.Writer, s return errors.Wrap(err, "unable to stop workshop") } - cli, err2 := client.NewClientWithOpts(client.FromEnv) - - if err2 != nil { - return errors.Wrap(err2, "unable to create docker client") + cli, err := m.GetDockerClient() + if err != nil { + return err } - err2 = cli.VolumeRemove(ctx, fmt.Sprintf("%s_workshop", name), false) - - if err2 != nil { - return errors.Wrap(err2, "unable to delete workshop volume") + // List volumes that match the workshop name pattern and remove them + filters := filters.NewArgs() + filters.Add("name", fmt.Sprintf("%s_workshop", name)) + volumesListResponse, err := cli.VolumeList(ctx, volume.ListOptions{Filters: filters}) + if err != nil { + return errors.Wrap(err, "unable to list workshop volumes") } + for _, volume := range volumesListResponse.Volumes { + if err := cli.VolumeRemove(ctx, volume.Name, false); err != nil { + return errors.Wrap(err, "unable to delete workshop volume") + } + } workshopConfigDir := path.Join(configFileDir, "workshops", name) - os.RemoveAll(workshopConfigDir) - os.RemoveAll(composeConfigDir) + if err := os.RemoveAll(workshopConfigDir); err != nil { + fmt.Fprintf(stderr, "Warning: failed to remove workshop config dir: %v\n", err) + } + if err := os.RemoveAll(composeConfigDir); err != nil { + fmt.Fprintf(stderr, "Warning: failed to remove compose config dir: %v\n", err) + } return nil } @@ -679,139 +755,135 @@ func generateVendirFilesConfig(workshop *unstructured.Unstructured, name string, var vendirConfigs []string workshopVersion, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") - if !found { workshopVersion = version } filesItems, found, _ := unstructured.NestedSlice(workshop.Object, "spec", "workshop", "files") + if !found || len(filesItems) == 0 { + return vendirConfigs, nil + } - if found && len(filesItems) != 0 { - for _, filesItem := range filesItems { - directoriesConfig := []map[string]interface{}{} - - tmpPath, found := filesItem.(map[string]interface{})["path"] + for _, filesItem := range filesItems { + filesMap, ok := filesItem.(map[string]interface{}) + if !ok { + continue + } - var filesItemPath string + directoriesConfig := []map[string]interface{}{} - if found { - filesItemPath = tmpPath.(string) + var filesItemPath string + if tmpPath, found := filesMap["path"]; found { + if pathStr, ok := tmpPath.(string); ok { + filesItemPath = pathStr } else { filesItemPath = "." } + } else { + filesItemPath = "." + } - filesItemPath = filepath.Clean(path.Join("/opt/assets/files", filesItemPath)) - - filesItem.(map[string]interface{})["path"] = "." - - directoriesConfig = append(directoriesConfig, map[string]interface{}{ - "path": filesItemPath, - "contents": []interface{}{filesItem}, - }) - - vendirConfig := map[string]interface{}{ - "apiVersion": "vendir.k14s.io/v1alpha1", - "kind": "Config", - "directories": directoriesConfig, - } - - vendirConfigBytes, err := yaml.Marshal(&vendirConfig) - - if err != nil { - return []string{}, errors.Wrap(err, "failed to generate vendir config") - } + filesItemPath = filepath.Clean(path.Join("/opt/assets/files", filesItemPath)) + filesMap["path"] = "." - vendirConfigString := string(vendirConfigBytes) + directoriesConfig = append(directoriesConfig, map[string]interface{}{ + "path": filesItemPath, + "contents": []interface{}{filesItem}, + }) - vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(image_repository)", localRepository) - vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(workshop_name)", name) - vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(workshop_version)", workshopVersion) - vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(platform_arch)", runtime.GOARCH) + vendirConfig := map[string]interface{}{ + "apiVersion": "vendir.k14s.io/v1alpha1", + "kind": "Config", + "directories": directoriesConfig, + } - vendirConfigs = append(vendirConfigs, vendirConfigString) + vendirConfigBytes, err := yaml.Marshal(&vendirConfig) + if err != nil { + return []string{}, errors.Wrap(err, "failed to generate vendir config") } + + vendirConfigString := applyWorkshopVariables(string(vendirConfigBytes), name, localRepository, workshopVersion) + vendirConfigs = append(vendirConfigs, vendirConfigString) } return vendirConfigs, nil } func generateVendirPackagesConfig(workshop *unstructured.Unstructured, name string, localRepository string, version string) (string, error) { - var vendirConfigString string - workshopVersion, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") - if !found { workshopVersion = version } packagesItems, found, _ := unstructured.NestedSlice(workshop.Object, "spec", "workshop", "packages") + if !found || len(packagesItems) == 0 { + return "", nil + } - if found && len(packagesItems) != 0 { - directoriesConfig := []map[string]interface{}{} - - for _, packagesItem := range packagesItems { - tmpPackagesItem := packagesItem.(map[string]interface{}) - - tmpName, found := tmpPackagesItem["name"] + directoriesConfig := []map[string]interface{}{} - if !found { - continue - } + for _, packagesItem := range packagesItems { + tmpPackagesItem, ok := packagesItem.(map[string]interface{}) + if !ok { + continue + } - packagesItemPath := filepath.Clean(path.Join("/opt/packages", tmpName.(string))) + tmpName, found := tmpPackagesItem["name"] + if !found { + continue + } - tmpPackagesFilesItem := tmpPackagesItem["files"] + nameStr, ok := tmpName.(string) + if !ok { + continue + } - packagesFilesItem := tmpPackagesFilesItem.([]interface{}) + packagesItemPath := filepath.Clean(path.Join("/opt/packages", nameStr)) - for _, tmpEntry := range packagesFilesItem { - entry := tmpEntry.(map[string]interface{}) + tmpPackagesFilesItem, found := tmpPackagesItem["files"] + if !found { + continue + } - _, found = entry["path"] + packagesFilesItem, ok := tmpPackagesFilesItem.([]interface{}) + if !ok { + continue + } - if !found { + for _, tmpEntry := range packagesFilesItem { + if entry, ok := tmpEntry.(map[string]interface{}); ok { + if _, found := entry["path"]; !found { entry["path"] = "." } } - - directoriesConfig = append(directoriesConfig, map[string]interface{}{ - "path": packagesItemPath, - "contents": packagesFilesItem, - }) - - } - - vendirConfig := map[string]interface{}{ - "apiVersion": "vendir.k14s.io/v1alpha1", - "kind": "Config", - "directories": directoriesConfig, } - vendirConfigBytes, err := yaml.Marshal(&vendirConfig) - - if err != nil { - return "", errors.Wrap(err, "failed to generate vendir config") - } + directoriesConfig = append(directoriesConfig, map[string]interface{}{ + "path": packagesItemPath, + "contents": packagesFilesItem, + }) + } - vendirConfigString = string(vendirConfigBytes) + vendirConfig := map[string]interface{}{ + "apiVersion": "vendir.k14s.io/v1alpha1", + "kind": "Config", + "directories": directoriesConfig, + } - vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(image_repository)", localRepository) - vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(workshop_name)", name) - vendirConfigString = strings.ReplaceAll(vendirConfigString, "$(workshop_version)", workshopVersion) + vendirConfigBytes, err := yaml.Marshal(&vendirConfig) + if err != nil { + return "", errors.Wrap(err, "failed to generate vendir config") } - return vendirConfigString, nil + return applyWorkshopVariables(string(vendirConfigBytes), name, localRepository, workshopVersion), nil } func generateWorkshopImageName(workshop *unstructured.Unstructured, localRepository string, imageRepository string, baseImageVersion string, workshopImage string, workshopVersion string) (string, error) { - _, found, _ := unstructured.NestedString(workshop.Object, "spec", "version") - - if found { - workshopVersion, _, _ = unstructured.NestedString(workshop.Object, "spec", "version") + if version, found, _ := unstructured.NestedString(workshop.Object, "spec", "version"); found { + workshopVersion = version } image, found, err := unstructured.NestedString(workshop.Object, "spec", "workshop", "image") - if err != nil { return "", errors.Wrapf(err, "unable to parse workshop definition") } @@ -820,32 +892,33 @@ func generateWorkshopImageName(workshop *unstructured.Unstructured, localReposit image = "base-environment:*" } + if workshopImage != "" { + return workshopImage, nil + } + defaultImageVersion := strings.TrimSpace(baseImageVersion) - if workshopImage != "" { - image = workshopImage - } else { - if defaultImageVersion == "latest" { - image = strings.ReplaceAll(image, "base-environment:*", fmt.Sprintf("localhost:5001/educates-base-environment:%s", defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk8-environment:*", fmt.Sprintf("localhost:5001/educates-jdk8-environment:%s", defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk11-environment:*", fmt.Sprintf("localhost:5001/educates-jdk11-environment:%s", defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk17-environment:*", fmt.Sprintf("localhost:5001/educates-jdk17-environment:%s", defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk21-environment:*", fmt.Sprintf("localhost:5001/educates-jdk21-environment:%s", defaultImageVersion)) - image = strings.ReplaceAll(image, "conda-environment:*", fmt.Sprintf("localhost:5001/educates-conda-environment:%s", defaultImageVersion)) - } else { - image = strings.ReplaceAll(image, "base-environment:*", fmt.Sprintf("%s/educates-base-environment:%s", imageRepository, defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk8-environment:*", fmt.Sprintf("%s/educates-jdk8-environment:%s", imageRepository, defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk11-environment:*", fmt.Sprintf("%s/educates-jdk11-environment:%s", imageRepository, defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk17-environment:*", fmt.Sprintf("%s/educates-jdk17-environment:%s", imageRepository, defaultImageVersion)) - image = strings.ReplaceAll(image, "jdk21-environment:*", fmt.Sprintf("%s/educates-jdk21-environment:%s", imageRepository, defaultImageVersion)) - image = strings.ReplaceAll(image, "conda-environment:*", fmt.Sprintf("%s/educates-conda-environment:%s", imageRepository, defaultImageVersion)) - } + // Map of environment placeholders to their image names + imageMap := map[string]string{ + "base-environment:*": "educates-base-environment", + "jdk8-environment:*": "educates-jdk8-environment", + "jdk11-environment:*": "educates-jdk11-environment", + "jdk17-environment:*": "educates-jdk17-environment", + "jdk21-environment:*": "educates-jdk21-environment", + "conda-environment:*": "educates-conda-environment", } - image = strings.ReplaceAll(image, "$(image_repository)", localRepository) - image = strings.ReplaceAll(image, "$(workshop_version)", workshopVersion) + repo := imageRepository + if defaultImageVersion == "latest" { + repo = "localhost:5001" + } + + for placeholder, imageName := range imageMap { + replacement := fmt.Sprintf("%s/%s:%s", repo, imageName, defaultImageVersion) + image = strings.ReplaceAll(image, placeholder, replacement) + } - return image, nil + return applyWorkshopVariables(image, "", localRepository, workshopVersion), nil } func generateWorkshopVolumeMounts(workshop *unstructured.Unstructured, assets string) ([]composetypes.ServiceVolumeConfig, error) { @@ -859,54 +932,31 @@ func generateWorkshopVolumeMounts(workshop *unstructured.Unstructured, assets st if assets != "" { assets = filepath.Clean(assets) - assets, err := filepath.Abs(assets) - + absAssets, err := filepath.Abs(assets) if err != nil { return []composetypes.ServiceVolumeConfig{}, errors.Wrap(err, "can't resolve local workshop assets path") } filesMounts = append(filesMounts, composetypes.ServiceVolumeConfig{ Type: "bind", - Source: assets, + Source: absAssets, Target: "/opt/eduk8s/mnt/assets", ReadOnly: true, }) } - dockerEnabled, found, _ := unstructured.NestedBool(workshop.Object, "spec", "session", "applications", "docker", "enabled") - - if found && dockerEnabled { - extraServices, _, _ := unstructured.NestedMap(workshop.Object, "spec", "session", "applications", "docker", "compose") - - socketEnabledDefault := true - - if len(extraServices) != 0 { - socketEnabledDefault = false - } - - socketEnabled, found, _ := unstructured.NestedBool(workshop.Object, "spec", "session", "applications", "docker", "socket", "enabled") - - if !found { - socketEnabled = socketEnabledDefault + if isDockerSocketEnabled(workshop) { + dockerSocketSource := "/var/run/docker.sock" + if runtime.GOOS != "linux" { + dockerSocketSource = "/var/run/docker.sock.raw" } - if socketEnabled { - if runtime.GOOS == "linux" { - filesMounts = append(filesMounts, composetypes.ServiceVolumeConfig{ - Type: "bind", - Source: "/var/run/docker.sock", - Target: "/var/run/docker/docker.sock", - ReadOnly: true, - }) - } else { - filesMounts = append(filesMounts, composetypes.ServiceVolumeConfig{ - Type: "bind", - Source: "/var/run/docker.sock.raw", - Target: "/var/run/docker/docker.sock", - ReadOnly: true, - }) - } - } + filesMounts = append(filesMounts, composetypes.ServiceVolumeConfig{ + Type: "bind", + Source: dockerSocketSource, + Target: "/var/run/docker/docker.sock", + ReadOnly: true, + }) } return filesMounts, nil @@ -931,8 +981,8 @@ func generateWorkshopLabels(workshop *unstructured.Unstructured, host string, po domain := fmt.Sprintf("%s.nip.io", strings.ReplaceAll(host, ".", "-")) - labels["training.educates.dev/url"] = fmt.Sprintf("http://workshop.%s:%d", domain, port) - labels["training.educates.dev/session"] = workshop.GetName() + labels[LabelURL] = fmt.Sprintf("http://workshop.%s:%d", domain, port) + labels[LabelSession] = workshop.GetName() return labels, nil } diff --git a/docker-extension/Dockerfile b/docker-extension/Dockerfile index 3fba46a6a..ace68179d 100644 --- a/docker-extension/Dockerfile +++ b/docker-extension/Dockerfile @@ -4,7 +4,7 @@ ARG TAG=latest FROM ${REPOSITORY}/${CLI_IMAGE_NAME}:${TAG} AS client-programs -FROM node:24-alpine AS client-builder +FROM node:24 AS client-builder WORKDIR /ui # cache packages in layer COPY ui/package.json /ui/package.json @@ -20,9 +20,9 @@ FROM debian:trixie-slim LABEL org.opencontainers.image.title="Educates Docker Desktop Extension" \ org.opencontainers.image.description="Spin up a local Educates Training Platform workshop" \ - org.opencontainers.image.vendor="Educates" \ + org.opencontainers.image.vendor="Educates Team" \ org.opencontainers.image.licenses="Apache-2.0" \ - com.docker.desktop.extension.api.version="0.3.4" \ + com.docker.desktop.extension.api.version="0.4.2" \ com.docker.extension.screenshots="" \ com.docker.desktop.extension.icon="https://raw.githubusercontent.com/educates/educates-training-platform/main/project-assets/educates-logo.svg" \ com.docker.extension.detailed-description="Spin up a local Educates Training Platform workshop" \ diff --git a/docker-extension/Makefile b/docker-extension/Makefile index fdcc64e6e..13a0b878a 100644 --- a/docker-extension/Makefile +++ b/docker-extension/Makefile @@ -1,6 +1,13 @@ IMAGE_REPOSITORY = localhost:5001 PACKAGE_VERSION = latest +# Create an alias for the image repository that sanitizes invalid characters +# Docker Compose project names must consist only of lowercase alphanumeric characters, +# hyphens, and underscores, and start with a letter or number +IMAGE_ALIAS := $(shell echo $(IMAGE_REPOSITORY) | tr '[:upper:]' '[:lower:]' | sed 's/:/_/g' | sed 's/[^a-z0-9_-]//g' | sed 's/^[^a-z0-9]/a&/') +IMAGE = $(IMAGE_REPOSITORY)/educates-docker-extension:$(PACKAGE_VERSION) +IMAGE_ALIAS_FULL = $(IMAGE_ALIAS)/educates-docker-extension:$(PACKAGE_VERSION) + UNAME_SYSTEM := $(shell uname -s | tr '[:upper:]' '[:lower:]') UNAME_MACHINE := $(shell uname -m) @@ -42,26 +49,36 @@ DEV_UI_SOURCE?=http://localhost:3000 build-extension: setup-buildx ## Build service image to be deployed as a desktop extension docker build --progress plain --platform $(MULTIARCH_PLATFORMS) \ $(if $(DOCKER_BUILDER),$(DOCKER_BUILDER)) \ - -t $(IMAGE_REPOSITORY)/educates-docker-extension:$(PACKAGE_VERSION) \ + -t $(IMAGE) \ . -install-extension: build-extension ## Install the extension - docker extension install --force $(IMAGE_REPOSITORY)/educates-docker-extension:$(PACKAGE_VERSION) +# Internal target to ensure alias tag exists when repository has invalid characters +.PHONY: _ensure-alias-tag +_ensure-alias-tag: build-extension + @if [ "$(IMAGE)" != "$(IMAGE_ALIAS_FULL)" ]; then \ + if ! docker image inspect $(IMAGE_ALIAS_FULL) >/dev/null 2>&1; then \ + echo "Tagging image with alias: $(IMAGE_ALIAS_FULL)"; \ + docker tag $(IMAGE) $(IMAGE_ALIAS_FULL) || true; \ + fi; \ + fi + +install-extension: _ensure-alias-tag ## Install the extension + docker extension install --force $(IMAGE_ALIAS_FULL) -update-extension: build-extension ## Update the extension - docker extension update --force $(IMAGE_REPOSITORY)/educates-docker-extension:$(PACKAGE_VERSION) +update-extension: _ensure-alias-tag ## Update the extension + docker extension update --force $(IMAGE_ALIAS_FULL) setup-buildx: ## Create buildx builder for multi-arch build, if not exists docker buildx create --name $(BUILDX_BUILDER) --driver docker-container --driver-opt default-load=true --driver-opt network=host --use || true docker buildx inspect $(BUILDX_BUILDER) --bootstrap .PHONY: debug -debug: ## Enable debug in the extension - docker extension dev debug $(IMAGE_REPOSITORY)/educates-docker-extension:$(PACKAGE_VERSION) +debug: _ensure-alias-tag ## Enable debug in the extension + docker extension dev debug $(IMAGE_ALIAS_FULL) .PHONY: source -source: ## Replace the UI source of the extension - docker extension dev ui-source $(IMAGE_REPOSITORY)/educates-docker-extension:$(PACKAGE_VERSION) $(DEV_UI_SOURCE) +source: _ensure-alias-tag ## Replace the UI source of the extension + docker extension dev ui-source $(IMAGE_ALIAS_FULL) $(DEV_UI_SOURCE) .PHONY: dev-enable dev-enable: source debug diff --git a/docker-extension/ui/package-lock.json b/docker-extension/ui/package-lock.json index fdf371a62..d1c015d06 100644 --- a/docker-extension/ui/package-lock.json +++ b/docker-extension/ui/package-lock.json @@ -9,17 +9,16 @@ "version": "0.1.0", "dependencies": { "@docker/docker-mui-theme": "<0.1.0", - "@docker/extension-api-client": "0.3.4", - "@emotion/react": "^11.10.4", - "@emotion/styled": "^11.10.4", - "@mui/icons-material": "^5.14.12", - "@mui/lab": "^5.0.0-alpha.147", - "@mui/material": "^5.14.12", + "@docker/extension-api-client": "0.4.2", + "@emotion/react": "11.14.0", + "@emotion/styled": "11.14.1", + "@mui/icons-material": "6.5.0", + "@mui/material": "6.5.0", "react": "^18.2.0", "react-dom": "^18.2.0" }, "devDependencies": { - "@docker/extension-api-client-types": "0.3.4", + "@docker/extension-api-client-types": "0.4.2", "@types/jest": "^29.5.5", "@types/node": "^20.8.4", "@types/react": "^18.2.27", @@ -30,37 +29,24 @@ "vite": "^6.4.1" } }, - "node_modules/@ampproject/remapping": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.1.tgz", - "integrity": "sha512-lFMjJTrFL3j7L9yBxwYfCq2k6qqwHyzuUl/XBnif78PWTJYyL/dfowQHWE3sp6U6ZzqWiiIZnpTMO96zhkjwtg==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.3.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@babel/code-frame": { - "version": "7.26.2", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.26.2.tgz", - "integrity": "sha512-RJlIHRueQgwWitWgF8OdFYGZX328Ax5BCemNGlqHfplnRT9ESi8JkFlvaVYbS+UubVY6dpv87Fs2u5M29iNFVQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.25.9", + "@babel/helper-validator-identifier": "^7.28.5", "js-tokens": "^4.0.0", - "picocolors": "^1.0.0" + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { - "version": "7.26.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.26.8.tgz", - "integrity": "sha512-oH5UPLMWR3L2wEFLnFJ1TZXqHufiTKAiLfqw5zkhS4dKXLJ10yVztfil/twG8EDTA4F/tvVNw9nOl4ZMslB8rQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", "dev": true, "license": "MIT", "engines": { @@ -68,22 +54,22 @@ } }, "node_modules/@babel/core": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.26.9.tgz", - "integrity": "sha512-lWBYIrF7qK5+GjY5Uy+/hEgp8OJWOD/rpy74GplYRhEauvbHDeFB8t5hPOZxCZ0Oxf4Cc36tK51/l3ymJysrKw==", - "dev": true, - "license": "MIT", - "dependencies": { - "@ampproject/remapping": "^2.2.0", - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", - "@babel/helper-compilation-targets": "^7.26.5", - "@babel/helper-module-transforms": "^7.26.0", - "@babel/helpers": "^7.26.9", - "@babel/parser": "^7.26.9", - "@babel/template": "^7.26.9", - "@babel/traverse": "^7.26.9", - "@babel/types": "^7.26.9", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/remapping": "^2.3.5", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", @@ -102,18 +88,19 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@babel/generator": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.26.9.tgz", - "integrity": "sha512-kEWdzjOAUMW4hAyrzJ0ZaTOu9OmpyDIQicIh0zg0EEcEkYXZb2TjtBhnHi2ViX7PKwZqF4xwqfAm299/QMP3lg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", "license": "MIT", "dependencies": { - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9", - "@jridgewell/gen-mapping": "^0.3.5", - "@jridgewell/trace-mapping": "^0.3.25", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { @@ -121,14 +108,14 @@ } }, "node_modules/@babel/helper-compilation-targets": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.26.5.tgz", - "integrity": "sha512-IXuyn5EkouFJscIDuFF5EsiSolseme1s0CZB+QxVugqJLYmKdxI1VfIBOst0SUu4rnk2Z7kqTwmoO1lp3HIfnA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/compat-data": "^7.26.5", - "@babel/helper-validator-option": "^7.25.9", + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" @@ -137,29 +124,38 @@ "node": ">=6.9.0" } }, + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, "node_modules/@babel/helper-module-imports": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.25.9.tgz", - "integrity": "sha512-tnUA4RsrmflIM6W6RFTLFSXITtl0wKjgpnLgXyowocVPrbYrLUXSBXDgTs8BlbmIzIdlBySRQjINYs2BAkiLtw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "license": "MIT", "dependencies": { - "@babel/traverse": "^7.25.9", - "@babel/types": "^7.25.9" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.26.0", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.26.0.tgz", - "integrity": "sha512-xO+xu6B5K2czEnQye6BHA7DolFFmS3LB7stHZFaOLb1pAwO1HWLS8fXA+eh0A2yIvltPVmx3eNNDBJA2SLHXFw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-module-imports": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9", - "@babel/traverse": "^7.25.9" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -169,9 +165,9 @@ } }, "node_modules/@babel/helper-plugin-utils": { - "version": "7.26.5", - "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.26.5.tgz", - "integrity": "sha512-RS+jZcRdZdRFzMyr+wcsaqOmld1/EqTghfaBGQQd/WnRdzdlvSZ//kF7U8VQTxf1ynZ4cjUcYgjVGx13ewNPMg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.28.6.tgz", + "integrity": "sha512-S9gzZ/bz83GRysI7gAD4wPT/AI3uCnY+9xn+Mx/KPs2JwHJIz1W8PZkg2cqyt3RNOBM8ejcXhV6y8Og7ly/Dug==", "dev": true, "license": "MIT", "engines": { @@ -179,27 +175,27 @@ } }, "node_modules/@babel/helper-string-parser": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.25.9.tgz", - "integrity": "sha512-4A/SCr/2KLd5jrtOMFzaKjVtAei3+2r/NChoBNoZ3EyP/+GlhoaEGoWOZUmFmoITP7zOJyHIMm+DYRd8o3PvHA==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.25.9.tgz", - "integrity": "sha512-Ed61U6XJc3CVRfkERJWDz4dJwKe7iLmmJsbOGu9wSloNSFttHV0I8g6UAgb7qnK5ly5bGLPd4oXZlxCdANBOWQ==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.25.9.tgz", - "integrity": "sha512-e/zv1co8pp55dNdEcCynfj9X7nyUKUXoUEwfXqaZt0omVOmDe9oOTdKStH4GmAw6zxMFs50ZayuMfHDKlO7Tfw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "license": "MIT", "engines": { @@ -207,26 +203,26 @@ } }, "node_modules/@babel/helpers": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.26.10.tgz", - "integrity": "sha512-UPYc3SauzZ3JGgj87GgZ89JVdC5dj0AoetR5Bw6wj4niittNyFh6+eOGonYvJ1ao6B8lEa3Q3klS7ADZ53bc5g==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.10" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.26.9.tgz", - "integrity": "sha512-81NWa1njQblgZbQHxWHpxxCzNsa3ZwvFqpUg7P+NNUU6f3UU2jBEg4OlF/J6rl8+PQGh1q6/zWScd001YwcA5A==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", "license": "MIT", "dependencies": { - "@babel/types": "^7.26.9" + "@babel/types": "^7.28.6" }, "bin": { "parser": "bin/babel-parser.js" @@ -240,6 +236,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -252,6 +249,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -264,6 +262,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" }, @@ -271,11 +270,44 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-class-static-block": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", + "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, + "node_modules/@babel/plugin-syntax-import-attributes": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.28.6.tgz", + "integrity": "sha512-jiLC0ma9XkQT3TKJ9uYvlakm66Pamywo+qwL+oL8HJOvc6TWdZXVfhqJr8CCzbSGUAbDOzlGHJC1U+vRfLQDvw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.28.6" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -288,6 +320,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -296,12 +329,13 @@ } }, "node_modules/@babel/plugin-syntax-jsx": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.22.5.tgz", - "integrity": "sha512-gvyP4hZrgrs/wWMaocvxZ44Hw0b3W8Pe+cMxc8V1ULQ07oh8VNbIRaoD1LRZVTvD+0nieDKjfgKg89sD7rrKrg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.28.6.tgz", + "integrity": "sha512-wgEmr06G6sIpqr8YDwA2dSRTE3bJ+V0IfpzfSY3Lfgd7YWOaAdlykvJi13ZKBt8cZHfgH1IXN+CL656W3uUa4w==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -315,6 +349,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -327,6 +362,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -339,6 +375,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, @@ -351,6 +388,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -363,6 +401,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -375,6 +414,7 @@ "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, @@ -382,11 +422,28 @@ "@babel/core": "^7.0.0-0" } }, + "node_modules/@babel/plugin-syntax-private-property-in-object": { + "version": "7.14.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", + "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-plugin-utils": "^7.14.5" + }, + "engines": { + "node": ">=6.9.0" + }, + "peerDependencies": { + "@babel/core": "^7.0.0-0" + } + }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, @@ -398,12 +455,13 @@ } }, "node_modules/@babel/plugin-syntax-typescript": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.22.5.tgz", - "integrity": "sha512-1mS2o03i7t1c6VzH6fdQ3OA8tcEIxwG18zIPRp+UY1Ihv6W+XZzBCVxExF9upussPXJ0xE9XRHwMoNs1ep/nRQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.28.6.tgz", + "integrity": "sha512-+nDNmQye7nlnuuHDboPbGm00Vqg3oO8niRRL27/4LYHUsHYh0zJ1xWOz0uRwNFmM1Avzk8wZbc6rdiYhomzv/A==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.22.5" + "@babel/helper-plugin-utils": "^7.28.6" }, "engines": { "node": ">=6.9.0" @@ -413,13 +471,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-self": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.25.9.tgz", - "integrity": "sha512-y8quW6p0WHkEhmErnfe58r7x0A70uKphQm8Sp8cV7tjNQwK56sNVK0M73LK3WuYmsuyrftut4xAkjjgU0twaMg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-self/-/plugin-transform-react-jsx-self-7.27.1.tgz", + "integrity": "sha512-6UzkCs+ejGdZ5mFFC/OCUrv028ab2fp1znZmCZjAOBKiBK2jXD1O+BPSfX8X2qjJ75fZBMSnQn3Rq2mrBJK2mw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -429,13 +487,13 @@ } }, "node_modules/@babel/plugin-transform-react-jsx-source": { - "version": "7.25.9", - "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.25.9.tgz", - "integrity": "sha512-+iqjT8xmXhhYv4/uiYd8FNQsraMFZIfxVSqxxVSZP0WbbSAWvBXAul0m/zu+7Vv4O/3WtApy9pmaTMiumEZgfg==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-react-jsx-source/-/plugin-transform-react-jsx-source-7.27.1.tgz", + "integrity": "sha512-zbwoTsBruTeKB9hSq73ha66iFeJHuaFkUbwvqElnygoNbj/jHRsSeokowZFN3CZ64IvEqcmmkVe89OPXc7ldAw==", "dev": true, "license": "MIT", "dependencies": { - "@babel/helper-plugin-utils": "^7.25.9" + "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" @@ -445,57 +503,54 @@ } }, "node_modules/@babel/runtime": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz", - "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "license": "MIT", - "dependencies": { - "regenerator-runtime": "^0.14.0" - }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/template": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.26.9.tgz", - "integrity": "sha512-qyRplbeIpNZhmzOysF/wFMuP9sctmh2cFzRAZOn1YapxBsE1i9bJIY586R/WBLfLcmcBlM8ROBiQURnnNy+zfA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/parser": "^7.26.9", - "@babel/types": "^7.26.9" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.26.9", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.26.9.tgz", - "integrity": "sha512-ZYW7L+pL8ahU5fXmNbPF+iZFHCv5scFak7MZ9bwaRPLUhHh7QQEMjZUg0HevihoqCM5iSYHN61EyCoZvqC+bxg==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.26.2", - "@babel/generator": "^7.26.9", - "@babel/parser": "^7.26.9", - "@babel/template": "^7.26.9", - "@babel/types": "^7.26.9", - "debug": "^4.3.1", - "globals": "^11.1.0" + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", + "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { - "version": "7.26.10", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.26.10.tgz", - "integrity": "sha512-emqcG3vHrpxUKTrxcblR36dcrcoRDvKmnL/dCL6ZsHaShW80qxCAcNhzQZrpeM765VzEos+xOi4s+r4IXzTwdQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.25.9", - "@babel/helper-validator-identifier": "^7.25.9" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" @@ -505,41 +560,46 @@ "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@docker/docker-mui-theme": { - "version": "0.0.11", - "resolved": "https://registry.npmjs.org/@docker/docker-mui-theme/-/docker-mui-theme-0.0.11.tgz", - "integrity": "sha512-3A1axAPkmj9VPHqJYibojfron4cGl+jRMrHlzBqqRvvnH9jMGhv5lH4BPkXWQ2QPEWeqZ/yt7M9mN2ZZksBUwQ==", + "version": "0.0.13", + "resolved": "https://registry.npmjs.org/@docker/docker-mui-theme/-/docker-mui-theme-0.0.13.tgz", + "integrity": "sha512-ydWCcaSWwfpq/tfqiWT3DewGGt3xyR7mgTuhEc0QIlE+g7l1iDgpXU+Kso6nZDN7YhVsD4HS28P3NXpLygwzWg==", + "license": "Apache-2.0", "peerDependencies": { - "@mui/material": "^5.0.0", + "@mui/material": ">=5.x.x <=6.x.x", "react": "^17.0.0 || ^18.0.0", "react-dom": "^17.0.0 || ^18.0.0" } }, "node_modules/@docker/extension-api-client": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@docker/extension-api-client/-/extension-api-client-0.3.4.tgz", - "integrity": "sha512-bPfMyIy/mf1ir8lhc7qeVWEV0VF/JZShr5UjFcDxG79JW3umCOGU4c17EtYqwk8EY1kF3exWM6G1ITHOETUmGw==", + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@docker/extension-api-client/-/extension-api-client-0.4.2.tgz", + "integrity": "sha512-8iT3Wy1qqqTYgrI2YC/ShFC3D3oFbKVptgeh2VQ0hy+Ywb5pwD3Qi5Qr+L5lYakw5LUqMQSjQa3p7J9OxLF+0Q==", + "license": "Apache-2.0", "dependencies": { - "@docker/extension-api-client-types": "^0.3.4" + "@docker/extension-api-client-types": "^0.4.2" } }, "node_modules/@docker/extension-api-client-types": { - "version": "0.3.4", - "resolved": "https://registry.npmjs.org/@docker/extension-api-client-types/-/extension-api-client-types-0.3.4.tgz", - "integrity": "sha512-cDdD+dNSE0XCvQiw0R4j9aHpK+p6E7vi+z7RbKXfxwuQpfEMoeNCKFlp4W7K3XT78iWmoPz3DxQtZEAe4VJ1oQ==" + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@docker/extension-api-client-types/-/extension-api-client-types-0.4.2.tgz", + "integrity": "sha512-nsIFZ67B09q9rY0vOnWSTIM8+wcrxjVtKvDxu66WrEOiBQhrObPRUmyXDQuTyR+vSLlU0nunKexLLp2ge+d5vA==", + "license": "Apache-2.0" }, "node_modules/@emotion/babel-plugin": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.11.0.tgz", - "integrity": "sha512-m4HEDZleaaCH+XgDDsPF15Ht6wTLsgDTeR3WYj9Q/k76JtWhrJjcP4+/XlG8LGT/Rol9qUfOIztXeA84ATpqPQ==", + "version": "11.13.5", + "resolved": "https://registry.npmjs.org/@emotion/babel-plugin/-/babel-plugin-11.13.5.tgz", + "integrity": "sha512-pxHCpT2ex+0q+HH91/zsdHkw/lXd468DIN2zvfvLtPKLLMo6gQj7oLObq8PhkrxOZb/gGCq03S3Z7PDhS8pduQ==", + "license": "MIT", "dependencies": { "@babel/helper-module-imports": "^7.16.7", "@babel/runtime": "^7.18.3", - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/serialize": "^1.1.2", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/serialize": "^1.3.3", "babel-plugin-macros": "^3.1.0", "convert-source-map": "^1.5.0", "escape-string-regexp": "^4.0.0", @@ -549,47 +609,52 @@ } }, "node_modules/@emotion/cache": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.11.0.tgz", - "integrity": "sha512-P34z9ssTCBi3e9EI1ZsWpNHcfY1r09ZO0rZbRO2ob3ZQMnFI35jB536qoXbkdesr5EUhYi22anuEJuyxifaqAQ==", - "dependencies": { - "@emotion/memoize": "^0.8.1", - "@emotion/sheet": "^1.2.2", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/cache/-/cache-11.14.0.tgz", + "integrity": "sha512-L/B1lc/TViYk4DcpGxtAVbx0ZyiKM5ktoIyafGkH6zg/tj+mA+NE//aPYKG0k8kCHSHVJrpLpcAlOBEXQ3SavA==", + "license": "MIT", + "dependencies": { + "@emotion/memoize": "^0.9.0", + "@emotion/sheet": "^1.4.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", "stylis": "4.2.0" } }, "node_modules/@emotion/hash": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.1.tgz", - "integrity": "sha512-gJB6HLm5rYwSLI6PQa+X1t5CFGrv1J1TWG+sOyMCeKz2ojaj6Fnl/rZEspogG+cvqbt4AE/2eIyD2QfLKTBNlQ==" + "version": "0.9.2", + "resolved": "https://registry.npmjs.org/@emotion/hash/-/hash-0.9.2.tgz", + "integrity": "sha512-MyqliTZGuOm3+5ZRSaaBGP3USLw6+EGykkwZns2EPC5g8jJ4z9OrdZY9apkl3+UP9+sdz76YYkwCKP5gh8iY3g==", + "license": "MIT" }, "node_modules/@emotion/is-prop-valid": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.2.1.tgz", - "integrity": "sha512-61Mf7Ufx4aDxx1xlDeOm8aFFigGHE4z+0sKCa+IHCeZKiyP9RLD0Mmx7m8b9/Cf37f7NAvQOOJAbQQGVr5uERw==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/is-prop-valid/-/is-prop-valid-1.4.0.tgz", + "integrity": "sha512-QgD4fyscGcbbKwJmqNvUMSE02OsHUa+lAWKdEUIJKgqe5IwRSKd7+KhibEWdaKwgjLj0DRSHA9biAIqGBk05lw==", + "license": "MIT", "dependencies": { - "@emotion/memoize": "^0.8.1" + "@emotion/memoize": "^0.9.0" } }, "node_modules/@emotion/memoize": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.8.1.tgz", - "integrity": "sha512-W2P2c/VRW1/1tLox0mVUalvnWXxavmv/Oum2aPsRcoDJuob75FC3Y8FbpfLwUegRcxINtGUMPq0tFCvYNTBXNA==" + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/@emotion/memoize/-/memoize-0.9.0.tgz", + "integrity": "sha512-30FAj7/EoJ5mwVPOWhAyCX+FPfMDrVecJAM+Iw9NRoSl4BBAQeqj4cApHHUXOVvIPgLVDsCFoz/hGD+5QQD1GQ==", + "license": "MIT" }, "node_modules/@emotion/react": { - "version": "11.11.1", - "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.11.1.tgz", - "integrity": "sha512-5mlW1DquU5HaxjLkfkGN1GA/fvVGdyHURRiX/0FHl2cfIfRxSOfmxEH5YS43edp0OldZrZ+dkBKbngxcNCdZvA==", + "version": "11.14.0", + "resolved": "https://registry.npmjs.org/@emotion/react/-/react-11.14.0.tgz", + "integrity": "sha512-O000MLDBDdk/EohJPFUqvnp4qnHeYkVP5B0xEG0D/L7cOKP9kefu2DXn8dj74cQfsEzUqh+sr1RzFqiL1o+PpA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/cache": "^11.11.0", - "@emotion/serialize": "^1.1.2", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1", - "@emotion/weak-memoize": "^0.3.1", + "@emotion/babel-plugin": "^11.13.5", + "@emotion/cache": "^11.14.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2", + "@emotion/weak-memoize": "^0.4.0", "hoist-non-react-statics": "^3.3.1" }, "peerDependencies": { @@ -602,33 +667,36 @@ } }, "node_modules/@emotion/serialize": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.1.2.tgz", - "integrity": "sha512-zR6a/fkFP4EAcCMQtLOhIgpprZOwNmCldtpaISpvz348+DP4Mz8ZoKaGGCQpbzepNIUWbq4w6hNZkwDyKoS+HA==", + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/@emotion/serialize/-/serialize-1.3.3.tgz", + "integrity": "sha512-EISGqt7sSNWHGI76hC7x1CksiXPahbxEOrC5RjmFRJTqLyEK9/9hZvBbiYn70dw4wuwMKiEMCUlR6ZXTSWQqxA==", + "license": "MIT", "dependencies": { - "@emotion/hash": "^0.9.1", - "@emotion/memoize": "^0.8.1", - "@emotion/unitless": "^0.8.1", - "@emotion/utils": "^1.2.1", + "@emotion/hash": "^0.9.2", + "@emotion/memoize": "^0.9.0", + "@emotion/unitless": "^0.10.0", + "@emotion/utils": "^1.4.2", "csstype": "^3.0.2" } }, "node_modules/@emotion/sheet": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.2.2.tgz", - "integrity": "sha512-0QBtGvaqtWi+nx6doRwDdBIzhNdZrXUppvTM4dtZZWEGTXL/XE/yJxLMGlDT1Gt+UHH5IX1n+jkXyytE/av7OA==" + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@emotion/sheet/-/sheet-1.4.0.tgz", + "integrity": "sha512-fTBW9/8r2w3dXWYM4HCB1Rdp8NLibOw2+XELH5m5+AkWiL/KqYX6dc0kKYlaYyKjrQ6ds33MCdMPEwgs2z1rqg==", + "license": "MIT" }, "node_modules/@emotion/styled": { - "version": "11.11.0", - "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.11.0.tgz", - "integrity": "sha512-hM5Nnvu9P3midq5aaXj4I+lnSfNi7Pmd4EWk1fOZ3pxookaQTNew6bp4JaCBYM4HVFZF9g7UjJmsUmC2JlxOng==", + "version": "11.14.1", + "resolved": "https://registry.npmjs.org/@emotion/styled/-/styled-11.14.1.tgz", + "integrity": "sha512-qEEJt42DuToa3gurlH4Qqc1kVpNq8wO8cJtDzU46TjlzWjDlsVyevtYCRijVq3SrHsROS+gVQ8Fnea108GnKzw==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.18.3", - "@emotion/babel-plugin": "^11.11.0", - "@emotion/is-prop-valid": "^1.2.1", - "@emotion/serialize": "^1.1.2", - "@emotion/use-insertion-effect-with-fallbacks": "^1.0.1", - "@emotion/utils": "^1.2.1" + "@emotion/babel-plugin": "^11.13.5", + "@emotion/is-prop-valid": "^1.3.0", + "@emotion/serialize": "^1.3.3", + "@emotion/use-insertion-effect-with-fallbacks": "^1.2.0", + "@emotion/utils": "^1.4.2" }, "peerDependencies": { "@emotion/react": "^11.0.0-rc.0", @@ -641,32 +709,36 @@ } }, "node_modules/@emotion/unitless": { - "version": "0.8.1", - "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.8.1.tgz", - "integrity": "sha512-KOEGMu6dmJZtpadb476IsZBclKvILjopjUii3V+7MnXIQCYh8W3NgNcgwo21n9LXZX6EDIKvqfjYxXebDwxKmQ==" + "version": "0.10.0", + "resolved": "https://registry.npmjs.org/@emotion/unitless/-/unitless-0.10.0.tgz", + "integrity": "sha512-dFoMUuQA20zvtVTuxZww6OHoJYgrzfKM1t52mVySDJnMSEa08ruEvdYQbhvyu6soU+NeLVd3yKfTfT0NeV6qGg==", + "license": "MIT" }, "node_modules/@emotion/use-insertion-effect-with-fallbacks": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.0.1.tgz", - "integrity": "sha512-jT/qyKZ9rzLErtrjGgdkMBn2OP8wl0G3sQlBb3YPryvKHsjvINUhVaPFfP+fpBcOkmrVOVEEHQFJ7nbj2TH2gw==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@emotion/use-insertion-effect-with-fallbacks/-/use-insertion-effect-with-fallbacks-1.2.0.tgz", + "integrity": "sha512-yJMtVdH59sxi/aVJBpk9FQq+OR8ll5GT8oWd57UpeaKEVGab41JWaCFA7FRLoMLloOZF/c/wsPoe+bfGmRKgDg==", + "license": "MIT", "peerDependencies": { "react": ">=16.8.0" } }, "node_modules/@emotion/utils": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.2.1.tgz", - "integrity": "sha512-Y2tGf3I+XVnajdItskUCn6LX+VUDmP6lTL4fcqsXAv43dnlbZiuW4MWQW38rW/BVWSE7Q/7+XQocmpnRYILUmg==" + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/@emotion/utils/-/utils-1.4.2.tgz", + "integrity": "sha512-3vLclRofFziIa3J2wDh9jjbkUz9qk5Vi3IZ/FSTKViB0k+ef0fPV7dYrUIugbgupYDx7v9ud/SjrtEP8Y4xLoA==", + "license": "MIT" }, "node_modules/@emotion/weak-memoize": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.3.1.tgz", - "integrity": "sha512-EsBwpc7hBUJWAsNPBmJy4hxWx12v6bshQsldrVmjxJoc3isbxhOrF2IcCpaXxfvq03NwkI7sbsOLXbYuqF/8Ww==" + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/@emotion/weak-memoize/-/weak-memoize-0.4.0.tgz", + "integrity": "sha512-snKqtPW01tN0ui7yu9rGv69aJXr/a/Ywvl11sUjNtEcRc+ng/mQriFL0wLXMef74iHa/EkftbDzU9F8iFbH+zg==", + "license": "MIT" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.1.tgz", - "integrity": "sha512-kfYGy8IdzTGy+z0vFGvExZtxkFlA4zAxgKEahG9KE1ScBjpQnFsNOX8KTU5ojNru5ed5CVoJYXFtoxaq5nFbjQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.12.tgz", + "integrity": "sha512-Hhmwd6CInZ3dwpuGTF8fJG6yoWmsToE+vYgD4nytZVxcu1ulHpUQRAB1UJ8+N1Am3Mz4+xOByoQoSZf4D+CpkA==", "cpu": [ "ppc64" ], @@ -681,9 +753,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.1.tgz", - "integrity": "sha512-dp+MshLYux6j/JjdqVLnMglQlFu+MuVeNrmT5nk6q07wNhCdSnB7QZj+7G8VMUGh1q+vj2Bq8kRsuyA00I/k+Q==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.12.tgz", + "integrity": "sha512-VJ+sKvNA/GE7Ccacc9Cha7bpS8nyzVv0jdVgwNDaR4gDMC/2TTRc33Ip8qrNYUcpkOHUT5OZ0bUcNNVZQ9RLlg==", "cpu": [ "arm" ], @@ -698,9 +770,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.1.tgz", - "integrity": "sha512-50tM0zCJW5kGqgG7fQ7IHvQOcAn9TKiVRuQ/lN0xR+T2lzEFvAi1ZcS8DiksFcEpf1t/GYOeOfCAgDHFpkiSmA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.12.tgz", + "integrity": "sha512-6AAmLG7zwD1Z159jCKPvAxZd4y/VTO0VkprYy+3N2FtJ8+BQWFXU+OxARIwA46c5tdD9SsKGZ/1ocqBS/gAKHg==", "cpu": [ "arm64" ], @@ -715,9 +787,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.1.tgz", - "integrity": "sha512-GCj6WfUtNldqUzYkN/ITtlhwQqGWu9S45vUXs7EIYf+7rCiiqH9bCloatO9VhxsL0Pji+PF4Lz2XXCES+Q8hDw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.12.tgz", + "integrity": "sha512-5jbb+2hhDHx5phYR2By8GTWEzn6I9UqR11Kwf22iKbNpYrsmRB18aX/9ivc5cabcUiAT/wM+YIZ6SG9QO6a8kg==", "cpu": [ "x64" ], @@ -732,9 +804,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.1.tgz", - "integrity": "sha512-5hEZKPf+nQjYoSr/elb62U19/l1mZDdqidGfmFutVUjjUZrOazAtwK+Kr+3y0C/oeJfLlxo9fXb1w7L+P7E4FQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.12.tgz", + "integrity": "sha512-N3zl+lxHCifgIlcMUP5016ESkeQjLj/959RxxNYIthIg+CQHInujFuXeWbWMgnTo4cp5XVHqFPmpyu9J65C1Yg==", "cpu": [ "arm64" ], @@ -749,9 +821,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.1.tgz", - "integrity": "sha512-hxVnwL2Dqs3fM1IWq8Iezh0cX7ZGdVhbTfnOy5uURtao5OIVCEyj9xIzemDi7sRvKsuSdtCAhMKarxqtlyVyfA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.12.tgz", + "integrity": "sha512-HQ9ka4Kx21qHXwtlTUVbKJOAnmG1ipXhdWTmNXiPzPfWKpXqASVcWdnf2bnL73wgjNrFXAa3yYvBSd9pzfEIpA==", "cpu": [ "x64" ], @@ -766,9 +838,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.1.tgz", - "integrity": "sha512-1MrCZs0fZa2g8E+FUo2ipw6jw5qqQiH+tERoS5fAfKnRx6NXH31tXBKI3VpmLijLH6yriMZsxJtaXUyFt/8Y4A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.12.tgz", + "integrity": "sha512-gA0Bx759+7Jve03K1S0vkOu5Lg/85dou3EseOGUes8flVOGxbhDDh/iZaoek11Y8mtyKPGF3vP8XhnkDEAmzeg==", "cpu": [ "arm64" ], @@ -783,9 +855,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.1.tgz", - "integrity": "sha512-0IZWLiTyz7nm0xuIs0q1Y3QWJC52R8aSXxe40VUxm6BB1RNmkODtW6LHvWRrGiICulcX7ZvyH6h5fqdLu4gkww==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.12.tgz", + "integrity": "sha512-TGbO26Yw2xsHzxtbVFGEXBFH0FRAP7gtcPE7P5yP7wGy7cXK2oO7RyOhL5NLiqTlBh47XhmIUXuGciXEqYFfBQ==", "cpu": [ "x64" ], @@ -800,9 +872,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.1.tgz", - "integrity": "sha512-NdKOhS4u7JhDKw9G3cY6sWqFcnLITn6SqivVArbzIaf3cemShqfLGHYMx8Xlm/lBit3/5d7kXvriTUGa5YViuQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.12.tgz", + "integrity": "sha512-lPDGyC1JPDou8kGcywY0YILzWlhhnRjdof3UlcoqYmS9El818LLfJJc3PXXgZHrHCAKs/Z2SeZtDJr5MrkxtOw==", "cpu": [ "arm" ], @@ -817,9 +889,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.1.tgz", - "integrity": "sha512-jaN3dHi0/DDPelk0nLcXRm1q7DNJpjXy7yWaWvbfkPvI+7XNSc/lDOnCLN7gzsyzgu6qSAmgSvP9oXAhP973uQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.12.tgz", + "integrity": "sha512-8bwX7a8FghIgrupcxb4aUmYDLp8pX06rGh5HqDT7bB+8Rdells6mHvrFHHW2JAOPZUbnjUpKTLg6ECyzvas2AQ==", "cpu": [ "arm64" ], @@ -834,9 +906,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.1.tgz", - "integrity": "sha512-OJykPaF4v8JidKNGz8c/q1lBO44sQNUQtq1KktJXdBLn1hPod5rE/Hko5ugKKZd+D2+o1a9MFGUEIUwO2YfgkQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.12.tgz", + "integrity": "sha512-0y9KrdVnbMM2/vG8KfU0byhUN+EFCny9+8g202gYqSSVMonbsCfLjUO+rCci7pM0WBEtz+oK/PIwHkzxkyharA==", "cpu": [ "ia32" ], @@ -851,9 +923,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.1.tgz", - "integrity": "sha512-nGfornQj4dzcq5Vp835oM/o21UMlXzn79KobKlcs3Wz9smwiifknLy4xDCLUU0BWp7b/houtdrgUz7nOGnfIYg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.12.tgz", + "integrity": "sha512-h///Lr5a9rib/v1GGqXVGzjL4TMvVTv+s1DPoxQdz7l/AYv6LDSxdIwzxkrPW438oUXiDtwM10o9PmwS/6Z0Ng==", "cpu": [ "loong64" ], @@ -868,9 +940,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.1.tgz", - "integrity": "sha512-1osBbPEFYwIE5IVB/0g2X6i1qInZa1aIoj1TdL4AaAb55xIIgbg8Doq6a5BzYWgr+tEcDzYH67XVnTmUzL+nXg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.12.tgz", + "integrity": "sha512-iyRrM1Pzy9GFMDLsXn1iHUm18nhKnNMWscjmp4+hpafcZjrr2WbT//d20xaGljXDBYHqRcl8HnxbX6uaA/eGVw==", "cpu": [ "mips64el" ], @@ -885,9 +957,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.1.tgz", - "integrity": "sha512-/6VBJOwUf3TdTvJZ82qF3tbLuWsscd7/1w+D9LH0W/SqUgM5/JJD0lrJ1fVIfZsqB6RFmLCe0Xz3fmZc3WtyVg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.12.tgz", + "integrity": "sha512-9meM/lRXxMi5PSUqEXRCtVjEZBGwB7P/D4yT8UG/mwIdze2aV4Vo6U5gD3+RsoHXKkHCfSxZKzmDssVlRj1QQA==", "cpu": [ "ppc64" ], @@ -902,9 +974,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.1.tgz", - "integrity": "sha512-nSut/Mx5gnilhcq2yIMLMe3Wl4FK5wx/o0QuuCLMtmJn+WeWYoEGDN1ipcN72g1WHsnIbxGXd4i/MF0gTcuAjQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.12.tgz", + "integrity": "sha512-Zr7KR4hgKUpWAwb1f3o5ygT04MzqVrGEGXGLnj15YQDJErYu/BGg+wmFlIDOdJp0PmB0lLvxFIOXZgFRrdjR0w==", "cpu": [ "riscv64" ], @@ -919,9 +991,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.1.tgz", - "integrity": "sha512-cEECeLlJNfT8kZHqLarDBQso9a27o2Zd2AQ8USAEoGtejOrCYHNtKP8XQhMDJMtthdF4GBmjR2au3x1udADQQQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.12.tgz", + "integrity": "sha512-MsKncOcgTNvdtiISc/jZs/Zf8d0cl/t3gYWX8J9ubBnVOwlk65UIEEvgBORTiljloIWnBzLs4qhzPkJcitIzIg==", "cpu": [ "s390x" ], @@ -936,9 +1008,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.1.tgz", - "integrity": "sha512-xbfUhu/gnvSEg+EGovRc+kjBAkrvtk38RlerAzQxvMzlB4fXpCFCeUAYzJvrnhFtdeyVCDANSjJvOvGYoeKzFA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.12.tgz", + "integrity": "sha512-uqZMTLr/zR/ed4jIGnwSLkaHmPjOjJvnm6TVVitAa08SLS9Z0VM8wIRx7gWbJB5/J54YuIMInDquWyYvQLZkgw==", "cpu": [ "x64" ], @@ -953,9 +1025,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.1.tgz", - "integrity": "sha512-O96poM2XGhLtpTh+s4+nP7YCCAfb4tJNRVZHfIE7dgmax+yMP2WgMd2OecBuaATHKTHsLWHQeuaxMRnCsH8+5g==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.12.tgz", + "integrity": "sha512-xXwcTq4GhRM7J9A8Gv5boanHhRa/Q9KLVmcyXHCTaM4wKfIpWkdXiMog/KsnxzJ0A1+nD+zoecuzqPmCRyBGjg==", "cpu": [ "arm64" ], @@ -970,9 +1042,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.1.tgz", - "integrity": "sha512-X53z6uXip6KFXBQ+Krbx25XHV/NCbzryM6ehOAeAil7X7oa4XIq+394PWGnwaSQ2WRA0KI6PUO6hTO5zeF5ijA==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.12.tgz", + "integrity": "sha512-Ld5pTlzPy3YwGec4OuHh1aCVCRvOXdH8DgRjfDy/oumVovmuSzWfnSJg+VtakB9Cm0gxNO9BzWkj6mtO1FMXkQ==", "cpu": [ "x64" ], @@ -987,9 +1059,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.1.tgz", - "integrity": "sha512-Na9T3szbXezdzM/Kfs3GcRQNjHzM6GzFBeU1/6IV/npKP5ORtp9zbQjvkDJ47s6BCgaAZnnnu/cY1x342+MvZg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.12.tgz", + "integrity": "sha512-fF96T6KsBo/pkQI950FARU9apGNTSlZGsv1jZBAlcLL1MLjLNIWPBkj5NlSz8aAzYKg+eNqknrUJ24QBybeR5A==", "cpu": [ "arm64" ], @@ -1004,9 +1076,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.1.tgz", - "integrity": "sha512-T3H78X2h1tszfRSf+txbt5aOp/e7TAz3ptVKu9Oyir3IAOFPGV6O9c2naym5TOriy1l0nNf6a4X5UXRZSGX/dw==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.12.tgz", + "integrity": "sha512-MZyXUkZHjQxUvzK7rN8DJ3SRmrVrke8ZyRusHlP+kuwqTcfWLyqMOE3sScPPyeIXN/mDJIfGXvcMqCgYKekoQw==", "cpu": [ "x64" ], @@ -1020,10 +1092,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.25.12.tgz", + "integrity": "sha512-rm0YWsqUSRrjncSXGA7Zv78Nbnw4XL6/dzr20cyrQf7ZmRcsovpcRBdhD43Nuk3y7XIoW2OxMVvwuRvk9XdASg==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.1.tgz", - "integrity": "sha512-2H3RUvcmULO7dIE5EWJH8eubZAI4xw54H1ilJnRNZdeo8dTADEZ21w6J22XBkXqGJbe0+wnNJtw3UXRoLJnFEg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.12.tgz", + "integrity": "sha512-3wGSCDyuTHQUzt0nV7bocDy72r2lI33QL3gkDNGkod22EsYl04sMf0qLb8luNKTOmgF/eDEDP5BFNwoBKH441w==", "cpu": [ "x64" ], @@ -1038,9 +1127,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.1.tgz", - "integrity": "sha512-GE7XvrdOzrb+yVKB9KsRMq+7a2U/K5Cf/8grVFRAGJmfADr/e/ODQ134RK2/eeHqYV5eQRFxb1hY7Nr15fv1NQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.12.tgz", + "integrity": "sha512-rMmLrur64A7+DKlnSuwqUdRKyd3UE7oPJZmnljqEptesKM8wx9J8gx5u0+9Pq0fQQW8vqeKebwNXdfOyP+8Bsg==", "cpu": [ "arm64" ], @@ -1055,9 +1144,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.1.tgz", - "integrity": "sha512-uOxSJCIcavSiT6UnBhBzE8wy3n0hOkJsBOzy7HDAuTDE++1DJMRRVCPGisULScHL+a/ZwdXPpXD3IyFKjA7K8A==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.12.tgz", + "integrity": "sha512-HkqnmmBoCbCwxUKKNPBixiWDGCpQGVsrQfJoVGYLPT41XWF8lHuE5N6WhVia2n4o5QK5M4tYr21827fNhi4byQ==", "cpu": [ "ia32" ], @@ -1072,9 +1161,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.1.tgz", - "integrity": "sha512-Y1EQdcfwMSeQN/ujR5VayLOJ1BHaK+ssyk0AEzPjC+t1lITgsnccPqFjb6V+LsTp/9Iov4ysfjxLaGJ9RPtkVg==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.12.tgz", + "integrity": "sha512-alJC0uCZpTFrSL0CCDjcgleBXPnCrEAhTBILpeAp7M/OFgoqtAetfBzX0xM00MUsVVPpVjlPuMbREqnZCXaTnA==", "cpu": [ "x64" ], @@ -1088,45 +1177,12 @@ "node": ">=18" } }, - "node_modules/@floating-ui/core": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.0.tgz", - "integrity": "sha512-kK1h4m36DQ0UHGj5Ah4db7R0rHemTqqO0QLvUqi1/mUUp3LuAWbWxdxSIf/XsnH9VS6rRVPLJCncjRzUvyCLXg==", - "dependencies": { - "@floating-ui/utils": "^0.1.3" - } - }, - "node_modules/@floating-ui/dom": { - "version": "1.5.3", - "resolved": "https://registry.npmjs.org/@floating-ui/dom/-/dom-1.5.3.tgz", - "integrity": "sha512-ClAbQnEqJAKCJOEbbLo5IUlZHkNszqhuxS4fHAVxRPXPya6Ysf2G8KypnYcOTpx6I8xcgF9bbHb6g/2KpbV8qA==", - "dependencies": { - "@floating-ui/core": "^1.4.2", - "@floating-ui/utils": "^0.1.3" - } - }, - "node_modules/@floating-ui/react-dom": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@floating-ui/react-dom/-/react-dom-2.0.2.tgz", - "integrity": "sha512-5qhlDvjaLmAst/rKb3VdlCinwTF4EYMiVxuuc/HVUjs46W0zgtbMmAZ1UTsDrRTxRmUEzl92mOtWbeeXL26lSQ==", - "dependencies": { - "@floating-ui/dom": "^1.5.1" - }, - "peerDependencies": { - "react": ">=16.8.0", - "react-dom": ">=16.8.0" - } - }, - "node_modules/@floating-ui/utils": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/@floating-ui/utils/-/utils-0.1.6.tgz", - "integrity": "sha512-OfX7E2oUDYxtBvsuS4e/jSn4Q9Qb6DzgeYtsAdkPZ47znpoNsMgZw0+tVijiv3uGNR6dgNlty6r9rzIzHjtd/A==" - }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, + "license": "ISC", "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", @@ -1143,6 +1199,7 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1152,6 +1209,7 @@ "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1161,6 +1219,7 @@ "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", @@ -1173,81 +1232,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/console/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/console/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/console/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/console/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/console/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/console/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/core": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, + "license": "MIT", "dependencies": { "@jest/console": "^29.7.0", "@jest/reporters": "^29.7.0", @@ -1290,81 +1280,12 @@ } } }, - "node_modules/@jest/core/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/core/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/core/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/core/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/core/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/environment": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, + "license": "MIT", "dependencies": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", @@ -1380,6 +1301,7 @@ "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, + "license": "MIT", "dependencies": { "expect": "^29.7.0", "jest-snapshot": "^29.7.0" @@ -1393,6 +1315,7 @@ "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, + "license": "MIT", "dependencies": { "jest-get-type": "^29.6.3" }, @@ -1405,6 +1328,7 @@ "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", @@ -1422,6 +1346,7 @@ "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, + "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", @@ -1437,6 +1362,7 @@ "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, + "license": "MIT", "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^29.7.0", @@ -1475,81 +1401,12 @@ } } }, - "node_modules/@jest/reporters/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/reporters/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/reporters/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/reporters/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/reporters/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, + "license": "MIT", "dependencies": { "@sinclair/typebox": "^0.27.8" }, @@ -1562,6 +1419,7 @@ "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", @@ -1576,6 +1434,7 @@ "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, + "license": "MIT", "dependencies": { "@jest/console": "^29.7.0", "@jest/types": "^29.6.3", @@ -1591,6 +1450,7 @@ "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, + "license": "MIT", "dependencies": { "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", @@ -1606,6 +1466,7 @@ "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", @@ -1627,87 +1488,19 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/transform/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/transform/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/transform/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/@jest/transform/node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true - }, - "node_modules/@jest/transform/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jest/transform/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } + "license": "MIT" }, "node_modules/@jest/types": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", @@ -1720,180 +1513,81 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/@jest/types/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/@jest/types/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, + "node_modules/@jridgewell/gen-mapping": { + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/@jest/types/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/@jest/types/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" } }, - "node_modules/@jest/types/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/@jridgewell/gen-mapping": { - "version": "0.3.8", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.8.tgz", - "integrity": "sha512-imAbBGkb+ebQyxKgzv5Hu2nmROxoDOXHh80evxdoXNOrvAnVx7zimzc1Oo5h9RlfV4vPXaE2iM5pOFbvOCClWA==", "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", + "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" } }, "node_modules/@jridgewell/resolve-uri": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.1.tgz", - "integrity": "sha512-dSYZh7HhCDtCKm4QakX0xFpsRDqjjtZf/kjI/v3T3Nwt5r8/qz/M19F9ySyOqU94SXBmeG9ttTul+YnR4LOxFA==", - "engines": { - "node": ">=6.0.0" - } - }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "license": "MIT", "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.15", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz", - "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, - "node_modules/@mui/base": { - "version": "5.0.0-beta.18", - "resolved": "https://registry.npmjs.org/@mui/base/-/base-5.0.0-beta.18.tgz", - "integrity": "sha512-e9ZCy/ndhyt5MTshAS3qAUy/40UiO0jX+kAo6a+XirrPJE+rrQW+mKPSI0uyp+5z4Vh+z0pvNoJ2S2gSrNz3BQ==", - "dependencies": { - "@babel/runtime": "^7.23.1", - "@floating-ui/react-dom": "^2.0.2", - "@mui/types": "^7.2.5", - "@mui/utils": "^5.14.12", - "@popperjs/core": "^2.11.8", - "clsx": "^2.0.0", - "prop-types": "^15.8.1" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui" - }, - "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" - }, - "peerDependenciesMeta": { - "@types/react": { - "optional": true - } - } - }, "node_modules/@mui/core-downloads-tracker": { - "version": "5.14.12", - "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-5.14.12.tgz", - "integrity": "sha512-WZhCkKqhrXaSVBzoC6LNcVkIawS000OOt7gmnp4g9HhyvN0PSclRXc/JrkC7EwfzUAZJh+hiK2LaVsbtOpNuOg==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@mui/core-downloads-tracker/-/core-downloads-tracker-6.5.0.tgz", + "integrity": "sha512-LGb8t8i6M2ZtS3Drn3GbTI1DVhDY6FJ9crEey2lZ0aN2EMZo8IZBZj9wRf4vqbZHaWjsYgtbOnJw5V8UWbmK2Q==", + "license": "MIT", "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" } }, "node_modules/@mui/icons-material": { - "version": "5.14.12", - "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-5.14.12.tgz", - "integrity": "sha512-aFm6g/AIB3RQN9h/4MKoBoBybLZXeR3aDHWNx6KzemEpIlElUxv5uXRX5Qk1VC6v/YPkhbaPsiLLjsRSTiZF3w==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@mui/icons-material/-/icons-material-6.5.0.tgz", + "integrity": "sha512-VPuPqXqbBPlcVSA0BmnoE4knW4/xG6Thazo8vCLWkOKusko6DtwFV6B665MMWJ9j0KFohTIf3yx2zYtYacvG1g==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.1" + "@babel/runtime": "^7.26.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@mui/material": "^5.0.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@mui/material": "^6.5.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -1901,34 +1595,39 @@ } } }, - "node_modules/@mui/lab": { - "version": "5.0.0-alpha.147", - "resolved": "https://registry.npmjs.org/@mui/lab/-/lab-5.0.0-alpha.147.tgz", - "integrity": "sha512-AZjDEl31/co9baYrOBHMlXI8BCrV9JTCGDE2+IswVm32HNPYL5V2gHL3wKqn+MIFeCEg+z2es+8CU/Bau0JNSQ==", + "node_modules/@mui/material": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@mui/material/-/material-6.5.0.tgz", + "integrity": "sha512-yjvtXoFcrPLGtgKRxFaH6OQPtcLPhkloC0BML6rBG5UeldR0nPULR/2E2BfXdo5JNV7j7lOzrrLX2Qf/iSidow==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.1", - "@mui/base": "5.0.0-beta.18", - "@mui/system": "^5.14.12", - "@mui/types": "^7.2.5", - "@mui/utils": "^5.14.12", - "@mui/x-tree-view": "6.0.0-alpha.1", - "clsx": "^2.0.0", - "prop-types": "^15.8.1" + "@babel/runtime": "^7.26.0", + "@mui/core-downloads-tracker": "^6.5.0", + "@mui/system": "^6.5.0", + "@mui/types": "~7.2.24", + "@mui/utils": "^6.4.9", + "@popperjs/core": "^2.11.8", + "@types/react-transition-group": "^4.4.12", + "clsx": "^2.1.1", + "csstype": "^3.1.3", + "prop-types": "^15.8.1", + "react-is": "^19.0.0", + "react-transition-group": "^4.4.5" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@mui/material": "^5.0.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@mui/material-pigment-css": "^6.5.0", + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react-dom": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -1937,74 +1636,48 @@ "@emotion/styled": { "optional": true }, + "@mui/material-pigment-css": { + "optional": true + }, "@types/react": { "optional": true } } }, - "node_modules/@mui/material": { - "version": "5.14.12", - "resolved": "https://registry.npmjs.org/@mui/material/-/material-5.14.12.tgz", - "integrity": "sha512-EelF2L46VcVqhg3KjzIGBBpOtcBgRh0MMy9Efuk6Do81QdcZsFC9RebCVAflo5jIdbHiBmxBs5/l5Q9NjONozg==", - "dependencies": { - "@babel/runtime": "^7.23.1", - "@mui/base": "5.0.0-beta.18", - "@mui/core-downloads-tracker": "^5.14.12", - "@mui/system": "^5.14.12", - "@mui/types": "^7.2.5", - "@mui/utils": "^5.14.12", - "@types/react-transition-group": "^4.4.6", - "clsx": "^2.0.0", - "csstype": "^3.1.2", - "prop-types": "^15.8.1", - "react-is": "^18.2.0", - "react-transition-group": "^4.4.5" - }, - "engines": { - "node": ">=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui" - }, + "node_modules/@mui/material/node_modules/@mui/types": { + "version": "7.2.24", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", + "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", + "license": "MIT", "peerDependencies": { - "@emotion/react": "^11.5.0", - "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { - "@emotion/react": { - "optional": true - }, - "@emotion/styled": { - "optional": true - }, "@types/react": { "optional": true } } }, "node_modules/@mui/private-theming": { - "version": "5.14.12", - "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-5.14.12.tgz", - "integrity": "sha512-TWwm+9+BgHFpoR3w04FG+IqID4ALa74A27RuKq2CEaWgxliBZB24EVeI6djfjFt5t4FYmIb8BMw2ZJEir7YjLQ==", + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/@mui/private-theming/-/private-theming-6.4.9.tgz", + "integrity": "sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.1", - "@mui/utils": "^5.14.12", + "@babel/runtime": "^7.26.0", + "@mui/utils": "^6.4.9", "prop-types": "^15.8.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -2013,26 +1686,29 @@ } }, "node_modules/@mui/styled-engine": { - "version": "5.14.12", - "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-5.14.12.tgz", - "integrity": "sha512-bocxt1nDmXfB3gpLfCCmFCyJ7sVmscFs+PuheO210QagZwHVp47UIRT1AiswLDYSQo1ZqmVGn7KLEJEYK0d4Xw==", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@mui/styled-engine/-/styled-engine-6.5.0.tgz", + "integrity": "sha512-8woC2zAqF4qUDSPIBZ8v3sakj+WgweolpyM/FXf8jAx6FMls+IE4Y8VDZc+zS805J7PRz31vz73n2SovKGaYgw==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.1", - "@emotion/cache": "^11.11.0", - "csstype": "^3.1.2", + "@babel/runtime": "^7.26.0", + "@emotion/cache": "^11.13.5", + "@emotion/serialize": "^1.3.3", + "@emotion/sheet": "^1.4.0", + "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.4.1", "@emotion/styled": "^11.3.0", - "react": "^17.0.0 || ^18.0.0" + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -2044,31 +1720,32 @@ } }, "node_modules/@mui/system": { - "version": "5.14.12", - "resolved": "https://registry.npmjs.org/@mui/system/-/system-5.14.12.tgz", - "integrity": "sha512-6DXfjjLhW0/ia5qU3Crke7j+MnfDbMBOHlLIrqbrEqNs0AuSBv8pXniEGb+kqO0H804NJreRTEJRjCngwOX5CA==", - "dependencies": { - "@babel/runtime": "^7.23.1", - "@mui/private-theming": "^5.14.12", - "@mui/styled-engine": "^5.14.12", - "@mui/types": "^7.2.5", - "@mui/utils": "^5.14.12", - "clsx": "^2.0.0", - "csstype": "^3.1.2", + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/@mui/system/-/system-6.5.0.tgz", + "integrity": "sha512-XcbBYxDS+h/lgsoGe78ExXFZXtuIlSBpn/KsZq8PtZcIkUNJInkuDqcLd2rVBQrDC1u+rvVovdaWPf2FHKJf3w==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.26.0", + "@mui/private-theming": "^6.4.9", + "@mui/styled-engine": "^6.5.0", + "@mui/types": "~7.2.24", + "@mui/utils": "^6.4.9", + "clsx": "^2.1.1", + "csstype": "^3.1.3", "prop-types": "^15.8.1" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { "@emotion/react": "^11.5.0", "@emotion/styled": "^11.3.0", - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@emotion/react": { @@ -2082,12 +1759,13 @@ } } }, - "node_modules/@mui/types": { - "version": "7.2.5", - "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.5.tgz", - "integrity": "sha512-S2BwfNczr7VwS6ki8GoAXJyARoeSJDLuxOEPs3vEMyTALlf9PrdHv+sluX7kk3iKrCg/ML2mIWwapZvWbkMCQA==", + "node_modules/@mui/system/node_modules/@mui/types": { + "version": "7.2.24", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", + "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", + "license": "MIT", "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -2096,25 +1774,28 @@ } }, "node_modules/@mui/utils": { - "version": "5.14.12", - "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-5.14.12.tgz", - "integrity": "sha512-RFNXnhKQlzIkIUig6mmv0r5VbtjPdWoaBPYicq25LETdZux59HAqoRdWw15T7lp3c7gXOoE8y67+hTB8C64m2g==", + "version": "6.4.9", + "resolved": "https://registry.npmjs.org/@mui/utils/-/utils-6.4.9.tgz", + "integrity": "sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==", + "license": "MIT", "dependencies": { - "@babel/runtime": "^7.23.1", - "@types/prop-types": "^15.7.7", + "@babel/runtime": "^7.26.0", + "@mui/types": "~7.2.24", + "@types/prop-types": "^15.7.14", + "clsx": "^2.1.1", "prop-types": "^15.8.1", - "react-is": "^18.2.0" + "react-is": "^19.0.0" }, "engines": { - "node": ">=12.0.0" + "node": ">=14.0.0" }, "funding": { "type": "opencollective", - "url": "https://opencollective.com/mui" + "url": "https://opencollective.com/mui-org" }, "peerDependencies": { - "@types/react": "^17.0.0 || ^18.0.0", - "react": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0", + "react": "^17.0.0 || ^18.0.0 || ^19.0.0" }, "peerDependenciesMeta": { "@types/react": { @@ -2122,48 +1803,41 @@ } } }, - "node_modules/@mui/x-tree-view": { - "version": "6.0.0-alpha.1", - "resolved": "https://registry.npmjs.org/@mui/x-tree-view/-/x-tree-view-6.0.0-alpha.1.tgz", - "integrity": "sha512-JUG3HmBrmGEALbCFg1b+i7h726e1dWYZs4db3syO1j+Q++E3nbvE4Lehp5yGTFm+8esH0Tny50tuJaa4WX6VSA==", - "dependencies": { - "@babel/runtime": "^7.22.6", - "@mui/utils": "^5.14.3", - "@types/react-transition-group": "^4.4.6", - "clsx": "^2.0.0", - "prop-types": "^15.8.1", - "react-transition-group": "^4.4.5" - }, - "engines": { - "node": ">=14.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mui" - }, + "node_modules/@mui/utils/node_modules/@mui/types": { + "version": "7.2.24", + "resolved": "https://registry.npmjs.org/@mui/types/-/types-7.2.24.tgz", + "integrity": "sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==", + "license": "MIT", "peerDependencies": { - "@emotion/react": "^11.9.0", - "@emotion/styled": "^11.8.1", - "@mui/base": "^5.0.0-alpha.87", - "@mui/material": "^5.8.6", - "@mui/system": "^5.8.0", - "react": "^17.0.0 || ^18.0.0", - "react-dom": "^17.0.0 || ^18.0.0" + "@types/react": "^17.0.0 || ^18.0.0 || ^19.0.0" + }, + "peerDependenciesMeta": { + "@types/react": { + "optional": true + } } }, "node_modules/@popperjs/core": { "version": "2.11.8", "resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz", "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A==", + "license": "MIT", "funding": { "type": "opencollective", "url": "https://opencollective.com/popperjs" } }, + "node_modules/@rolldown/pluginutils": { + "version": "1.0.0-beta.27", + "resolved": "https://registry.npmjs.org/@rolldown/pluginutils/-/pluginutils-1.0.0-beta.27.tgz", + "integrity": "sha512-+d0F4MKMCbeVUJwG96uQ4SgAznZNSq93I3V+9NHA4OpvqG8mRCpGdKmK8l/dl02h2CCDHwW2FqilnTyDcAnqjA==", + "dev": true, + "license": "MIT" + }, "node_modules/@rollup/rollup-android-arm-eabi": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.35.0.tgz", - "integrity": "sha512-uYQ2WfPaqz5QtVgMxfN6NpLD+no0MYHDBywl7itPYd3K5TjjSghNKmX8ic9S8NU8w81NVhJv/XojcHptRly7qQ==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.57.0.tgz", + "integrity": "sha512-tPgXB6cDTndIe1ah7u6amCI1T0SsnlOuKgg10Xh3uizJk4e5M1JGaUMk7J4ciuAUcFpbOiNhm2XIjP9ON0dUqA==", "cpu": [ "arm" ], @@ -2175,9 +1849,9 @@ ] }, "node_modules/@rollup/rollup-android-arm64": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.35.0.tgz", - "integrity": "sha512-FtKddj9XZudurLhdJnBl9fl6BwCJ3ky8riCXjEw3/UIbjmIY58ppWwPEvU3fNu+W7FUsAsB1CdH+7EQE6CXAPA==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm64/-/rollup-android-arm64-4.57.0.tgz", + "integrity": "sha512-sa4LyseLLXr1onr97StkU1Nb7fWcg6niokTwEVNOO7awaKaoRObQ54+V/hrF/BP1noMEaaAW6Fg2d/CfLiq3Mg==", "cpu": [ "arm64" ], @@ -2189,9 +1863,9 @@ ] }, "node_modules/@rollup/rollup-darwin-arm64": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.35.0.tgz", - "integrity": "sha512-Uk+GjOJR6CY844/q6r5DR/6lkPFOw0hjfOIzVx22THJXMxktXG6CbejseJFznU8vHcEBLpiXKY3/6xc+cBm65Q==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-arm64/-/rollup-darwin-arm64-4.57.0.tgz", + "integrity": "sha512-/NNIj9A7yLjKdmkx5dC2XQ9DmjIECpGpwHoGmA5E1AhU0fuICSqSWScPhN1yLCkEdkCwJIDu2xIeLPs60MNIVg==", "cpu": [ "arm64" ], @@ -2203,9 +1877,9 @@ ] }, "node_modules/@rollup/rollup-darwin-x64": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.35.0.tgz", - "integrity": "sha512-3IrHjfAS6Vkp+5bISNQnPogRAW5GAV1n+bNCrDwXmfMHbPl5EhTmWtfmwlJxFRUCBZ+tZ/OxDyU08aF6NI/N5Q==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-darwin-x64/-/rollup-darwin-x64-4.57.0.tgz", + "integrity": "sha512-xoh8abqgPrPYPr7pTYipqnUi1V3em56JzE/HgDgitTqZBZ3yKCWI+7KUkceM6tNweyUKYru1UMi7FC060RyKwA==", "cpu": [ "x64" ], @@ -2217,9 +1891,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-arm64": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.35.0.tgz", - "integrity": "sha512-sxjoD/6F9cDLSELuLNnY0fOrM9WA0KrM0vWm57XhrIMf5FGiN8D0l7fn+bpUeBSU7dCgPV2oX4zHAsAXyHFGcQ==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-arm64/-/rollup-freebsd-arm64-4.57.0.tgz", + "integrity": "sha512-PCkMh7fNahWSbA0OTUQ2OpYHpjZZr0hPr8lId8twD7a7SeWrvT3xJVyza+dQwXSSq4yEQTMoXgNOfMCsn8584g==", "cpu": [ "arm64" ], @@ -2231,9 +1905,9 @@ ] }, "node_modules/@rollup/rollup-freebsd-x64": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.35.0.tgz", - "integrity": "sha512-2mpHCeRuD1u/2kruUiHSsnjWtHjqVbzhBkNVQ1aVD63CcexKVcQGwJ2g5VphOd84GvxfSvnnlEyBtQCE5hxVVw==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-freebsd-x64/-/rollup-freebsd-x64-4.57.0.tgz", + "integrity": "sha512-1j3stGx+qbhXql4OCDZhnK7b01s6rBKNybfsX+TNrEe9JNq4DLi1yGiR1xW+nL+FNVvI4D02PUnl6gJ/2y6WJA==", "cpu": [ "x64" ], @@ -2245,9 +1919,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-gnueabihf": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.35.0.tgz", - "integrity": "sha512-mrA0v3QMy6ZSvEuLs0dMxcO2LnaCONs1Z73GUDBHWbY8tFFocM6yl7YyMu7rz4zS81NDSqhrUuolyZXGi8TEqg==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-gnueabihf/-/rollup-linux-arm-gnueabihf-4.57.0.tgz", + "integrity": "sha512-eyrr5W08Ms9uM0mLcKfM/Uzx7hjhz2bcjv8P2uynfj0yU8GGPdz8iYrBPhiLOZqahoAMB8ZiolRZPbbU2MAi6Q==", "cpu": [ "arm" ], @@ -2259,9 +1933,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm-musleabihf": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.35.0.tgz", - "integrity": "sha512-DnYhhzcvTAKNexIql8pFajr0PiDGrIsBYPRvCKlA5ixSS3uwo/CWNZxB09jhIapEIg945KOzcYEAGGSmTSpk7A==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm-musleabihf/-/rollup-linux-arm-musleabihf-4.57.0.tgz", + "integrity": "sha512-Xds90ITXJCNyX9pDhqf85MKWUI4lqjiPAipJ8OLp8xqI2Ehk+TCVhF9rvOoN8xTbcafow3QOThkNnrM33uCFQA==", "cpu": [ "arm" ], @@ -2273,9 +1947,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-gnu": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.35.0.tgz", - "integrity": "sha512-uagpnH2M2g2b5iLsCTZ35CL1FgyuzzJQ8L9VtlJ+FckBXroTwNOaD0z0/UF+k5K3aNQjbm8LIVpxykUOQt1m/A==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-gnu/-/rollup-linux-arm64-gnu-4.57.0.tgz", + "integrity": "sha512-Xws2KA4CLvZmXjy46SQaXSejuKPhwVdaNinldoYfqruZBaJHqVo6hnRa8SDo9z7PBW5x84SH64+izmldCgbezw==", "cpu": [ "arm64" ], @@ -2287,9 +1961,9 @@ ] }, "node_modules/@rollup/rollup-linux-arm64-musl": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.35.0.tgz", - "integrity": "sha512-XQxVOCd6VJeHQA/7YcqyV0/88N6ysSVzRjJ9I9UA/xXpEsjvAgDTgH3wQYz5bmr7SPtVK2TsP2fQ2N9L4ukoUg==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-arm64-musl/-/rollup-linux-arm64-musl-4.57.0.tgz", + "integrity": "sha512-hrKXKbX5FdaRJj7lTMusmvKbhMJSGWJ+w++4KmjiDhpTgNlhYobMvKfDoIWecy4O60K6yA4SnztGuNTQF+Lplw==", "cpu": [ "arm64" ], @@ -2300,10 +1974,24 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-loongarch64-gnu": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loongarch64-gnu/-/rollup-linux-loongarch64-gnu-4.35.0.tgz", - "integrity": "sha512-5pMT5PzfgwcXEwOaSrqVsz/LvjDZt+vQ8RT/70yhPU06PTuq8WaHhfT1LW+cdD7mW6i/J5/XIkX/1tCAkh1W6g==", + "node_modules/@rollup/rollup-linux-loong64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-gnu/-/rollup-linux-loong64-gnu-4.57.0.tgz", + "integrity": "sha512-6A+nccfSDGKsPm00d3xKcrsBcbqzCTAukjwWK6rbuAnB2bHaL3r9720HBVZ/no7+FhZLz/U3GwwZZEh6tOSI8Q==", + "cpu": [ + "loong64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-loong64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-loong64-musl/-/rollup-linux-loong64-musl-4.57.0.tgz", + "integrity": "sha512-4P1VyYUe6XAJtQH1Hh99THxr0GKMMwIXsRNOceLrJnaHTDgk1FTcTimDgneRJPvB3LqDQxUmroBclQ1S0cIJwQ==", "cpu": [ "loong64" ], @@ -2314,10 +2002,24 @@ "linux" ] }, - "node_modules/@rollup/rollup-linux-powerpc64le-gnu": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-powerpc64le-gnu/-/rollup-linux-powerpc64le-gnu-4.35.0.tgz", - "integrity": "sha512-c+zkcvbhbXF98f4CtEIP1EBA/lCic5xB0lToneZYvMeKu5Kamq3O8gqrxiYYLzlZH6E3Aq+TSW86E4ay8iD8EA==", + "node_modules/@rollup/rollup-linux-ppc64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-gnu/-/rollup-linux-ppc64-gnu-4.57.0.tgz", + "integrity": "sha512-8Vv6pLuIZCMcgXre6c3nOPhE0gjz1+nZP6T+hwWjr7sVH8k0jRkH+XnfjjOTglyMBdSKBPPz54/y1gToSKwrSQ==", + "cpu": [ + "ppc64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-ppc64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-ppc64-musl/-/rollup-linux-ppc64-musl-4.57.0.tgz", + "integrity": "sha512-r1te1M0Sm2TBVD/RxBPC6RZVwNqUTwJTA7w+C/IW5v9Ssu6xmxWEi+iJQlpBhtUiT1raJ5b48pI8tBvEjEFnFA==", "cpu": [ "ppc64" ], @@ -2329,9 +2031,23 @@ ] }, "node_modules/@rollup/rollup-linux-riscv64-gnu": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.35.0.tgz", - "integrity": "sha512-s91fuAHdOwH/Tad2tzTtPX7UZyytHIRR6V4+2IGlV0Cej5rkG0R61SX4l4y9sh0JBibMiploZx3oHKPnQBKe4g==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-gnu/-/rollup-linux-riscv64-gnu-4.57.0.tgz", + "integrity": "sha512-say0uMU/RaPm3CDQLxUUTF2oNWL8ysvHkAjcCzV2znxBr23kFfaxocS9qJm+NdkRhF8wtdEEAJuYcLPhSPbjuQ==", + "cpu": [ + "riscv64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@rollup/rollup-linux-riscv64-musl": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-riscv64-musl/-/rollup-linux-riscv64-musl-4.57.0.tgz", + "integrity": "sha512-/MU7/HizQGsnBREtRpcSbSV1zfkoxSTR7wLsRmBPQ8FwUj5sykrP1MyJTvsxP5KBq9SyE6kH8UQQQwa0ASeoQQ==", "cpu": [ "riscv64" ], @@ -2343,9 +2059,9 @@ ] }, "node_modules/@rollup/rollup-linux-s390x-gnu": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.35.0.tgz", - "integrity": "sha512-hQRkPQPLYJZYGP+Hj4fR9dDBMIM7zrzJDWFEMPdTnTy95Ljnv0/4w/ixFw3pTBMEuuEuoqtBINYND4M7ujcuQw==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-s390x-gnu/-/rollup-linux-s390x-gnu-4.57.0.tgz", + "integrity": "sha512-Q9eh+gUGILIHEaJf66aF6a414jQbDnn29zeu0eX3dHMuysnhTvsUvZTCAyZ6tJhUjnvzBKE4FtuaYxutxRZpOg==", "cpu": [ "s390x" ], @@ -2357,9 +2073,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-gnu": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.35.0.tgz", - "integrity": "sha512-Pim1T8rXOri+0HmV4CdKSGrqcBWX0d1HoPnQ0uw0bdp1aP5SdQVNBy8LjYncvnLgu3fnnCt17xjWGd4cqh8/hA==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-gnu/-/rollup-linux-x64-gnu-4.57.0.tgz", + "integrity": "sha512-OR5p5yG5OKSxHReWmwvM0P+VTPMwoBS45PXTMYaskKQqybkS3Kmugq1W+YbNWArF8/s7jQScgzXUhArzEQ7x0A==", "cpu": [ "x64" ], @@ -2371,9 +2087,9 @@ ] }, "node_modules/@rollup/rollup-linux-x64-musl": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.35.0.tgz", - "integrity": "sha512-QysqXzYiDvQWfUiTm8XmJNO2zm9yC9P/2Gkrwg2dH9cxotQzunBHYr6jk4SujCTqnfGxduOmQcI7c2ryuW8XVg==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-linux-x64-musl/-/rollup-linux-x64-musl-4.57.0.tgz", + "integrity": "sha512-XeatKzo4lHDsVEbm1XDHZlhYZZSQYym6dg2X/Ko0kSFgio+KXLsxwJQprnR48GvdIKDOpqWqssC3iBCjoMcMpw==", "cpu": [ "x64" ], @@ -2384,10 +2100,38 @@ "linux" ] }, + "node_modules/@rollup/rollup-openbsd-x64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openbsd-x64/-/rollup-openbsd-x64-4.57.0.tgz", + "integrity": "sha512-Lu71y78F5qOfYmubYLHPcJm74GZLU6UJ4THkf/a1K7Tz2ycwC2VUbsqbJAXaR6Bx70SRdlVrt2+n5l7F0agTUw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ] + }, + "node_modules/@rollup/rollup-openharmony-arm64": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-openharmony-arm64/-/rollup-openharmony-arm64-4.57.0.tgz", + "integrity": "sha512-v5xwKDWcu7qhAEcsUubiav7r+48Uk/ENWdr82MBZZRIm7zThSxCIVDfb3ZeRRq9yqk+oIzMdDo6fCcA5DHfMyA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ] + }, "node_modules/@rollup/rollup-win32-arm64-msvc": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.35.0.tgz", - "integrity": "sha512-OUOlGqPkVJCdJETKOCEf1mw848ZyJ5w50/rZ/3IBQVdLfR5jk/6Sr5m3iO2tdPgwo0x7VcncYuOvMhBWZq8ayg==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-arm64-msvc/-/rollup-win32-arm64-msvc-4.57.0.tgz", + "integrity": "sha512-XnaaaSMGSI6Wk8F4KK3QP7GfuuhjGchElsVerCplUuxRIzdvZ7hRBpLR0omCmw+kI2RFJB80nenhOoGXlJ5TfQ==", "cpu": [ "arm64" ], @@ -2399,9 +2143,9 @@ ] }, "node_modules/@rollup/rollup-win32-ia32-msvc": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.35.0.tgz", - "integrity": "sha512-2/lsgejMrtwQe44glq7AFFHLfJBPafpsTa6JvP2NGef/ifOa4KBoglVf7AKN7EV9o32evBPRqfg96fEHzWo5kw==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-ia32-msvc/-/rollup-win32-ia32-msvc-4.57.0.tgz", + "integrity": "sha512-3K1lP+3BXY4t4VihLw5MEg6IZD3ojSYzqzBG571W3kNQe4G4CcFpSUQVgurYgib5d+YaCjeFow8QivWp8vuSvA==", "cpu": [ "ia32" ], @@ -2412,10 +2156,24 @@ "win32" ] }, + "node_modules/@rollup/rollup-win32-x64-gnu": { + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-gnu/-/rollup-win32-x64-gnu-4.57.0.tgz", + "integrity": "sha512-MDk610P/vJGc5L5ImE4k5s+GZT3en0KoK1MKPXCRgzmksAMk79j4h3k1IerxTNqwDLxsGxStEZVBqG0gIqZqoA==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "win32" + ] + }, "node_modules/@rollup/rollup-win32-x64-msvc": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.35.0.tgz", - "integrity": "sha512-PIQeY5XDkrOysbQblSW7v3l1MDZzkTEzAfTPkj5VAu3FW8fS4ynyLg2sINp0fp3SjZ8xkRYpLqoKcYqAkhU1dw==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/@rollup/rollup-win32-x64-msvc/-/rollup-win32-x64-msvc-4.57.0.tgz", + "integrity": "sha512-Zv7v6q6aV+VslnpwzqKAmrk5JdVkLUzok2208ZXGipjb+msxBr/fJPZyeEXiFgH7k62Ak0SLIfxQRZQvTuf7rQ==", "cpu": [ "x64" ], @@ -2430,13 +2188,15 @@ "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@sinonjs/commons": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.0.tgz", - "integrity": "sha512-jXBtWAF4vmdNmZgD5FoKsVLv3rPgDnLgPbU84LIJ3otV44vJlDRokVng5v8NFJdCf/da9legHcKaRuZs4L7faA==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "type-detect": "4.0.8" } @@ -2446,6 +2206,7 @@ "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@sinonjs/commons": "^3.0.0" } @@ -2465,173 +2226,185 @@ } }, "node_modules/@types/babel__generator": { - "version": "7.6.5", - "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.6.5.tgz", - "integrity": "sha512-h9yIuWbJKdOPLJTbmSpPzkF67e659PbQDba7ifWm5BJ8xTv+sDmS7rFmywkWOvXedGTivCdeGSIIX8WLcRTz8w==", + "version": "7.27.0", + "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", + "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { - "version": "7.4.2", - "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.2.tgz", - "integrity": "sha512-/AVzPICMhMOMYoSx9MoKpGDKdBRsIXMNByh1PXSZoa+v6ZoLa8xxtsT/uLQ/NJm0XVAWl/BvId4MlDeXJaeIZQ==", + "version": "7.4.4", + "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", + "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, + "license": "MIT", "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__traverse": { - "version": "7.20.2", - "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.2.tgz", - "integrity": "sha512-ojlGK1Hsfce93J0+kn3H5R73elidKUaZonirN33GSmgTUMpzI/MIFfSpF3haANe3G1bEBS9/9/QEqwTzwqFsKw==", + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.28.0.tgz", + "integrity": "sha512-8PvcXf70gTDZBgt9ptxJ8elBeBjcLOAcOtoO/mPJjtji1+CdGbHgm77om1GrsPxsiE+uXIpNSK64UYaIwQXd4Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.20.7" + "@babel/types": "^7.28.2" } }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", "dev": true, "license": "MIT" }, "node_modules/@types/graceful-fs": { - "version": "4.1.7", - "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.7.tgz", - "integrity": "sha512-MhzcwU8aUygZroVwL2jeYk6JisJrPl/oov/gsgGCue9mkgl9wjGbzReYQClxiUgFDnib9FuHqTndccKeZKxTRw==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", + "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/istanbul-lib-coverage": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.4.tgz", - "integrity": "sha512-z/QT1XN4K4KYuslS23k62yDIDLwLFkzxOuMplDtObz0+y7VqJCaO2o+SPwHCvLFZh7xazvvoor2tA/hPz9ee7g==", - "dev": true + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" }, "node_modules/@types/istanbul-lib-report": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.0.tgz", - "integrity": "sha512-plGgXAPfVKFoYfa9NpYDAkseG+g6Jr294RqeqcqDixSbU34MZVJRi/P+7Y8GDpzkEwLaGZZOpKIEmeVZNtKsrg==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", + "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.1.tgz", - "integrity": "sha512-c3mAZEuK0lvBp8tmuL74XRKn1+y2dcwOUpH7x4WrF6gk1GIgiluDRgMYQtw2OFcBvAJWlt6ASU3tSqxp0Uu0Aw==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", + "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/istanbul-lib-report": "*" } }, "node_modules/@types/jest": { - "version": "29.5.5", - "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.5.tgz", - "integrity": "sha512-ebylz2hnsWR9mYvmBFbXJXr+33UPc4+ZdxyDXh5w0FlPBTfCVN3wPL+kuOiQt3xvrK419v7XWeAs+AeOksafXg==", + "version": "29.5.14", + "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", + "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, + "license": "MIT", "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" } }, "node_modules/@types/node": { - "version": "20.8.4", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.8.4.tgz", - "integrity": "sha512-ZVPnqU58giiCjSxjVUESDtdPk4QR5WQhhINbc9UBrKLU68MX5BF6kbQzTrkwbolyr0X8ChBpXfavr5mZFKZQ5A==", + "version": "20.19.30", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.30.tgz", + "integrity": "sha512-WJtwWJu7UdlvzEAUm484QNg5eAoq5QR08KDNx7g45Usrs2NtOPiX8ugDqmKdXkyL03rBqU5dYNYVQetEpBHq2g==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.25.1" + "undici-types": "~6.21.0" } }, "node_modules/@types/parse-json": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz", - "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==" + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.2.tgz", + "integrity": "sha512-dISoDXWWQwUquiKsyZ4Ng+HX2KsPL7LyHKHQwgGFEA3IaKac4Obd+h2a/a6waisAoepJlBcx9paWqjA8/HVjCw==", + "license": "MIT" }, "node_modules/@types/prop-types": { - "version": "15.7.8", - "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.8.tgz", - "integrity": "sha512-kMpQpfZKSCBqltAJwskgePRaYRFukDkm1oItcAbC3gNELR20XIBcN9VRgg4+m8DKsTfkWeA4m4Imp4DDuWy7FQ==" + "version": "15.7.15", + "resolved": "https://registry.npmjs.org/@types/prop-types/-/prop-types-15.7.15.tgz", + "integrity": "sha512-F6bEyamV9jKGAFBEmlQnesRPGOQqS2+Uwi0Em15xenOxHaf2hv6L8YCVn3rPdPJOiJfPiCnLIRyvwVaqMY3MIw==", + "license": "MIT" }, "node_modules/@types/react": { - "version": "18.2.27", - "resolved": "https://registry.npmjs.org/@types/react/-/react-18.2.27.tgz", - "integrity": "sha512-Wfv7B7FZiR2r3MIqbAlXoY1+tXm4bOqfz4oRr+nyXdBqapDBZ0l/IGcSlAfvxIHEEJjkPU0MYAc/BlFPOcrgLw==", + "version": "18.3.27", + "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.27.tgz", + "integrity": "sha512-cisd7gxkzjBKU2GgdYrTdtQx1SORymWyaAFhaxQPK9bYO9ot3Y5OikQRvY0VYQtvwjeQnizCINJAenh/V7MK2w==", + "license": "MIT", "dependencies": { "@types/prop-types": "*", - "@types/scheduler": "*", - "csstype": "^3.0.2" + "csstype": "^3.2.2" } }, "node_modules/@types/react-dom": { - "version": "18.2.12", - "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.12.tgz", - "integrity": "sha512-QWZuiA/7J/hPIGocXreCRbx7wyoeet9ooxfbSA+zbIWqyQEE7GMtRn4A37BdYyksnN+/NDnWgfxZH9UVGDw1hg==", + "version": "18.3.7", + "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.3.7.tgz", + "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "dev": true, - "dependencies": { - "@types/react": "*" + "license": "MIT", + "peerDependencies": { + "@types/react": "^18.0.0" } }, "node_modules/@types/react-transition-group": { - "version": "4.4.6", - "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.6.tgz", - "integrity": "sha512-VnCdSxfcm08KjsJVQcfBmhEQAPnLB8G08hAxn39azX1qYBQ/5RVQuoHuKIcfKOdncuaUvEpFKFzEvbtIMsfVew==", - "dependencies": { + "version": "4.4.12", + "resolved": "https://registry.npmjs.org/@types/react-transition-group/-/react-transition-group-4.4.12.tgz", + "integrity": "sha512-8TV6R3h2j7a91c+1DXdJi3Syo69zzIZbz7Lg5tORM5LEJG7X/E6a1V3drRyBRZq7/utz7A+c4OgYLiLcYGHG6w==", + "license": "MIT", + "peerDependencies": { "@types/react": "*" } }, - "node_modules/@types/scheduler": { - "version": "0.16.3", - "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", - "integrity": "sha512-5cJ8CB4yAx7BH1oMvdU0Jh9lrEXyPkar6F9G/ERswkCuvP4KQZfZkSjcMbAICCpQTN4OuZn8tz0HiKv9TGZgrQ==" - }, "node_modules/@types/stack-utils": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", - "integrity": "sha512-Hl219/BT5fLAaz6NDkSuhzasy49dwQS/DSdu4MdggFB8zcXv7vflBI3xp7FEmkmdDkBUI2bPUNeMttp2knYdxw==", - "dev": true + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", + "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/yargs": { - "version": "17.0.24", - "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.24.tgz", - "integrity": "sha512-6i0aC7jV6QzQB8ne1joVZ0eSFIstHsCrobmOtghM11yGlH0j43FKL2UhWdELkyps0zuf7qVTUVCCR+tgSlyLLw==", + "version": "17.0.35", + "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.35.tgz", + "integrity": "sha512-qUHkeCyQFxMXg79wQfTtfndEC+N9ZZg76HJftDJp+qH2tV7Gj4OJi7l+PiWwJ+pWtW8GwSmqsDj/oymhrTWXjg==", "dev": true, + "license": "MIT", "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { - "version": "21.0.0", - "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.0.tgz", - "integrity": "sha512-iO9ZQHkZxHn4mSakYV0vFHAVDyEOIJQrV2uZ06HxEPcx+mt8swXoZHIbaaJ2crJYFfErySgktuTZ3BeLz+XmFA==", - "dev": true + "version": "21.0.3", + "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", + "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", + "dev": true, + "license": "MIT" }, "node_modules/@vitejs/plugin-react": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.3.4.tgz", - "integrity": "sha512-SCCPBJtYLdE8PX/7ZQAs1QAZ8Jqwih+0VBLum1EGqmCCQal+MIUqLCzj3ZUy8ufbC0cAM4LRlSTm7IQJwWT4ug==", + "version": "4.7.0", + "resolved": "https://registry.npmjs.org/@vitejs/plugin-react/-/plugin-react-4.7.0.tgz", + "integrity": "sha512-gUu9hwfWvvEDBBmgtAowQCojwZmJ5mcLn3aufeCsitijs3+f2NsrPtlAWIR6OPiqljl96GVCUbLe0HyqIpVaoA==", "dev": true, "license": "MIT", "dependencies": { - "@babel/core": "^7.26.0", - "@babel/plugin-transform-react-jsx-self": "^7.25.9", - "@babel/plugin-transform-react-jsx-source": "^7.25.9", + "@babel/core": "^7.28.0", + "@babel/plugin-transform-react-jsx-self": "^7.27.1", + "@babel/plugin-transform-react-jsx-source": "^7.27.1", + "@rolldown/pluginutils": "1.0.0-beta.27", "@types/babel__core": "^7.20.5", - "react-refresh": "^0.14.2" + "react-refresh": "^0.17.0" }, "engines": { "node": "^14.18.0 || >=16.0.0" }, "peerDependencies": { - "vite": "^4.2.0 || ^5.0.0 || ^6.0.0" + "vite": "^4.2.0 || ^5.0.0 || ^6.0.0 || ^7.0.0" } }, "node_modules/ansi-escapes": { @@ -2639,6 +2412,7 @@ "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.21.3" }, @@ -2654,8 +2428,25 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/anymatch": { @@ -2663,6 +2454,7 @@ "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -2671,11 +2463,25 @@ "node": ">= 8" } }, + "node_modules/anymatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, + "license": "MIT", "dependencies": { "sprintf-js": "~1.0.2" } @@ -2685,6 +2491,7 @@ "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, + "license": "MIT", "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", @@ -2701,81 +2508,12 @@ "@babel/core": "^7.8.0" } }, - "node_modules/babel-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/babel-jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/babel-jest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/babel-jest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/babel-jest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", @@ -2792,6 +2530,7 @@ "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", @@ -2808,6 +2547,7 @@ "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", @@ -2822,6 +2562,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/babel-plugin-macros/-/babel-plugin-macros-3.1.0.tgz", "integrity": "sha512-Cg7TFGpIr01vOQNODXOOaGz2NpCU5gl8x1qJFbb6hbZxR7XrcE2vtbAsTAbJ7/xwJtUuJEw8K8Zr/AE0LHlesg==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.12.5", "cosmiconfig": "^7.0.0", @@ -2833,26 +2574,30 @@ } }, "node_modules/babel-preset-current-node-syntax": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.0.1.tgz", - "integrity": "sha512-M7LQ0bxarkxQoN+vz5aJPsLBn77n8QgTFmo8WK0/44auK2xlCXrYcUxHFxgU7qW5Yzw/CjmLRK2uJzaCd7LvqQ==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.2.0.tgz", + "integrity": "sha512-E/VlAEzRrsLEb2+dv8yp3bo4scof3l9nR4lrld+Iy5NyVqgVYUJnDAmunkhPMisRI32Qc4iRiz425d8vM++2fg==", "dev": true, + "license": "MIT", "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", - "@babel/plugin-syntax-class-properties": "^7.8.3", - "@babel/plugin-syntax-import-meta": "^7.8.3", + "@babel/plugin-syntax-class-properties": "^7.12.13", + "@babel/plugin-syntax-class-static-block": "^7.14.5", + "@babel/plugin-syntax-import-attributes": "^7.24.7", + "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", - "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3", + "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", - "@babel/plugin-syntax-numeric-separator": "^7.8.3", + "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", - "@babel/plugin-syntax-top-level-await": "^7.8.3" + "@babel/plugin-syntax-private-property-in-object": "^7.14.5", + "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { - "@babel/core": "^7.0.0" + "@babel/core": "^7.0.0 || ^8.0.0-0" } }, "node_modules/babel-preset-jest": { @@ -2860,6 +2605,7 @@ "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, + "license": "MIT", "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" @@ -2875,7 +2621,18 @@ "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true + "dev": true, + "license": "MIT" + }, + "node_modules/baseline-browser-mapping": { + "version": "2.9.18", + "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.9.18.tgz", + "integrity": "sha512-e23vBV1ZLfjb9apvfPk4rHVu2ry6RIr2Wfs+O324okSidrX7pTAnEJPCh/O5BtRlr7QtZI7ktOP3vsqr7Z5XoA==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "baseline-browser-mapping": "dist/cli.js" + } }, "node_modules/brace-expansion": { "version": "1.1.12", @@ -2893,6 +2650,7 @@ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, + "license": "MIT", "dependencies": { "fill-range": "^7.1.1" }, @@ -2901,9 +2659,9 @@ } }, "node_modules/browserslist": { - "version": "4.24.4", - "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.4.tgz", - "integrity": "sha512-KDi1Ny1gSePi1vm0q4oxSF8b4DR44GF4BbmS2YdhPLOEqd8pDviZOGH/GsmRwoWJ2+5Lr085X7naowMwKHDG1A==", + "version": "4.28.1", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.1.tgz", + "integrity": "sha512-ZC5Bd0LgJXgwGqUknZY/vkUQ04r8NXnJZ3yYi4vDmSiZmC/pdSN0NbNRPxZpbtO4uAfDUAFffO8IZoM3Gj8IkA==", "dev": true, "funding": [ { @@ -2921,10 +2679,11 @@ ], "license": "MIT", "dependencies": { - "caniuse-lite": "^1.0.30001688", - "electron-to-chromium": "^1.5.73", - "node-releases": "^2.0.19", - "update-browserslist-db": "^1.1.1" + "baseline-browser-mapping": "^2.9.0", + "caniuse-lite": "^1.0.30001759", + "electron-to-chromium": "^1.5.263", + "node-releases": "^2.0.27", + "update-browserslist-db": "^1.2.0" }, "bin": { "browserslist": "cli.js" @@ -2938,6 +2697,7 @@ "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "node-int64": "^0.4.0" } @@ -2946,12 +2706,14 @@ "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", + "license": "MIT", "engines": { "node": ">=6" } @@ -2961,14 +2723,15 @@ "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/caniuse-lite": { - "version": "1.0.30001703", - "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001703.tgz", - "integrity": "sha512-kRlAGTRWgPsOj7oARC9m1okJEXdL/8fekFVcxA8Hl7GH4r/sN4OJn/i6Flde373T50KS7Y37oFbMwlE8+F42kQ==", + "version": "1.0.30001766", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001766.tgz", + "integrity": "sha512-4C0lfJ0/YPjJQHagaE9x2Elb69CIqEPZeG0anQt9SIvIoOH4a4uaRl73IavyO+0qZh6MDLH//DrXThEYKHkmYA==", "dev": true, "funding": [ { @@ -2986,19 +2749,37 @@ ], "license": "CC-BY-4.0" }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, "node_modules/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/ci-info": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.8.0.tgz", - "integrity": "sha512-eXTggHWSooYhq49F2opQhuHWgzucfF2YgODK4e1566GQs5BIfP30B0oenwBJHfWxAs2fyPB1s7Mg949zLf61Yw==", + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", + "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { @@ -3006,21 +2787,24 @@ "url": "https://github.com/sponsors/sibiraj-s" } ], + "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/cjs-module-lexer": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.2.3.tgz", - "integrity": "sha512-0TNiGstbQmCFwt4akjjBg5pLRTSyj/PkWQ1ZoO2zntmg9yLqSRxwEa4iCfQLGjqhiqBfOJa7W/E8wfGrTDmlZQ==", - "dev": true + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", + "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", + "dev": true, + "license": "MIT" }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", @@ -3031,9 +2815,10 @@ } }, "node_modules/clsx": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.0.0.tgz", - "integrity": "sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/clsx/-/clsx-2.1.1.tgz", + "integrity": "sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA==", + "license": "MIT", "engines": { "node": ">=6" } @@ -3043,32 +2828,57 @@ "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, + "license": "MIT", "engines": { "iojs": ">= 1.0.0", "node": ">= 0.12.0" } }, "node_modules/collect-v8-coverage": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", - "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", - "dev": true + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.3.tgz", + "integrity": "sha512-1L5aqIkwPfiodaMgQunkF1zRhNqifHBmtbbbxcr6yVxxBnliw4TDOW6NxpO8DJLgJ16OT+Y4ztZqP6p/FtXnAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/convert-source-map": { "version": "1.9.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.9.0.tgz", - "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==" + "integrity": "sha512-ASFBup0Mz1uyiIjANan1jzLQami9z1PoYSZCiiYW2FczPbenXc45FZdBZLzOT+r6+iciuEModtmCti+hjaAk0A==", + "license": "MIT" }, "node_modules/cosmiconfig": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "license": "MIT", "dependencies": { "@types/parse-json": "^4.0.0", "import-fresh": "^3.2.1", @@ -3094,6 +2904,7 @@ "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", "dev": true, + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", @@ -3110,76 +2921,6 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/create-jest/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/create-jest/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/create-jest/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/create-jest/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/create-jest/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/create-jest/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", @@ -3196,16 +2937,18 @@ } }, "node_modules/csstype": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.2.tgz", - "integrity": "sha512-I7K1Uu0MBPzaFKg4nI5Q7Vs2t+3gWWW648spaF+Rg7pI9ds18Ugn+lvg4SHczUdKlHI5LWBXyqfS8+DufyBsgQ==" + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.2.3.tgz", + "integrity": "sha512-z1HGKcYy2xA8AGQfwrn0PAy+PB7X/GSj3UVJW9qKyn43xWa+gl5nXmU4qqLMRzWVLFC8KusUX8T/0kCiOYpAIQ==", + "license": "MIT" }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -3217,10 +2960,11 @@ } }, "node_modules/dedent": { - "version": "1.5.1", - "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.5.1.tgz", - "integrity": "sha512-+LxW+KLWxu3HW3M2w2ympwtqPrqYRzU8fqi6Fhd18fBALe15blJPI/I4+UHveMVG6lJqB4JNd4UG0S5cnVHwIg==", + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.7.1.tgz", + "integrity": "sha512-9JmrhGZpOlEgOLdQgSm0zxFaYoQon408V1v49aqTWuXENVlnCuY9JBZcXZiCsZQWDjTm5Qf/nIvAy77mXDAjEg==", "dev": true, + "license": "MIT", "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, @@ -3235,6 +2979,7 @@ "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3244,6 +2989,7 @@ "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -3253,6 +2999,7 @@ "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, + "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } @@ -3261,15 +3008,16 @@ "version": "5.2.1", "resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz", "integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==", + "license": "MIT", "dependencies": { "@babel/runtime": "^7.8.7", "csstype": "^3.0.2" } }, "node_modules/electron-to-chromium": { - "version": "1.5.114", - "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.114.tgz", - "integrity": "sha512-DFptFef3iktoKlFQK/afbo274/XNWD00Am0xa7M8FZUepHlHT8PEuiNBoRfFHbH1okqN58AlhbJ4QTkcnXorjA==", + "version": "1.5.279", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.279.tgz", + "integrity": "sha512-0bblUU5UNdOt5G7XqGiJtpZMONma6WAfq9vsFmtn9x1+joAObr6x1chfqyxFSDCAFwFhCQDrqeAr6MYdpwJ9Hg==", "dev": true, "license": "ISC" }, @@ -3278,6 +3026,7 @@ "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -3289,20 +3038,22 @@ "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/error-ex": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", - "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", + "version": "1.3.4", + "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.4.tgz", + "integrity": "sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==", + "license": "MIT", "dependencies": { "is-arrayish": "^0.2.1" } }, "node_modules/esbuild": { - "version": "0.25.1", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.1.tgz", - "integrity": "sha512-BGO5LtrGC7vxnqucAe/rmvKdJllfGaYWdyABvyMoXQlfYMb2bbRuReWR5tEGE//4LcNJj9XrkovTqNYRFZHAMQ==", + "version": "0.25.12", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.12.tgz", + "integrity": "sha512-bbPBYYrtZbkt6Os6FiTLCTFxvq4tt3JKall1vRwshA3fdVztsLAatFaZobhkBC8/BrPetoa0oksYoKXoG4ryJg==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -3313,31 +3064,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.1", - "@esbuild/android-arm": "0.25.1", - "@esbuild/android-arm64": "0.25.1", - "@esbuild/android-x64": "0.25.1", - "@esbuild/darwin-arm64": "0.25.1", - "@esbuild/darwin-x64": "0.25.1", - "@esbuild/freebsd-arm64": "0.25.1", - "@esbuild/freebsd-x64": "0.25.1", - "@esbuild/linux-arm": "0.25.1", - "@esbuild/linux-arm64": "0.25.1", - "@esbuild/linux-ia32": "0.25.1", - "@esbuild/linux-loong64": "0.25.1", - "@esbuild/linux-mips64el": "0.25.1", - "@esbuild/linux-ppc64": "0.25.1", - "@esbuild/linux-riscv64": "0.25.1", - "@esbuild/linux-s390x": "0.25.1", - "@esbuild/linux-x64": "0.25.1", - "@esbuild/netbsd-arm64": "0.25.1", - "@esbuild/netbsd-x64": "0.25.1", - "@esbuild/openbsd-arm64": "0.25.1", - "@esbuild/openbsd-x64": "0.25.1", - "@esbuild/sunos-x64": "0.25.1", - "@esbuild/win32-arm64": "0.25.1", - "@esbuild/win32-ia32": "0.25.1", - "@esbuild/win32-x64": "0.25.1" + "@esbuild/aix-ppc64": "0.25.12", + "@esbuild/android-arm": "0.25.12", + "@esbuild/android-arm64": "0.25.12", + "@esbuild/android-x64": "0.25.12", + "@esbuild/darwin-arm64": "0.25.12", + "@esbuild/darwin-x64": "0.25.12", + "@esbuild/freebsd-arm64": "0.25.12", + "@esbuild/freebsd-x64": "0.25.12", + "@esbuild/linux-arm": "0.25.12", + "@esbuild/linux-arm64": "0.25.12", + "@esbuild/linux-ia32": "0.25.12", + "@esbuild/linux-loong64": "0.25.12", + "@esbuild/linux-mips64el": "0.25.12", + "@esbuild/linux-ppc64": "0.25.12", + "@esbuild/linux-riscv64": "0.25.12", + "@esbuild/linux-s390x": "0.25.12", + "@esbuild/linux-x64": "0.25.12", + "@esbuild/netbsd-arm64": "0.25.12", + "@esbuild/netbsd-x64": "0.25.12", + "@esbuild/openbsd-arm64": "0.25.12", + "@esbuild/openbsd-x64": "0.25.12", + "@esbuild/openharmony-arm64": "0.25.12", + "@esbuild/sunos-x64": "0.25.12", + "@esbuild/win32-arm64": "0.25.12", + "@esbuild/win32-ia32": "0.25.12", + "@esbuild/win32-x64": "0.25.12" } }, "node_modules/escalade": { @@ -3354,6 +3106,7 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -3366,6 +3119,7 @@ "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, + "license": "BSD-2-Clause", "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" @@ -3379,6 +3133,7 @@ "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, + "license": "MIT", "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", @@ -3411,6 +3166,7 @@ "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, + "license": "MIT", "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", @@ -3426,22 +3182,43 @@ "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, + "license": "Apache-2.0", "dependencies": { "bser": "2.1.1" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, + "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, @@ -3452,13 +3229,15 @@ "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", - "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==" + "integrity": "sha512-NKfW6bec6GfKc0SGx1e07QZY9PE99u0Bft/0rzSD5k3sO/vwkVUpDUKVm5Gpp5Ue3YfShPFTX2070tDs5kB9Ng==", + "license": "MIT" }, "node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" @@ -3471,7 +3250,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/fsevents": { "version": "2.3.3", @@ -3479,6 +3259,7 @@ "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -3488,15 +3269,20 @@ } }, "node_modules/function-bind": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", - "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -3506,6 +3292,7 @@ "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, + "license": "ISC", "engines": { "node": "6.* || 8.* || >= 10.*" } @@ -3515,6 +3302,7 @@ "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.0.0" } @@ -3524,6 +3312,7 @@ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -3535,7 +3324,9 @@ "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, + "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -3551,36 +3342,40 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", - "license": "MIT", - "engines": { - "node": ">=4" - } - }, "node_modules/graceful-fs": { "version": "4.2.11", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", - "dev": true + "dev": true, + "license": "ISC" }, - "node_modules/has": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", - "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "license": "MIT", "dependencies": { - "function-bind": "^1.1.1" + "function-bind": "^1.1.2" }, "engines": { - "node": ">= 0.4.0" + "node": ">= 0.4" } }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", "integrity": "sha512-/gGivxi8JPKWNm/W0jSmzcMPpfpPLc3dY/6GxhX2hQ9iGj3aDfklV4ET7NjKpSinLpJ5vafa9iiGIEZg10SfBw==", + "license": "BSD-3-Clause", "dependencies": { "react-is": "^16.7.0" } @@ -3588,27 +3383,31 @@ "node_modules/hoist-non-react-statics/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=10.17.0" } }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -3621,10 +3420,11 @@ } }, "node_modules/import-local": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.1.0.tgz", - "integrity": "sha512-ASB07uLtnDs1o6EHjKpX34BKYDSqnFerfTOJL2HvMqF70LnxpjkzDB8J44oT9pu4AMPkQwf8jl6szgvNd2tRIg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", + "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, + "license": "MIT", "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" @@ -3644,6 +3444,7 @@ "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.19" } @@ -3652,7 +3453,9 @@ "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, + "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" @@ -3662,19 +3465,25 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", - "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==" + "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", + "license": "MIT" }, "node_modules/is-core-module": { - "version": "2.13.0", - "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.13.0.tgz", - "integrity": "sha512-Z7dk6Qo8pOCp3l4tsX2C5ZVas4V+UxwQodwZhLopL91TX8UyyHEXafPcyoeeWuLrwzHcr3igO78wNLwHJHsMCQ==", + "version": "2.16.1", + "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", + "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", + "license": "MIT", "dependencies": { - "has": "^1.0.3" + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -3685,6 +3494,7 @@ "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -3694,6 +3504,7 @@ "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -3703,6 +3514,7 @@ "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.12.0" } @@ -3712,6 +3524,7 @@ "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -3723,26 +3536,29 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-instrument": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", - "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", "istanbul-lib-coverage": "^3.2.0", "semver": "^7.5.4" }, @@ -3750,26 +3566,12 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-instrument/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/istanbul-lib-instrument/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -3777,17 +3579,12 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-instrument/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", @@ -3797,32 +3594,12 @@ "node": ">=10" } }, - "node_modules/istanbul-lib-report/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/istanbul-lib-report/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", @@ -3837,15 +3614,17 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } }, "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -3859,6 +3638,7 @@ "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, + "license": "MIT", "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", @@ -3885,6 +3665,7 @@ "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, + "license": "MIT", "dependencies": { "execa": "^5.0.0", "jest-util": "^29.7.0", @@ -3899,6 +3680,7 @@ "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, + "license": "MIT", "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", @@ -3925,81 +3707,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-circus/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-circus/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-circus/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-circus/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-circus/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-cli": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, + "license": "MIT", "dependencies": { "@jest/core": "^29.7.0", "@jest/test-result": "^29.7.0", @@ -4028,81 +3741,12 @@ } } }, - "node_modules/jest-cli/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-cli/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-cli/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-cli/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-cli/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-config": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, + "license": "MIT", "dependencies": { "@babel/core": "^7.11.6", "@jest/test-sequencer": "^29.7.0", @@ -4143,84 +3787,113 @@ } } }, - "node_modules/jest-config/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-diff": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", + "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "chalk": "^4.0.0", + "diff-sequences": "^29.6.3", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-config/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-docblock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", + "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "detect-newline": "^3.0.0" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-config/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest-each": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", + "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@jest/types": "^29.6.3", + "chalk": "^4.0.0", + "jest-get-type": "^29.6.3", + "jest-util": "^29.7.0", + "pretty-format": "^29.7.0" }, "engines": { - "node": ">=7.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-config/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true + "node_modules/jest-environment-node": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", + "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-mock": "^29.7.0", + "jest-util": "^29.7.0" + }, + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + } }, - "node_modules/jest-config/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-get-type": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", + "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-config/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-haste-map": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", + "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@jest/types": "^29.6.3", + "@types/graceful-fs": "^4.1.3", + "@types/node": "*", + "anymatch": "^3.0.3", + "fb-watchman": "^2.0.0", + "graceful-fs": "^4.2.9", + "jest-regex-util": "^29.6.3", + "jest-util": "^29.7.0", + "jest-worker": "^29.7.0", + "micromatch": "^4.0.4", + "walker": "^1.0.8" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" + }, + "optionalDependencies": { + "fsevents": "^2.3.2" } }, - "node_modules/jest-diff": { + "node_modules/jest-leak-detector": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", - "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", + "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", + "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, + "license": "MIT", "dependencies": { - "chalk": "^4.0.0", - "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" }, @@ -4228,980 +3901,262 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-diff/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-matcher-utils": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", + "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" + "chalk": "^4.0.0", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "pretty-format": "^29.7.0" }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "engines": { + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-diff/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-message-util": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", + "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@babel/code-frame": "^7.12.13", + "@jest/types": "^29.6.3", + "@types/stack-utils": "^2.0.0", + "chalk": "^4.0.0", + "graceful-fs": "^4.2.9", + "micromatch": "^4.0.4", + "pretty-format": "^29.7.0", + "slash": "^3.0.0", + "stack-utils": "^2.0.3" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-diff/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest-mock": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", + "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@jest/types": "^29.6.3", + "@types/node": "*", + "jest-util": "^29.7.0" }, "engines": { - "node": ">=7.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-diff/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-diff/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-pnp-resolver": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", + "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" - } - }, - "node_modules/jest-diff/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" + "node": ">=6" }, - "engines": { - "node": ">=8" + "peerDependencies": { + "jest-resolve": "*" + }, + "peerDependenciesMeta": { + "jest-resolve": { + "optional": true + } } }, - "node_modules/jest-docblock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", - "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", + "node_modules/jest-regex-util": { + "version": "29.6.3", + "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", + "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, - "dependencies": { - "detect-newline": "^3.0.0" - }, + "license": "MIT", "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-each": { + "node_modules/jest-resolve": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", - "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", + "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", + "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/types": "^29.6.3", "chalk": "^4.0.0", - "jest-get-type": "^29.6.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-pnp-resolver": "^1.2.2", "jest-util": "^29.7.0", - "pretty-format": "^29.7.0" + "jest-validate": "^29.7.0", + "resolve": "^1.20.0", + "resolve.exports": "^2.0.0", + "slash": "^3.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-each/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/jest-resolve-dependencies": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", + "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, + "license": "MIT", "dependencies": { - "color-convert": "^2.0.1" + "jest-regex-util": "^29.6.3", + "jest-snapshot": "^29.7.0" }, "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-each/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-runner": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", + "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" + "@jest/console": "^29.7.0", + "@jest/environment": "^29.7.0", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "graceful-fs": "^4.2.9", + "jest-docblock": "^29.7.0", + "jest-environment-node": "^29.7.0", + "jest-haste-map": "^29.7.0", + "jest-leak-detector": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-resolve": "^29.7.0", + "jest-runtime": "^29.7.0", + "jest-util": "^29.7.0", + "jest-watcher": "^29.7.0", + "jest-worker": "^29.7.0", + "p-limit": "^3.1.0", + "source-map-support": "0.5.13" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-each/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "node_modules/jest-runtime": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", + "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, + "license": "MIT", "dependencies": { - "color-name": "~1.1.4" + "@jest/environment": "^29.7.0", + "@jest/fake-timers": "^29.7.0", + "@jest/globals": "^29.7.0", + "@jest/source-map": "^29.6.3", + "@jest/test-result": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "chalk": "^4.0.0", + "cjs-module-lexer": "^1.0.0", + "collect-v8-coverage": "^1.0.0", + "glob": "^7.1.3", + "graceful-fs": "^4.2.9", + "jest-haste-map": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-mock": "^29.7.0", + "jest-regex-util": "^29.6.3", + "jest-resolve": "^29.7.0", + "jest-snapshot": "^29.7.0", + "jest-util": "^29.7.0", + "slash": "^3.0.0", + "strip-bom": "^4.0.0" }, "engines": { - "node": ">=7.0.0" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-each/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-each/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/jest-snapshot": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", + "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, + "license": "MIT", + "dependencies": { + "@babel/core": "^7.11.6", + "@babel/generator": "^7.7.2", + "@babel/plugin-syntax-jsx": "^7.7.2", + "@babel/plugin-syntax-typescript": "^7.7.2", + "@babel/types": "^7.3.3", + "@jest/expect-utils": "^29.7.0", + "@jest/transform": "^29.7.0", + "@jest/types": "^29.6.3", + "babel-preset-current-node-syntax": "^1.0.0", + "chalk": "^4.0.0", + "expect": "^29.7.0", + "graceful-fs": "^4.2.9", + "jest-diff": "^29.7.0", + "jest-get-type": "^29.6.3", + "jest-matcher-utils": "^29.7.0", + "jest-message-util": "^29.7.0", + "jest-util": "^29.7.0", + "natural-compare": "^1.4.0", + "pretty-format": "^29.7.0", + "semver": "^7.5.3" + }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-each/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "node_modules/jest-snapshot/node_modules/semver": { + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, - "dependencies": { - "has-flag": "^4.0.0" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" }, "engines": { - "node": ">=8" + "node": ">=10" } }, - "node_modules/jest-environment-node": { + "node_modules/jest-util": { "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", - "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", + "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", + "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, + "license": "MIT", "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", - "jest-mock": "^29.7.0", - "jest-util": "^29.7.0" + "chalk": "^4.0.0", + "ci-info": "^3.2.0", + "graceful-fs": "^4.2.9", + "picomatch": "^2.2.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-get-type": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", - "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-haste-map": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", - "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/graceful-fs": "^4.1.3", - "@types/node": "*", - "anymatch": "^3.0.3", - "fb-watchman": "^2.0.0", - "graceful-fs": "^4.2.9", - "jest-regex-util": "^29.6.3", - "jest-util": "^29.7.0", - "jest-worker": "^29.7.0", - "micromatch": "^4.0.4", - "walker": "^1.0.8" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - }, - "optionalDependencies": { - "fsevents": "^2.3.2" - } - }, - "node_modules/jest-leak-detector": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", - "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", - "dev": true, - "dependencies": { - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", - "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "pretty-format": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-matcher-utils/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-matcher-utils/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-matcher-utils/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-matcher-utils/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", - "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.12.13", - "@jest/types": "^29.6.3", - "@types/stack-utils": "^2.0.0", - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "micromatch": "^4.0.4", - "pretty-format": "^29.7.0", - "slash": "^3.0.0", - "stack-utils": "^2.0.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-message-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-message-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-message-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-message-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-message-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-mock": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", - "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "jest-util": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-pnp-resolver": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", - "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", - "dev": true, - "engines": { - "node": ">=6" - }, - "peerDependencies": { - "jest-resolve": "*" - }, - "peerDependenciesMeta": { - "jest-resolve": { - "optional": true - } - } - }, - "node_modules/jest-regex-util": { - "version": "29.6.3", - "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", - "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", - "dev": true, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", - "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", - "dev": true, - "dependencies": { - "chalk": "^4.0.0", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-pnp-resolver": "^1.2.2", - "jest-util": "^29.7.0", - "jest-validate": "^29.7.0", - "resolve": "^1.20.0", - "resolve.exports": "^2.0.0", - "slash": "^3.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve-dependencies": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", - "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", - "dev": true, - "dependencies": { - "jest-regex-util": "^29.6.3", - "jest-snapshot": "^29.7.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-resolve/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-resolve/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-resolve/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-resolve/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-resolve/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-resolve/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", - "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", - "dev": true, - "dependencies": { - "@jest/console": "^29.7.0", - "@jest/environment": "^29.7.0", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "graceful-fs": "^4.2.9", - "jest-docblock": "^29.7.0", - "jest-environment-node": "^29.7.0", - "jest-haste-map": "^29.7.0", - "jest-leak-detector": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-resolve": "^29.7.0", - "jest-runtime": "^29.7.0", - "jest-util": "^29.7.0", - "jest-watcher": "^29.7.0", - "jest-worker": "^29.7.0", - "p-limit": "^3.1.0", - "source-map-support": "0.5.13" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runner/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runner/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runner/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-runner/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runner/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", - "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", - "dev": true, - "dependencies": { - "@jest/environment": "^29.7.0", - "@jest/fake-timers": "^29.7.0", - "@jest/globals": "^29.7.0", - "@jest/source-map": "^29.6.3", - "@jest/test-result": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "cjs-module-lexer": "^1.0.0", - "collect-v8-coverage": "^1.0.0", - "glob": "^7.1.3", - "graceful-fs": "^4.2.9", - "jest-haste-map": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-mock": "^29.7.0", - "jest-regex-util": "^29.6.3", - "jest-resolve": "^29.7.0", - "jest-snapshot": "^29.7.0", - "jest-util": "^29.7.0", - "slash": "^3.0.0", - "strip-bom": "^4.0.0" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-runtime/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-runtime/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-runtime/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-runtime/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-runtime/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", - "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", - "dev": true, - "dependencies": { - "@babel/core": "^7.11.6", - "@babel/generator": "^7.7.2", - "@babel/plugin-syntax-jsx": "^7.7.2", - "@babel/plugin-syntax-typescript": "^7.7.2", - "@babel/types": "^7.3.3", - "@jest/expect-utils": "^29.7.0", - "@jest/transform": "^29.7.0", - "@jest/types": "^29.6.3", - "babel-preset-current-node-syntax": "^1.0.0", - "chalk": "^4.0.0", - "expect": "^29.7.0", - "graceful-fs": "^4.2.9", - "jest-diff": "^29.7.0", - "jest-get-type": "^29.6.3", - "jest-matcher-utils": "^29.7.0", - "jest-message-util": "^29.7.0", - "jest-util": "^29.7.0", - "natural-compare": "^1.4.0", - "pretty-format": "^29.7.0", - "semver": "^7.5.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-snapshot/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-snapshot/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-snapshot/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/jest-snapshot/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-snapshot/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, - "node_modules/jest-util": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", - "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", - "dev": true, - "dependencies": { - "@jest/types": "^29.6.3", - "@types/node": "*", - "chalk": "^4.0.0", - "ci-info": "^3.2.0", - "graceful-fs": "^4.2.9", - "picomatch": "^2.2.3" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-util/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "node_modules/jest-util/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=8.6" }, "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-util/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-util/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-util/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-util/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" + "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/jest-validate": { @@ -5209,6 +4164,7 @@ "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, + "license": "MIT", "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", @@ -5221,175 +4177,37 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-validate/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, "node_modules/jest-validate/node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/jest-validate/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-validate/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-validate/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-validate/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-validate/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher": { - "version": "29.7.0", - "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", - "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", - "dev": true, - "dependencies": { - "@jest/test-result": "^29.7.0", - "@jest/types": "^29.6.3", - "@types/node": "*", - "ansi-escapes": "^4.2.1", - "chalk": "^4.0.0", - "emittery": "^0.13.1", - "jest-util": "^29.7.0", - "string-length": "^4.0.1" - }, - "engines": { - "node": "^14.15.0 || ^16.10.0 || >=18.0.0" - } - }, - "node_modules/jest-watcher/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/jest-watcher/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/jest-watcher/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/jest-watcher/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/jest-watcher/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + } + }, + "node_modules/jest-watcher": { + "version": "29.7.0", + "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", + "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, + "license": "MIT", "dependencies": { - "has-flag": "^4.0.0" + "@jest/test-result": "^29.7.0", + "@jest/types": "^29.6.3", + "@types/node": "*", + "ansi-escapes": "^4.2.1", + "chalk": "^4.0.0", + "emittery": "^0.13.1", + "jest-util": "^29.7.0", + "string-length": "^4.0.1" }, "engines": { - "node": ">=8" + "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-worker": { @@ -5397,6 +4215,7 @@ "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", @@ -5407,20 +4226,12 @@ "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, - "node_modules/jest-worker/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/jest-worker/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -5434,7 +4245,8 @@ "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==" + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "license": "MIT" }, "node_modules/js-yaml": { "version": "3.14.2", @@ -5465,13 +4277,15 @@ "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", - "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==" + "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", + "license": "MIT" }, "node_modules/json5": { "version": "2.2.3", "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -5484,6 +4298,7 @@ "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -5493,6 +4308,7 @@ "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -5500,13 +4316,15 @@ "node_modules/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", - "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==" + "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", + "license": "MIT" }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^4.1.0" }, @@ -5518,6 +4336,7 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "license": "MIT", "dependencies": { "js-tokens": "^3.0.0 || ^4.0.0" }, @@ -5540,6 +4359,7 @@ "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, + "license": "MIT", "dependencies": { "semver": "^7.5.3" }, @@ -5550,26 +4370,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/make-dir/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/make-dir/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -5577,17 +4383,12 @@ "node": ">=10" } }, - "node_modules/make-dir/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true - }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "tmpl": "1.0.5" } @@ -5596,7 +4397,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/micromatch": { "version": "4.0.8", @@ -5612,11 +4414,25 @@ "node": ">=8.6" } }, + "node_modules/micromatch/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -5626,6 +4442,7 @@ "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, @@ -5634,14 +4451,15 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/nanoid": { - "version": "3.3.9", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.9.tgz", - "integrity": "sha512-SppoicMGpZvbF1l3z4x7No3OlIjP7QJvC9XR7AhZr1kL133KHnKPztkKDc+Ir4aJ/1VhTySrtKhrsycmrMQfvg==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz", + "integrity": "sha512-N8SpfPUnUp1bK+PMYW8qSWdl9U+wwNWI4QKxOYDy9JAro3WMX7p2OeVRF9v+347pnakNevPmiHhNmZ2HbFA76w==", "dev": true, "funding": [ { @@ -5661,18 +4479,20 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/node-releases": { - "version": "2.0.19", - "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", - "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", + "version": "2.0.27", + "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.27.tgz", + "integrity": "sha512-nmh3lCkYZ3grZvqcCH+fjmQ7X+H0OeZgP40OierEaAptX4XofMh5kwNbWh7lBduUzCcV/8kZ+NDLCwm2iorIlA==", "dev": true, "license": "MIT" }, @@ -5681,6 +4501,7 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5690,6 +4511,7 @@ "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.0.0" }, @@ -5701,6 +4523,7 @@ "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==", + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5710,6 +4533,7 @@ "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", "dev": true, + "license": "ISC", "dependencies": { "wrappy": "1" } @@ -5719,6 +4543,7 @@ "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, + "license": "MIT", "dependencies": { "mimic-fn": "^2.1.0" }, @@ -5734,6 +4559,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -5749,6 +4575,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^2.2.0" }, @@ -5761,6 +4588,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, + "license": "MIT", "dependencies": { "p-try": "^2.0.0" }, @@ -5776,6 +4604,7 @@ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -5784,6 +4613,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -5795,6 +4625,7 @@ "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", + "license": "MIT", "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", @@ -5813,6 +4644,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -5822,6 +4654,7 @@ "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5831,6 +4664,7 @@ "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -5838,12 +4672,14 @@ "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", - "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==" + "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", + "license": "MIT" }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "license": "MIT", "engines": { "node": ">=8" } @@ -5855,22 +4691,24 @@ "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/pirates": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.6.tgz", - "integrity": "sha512-saLsH7WeYYPiD25LDuLRRY/i+6HaPYr6G1OUlN39otzkSTxKnubR9RTxS3/Kk50s1g2JTgFwWQDQyplC5/SHZg==", + "version": "4.0.7", + "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", + "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 6" } @@ -5880,6 +4718,7 @@ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, + "license": "MIT", "dependencies": { "find-up": "^4.0.0" }, @@ -5888,9 +4727,9 @@ } }, "node_modules/postcss": { - "version": "8.5.3", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", - "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", + "version": "8.5.6", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.6.tgz", + "integrity": "sha512-3Ybi1tAuwAP9s0r1UQ2J4n5Y0G05bJkpUIO0/bI9MhwmD70S5aTWbXGBwxHrelT+XM1k6dM0pk+SwNkpTRN7Pg==", "dev": true, "funding": [ { @@ -5908,7 +4747,7 @@ ], "license": "MIT", "dependencies": { - "nanoid": "^3.3.8", + "nanoid": "^3.3.11", "picocolors": "^1.1.1", "source-map-js": "^1.2.1" }, @@ -5921,6 +4760,7 @@ "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, + "license": "MIT", "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", @@ -5935,6 +4775,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -5942,11 +4783,19 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/pretty-format/node_modules/react-is": { + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", + "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", + "dev": true, + "license": "MIT" + }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, + "license": "MIT", "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" @@ -5959,6 +4808,7 @@ "version": "15.8.1", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", + "license": "MIT", "dependencies": { "loose-envify": "^1.4.0", "object-assign": "^4.1.1", @@ -5968,12 +4818,13 @@ "node_modules/prop-types/node_modules/react-is": { "version": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", - "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==" + "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", + "license": "MIT" }, "node_modules/pure-rand": { - "version": "6.0.4", - "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.0.4.tgz", - "integrity": "sha512-LA0Y9kxMYv47GIPJy6MI84fqTd2HmYZI83W/kM/SkKfDlajnZYfmXFTxkbY+xSBPkLJxltMa9hIkmdc29eguMA==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", + "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", "dev": true, "funding": [ { @@ -5984,12 +4835,14 @@ "type": "opencollective", "url": "https://opencollective.com/fast-check" } - ] + ], + "license": "MIT" }, "node_modules/react": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react/-/react-18.2.0.tgz", - "integrity": "sha512-/3IjMdb2L9QbBdWiW5e3P2/npwMBaU9mHCSCUzNln0ZCYbcfTsGbTJrU/kGemdH2IWmB2ioZ+zkxtmq6g09fGQ==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", + "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" }, @@ -5998,26 +4851,28 @@ } }, "node_modules/react-dom": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.2.0.tgz", - "integrity": "sha512-6IMTriUmvsjHUjNtEDudZfuDQUoWXVxKHhlEGSk81n4YFS+r/Kl99wXiwlVXtPBtJenozv2P+hxDsw9eA7Xo6g==", + "version": "18.3.1", + "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", + "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0", - "scheduler": "^0.23.0" + "scheduler": "^0.23.2" }, "peerDependencies": { - "react": "^18.2.0" + "react": "^18.3.1" } }, "node_modules/react-is": { - "version": "18.2.0", - "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.2.0.tgz", - "integrity": "sha512-xWGDIW6x921xtzPkhiULtthJHoJvBbF3q26fzloPCK0hsvxtPVelvftw3zjbHWSkR2km9Z+4uxbDDK/6Zw9B8w==" + "version": "19.2.4", + "resolved": "https://registry.npmjs.org/react-is/-/react-is-19.2.4.tgz", + "integrity": "sha512-W+EWGn2v0ApPKgKKCy/7s7WHXkboGcsrXE+2joLyVxkbyVQfO3MUEaUQDHoSmb8TFFrSKYa9mw64WZHNHSDzYA==", + "license": "MIT" }, "node_modules/react-refresh": { - "version": "0.14.2", - "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.14.2.tgz", - "integrity": "sha512-jCvmsr+1IUSMUyzOkRcvnVbX3ZYC6g9TDrDbFuFmRDq7PD4yaGbLKNQL6k2jnArV8hjYxh7hVhAZB6s9HDGpZA==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/react-refresh/-/react-refresh-0.17.0.tgz", + "integrity": "sha512-z6F7K9bV85EfseRCp2bzrpyQ0Gkw1uLoCel9XBVWPg/TjRj94SkJzUTGfOa4bs7iJvBWtQG0Wq7wnI0syw3EBQ==", "dev": true, "license": "MIT", "engines": { @@ -6028,6 +4883,7 @@ "version": "4.4.5", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz", "integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==", + "license": "BSD-3-Clause", "dependencies": { "@babel/runtime": "^7.5.5", "dom-helpers": "^5.0.1", @@ -6039,32 +4895,32 @@ "react-dom": ">=16.6.0" } }, - "node_modules/regenerator-runtime": { - "version": "0.14.0", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.0.tgz", - "integrity": "sha512-srw17NI0TUWHuGa5CFGGmhfNIeja30WMBfbslPNhf6JrqQlLN5gcrvig1oqPxiVaXb0oW0XRKtH6Nngs5lKCIA==" - }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/resolve": { - "version": "1.22.4", - "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.4.tgz", - "integrity": "sha512-PXNdCiPqDqeUou+w1C2eTQbNfxKSuMxqTCuvlmmMsk1NWHL5fRrhY6Pl0qEYYc6+QqGClco1Qj8XnjPego4wfg==", + "version": "1.22.11", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.11.tgz", + "integrity": "sha512-RfqAvLnMl313r7c9oclB1HhUEAezcpLjz95wFH4LVuhk9JF/r22qmVP9AMmOU4vMX7Q8pN8jwNg/CSpdFnMjTQ==", + "license": "MIT", "dependencies": { - "is-core-module": "^2.13.0", + "is-core-module": "^2.16.1", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, + "engines": { + "node": ">= 0.4" + }, "funding": { "url": "https://github.com/sponsors/ljharb" } @@ -6074,6 +4930,7 @@ "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, + "license": "MIT", "dependencies": { "resolve-from": "^5.0.0" }, @@ -6086,6 +4943,7 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -6094,27 +4952,29 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", + "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/resolve.exports": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.2.tgz", - "integrity": "sha512-X2UW6Nw3n/aMgDVy+0rSqgHlv39WZAlZrXCdnbyEiKm17DSqHX4MmQMaST3FbeWR5FTuRcUwYAziZajji0Y7mg==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", + "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" } }, "node_modules/rollup": { - "version": "4.35.0", - "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.35.0.tgz", - "integrity": "sha512-kg6oI4g+vc41vePJyO6dHt/yl0Rz3Thv0kJeVQ3D1kS3E5XSuKbPc29G4IpT/Kv1KQwgHVcN+HtyS+HYLNSvQg==", + "version": "4.57.0", + "resolved": "https://registry.npmjs.org/rollup/-/rollup-4.57.0.tgz", + "integrity": "sha512-e5lPJi/aui4TO1LpAXIRLySmwXSE8k3b9zoGfd42p67wzxog4WHjiZF3M2uheQih4DGyc25QEV4yRBbpueNiUA==", "dev": true, "license": "MIT", "dependencies": { - "@types/estree": "1.0.6" + "@types/estree": "1.0.8" }, "bin": { "rollup": "dist/bin/rollup" @@ -6124,32 +4984,39 @@ "npm": ">=8.0.0" }, "optionalDependencies": { - "@rollup/rollup-android-arm-eabi": "4.35.0", - "@rollup/rollup-android-arm64": "4.35.0", - "@rollup/rollup-darwin-arm64": "4.35.0", - "@rollup/rollup-darwin-x64": "4.35.0", - "@rollup/rollup-freebsd-arm64": "4.35.0", - "@rollup/rollup-freebsd-x64": "4.35.0", - "@rollup/rollup-linux-arm-gnueabihf": "4.35.0", - "@rollup/rollup-linux-arm-musleabihf": "4.35.0", - "@rollup/rollup-linux-arm64-gnu": "4.35.0", - "@rollup/rollup-linux-arm64-musl": "4.35.0", - "@rollup/rollup-linux-loongarch64-gnu": "4.35.0", - "@rollup/rollup-linux-powerpc64le-gnu": "4.35.0", - "@rollup/rollup-linux-riscv64-gnu": "4.35.0", - "@rollup/rollup-linux-s390x-gnu": "4.35.0", - "@rollup/rollup-linux-x64-gnu": "4.35.0", - "@rollup/rollup-linux-x64-musl": "4.35.0", - "@rollup/rollup-win32-arm64-msvc": "4.35.0", - "@rollup/rollup-win32-ia32-msvc": "4.35.0", - "@rollup/rollup-win32-x64-msvc": "4.35.0", + "@rollup/rollup-android-arm-eabi": "4.57.0", + "@rollup/rollup-android-arm64": "4.57.0", + "@rollup/rollup-darwin-arm64": "4.57.0", + "@rollup/rollup-darwin-x64": "4.57.0", + "@rollup/rollup-freebsd-arm64": "4.57.0", + "@rollup/rollup-freebsd-x64": "4.57.0", + "@rollup/rollup-linux-arm-gnueabihf": "4.57.0", + "@rollup/rollup-linux-arm-musleabihf": "4.57.0", + "@rollup/rollup-linux-arm64-gnu": "4.57.0", + "@rollup/rollup-linux-arm64-musl": "4.57.0", + "@rollup/rollup-linux-loong64-gnu": "4.57.0", + "@rollup/rollup-linux-loong64-musl": "4.57.0", + "@rollup/rollup-linux-ppc64-gnu": "4.57.0", + "@rollup/rollup-linux-ppc64-musl": "4.57.0", + "@rollup/rollup-linux-riscv64-gnu": "4.57.0", + "@rollup/rollup-linux-riscv64-musl": "4.57.0", + "@rollup/rollup-linux-s390x-gnu": "4.57.0", + "@rollup/rollup-linux-x64-gnu": "4.57.0", + "@rollup/rollup-linux-x64-musl": "4.57.0", + "@rollup/rollup-openbsd-x64": "4.57.0", + "@rollup/rollup-openharmony-arm64": "4.57.0", + "@rollup/rollup-win32-arm64-msvc": "4.57.0", + "@rollup/rollup-win32-ia32-msvc": "4.57.0", + "@rollup/rollup-win32-x64-gnu": "4.57.0", + "@rollup/rollup-win32-x64-msvc": "4.57.0", "fsevents": "~2.3.2" } }, "node_modules/scheduler": { - "version": "0.23.0", - "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", - "integrity": "sha512-CtuThmgHNg7zIZWAXi3AsyIzA3n4xx7aNyjwC2VJldO2LMVDhFK+63xGqq6CsJH4rTAt6/M+N4GhZiDYPx9eUw==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.2.tgz", + "integrity": "sha512-UOShsPwz7NrMUqhR6t0hWjFduvOzbtv7toDH1/hIrfRNIDBnnBWd0CwJTGvTpngVlmwGCdP9/Zl/tVrDqcuYzQ==", + "license": "MIT", "dependencies": { "loose-envify": "^1.1.0" } @@ -6159,6 +5026,7 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } @@ -6168,6 +5036,7 @@ "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, + "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, @@ -6180,6 +5049,7 @@ "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -6188,19 +5058,22 @@ "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -6209,6 +5082,7 @@ "version": "0.5.7", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -6228,6 +5102,7 @@ "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, + "license": "MIT", "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" @@ -6238,6 +5113,7 @@ "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -6246,13 +5122,15 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, + "license": "MIT", "dependencies": { "escape-string-regexp": "^2.0.0" }, @@ -6265,6 +5143,7 @@ "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -6274,6 +5153,7 @@ "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, + "license": "MIT", "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" @@ -6287,6 +5167,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -6301,6 +5182,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -6313,6 +5195,7 @@ "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -6322,6 +5205,7 @@ "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -6331,6 +5215,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -6341,12 +5226,27 @@ "node_modules/stylis": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/stylis/-/stylis-4.2.0.tgz", - "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==" + "integrity": "sha512-Orov6g6BB1sDfYgzWfTHDOxamtX1bE/zo104Dh9e6fqJ3PooipYyfJ0pUmrZO2wAvO8YbEyeFrkV91XTsGMSrw==", + "license": "MIT" + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } }, "node_modules/supports-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -6359,6 +5259,7 @@ "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, + "license": "ISC", "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", @@ -6369,14 +5270,14 @@ } }, "node_modules/tinyglobby": { - "version": "0.2.13", - "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.13.tgz", - "integrity": "sha512-mEwzpUgrLySlveBwEVDMKk5B57bhLPYovRfPAXD5gA/98Opn0rCDj3GtLwFvCvH5RK9uPCExUROW5NjDwvqkxw==", + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, "license": "MIT", "dependencies": { - "fdir": "^6.4.4", - "picomatch": "^4.0.2" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">=12.0.0" @@ -6385,45 +5286,19 @@ "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/tinyglobby/node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/tinyglobby/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", - "dev": true + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, + "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, @@ -6436,6 +5311,7 @@ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -6445,6 +5321,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -6453,10 +5330,11 @@ } }, "node_modules/typescript": { - "version": "5.2.2", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.2.2.tgz", - "integrity": "sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -6466,15 +5344,16 @@ } }, "node_modules/undici-types": { - "version": "5.25.3", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.25.3.tgz", - "integrity": "sha512-Ga1jfYwRn7+cP9v8auvEXN1rX3sWqlayd4HP7OKk4mZWylEmu3KzXDUGrQUN6Ol7qo1gPvB2e5gX6udnyEPgdA==", - "dev": true + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true, + "license": "MIT" }, "node_modules/update-browserslist-db": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", - "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.2.3.tgz", + "integrity": "sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==", "dev": true, "funding": [ { @@ -6503,10 +5382,11 @@ } }, "node_modules/v8-to-istanbul": { - "version": "9.1.3", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.1.3.tgz", - "integrity": "sha512-9lDD+EVI2fjFsMWXc6dy5JJzBsVTcQ2fVkfBvncZ6xJWG9wtBhOldG+mHkSL0+V1K/xgZz0JDO5UT5hFwHUghg==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, + "license": "ISC", "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", @@ -6520,7 +5400,8 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/vite": { "version": "6.4.1", @@ -6597,39 +5478,12 @@ } } }, - "node_modules/vite/node_modules/fdir": { - "version": "6.4.4", - "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.4.4.tgz", - "integrity": "sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==", - "dev": true, - "license": "MIT", - "peerDependencies": { - "picomatch": "^3 || ^4" - }, - "peerDependenciesMeta": { - "picomatch": { - "optional": true - } - } - }, - "node_modules/vite/node_modules/picomatch": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.2.tgz", - "integrity": "sha512-M7BAV6Rlcy5u+m6oPhAPFgJTzAioX/6B0DxyvDlo9l8+T3nLKbrczg2WLUyzd45L8RqfUMyGPzekbMvX2Ldkwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/jonschlinkert" - } - }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { "makeerror": "1.0.12" } @@ -6639,6 +5493,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -6654,6 +5509,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -6666,50 +5522,19 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true + "dev": true, + "license": "ISC" }, "node_modules/write-file-atomic": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, + "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" @@ -6723,6 +5548,7 @@ "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, + "license": "ISC", "engines": { "node": ">=10" } @@ -6734,26 +5560,12 @@ "dev": true, "license": "ISC" }, - "node_modules/yaml": { - "version": "2.7.0", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.7.0.tgz", - "integrity": "sha512-+hSoy/QHluxmC9kCIJyL/uyFmLmc+e5CFR5Wa+bpIhIj85LVb9ZH2nVnqrHoSvKogwODv0ClqZkmiSSaIH5LTA==", - "dev": true, - "license": "ISC", - "optional": true, - "peer": true, - "bin": { - "yaml": "bin.mjs" - }, - "engines": { - "node": ">= 14" - } - }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", @@ -6772,6 +5584,7 @@ "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, + "license": "ISC", "engines": { "node": ">=12" } @@ -6781,6 +5594,7 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, diff --git a/docker-extension/ui/package.json b/docker-extension/ui/package.json index 087eda3d9..0e20fc5ad 100644 --- a/docker-extension/ui/package.json +++ b/docker-extension/ui/package.json @@ -5,14 +5,13 @@ "type": "module", "dependencies": { "@docker/docker-mui-theme": "<0.1.0", - "@docker/extension-api-client": "0.3.4", - "@emotion/react": "^11.10.4", - "@emotion/styled": "^11.10.4", - "@mui/icons-material": "^5.14.12", - "@mui/lab": "^5.0.0-alpha.147", - "@mui/material": "^5.14.12", + "@docker/extension-api-client": "0.4.2", + "@emotion/react": "11.14.0", + "@emotion/styled": "11.14.1", + "@mui/material": "6.5.0", "react": "^18.2.0", - "react-dom": "^18.2.0" + "react-dom": "^18.2.0", + "@mui/icons-material": "6.5.0" }, "scripts": { "dev": "vite", @@ -20,7 +19,7 @@ "test": "jest src" }, "devDependencies": { - "@docker/extension-api-client-types": "0.3.4", + "@docker/extension-api-client-types": "0.4.2", "@types/jest": "^29.5.5", "@types/node": "^20.8.4", "@types/react": "^18.2.27", diff --git a/docker-extension/ui/src/main.tsx b/docker-extension/ui/src/main.tsx index c730e6d27..d39b4cce3 100644 --- a/docker-extension/ui/src/main.tsx +++ b/docker-extension/ui/src/main.tsx @@ -1,15 +1,15 @@ import React from "react"; import ReactDOM from "react-dom/client"; import CssBaseline from "@mui/material/CssBaseline"; -import { DockerMuiThemeProvider } from "@docker/docker-mui-theme"; +import { DockerMuiV6ThemeProvider } from "@docker/docker-mui-theme"; import { App } from "./views/App"; ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render( - + - + ); diff --git a/docker-extension/ui/src/views/App.tsx b/docker-extension/ui/src/views/App.tsx index 1182e3db0..df35278e0 100644 --- a/docker-extension/ui/src/views/App.tsx +++ b/docker-extension/ui/src/views/App.tsx @@ -7,7 +7,7 @@ import { handleGoTo } from "../common/goto"; import { Workshop, ListResponse, DeployResponse, DeleteResponse } from "../common/types"; import { isValidURL } from "../common/validations"; import OptionsPane from "../components/OptionsPane/OptionsPane"; -import { LoadingButton } from "@mui/lab"; +import Button from "@mui/material/Button"; const sampleWorkshopURL = "https://github.com/educates/lab-container-basics/releases/latest/download/workshop.yaml"; @@ -171,9 +171,9 @@ export function App() { - + diff --git a/go.work b/go.work index b095f6393..2af8d684d 100644 --- a/go.work +++ b/go.work @@ -1,3 +1,3 @@ -go 1.24.10 +go 1.25.6 use ./client-programs/ diff --git a/tunnel-manager/go.mod b/tunnel-manager/go.mod index 04fbb8780..7f738701e 100644 --- a/tunnel-manager/go.mod +++ b/tunnel-manager/go.mod @@ -1,6 +1,6 @@ module main -go 1.20 +go 1.25.6 require ( github.com/gorilla/websocket v1.5.0 // indirect diff --git a/workshop-images/base-environment/Dockerfile b/workshop-images/base-environment/Dockerfile index 2ba3bb309..81f48cbe0 100644 --- a/workshop-images/base-environment/Dockerfile +++ b/workshop-images/base-environment/Dockerfile @@ -78,7 +78,9 @@ WORKDIR /opt/helper RUN npm install && \ npm run vsce-package -FROM golang:1.19-buster as builder-image +FROM golang:1.25.6 as builder-image +ARG TARGETOS +ARG TARGETARCH WORKDIR /app @@ -87,7 +89,7 @@ echo "09cd14a34f17d88cd4f0d2b73e0bbd0bf56984be21bc947f416a7824a709011e /tmp/git- tar xvf /tmp/git-serve.tar.gz && \ cd git-serve-0.0.5 && \ go mod download && \ - go build -o git-serve cmd/git-serve/main.go + GOOS=${TARGETOS} GOARCH=${TARGETARCH} go build -o git-serve cmd/git-serve/main.go FROM system-base AS scratch-image From 8da01a3296b4269bdd2f52b99110f254ef6c3fc8 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Wed, 28 Jan 2026 18:58:07 +0100 Subject: [PATCH 22/24] Adding labels to containers --- .../pkg/cmd/docker_workshop_open_cmd.go | 2 +- .../pkg/cmd/local_secrets_add_ca_cmd.go | 3 +- .../pkg/cmd/local_secrets_add_tls_cmd.go | 3 +- client-programs/pkg/constants/names.go | 33 +++++++++++++++++-- .../pkg/diagnostics/diagnostics.go | 2 +- client-programs/pkg/diagnostics/fetcher.go | 4 +-- .../pkg/docker/workshop_manager.go | 29 ++++++++-------- .../pkg/educates/local/workshops/manager.go | 5 +-- .../pkg/educates/resources/portal/manager.go | 2 +- .../educates/resources/sessions/manager.go | 9 ++--- .../educates/resources/workshops/manager.go | 12 +++---- client-programs/pkg/registry/base.go | 30 ++++++++++++++++- client-programs/pkg/registry/mirror.go | 18 +++------- client-programs/pkg/registry/registry.go | 5 +-- client-programs/pkg/resolver/resolver.go | 8 +++++ client-programs/pkg/secrets/secrets.go | 4 +-- 16 files changed, 114 insertions(+), 55 deletions(-) diff --git a/client-programs/pkg/cmd/docker_workshop_open_cmd.go b/client-programs/pkg/cmd/docker_workshop_open_cmd.go index 0412cb8ea..683de9277 100644 --- a/client-programs/pkg/cmd/docker_workshop_open_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_open_cmd.go @@ -82,7 +82,7 @@ func (o *DockerWorkshopOpenOptions) Run() error { return errors.New("unable to find workshop") } - url, found := container.Config.Labels["training.educates.dev/url"] + url, found := container.Config.Labels[constants.EducatesWorkshopLabelAnnotationURL] if !found || url == "" { return errors.New("can't determine URL for workshop") diff --git a/client-programs/pkg/cmd/local_secrets_add_ca_cmd.go b/client-programs/pkg/cmd/local_secrets_add_ca_cmd.go index 0810931cb..b6220e0be 100644 --- a/client-programs/pkg/cmd/local_secrets_add_ca_cmd.go +++ b/client-programs/pkg/cmd/local_secrets_add_ca_cmd.go @@ -6,6 +6,7 @@ import ( "path" "regexp" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -64,7 +65,7 @@ func (o *LocalSecretsAddCaOptions) Run(name string) error { } if o.IngressDomain != "" { - secret.ObjectMeta.Annotations["training.educates.dev/domain"] = o.IngressDomain + secret.ObjectMeta.Annotations[constants.EducatesTrainingLabelAnnotationDomain] = o.IngressDomain } secretData, err := json.MarshalIndent(&secret, "", " ") diff --git a/client-programs/pkg/cmd/local_secrets_add_tls_cmd.go b/client-programs/pkg/cmd/local_secrets_add_tls_cmd.go index 01be3588a..c8a95891f 100644 --- a/client-programs/pkg/cmd/local_secrets_add_tls_cmd.go +++ b/client-programs/pkg/cmd/local_secrets_add_tls_cmd.go @@ -6,6 +6,7 @@ import ( "path" "regexp" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" "github.com/spf13/cobra" @@ -72,7 +73,7 @@ func (o *LocalSecretsAddTlsOptions) Run(name string) error { } if o.IngressDomain != "" { - secret.ObjectMeta.Annotations["training.educates.dev/domain"] = o.IngressDomain + secret.ObjectMeta.Annotations[constants.EducatesTrainingLabelAnnotationDomain] = o.IngressDomain } secretData, err := json.MarshalIndent(&secret, "", " ") diff --git a/client-programs/pkg/constants/names.go b/client-programs/pkg/constants/names.go index b9553a6b1..2f6aa9f52 100644 --- a/client-programs/pkg/constants/names.go +++ b/client-programs/pkg/constants/names.go @@ -8,8 +8,35 @@ const ( EducatesNetworkName = "educates" EducatesRegistryContainer = "educates-registry" EducatesControlPlaneContainer = "educates-control-plane" - EducatesRegistryRoleLabel = "registry" - EducatesMirrorRoleLabel = "mirror" - EducatesAppLabel = "educates" EducatesResolverContainerName = "educates-resolver" + + // Workshop API Group and Version + EducatesTrainingAPIGroup = "training.educates.dev" + EducatesTrainingAPIVersion = "v1beta1" + EducatesTrainingAPIGroupVersion = "training.educates.dev/v1beta1" + + // Workshop Pod Label/Annotations Keys + EducatesWorkshopLabelAnnotationURL = "training.educates.dev/url" + EducatesWorkshopLabelAnnotationSource = "training.educates.dev/source" + EducatesWorkshopLabelAnnotationSession = "training.educates.dev/session" + EducatesWorkshopLabelAnnotationWorkshop = "training.educates.dev/workshop" + EducatesWorkshopLabelAnnotationComponent = "training.educates.dev/component" + EducatesWorkshopLabelAnnotationComponentPortal = "training.educates.dev/component=portal" + + EducatesTrainingLabelAnnotationDomain = "training.educates.dev/domain" + EducatesTrainingLabelAnnotationEnvironmentName = "training.educates.dev/environment.name" + EducatesTrainingLabelAnnotationPortalName = "training.educates.dev/portal.name" + + // Container Label Keys + EducatesContainersAppLabelKey = "educates.dev/app" + EducatesContainersRoleLabelKey = "educates.dev/role" + EducatesContainersMirrorLabelKey = "educates.dev/mirror" + EducatesContainersURLLabelKey = "educates.dev/url" + EducatesContainersUsernameLabelKey = "educates.dev/username" + // Container Label Values + EducatesContainersRegistryRoleLabel = "registry" + EducatesContainersMirrorRoleLabel = "mirror" + EducatesContainersResolverRoleLabel = "resolver" + EducatesContainersWorkshopRoleLabel = "workshop" + EducatesContainersAppLabel = "educates" ) diff --git a/client-programs/pkg/diagnostics/diagnostics.go b/client-programs/pkg/diagnostics/diagnostics.go index 888768c45..d129414ad 100644 --- a/client-programs/pkg/diagnostics/diagnostics.go +++ b/client-programs/pkg/diagnostics/diagnostics.go @@ -83,7 +83,7 @@ func (c *ClusterDiagnostics) Run() error { fmt.Println("Error fetching logs for secrets-manager: ", err) } // dump logs for all training-portal deployments - if err = clusterDiagnosticsFetcher.fetchLogsForDeployment("deployment=training-portal", "training.educates.dev/component=portal", "training-portal-%v.log"); err != nil { + if err = clusterDiagnosticsFetcher.fetchLogsForDeployment("deployment=training-portal", constants.EducatesWorkshopLabelAnnotationComponentPortal, "training-portal-%v.log"); err != nil { fmt.Println("Error fetching logs for secrets-manager: ", err) } // Fetch workshop_list from Rest API for each training-portal diff --git a/client-programs/pkg/diagnostics/fetcher.go b/client-programs/pkg/diagnostics/fetcher.go index 9662eb05e..e195926d6 100644 --- a/client-programs/pkg/diagnostics/fetcher.go +++ b/client-programs/pkg/diagnostics/fetcher.go @@ -35,7 +35,7 @@ func (c *ClusterDiagnosticsFetcher) getEducatesNamespaces(fileName string) error } namespaces, err := client.CoreV1().Namespaces().List(context.TODO(), metav1.ListOptions{ - // LabelSelector: "training.educates.dev/component", + // LabelSelector: constants.EducatesWorkshopLabelAnnotationComponent, }) if err != nil { return err @@ -88,7 +88,7 @@ func (c *ClusterDiagnosticsFetcher) getEducatesNamespacesEvents(fileName string) continue } events, err := client.CoreV1().Events(namespace.Name).List(context.TODO(), metav1.ListOptions{ - // LabelSelector: "training.educates.dev/component", + // LabelSelector: constants.EducatesWorkshopLabelAnnotationComponent, }) for _, object := range events.Items { object.SetManagedFields(nil) // Remove managedFields from the object diff --git a/client-programs/pkg/docker/workshop_manager.go b/client-programs/pkg/docker/workshop_manager.go index dbb1adfa8..e03e8ed52 100644 --- a/client-programs/pkg/docker/workshop_manager.go +++ b/client-programs/pkg/docker/workshop_manager.go @@ -40,11 +40,6 @@ const ( WorkshopStatusStarting = "Starting" WorkshopStatusRunning = "Running" WorkshopStatusStopping = "Stopping" - - // Workshop label constants - LabelURL = "training.educates.dev/url" - LabelSource = "training.educates.dev/source" - LabelSession = "training.educates.dev/session" ) const containerScript = `exec bash -s << "EOF" @@ -178,9 +173,9 @@ func (m *DockerWorkshopsManager) ListWorkshops() ([]DockerWorkshopDetails, error return nil, err } - containers, err := cli.ContainerList(ctx, container.ListOptions{}) + containers, err := cli.ContainerList(ctx, container.ListOptions{Filters: getWorkshopContainerLabelFilters()}) if err != nil { - return nil, errors.Wrap(err, "unable to list containers") + return nil, errors.Wrap(err, "unable to list Educates workshop containers") } // Copy statuses while holding lock briefly @@ -198,9 +193,9 @@ func (m *DockerWorkshopsManager) ListWorkshops() ([]DockerWorkshopDetails, error m.StatusesMutex.Unlock() for _, ctr := range containers { - url, found := ctr.Labels[LabelURL] - source := ctr.Labels[LabelSource] - instance := ctr.Labels[LabelSession] + url, found := ctr.Labels[constants.EducatesWorkshopLabelAnnotationURL] + source := ctr.Labels[constants.EducatesWorkshopLabelAnnotationSource] + instance := ctr.Labels[constants.EducatesWorkshopLabelAnnotationSession] status := WorkshopStatusRunning if details, statusFound := statusesCopy[instance]; statusFound { @@ -370,7 +365,7 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployConfig, s defer m.ClearWorkshopStatus(name) - originalName := workshop.GetAnnotations()["training.educates.dev/workshop"] + originalName := workshop.GetAnnotations()[constants.EducatesWorkshopLabelAnnotationWorkshop] configFileDir := utils.GetEducatesHomeDir() composeConfigDir := path.Join(configFileDir, "compose", name) @@ -981,8 +976,10 @@ func generateWorkshopLabels(workshop *unstructured.Unstructured, host string, po domain := fmt.Sprintf("%s.nip.io", strings.ReplaceAll(host, ".", "-")) - labels[LabelURL] = fmt.Sprintf("http://workshop.%s:%d", domain, port) - labels[LabelSession] = workshop.GetName() + labels[constants.EducatesContainersAppLabelKey] = constants.EducatesContainersAppLabel + labels[constants.EducatesContainersRoleLabelKey] = constants.EducatesContainersWorkshopRoleLabel + labels[constants.EducatesWorkshopLabelAnnotationURL] = fmt.Sprintf("http://workshop.%s:%d", domain, port) + labels[constants.EducatesWorkshopLabelAnnotationSession] = workshop.GetName() return labels, nil } @@ -1064,3 +1061,9 @@ func generateClusterKubeconfig(name string) (string, error) { return string(kubeConfigData), nil } +func getWorkshopContainerLabelFilters() filters.Args { + return filters.NewArgs( + filters.Arg("label", constants.EducatesContainersAppLabelKey+"="+constants.EducatesContainersAppLabel), + filters.Arg("label", constants.EducatesContainersRoleLabelKey+"="+constants.EducatesContainersWorkshopRoleLabel), + ) +} diff --git a/client-programs/pkg/educates/local/workshops/manager.go b/client-programs/pkg/educates/local/workshops/manager.go index fb75f55a2..b75d4dc64 100644 --- a/client-programs/pkg/educates/local/workshops/manager.go +++ b/client-programs/pkg/educates/local/workshops/manager.go @@ -11,6 +11,7 @@ import ( vendirsync "carvel.dev/vendir/pkg/vendir/cmd" yttcmd "carvel.dev/ytt/pkg/cmd/template" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" eduk8sWorkshops "github.com/educates/educates-training-platform/client-programs/pkg/educates/resources/workshops" "github.com/educates/educates-training-platform/client-programs/pkg/logger" "github.com/educates/educates-training-platform/client-programs/pkg/templates" @@ -134,7 +135,7 @@ func (m *WorkshopManager) Export(directory string,o *WorkshopExportConfig) (stri return "", errors.Wrap(err, "couldn't parse workshop definition") } - if workshop.GetAPIVersion() != "training.educates.dev/v1beta1" || workshop.GetKind() != "Workshop" { + if workshop.GetAPIVersion() != constants.EducatesTrainingAPIGroupVersion || workshop.GetKind() != "Workshop" { return "", errors.New("invalid type for workshop definition") } @@ -213,7 +214,7 @@ func (m *WorkshopManager) Publish(directory string,o *WorkshopPublishConfig) err carvelUI.PrintLinef("Processing workshop with name %q", workshop.GetName()) - if workshop.GetAPIVersion() != "training.educates.dev/v1beta1" || workshop.GetKind() != "Workshop" { + if workshop.GetAPIVersion() != constants.EducatesTrainingAPIGroupVersion || workshop.GetKind() != "Workshop" { return errors.New("invalid type for workshop definition") } diff --git a/client-programs/pkg/educates/resources/portal/manager.go b/client-programs/pkg/educates/resources/portal/manager.go index 5f5c4372d..df2b45385 100644 --- a/client-programs/pkg/educates/resources/portal/manager.go +++ b/client-programs/pkg/educates/resources/portal/manager.go @@ -107,7 +107,7 @@ func (m *PortalManager) CreateTrainingPortal(cfg *TrainingPortalCreateConfig) er } trainingPortal.SetUnstructuredContent(map[string]interface{}{ - "apiVersion": "training.educates.dev/v1beta1", + "apiVersion": constants.EducatesTrainingAPIGroupVersion, "kind": "TrainingPortal", "metadata": map[string]interface{}{ "name": cfg.Portal, diff --git a/client-programs/pkg/educates/resources/sessions/manager.go b/client-programs/pkg/educates/resources/sessions/manager.go index 136c87040..e8bf970fb 100644 --- a/client-programs/pkg/educates/resources/sessions/manager.go +++ b/client-programs/pkg/educates/resources/sessions/manager.go @@ -5,6 +5,7 @@ import ( "fmt" "github.com/educates/educates-training-platform/client-programs/pkg/cluster" + "github.com/educates/educates-training-platform/client-programs/pkg/constants" educatesrestapi "github.com/educates/educates-training-platform/client-programs/pkg/educates/restapi" educatesTypes "github.com/educates/educates-training-platform/client-programs/pkg/educates/types" "github.com/educates/educates-training-platform/client-programs/pkg/utils" @@ -61,11 +62,11 @@ func (m *SessionManager) ListSessions(cfg ListSessionsConfig) (string, error) { for _, item := range workshopSessions.Items { labels := item.GetLabels() - portal, ok := labels["training.educates.dev/portal.name"] + portal, ok := labels[constants.EducatesTrainingLabelAnnotationPortalName] if ok && portal == cfg.Portal { if cfg.Environment != "" { - environment, ok := labels["training.educates.dev/environment.name"] + environment, ok := labels[constants.EducatesTrainingLabelAnnotationEnvironmentName] if ok && environment == cfg.Environment { sessions = append(sessions, item) @@ -85,8 +86,8 @@ func (m *SessionManager) ListSessions(cfg ListSessionsConfig) (string, error) { for _, item := range sessions { name := item.GetName() labels := item.GetLabels() - portal := labels["training.educates.dev/portal.name"] - environment := labels["training.educates.dev/environment.name"] + portal := labels[constants.EducatesTrainingLabelAnnotationPortalName] + environment := labels[constants.EducatesTrainingLabelAnnotationEnvironmentName] status, _, _ := unstructured.NestedString(item.Object, "status", "educates", "phase") diff --git a/client-programs/pkg/educates/resources/workshops/manager.go b/client-programs/pkg/educates/resources/workshops/manager.go index 858d528c5..4d08d3e4b 100644 --- a/client-programs/pkg/educates/resources/workshops/manager.go +++ b/client-programs/pkg/educates/resources/workshops/manager.go @@ -97,7 +97,7 @@ func (m *WorkshopManager) DeployWorkshopResource(o *DeployWorkshopConfig) error trainingPortal = &unstructured.Unstructured{} trainingPortal.SetUnstructuredContent(map[string]interface{}{ - "apiVersion": "training.educates.dev/v1beta1", + "apiVersion": constants.EducatesTrainingAPIGroupVersion, "kind": "TrainingPortal", "metadata": map[string]interface{}{ "name": o.Portal, @@ -483,7 +483,7 @@ func (m *WorkshopManager) ListWorkshopResources(o *ListWorkshopResourcesConfig) if err == nil { annotations := workshop.GetAnnotations() - if val, ok := annotations["training.educates.dev/source"]; ok { + if val, ok := annotations[constants.EducatesWorkshopLabelAnnotationSource]; ok { source = val } } @@ -624,7 +624,7 @@ func LoadWorkshopDefinition(o *WorkshopDefinitionConfig) (*unstructured.Unstruct // Verify the type of resource definition. - if workshop.GetAPIVersion() != "training.educates.dev/v1beta1" || workshop.GetKind() != "Workshop" { + if workshop.GetAPIVersion() != constants.EducatesTrainingAPIGroupVersion || workshop.GetKind() != "Workshop" { return nil, errors.New("invalid type for workshop definition") } @@ -636,12 +636,12 @@ func LoadWorkshopDefinition(o *WorkshopDefinitionConfig) (*unstructured.Unstruct annotations = map[string]string{} } - annotations["training.educates.dev/workshop"] = workshop.GetName() + annotations[constants.EducatesWorkshopLabelAnnotationWorkshop] = workshop.GetName() if urlInfo.Scheme != "http" && urlInfo.Scheme != "https" { - annotations["training.educates.dev/source"] = fmt.Sprintf("file://%s", o.Path) + annotations[constants.EducatesWorkshopLabelAnnotationSource] = fmt.Sprintf("file://%s", o.Path) } else { - annotations["training.educates.dev/source"] = o.Path + annotations[constants.EducatesWorkshopLabelAnnotationSource] = o.Path } workshop.SetAnnotations(annotations) diff --git a/client-programs/pkg/registry/base.go b/client-programs/pkg/registry/base.go index f8e656fc8..fd2498fde 100644 --- a/client-programs/pkg/registry/base.go +++ b/client-programs/pkg/registry/base.go @@ -11,10 +11,12 @@ import ( "path" "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" "github.com/docker/docker/api/types/image" "github.com/docker/docker/api/types/network" "github.com/docker/docker/client" "github.com/docker/go-connections/nat" + "github.com/educates/educates-training-platform/client-programs/pkg/config" "github.com/educates/educates-training-platform/client-programs/pkg/constants" "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" @@ -147,7 +149,9 @@ func (b *baseContainer) stopAndRemoveContainer(cli *client.Client) error { } timeout := 30 - err := cli.ContainerStop(ctx, b.containerName, container.StopOptions{Timeout: &timeout}) + err := cli.ContainerStop(ctx, b.containerName, container.StopOptions{ + Timeout: &timeout, + }) if err != nil { return errors.Wrap(err, "unable to stop container") } @@ -287,3 +291,27 @@ func tarFile(fileContent []byte, basePath string, fileMode int64) (*bytes.Buffer return buffer, nil } + +func getRegistryMirrorLabelFilters() filters.Args { + return filters.NewArgs( + filters.Arg("label", constants.EducatesContainersRoleLabelKey+"="+constants.EducatesContainersMirrorRoleLabel), + filters.Arg("label", constants.EducatesContainersAppLabelKey+"="+constants.EducatesContainersAppLabel), + ) +} + +func newRegistryContainerLabels() map[string]string { + return map[string]string{ + constants.EducatesContainersRoleLabelKey: constants.EducatesContainersRegistryRoleLabel, + constants.EducatesContainersAppLabelKey: constants.EducatesContainersAppLabel, + } +} + +func newMirrorContainerLabels(mirrorConfig *config.RegistryMirrorConfig) map[string]string { + return map[string]string{ + constants.EducatesContainersRoleLabelKey: constants.EducatesContainersMirrorRoleLabel, + constants.EducatesContainersAppLabelKey: constants.EducatesContainersAppLabel, + constants.EducatesContainersMirrorLabelKey: mirrorConfig.Mirror, + constants.EducatesContainersURLLabelKey: mirrorConfig.URL, + constants.EducatesContainersUsernameLabelKey: mirrorConfig.Username, + } +} diff --git a/client-programs/pkg/registry/mirror.go b/client-programs/pkg/registry/mirror.go index fb6936fb6..59cc035c2 100644 --- a/client-programs/pkg/registry/mirror.go +++ b/client-programs/pkg/registry/mirror.go @@ -6,7 +6,6 @@ import ( "fmt" "github.com/docker/docker/api/types/container" - "github.com/docker/docker/api/types/filters" "github.com/docker/docker/client" "github.com/educates/educates-training-platform/client-programs/pkg/config" "github.com/educates/educates-training-platform/client-programs/pkg/constants" @@ -31,13 +30,7 @@ func NewMirror(mirrorConfig *config.RegistryMirrorConfig) *Mirror { containerName: fmt.Sprintf("%s-mirror-%s", constants.EducatesRegistryContainer, mirrorConfig.Mirror), bindIP: "127.0.0.1", hostPort: "", // dynamic port - labels: map[string]string{ - "app": constants.EducatesAppLabel, - "role": constants.EducatesMirrorRoleLabel, - "mirror": mirrorConfig.Mirror, - "url": mirrorConfig.URL, - "username": mirrorConfig.Username, - }, + labels: newMirrorContainerLabels(mirrorConfig), envVars: buildMirrorEnvVars(mirrorConfig), }, config: mirrorConfig, @@ -168,10 +161,7 @@ func DeleteRegistryMirrors() error { } mirrors, err := cli.ContainerList(ctx, container.ListOptions{ - Filters: filters.NewArgs( - filters.Arg("label", "role="+constants.EducatesMirrorRoleLabel), - filters.Arg("label", "app="+constants.EducatesAppLabel), - ), + Filters: getRegistryMirrorLabelFilters(), }) if err != nil { return errors.Wrap(err, "unable to list registry mirrors") @@ -203,7 +193,9 @@ func ListRegistryMirrors() (string, error) { return "", errors.Wrap(err, "unable to create docker client") } - mirrors, err := cli.ContainerList(ctx, container.ListOptions{Filters: filters.NewArgs(filters.Arg("label", "role="+constants.EducatesMirrorRoleLabel), filters.Arg("label", "app="+constants.EducatesAppLabel))}) + mirrors, err := cli.ContainerList(ctx, container.ListOptions{ + Filters: getRegistryMirrorLabelFilters(), + }) if err != nil { return "", errors.Wrap(err, "unable to list registry mirrors") } diff --git a/client-programs/pkg/registry/registry.go b/client-programs/pkg/registry/registry.go index 9ad71baab..bafdbc602 100644 --- a/client-programs/pkg/registry/registry.go +++ b/client-programs/pkg/registry/registry.go @@ -34,10 +34,7 @@ func NewRegistry(bindIP string, k8sClient *kubernetes.Clientset) *Registry { containerName: constants.EducatesRegistryContainer, bindIP: bindIP, hostPort: "5001", - labels: map[string]string{ - "app": constants.EducatesAppLabel, - "role": constants.EducatesRegistryRoleLabel, - }, + labels: newRegistryContainerLabels(), }, k8sClient: k8sClient, } diff --git a/client-programs/pkg/resolver/resolver.go b/client-programs/pkg/resolver/resolver.go index 953a09fc3..bfbbe6ec8 100644 --- a/client-programs/pkg/resolver/resolver.go +++ b/client-programs/pkg/resolver/resolver.go @@ -105,6 +105,7 @@ func DeployResolver(domain string, targetAddress string, extraDomains []string) ExposedPorts: nat.PortSet{ "53/udp": struct{}{}, }, + Labels: newResolverContainerLabels(), }, hostConfig, nil, nil, constants.EducatesResolverContainerName) if err != nil { @@ -252,3 +253,10 @@ func generateDnsmasqConfig(domain string, targetAddress string, extraDomains []s return configFileName, nil } + +func newResolverContainerLabels() map[string]string { + return map[string]string{ + constants.EducatesContainersRoleLabelKey: constants.EducatesContainersResolverRoleLabel, + constants.EducatesContainersAppLabelKey: constants.EducatesContainersAppLabel, + } +} diff --git a/client-programs/pkg/secrets/secrets.go b/client-programs/pkg/secrets/secrets.go index f0b8bad81..2591332dd 100644 --- a/client-programs/pkg/secrets/secrets.go +++ b/client-programs/pkg/secrets/secrets.go @@ -41,7 +41,7 @@ func LocalCachedSecretForIngressDomain(domain string) string { annotations := secretObj.ObjectMeta.Annotations // Domain name must match. - if val, found := annotations["training.educates.dev/domain"]; !found || val != domain { + if val, found := annotations[constants.EducatesTrainingLabelAnnotationDomain]; !found || val != domain { continue } @@ -84,7 +84,7 @@ func LocalCachedSecretForCertificateAuthority(domain string) string { annotations := secretObj.ObjectMeta.Annotations // Domain name must match. - if val, found := annotations["training.educates.dev/domain"]; !found || val != domain { + if val, found := annotations[constants.EducatesTrainingLabelAnnotationDomain]; !found || val != domain { continue } From 8b9b7e721a0c1ee65b74ce9ea43c300b84072983 Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Sat, 31 Jan 2026 19:37:16 +0100 Subject: [PATCH 23/24] Missing flags to docker_workshop_deploy and allow port mapping for services in compose --- client-programs/go.mod | 2 +- client-programs/go.sum | 4 ++-- client-programs/pkg/cmd/docker_workshop_deploy_cmd.go | 3 +++ client-programs/pkg/docker/workshop_manager.go | 5 ++--- go.work.sum | 4 ++-- 5 files changed, 10 insertions(+), 8 deletions(-) diff --git a/client-programs/go.mod b/client-programs/go.mod index b399b883f..e677d6dde 100644 --- a/client-programs/go.mod +++ b/client-programs/go.mod @@ -9,7 +9,7 @@ require ( carvel.dev/vendir v0.44.0 carvel.dev/ytt v0.52.1 github.com/adrg/xdg v0.5.3 - github.com/compose-spec/compose-go/v2 v2.10.0 + github.com/compose-spec/compose-go/v2 v2.10.1 github.com/cppforlife/go-cli-ui v0.0.0-20250603184554-47874c9078ad // Docker packages must be kept aligned with docker/compose v5.0.1 requirements. This still relies on docker/docker v28.5.2 github.com/docker/docker v28.5.2+incompatible diff --git a/client-programs/go.sum b/client-programs/go.sum index e14880223..85248def8 100644 --- a/client-programs/go.sum +++ b/client-programs/go.sum @@ -140,8 +140,8 @@ github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004 h1:lkAMpLVBDaj17e github.com/cloudflare/cfssl v0.0.0-20180223231731-4e2dcbde5004/go.mod h1:yMWuSON2oQp+43nFtAV/uvKQIFpSPerB57DCt9t8sSA= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb h1:EDmT6Q9Zs+SbUoc7Ik9EfrFqcylYqgPZ9ANSbTAntnE= github.com/codahale/rfc6979 v0.0.0-20141003034818-6a90f24967eb/go.mod h1:ZjrT6AXHbDs86ZSdt/osfBi5qfexBrKUdONk989Wnk4= -github.com/compose-spec/compose-go/v2 v2.10.0 h1:K2C5LQ3KXvkYpy5N/SG6kIYB90iiAirA9btoTh/gB0Y= -github.com/compose-spec/compose-go/v2 v2.10.0/go.mod h1:Ohac1SzhO/4fXXrzWIztIVB6ckmKBv1Nt5Z5mGVESUg= +github.com/compose-spec/compose-go/v2 v2.10.1 h1:mFbXobojGRFIVi1UknrvaDAZ+PkJfyjqkA1yseh+vAU= +github.com/compose-spec/compose-go/v2 v2.10.1/go.mod h1:Ohac1SzhO/4fXXrzWIztIVB6ckmKBv1Nt5Z5mGVESUg= github.com/containerd/cgroups/v3 v3.1.0 h1:azxYVj+91ZgSnIBp2eI3k9y2iYQSR/ZQIgh9vKO+HSY= github.com/containerd/cgroups/v3 v3.1.0/go.mod h1:SA5DLYnXO8pTGYiAHXz94qvLQTKfVM5GEVisn4jpins= github.com/containerd/console v1.0.5 h1:R0ymNeydRqH2DmakFNdmjR2k0t7UPuiOV/N/27/qqsc= diff --git a/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go b/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go index aaff367fb..41ee0e235 100644 --- a/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go +++ b/client-programs/pkg/cmd/docker_workshop_deploy_cmd.go @@ -64,6 +64,9 @@ func (o *DockerWorkshopDeployOptions) Run(cmd *cobra.Command) error { DisableOpenBrowser: o.DisableOpenBrowser, ImageRepository: o.ImageRepository, ImageVersion: o.ImageVersion, + WorkshopFile: o.WorkshopFile, + WorkshopVersion: o.WorkshopVersion, + DataValuesFlags: o.DataValuesFlags, } _, err := dockerWorkshopsManager.DeployWorkshop(&config, cmd.OutOrStdout(), cmd.OutOrStderr()) diff --git a/client-programs/pkg/docker/workshop_manager.go b/client-programs/pkg/docker/workshop_manager.go index e03e8ed52..e0c7db4e9 100644 --- a/client-programs/pkg/docker/workshop_manager.go +++ b/client-programs/pkg/docker/workshop_manager.go @@ -572,12 +572,11 @@ func (m *DockerWorkshopsManager) DeployWorkshop(o *DockerWorkshopDeployConfig, s if workshopComposeProject != nil { for serviceName, extraService := range workshopComposeProject.Services { - extraService.Ports = []composetypes.ServicePortConfig{} - + // TODO: Maybe modify extraService.Ports to add the host IP composeConfig.Services[serviceName] = extraService workshopServiceConfig.DependsOn[serviceName] = composetypes.ServiceDependency{ - Condition: composetypes.ServiceConditionStarted, + Condition: composetypes.ServiceConditionHealthy, } } diff --git a/go.work.sum b/go.work.sum index 7cf4f20ec..1b0134b4f 100644 --- a/go.work.sum +++ b/go.work.sum @@ -325,6 +325,8 @@ github.com/cncf/xds/go v0.0.0-20250501225837-2ac532fd4443/go.mod h1:W+zGtBO5Y1Ig github.com/cncf/xds/go v0.0.0-20251022180443-0feb69152e9f/go.mod h1:HlzOvOjVBOfTGSRXRyY0OiCS/3J1akRGQQpRO/7zyF4= github.com/compose-spec/compose-go/v2 v2.9.1 h1:8UwI+ujNU+9Ffkf/YgAm/qM9/eU7Jn8nHzWG721W4rs= github.com/compose-spec/compose-go/v2 v2.9.1/go.mod h1:Oky9AZGTRB4E+0VbTPZTUu4Kp+oEMMuwZXZtPPVT1iE= +github.com/compose-spec/compose-go/v2 v2.10.1 h1:mFbXobojGRFIVi1UknrvaDAZ+PkJfyjqkA1yseh+vAU= +github.com/compose-spec/compose-go/v2 v2.10.1/go.mod h1:Ohac1SzhO/4fXXrzWIztIVB6ckmKBv1Nt5Z5mGVESUg= github.com/container-storage-interface/spec v1.8.0/go.mod h1:ROLik+GhPslwwWRNFF1KasPzroNARibH2rfz1rkg4H0= github.com/container-storage-interface/spec v1.9.0/go.mod h1:ZfDu+3ZRyeVqxZM0Ds19MVLkN2d1XJ5MAfi1L3VjlT0= github.com/containerd/accelerated-container-image v1.3.0/go.mod h1:EvKVWor6ZQNUyYp0MZm5hw4k21ropuz7EegM+m/Jb/Q= @@ -1433,8 +1435,6 @@ sigs.k8s.io/json v0.0.0-20211208200746-9f7c6b3444d2/go.mod h1:B+TnT182UBxE84DiCz sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd/go.mod h1:B8JuhiUyNFVKdsE8h686QcCxMaH6HrOAZj4vswFpcB0= sigs.k8s.io/kind v0.29.0 h1:3TpCsyh908IkXXpcSnsMjWdwdWjIl7o9IMZImZCWFnI= sigs.k8s.io/kind v0.29.0/go.mod h1:ldWQisw2NYyM6k64o/tkZng/1qQW7OlzcN5a8geJX3o= -sigs.k8s.io/kind v0.31.0 h1:UcT4nzm+YM7YEbqiAKECk+b6dsvc/HRZZu9U0FolL1g= -sigs.k8s.io/kind v0.31.0/go.mod h1:FSqriGaoTPruiXWfRnUXNykF8r2t+fHtK0P0m1AbGF8= sigs.k8s.io/knftables v0.0.14/go.mod h1:f/5ZLKYEUPUhVjUCg6l80ACdL7CIIyeL0DxfgojGRTk= sigs.k8s.io/knftables v0.0.17/go.mod h1:f/5ZLKYEUPUhVjUCg6l80ACdL7CIIyeL0DxfgojGRTk= sigs.k8s.io/kustomize/api v0.13.5-0.20230601165947-6ce0bf390ce3/go.mod h1:9n16EZKMhXBNSiUC5kSdFQJkdH3zbxS/JoO619G1VAY= From 625520ba07b1150555b5b160bc2cd85ba5156f0b Mon Sep 17 00:00:00 2001 From: Jorge Morales Pou Date: Sun, 1 Feb 2026 13:18:43 +0100 Subject: [PATCH 24/24] Adding multinode support, and labels/taints support for local clusters --- client-programs/pkg/cluster/kindcluster.go | 182 ++++++++++++++++-- .../pkg/cluster/kindclusterconfig.yaml.tpl | 69 ++++++- .../pkg/config/installationconfig.go | 86 +++++++++ client-programs/pkg/registry/base.go | 90 +++++++-- .../getting-started/local-environment.md | 168 +++++++++++++++- 5 files changed, 544 insertions(+), 51 deletions(-) diff --git a/client-programs/pkg/cluster/kindcluster.go b/client-programs/pkg/cluster/kindcluster.go index 311b9ee9b..9f23907cb 100644 --- a/client-programs/pkg/cluster/kindcluster.go +++ b/client-programs/pkg/cluster/kindcluster.go @@ -8,11 +8,15 @@ import ( "html/template" "os" "path/filepath" + "strings" "time" "github.com/docker/docker/api/types/container" + "github.com/docker/docker/api/types/filters" "github.com/pkg/errors" "golang.org/x/exp/slices" + corev1 "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/client-go/tools/clientcmd" "sigs.k8s.io/kind/pkg/cluster" "sigs.k8s.io/kind/pkg/cmd" @@ -146,25 +150,40 @@ func (o *KindClusterConfig) StopCluster() error { return errors.Wrap(err, "unable to create docker client") } - _, err = cli.ContainerInspect(ctx, constants.EducatesControlPlaneContainer) + // Get all kind node containers for the educates cluster + nodeFilters := filters.NewArgs() + nodeFilters.Add("label", fmt.Sprintf("io.x-k8s.kind.cluster=%s", constants.EducatesClusterName)) + containers, err := cli.ContainerList(ctx, container.ListOptions{ + Filters: nodeFilters, + }) if err != nil { - return errors.Wrap(err, "no container for Educates cluster") + return errors.Wrap(err, "failed to list kind node containers") + } + + if len(containers) == 0 { + return errors.New("no containers found for Educates cluster") } fmt.Println("Stopping cluster educates ...") timeout := 30 - if err := cli.ContainerStop(ctx, constants.EducatesControlPlaneContainer, container.StopOptions{Timeout: &timeout}); err != nil { - return errors.Wrapf(err, "failed to stop cluster") - } - - // timeout := time.Duration(30) * time.Second + // Stop all containers (control-plane and workers) + for _, c := range containers { + containerName := c.Names[0] + if len(c.Names) > 0 { + // Remove leading slash from container name + if len(containerName) > 0 && containerName[0] == '/' { + containerName = containerName[1:] + } + } - // if err := cli.ContainerStop(ctx, EducatesControlPlaneContainer, &timeout); err != nil { - // return errors.Wrapf(err, "failed to stop cluster") - // } + if err := cli.ContainerStop(ctx, c.ID, container.StopOptions{Timeout: &timeout}); err != nil { + return errors.Wrapf(err, "failed to stop container %s", containerName) + } + fmt.Printf(" Stopped %s\n", containerName) + } return nil } @@ -185,16 +204,42 @@ func (o *KindClusterConfig) StartCluster() error { return errors.Wrap(err, "unable to create docker client") } - _, err = cli.ContainerInspect(ctx, constants.EducatesControlPlaneContainer) + // Get all kind node containers for the educates cluster + nodeFilters := filters.NewArgs() + nodeFilters.Add("label", fmt.Sprintf("io.x-k8s.kind.cluster=%s", constants.EducatesClusterName)) + containers, err := cli.ContainerList(ctx, container.ListOptions{ + All: true, // Include stopped containers + Filters: nodeFilters, + }) if err != nil { - return errors.Wrap(err, "no container for Educates cluster") + return errors.Wrap(err, "failed to list kind node containers") + } + + if len(containers) == 0 { + return errors.New("no containers found for Educates cluster") } fmt.Println("Starting cluster educates ...") - if err := cli.ContainerStart(ctx, constants.EducatesControlPlaneContainer, container.StartOptions{}); err != nil { - return errors.Wrapf(err, "failed to start cluster") + // Start all containers (control-plane and workers) + for _, c := range containers { + containerName := c.Names[0] + if len(c.Names) > 0 { + // Remove leading slash from container name + if len(containerName) > 0 && containerName[0] == '/' { + containerName = containerName[1:] + } + } + + if c.State != "running" { + if err := cli.ContainerStart(ctx, c.ID, container.StartOptions{}); err != nil { + return errors.Wrapf(err, "failed to start container %s", containerName) + } + fmt.Printf(" Started %s\n", containerName) + } else { + fmt.Printf(" %s already running\n", containerName) + } } return nil @@ -216,20 +261,115 @@ func (o *KindClusterConfig) ClusterStatus() error { return errors.Wrap(err, "unable to create docker client") } - containerJSON, err := cli.ContainerInspect(ctx, constants.EducatesControlPlaneContainer) + // Get all kind node containers for the educates cluster + nodeFilters := filters.NewArgs() + nodeFilters.Add("label", fmt.Sprintf("io.x-k8s.kind.cluster=%s", constants.EducatesClusterName)) + containers, err := cli.ContainerList(ctx, container.ListOptions{ + All: true, + Filters: nodeFilters, + }) if err != nil { - return errors.Wrap(err, "no container for Educates cluster") + return errors.Wrap(err, "failed to list kind node containers") + } + + if len(containers) == 0 { + return errors.New("no containers found for Educates cluster") } - if containerJSON.State.Running { + // Check if all containers are running + allRunning := true + for _, c := range containers { + if c.State != "running" { + allRunning = false + break + } + } + + if allRunning { fmt.Println("Educates cluster is Running") - // if ip, err := config.HostIP(); err == nil { - // fmt.Println(" Cluster IP: ", ip) - // } } else { - fmt.Println("Educates cluster is NOT Running") + fmt.Println("Educates cluster is NOT Running (some containers stopped)") + return nil } + // Get Kubernetes client to query nodes + k8sClient, err := o.Config.GetClient() + if err != nil { + fmt.Println(" Warning: Unable to connect to Kubernetes API") + return nil + } + + // List nodes from Kubernetes API + nodes, err := k8sClient.CoreV1().Nodes().List(ctx, metav1.ListOptions{}) + if err != nil { + fmt.Println(" Warning: Unable to list nodes from Kubernetes") + return nil + } + + var formattedData [][]string + + for _, node := range nodes.Items { + var customLabelsData []string + var taintsData []string + // Determine role + role := "worker" + if _, ok := node.Labels["node-role.kubernetes.io/control-plane"]; ok { + role = "control-plane" + } else if _, ok := node.Labels["node-role.kubernetes.io/master"]; ok { + role = "control-plane" + } + + // Get status + status := "Unknown" + for _, condition := range node.Status.Conditions { + if condition.Type == corev1.NodeReady { + if condition.Status == corev1.ConditionTrue { + status = "Ready" + } else { + status = "NotReady" + } + break + } + } + + // Get version + version := node.Status.NodeInfo.KubeletVersion + + // Show custom labels (exclude system labels) + customLabels := make(map[string]string) + for k, v := range node.Labels { + if !strings.HasPrefix(k, "node-role.kubernetes.io/") && + !strings.HasPrefix(k, "kubernetes.io/") && + !strings.HasPrefix(k, "beta.kubernetes.io/"){ + // && k != "ingress-ready" { + customLabels[k] = v + } + } + + if len(customLabels) > 0 { + for k, v := range customLabels { + customLabelsData = append(customLabelsData, fmt.Sprintf("%s=%s", k, v)) + } + } + + // Show taints + if len(node.Spec.Taints) > 0 { + for _, taint := range node.Spec.Taints { + if taint.Value != "" { + taintsData = append(taintsData, fmt.Sprintf("%s=%s:%s", taint.Key, taint.Value, taint.Effect)) + } else { + taintsData = append(taintsData, fmt.Sprintf("%s:%s", taint.Key, taint.Effect)) + } + } + } + formattedData = append(formattedData, []string{node.Name, role, status, version, strings.Join(customLabelsData, ", "), strings.Join(taintsData, ", ")}) + } + + fmt.Println(utils.PrintTable( + []string{"NODE", "ROLE", "STATUS", "VERSION", "LABELS", "TAINTS"}, + formattedData, + )) + return nil } diff --git a/client-programs/pkg/cluster/kindclusterconfig.yaml.tpl b/client-programs/pkg/cluster/kindclusterconfig.yaml.tpl index fecad8584..840ba626a 100644 --- a/client-programs/pkg/cluster/kindclusterconfig.yaml.tpl +++ b/client-programs/pkg/cluster/kindclusterconfig.yaml.tpl @@ -21,6 +21,58 @@ networking: {{- end }} {{- end }} nodes: +{{- if .LocalKindCluster.Nodes }} +{{- range .LocalKindCluster.Nodes }} +- role: {{ .Role }} +{{- if or .Labels .Taints (eq .Role "control-plane") }} + kubeadmConfigPatches: +{{- if eq .Role "control-plane" }} + - | + kind: InitConfiguration + nodeRegistration: + kubeletExtraArgs: +{{- if .Labels }} + node-labels: "{{- range $key, $value := .Labels }}{{ $key }}={{ $value }},{{- end }}ingress-ready=true" +{{- else }} + node-labels: "ingress-ready=true" +{{- end }} +{{- else }} + - | + kind: JoinConfiguration + nodeRegistration: + kubeletExtraArgs: +{{- if .Labels }} + node-labels: "{{- $first := true }}{{- range $key, $value := .Labels }}{{- if not $first }},{{- end }}{{ $key }}={{ $value }}{{- $first = false }}{{- end }}" +{{- end }} +{{- if .Taints }} + register-with-taints: "{{- range $i, $taint := .Taints }}{{- if $i }},{{- end }}{{ $taint.Key }}={{- if $taint.Value }}{{ $taint.Value }}{{- end }}:{{ $taint.Effect }}{{- end }}" +{{- end }} +{{- end }} +{{- end }} +{{- if eq .Role "control-plane" }} + extraPortMappings: + - containerPort: 80 +{{- if $.LocalKindCluster.ListenAddress }} + listenAddress: {{ $.LocalKindCluster.ListenAddress }} +{{- end }} + hostPort: 80 + protocol: TCP + - containerPort: 443 +{{- if $.LocalKindCluster.ListenAddress }} + listenAddress: {{ $.LocalKindCluster.ListenAddress }} +{{- end }} + hostPort: 443 + protocol: TCP +{{- if $.LocalKindCluster.VolumeMounts }} + extraMounts: +{{- range $.LocalKindCluster.VolumeMounts }} + - hostPath: {{ .HostPath }} + containerPath: {{ .ContainerPath }} +{{- end }} +{{- end }} +{{- end }} +{{- end }} +{{- else }} - role: control-plane kubeadmConfigPatches: - | @@ -39,24 +91,25 @@ nodes: {{- end }} extraPortMappings: - containerPort: 80 - {{- if .LocalKindCluster.ListenAddress }} +{{- if .LocalKindCluster.ListenAddress }} listenAddress: {{ .LocalKindCluster.ListenAddress }} - {{- end }} +{{- end }} hostPort: 80 protocol: TCP - containerPort: 443 - {{- if .LocalKindCluster.ListenAddress }} +{{- if .LocalKindCluster.ListenAddress }} listenAddress: {{ .LocalKindCluster.ListenAddress }} - {{- end }} +{{- end }} hostPort: 443 protocol: TCP - {{- if .LocalKindCluster.VolumeMounts }} +{{- if .LocalKindCluster.VolumeMounts }} extraMounts: - {{- range .LocalKindCluster.VolumeMounts }} +{{- range .LocalKindCluster.VolumeMounts }} - hostPath: {{ .HostPath }} containerPath: {{ .ContainerPath }} - {{- end }} - {{- end }} +{{- end }} +{{- end }} +{{- end }} containerdConfigPatches: - |- [plugins."io.containerd.grpc.v1.cri".registry] diff --git a/client-programs/pkg/config/installationconfig.go b/client-programs/pkg/config/installationconfig.go index d3c7230e0..c90f1d881 100644 --- a/client-programs/pkg/config/installationconfig.go +++ b/client-programs/pkg/config/installationconfig.go @@ -17,12 +17,27 @@ type VolumeMountConfig struct { ReadOnly *bool `yaml:"readOnly,omitempty"` } +// NodeConfig defines configuration for a single kind node +type NodeConfig struct { + Role string `yaml:"role"` // "control-plane" or "worker" + Labels map[string]string `yaml:"labels,omitempty"` // Custom labels for the node + Taints []TaintConfig `yaml:"taints,omitempty"` // Taints for the node +} + +// TaintConfig defines a Kubernetes taint +type TaintConfig struct { + Key string `yaml:"key"` // Taint key + Value string `yaml:"value,omitempty"` // Taint value (optional) + Effect string `yaml:"effect"` // NoSchedule, PreferNoSchedule, or NoExecute +} + type LocalKindClusterConfig struct { ListenAddress string `yaml:"listenAddress,omitempty"` ApiServer KindApiServerConfig `yaml:"apiServer,omitempty"` Networking KindNetworkingConfig `yaml:"networking,omitempty"` VolumeMounts []VolumeMountConfig `yaml:"volumeMounts,omitempty"` RegistryMirrors []RegistryMirrorConfig `yaml:"registryMirrors,omitempty"` + Nodes []NodeConfig `yaml:"nodes,omitempty"` // Nodes configuration for multi-node clusters } type RegistryMirrorConfig struct { @@ -464,6 +479,13 @@ func ConfigForLocalClusters(configFile string, domain string, local bool) (fullC return nil, err } + // Validate nodes configuration for kind clusters + if local && fullConfig.ClusterInfrastructure.Provider == "kind" { + if err := ValidateNodesConfig(&fullConfig.LocalKindCluster.Nodes); err != nil { + return nil, errors.Wrap(err, "invalid nodes configuration") + } + } + return fullConfig, nil } @@ -515,3 +537,67 @@ func ValidateProvider(provider string) error { return errors.New("Invalid ClusterInsfrastructure Provider. Valid values are (eks, gke, kind, custom, vcluster, generic, minikube, openshift)") } } + +// ValidateNodesConfig validates the nodes configuration for a kind cluster +func ValidateNodesConfig(nodes *[]NodeConfig) error { + if len(*nodes) == 0 { + // Empty is valid - will use default single control-plane + return nil + } + + controlPlaneCount := 0 + workerCount := 0 + + for i, node := range *nodes { + // Validate role + if node.Role != "control-plane" && node.Role != "worker" { + return errors.Errorf("node %d has invalid role %q, must be 'control-plane' or 'worker'", i, node.Role) + } + + // Count nodes by role + if node.Role == "control-plane" { + controlPlaneCount++ + } else { + workerCount++ + } + + // Validate taints + for j, taint := range node.Taints { + if taint.Key == "" { + return errors.Errorf("node %d taint %d has empty key", i, j) + } + if taint.Effect == "" { + return errors.Errorf("node %d taint %d (%s) has empty effect", i, j, taint.Key) + } + // Validate taint effect + validEffects := map[string]bool{ + "NoSchedule": true, + "PreferNoSchedule": true, + "NoExecute": true, + } + if !validEffects[taint.Effect] { + return errors.Errorf("node %d taint %d (%s) has invalid effect %q, must be 'NoSchedule', 'PreferNoSchedule', or 'NoExecute'", + i, j, taint.Key, taint.Effect) + } + } + } + + // Validate exactly one control-plane + if controlPlaneCount == 0 { + // We add a default control-plane node if no control-plane nodes are configured + *nodes = append(*nodes, NodeConfig{ + Role: "control-plane", + }) + controlPlaneCount = 1 + } + if controlPlaneCount > 1 { + return errors.Errorf("nodes configuration must have exactly one control-plane node, found %d", controlPlaneCount) + } + + // Validate maximum 5 workers + if workerCount > 5 { + return errors.Errorf("nodes configuration supports maximum 5 worker nodes, found %d", workerCount) + } + + return nil +} diff --git a/client-programs/pkg/registry/base.go b/client-programs/pkg/registry/base.go index fd2498fde..7350bb677 100644 --- a/client-programs/pkg/registry/base.go +++ b/client-programs/pkg/registry/base.go @@ -18,7 +18,6 @@ import ( "github.com/docker/go-connections/nat" "github.com/educates/educates-training-platform/client-programs/pkg/config" "github.com/educates/educates-training-platform/client-programs/pkg/constants" - "github.com/educates/educates-training-platform/client-programs/pkg/utils" "github.com/pkg/errors" ) @@ -164,23 +163,38 @@ func (b *baseContainer) stopAndRemoveContainer(cli *client.Client) error { return nil } -// addRegistryConfigToKindNodes adds the registry config to the kind nodes. -// It is used when creating a new local registry or registry mirror. -func addRegistryConfigToKindNodes(repositoryName string, content string) error { +// getEducatesKindNodeContainers returns all kind node containers for the educates cluster +func getEducatesKindNodeContainers(cli *client.Client) ([]string, error) { ctx := context.Background() - fmt.Printf("Adding local image registry config (%s) to Kind nodes\n", repositoryName) + // Kind labels all node containers with io.x-k8s.kind.cluster= + nodeFilters := filters.NewArgs() + nodeFilters.Add("label", fmt.Sprintf("io.x-k8s.kind.cluster=%s", constants.EducatesClusterName)) - cli, err := client.NewClientWithOpts(client.FromEnv) + containers, err := cli.ContainerList(ctx, container.ListOptions{ + All: true, + Filters: nodeFilters, + }) if err != nil { - return errors.Wrap(err, "unable to create docker client") + return nil, errors.Wrap(err, "failed to list kind node containers") } - containerID, _ := utils.GetContainerInfo(constants.EducatesControlPlaneContainer) - if containerID == "" { - return errors.New(fmt.Sprintf("%s container not found", constants.EducatesControlPlaneContainer)) + if len(containers) == 0 { + return nil, errors.New("no kind node containers found for educates cluster") } + containerIDs := make([]string, len(containers)) + for i, c := range containers { + containerIDs[i] = c.ID + } + + return containerIDs, nil +} + +// addRegistryConfigToNode adds the registry config to a single kind node container +func addRegistryConfigToNode(cli *client.Client, containerID, repositoryName, content string) error { + ctx := context.Background() + registryDir := "/etc/containerd/certs.d/" + repositoryName cmdStatement := []string{"mkdir", "-p", registryDir} @@ -219,23 +233,35 @@ func addRegistryConfigToKindNodes(repositoryName string, content string) error { return nil } -// removeRegistryConfigFromKindNodes removes the registry config from the kind nodes. -// It is used when deleting a local registry mirror. -func removeRegistryConfigFromKindNodes(repositoryName string) error { - ctx := context.Background() - - fmt.Printf("Removing local image registry config (%s) from Kind nodes\n", repositoryName) +// addRegistryConfigToKindNodes adds the registry config to all kind nodes. +// It is used when creating a new local registry or registry mirror. +func addRegistryConfigToKindNodes(repositoryName string, content string) error { + fmt.Printf("Adding local image registry config (%s) to Kind nodes\n", repositoryName) cli, err := client.NewClientWithOpts(client.FromEnv) if err != nil { return errors.Wrap(err, "unable to create docker client") } - containerID, _ := utils.GetContainerInfo(constants.EducatesControlPlaneContainer) - if containerID == "" { - return nil + containerIDs, err := getEducatesKindNodeContainers(cli) + if err != nil { + return err + } + + // Apply config to all nodes (control-plane and workers) + for _, containerID := range containerIDs { + if err := addRegistryConfigToNode(cli, containerID, repositoryName, content); err != nil { + return errors.Wrapf(err, "failed to add registry config to node %s", containerID) + } } + return nil +} + +// removeRegistryConfigFromNode removes the registry config from a single kind node container +func removeRegistryConfigFromNode(cli *client.Client, containerID, repositoryName string) error { + ctx := context.Background() + registryDir := "/etc/containerd/certs.d/" + repositoryName cmdStatement := []string{"rm", "-rf", registryDir} @@ -261,6 +287,32 @@ func removeRegistryConfigFromKindNodes(repositoryName string) error { return nil } +// removeRegistryConfigFromKindNodes removes the registry config from all kind nodes. +// It is used when deleting a local registry mirror. +func removeRegistryConfigFromKindNodes(repositoryName string) error { + fmt.Printf("Removing local image registry config (%s) from Kind nodes\n", repositoryName) + + cli, err := client.NewClientWithOpts(client.FromEnv) + if err != nil { + return errors.Wrap(err, "unable to create docker client") + } + + containerIDs, err := getEducatesKindNodeContainers(cli) + if err != nil { + // If nodes don't exist, nothing to remove + return nil + } + + // Remove config from all nodes (control-plane and workers) + for _, containerID := range containerIDs { + if err := removeRegistryConfigFromNode(cli, containerID, repositoryName); err != nil { + return errors.Wrapf(err, "failed to remove registry config from node %s", containerID) + } + } + + return nil +} + // tarFile creates a tar archive with a single file. func tarFile(fileContent []byte, basePath string, fileMode int64) (*bytes.Buffer, error) { buffer := &bytes.Buffer{} diff --git a/project-docs/getting-started/local-environment.md b/project-docs/getting-started/local-environment.md index e82789917..6d3779acb 100644 --- a/project-docs/getting-started/local-environment.md +++ b/project-docs/getting-started/local-environment.md @@ -188,14 +188,14 @@ $ curl -v www.educates-local-dev.test > Host: www.educates-local-dev.test > User-Agent: curl/7.79.1 > Accept: */* -> +> * Mark bundle as not supporting multiuse < HTTP/1.1 404 Not Found < vary: Accept-Encoding < date: Fri, 25 Mar 2022 03:07:19 GMT < server: envoy < content-length: 0 -< +< * Connection #0 to host www.educates-local-dev.test left intact ``` @@ -287,10 +287,172 @@ To delete a local registry mirror, run: educates local mirror delete ghcr.io ``` -This will stop and remove the mirror container and clean up the configuration from the cluster. +This will stop and remove the mirror container and clean up the configuration from the cluster. As pointed out earlier, if the mirror configuration exists in the local cluster's configuration, if the Educates local cluster is recreated then the mirror will be recreated as well. +Multi-node clusters +------------------- + +By default, Educates creates a local Kind cluster with a single control-plane node. For testing scenarios that require multiple nodes, such as workload isolation, resource management, or simulating production-like environments, you can configure the cluster to include additional worker nodes. + +### Default behavior + +When no node configuration is provided, Educates automatically creates a cluster with: +- 1 control-plane node +- The `ingress-ready=true` label (required for ingress controller) +- Port mappings for HTTP (80) and HTTPS (443) + +### Configuring multiple nodes + +To create a multi-node cluster, add a `nodes` section to your local cluster configuration. You can do this by running `educates local config edit` and adding configuration like: + +```yaml +localKindCluster: + nodes: + - role: control-plane + labels: + environment: dev + node-type: control + - role: worker + labels: + tier: frontend + workload-type: web + - role: worker + labels: + tier: backend + workload-type: api +``` + +Or create a configuration file and use it when creating the cluster: + +``` +educates create-cluster --config multi-node-config.yaml +``` + +### Node configuration constraints + +When configuring nodes, the following constraints apply: + +- **Exactly 1 control-plane node** is required (if none is specified, one will be created automatically) +- **Maximum 5 worker nodes** are allowed +- Valid node roles are: `control-plane` or `worker` + +### Adding labels to nodes + +You can add custom labels to nodes to organize and identify them. Labels are key-value pairs that can be used with Kubernetes node selectors and affinity rules. + +```yaml +localKindCluster: + nodes: + - role: control-plane + labels: + environment: production + region: local + - role: worker + labels: + tier: frontend + disk-type: ssd + - role: worker + labels: + tier: backend + disk-type: hdd +``` + +The control-plane node will automatically have the `ingress-ready: true` label added in addition to any custom labels you specify. + +### Adding taints to nodes + +Taints allow you to mark nodes so that only pods with matching tolerations can be scheduled on them. This is useful for dedicating nodes to specific workloads. + +```yaml +localKindCluster: + nodes: + - role: control-plane + - role: worker + labels: + tier: frontend + - role: worker + labels: + tier: backend + taints: + - key: dedicated + value: backend + effect: NoSchedule + - role: worker + labels: + tier: database + taints: + - key: dedicated + value: database + effect: NoSchedule + - key: storage + value: "true" + effect: NoExecute +``` + +Valid taint effects are: +- `NoSchedule`: Pods will not be scheduled on the node unless they have a matching toleration +- `PreferNoSchedule`: Kubernetes will try to avoid scheduling pods on the node, but it's not required +- `NoExecute`: Existing pods without matching tolerations will be evicted from the node + +### Registry and mirror configuration + +When you create a multi-node cluster, the local image registry and any configured registry mirrors are automatically configured on **all nodes** (both control-plane and workers). This ensures that all nodes can pull images from the local registry and benefit from cached images in the mirrors. + +You don't need to do anything special - the registry configuration at `/etc/containerd/certs.d/` is automatically applied to every node in the cluster when the cluster is created. + +### Example: Complete multi-node configuration + +Here's a complete example showing a 4-node cluster with labels, taints, and registry mirrors: + +```yaml +localKindCluster: + nodes: + - role: control-plane + labels: + environment: dev + node-type: control + - role: worker + labels: + tier: frontend + workload-type: web + - role: worker + labels: + tier: backend + workload-type: api + taints: + - key: dedicated + value: backend + effect: NoSchedule + - role: worker + labels: + tier: database + workload-type: database + taints: + - key: dedicated + value: database + effect: NoSchedule + registryMirrors: + - mirror: ghcr.io + - mirror: docker.io + url: registry-1.docker.io +``` + +This configuration creates: +- 1 control-plane node with custom labels +- 1 frontend worker (no taints - accepts all workloads) +- 1 backend worker with a taint requiring pods to tolerate `dedicated=backend:NoSchedule` +- 1 database worker with a taint requiring pods to tolerate `dedicated=database:NoSchedule` +- Registry mirrors for GitHub Container Registry and Docker Hub configured on all nodes + +### Viewing node information + +After creating a multi-node cluster, you can view detailed information about the cluster status and all the nodes including their labels and taints by running: + +``` +educates local cluster status +``` Customize local pod and service CIDRs -------------------------------------