From f0f826a8aeb71bd434ee855833b21a6270f2dc0b Mon Sep 17 00:00:00 2001 From: Ryotaro Banno Date: Mon, 26 Aug 2024 02:39:10 +0000 Subject: [PATCH 1/3] support gRPC communication between primary and secondary mantle controllers This commit allows a mantle-controller to serve as one of standalone, primary, or secondary mode. This commit doesn't have any e2e test's update and it will land on in the following commits. Signed-off-by: Ryotaro Banno --- .gitignore | 1 + Dockerfile | 1 + Makefile | 51 ++- api/v1/mantlebackup_types.go | 3 +- charts/mantle/templates/deployment.yaml | 6 + charts/mantle/values.yaml | 4 +- cmd/controller/main.go | 214 +++++++++-- docs/controller-protocol.md | 121 ++++++ go.mod | 45 ++- go.sum | 91 +++-- .../controller/mantlebackup_controller.go | 43 ++- .../mantlebackup_controller_test.go | 2 +- .../controller/mantlerestore_controller.go | 11 +- .../mantlerestore_controller_test.go | 4 +- internal/controller/replication.go | 40 ++ pkg/controller/proto/controller.pb.go | 349 ++++++++++++++++++ pkg/controller/proto/controller.proto | 30 ++ pkg/controller/proto/controller_grpc.pb.go | 159 ++++++++ tools_test.go | 11 + 19 files changed, 1078 insertions(+), 108 deletions(-) create mode 100644 docs/controller-protocol.md create mode 100644 internal/controller/replication.go create mode 100644 pkg/controller/proto/controller.pb.go create mode 100644 pkg/controller/proto/controller.proto create mode 100644 pkg/controller/proto/controller_grpc.pb.go create mode 100644 tools_test.go diff --git a/.gitignore b/.gitignore index f8847a3..a3b201b 100644 --- a/.gitignore +++ b/.gitignore @@ -26,3 +26,4 @@ Dockerfile.cross *~ test/e2e/testdata/persistentvolumes.yaml +include/ diff --git a/Dockerfile b/Dockerfile index 7dfafff..8e276d6 100644 --- a/Dockerfile +++ b/Dockerfile @@ -14,6 +14,7 @@ COPY main.go main.go COPY cmd/ cmd/ COPY api/ api/ COPY internal/ internal/ +COPY pkg/ pkg/ # Build # the GOARCH has not a default value to allow the binary be built according to the host where the command diff --git a/Makefile b/Makefile index 5aaaa31..ae9eaff 100644 --- a/Makefile +++ b/Makefile @@ -1,5 +1,22 @@ include versions.mk +## Location to install dependencies to +LOCALBIN ?= $(shell pwd)/bin +$(LOCALBIN): + mkdir -p $(LOCALBIN) + +## Tool Binaries +KUBECTL ?= $(LOCALBIN)/kubectl +KUSTOMIZE ?= $(LOCALBIN)/kustomize +CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen +ENVTEST ?= $(LOCALBIN)/setup-envtest +MOCKGEN ?= $(LOCALBIN)/mockgen +PROTOC ?= $(LOCALBIN)/protoc +PROTOC_RUN ?= PATH=$(LOCALBIN):$(PATH) $(PROTOC) -I=$(shell pwd)/include:. + +# Files to be generated by protoc +PROTOBUF_GEN = pkg/controller/proto/controller.pb.go pkg/controller/proto/controller_grpc.pb.go docs/controller-protocol.md + # Image URL to use all building/pushing image targets IMG ?= controller:latest @@ -52,7 +69,7 @@ manifests: controller-gen ## Generate WebhookConfiguration, ClusterRole and Cust cat config/rbac/role.yaml | yq '.metadata.name = "mantle-controller"' > charts/mantle-cluster-wide/templates/clusterrole.yaml .PHONY: generate -generate: controller-gen ## Generate code containing DeepCopy, DeepCopyInto, and DeepCopyObject method implementations. +generate: $(PROTOBUF_GEN) controller-gen ## Generate boilerplate code. $(CONTROLLER_GEN) object paths="./..." .PHONY: fmt @@ -151,17 +168,29 @@ undeploy: kubectl ## Undeploy controller from the K8s cluster specified in ~/.ku ##@ Build Dependencies -## Location to install dependencies to -LOCALBIN ?= $(shell pwd)/bin -$(LOCALBIN): - mkdir -p $(LOCALBIN) +pkg/controller/proto/controller.pb.go: pkg/controller/proto/controller.proto $(PROTOC) + $(PROTOC_RUN) --go_out=module=github.com/cybozu-go/mantle:. $< -## Tool Binaries -KUBECTL ?= $(LOCALBIN)/kubectl -KUSTOMIZE ?= $(LOCALBIN)/kustomize -CONTROLLER_GEN ?= $(LOCALBIN)/controller-gen -ENVTEST ?= $(LOCALBIN)/setup-envtest -MOCKGEN ?= $(LOCALBIN)/mockgen +pkg/controller/proto/controller_grpc.pb.go: pkg/controller/proto/controller.proto $(PROTOC) + $(PROTOC_RUN) --go-grpc_out=module=github.com/cybozu-go/mantle:. $< + +docs/controller-protocol.md: pkg/controller/proto/controller.proto $(PROTOC) + $(PROTOC_RUN) --doc_out=./docs --doc_opt=markdown,$@ $< + +.PHONY: $(PROTOC) +$(PROTOC): $(LOCALBIN) + [ -f $(PROTOC) ] || { \ + set -e ;\ + curl -Lo $(LOCALBIN)/protoc.zip https://github.com/protocolbuffers/protobuf/releases/download/v$(PROTOC_VERSION)/protoc-$(PROTOC_VERSION)-linux-x86_64.zip ;\ + rm -rf $(LOCALBIN)/protoc.unzip ;\ + unzip $(LOCALBIN)/protoc.zip -d $(LOCALBIN)/protoc.unzip ;\ + cp -f $(LOCALBIN)/protoc.unzip/bin/protoc $(PROTOC) ;\ + cp -r $(LOCALBIN)/protoc.unzip/include . ;\ + rm -rf $(LOCALBIN)/protoc.unzip $(LOCALBIN)/protoc.zip ;\ + GOBIN=$(LOCALBIN) go install google.golang.org/protobuf/cmd/protoc-gen-go@v$(PROTOC_GEN_GO_VERSION) ;\ + GOBIN=$(LOCALBIN) go install google.golang.org/grpc/cmd/protoc-gen-go-grpc@v$(PROTOC_GEN_GO_GRPC_VERSION) ;\ + GOBIN=$(LOCALBIN) go install github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc@v$(PROTOC_GEN_DOC_VERSION) ;\ + } .PHONY: kustomize kustomize: $(KUSTOMIZE) ## Download kustomize locally if necessary. If wrong version is installed, it will be removed before downloading. diff --git a/api/v1/mantlebackup_types.go b/api/v1/mantlebackup_types.go index 5dcaad2..243c2c4 100644 --- a/api/v1/mantlebackup_types.go +++ b/api/v1/mantlebackup_types.go @@ -39,7 +39,8 @@ type MantleBackupStatus struct { } const ( - BackupConditionReadyToUse = "ReadyToUse" + BackupConditionReadyToUse = "ReadyToUse" + BackupConditionSyncedToRemote = "SyncedToRemote" // Reasons for ConditionReadyToUse BackupReasonNone = "NoProblem" diff --git a/charts/mantle/templates/deployment.yaml b/charts/mantle/templates/deployment.yaml index 2942bbe..4836aad 100644 --- a/charts/mantle/templates/deployment.yaml +++ b/charts/mantle/templates/deployment.yaml @@ -60,6 +60,10 @@ spec: args: - controller - --leader-elect + - --role={{ .Values.controller.role }} + {{- with .Values.controller.mantleServiceEndpoint }} + - --mantle-service-endpoint={{ . }} + {{- end }} {{- with .Values.controller.expireOffset }} - --expire-offset={{ . }} {{- end }} @@ -75,6 +79,8 @@ spec: valueFrom: fieldRef: fieldPath: metadata.namespace + ports: + {{- toYaml .Values.controller.ports | nindent 12 }} - command: - /bin/bash - -c diff --git a/charts/mantle/values.yaml b/charts/mantle/values.yaml index c3927c0..3bf545f 100644 --- a/charts/mantle/values.yaml +++ b/charts/mantle/values.yaml @@ -22,4 +22,6 @@ nodeSelector: {} tolerations: [] affinity: {} -controller: {} +controller: + role: standalone + ports: [] diff --git a/cmd/controller/main.go b/cmd/controller/main.go index e6ef68d..127cac3 100644 --- a/cmd/controller/main.go +++ b/cmd/controller/main.go @@ -1,12 +1,21 @@ package controller import ( + "context" "errors" "flag" + "fmt" + "net" "os" + "sync" + "time" // Import all Kubernetes client auth plugins (e.g. Azure, GCP, OIDC, etc.) // to ensure that exec-entrypoint and run can make use of them. + "github.com/grpc-ecosystem/go-grpc-middleware/v2/interceptors/logging" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials/insecure" + "google.golang.org/grpc/keepalive" _ "k8s.io/client-go/plugin/pkg/client/auth" "github.com/spf13/cobra" @@ -16,10 +25,12 @@ import ( ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/healthz" "sigs.k8s.io/controller-runtime/pkg/log/zap" + "sigs.k8s.io/controller-runtime/pkg/manager" metricsserver "sigs.k8s.io/controller-runtime/pkg/metrics/server" mantlev1 "github.com/cybozu-go/mantle/api/v1" "github.com/cybozu-go/mantle/internal/controller" + "github.com/cybozu-go/mantle/pkg/controller/proto" //+kubebuilder:scaffold:imports ) @@ -31,12 +42,14 @@ var ControllerCmd = &cobra.Command{ } var ( - metricsAddr string - enableLeaderElection bool - probeAddr string - zapOpts zap.Options - expireOffset string - overwriteMBCSchedule string + metricsAddr string + enableLeaderElection bool + probeAddr string + zapOpts zap.Options + expireOffset string + overwriteMBCSchedule string + role string + mantleServiceEndpoint string scheme = runtime.NewScheme() setupLog = ctrl.Log.WithName("setup") @@ -55,6 +68,15 @@ func init() { flags.StringVar(&overwriteMBCSchedule, "overwrite-mbc-schedule", "", "By setting this option, every CronJob created by this controller for every MantleBackupConfig "+ "will use its value as .spec.schedule. This option is intended for testing purposes only.") + flags.StringVar(&role, "role", "", + "Specifies how this controller should behave. This option is required. The value should be one of 'standalone' "+ + "(no replication), 'primary' (serve as a primary mantle), or 'secondary' (serve as a secondary mantle).") + flags.StringVar(&mantleServiceEndpoint, "mantle-service-endpoint", "", + "The gRPC endpoint of the secondary mantle. "+ + "This option has different meanings depending on the value of --role. "+ + "(i) If --role is 'standalone', this option is ignored. (ii) If --role is 'primary', this option is required "+ + "and is interpreted as the address that the primary mantle should connect to. (iii) If --role is 'secondary', "+ + "this option is required and is interpreted as the address that the secondary mantle should listen to.") goflags := flag.NewFlagSet("goflags", flag.ExitOnError) zapOpts.Development = true @@ -67,45 +89,39 @@ func init() { //+kubebuilder:scaffold:scheme } -func subMain() error { - ctrl.SetLogger(zap.New(zap.UseFlagOptions(&zapOpts))) - - mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ - Scheme: scheme, - Metrics: metricsserver.Options{BindAddress: metricsAddr}, - HealthProbeBindAddress: probeAddr, - LeaderElection: enableLeaderElection, - LeaderElectionID: "cfdaa833.cybozu.io", - // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily - // when the Manager ends. This requires the binary to immediately end when the - // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly - // speeds up voluntary leader transitions as the new leader don't have to wait - // LeaseDuration time first. - // - // In the default scaffold provided, the program ends immediately after - // the manager stops, so would be fine to enable this option. However, - // if you are doing or is intended to do any operation such as perform cleanups - // after the manager stops then its usage might be unsafe. - // LeaderElectionReleaseOnCancel: true, - }) - if err != nil { - setupLog.Error(err, "unable to start manager") - return err +func checkCommandlineArgs() error { + switch role { + case controller.RoleStandalone: + // nothing to do + case controller.RolePrimary: + if mantleServiceEndpoint == "" { + return errors.New("--mantle-service-endpoint must be specified if --role is 'primary'") + } + case controller.RoleSecondary: + if mantleServiceEndpoint == "" { + return errors.New("--mantle-service-endpoint must be specified if --role is 'secondary'") + } + default: + return fmt.Errorf("role should be one of 'standalone', 'primary', or 'secondary': %s", role) } + return nil +} +func setupReconcilers(mgr manager.Manager, primarySettings *controller.PrimarySettings) error { managedCephClusterID := os.Getenv("POD_NAMESPACE") if managedCephClusterID == "" { setupLog.Error(errors.New("POD_NAMESPACE is empty"), "POD_NAMESPACE is empty") - return err + return errors.New("POD_NAMESPACE is empty") } backupReconciler := controller.NewMantleBackupReconciler( mgr.GetClient(), mgr.GetScheme(), managedCephClusterID, + role, + primarySettings, ) - - if err = backupReconciler.SetupWithManager(mgr); err != nil { + if err := backupReconciler.SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "MantleBackup") return err } @@ -114,12 +130,13 @@ func subMain() error { mgr.GetClient(), mgr.GetScheme(), managedCephClusterID, + role, ) - if err = restoreReconciler.SetupWithManager(mgr); err != nil { + if err := restoreReconciler.SetupWithManager(mgr); err != nil { setupLog.Error(err, "unable to create controller", "controller", "MantleRestore") return err } - if err = controller.NewMantleBackupConfigReconciler( + if err := controller.NewMantleBackupConfigReconciler( mgr.GetClient(), mgr.GetScheme(), managedCephClusterID, @@ -131,6 +148,133 @@ func subMain() error { } //+kubebuilder:scaffold:builder + return nil +} + +func setupStandalone(mgr manager.Manager) error { + return setupReconcilers(mgr, nil) +} + +func setupPrimary(ctx context.Context, mgr manager.Manager, wg *sync.WaitGroup) error { + conn, err := grpc.NewClient( + mantleServiceEndpoint, + grpc.WithTransportCredentials(insecure.NewCredentials()), + grpc.WithKeepaliveParams(keepalive.ClientParameters{ + Time: 1 * time.Minute, + }), + ) + if err != nil { + setupLog.Error(err, "failed to create a new client for the secondary mantle") + return err + } + + wg.Add(1) + go func() { + defer wg.Done() + <-ctx.Done() + _ = conn.Close() + }() + + primarySettings := &controller.PrimarySettings{ + ServiceEndpoint: mantleServiceEndpoint, + Conn: conn, + Client: proto.NewMantleServiceClient(conn), + } + + return setupReconcilers(mgr, primarySettings) +} + +func setupSecondary(ctx context.Context, mgr manager.Manager, wg *sync.WaitGroup, cancel context.CancelFunc) error { + logger := ctrl.Log.WithName("grpc") + + serv := grpc.NewServer(grpc.ChainUnaryInterceptor( + logging.UnaryServerInterceptor( + logging.LoggerFunc(func(_ context.Context, _ logging.Level, msg string, fields ...any) { + logger.Info(msg, fields...) + }), + logging.WithLogOnEvents(logging.StartCall, logging.FinishCall), + ), + )) + + proto.RegisterMantleServiceServer(serv, &controller.SecondaryServer{}) + + l, err := net.Listen("tcp", mantleServiceEndpoint) + if err != nil { + return fmt.Errorf("failed to listen %s: %w", mantleServiceEndpoint, err) + } + + wg.Add(1) + go func() { + defer wg.Done() + err := serv.Serve(l) + if err != nil { + logger.Error(err, "gRPC server failed") + } + cancel() + }() + + wg.Add(1) + go func() { + defer wg.Done() + <-ctx.Done() + serv.GracefulStop() + }() + + return setupReconcilers(mgr, nil) +} + +func subMain() error { + ctrl.SetLogger(zap.New(zap.UseFlagOptions(&zapOpts))) + + if err := checkCommandlineArgs(); err != nil { + setupLog.Error(err, "invalid command line arguments") + return err + } + + mgr, err := ctrl.NewManager(ctrl.GetConfigOrDie(), ctrl.Options{ + Scheme: scheme, + Metrics: metricsserver.Options{BindAddress: metricsAddr}, + HealthProbeBindAddress: probeAddr, + LeaderElection: enableLeaderElection, + LeaderElectionID: "cfdaa833.cybozu.io", + // LeaderElectionReleaseOnCancel defines if the leader should step down voluntarily + // when the Manager ends. This requires the binary to immediately end when the + // Manager is stopped, otherwise, this setting is unsafe. Setting this significantly + // speeds up voluntary leader transitions as the new leader don't have to wait + // LeaseDuration time first. + // + // In the default scaffold provided, the program ends immediately after + // the manager stops, so would be fine to enable this option. However, + // if you are doing or is intended to do any operation such as perform cleanups + // after the manager stops then its usage might be unsafe. + // LeaderElectionReleaseOnCancel: true, + }) + if err != nil { + setupLog.Error(err, "unable to start manager") + return err + } + + var wg sync.WaitGroup + defer wg.Wait() + + ctx, cancel := context.WithCancel(context.Background()) + defer cancel() + + switch role { + case controller.RoleStandalone: + if err := setupStandalone(mgr); err != nil { + return err + } + case controller.RolePrimary: + if err := setupPrimary(ctx, mgr, &wg); err != nil { + return err + } + case controller.RoleSecondary: + if err := setupSecondary(ctx, mgr, &wg, cancel); err != nil { + return err + } + } + if err := mgr.AddHealthzCheck("healthz", healthz.Ping); err != nil { setupLog.Error(err, "unable to set up health check") return err diff --git a/docs/controller-protocol.md b/docs/controller-protocol.md new file mode 100644 index 0000000..bf25950 --- /dev/null +++ b/docs/controller-protocol.md @@ -0,0 +1,121 @@ +# Protocol Documentation + + +## Table of Contents + +- [pkg/controller/proto/controller.proto](#pkg_controller_proto_controller-proto) + - [CreateOrUpdateMantleBackupRequest](#proto-CreateOrUpdateMantleBackupRequest) + - [CreateOrUpdateMantleBackupResponse](#proto-CreateOrUpdateMantleBackupResponse) + - [CreateOrUpdatePVCRequest](#proto-CreateOrUpdatePVCRequest) + - [CreateOrUpdatePVCResponse](#proto-CreateOrUpdatePVCResponse) + + - [MantleService](#proto-MantleService) + +- [Scalar Value Types](#scalar-value-types) + + + + +

Top

+ +## pkg/controller/proto/controller.proto + + + + + +### CreateOrUpdateMantleBackupRequest +CreateOrUpdateMantleBackupRequest is a request message for CreateOrUpdateMantleBackup RPC. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| mantleBackup | [string](#string) | | | + + + + + + + + +### CreateOrUpdateMantleBackupResponse +CreateOrUpdateMantleBackupResponse is a response message for CreateOrUpdateMantleBackup RPC. + +nothing. + + + + + + + + +### CreateOrUpdatePVCRequest +CreateOrUpdatePVCRequest is a request message for CreateOrUpdatePVC RPC. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| pvc | [string](#string) | | | + + + + + + + + +### CreateOrUpdatePVCResponse +CreateOrUpdatePVCResponse is a response message for CreateOrUpdatePVC RPC. + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| uid | [string](#string) | | | + + + + + + + + + + + + + + +### MantleService + + +| Method Name | Request Type | Response Type | Description | +| ----------- | ------------ | ------------- | ------------| +| CreateOrUpdatePVC | [CreateOrUpdatePVCRequest](#proto-CreateOrUpdatePVCRequest) | [CreateOrUpdatePVCResponse](#proto-CreateOrUpdatePVCResponse) | | +| CreateOrUpdateMantleBackup | [CreateOrUpdateMantleBackupRequest](#proto-CreateOrUpdateMantleBackupRequest) | [CreateOrUpdateMantleBackupResponse](#proto-CreateOrUpdateMantleBackupResponse) | | + + + + + +## Scalar Value Types + +| .proto Type | Notes | C++ | Java | Python | Go | C# | PHP | Ruby | +| ----------- | ----- | --- | ---- | ------ | -- | -- | --- | ---- | +| double | | double | double | float | float64 | double | float | Float | +| float | | float | float | float | float32 | float | float | Float | +| int32 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint32 instead. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| int64 | Uses variable-length encoding. Inefficient for encoding negative numbers – if your field is likely to have negative values, use sint64 instead. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| uint32 | Uses variable-length encoding. | uint32 | int | int/long | uint32 | uint | integer | Bignum or Fixnum (as required) | +| uint64 | Uses variable-length encoding. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum or Fixnum (as required) | +| sint32 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int32s. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sint64 | Uses variable-length encoding. Signed int value. These more efficiently encode negative numbers than regular int64s. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| fixed32 | Always four bytes. More efficient than uint32 if values are often greater than 2^28. | uint32 | int | int | uint32 | uint | integer | Bignum or Fixnum (as required) | +| fixed64 | Always eight bytes. More efficient than uint64 if values are often greater than 2^56. | uint64 | long | int/long | uint64 | ulong | integer/string | Bignum | +| sfixed32 | Always four bytes. | int32 | int | int | int32 | int | integer | Bignum or Fixnum (as required) | +| sfixed64 | Always eight bytes. | int64 | long | int/long | int64 | long | integer/string | Bignum | +| bool | | bool | boolean | boolean | bool | bool | boolean | TrueClass/FalseClass | +| string | A string must always contain UTF-8 encoded or 7-bit ASCII text. | string | String | str/unicode | string | string | string | String (UTF-8) | +| bytes | May contain any arbitrary sequence of bytes. | string | ByteString | str | []byte | ByteString | string | String (ASCII-8BIT) | + diff --git a/go.mod b/go.mod index fd5097f..b5ea8d0 100644 --- a/go.mod +++ b/go.mod @@ -3,27 +3,36 @@ module github.com/cybozu-go/mantle go 1.21 require ( + github.com/go-logr/logr v1.2.4 + github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 github.com/onsi/ginkgo/v2 v2.11.0 github.com/onsi/gomega v1.27.10 + github.com/pseudomuto/protoc-gen-doc v1.5.1 github.com/spf13/cobra v1.7.0 go.uber.org/mock v0.4.0 + google.golang.org/grpc v1.65.0 + google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 + google.golang.org/protobuf v1.34.1 + gopkg.in/yaml.v2 v2.4.0 k8s.io/api v0.28.3 k8s.io/apimachinery v0.28.3 k8s.io/client-go v0.28.3 k8s.io/kube-openapi v0.0.0-20230717233707-2695361300d9 sigs.k8s.io/controller-runtime v0.16.3 - sigs.k8s.io/yaml v1.3.0 ) require ( + github.com/Masterminds/semver v1.4.2 // indirect + github.com/Masterminds/sprig v2.15.0+incompatible // indirect + github.com/aokoli/goutils v1.0.1 // indirect github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a // indirect github.com/beorn7/perks v1.0.1 // indirect - github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect github.com/davecgh/go-spew v1.1.1 // indirect github.com/emicklei/go-restful/v3 v3.11.0 // indirect + github.com/envoyproxy/protoc-gen-validate v1.0.4 // indirect github.com/evanphx/json-patch/v5 v5.6.0 // indirect github.com/fsnotify/fsnotify v1.6.0 // indirect - github.com/go-logr/logr v1.2.4 // indirect github.com/go-logr/zapr v1.2.4 // indirect github.com/go-openapi/jsonpointer v0.19.6 // indirect github.com/go-openapi/jsonreference v0.20.2 // indirect @@ -31,12 +40,13 @@ require ( github.com/go-task/slim-sprig v0.0.0-20230315185526-52ccab3ef572 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect - github.com/golang/protobuf v1.5.3 // indirect + github.com/golang/protobuf v1.5.4 // indirect github.com/google/gnostic-models v0.6.8 // indirect - github.com/google/go-cmp v0.5.9 // indirect + github.com/google/go-cmp v0.6.0 // indirect github.com/google/gofuzz v1.2.0 // indirect github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1 // indirect - github.com/google/uuid v1.3.0 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/huandu/xstrings v1.0.0 // indirect github.com/imdario/mergo v0.3.6 // indirect github.com/inconshreveable/mousetrap v1.1.0 // indirect github.com/josharian/intern v1.0.0 // indirect @@ -46,27 +56,29 @@ require ( github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect github.com/modern-go/reflect2 v1.0.2 // indirect github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect + github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007 // indirect github.com/pkg/errors v0.9.1 // indirect github.com/prometheus/client_golang v1.16.0 // indirect github.com/prometheus/client_model v0.4.0 // indirect github.com/prometheus/common v0.44.0 // indirect github.com/prometheus/procfs v0.10.1 // indirect + github.com/pseudomuto/protokit v0.2.0 // indirect github.com/spf13/pflag v1.0.5 // indirect go.uber.org/multierr v1.11.0 // indirect go.uber.org/zap v1.25.0 // indirect - golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e // indirect - golang.org/x/net v0.23.0 // indirect - golang.org/x/oauth2 v0.8.0 // indirect - golang.org/x/sys v0.18.0 // indirect - golang.org/x/term v0.18.0 // indirect - golang.org/x/text v0.14.0 // indirect + golang.org/x/crypto v0.24.0 // indirect + golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 // indirect + golang.org/x/net v0.26.0 // indirect + golang.org/x/oauth2 v0.20.0 // indirect + golang.org/x/sys v0.21.0 // indirect + golang.org/x/term v0.21.0 // indirect + golang.org/x/text v0.16.0 // indirect golang.org/x/time v0.3.0 // indirect - golang.org/x/tools v0.9.3 // indirect + golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d // indirect gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect - google.golang.org/appengine v1.6.7 // indirect - google.golang.org/protobuf v1.33.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 // indirect gopkg.in/inf.v0 v0.9.1 // indirect - gopkg.in/yaml.v2 v2.4.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect k8s.io/apiextensions-apiserver v0.28.3 // indirect k8s.io/component-base v0.28.3 // indirect @@ -74,4 +86,5 @@ require ( k8s.io/utils v0.0.0-20230406110748-d93618cff8a2 // indirect sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect sigs.k8s.io/structured-merge-diff/v4 v4.2.3 // indirect + sigs.k8s.io/yaml v1.3.0 // indirect ) diff --git a/go.sum b/go.sum index 95816fa..5e614a7 100644 --- a/go.sum +++ b/go.sum @@ -1,3 +1,9 @@ +github.com/Masterminds/semver v1.4.2 h1:WBLTQ37jOCzSLtXNdoo8bNM8876KhNqOKvrlGITgsTc= +github.com/Masterminds/semver v1.4.2/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y= +github.com/Masterminds/sprig v2.15.0+incompatible h1:0gSxPGWS9PAr7U2NsQ2YQg6juRDINkUyuvbb4b2Xm8w= +github.com/Masterminds/sprig v2.15.0+incompatible/go.mod h1:y6hNFY5UBTIWBxnzTeuNhlNS5hqE0NB0E6fgfo2Br3o= +github.com/aokoli/goutils v1.0.1 h1:7fpzNGoJ3VA8qcrm++XEE1QUe0mIwNeLa02Nwq7RDkg= +github.com/aokoli/goutils v1.0.1/go.mod h1:SijmP0QR8LtwsmDs8Yii5Z/S4trXFGFC2oO5g9DP+DQ= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= github.com/benbjohnson/clock v1.1.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= @@ -5,8 +11,8 @@ github.com/benbjohnson/clock v1.3.0 h1:ip6w0uFQkncKQ979AypyG0ER7mqUSBdKLOgAle/AT github.com/benbjohnson/clock v1.3.0/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= 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/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= -github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +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/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= @@ -17,6 +23,8 @@ github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= 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/envoyproxy/protoc-gen-validate v1.0.4 h1:gVPz/FMfvh57HdSJQyvBtF00j8JU4zdyUgIUNhlgg0A= +github.com/envoyproxy/protoc-gen-validate v1.0.4/go.mod h1:qys6tmnRsYrQqIhm2bvKZH4Blx/1gTIZ2UKVY1M+Yew= github.com/evanphx/json-patch v5.6.0+incompatible h1:jBYDEEiFBPxA0v50tFdvOzQQTCvpL6mnFh5mB2/l16U= github.com/evanphx/json-patch v5.6.0+incompatible/go.mod h1:50XU6AFN0ol/bzJsmQLiYLvXMP4fmwYFNcr97nuDLSk= github.com/evanphx/json-patch/v5 v5.6.0 h1:b91NhWfaz02IuVxO9faSllyAtNXHMPkC5J8sJCLunww= @@ -41,22 +49,23 @@ github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69 github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= 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.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= -github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= -github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= +github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps= github.com/google/gnostic-models v0.6.8 h1:yo/ABAfM5IMRsS1VnXjTBvUb61tFIHozhlYvRgGre9I= github.com/google/gnostic-models v0.6.8/go.mod h1:5n7qKqH0f5wFt+aWF8CW6pZLLNOfYuF5OpfBSENuI8U= -github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= -github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= -github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= +github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/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-20210720184732-4bb14d4b1be1 h1:K6RDEckDVWvDI9JAJYCmNdQXq6neHJOYx3V6jnqNEec= github.com/google/pprof v0.0.0-20210720184732-4bb14d4b1be1/go.mod h1:kpwsk12EmLew5upagYY7GY0pfYCcupk39gWOCRROcvE= -github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I= -github.com/google/uuid v1.3.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/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0 h1:pRhl55Yx1eC7BZ1N+BBWwnKaMyD8uC+34TLdndZMAKk= +github.com/grpc-ecosystem/go-grpc-middleware/v2 v2.1.0/go.mod h1:XKMd7iuf/RGPSMJ/U4HP0zS2Z9Fh8Ps9a+6X26m/tmI= +github.com/huandu/xstrings v1.0.0 h1:pO2K/gKgKaat5LdpAhxhluX2GPQMaI3W5FUz/I/UnWk= +github.com/huandu/xstrings v1.0.0/go.mod h1:4qWG/gcEcfX4z/mBDHJ++3ReCw9ibxbsNJbcucJdbSo= github.com/ianlancetaylor/demangle v0.0.0-20200824232613-28f6c0f3b639/go.mod h1:aSSvb/t6k1mPoxDqO4vJh6VOCGPwU4O0C2/Eqndh1Sc= github.com/imdario/mergo v0.3.6 h1:xTNEAn+kxVO7dTZGu0CegyqKZmoWFI0rF8UxjlB2d28= github.com/imdario/mergo v0.3.6/go.mod h1:2EnlNZ0deacrJVfApfmtdGgDfMuh/nq6Ok1EcJh5FfA= @@ -88,6 +97,8 @@ github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9G github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk= 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-proto-validators v0.0.0-20180403085117-0950a7990007 h1:28i1IjGcx8AofiB4N3q5Yls55VEaitzuEPkFJEVgGkA= +github.com/mwitkow/go-proto-validators v0.0.0-20180403085117-0950a7990007/go.mod h1:m2XC9Qq0AlmmVksL6FktJCdTYyLk7V3fKyp0sl1yWQo= github.com/onsi/ginkgo/v2 v2.11.0 h1:WgqUCUt/lT6yXoQ8Wef0fsNn5cAuMK7+KT9UFRz2tcU= github.com/onsi/ginkgo/v2 v2.11.0/go.mod h1:ZhrRA5XmEE3x3rhlzamx/JJvujdZoJ2uvgI7kR0iZvM= github.com/onsi/gomega v1.27.10 h1:naR28SdDFlqrG6kScpT8VWpu1xWY5nJRCF3XaYyBjhI= @@ -105,6 +116,10 @@ github.com/prometheus/common v0.44.0 h1:+5BrQJwiBB9xsMygAB3TNvpQKOwlkc25LbISbrdO github.com/prometheus/common v0.44.0/go.mod h1:ofAIvZbQ1e/nugmZGz4/qCb9Ap1VoSTIO7x0VV9VvuY= github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/pseudomuto/protoc-gen-doc v1.5.1 h1:Ah259kcrio7Ix1Rhb6u8FCaOkzf9qRBqXnvAufg061w= +github.com/pseudomuto/protoc-gen-doc v1.5.1/go.mod h1:XpMKYg6zkcpgfpCfQ8GcWBDRtRxOmMR5w7pz4Xo+dYM= +github.com/pseudomuto/protokit v0.2.0 h1:hlnBDcy3YEDXH7kc9gV+NLaN0cDzhDvD1s7Y6FZ8RpM= +github.com/pseudomuto/protokit v0.2.0/go.mod h1:2PdH30hxVHsup8KpBTOXTBeMVhJZVio3Q8ViKSAXT0Q= github.com/rogpeppe/go-internal v1.10.0 h1:TMyTOH3F/DB16zRVcYyreMH6GnZZrwQVAoYjRBZyWFQ= github.com/rogpeppe/go-internal v1.10.0/go.mod h1:UQnix2H7Ngw/k4C5ijL5+65zddjncjaFoBhdsK/akog= github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM= @@ -121,8 +136,8 @@ github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/ 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 h1:+h33VjcLVPDHtOdpUCuF+7gSuG3yGIftsP1YvFihtJ8= -github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= 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.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k= @@ -141,25 +156,24 @@ go.uber.org/zap v1.25.0/go.mod h1:JIAUzQIH94IC4fOJQm7gMmBJP5k7wQfdcnYdPoEXJYk= 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/exp v0.0.0-20220722155223-a9213eeb770e h1:+WEEuIdZHnUeJJmEUjyYC2gfUMj69yZXw17EnHg/otA= -golang.org/x/exp v0.0.0-20220722155223-a9213eeb770e/go.mod h1:Kr81I6Kryrl9sr8s2FK3vxD90NdsKWRuOIl2O4CvYbA= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= +golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1 h1:k/i9J1pBpvlfR+9QsetwPyERsqu1GIbi967PQMq3Ivc= +golang.org/x/exp v0.0.0-20230522175609-2e198f4a06a1/go.mod h1:V1LtkGg67GoY2N1AnLN78QLrzxkLyJw7RJb1gzOOz9w= golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= 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.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= -golang.org/x/mod v0.11.0 h1:bUO06HqtnRcc/7l71XBe4WcqTZ+3AH1J59zWDDwLKgU= -golang.org/x/mod v0.11.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= -golang.org/x/net v0.0.0-20190603091049-60506f45cf65/go.mod h1:HSz+uSET+XFnRR8LxR5pz3Of3rY3CfYBVs4xY44aLks= golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU= golang.org/x/net v0.0.0-20210405180319-a5a99cb37ef4/go.mod h1:p54w0d4576C0XHj96bSt6lcn1PtDYWL6XObtHCRCNQM= -golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs= -golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg= -golang.org/x/oauth2 v0.8.0 h1:6dkIjl3j3LtZ/O3sTgZTMsLKSftL/B8Zgq4huOIIUu8= -golang.org/x/oauth2 v0.8.0/go.mod h1:yr7u4HXZRm1R1kBWqr/xKNqewf0plRYoB7sla+BCIXE= +golang.org/x/net v0.26.0 h1:soB7SVo0PWrY4vPW/+ay0jKDNScG2X9wFeYlXIvJsOQ= +golang.org/x/net v0.26.0/go.mod h1:5YKkiSynbBIh3p6iOc/vibscux0x38BZDkn8sCUPxHE= +golang.org/x/oauth2 v0.20.0 h1:4mQdhULixXKP1rwYBW0vAijoXnkTG0BLCDRzfe1idMo= +golang.org/x/oauth2 v0.20.0/go.mod h1:XYTD2NtWslqkgxebSiOHnXEap4TF09sJSc7H1sXbhtI= 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= @@ -173,16 +187,15 @@ golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7w golang.org/x/sys v0.0.0-20210330210617-4fbd30eecc44/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.18.0 h1:DBdB3niSjOA/O0blCZBqDefyWNYveAYMNF1Wum0DYQ4= -golang.org/x/sys v0.18.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/sys v0.21.0 h1:rF+pYz3DAGSQAxAu1CbC7catZg4ebC4UIeIhKxBZvws= +golang.org/x/sys v0.21.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= -golang.org/x/term v0.18.0 h1:FcHjZXDMxI8mM3nwhX9HlKop4C0YQvCVCdwYl2wOtE8= -golang.org/x/term v0.18.0/go.mod h1:ILwASektA3OnRv7amZ1xhE/KTR+u50pbXfZ03+6Nx58= +golang.org/x/term v0.21.0 h1:WVXCp+/EBEHOj53Rvu+7KiT/iElMrO8ACK16SMZ3jaA= +golang.org/x/term v0.21.0/go.mod h1:ooXLefLobQVslOqselCNF4SxFAaoS6KujMbsGzSDmX0= 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.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= -golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/text v0.16.0 h1:a94ExnEXNtEwYLGJSIUxnWoxoRz/ZcCsV63ROupILh4= +golang.org/x/text v0.16.0/go.mod h1:GhwF1Be+LQoKShO3cGOHzqOgRrGaYc9AvblQOmPVHnI= golang.org/x/time v0.3.0 h1:rg5rLMjNzMS1RkNLzCG38eapWhnYLFYXDXj2gOlr8j4= golang.org/x/time v0.3.0/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= @@ -191,20 +204,24 @@ golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtn 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.5/go.mod h1:o0xws9oXOQQZyjljx8fwUC0k7L1pTE6eaCbjGeHmOkk= -golang.org/x/tools v0.9.3 h1:Gn1I8+64MsuTb/HpH+LmQtNas23LhUVr3rYZ0eKuaMM= -golang.org/x/tools v0.9.3/go.mod h1:owI94Op576fPu3cIGQeHs3joujW/2Oc6MtlxbF5dfNc= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d h1:vU5i/LfpvrRCpgM/VPfJLg5KjxD3E+hfT1SH+d9zLwg= +golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= gomodules.xyz/jsonpatch/v2 v2.4.0 h1:Ci3iUJyx9UeRx7CeFN8ARgGbkESwJK+KB9lLcWxY/Zw= gomodules.xyz/jsonpatch/v2 v2.4.0/go.mod h1:AH3dM2RI6uoBZxn3LVrfvJ3E0/9dG4cSrbuBJT4moAY= -google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= -google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= -google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= -google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= -google.golang.org/protobuf v1.33.0 h1:uNO2rsAINq/JlFpSdYEKIZ0uKD/R9cpdv0T+yoGwGmI= -google.golang.org/protobuf v1.33.0/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157 h1:7whR9kGa5LUwFtpLm2ArCEejtnxlGeLbAyjFY8sGNFw= +google.golang.org/genproto/googleapis/api v0.0.0-20240528184218-531527333157/go.mod h1:99sLkeliLXfdj2J75X3Ho+rrVCaJze0uwN7zDDkjPVU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117 h1:1GBuWVLM/KMVUv1t1En5Gs+gFZCNd360GGb4sSxtrhU= +google.golang.org/genproto/googleapis/rpc v0.0.0-20240604185151-ef581f913117/go.mod h1:EfXuqaE1J41VCDicxHzUDm+8rk+7ZdXzHV0IhO/I6s0= +google.golang.org/grpc v1.65.0 h1:bs/cUb4lp1G5iImFFd3u5ixQzweKizoZJAwBNLR42lc= +google.golang.org/grpc v1.65.0/go.mod h1:WgYC2ypjlB0EiQi6wdKixMqukr6lBc0Vo+oOgjrM5ZQ= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1 h1:F29+wU6Ee6qgu9TddPgooOdaqsxTMunOoj8KA5yuS5A= +google.golang.org/grpc/cmd/protoc-gen-go-grpc v1.5.1/go.mod h1:5KF+wpkbTSbGcR9zteSqZV6fqFOWBl4Yde8En8MryZA= +google.golang.org/protobuf v1.34.1 h1:9ddQBjfCyZPOHPUiPxpYESBLc+T8P3E+Vo4IbKZgFWg= +google.golang.org/protobuf v1.34.1/go.mod h1:c6P6GXX6sHbq/GpV6MGZEdwhWPcYBgnhAHhKbcUYpos= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= diff --git a/internal/controller/mantlebackup_controller.go b/internal/controller/mantlebackup_controller.go index 0b35c9f..be05ec4 100644 --- a/internal/controller/mantlebackup_controller.go +++ b/internal/controller/mantlebackup_controller.go @@ -20,6 +20,7 @@ import ( "sigs.k8s.io/controller-runtime/pkg/controller/controllerutil" mantlev1 "github.com/cybozu-go/mantle/api/v1" + "github.com/cybozu-go/mantle/pkg/controller/proto" ) // MantleBackupReconciler reconciles a MantleBackup object @@ -27,6 +28,8 @@ type MantleBackupReconciler struct { client.Client Scheme *runtime.Scheme managedCephClusterID string + role string + primarySettings *PrimarySettings // This should be non-nil if and only if role equals 'primary'. } type Snapshot struct { @@ -42,11 +45,13 @@ const ( ) // NewMantleBackupReconciler returns NodeReconciler. -func NewMantleBackupReconciler(client client.Client, scheme *runtime.Scheme, managedCephClusterID string) *MantleBackupReconciler { +func NewMantleBackupReconciler(client client.Client, scheme *runtime.Scheme, managedCephClusterID, role string, primarySettings *PrimarySettings) *MantleBackupReconciler { return &MantleBackupReconciler{ Client: client, Scheme: scheme, managedCephClusterID: managedCephClusterID, + role: role, + primarySettings: primarySettings, } } @@ -195,6 +200,10 @@ func (r *MantleBackupReconciler) Reconcile(ctx context.Context, req ctrl.Request var backup mantlev1.MantleBackup logger := logger.With("MantleBackup", req.NamespacedName) + if r.role == RoleSecondary { + return ctrl.Result{}, nil + } + err := r.Get(ctx, req.NamespacedName, &backup) if errors.IsNotFound(err) { logger.Info("MantleBackup is not found", "name", backup.Name, "error", err) @@ -345,6 +354,38 @@ func (r *MantleBackupReconciler) Reconcile(ctx context.Context, req ctrl.Request return ctrl.Result{}, err } + if r.primarySettings != nil { + client := r.primarySettings.Client + _, err := client.CreateOrUpdatePVC( + ctx, + &proto.CreateOrUpdatePVCRequest{ + Pvc: "", // FIXME: this field should be correctly populated. + }, + ) + if err != nil { + return ctrl.Result{}, err + } + + _, err = client.CreateOrUpdateMantleBackup( + ctx, + &proto.CreateOrUpdateMantleBackupRequest{ + MantleBackup: "", // FIXME: this field should be correctly populated. + }, + ) + if err != nil { + return ctrl.Result{}, err + } + + err = r.updateStatus(ctx, &backup, metav1.Condition{ + Type: mantlev1.BackupConditionSyncedToRemote, + Status: metav1.ConditionTrue, + Reason: mantlev1.BackupReasonNone, + }) + if err != nil { + return ctrl.Result{}, err + } + } + return ctrl.Result{}, nil } diff --git a/internal/controller/mantlebackup_controller_test.go b/internal/controller/mantlebackup_controller_test.go index 47efa4d..3b55d77 100644 --- a/internal/controller/mantlebackup_controller_test.go +++ b/internal/controller/mantlebackup_controller_test.go @@ -39,7 +39,7 @@ var _ = Describe("MantleBackup controller", func() { BeforeEach(func() { mgrUtil = util.NewManagerUtil(ctx, cfg, scheme.Scheme) - reconciler = NewMantleBackupReconciler(k8sClient, mgrUtil.GetScheme(), storageClassClusterID) + reconciler = NewMantleBackupReconciler(k8sClient, mgrUtil.GetScheme(), storageClassClusterID, RoleStandalone, nil) err := reconciler.SetupWithManager(mgrUtil.GetManager()) Expect(err).NotTo(HaveOccurred()) diff --git a/internal/controller/mantlerestore_controller.go b/internal/controller/mantlerestore_controller.go index cd5e8bc..caa9c84 100644 --- a/internal/controller/mantlerestore_controller.go +++ b/internal/controller/mantlerestore_controller.go @@ -25,6 +25,7 @@ type MantleRestoreReconciler struct { Scheme *runtime.Scheme managedCephClusterID string ceph ceph.CephCmd + role string } const ( @@ -40,24 +41,28 @@ const ( // +kubebuilder:rbac:groups="",resources=persistentvolumeclaims,verbs=get;list;watch;create;update;patch;delete // +kubebuilder:rbac:groups="",resources=persistentvolumes,verbs=get;list;watch;create;update;patch;delete -func NewMantleRestoreReconciler(cli client.Client, scheme *runtime.Scheme, managedCephClusterID string) *MantleRestoreReconciler { +func NewMantleRestoreReconciler(cli client.Client, scheme *runtime.Scheme, managedCephClusterID, role string) *MantleRestoreReconciler { return &MantleRestoreReconciler{ Client: cli, Scheme: scheme, managedCephClusterID: managedCephClusterID, ceph: ceph.NewCephCmd(), + role: role, } } func (r *MantleRestoreReconciler) Reconcile(ctx context.Context, req ctrl.Request) (ctrl.Result, error) { logger := logger.With("MantleRestore", req.NamespacedName) + if r.role == RoleSecondary { + return ctrl.Result{}, nil + } + var restore mantlev1.MantleRestore err := r.Get(ctx, req.NamespacedName, &restore) if errors.IsNotFound(err) { logger.Info("MantleRestore resource not found", "name", req.Name, "error", err) - return ctrl. - Result{}, nil + return ctrl.Result{}, nil } if err != nil { logger.Error("failed to get MantleRestore", "name", req.NamespacedName, "error", err) diff --git a/internal/controller/mantlerestore_controller_test.go b/internal/controller/mantlerestore_controller_test.go index 56417c3..f867080 100644 --- a/internal/controller/mantlerestore_controller_test.go +++ b/internal/controller/mantlerestore_controller_test.go @@ -69,13 +69,13 @@ func (test *mantleRestoreControllerUnitTest) setupEnv() { By("prepare MantleBackup reconciler") executeCommand = mockExecuteCommand test.mgrUtil = testutil.NewManagerUtil(ctx, cfg, scheme.Scheme) - backupReconciler := NewMantleBackupReconciler(k8sClient, test.mgrUtil.GetScheme(), test.cephClusterID) + backupReconciler := NewMantleBackupReconciler(k8sClient, test.mgrUtil.GetScheme(), test.cephClusterID, RoleStandalone, nil) err := backupReconciler.SetupWithManager(test.mgrUtil.GetManager()) Expect(err).NotTo(HaveOccurred()) By("prepare MantleRestore reconciler") // just allocate the reconciler, and does not start it. - test.reconciler = NewMantleRestoreReconciler(k8sClient, test.mgrUtil.GetScheme(), test.cephClusterID) + test.reconciler = NewMantleRestoreReconciler(k8sClient, test.mgrUtil.GetScheme(), test.cephClusterID, RoleStandalone) test.mgrUtil.Start() time.Sleep(100 * time.Millisecond) diff --git a/internal/controller/replication.go b/internal/controller/replication.go new file mode 100644 index 0000000..7523b23 --- /dev/null +++ b/internal/controller/replication.go @@ -0,0 +1,40 @@ +package controller + +import ( + "context" + + "github.com/cybozu-go/mantle/pkg/controller/proto" + "google.golang.org/grpc" +) + +const ( + RoleStandalone = "standalone" + RolePrimary = "primary" + RoleSecondary = "secondary" +) + +type PrimarySettings struct { + ServiceEndpoint string + Conn *grpc.ClientConn + Client proto.MantleServiceClient +} + +type SecondaryServer struct { + proto.UnimplementedMantleServiceServer +} + +var _ proto.MantleServiceServer = &SecondaryServer{} + +func (s *SecondaryServer) CreateOrUpdatePVC( + ctx context.Context, + req *proto.CreateOrUpdatePVCRequest, +) (*proto.CreateOrUpdatePVCResponse, error) { + return &proto.CreateOrUpdatePVCResponse{Uid: ""}, nil +} + +func (s *SecondaryServer) CreateOrUpdateMantleBackup( + ctx context.Context, + req *proto.CreateOrUpdateMantleBackupRequest, +) (*proto.CreateOrUpdateMantleBackupResponse, error) { + return &proto.CreateOrUpdateMantleBackupResponse{}, nil +} diff --git a/pkg/controller/proto/controller.pb.go b/pkg/controller/proto/controller.pb.go new file mode 100644 index 0000000..bcbcc87 --- /dev/null +++ b/pkg/controller/proto/controller.pb.go @@ -0,0 +1,349 @@ +// Code generated by protoc-gen-go. DO NOT EDIT. +// versions: +// protoc-gen-go v1.34.1 +// protoc v4.25.2 +// source: pkg/controller/proto/controller.proto + +package proto + +import ( + protoreflect "google.golang.org/protobuf/reflect/protoreflect" + protoimpl "google.golang.org/protobuf/runtime/protoimpl" + reflect "reflect" + sync "sync" +) + +const ( + // Verify that this generated code is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(20 - protoimpl.MinVersion) + // Verify that runtime/protoimpl is sufficiently up-to-date. + _ = protoimpl.EnforceVersion(protoimpl.MaxVersion - 20) +) + +// CreateOrUpdatePVCRequest is a request message for CreateOrUpdatePVC RPC. +type CreateOrUpdatePVCRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Pvc string `protobuf:"bytes,1,opt,name=pvc,proto3" json:"pvc,omitempty"` +} + +func (x *CreateOrUpdatePVCRequest) Reset() { + *x = CreateOrUpdatePVCRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_controller_proto_controller_proto_msgTypes[0] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateOrUpdatePVCRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateOrUpdatePVCRequest) ProtoMessage() {} + +func (x *CreateOrUpdatePVCRequest) ProtoReflect() protoreflect.Message { + mi := &file_pkg_controller_proto_controller_proto_msgTypes[0] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateOrUpdatePVCRequest.ProtoReflect.Descriptor instead. +func (*CreateOrUpdatePVCRequest) Descriptor() ([]byte, []int) { + return file_pkg_controller_proto_controller_proto_rawDescGZIP(), []int{0} +} + +func (x *CreateOrUpdatePVCRequest) GetPvc() string { + if x != nil { + return x.Pvc + } + return "" +} + +// CreateOrUpdatePVCResponse is a response message for CreateOrUpdatePVC RPC. +type CreateOrUpdatePVCResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Uid string `protobuf:"bytes,1,opt,name=uid,proto3" json:"uid,omitempty"` +} + +func (x *CreateOrUpdatePVCResponse) Reset() { + *x = CreateOrUpdatePVCResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_controller_proto_controller_proto_msgTypes[1] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateOrUpdatePVCResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateOrUpdatePVCResponse) ProtoMessage() {} + +func (x *CreateOrUpdatePVCResponse) ProtoReflect() protoreflect.Message { + mi := &file_pkg_controller_proto_controller_proto_msgTypes[1] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateOrUpdatePVCResponse.ProtoReflect.Descriptor instead. +func (*CreateOrUpdatePVCResponse) Descriptor() ([]byte, []int) { + return file_pkg_controller_proto_controller_proto_rawDescGZIP(), []int{1} +} + +func (x *CreateOrUpdatePVCResponse) GetUid() string { + if x != nil { + return x.Uid + } + return "" +} + +// CreateOrUpdateMantleBackupRequest is a request message for CreateOrUpdateMantleBackup RPC. +type CreateOrUpdateMantleBackupRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + MantleBackup string `protobuf:"bytes,1,opt,name=mantleBackup,proto3" json:"mantleBackup,omitempty"` +} + +func (x *CreateOrUpdateMantleBackupRequest) Reset() { + *x = CreateOrUpdateMantleBackupRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_controller_proto_controller_proto_msgTypes[2] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateOrUpdateMantleBackupRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateOrUpdateMantleBackupRequest) ProtoMessage() {} + +func (x *CreateOrUpdateMantleBackupRequest) ProtoReflect() protoreflect.Message { + mi := &file_pkg_controller_proto_controller_proto_msgTypes[2] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateOrUpdateMantleBackupRequest.ProtoReflect.Descriptor instead. +func (*CreateOrUpdateMantleBackupRequest) Descriptor() ([]byte, []int) { + return file_pkg_controller_proto_controller_proto_rawDescGZIP(), []int{2} +} + +func (x *CreateOrUpdateMantleBackupRequest) GetMantleBackup() string { + if x != nil { + return x.MantleBackup + } + return "" +} + +// CreateOrUpdateMantleBackupResponse is a response message for CreateOrUpdateMantleBackup RPC. +type CreateOrUpdateMantleBackupResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *CreateOrUpdateMantleBackupResponse) Reset() { + *x = CreateOrUpdateMantleBackupResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_pkg_controller_proto_controller_proto_msgTypes[3] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *CreateOrUpdateMantleBackupResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*CreateOrUpdateMantleBackupResponse) ProtoMessage() {} + +func (x *CreateOrUpdateMantleBackupResponse) ProtoReflect() protoreflect.Message { + mi := &file_pkg_controller_proto_controller_proto_msgTypes[3] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use CreateOrUpdateMantleBackupResponse.ProtoReflect.Descriptor instead. +func (*CreateOrUpdateMantleBackupResponse) Descriptor() ([]byte, []int) { + return file_pkg_controller_proto_controller_proto_rawDescGZIP(), []int{3} +} + +var File_pkg_controller_proto_controller_proto protoreflect.FileDescriptor + +var file_pkg_controller_proto_controller_proto_rawDesc = []byte{ + 0x0a, 0x25, 0x70, 0x6b, 0x67, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, 0x72, + 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x6c, 0x65, + 0x72, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x05, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x22, 0x2c, + 0x0a, 0x18, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, + 0x50, 0x56, 0x43, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x10, 0x0a, 0x03, 0x70, 0x76, + 0x63, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x70, 0x76, 0x63, 0x22, 0x2d, 0x0a, 0x19, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x56, + 0x43, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x69, 0x64, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x69, 0x64, 0x22, 0x47, 0x0a, 0x21, 0x43, + 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x6e, + 0x74, 0x6c, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x12, 0x22, 0x0a, 0x0c, 0x6d, 0x61, 0x6e, 0x74, 0x6c, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0c, 0x6d, 0x61, 0x6e, 0x74, 0x6c, 0x65, 0x42, 0x61, + 0x63, 0x6b, 0x75, 0x70, 0x22, 0x24, 0x0a, 0x22, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x72, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x74, 0x6c, 0x65, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x32, 0xda, 0x01, 0x0a, 0x0d, 0x4d, + 0x61, 0x6e, 0x74, 0x6c, 0x65, 0x53, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x12, 0x56, 0x0a, 0x11, + 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x56, + 0x43, 0x12, 0x1f, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, + 0x4f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x56, 0x43, 0x52, 0x65, 0x71, 0x75, 0x65, + 0x73, 0x74, 0x1a, 0x20, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x4f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x50, 0x56, 0x43, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x71, 0x0a, 0x1a, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x72, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x74, 0x6c, 0x65, 0x42, 0x61, 0x63, 0x6b, + 0x75, 0x70, 0x12, 0x28, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, + 0x65, 0x4f, 0x72, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x74, 0x6c, 0x65, 0x42, + 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x29, 0x2e, 0x70, + 0x72, 0x6f, 0x74, 0x6f, 0x2e, 0x43, 0x72, 0x65, 0x61, 0x74, 0x65, 0x4f, 0x72, 0x55, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x4d, 0x61, 0x6e, 0x74, 0x6c, 0x65, 0x42, 0x61, 0x63, 0x6b, 0x75, 0x70, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x42, 0x32, 0x5a, 0x30, 0x67, 0x69, 0x74, 0x68, 0x75, + 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x63, 0x79, 0x62, 0x6f, 0x7a, 0x75, 0x2d, 0x67, 0x6f, 0x2f, + 0x6d, 0x61, 0x6e, 0x74, 0x6c, 0x65, 0x2f, 0x70, 0x6b, 0x67, 0x2f, 0x63, 0x6f, 0x6e, 0x74, 0x72, + 0x6f, 0x6c, 0x6c, 0x65, 0x72, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x06, 0x70, 0x72, 0x6f, + 0x74, 0x6f, 0x33, +} + +var ( + file_pkg_controller_proto_controller_proto_rawDescOnce sync.Once + file_pkg_controller_proto_controller_proto_rawDescData = file_pkg_controller_proto_controller_proto_rawDesc +) + +func file_pkg_controller_proto_controller_proto_rawDescGZIP() []byte { + file_pkg_controller_proto_controller_proto_rawDescOnce.Do(func() { + file_pkg_controller_proto_controller_proto_rawDescData = protoimpl.X.CompressGZIP(file_pkg_controller_proto_controller_proto_rawDescData) + }) + return file_pkg_controller_proto_controller_proto_rawDescData +} + +var file_pkg_controller_proto_controller_proto_msgTypes = make([]protoimpl.MessageInfo, 4) +var file_pkg_controller_proto_controller_proto_goTypes = []interface{}{ + (*CreateOrUpdatePVCRequest)(nil), // 0: proto.CreateOrUpdatePVCRequest + (*CreateOrUpdatePVCResponse)(nil), // 1: proto.CreateOrUpdatePVCResponse + (*CreateOrUpdateMantleBackupRequest)(nil), // 2: proto.CreateOrUpdateMantleBackupRequest + (*CreateOrUpdateMantleBackupResponse)(nil), // 3: proto.CreateOrUpdateMantleBackupResponse +} +var file_pkg_controller_proto_controller_proto_depIdxs = []int32{ + 0, // 0: proto.MantleService.CreateOrUpdatePVC:input_type -> proto.CreateOrUpdatePVCRequest + 2, // 1: proto.MantleService.CreateOrUpdateMantleBackup:input_type -> proto.CreateOrUpdateMantleBackupRequest + 1, // 2: proto.MantleService.CreateOrUpdatePVC:output_type -> proto.CreateOrUpdatePVCResponse + 3, // 3: proto.MantleService.CreateOrUpdateMantleBackup:output_type -> proto.CreateOrUpdateMantleBackupResponse + 2, // [2:4] is the sub-list for method output_type + 0, // [0:2] is the sub-list for method input_type + 0, // [0:0] is the sub-list for extension type_name + 0, // [0:0] is the sub-list for extension extendee + 0, // [0:0] is the sub-list for field type_name +} + +func init() { file_pkg_controller_proto_controller_proto_init() } +func file_pkg_controller_proto_controller_proto_init() { + if File_pkg_controller_proto_controller_proto != nil { + return + } + if !protoimpl.UnsafeEnabled { + file_pkg_controller_proto_controller_proto_msgTypes[0].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateOrUpdatePVCRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_controller_proto_controller_proto_msgTypes[1].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateOrUpdatePVCResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_controller_proto_controller_proto_msgTypes[2].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateOrUpdateMantleBackupRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_pkg_controller_proto_controller_proto_msgTypes[3].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*CreateOrUpdateMantleBackupResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + } + type x struct{} + out := protoimpl.TypeBuilder{ + File: protoimpl.DescBuilder{ + GoPackagePath: reflect.TypeOf(x{}).PkgPath(), + RawDescriptor: file_pkg_controller_proto_controller_proto_rawDesc, + NumEnums: 0, + NumMessages: 4, + NumExtensions: 0, + NumServices: 1, + }, + GoTypes: file_pkg_controller_proto_controller_proto_goTypes, + DependencyIndexes: file_pkg_controller_proto_controller_proto_depIdxs, + MessageInfos: file_pkg_controller_proto_controller_proto_msgTypes, + }.Build() + File_pkg_controller_proto_controller_proto = out.File + file_pkg_controller_proto_controller_proto_rawDesc = nil + file_pkg_controller_proto_controller_proto_goTypes = nil + file_pkg_controller_proto_controller_proto_depIdxs = nil +} diff --git a/pkg/controller/proto/controller.proto b/pkg/controller/proto/controller.proto new file mode 100644 index 0000000..79adbf1 --- /dev/null +++ b/pkg/controller/proto/controller.proto @@ -0,0 +1,30 @@ +syntax = "proto3"; + +package proto; + +option go_package = "github.com/cybozu-go/mantle/pkg/controller/proto"; + +// CreateOrUpdatePVCRequest is a request message for CreateOrUpdatePVC RPC. +message CreateOrUpdatePVCRequest { + string pvc = 1; +} + +// CreateOrUpdatePVCResponse is a response message for CreateOrUpdatePVC RPC. +message CreateOrUpdatePVCResponse { + string uid = 1; +} + +// CreateOrUpdateMantleBackupRequest is a request message for CreateOrUpdateMantleBackup RPC. +message CreateOrUpdateMantleBackupRequest { + string mantleBackup = 1; +} + +// CreateOrUpdateMantleBackupResponse is a response message for CreateOrUpdateMantleBackup RPC. +message CreateOrUpdateMantleBackupResponse { + // nothing. +} + +service MantleService { + rpc CreateOrUpdatePVC(CreateOrUpdatePVCRequest) returns (CreateOrUpdatePVCResponse); + rpc CreateOrUpdateMantleBackup(CreateOrUpdateMantleBackupRequest) returns (CreateOrUpdateMantleBackupResponse); +} diff --git a/pkg/controller/proto/controller_grpc.pb.go b/pkg/controller/proto/controller_grpc.pb.go new file mode 100644 index 0000000..a000e4e --- /dev/null +++ b/pkg/controller/proto/controller_grpc.pb.go @@ -0,0 +1,159 @@ +// Code generated by protoc-gen-go-grpc. DO NOT EDIT. +// versions: +// - protoc-gen-go-grpc v1.5.1 +// - protoc v4.25.2 +// source: pkg/controller/proto/controller.proto + +package proto + +import ( + context "context" + grpc "google.golang.org/grpc" + codes "google.golang.org/grpc/codes" + status "google.golang.org/grpc/status" +) + +// This is a compile-time assertion to ensure that this generated file +// is compatible with the grpc package it is being compiled against. +// Requires gRPC-Go v1.64.0 or later. +const _ = grpc.SupportPackageIsVersion9 + +const ( + MantleService_CreateOrUpdatePVC_FullMethodName = "/proto.MantleService/CreateOrUpdatePVC" + MantleService_CreateOrUpdateMantleBackup_FullMethodName = "/proto.MantleService/CreateOrUpdateMantleBackup" +) + +// MantleServiceClient is the client API for MantleService service. +// +// For semantics around ctx use and closing/ending streaming RPCs, please refer to https://pkg.go.dev/google.golang.org/grpc/?tab=doc#ClientConn.NewStream. +type MantleServiceClient interface { + CreateOrUpdatePVC(ctx context.Context, in *CreateOrUpdatePVCRequest, opts ...grpc.CallOption) (*CreateOrUpdatePVCResponse, error) + CreateOrUpdateMantleBackup(ctx context.Context, in *CreateOrUpdateMantleBackupRequest, opts ...grpc.CallOption) (*CreateOrUpdateMantleBackupResponse, error) +} + +type mantleServiceClient struct { + cc grpc.ClientConnInterface +} + +func NewMantleServiceClient(cc grpc.ClientConnInterface) MantleServiceClient { + return &mantleServiceClient{cc} +} + +func (c *mantleServiceClient) CreateOrUpdatePVC(ctx context.Context, in *CreateOrUpdatePVCRequest, opts ...grpc.CallOption) (*CreateOrUpdatePVCResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateOrUpdatePVCResponse) + err := c.cc.Invoke(ctx, MantleService_CreateOrUpdatePVC_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *mantleServiceClient) CreateOrUpdateMantleBackup(ctx context.Context, in *CreateOrUpdateMantleBackupRequest, opts ...grpc.CallOption) (*CreateOrUpdateMantleBackupResponse, error) { + cOpts := append([]grpc.CallOption{grpc.StaticMethod()}, opts...) + out := new(CreateOrUpdateMantleBackupResponse) + err := c.cc.Invoke(ctx, MantleService_CreateOrUpdateMantleBackup_FullMethodName, in, out, cOpts...) + if err != nil { + return nil, err + } + return out, nil +} + +// MantleServiceServer is the server API for MantleService service. +// All implementations must embed UnimplementedMantleServiceServer +// for forward compatibility. +type MantleServiceServer interface { + CreateOrUpdatePVC(context.Context, *CreateOrUpdatePVCRequest) (*CreateOrUpdatePVCResponse, error) + CreateOrUpdateMantleBackup(context.Context, *CreateOrUpdateMantleBackupRequest) (*CreateOrUpdateMantleBackupResponse, error) + mustEmbedUnimplementedMantleServiceServer() +} + +// UnimplementedMantleServiceServer must be embedded to have +// forward compatible implementations. +// +// NOTE: this should be embedded by value instead of pointer to avoid a nil +// pointer dereference when methods are called. +type UnimplementedMantleServiceServer struct{} + +func (UnimplementedMantleServiceServer) CreateOrUpdatePVC(context.Context, *CreateOrUpdatePVCRequest) (*CreateOrUpdatePVCResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateOrUpdatePVC not implemented") +} +func (UnimplementedMantleServiceServer) CreateOrUpdateMantleBackup(context.Context, *CreateOrUpdateMantleBackupRequest) (*CreateOrUpdateMantleBackupResponse, error) { + return nil, status.Errorf(codes.Unimplemented, "method CreateOrUpdateMantleBackup not implemented") +} +func (UnimplementedMantleServiceServer) mustEmbedUnimplementedMantleServiceServer() {} +func (UnimplementedMantleServiceServer) testEmbeddedByValue() {} + +// UnsafeMantleServiceServer may be embedded to opt out of forward compatibility for this service. +// Use of this interface is not recommended, as added methods to MantleServiceServer will +// result in compilation errors. +type UnsafeMantleServiceServer interface { + mustEmbedUnimplementedMantleServiceServer() +} + +func RegisterMantleServiceServer(s grpc.ServiceRegistrar, srv MantleServiceServer) { + // If the following call pancis, it indicates UnimplementedMantleServiceServer was + // embedded by pointer and is nil. This will cause panics if an + // unimplemented method is ever invoked, so we test this at initialization + // time to prevent it from happening at runtime later due to I/O. + if t, ok := srv.(interface{ testEmbeddedByValue() }); ok { + t.testEmbeddedByValue() + } + s.RegisterService(&MantleService_ServiceDesc, srv) +} + +func _MantleService_CreateOrUpdatePVC_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateOrUpdatePVCRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MantleServiceServer).CreateOrUpdatePVC(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MantleService_CreateOrUpdatePVC_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MantleServiceServer).CreateOrUpdatePVC(ctx, req.(*CreateOrUpdatePVCRequest)) + } + return interceptor(ctx, in, info, handler) +} + +func _MantleService_CreateOrUpdateMantleBackup_Handler(srv interface{}, ctx context.Context, dec func(interface{}) error, interceptor grpc.UnaryServerInterceptor) (interface{}, error) { + in := new(CreateOrUpdateMantleBackupRequest) + if err := dec(in); err != nil { + return nil, err + } + if interceptor == nil { + return srv.(MantleServiceServer).CreateOrUpdateMantleBackup(ctx, in) + } + info := &grpc.UnaryServerInfo{ + Server: srv, + FullMethod: MantleService_CreateOrUpdateMantleBackup_FullMethodName, + } + handler := func(ctx context.Context, req interface{}) (interface{}, error) { + return srv.(MantleServiceServer).CreateOrUpdateMantleBackup(ctx, req.(*CreateOrUpdateMantleBackupRequest)) + } + return interceptor(ctx, in, info, handler) +} + +// MantleService_ServiceDesc is the grpc.ServiceDesc for MantleService service. +// It's only intended for direct use with grpc.RegisterService, +// and not to be introspected or modified (even as a copy) +var MantleService_ServiceDesc = grpc.ServiceDesc{ + ServiceName: "proto.MantleService", + HandlerType: (*MantleServiceServer)(nil), + Methods: []grpc.MethodDesc{ + { + MethodName: "CreateOrUpdatePVC", + Handler: _MantleService_CreateOrUpdatePVC_Handler, + }, + { + MethodName: "CreateOrUpdateMantleBackup", + Handler: _MantleService_CreateOrUpdateMantleBackup_Handler, + }, + }, + Streams: []grpc.StreamDesc{}, + Metadata: "pkg/controller/proto/controller.proto", +} diff --git a/tools_test.go b/tools_test.go new file mode 100644 index 0000000..82968d5 --- /dev/null +++ b/tools_test.go @@ -0,0 +1,11 @@ +//go:build tools +// +build tools + +package main + +import ( + // Declare dependencies on tools + _ "github.com/pseudomuto/protoc-gen-doc/cmd/protoc-gen-doc" + _ "google.golang.org/grpc/cmd/protoc-gen-go-grpc" + _ "google.golang.org/protobuf/cmd/protoc-gen-go" +) From bd1f07e1a49d9479a7e970b15450f8cab5911ba2 Mon Sep 17 00:00:00 2001 From: Ryotaro Banno Date: Tue, 27 Aug 2024 02:00:11 +0000 Subject: [PATCH 2/3] move all e2e tests to singlek8s package All of the e2e tests we currently have are written for a single Kubernetes cluster. This commit moves them into a `singlek8s` package. Signed-off-by: Ryotaro Banno --- test/e2e/Makefile | 2 +- test/e2e/{ => singlek8s}/backup_test.go | 2 +- test/e2e/{ => singlek8s}/multi_rook_ceph_test.go | 2 +- test/e2e/{ => singlek8s}/restore_test.go | 2 +- test/e2e/{ => singlek8s}/suite_test.go | 2 +- test/e2e/{ => singlek8s}/testdata/mantlebackup-template.yaml | 0 .../{ => singlek8s}/testdata/mantlebackupconfig-template.yaml | 0 test/e2e/{ => singlek8s}/testdata/mantlerestore-template.yaml | 0 .../e2e/{ => singlek8s}/testdata/pod-volume-mount-template.yaml | 0 test/e2e/{ => singlek8s}/testdata/pvc-template.yaml | 0 test/e2e/{ => singlek8s}/testdata/rbd-pool-sc-template.yaml | 0 test/e2e/{ => singlek8s}/util.go | 2 +- 12 files changed, 6 insertions(+), 6 deletions(-) rename test/e2e/{ => singlek8s}/backup_test.go (99%) rename test/e2e/{ => singlek8s}/multi_rook_ceph_test.go (99%) rename test/e2e/{ => singlek8s}/restore_test.go (99%) rename test/e2e/{ => singlek8s}/suite_test.go (98%) rename test/e2e/{ => singlek8s}/testdata/mantlebackup-template.yaml (100%) rename test/e2e/{ => singlek8s}/testdata/mantlebackupconfig-template.yaml (100%) rename test/e2e/{ => singlek8s}/testdata/mantlerestore-template.yaml (100%) rename test/e2e/{ => singlek8s}/testdata/pod-volume-mount-template.yaml (100%) rename test/e2e/{ => singlek8s}/testdata/pvc-template.yaml (100%) rename test/e2e/{ => singlek8s}/testdata/rbd-pool-sc-template.yaml (100%) rename test/e2e/{ => singlek8s}/util.go (99%) diff --git a/test/e2e/Makefile b/test/e2e/Makefile index 211d719..71ea2ba 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -165,4 +165,4 @@ do_test: $(GINKGO) PATH=${PATH} \ E2ETEST=1 \ KUBECTL=$(KUBECTL) \ - $(GINKGO) --fail-fast -v $(GINKGO_FLAGS) . + $(GINKGO) --fail-fast -v $(GINKGO_FLAGS) singlek8s diff --git a/test/e2e/backup_test.go b/test/e2e/singlek8s/backup_test.go similarity index 99% rename from test/e2e/backup_test.go rename to test/e2e/singlek8s/backup_test.go index b1e3b0a..fbf35c5 100644 --- a/test/e2e/backup_test.go +++ b/test/e2e/singlek8s/backup_test.go @@ -1,4 +1,4 @@ -package e2e +package singlek8s import ( "errors" diff --git a/test/e2e/multi_rook_ceph_test.go b/test/e2e/singlek8s/multi_rook_ceph_test.go similarity index 99% rename from test/e2e/multi_rook_ceph_test.go rename to test/e2e/singlek8s/multi_rook_ceph_test.go index d21c065..0470130 100644 --- a/test/e2e/multi_rook_ceph_test.go +++ b/test/e2e/singlek8s/multi_rook_ceph_test.go @@ -1,4 +1,4 @@ -package e2e +package singlek8s import ( "fmt" diff --git a/test/e2e/restore_test.go b/test/e2e/singlek8s/restore_test.go similarity index 99% rename from test/e2e/restore_test.go rename to test/e2e/singlek8s/restore_test.go index d6ccb46..37f3d72 100644 --- a/test/e2e/restore_test.go +++ b/test/e2e/singlek8s/restore_test.go @@ -1,4 +1,4 @@ -package e2e +package singlek8s import ( "encoding/json" diff --git a/test/e2e/suite_test.go b/test/e2e/singlek8s/suite_test.go similarity index 98% rename from test/e2e/suite_test.go rename to test/e2e/singlek8s/suite_test.go index c636944..a30a2ab 100644 --- a/test/e2e/suite_test.go +++ b/test/e2e/singlek8s/suite_test.go @@ -1,4 +1,4 @@ -package e2e +package singlek8s import ( _ "embed" diff --git a/test/e2e/testdata/mantlebackup-template.yaml b/test/e2e/singlek8s/testdata/mantlebackup-template.yaml similarity index 100% rename from test/e2e/testdata/mantlebackup-template.yaml rename to test/e2e/singlek8s/testdata/mantlebackup-template.yaml diff --git a/test/e2e/testdata/mantlebackupconfig-template.yaml b/test/e2e/singlek8s/testdata/mantlebackupconfig-template.yaml similarity index 100% rename from test/e2e/testdata/mantlebackupconfig-template.yaml rename to test/e2e/singlek8s/testdata/mantlebackupconfig-template.yaml diff --git a/test/e2e/testdata/mantlerestore-template.yaml b/test/e2e/singlek8s/testdata/mantlerestore-template.yaml similarity index 100% rename from test/e2e/testdata/mantlerestore-template.yaml rename to test/e2e/singlek8s/testdata/mantlerestore-template.yaml diff --git a/test/e2e/testdata/pod-volume-mount-template.yaml b/test/e2e/singlek8s/testdata/pod-volume-mount-template.yaml similarity index 100% rename from test/e2e/testdata/pod-volume-mount-template.yaml rename to test/e2e/singlek8s/testdata/pod-volume-mount-template.yaml diff --git a/test/e2e/testdata/pvc-template.yaml b/test/e2e/singlek8s/testdata/pvc-template.yaml similarity index 100% rename from test/e2e/testdata/pvc-template.yaml rename to test/e2e/singlek8s/testdata/pvc-template.yaml diff --git a/test/e2e/testdata/rbd-pool-sc-template.yaml b/test/e2e/singlek8s/testdata/rbd-pool-sc-template.yaml similarity index 100% rename from test/e2e/testdata/rbd-pool-sc-template.yaml rename to test/e2e/singlek8s/testdata/rbd-pool-sc-template.yaml diff --git a/test/e2e/util.go b/test/e2e/singlek8s/util.go similarity index 99% rename from test/e2e/util.go rename to test/e2e/singlek8s/util.go index 6a451d9..2c40575 100644 --- a/test/e2e/util.go +++ b/test/e2e/singlek8s/util.go @@ -1,4 +1,4 @@ -package e2e +package singlek8s import ( "bytes" From cdcc238e4551bfc98a1745e25fe12518b38982af Mon Sep 17 00:00:00 2001 From: Ryotaro Banno Date: Thu, 29 Aug 2024 05:08:42 +0000 Subject: [PATCH 3/3] add simple e2e tests for replication Signed-off-by: Ryotaro Banno --- .gitignore | 1 + test/e2e/Makefile | 110 ++++++++++----- test/e2e/multik8s/suite_test.go | 91 ++++++++++++ .../testdata/mantlebackup-template.yaml | 13 ++ test/e2e/multik8s/testdata/pvc-template.yaml | 12 ++ .../testdata/rbd-pool-sc-template.yaml | 30 ++++ test/e2e/multik8s/util.go | 130 ++++++++++++++++++ test/e2e/test-multiple-k8s-clusters.sh | 53 ------- .../testdata/secondary-mantle-service.yaml | 15 ++ .../values-mantle-primary-template.yaml | 3 + .../e2e/testdata/values-mantle-secondary.yaml | 5 + ...values-mantle.yaml => values-mantle1.yaml} | 0 test/e2e/testdata/values-mantle2.yaml | 0 13 files changed, 378 insertions(+), 85 deletions(-) create mode 100644 test/e2e/multik8s/suite_test.go create mode 100644 test/e2e/multik8s/testdata/mantlebackup-template.yaml create mode 100644 test/e2e/multik8s/testdata/pvc-template.yaml create mode 100644 test/e2e/multik8s/testdata/rbd-pool-sc-template.yaml create mode 100644 test/e2e/multik8s/util.go delete mode 100755 test/e2e/test-multiple-k8s-clusters.sh create mode 100644 test/e2e/testdata/secondary-mantle-service.yaml create mode 100644 test/e2e/testdata/values-mantle-primary-template.yaml create mode 100644 test/e2e/testdata/values-mantle-secondary.yaml rename test/e2e/testdata/{values-mantle.yaml => values-mantle1.yaml} (100%) create mode 100644 test/e2e/testdata/values-mantle2.yaml diff --git a/.gitignore b/.gitignore index a3b201b..4d42b5f 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,5 @@ Dockerfile.cross *~ test/e2e/testdata/persistentvolumes.yaml +test/e2e/testdata/values-mantle-primary.yaml include/ diff --git a/test/e2e/Makefile b/test/e2e/Makefile index 71ea2ba..2b907bb 100644 --- a/test/e2e/Makefile +++ b/test/e2e/Makefile @@ -36,21 +36,49 @@ setup: .PHONY: test test: - $(MAKE) launch-cluster MINIKUBE_PROFILE=$(MINIKUBE_PROFILE_PRIMARY) - $(MINIKUBE) profile $(MINIKUBE_PROFILE_PRIMARY) + $(MAKE) launch-minikube MINIKUBE_PROFILE=$(MINIKUBE_PROFILE_PRIMARY) + $(MAKE) install-rook-ceph-operator + $(MAKE) install-rook-ceph-cluster1 + $(MAKE) install-rook-ceph-cluster2 + $(MAKE) install-mantle-cluster-wide + $(MAKE) install-mantle \ + NAMESPACE=$(CEPH_CLUSTER1_NAMESPACE) \ + HELM_RELEASE=mantle \ + VALUES_YAML=testdata/values-mantle1.yaml + $(MAKE) install-mantle \ + NAMESPACE=$(CEPH_CLUSTER2_NAMESPACE) \ + HELM_RELEASE=mantle2 \ + VALUES_YAML=testdata/values-mantle2.yaml $(MAKE) do_test .PHONY: test-multiple-k8s-clusters test-multiple-k8s-clusters: - $(MAKE) launch-cluster MINIKUBE_PROFILE=$(MINIKUBE_PROFILE_PRIMARY) - $(MAKE) launch-cluster MINIKUBE_PROFILE=$(MINIKUBE_PROFILE_SECONDARY) +# set up a k8s cluster for secondary mantle + $(MAKE) launch-minikube MINIKUBE_PROFILE=$(MINIKUBE_PROFILE_SECONDARY) + $(MAKE) install-rook-ceph-operator + $(MAKE) install-rook-ceph-cluster1 + $(MAKE) install-mantle-cluster-wide + $(MAKE) install-mantle \ + NAMESPACE=$(CEPH_CLUSTER1_NAMESPACE) \ + HELM_RELEASE=mantle \ + VALUES_YAML=testdata/values-mantle-secondary.yaml + $(KUBECTL) apply -f testdata/secondary-mantle-service.yaml +# set up a k8s cluster for primary mantle + $(MAKE) launch-minikube MINIKUBE_PROFILE=$(MINIKUBE_PROFILE_PRIMARY) + $(MAKE) install-rook-ceph-operator + $(MAKE) install-rook-ceph-cluster1 + $(MAKE) install-mantle-cluster-wide + sed \ + -e "s%{ENDPOINT}%$$($(MINIKUBE) service list -p $(MINIKUBE_PROFILE_SECONDARY) -o json | jq -r '.[].URLs | select(. | length > 0)[]' | head -1 | sed -r 's/^http:\/\///')%" \ + testdata/values-mantle-primary-template.yaml \ + > testdata/values-mantle-primary.yaml + $(MAKE) install-mantle \ + NAMESPACE=$(CEPH_CLUSTER1_NAMESPACE) \ + HELM_RELEASE=mantle \ + VALUES_YAML=testdata/values-mantle-primary.yaml +# start testing $(MINIKUBE) profile $(MINIKUBE_PROFILE_PRIMARY) - env \ - MINIKUBE=$(MINIKUBE) \ - MINIKUBE_HOME=$(MINIKUBE_HOME) \ - MINIKUBE_PROFILE_PRIMARY=$(MINIKUBE_PROFILE_PRIMARY) \ - MINIKUBE_PROFILE_SECONDARY=$(MINIKUBE_PROFILE_SECONDARY) \ - ./test-multiple-k8s-clusters.sh + $(MAKE) do-test-multik8s .PHONY: clean clean: @@ -75,9 +103,9 @@ $(HELM): | $(BINDIR) $(CURL) https://get.helm.sh/helm-v$(HELM_VERSION)-linux-amd64.tar.gz \ | tar xvz -C $(BINDIR) --strip-components 1 linux-amd64/helm -.PHONY: launch-cluster -launch-cluster: MINIKUBE_PROFILE= -launch-cluster: +.PHONY: launch-minikube +launch-minikube: MINIKUBE_PROFILE= +launch-minikube: # TODO: Is there any better way to verify whether k8s cluster is available or not? if $(MINIKUBE) profile $(MINIKUBE_PROFILE) |& grep "not found" > /dev/null; then \ $(MINIKUBE) start \ @@ -92,8 +120,14 @@ launch-cluster: fi $(MINIKUBE) profile $(MINIKUBE_PROFILE) $(MAKE) image-build - $(MAKE) launch-rook-ceph - $(MAKE) setup-components + $(MAKE) create-loop-dev + sed \ + -e "s%{LOOP_DEV}%$(LOOP_DEV)%" \ + -e "s%{LOOP_DEV2}%$(LOOP_DEV2)%" \ + -e "s%{NODE_NAME}%$(NODE_NAME)%" \ + testdata/persistentvolumes-template.yaml \ + > testdata/persistentvolumes.yaml + $(KUBECTL) apply -f testdata/persistentvolumes.yaml .PHONY: create-loop-dev create-loop-dev: @@ -122,27 +156,26 @@ wait-deploy-ready: exit 1; \ fi -.PHONY: launch-rook-ceph -launch-rook-ceph: create-loop-dev +.PHONY: install-rook-ceph-operator +install-rook-ceph-operator: $(HELM) upgrade --install --version $(ROOK_CHART_VERSION) --repo https://charts.rook.io/release \ --create-namespace --namespace $(CEPH_CLUSTER1_NAMESPACE) -f testdata/values.yaml --wait \ rook-ceph rook-ceph - sed \ - -e "s%{LOOP_DEV}%$(LOOP_DEV)%" \ - -e "s%{LOOP_DEV2}%$(LOOP_DEV2)%" \ - -e "s%{NODE_NAME}%$(NODE_NAME)%" \ - testdata/persistentvolumes-template.yaml \ - > testdata/persistentvolumes.yaml - $(KUBECTL) apply -f testdata/persistentvolumes.yaml + $(MAKE) wait-deploy-ready NS=$(CEPH_CLUSTER1_NAMESPACE) DEPLOY=rook-ceph-operator + +.PHONY: install-rook-ceph-cluster1 +install-rook-ceph-cluster1: $(HELM) upgrade --install --version $(ROOK_CHART_VERSION) --repo https://charts.rook.io/release \ --namespace $(CEPH_CLUSTER1_NAMESPACE) -f testdata/values-cluster.yaml \ --wait rook-ceph-cluster rook-ceph-cluster + $(MAKE) wait-deploy-ready NS=$(CEPH_CLUSTER1_NAMESPACE) DEPLOY=rook-ceph-osd-0 + +.PHONY: install-rook-ceph-cluster2 +install-rook-ceph-cluster2: $(HELM) upgrade --install --version $(ROOK_CHART_VERSION) --repo https://charts.rook.io/release \ --create-namespace --namespace $(CEPH_CLUSTER2_NAMESPACE) -f testdata/values-cluster.yaml \ --set cephClusterSpec.dataDirHostPath=/var/lib/rook2 \ --wait rook-ceph-cluster2 rook-ceph-cluster - $(MAKE) wait-deploy-ready NS=$(CEPH_CLUSTER1_NAMESPACE) DEPLOY=rook-ceph-operator - $(MAKE) wait-deploy-ready NS=$(CEPH_CLUSTER1_NAMESPACE) DEPLOY=rook-ceph-osd-0 $(MAKE) wait-deploy-ready NS=$(CEPH_CLUSTER2_NAMESPACE) DEPLOY=rook-ceph-osd-0 .PHONY: image-build @@ -151,13 +184,17 @@ image-build: $(MAKE) -C ../.. docker-build $(MINIKUBE) ssh -- docker images -.PHONY: setup-components -setup-components: +.PHONY: install-mantle-cluster-wide +install-mantle-cluster-wide: $(HELM) upgrade --install mantle-cluster-wide ../../charts/mantle-cluster-wide/ --wait - $(HELM) upgrade --install --namespace=$(CEPH_CLUSTER1_NAMESPACE) mantle ../../charts/mantle/ --wait -f testdata/values-mantle.yaml - $(HELM) upgrade --install --namespace=$(CEPH_CLUSTER2_NAMESPACE) mantle2 ../../charts/mantle/ --wait - $(KUBECTL) rollout restart -n $(CEPH_CLUSTER1_NAMESPACE) deploy/mantle-controller - $(KUBECTL) rollout restart -n $(CEPH_CLUSTER2_NAMESPACE) deploy/mantle2-controller + +.PHONY: install-mantle +install-mantle: NAMESPACE= +install-mantle: HELM_RELEASE= +install-mantle: VALUES_YAML= +install-mantle: + $(HELM) upgrade --install --namespace=$(NAMESPACE) $(HELM_RELEASE) ../../charts/mantle/ --wait -f $(VALUES_YAML) + $(KUBECTL) rollout restart -n $(NAMESPACE) deploy/$(HELM_RELEASE)-controller .PHONY: do_test do_test: $(GINKGO) @@ -166,3 +203,12 @@ do_test: $(GINKGO) E2ETEST=1 \ KUBECTL=$(KUBECTL) \ $(GINKGO) --fail-fast -v $(GINKGO_FLAGS) singlek8s + +.PHONY: do-test-multik8s +do-test-multik8s: $(GINKGO) + env \ + PATH=${PATH} \ + E2ETEST=1 \ + KUBECTL_PRIMARY="$(MINIKUBE) -p $(MINIKUBE_PROFILE_PRIMARY) kubectl -- " \ + KUBECTL_SECONDARY="$(MINIKUBE) -p $(MINIKUBE_PROFILE_SECONDARY) kubectl -- " \ + $(GINKGO) --fail-fast -v $(GINKGO_FLAGS) multik8s diff --git a/test/e2e/multik8s/suite_test.go b/test/e2e/multik8s/suite_test.go new file mode 100644 index 0000000..0c6536d --- /dev/null +++ b/test/e2e/multik8s/suite_test.go @@ -0,0 +1,91 @@ +package multik8s + +import ( + _ "embed" + "errors" + "os" + "testing" + "time" + + "github.com/cybozu-go/mantle/test/util" + . "github.com/onsi/ginkgo/v2" + . "github.com/onsi/gomega" + "k8s.io/apimachinery/pkg/api/meta" + + mantlev1 "github.com/cybozu-go/mantle/api/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +func TestMtest(t *testing.T) { + if os.Getenv("E2ETEST") == "" { + t.Skip("Run under e2e/") + } + + RegisterFailHandler(Fail) + + SetDefaultEventuallyPollingInterval(time.Second) + SetDefaultEventuallyTimeout(3 * time.Minute) + + RunSpecs(t, "rbd backup system test with multiple k8s clusters") +} + +var _ = Describe("Mantle", func() { + Context("wait controller to be ready", waitControllerToBeReady) + Context("replication test", replicationTestSuite) +}) + +func waitControllerToBeReady() { + It("wait for mantle-controller to be ready", func() { + Eventually(func() error { + return checkDeploymentReady(primaryK8sCluster, "rook-ceph", "mantle-controller") + }).Should(Succeed()) + + Eventually(func() error { + return checkDeploymentReady(primaryK8sCluster, "rook-ceph", "mantle-controller") + }).Should(Succeed()) + }) +} + +func replicationTestSuite() { + Describe("replication test", func() { + It("should eventually set SyncedToRemote of a MantleBackup to True after it is created", func() { + namespace := util.GetUniqueName("ns-") + pvcName := util.GetUniqueName("pvc-") + backupName := util.GetUniqueName("mb-") + scName := util.GetUniqueName("sc-") + poolName := util.GetUniqueName("pool-") + + By("setting up the environment") + Eventually(func() error { + return createNamespace(primaryK8sCluster, namespace) + }).Should(Succeed()) + Eventually(func() error { + return applyRBDPoolAndSCTemplate(primaryK8sCluster, cephClusterNamespace, poolName, scName) + }).Should(Succeed()) + Eventually(func() error { + return applyPVCTemplate(primaryK8sCluster, namespace, pvcName, scName) + }).Should(Succeed()) + + By("creating a MantleBackup object") + Eventually(func() error { + return applyMantleBackupTemplate(primaryK8sCluster, namespace, pvcName, backupName) + }).Should(Succeed()) + + By("checking MantleBackup's SyncedToRemote status") + Eventually(func() error { + mb, err := getMB(primaryK8sCluster, namespace, backupName) + if err != nil { + return err + } + cond := meta.FindStatusCondition(mb.Status.Conditions, mantlev1.BackupConditionSyncedToRemote) + if cond == nil { + return errors.New("couldn't find condition SyncedToRemote") + } + if cond.Status != metav1.ConditionTrue { + return errors.New("status of SyncedToRemote condition is not True") + } + return nil + }).Should(Succeed()) + }) + }) +} diff --git a/test/e2e/multik8s/testdata/mantlebackup-template.yaml b/test/e2e/multik8s/testdata/mantlebackup-template.yaml new file mode 100644 index 0000000..4e25d7e --- /dev/null +++ b/test/e2e/multik8s/testdata/mantlebackup-template.yaml @@ -0,0 +1,13 @@ +apiVersion: mantle.cybozu.io/v1 +kind: MantleBackup +metadata: + labels: + app.kubernetes.io/name: mantlebackup + app.kubernetes.io/instance: %s + app.kubernetes.io/part-of: mantle + app.kubernetes.io/managed-by: kustomize + app.kubernetes.io/created-by: mantle + name: %s + namespace: %s +spec: + pvc: %s diff --git a/test/e2e/multik8s/testdata/pvc-template.yaml b/test/e2e/multik8s/testdata/pvc-template.yaml new file mode 100644 index 0000000..0bcdf27 --- /dev/null +++ b/test/e2e/multik8s/testdata/pvc-template.yaml @@ -0,0 +1,12 @@ +--- +apiVersion: v1 +kind: PersistentVolumeClaim +metadata: + name: %s +spec: + accessModes: + - ReadWriteOnce + resources: + requests: + storage: 1Gi + storageClassName: %s diff --git a/test/e2e/multik8s/testdata/rbd-pool-sc-template.yaml b/test/e2e/multik8s/testdata/rbd-pool-sc-template.yaml new file mode 100644 index 0000000..fb3a221 --- /dev/null +++ b/test/e2e/multik8s/testdata/rbd-pool-sc-template.yaml @@ -0,0 +1,30 @@ +apiVersion: ceph.rook.io/v1 +kind: CephBlockPool +metadata: + name: %s + namespace: %s +spec: + failureDomain: osd + replicated: + size: 1 + requireSafeReplicaSize: false +--- +apiVersion: storage.k8s.io/v1 +kind: StorageClass +metadata: + name: %s +provisioner: rook-ceph.rbd.csi.ceph.com +parameters: + clusterID: %s + pool: %s + imageFormat: "2" + imageFeatures: layering + csi.storage.k8s.io/provisioner-secret-name: rook-csi-rbd-provisioner + csi.storage.k8s.io/provisioner-secret-namespace: %s + csi.storage.k8s.io/controller-expand-secret-name: rook-csi-rbd-provisioner + csi.storage.k8s.io/controller-expand-secret-namespace: %s + csi.storage.k8s.io/node-stage-secret-name: rook-csi-rbd-node + csi.storage.k8s.io/node-stage-secret-namespace: %s + csi.storage.k8s.io/fstype: ext4 +allowVolumeExpansion: true +reclaimPolicy: Delete diff --git a/test/e2e/multik8s/util.go b/test/e2e/multik8s/util.go new file mode 100644 index 0000000..b3afee5 --- /dev/null +++ b/test/e2e/multik8s/util.go @@ -0,0 +1,130 @@ +package multik8s + +import ( + "bytes" + _ "embed" + "encoding/json" + "fmt" + "os" + "os/exec" + "strings" + + mantlev1 "github.com/cybozu-go/mantle/api/v1" +) + +const ( + cephClusterNamespace = "rook-ceph" + primaryK8sCluster = 1 + secondaryK8sCluster = 2 +) + +var ( + //go:embed testdata/pvc-template.yaml + testPVCTemplate string + //go:embed testdata/rbd-pool-sc-template.yaml + testRBDPoolSCTemplate string + //go:embed testdata/mantlebackup-template.yaml + testMantleBackupTemplate string + + kubectlPrefixPrimary = os.Getenv("KUBECTL_PRIMARY") + kubectlPrefixSecondary = os.Getenv("KUBECTL_SECONDARY") +) + +func execAtLocal(cmd string, input []byte, args ...string) ([]byte, []byte, error) { + var stdout, stderr bytes.Buffer + command := exec.Command(cmd, args...) + command.Stdout = &stdout + command.Stderr = &stderr + + if len(input) != 0 { + command.Stdin = bytes.NewReader(input) + } + + err := command.Run() + return stdout.Bytes(), stderr.Bytes(), err +} + +// input can be nil +func kubectl(clusterNo int, input []byte, args ...string) ([]byte, []byte, error) { + kubectlPrefix := "" + switch clusterNo { + case primaryK8sCluster: + kubectlPrefix = kubectlPrefixPrimary + case secondaryK8sCluster: + kubectlPrefix = kubectlPrefixSecondary + default: + panic(fmt.Sprintf("invalid clusterNo: %d", clusterNo)) + } + if len(kubectlPrefix) == 0 { + panic("Either KUBECTL_PRIMARY or KUBECTL_SECONDARY environment variable is not set") + } + fields := strings.Fields(kubectlPrefix) + fields = append(fields, args...) + return execAtLocal(fields[0], input, fields[1:]...) +} + +func checkDeploymentReady(clusterNo int, namespace, name string) error { + _, stderr, err := kubectl( + clusterNo, nil, + "-n", namespace, "wait", "--for=condition=Available", "deploy", name, "--timeout=1m", + ) + if err != nil { + return fmt.Errorf("kubectl wait deploy failed. stderr: %s, err: %w", string(stderr), err) + } + return nil +} + +func applyMantleBackupTemplate(clusterNo int, namespace, pvcName, backupName string) error { + manifest := fmt.Sprintf(testMantleBackupTemplate, backupName, backupName, namespace, pvcName) + _, _, err := kubectl(clusterNo, []byte(manifest), "apply", "-f", "-") + if err != nil { + return fmt.Errorf("kubectl apply mantlebackup failed. err: %w", err) + } + return nil +} + +func applyPVCTemplate(clusterNo int, namespace, name, storageClassName string) error { + manifest := fmt.Sprintf(testPVCTemplate, name, storageClassName) + _, _, err := kubectl(clusterNo, []byte(manifest), "apply", "-n", namespace, "-f", "-") + if err != nil { + return fmt.Errorf("kubectl apply pvc failed. err: %w", err) + } + return nil +} + +func createNamespace(clusterNo int, name string) error { + _, _, err := kubectl(clusterNo, nil, "create", "ns", name) + if err != nil { + return fmt.Errorf("kubectl create ns failed. err: %w", err) + } + return nil +} + +func applyRBDPoolAndSCTemplate(clusterNo int, namespace, poolName, storageClassName string) error { + manifest := fmt.Sprintf( + testRBDPoolSCTemplate, poolName, namespace, + storageClassName, namespace, poolName, namespace, namespace, namespace) + _, _, err := kubectl(clusterNo, []byte(manifest), "apply", "-n", namespace, "-f", "-") + if err != nil { + return err + } + return nil +} + +func getObject[T any](clusterNo int, kind, namespace, name string) (*T, error) { + stdout, _, err := kubectl(clusterNo, nil, "get", kind, "-n", namespace, name, "-o", "json") + if err != nil { + return nil, err + } + + var obj T + if err := json.Unmarshal(stdout, &obj); err != nil { + return nil, err + } + + return &obj, nil +} + +func getMB(clusterNo int, namespace, name string) (*mantlev1.MantleBackup, error) { + return getObject[mantlev1.MantleBackup](clusterNo, "mantlebackup", namespace, name) +} diff --git a/test/e2e/test-multiple-k8s-clusters.sh b/test/e2e/test-multiple-k8s-clusters.sh deleted file mode 100755 index a128830..0000000 --- a/test/e2e/test-multiple-k8s-clusters.sh +++ /dev/null @@ -1,53 +0,0 @@ -#!/usr/bin/bash -xeu - -set -o pipefail - -# Exits with an "unbound variable" error if one of the following environment -# variables is undefined, thanks to "-u" option to bash. -echo "${MINIKUBE}" -echo "${MINIKUBE_HOME}" -echo "${MINIKUBE_PROFILE_PRIMARY}" -echo "${MINIKUBE_PROFILE_SECONDARY}" - -cat < 0)[]' | head -1) - -# Exits with an errornous exit code if curl fails, thanks to "-e" option to bash. -${MINIKUBE} -p ${MINIKUBE_PROFILE_SECONDARY} kubectl -- exec -it -n rook-ceph deploy/rook-ceph-tools -- curl -vvv ${URL} > /dev/null diff --git a/test/e2e/testdata/secondary-mantle-service.yaml b/test/e2e/testdata/secondary-mantle-service.yaml new file mode 100644 index 0000000..4f1d283 --- /dev/null +++ b/test/e2e/testdata/secondary-mantle-service.yaml @@ -0,0 +1,15 @@ +apiVersion: v1 +kind: Service +metadata: + name: mantle + namespace: rook-ceph +spec: + type: NodePort + ports: + - port: 58080 + targetPort: 58080 + protocol: TCP + name: grpc + selector: + app.kubernetes.io/instance: mantle + app.kubernetes.io/name: mantle diff --git a/test/e2e/testdata/values-mantle-primary-template.yaml b/test/e2e/testdata/values-mantle-primary-template.yaml new file mode 100644 index 0000000..b0871cd --- /dev/null +++ b/test/e2e/testdata/values-mantle-primary-template.yaml @@ -0,0 +1,3 @@ +controller: + role: primary + mantleServiceEndpoint: {ENDPOINT} diff --git a/test/e2e/testdata/values-mantle-secondary.yaml b/test/e2e/testdata/values-mantle-secondary.yaml new file mode 100644 index 0000000..cdeb8a2 --- /dev/null +++ b/test/e2e/testdata/values-mantle-secondary.yaml @@ -0,0 +1,5 @@ +controller: + role: secondary + mantleServiceEndpoint: ":58080" + ports: + - containerPort: 58080 diff --git a/test/e2e/testdata/values-mantle.yaml b/test/e2e/testdata/values-mantle1.yaml similarity index 100% rename from test/e2e/testdata/values-mantle.yaml rename to test/e2e/testdata/values-mantle1.yaml diff --git a/test/e2e/testdata/values-mantle2.yaml b/test/e2e/testdata/values-mantle2.yaml new file mode 100644 index 0000000..e69de29