diff --git a/Makefile b/Makefile index e3b960ff..5c961e7a 100644 --- a/Makefile +++ b/Makefile @@ -53,7 +53,7 @@ BIN_PLATFORMS := $(DOCKER_PLATFORMS) windows/amd64 darwin/amd64 darwin/arm64 OS := $(if $(GOOS),$(GOOS),$(shell go env GOOS)) ARCH := $(if $(GOARCH),$(GOARCH),$(shell go env GOARCH)) -GO_VERSION ?= 1.20 +GO_VERSION ?= 1.21 BUILD_IMAGE ?= ghcr.io/appscode/golang-dev:$(GO_VERSION) RESTIC_VER := 0.16.0 diff --git a/go.mod b/go.mod index 3b927b26..00700c46 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( k8s.io/utils v0.0.0-20220823124924-e9cbc92d1a73 kmodules.xyz/client-go v0.25.43 kmodules.xyz/offshoot-api v0.25.5 - kubestash.dev/apimachinery v0.2.0 + kubestash.dev/apimachinery v0.2.1-0.20231213061518-17b1436fc227 sigs.k8s.io/controller-runtime v0.13.1 ) diff --git a/go.sum b/go.sum index 485b0441..e5d0a26e 100644 --- a/go.sum +++ b/go.sum @@ -372,8 +372,8 @@ github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxv github.com/konsorten/go-windows-terminal-sequences v1.0.3/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= -github.com/kr/pretty v0.2.0 h1:s5hAObm+yFO5uHYt5dYjxi2rXrsnmRpJx4OYvIWUaQs= github.com/kr/pretty v0.2.0/go.mod h1:ipq/a2n7PKx3OHsz4KJII5eveXtPO4qwEXGdVfWzfnI= +github.com/kr/pretty v0.2.1 h1:Fmg33tUaq4/8ym9TJN1x7sLJnHVwhP33CNkpYV/7rwI= github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= github.com/kr/pty v1.1.5/go.mod h1:9r2w37qlBe7rQ6e1fg1S/9xpWHSnaqNdHD3WcMdbPDA= github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= @@ -1069,8 +1069,8 @@ kmodules.xyz/offshoot-api v0.25.5 h1:erUtTDj9iljikd9CvrCz0E32P5mgEqq1NYxy06lxrNo kmodules.xyz/offshoot-api v0.25.5/go.mod h1:wotLtcXWHw6KrWX6Ry2EsHn2I2QTvyLX7gXAuwBjkFc= kmodules.xyz/prober v0.25.0 h1:R5uRLHJEvEtEoogj+vaTAob0Btph6+PX5IlS6hPh8PA= kmodules.xyz/prober v0.25.0/go.mod h1:z4RTnjaajNQa/vPltsiOnO3xI716I/ziD2ac2Exm+1M= -kubestash.dev/apimachinery v0.2.0 h1:xu6Um9Z0gFgMbKzUHIJAsvh+izJ6Nl4LJPyK4SUnntE= -kubestash.dev/apimachinery v0.2.0/go.mod h1:ouqYby7/IIHRJgadPc++tzh3vxtjIgk1pDzU0YRCCw4= +kubestash.dev/apimachinery v0.2.1-0.20231213061518-17b1436fc227 h1:zuX9E4EyCUTEDV3LouAuSBL55c5Q+l+xatx2enOZF54= +kubestash.dev/apimachinery v0.2.1-0.20231213061518-17b1436fc227/go.mod h1:mZlerbYdxPXYFJQfOrXFDHBPV8NzZkVF1qHUsP0/Q0s= rsc.io/binaryregexp v0.2.0/go.mod h1:qTv7/COck+e2FymRvadv62gMdZztPaShugOCi3I+8D8= rsc.io/quote/v3 v3.1.0/go.mod h1:yEA65RcK8LyAZtP9Kv3t0HmxON59tX3rD+tICJqUlj0= rsc.io/sampler v1.3.0/go.mod h1:T1hPZKmBbMNahiBKFy5HrXp6adAjACjK9JXDnKaTXpA= diff --git a/pkg/clone.go b/pkg/clone.go index 3e2d476d..f9da49ac 100644 --- a/pkg/clone.go +++ b/pkg/clone.go @@ -17,11 +17,10 @@ limitations under the License. package pkg import ( - "fmt" - "github.com/spf13/cobra" "gomodules.xyz/flags" "k8s.io/cli-runtime/pkg/genericclioptions" + "kubestash.dev/apimachinery/pkg" ) func NewCmdClone(clientGetter genericclioptions.RESTClientGetter) *cobra.Command { @@ -32,17 +31,14 @@ func NewCmdClone(clientGetter genericclioptions.RESTClientGetter) *cobra.Command PersistentPreRunE: func(cmd *cobra.Command, args []string) error { flags.EnsureRequiredFlags(cmd, "to-namespace") - cfg, err := clientGetter.ToRESTConfig() - if err != nil { - return fmt.Errorf("failed to read kubeconfig. Reason: %w", err) - } + var err error srcNamespace, _, err = clientGetter.ToRawKubeConfigLoader().Namespace() if err != nil { return err } - klient, err = newRuntimeClient(cfg) + klient, err = pkg.NewUncachedClient() if err != nil { return err } diff --git a/pkg/clone_pvc.go b/pkg/clone_pvc.go index 29c1fd6d..9242378a 100644 --- a/pkg/clone_pvc.go +++ b/pkg/clone_pvc.go @@ -63,7 +63,10 @@ func NewCmdClonePVC() *cobra.Command { Args: cobra.ExactArgs(1), DisableAutoGenTag: true, RunE: func(cmd *cobra.Command, args []string) error { - flags.EnsureRequiredFlags(cmd, "provider", "bucket", "encrypt-secret", "encrypt-secret-namespace") + flags.EnsureRequiredFlags(cmd, "encrypt-secret", "encrypt-secret-namespace") + if storageName == "" { + flags.EnsureRequiredFlags(cmd, "provider", "bucket") + } if storageOpt.provider == string(storageapi.ProviderS3) { flags.EnsureRequiredFlags(cmd, "endpoint") } @@ -213,14 +216,6 @@ func (opt *storageOption) getBackendInfo() storageapi.Backend { Secret: opt.storageSecret, }, } - case string(storageapi.ProviderRest): - backend = storageapi.Backend{ - Provider: storageapi.ProviderRest, - Rest: &storageapi.RestServerSpec{ - URL: opt.endpoint, - Secret: opt.storageSecret, - }, - } } return backend diff --git a/pkg/constants.go b/pkg/constants.go index 31d4f76e..2c774779 100644 --- a/pkg/constants.go +++ b/pkg/constants.go @@ -54,8 +54,8 @@ const ( // Constants for PVC cloning const ( PVCAddon = "pvc-addon" - PVCBackupTask = "pvc-backup" - PVCRestoreTask = "pvc-restore" + PVCBackupTask = "LogicalBackup" + PVCRestoreTask = "LogicalBackupRestore" PVCSchedule = "*/59 * * * *" LatestSnapshot = "latest" ) diff --git a/pkg/copy.go b/pkg/copy.go index 992cc4e6..f74615a6 100644 --- a/pkg/copy.go +++ b/pkg/copy.go @@ -17,11 +17,10 @@ limitations under the License. package pkg import ( - "fmt" - "github.com/spf13/cobra" "gomodules.xyz/flags" "k8s.io/cli-runtime/pkg/genericclioptions" + "kubestash.dev/apimachinery/pkg" ) func NewCmdCopy(clientGetter genericclioptions.RESTClientGetter) *cobra.Command { @@ -33,17 +32,14 @@ func NewCmdCopy(clientGetter genericclioptions.RESTClientGetter) *cobra.Command PersistentPreRunE: func(cmd *cobra.Command, args []string) error { flags.EnsureRequiredFlags(cmd, "to-namespace") - cfg, err := clientGetter.ToRESTConfig() - if err != nil { - return fmt.Errorf("failed to read kubeconfig. Reason: %w", err) - } + var err error srcNamespace, _, err = clientGetter.ToRawKubeConfigLoader().Namespace() if err != nil { return err } - klient, err = newRuntimeClient(cfg) + klient, err = pkg.NewUncachedClient() if err != nil { return err } diff --git a/pkg/debug.go b/pkg/debug.go index 1c692d45..f289b771 100644 --- a/pkg/debug.go +++ b/pkg/debug.go @@ -17,10 +17,9 @@ limitations under the License. package pkg import ( - "fmt" - "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" + "kubestash.dev/apimachinery/pkg" ) func NewCmdDebug(clientGetter genericclioptions.RESTClientGetter) *cobra.Command { @@ -29,17 +28,14 @@ func NewCmdDebug(clientGetter genericclioptions.RESTClientGetter) *cobra.Command Short: `Debug common KubeStash issues`, DisableAutoGenTag: true, PersistentPreRunE: func(cmd *cobra.Command, args []string) error { - cfg, err := clientGetter.ToRESTConfig() - if err != nil { - return fmt.Errorf("failed to read kubeconfig. Reason: %w", err) - } + var err error srcNamespace, _, err = clientGetter.ToRawKubeConfigLoader().Namespace() if err != nil { return err } - klient, err = newRuntimeClient(cfg) + klient, err = pkg.NewUncachedClient() if err != nil { return err } diff --git a/pkg/debug_backup.go b/pkg/debug_backup.go index 964d20f4..71370f68 100644 --- a/pkg/debug_backup.go +++ b/pkg/debug_backup.go @@ -20,6 +20,7 @@ import ( "context" "fmt" "os" + "slices" "github.com/spf13/cobra" core "k8s.io/api/core/v1" @@ -72,18 +73,18 @@ func NewCmdDebugBackup() *cobra.Command { } if latest { - if err := debugOpt.debugLatestBackupSessions(backupSessions); err != nil { + if err = debugOpt.debugLatestBackupSessions(backupSessions); err != nil { return err } return nil } for _, bs := range backupSessions { - if !debugOpt.shouldDebugSession() { + if !slices.Contains(debugOpt.sessions, bs.Spec.Session) { continue } debugOpt.backupSession = bs - if err := debugOpt.showTableForFailedBackupSession(); err != nil { + if err = debugOpt.showTableForFailedBackupSession(); err != nil { return err } } @@ -210,19 +211,6 @@ func (opt *backupDebugOptions) getLatestBackupSession(session string, backupSess return bs } -func (opt *backupDebugOptions) shouldDebugSession() bool { - if len(opt.sessions) == 0 { - return true - } - - for _, session := range opt.sessions { - if opt.backupSession.Spec.Session == session { - return true - } - } - return false -} - func (opt *backupDebugOptions) showTableForFailedBackupSession() error { var data [][]string diff --git a/pkg/download.go b/pkg/download.go index b5e72b38..8470f9ad 100644 --- a/pkg/download.go +++ b/pkg/download.go @@ -22,6 +22,7 @@ import ( "os/exec" "os/user" "path/filepath" + "slices" "strings" "github.com/spf13/cobra" @@ -32,6 +33,7 @@ import ( kmapi "kmodules.xyz/client-go/api/v1" v1 "kmodules.xyz/offshoot-api/api/v1" storageapi "kubestash.dev/apimachinery/apis/storage/v1alpha1" + "kubestash.dev/apimachinery/pkg" "kubestash.dev/apimachinery/pkg/restic" ) @@ -67,7 +69,7 @@ func NewCmdDownload(clientGetter genericclioptions.RESTClientGetter) *cobra.Comm return err } - klient, err = newRuntimeClient(downloadOpt.restConfig) + klient, err = pkg.NewUncachedClient() if err != nil { return err } @@ -156,7 +158,8 @@ func NewCmdDownload(clientGetter genericclioptions.RESTClientGetter) *cobra.Comm } for compName, comp := range snapshot.Status.Components { - if !downloadOpt.shouldRestoreComponent(compName) { + if len(downloadOpt.components) != 0 && + !slices.Contains(downloadOpt.components, compName) { continue } @@ -321,16 +324,3 @@ func (opt *downloadOptions) runRestoreViaDocker(destination string, args []strin } return nil } - -func (opt *downloadOptions) shouldRestoreComponent(componentName string) bool { - if opt.components == nil { - return true - } - - for _, comp := range opt.components { - if comp == componentName { - return true - } - } - return false -} diff --git a/pkg/key.go b/pkg/key.go index 74751d64..3fe25221 100644 --- a/pkg/key.go +++ b/pkg/key.go @@ -30,6 +30,7 @@ import ( "k8s.io/client-go/rest" "k8s.io/klog/v2" storageapi "kubestash.dev/apimachinery/apis/storage/v1alpha1" + "kubestash.dev/apimachinery/pkg" "kubestash.dev/apimachinery/pkg/restic" ) @@ -59,7 +60,7 @@ func NewCmdKey(clientGetter genericclioptions.RESTClientGetter) *cobra.Command { return err } - klient, err = newRuntimeClient(opt.config) + klient, err = pkg.NewUncachedClient() if err != nil { return err } diff --git a/pkg/pause.go b/pkg/pause.go index 4a8506a8..d48188e3 100644 --- a/pkg/pause.go +++ b/pkg/pause.go @@ -17,11 +17,10 @@ limitations under the License. package pkg import ( - "fmt" - "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/klog/v2" + "kubestash.dev/apimachinery/pkg" ) func NewCmdPause(clientGetter genericclioptions.RESTClientGetter) *cobra.Command { @@ -34,17 +33,14 @@ func NewCmdPause(clientGetter genericclioptions.RESTClientGetter) *cobra.Command RunE: func(cmd *cobra.Command, args []string) error { backupConfigName := args[0] - cfg, err := clientGetter.ToRESTConfig() - if err != nil { - return fmt.Errorf("failed to read kubeconfig. Reason: %w", err) - } + var err error srcNamespace, _, err = clientGetter.ToRawKubeConfigLoader().Namespace() if err != nil { return err } - klient, err = newRuntimeClient(cfg) + klient, err = pkg.NewUncachedClient() if err != nil { return err } diff --git a/pkg/resume.go b/pkg/resume.go index 5ff74e71..1e70c8b5 100644 --- a/pkg/resume.go +++ b/pkg/resume.go @@ -17,11 +17,10 @@ limitations under the License. package pkg import ( - "fmt" - "github.com/spf13/cobra" "k8s.io/cli-runtime/pkg/genericclioptions" "k8s.io/klog/v2" + "kubestash.dev/apimachinery/pkg" ) func NewCmdResume(clientGetter genericclioptions.RESTClientGetter) *cobra.Command { @@ -34,17 +33,14 @@ func NewCmdResume(clientGetter genericclioptions.RESTClientGetter) *cobra.Comman RunE: func(cmd *cobra.Command, args []string) error { backupConfigName := args[0] - cfg, err := clientGetter.ToRESTConfig() - if err != nil { - return fmt.Errorf("failed to read kubeconfig. Reason: %w", err) - } + var err error srcNamespace, _, err = clientGetter.ToRawKubeConfigLoader().Namespace() if err != nil { return err } - klient, err = newRuntimeClient(cfg) + klient, err = pkg.NewUncachedClient() if err != nil { return err } diff --git a/pkg/trigger.go b/pkg/trigger.go index 7eefbb6e..657d76b7 100644 --- a/pkg/trigger.go +++ b/pkg/trigger.go @@ -18,7 +18,7 @@ package pkg import ( "context" - "fmt" + "slices" "github.com/spf13/cobra" v1 "k8s.io/api/core/v1" @@ -29,10 +29,13 @@ import ( kmc "kmodules.xyz/client-go/client" core_util "kmodules.xyz/client-go/core/v1" coreapi "kubestash.dev/apimachinery/apis/core/v1alpha1" + "kubestash.dev/apimachinery/pkg" "sigs.k8s.io/controller-runtime/pkg/client" ) func NewCmdTriggerBackup(clientGetter genericclioptions.RESTClientGetter) *cobra.Command { + var sessions []string + cmd := &cobra.Command{ Use: "trigger", Short: `Trigger a backup`, @@ -42,12 +45,9 @@ func NewCmdTriggerBackup(clientGetter genericclioptions.RESTClientGetter) *cobra RunE: func(cmd *cobra.Command, args []string) error { backupConfigName := args[0] - cfg, err := clientGetter.ToRESTConfig() - if err != nil { - return fmt.Errorf("failed to read kubeconfig. Reason: %w", err) - } + var err error - klient, err = newRuntimeClient(cfg) + klient, err = pkg.NewUncachedClient() if err != nil { return err } @@ -66,7 +66,12 @@ func NewCmdTriggerBackup(clientGetter genericclioptions.RESTClientGetter) *cobra } for _, session := range backupConfig.Spec.Sessions { - _, err := triggerBackup(backupConfig, session) + if len(sessions) != 0 && + !slices.Contains(sessions, session.Name) { + continue + } + + _, err = triggerBackup(backupConfig, session) if err != nil { return err } @@ -75,6 +80,8 @@ func NewCmdTriggerBackup(clientGetter genericclioptions.RESTClientGetter) *cobra }, } + cmd.Flags().StringSliceVar(&sessions, "sessions", sessions, "List of sessions to trigger") + return cmd } diff --git a/pkg/unlock.go b/pkg/unlock.go index 89e8493f..d6f84f23 100644 --- a/pkg/unlock.go +++ b/pkg/unlock.go @@ -32,6 +32,7 @@ import ( kmapi "kmodules.xyz/client-go/api/v1" v1 "kmodules.xyz/offshoot-api/api/v1" storageapi "kubestash.dev/apimachinery/apis/storage/v1alpha1" + "kubestash.dev/apimachinery/pkg" "kubestash.dev/apimachinery/pkg/restic" ) @@ -62,7 +63,7 @@ func NewCmdUnlockRepository(clientGetter genericclioptions.RESTClientGetter) *co return err } - klient, err = newRuntimeClient(unlockOpt.restConfig) + klient, err = pkg.NewUncachedClient() if err != nil { return err } diff --git a/pkg/util.go b/pkg/util.go index 8359a887..ccacfffc 100644 --- a/pkg/util.go +++ b/pkg/util.go @@ -30,11 +30,8 @@ import ( "gomodules.xyz/go-sh" core "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" - "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" "k8s.io/client-go/kubernetes" "k8s.io/client-go/rest" - restclient "k8s.io/client-go/rest" "k8s.io/client-go/tools/remotecommand" "k8s.io/klog/v2" "k8s.io/kubectl/pkg/scheme" @@ -46,7 +43,6 @@ import ( coreapi "kubestash.dev/apimachinery/apis/core/v1alpha1" storageapi "kubestash.dev/apimachinery/apis/storage/v1alpha1" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/apiutil" ) var ( @@ -64,28 +60,6 @@ func init() { imgRestic.Tag = ResticTag } -func newRuntimeClient(config *restclient.Config) (client.Client, error) { - scheme := runtime.NewScheme() - utilruntime.Must(coreapi.AddToScheme(scheme)) - utilruntime.Must(storageapi.AddToScheme(scheme)) - utilruntime.Must(core.AddToScheme(scheme)) - utilruntime.Must(vsapi.AddToScheme(scheme)) - - mapper, err := apiutil.NewDynamicRESTMapper(config) - if err != nil { - return nil, err - } - - return client.New(config, client.Options{ - Scheme: scheme, - Mapper: mapper, - Opts: client.WarningHandlerOptions{ - SuppressWarnings: false, - AllowDuplicateLogs: false, - }, - }) -} - func getSecret(ref kmapi.ObjectReference) (*core.Secret, error) { secret := &core.Secret{ ObjectMeta: metav1.ObjectMeta{ diff --git a/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/addon_helpers.go b/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/addon_helpers.go new file mode 100644 index 00000000..19fbc251 --- /dev/null +++ b/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/addon_helpers.go @@ -0,0 +1,27 @@ +/* +Copyright AppsCode Inc. and Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "kubestash.dev/apimachinery/crds" + + "kmodules.xyz/client-go/apiextensions" +) + +func (_ Addon) CustomResourceDefinition() *apiextensions.CustomResourceDefinition { + return crds.MustCustomResourceDefinition(GroupVersion.WithResource(ResourcePluralAddon)) +} diff --git a/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/addon_types.go b/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/addon_types.go new file mode 100644 index 00000000..123a0f92 --- /dev/null +++ b/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/addon_types.go @@ -0,0 +1,149 @@ +/* +Copyright AppsCode Inc. and Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "kubestash.dev/apimachinery/apis" + + core "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" +) + +const ( + ResourceKindAddon = "Addon" + ResourceSingularAddon = "addon" + ResourcePluralAddon = "addons" +) + +// +k8s:openapi-gen=true +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=addons,singular=addon,scope=Cluster,categories={kubestash,appscode,all} +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" + +// Addon specifies the backup and restore capabilities for a particular resource. +// For example, MySQL addon specifies the backup and restore capabilities of MySQL database where +// Postgres addon specifies backup and restore capabilities for PostgreSQL database. +// An Addon CR defines the backup and restore tasks that can be performed by this addon. +type Addon struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec AddonSpec `json:"spec,omitempty"` +} + +// AddonSpec defines the specification for backup and restore tasks. +type AddonSpec struct { + // BackupTasks specifies a list of backup tasks that can be performed by the addon. + BackupTasks []Task `json:"backupTasks,omitempty"` + + // RestoreTasks specifies a list of restore tasks that can be performed by the addon. + RestoreTasks []Task `json:"restoreTasks,omitempty"` +} + +// Task defines the specification of a backup/restore task. +type Task struct { + // Name specifies the name of the task. The name of a Task should indicate what + // this task does. For example, a name LogicalBackup indicate that this task performs + // a logical backup of a database. + Name string `json:"name,omitempty"` + + // Function specifies the name of a Function CR that defines a container definition + // which will execute the backup/restore logic for a particular application. + Function string `json:"function,omitempty"` + + // Driver specifies the underlying tool that will be used to upload the data to the backend storage. + // Valid values are: + // - "Restic": The underlying tool is [restic](https://restic.net/). + // - "WalG": The underlying tool is [wal-g](https://github.com/wal-g/wal-g). + // +kubebuilder:validation:Enum=Restic;WalG;VolumeSnapshotter; + Driver apis.Driver `json:"driver,omitempty"` + + // Executor specifies the type of entity that will execute the task. For example, it can be a Job, + // a sidecar container, an ephemeral container, or a Job that creates additional Jobs/Pods + // for executing the backup/restore logic. + // Valid values are: + // - "Job": Stash will create a Job to execute the backup/restore task. + // - "Sidecar": Stash will inject a sidecar container into the application to execute the backup/restore task. + // - "EphemeralContainer": Stash will attach an ephemeral container to the respective Pods to execute the backup/restore task. + // - "MultiLevelJob": Stash will create a Job that will create additional Jobs/Pods to execute the backup/restore task. + Executor TaskExecutor `json:"executor,omitempty"` + + // Singleton specifies whether this task will be executed on a single job or across multiple jobs. + Singleton bool `json:"singleton,omitempty"` + + // Parameters defines a list of parameters that is used by the task to execute its logic. + // +optional + Parameters []apis.ParameterDefinition `json:"parameters,omitempty"` + + // VolumeTemplate specifies a list of volume templates that is used by the respective backup/restore + // Job to execute its logic. + // User can overwrite these volume templates using `addonVolumes` field of BackupConfiguration/BackupBatch. + // +optional + VolumeTemplate []VolumeTemplate `json:"volumeTemplate,omitempty"` + + // VolumeMounts specifies the mount path of the volumes specified in the VolumeTemplate section. + // These volumes will be mounted directly on the Job/Container created/injected by Stash operator. + // If the volume type is VolumeClaimTemplate, then Stash operator is responsible for creating the volume. + // +optional + VolumeMounts []core.VolumeMount `json:"volumeMounts,omitempty"` + + // PassThroughMounts specifies a list of volume mount for the VolumeTemplates that should be mounted + // on second level Jobs/Pods created by the first level executor Job. + // If the volume needs to be mounted on both first level and second level Jobs/Pods, then specify the + // mount in both VolumeMounts and PassThroughMounts section. + // If the volume type is VolumeClaimTemplate, then the first level job is responsible for creating the volume. + // +optional + PassThroughMounts []core.VolumeMount `json:"passThroughMounts,omitempty"` +} + +// TaskExecutor defines the type of the executor that will execute the backup/restore task. +// +kubebuilder:validation:Enum=Job;Sidecar;EphemeralContainer;MultiLevelJob +type TaskExecutor string + +const ( + ExecutorJob TaskExecutor = "Job" + ExecutorSidecar TaskExecutor = "Sidecar" + ExecutorEphemeralContainer TaskExecutor = "EphemeralContainer" + ExecutorMultiLevelJob TaskExecutor = "MultiLevelJob" +) + +// VolumeTemplate specifies the name, usage, and the source of volume that will be used by the +// addon to execute it's backup/restore task. +type VolumeTemplate struct { + // Name specifies the name of the volume + Name string `json:"name,omitempty"` + + // Usage specifies the usage of the volume. + // +optional + Usage string `json:"usage,omitempty"` + + // Source specifies the source of this volume. + Source *apis.VolumeSource `json:"source,omitempty"` +} + +//+kubebuilder:object:root=true + +// AddonList contains a list of Addon +type AddonList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Addon `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Addon{}, &AddonList{}) +} diff --git a/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/doc.go b/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/doc.go new file mode 100644 index 00000000..271374d7 --- /dev/null +++ b/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/doc.go @@ -0,0 +1,23 @@ +/* +Copyright AppsCode Inc. and Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1alpha1 is the v1alpha1 version of the API. + +// +k8s:openapi-gen=true +// +k8s:defaulter-gen=TypeMeta + +// +groupName=addons.kubestash.com +package v1alpha1 diff --git a/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/function_helpers.go b/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/function_helpers.go new file mode 100644 index 00000000..413b2c67 --- /dev/null +++ b/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/function_helpers.go @@ -0,0 +1,27 @@ +/* +Copyright AppsCode Inc. and Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + "kubestash.dev/apimachinery/crds" + + "kmodules.xyz/client-go/apiextensions" +) + +func (_ Function) CustomResourceDefinition() *apiextensions.CustomResourceDefinition { + return crds.MustCustomResourceDefinition(GroupVersion.WithResource(ResourcePluralFunction)) +} diff --git a/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/function_types.go b/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/function_types.go new file mode 100644 index 00000000..9945a96a --- /dev/null +++ b/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/function_types.go @@ -0,0 +1,116 @@ +/* +Copyright AppsCode Inc. and Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package v1alpha1 + +import ( + core "k8s.io/api/core/v1" + metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" + ofst "kmodules.xyz/offshoot-api/api/v1" +) + +const ( + ResourceKindFunction = "Function" + ResourcePluralFunction = "functions" + ResourceSingularFunction = "function" +) + +// +k8s:openapi-gen=true +// +kubebuilder:object:root=true +// +kubebuilder:resource:path=functions,singular=function,scope=Cluster,shortName=fn,categories={kubestash,appscode,all} +// +kubebuilder:printcolumn:name="Age",type="date",JSONPath=".metadata.creationTimestamp" + +// Function is the Schema for the functions API +type Function struct { + metav1.TypeMeta `json:",inline"` + metav1.ObjectMeta `json:"metadata,omitempty"` + + Spec FunctionSpec `json:"spec,omitempty"` +} + +type FunctionSpec struct { + // Image specifies the docker image name. + // More info: https://kubernetes.io/docs/concepts/containers/images + // This field is optional to allow higher level config management to default or override + // container images in workload controllers like Deployments and StatefulSets. + // +optional + Image string `json:"image,omitempty"` + // Entrypoint array. Not executed within a shell. + // The docker image's ENTRYPOINT is used if this is not provided. + // Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + // cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax + // can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, + // regardless of whether the variable exists or not. + // Cannot be updated. + // More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + // +optional + Command []string `json:"command,omitempty"` + // Args specifies the arguments to the entrypoint. + // The docker image's CMD is used if this is not provided. + // Variable references $(VAR_NAME) are expanded using the container's environment. If a variable + // cannot be resolved, the reference in the input string will be unchanged. The $(VAR_NAME) syntax + // can be escaped with a double $$, ie: $$(VAR_NAME). Escaped references will never be expanded, + // regardless of whether the variable exists or not. + // Cannot be updated. + // More info: https://kubernetes.io/docs/tasks/inject-data-application/define-command-argument-container/#running-a-command-in-a-shell + // +optional + Args []string `json:"args,omitempty"` + // WorkDir specifies the container's working directory. + // If not specified, the container runtime's default will be used, which + // might be configured in the container image. + // Cannot be updated. + // +optional + WorkingDir string `json:"workingDir,omitempty"` + // Ports specifies the list of ports to expose from the container. Exposing a port here gives + // the system additional information about the network connections a + // container uses, but is primarily informational. Not specifying a port here + // DOES NOT prevent that port from being exposed. Any port which is + // listening on the default "0.0.0.0" address inside a container will be + // accessible from the network. + // Cannot be updated. + // +optional + // +patchMergeKey=containerPort + // +patchStrategy=merge + Ports []core.ContainerPort `json:"ports,omitempty" patchStrategy:"merge" patchMergeKey:"containerPort"` + // VolumeMounts specifies the Pod volumes to mount into the container's filesystem. + // Cannot be updated. + // +optional + // +patchMergeKey=mountPath + // +patchStrategy=merge + VolumeMounts []core.VolumeMount `json:"volumeMounts,omitempty" patchStrategy:"merge" patchMergeKey:"mountPath"` + // VolumeDevices is the list of block devices to be used by the container. + // This is an alpha feature and may change in the future. + // +patchMergeKey=devicePath + // +patchStrategy=merge + // +optional + VolumeDevices []core.VolumeDevice `json:"volumeDevices,omitempty" patchStrategy:"merge" patchMergeKey:"devicePath"` + // RuntimeSettings allow to specify Resources, LivenessProbe, ReadinessProbe, Lifecycle, SecurityContext etc. + // +optional + RuntimeSettings *ofst.ContainerRuntimeSettings `json:"runtimeSettings,omitempty"` +} + +//+kubebuilder:object:root=true + +// FunctionList contains a list of Function +type FunctionList struct { + metav1.TypeMeta `json:",inline"` + metav1.ListMeta `json:"metadata,omitempty"` + Items []Function `json:"items"` +} + +func init() { + SchemeBuilder.Register(&Function{}, &FunctionList{}) +} diff --git a/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/groupversion_info.go b/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/groupversion_info.go new file mode 100644 index 00000000..a61ea8c9 --- /dev/null +++ b/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/groupversion_info.go @@ -0,0 +1,36 @@ +/* +Copyright AppsCode Inc. and Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Package v1alpha1 contains API Schema definitions for the addons v1alpha1 API group +// +kubebuilder:object:generate=true +// +groupName=addons.kubestash.com +package v1alpha1 + +import ( + "k8s.io/apimachinery/pkg/runtime/schema" + "sigs.k8s.io/controller-runtime/pkg/scheme" +) + +var ( + // GroupVersion is group version used to register these objects + GroupVersion = schema.GroupVersion{Group: "addons.kubestash.com", Version: "v1alpha1"} + + // SchemeBuilder is used to add go types to the GroupVersionKind scheme + SchemeBuilder = &scheme.Builder{GroupVersion: GroupVersion} + + // AddToScheme adds the types in this group-version to the given scheme. + AddToScheme = SchemeBuilder.AddToScheme +) diff --git a/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/zz_generated.deepcopy.go b/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/zz_generated.deepcopy.go new file mode 100644 index 00000000..970e34a2 --- /dev/null +++ b/vendor/kubestash.dev/apimachinery/apis/addons/v1alpha1/zz_generated.deepcopy.go @@ -0,0 +1,282 @@ +//go:build !ignore_autogenerated +// +build !ignore_autogenerated + +/* +Copyright AppsCode Inc. and Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +// Code generated by controller-gen. DO NOT EDIT. + +package v1alpha1 + +import ( + "k8s.io/api/core/v1" + runtime "k8s.io/apimachinery/pkg/runtime" + apiv1 "kmodules.xyz/offshoot-api/api/v1" + "kubestash.dev/apimachinery/apis" +) + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Addon) DeepCopyInto(out *Addon) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Addon. +func (in *Addon) DeepCopy() *Addon { + if in == nil { + return nil + } + out := new(Addon) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Addon) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AddonList) DeepCopyInto(out *AddonList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Addon, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddonList. +func (in *AddonList) DeepCopy() *AddonList { + if in == nil { + return nil + } + out := new(AddonList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *AddonList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *AddonSpec) DeepCopyInto(out *AddonSpec) { + *out = *in + if in.BackupTasks != nil { + in, out := &in.BackupTasks, &out.BackupTasks + *out = make([]Task, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.RestoreTasks != nil { + in, out := &in.RestoreTasks, &out.RestoreTasks + *out = make([]Task, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new AddonSpec. +func (in *AddonSpec) DeepCopy() *AddonSpec { + if in == nil { + return nil + } + out := new(AddonSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Function) DeepCopyInto(out *Function) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ObjectMeta.DeepCopyInto(&out.ObjectMeta) + in.Spec.DeepCopyInto(&out.Spec) +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Function. +func (in *Function) DeepCopy() *Function { + if in == nil { + return nil + } + out := new(Function) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *Function) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FunctionList) DeepCopyInto(out *FunctionList) { + *out = *in + out.TypeMeta = in.TypeMeta + in.ListMeta.DeepCopyInto(&out.ListMeta) + if in.Items != nil { + in, out := &in.Items, &out.Items + *out = make([]Function, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FunctionList. +func (in *FunctionList) DeepCopy() *FunctionList { + if in == nil { + return nil + } + out := new(FunctionList) + in.DeepCopyInto(out) + return out +} + +// DeepCopyObject is an autogenerated deepcopy function, copying the receiver, creating a new runtime.Object. +func (in *FunctionList) DeepCopyObject() runtime.Object { + if c := in.DeepCopy(); c != nil { + return c + } + return nil +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *FunctionSpec) DeepCopyInto(out *FunctionSpec) { + *out = *in + if in.Command != nil { + in, out := &in.Command, &out.Command + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Args != nil { + in, out := &in.Args, &out.Args + *out = make([]string, len(*in)) + copy(*out, *in) + } + if in.Ports != nil { + in, out := &in.Ports, &out.Ports + *out = make([]v1.ContainerPort, len(*in)) + copy(*out, *in) + } + if in.VolumeMounts != nil { + in, out := &in.VolumeMounts, &out.VolumeMounts + *out = make([]v1.VolumeMount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.VolumeDevices != nil { + in, out := &in.VolumeDevices, &out.VolumeDevices + *out = make([]v1.VolumeDevice, len(*in)) + copy(*out, *in) + } + if in.RuntimeSettings != nil { + in, out := &in.RuntimeSettings, &out.RuntimeSettings + *out = new(apiv1.ContainerRuntimeSettings) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new FunctionSpec. +func (in *FunctionSpec) DeepCopy() *FunctionSpec { + if in == nil { + return nil + } + out := new(FunctionSpec) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *Task) DeepCopyInto(out *Task) { + *out = *in + if in.Parameters != nil { + in, out := &in.Parameters, &out.Parameters + *out = make([]apis.ParameterDefinition, len(*in)) + copy(*out, *in) + } + if in.VolumeTemplate != nil { + in, out := &in.VolumeTemplate, &out.VolumeTemplate + *out = make([]VolumeTemplate, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.VolumeMounts != nil { + in, out := &in.VolumeMounts, &out.VolumeMounts + *out = make([]v1.VolumeMount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } + if in.PassThroughMounts != nil { + in, out := &in.PassThroughMounts, &out.PassThroughMounts + *out = make([]v1.VolumeMount, len(*in)) + for i := range *in { + (*in)[i].DeepCopyInto(&(*out)[i]) + } + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new Task. +func (in *Task) DeepCopy() *Task { + if in == nil { + return nil + } + out := new(Task) + in.DeepCopyInto(out) + return out +} + +// DeepCopyInto is an autogenerated deepcopy function, copying the receiver, writing into out. in must be non-nil. +func (in *VolumeTemplate) DeepCopyInto(out *VolumeTemplate) { + *out = *in + if in.Source != nil { + in, out := &in.Source, &out.Source + *out = new(apis.VolumeSource) + (*in).DeepCopyInto(*out) + } +} + +// DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new VolumeTemplate. +func (in *VolumeTemplate) DeepCopy() *VolumeTemplate { + if in == nil { + return nil + } + out := new(VolumeTemplate) + in.DeepCopyInto(out) + return out +} diff --git a/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/backupblueprint_webhook.go b/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/backupblueprint_webhook.go index 2d22b96e..3c438f98 100644 --- a/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/backupblueprint_webhook.go +++ b/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/backupblueprint_webhook.go @@ -65,24 +65,22 @@ var _ webhook.Validator = &BackupBlueprint{} func (r *BackupBlueprint) ValidateCreate() error { backupblueprintlog.Info("validate create", "name", r.Name) - c, err := getNewRuntimeClient() - if err != nil { - return fmt.Errorf("failed to set Kubernetes client, Reason: %w", err) + if err := r.validateUsagePolicy(); err != nil { + return err } - return r.validateBackendsAgainstUsagePolicy(context.Background(), c) + return r.validateBackendsAgainstUsagePolicy(context.Background(), apis.GetRuntimeClient()) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *BackupBlueprint) ValidateUpdate(old runtime.Object) error { backupblueprintlog.Info("validate update", "name", r.Name) - c, err := getNewRuntimeClient() - if err != nil { - return fmt.Errorf("failed to set Kubernetes client, Reason: %w", err) + if err := r.validateUsagePolicy(); err != nil { + return err } - return r.validateBackendsAgainstUsagePolicy(context.Background(), c) + return r.validateBackendsAgainstUsagePolicy(context.Background(), apis.GetRuntimeClient()) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type @@ -145,3 +143,11 @@ func (r *BackupBlueprint) getBackupStorage(ctx context.Context, c client.Client, } return bs, nil } + +func (r *BackupBlueprint) validateUsagePolicy() error { + if *r.Spec.UsagePolicy.AllowedNamespaces.From == apis.NamespacesFromSelector && + r.Spec.UsagePolicy.AllowedNamespaces.Selector == nil { + return fmt.Errorf("selector cannot be empty for usage policy of type %q", apis.NamespacesFromSelector) + } + return nil +} diff --git a/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/backupconfiguration_webhook.go b/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/backupconfiguration_webhook.go index a99af027..829dd903 100644 --- a/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/backupconfiguration_webhook.go +++ b/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/backupconfiguration_webhook.go @@ -23,16 +23,14 @@ import ( kerr "k8s.io/apimachinery/pkg/api/errors" v1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - restclient "k8s.io/client-go/rest" kmapi "kmodules.xyz/client-go/api/v1" "kubestash.dev/apimachinery/apis" storageapi "kubestash.dev/apimachinery/apis/storage/v1alpha1" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/apiutil" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" + "sync" ) // log is for logging in this package. @@ -54,10 +52,7 @@ var _ webhook.Defaulter = &BackupConfiguration{} func (b *BackupConfiguration) Default() { backupconfigurationlog.Info("default", "name", b.Name) - c, err := getNewRuntimeClient() - if err != nil { - backupconfigurationlog.Error(err, "failed to set Kubernetes client") - } + c := apis.GetRuntimeClient() if len(b.Spec.Backends) == 0 { b.setDefaultBackend(context.Background(), c) @@ -191,50 +186,27 @@ var _ webhook.Validator = &BackupConfiguration{} func (b *BackupConfiguration) ValidateCreate() error { backupconfigurationlog.Info("validate create", apis.KeyName, b.Name) - c, err := getNewRuntimeClient() - if err != nil { - return fmt.Errorf("failed to set Kubernetes client, Reason: %w", err) - } + c := apis.GetRuntimeClient() - if err = b.validateBackends(); err != nil { + if err := b.validateBackends(); err != nil { return err } - if err = b.validateSessions(context.Background(), c); err != nil { + if err := b.validateSessions(context.Background(), c); err != nil { return err } - if err = b.validateBackendsAgainstUsagePolicy(context.Background(), c); err != nil { + if err := b.validateBackendsAgainstUsagePolicy(context.Background(), c); err != nil { return err } return b.validateHookTemplatesAgainstUsagePolicy(context.Background(), c) } -func getNewRuntimeClient() (client.Client, error) { - config, err := restclient.InClusterConfig() - if err != nil { - return nil, fmt.Errorf("failed to get Kubernetes config. Reason: %w", err) - } - scheme := runtime.NewScheme() - utilruntime.Must(storageapi.AddToScheme(scheme)) - utilruntime.Must(core.AddToScheme(scheme)) - utilruntime.Must(AddToScheme(scheme)) - - mapper, err := apiutil.NewDynamicRESTMapper(config) - if err != nil { - return nil, err - } - - return client.New(config, client.Options{ - Scheme: scheme, - Mapper: mapper, - Opts: client.WarningHandlerOptions{ - SuppressWarnings: false, - AllowDuplicateLogs: false, - }, - }) -} +var ( + rc client.Client + once sync.Once +) func (b *BackupConfiguration) validateBackends() error { if len(b.Spec.Backends) == 0 { @@ -651,20 +623,16 @@ func (b *BackupConfiguration) getHookTemplatesFromHookInfo(hooks []HookInfo) []H // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (b *BackupConfiguration) ValidateUpdate(old runtime.Object) error { backupconfigurationlog.Info("validate update", apis.KeyName, b.Name) - c, err := getNewRuntimeClient() - if err != nil { - return fmt.Errorf("failed to set Kubernetes client. Reason: %w", err) - } - - if err = b.validateBackends(); err != nil { + c := apis.GetRuntimeClient() + if err := b.validateBackends(); err != nil { return err } - if err = b.validateSessions(context.Background(), c); err != nil { + if err := b.validateSessions(context.Background(), c); err != nil { return err } - if err = b.validateBackendsAgainstUsagePolicy(context.Background(), c); err != nil { + if err := b.validateBackendsAgainstUsagePolicy(context.Background(), c); err != nil { return err } diff --git a/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/hooktemplate_webhook.go b/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/hooktemplate_webhook.go index 09256949..29c985b5 100644 --- a/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/hooktemplate_webhook.go +++ b/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/hooktemplate_webhook.go @@ -67,6 +67,10 @@ func (r *HookTemplate) ValidateCreate() error { return err } + if err := r.validateUsagePolicy(); err != nil { + return err + } + return r.validateExecutorInfo() } @@ -82,6 +86,10 @@ func (r *HookTemplate) ValidateUpdate(old runtime.Object) error { return err } + if err := r.validateUsagePolicy(); err != nil { + return err + } + return r.validateExecutorInfo() } @@ -134,3 +142,11 @@ func (r *HookTemplate) validateActionForNonFunctionExecutor() error { } return nil } + +func (r *HookTemplate) validateUsagePolicy() error { + if *r.Spec.UsagePolicy.AllowedNamespaces.From == apis.NamespacesFromSelector && + r.Spec.UsagePolicy.AllowedNamespaces.Selector == nil { + return fmt.Errorf("selector cannot be empty for usage policy of type %q", apis.NamespacesFromSelector) + } + return nil +} diff --git a/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/restoresession_types.go b/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/restoresession_types.go index 49dbd9e4..e039d61b 100644 --- a/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/restoresession_types.go +++ b/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/restoresession_types.go @@ -90,6 +90,14 @@ type ManifestRestoreOptions struct { // Postgres specifies the options for selecting particular Postgres components to restore in manifest restore // +optional Postgres *KubeDBManifestOptions `json:"postgres,omitempty"` + + // MySQL specifies the options for selecting particular MySQL components to restore in manifest restore + // +optional + MySQL *KubeDBManifestOptions `json:"mySQL,omitempty"` + + // MariaDB specifies the options for selecting particular MariaDB components to restore in manifest restore + // +optional + MariaDB *KubeDBManifestOptions `json:"mariaDB,omitempty"` } type KubeDBManifestOptions struct { diff --git a/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/restoresession_webhook.go b/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/restoresession_webhook.go index 88656ebb..1ef12f14 100644 --- a/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/restoresession_webhook.go +++ b/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/restoresession_webhook.go @@ -23,6 +23,7 @@ import ( kerr "k8s.io/apimachinery/pkg/api/errors" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" + "kubestash.dev/apimachinery/apis" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" logf "sigs.k8s.io/controller-runtime/pkg/log" @@ -49,32 +50,22 @@ var _ webhook.Validator = &RestoreSession{} func (r *RestoreSession) ValidateCreate() error { restoresessionlog.Info("validate create", "name", r.Name) - c, err := getNewRuntimeClient() - if err != nil { - return fmt.Errorf("failed to set Kubernetes client, Reason: %w", err) - } - if err := r.ValidateDataSource(); err != nil { return err } - return r.validateHookTemplatesAgainstUsagePolicy(context.Background(), c) + return r.validateHookTemplatesAgainstUsagePolicy(context.Background(), apis.GetRuntimeClient()) } // ValidateUpdate implements webhook.Validator so a webhook will be registered for the type func (r *RestoreSession) ValidateUpdate(old runtime.Object) error { restoresessionlog.Info("validate update", "name", r.Name) - c, err := getNewRuntimeClient() - if err != nil { - return fmt.Errorf("failed to set Kubernetes client, Reason: %w", err) - } - if err := r.ValidateDataSource(); err != nil { return err } - return r.validateHookTemplatesAgainstUsagePolicy(context.Background(), c) + return r.validateHookTemplatesAgainstUsagePolicy(context.Background(), apis.GetRuntimeClient()) } // ValidateDelete implements webhook.Validator so a webhook will be registered for the type diff --git a/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/zz_generated.deepcopy.go b/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/zz_generated.deepcopy.go index 8f9a889d..85e3c5f3 100644 --- a/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/zz_generated.deepcopy.go +++ b/vendor/kubestash.dev/apimachinery/apis/core/v1alpha1/zz_generated.deepcopy.go @@ -1034,6 +1034,16 @@ func (in *ManifestRestoreOptions) DeepCopyInto(out *ManifestRestoreOptions) { *out = new(KubeDBManifestOptions) **out = **in } + if in.MySQL != nil { + in, out := &in.MySQL, &out.MySQL + *out = new(KubeDBManifestOptions) + **out = **in + } + if in.MariaDB != nil { + in, out := &in.MariaDB, &out.MariaDB + *out = new(KubeDBManifestOptions) + **out = **in + } } // DeepCopy is an autogenerated deepcopy function, copying the receiver, creating a new ManifestRestoreOptions. diff --git a/vendor/kubestash.dev/apimachinery/apis/helpers.go b/vendor/kubestash.dev/apimachinery/apis/helpers.go index 0a3fd926..58c0dee0 100644 --- a/vendor/kubestash.dev/apimachinery/apis/helpers.go +++ b/vendor/kubestash.dev/apimachinery/apis/helpers.go @@ -17,10 +17,29 @@ limitations under the License. package apis import ( - "encoding/json" - "gomodules.xyz/envsubst" + "sync" + + "sigs.k8s.io/controller-runtime/pkg/client" +) + +var ( + once sync.Once + kc client.Client ) +func GetRuntimeClient() client.Client { + if kc == nil { + panic("runtime client is not initialized!") + } + return kc +} + +func SetRuntimeClient(client client.Client) { + once.Do(func() { + kc = client + }) +} + func UpsertLabels(oldLabels, newLabels map[string]string) map[string]string { if oldLabels == nil { oldLabels = make(map[string]string, len(newLabels)) @@ -30,16 +49,3 @@ func UpsertLabels(oldLabels, newLabels map[string]string) map[string]string { } return oldLabels } - -func ResolveWithInputs(obj interface{}, inputs map[string]string) error { - // convert to JSON, apply replacements and convert back to struct - jsonObj, err := json.Marshal(obj) - if err != nil { - return err - } - resolved, err := envsubst.EvalMap(string(jsonObj), inputs) - if err != nil { - return err - } - return json.Unmarshal([]byte(resolved), obj) -} diff --git a/vendor/kubestash.dev/apimachinery/apis/storage/v1alpha1/backupstorage_webhook.go b/vendor/kubestash.dev/apimachinery/apis/storage/v1alpha1/backupstorage_webhook.go index 89dd4b08..89b06f58 100644 --- a/vendor/kubestash.dev/apimachinery/apis/storage/v1alpha1/backupstorage_webhook.go +++ b/vendor/kubestash.dev/apimachinery/apis/storage/v1alpha1/backupstorage_webhook.go @@ -20,13 +20,10 @@ import ( "context" "fmt" "k8s.io/apimachinery/pkg/runtime" - utilruntime "k8s.io/apimachinery/pkg/util/runtime" - restclient "k8s.io/client-go/rest" "kubestash.dev/apimachinery/apis" "reflect" ctrl "sigs.k8s.io/controller-runtime" "sigs.k8s.io/controller-runtime/pkg/client" - "sigs.k8s.io/controller-runtime/pkg/client/apiutil" logf "sigs.k8s.io/controller-runtime/pkg/log" "sigs.k8s.io/controller-runtime/pkg/webhook" ) @@ -64,10 +61,7 @@ var _ webhook.Validator = &BackupStorage{} func (r *BackupStorage) ValidateCreate() error { backupstoragelog.Info("validate create", "name", r.Name) - c, err := getNewRuntimeClient() - if err != nil { - return fmt.Errorf("failed to set Kubernetes client. Reason: %w", err) - } + c := apis.GetRuntimeClient() if r.Spec.Default { if err := r.validateSingleDefaultBackupStorageInSameNamespace(context.Background(), c); err != nil { @@ -75,6 +69,10 @@ func (r *BackupStorage) ValidateCreate() error { } } + if err := r.validateUsagePolicy(); err != nil { + return err + } + return r.validateUniqueDirectory(context.Background(), c) } @@ -82,18 +80,19 @@ func (r *BackupStorage) ValidateCreate() error { func (r *BackupStorage) ValidateUpdate(old runtime.Object) error { backupstoragelog.Info("validate update", "name", r.Name) - c, err := getNewRuntimeClient() - if err != nil { - return fmt.Errorf("failed to set Kubernetes client. Reason: %w", err) - } + c := apis.GetRuntimeClient() if r.Spec.Default { - if err = r.validateSingleDefaultBackupStorageInSameNamespace(context.Background(), c); err != nil { + if err := r.validateSingleDefaultBackupStorageInSameNamespace(context.Background(), c); err != nil { return err } } - if err = r.validateUpdateStorage(old.(*BackupStorage)); err != nil { + if err := r.validateUsagePolicy(); err != nil { + return err + } + + if err := r.validateUpdateStorage(old.(*BackupStorage)); err != nil { return err } @@ -133,6 +132,14 @@ func (r *BackupStorage) validateSingleDefaultBackupStorageInSameNamespace(ctx co return nil } +func (r *BackupStorage) validateUsagePolicy() error { + if *r.Spec.UsagePolicy.AllowedNamespaces.From == apis.NamespacesFromSelector && + r.Spec.UsagePolicy.AllowedNamespaces.Selector == nil { + return fmt.Errorf("selector cannot be empty for usage policy of type %q", apis.NamespacesFromSelector) + } + return nil +} + func (r *BackupStorage) isSameBackupStorage(bs BackupStorage) bool { if r.Namespace == bs.Namespace && r.Name == bs.Name { @@ -208,26 +215,3 @@ func (r *BackupStorage) isPointToSameDir(bs BackupStorage) bool { return false } } - -func getNewRuntimeClient() (client.Client, error) { - config, err := restclient.InClusterConfig() - if err != nil { - return nil, fmt.Errorf("failed to get Kubernetes config. Reason: %w", err) - } - scheme := runtime.NewScheme() - utilruntime.Must(AddToScheme(scheme)) - - mapper, err := apiutil.NewDynamicRESTMapper(config) - if err != nil { - return nil, err - } - - return client.New(config, client.Options{ - Scheme: scheme, - Mapper: mapper, - Opts: client.WarningHandlerOptions{ - SuppressWarnings: false, - AllowDuplicateLogs: false, - }, - }) -} diff --git a/vendor/kubestash.dev/apimachinery/apis/storage/v1alpha1/retentionpolicy_webhook.go b/vendor/kubestash.dev/apimachinery/apis/storage/v1alpha1/retentionpolicy_webhook.go index 4e3a5e94..a567c16d 100644 --- a/vendor/kubestash.dev/apimachinery/apis/storage/v1alpha1/retentionpolicy_webhook.go +++ b/vendor/kubestash.dev/apimachinery/apis/storage/v1alpha1/retentionpolicy_webhook.go @@ -80,10 +80,7 @@ var _ webhook.Validator = &RetentionPolicy{} func (r *RetentionPolicy) ValidateCreate() error { retentionpolicylog.Info("validate create", "name", r.Name) - c, err := getNewRuntimeClient() - if err != nil { - return fmt.Errorf("failed to set Kubernetes client. Reason: %w", err) - } + c := apis.GetRuntimeClient() if err := r.validateMaxRetentionPeriodFormat(); err != nil { return err @@ -92,6 +89,11 @@ func (r *RetentionPolicy) ValidateCreate() error { if err := r.validateProvidedPolicy(); err != nil { return err } + + if err := r.validateUsagePolicy(); err != nil { + return err + } + return r.validateSingleDefaultRetentionPolicyInSameNamespace(context.Background(), c) } @@ -99,10 +101,7 @@ func (r *RetentionPolicy) ValidateCreate() error { func (r *RetentionPolicy) ValidateUpdate(old runtime.Object) error { retentionpolicylog.Info("validate update", "name", r.Name) - c, err := getNewRuntimeClient() - if err != nil { - return fmt.Errorf("failed to set Kubernetes client. Reason: %w", err) - } + c := apis.GetRuntimeClient() if err := r.validateMaxRetentionPeriodFormat(); err != nil { return err @@ -111,6 +110,11 @@ func (r *RetentionPolicy) ValidateUpdate(old runtime.Object) error { if err := r.validateProvidedPolicy(); err != nil { return err } + + if err := r.validateUsagePolicy(); err != nil { + return err + } + return r.validateSingleDefaultRetentionPolicyInSameNamespace(context.Background(), c) } @@ -156,6 +160,14 @@ func (r *RetentionPolicy) isSameRetentionPolicy(rp RetentionPolicy) bool { return false } +func (r *RetentionPolicy) validateUsagePolicy() error { + if *r.Spec.UsagePolicy.AllowedNamespaces.From == apis.NamespacesFromSelector && + r.Spec.UsagePolicy.AllowedNamespaces.Selector == nil { + return fmt.Errorf("selector cannot be empty for usage policy of type %q", apis.NamespacesFromSelector) + } + return nil +} + // ValidateDelete implements webhook.Validator so a webhook will be registered for the type func (r *RetentionPolicy) ValidateDelete() error { retentionpolicylog.Info("validate delete", "name", r.Name) diff --git a/vendor/kubestash.dev/apimachinery/crds/core.kubestash.com_restoresessions.yaml b/vendor/kubestash.dev/apimachinery/crds/core.kubestash.com_restoresessions.yaml index 6474b2ba..4b869be6 100644 --- a/vendor/kubestash.dev/apimachinery/crds/core.kubestash.com_restoresessions.yaml +++ b/vendor/kubestash.dev/apimachinery/crds/core.kubestash.com_restoresessions.yaml @@ -20879,6 +20879,39 @@ spec: description: ManifestOptions provide options to select particular manifest object to restore properties: + mariaDB: + description: MariaDB specifies the options for selecting particular + MariaDB components to restore in manifest restore + properties: + authSecret: + description: AuthSecret specifies whether to restore the AuthSecret + manifest or not + type: boolean + authSecretName: + description: AuthSecretName specifies new name of the AuthSecret + yaml after restore + type: string + configSecret: + description: ConfigSecret specifies whether to restore the + ConfigSecret manifest or not + type: boolean + configSecretName: + description: ConfigSecretName specifies new name of the ConfigSecret + yaml after restore + type: string + db: + description: DB specifies whether to restore the DB manifest + or not + type: boolean + dbName: + description: DBName specifies the new name of the DB yaml + after restore + type: string + issuerRefName: + description: IssuerRefName specifies new name of the IssuerRef + after restore + type: string + type: object mongoDB: description: MongoDB specifies the options for selecting particular MongoDB components to restore in manifest restore @@ -20912,6 +20945,39 @@ spec: after restore type: string type: object + mySQL: + description: MySQL specifies the options for selecting particular + MySQL components to restore in manifest restore + properties: + authSecret: + description: AuthSecret specifies whether to restore the AuthSecret + manifest or not + type: boolean + authSecretName: + description: AuthSecretName specifies new name of the AuthSecret + yaml after restore + type: string + configSecret: + description: ConfigSecret specifies whether to restore the + ConfigSecret manifest or not + type: boolean + configSecretName: + description: ConfigSecretName specifies new name of the ConfigSecret + yaml after restore + type: string + db: + description: DB specifies whether to restore the DB manifest + or not + type: boolean + dbName: + description: DBName specifies the new name of the DB yaml + after restore + type: string + issuerRefName: + description: IssuerRefName specifies new name of the IssuerRef + after restore + type: string + type: object postgres: description: Postgres specifies the options for selecting particular Postgres components to restore in manifest restore diff --git a/vendor/kubestash.dev/apimachinery/pkg/utils.go b/vendor/kubestash.dev/apimachinery/pkg/utils.go new file mode 100644 index 00000000..1967185b --- /dev/null +++ b/vendor/kubestash.dev/apimachinery/pkg/utils.go @@ -0,0 +1,78 @@ +/* +Copyright AppsCode Inc. and Contributors + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +*/ + +package pkg + +import ( + "fmt" + "gomodules.xyz/envsubst" + core "k8s.io/api/core/v1" + "kubestash.dev/apimachinery/apis" + + "encoding/json" + vsapi "github.com/kubernetes-csi/external-snapshotter/client/v4/apis/volumesnapshot/v1" + clientsetscheme "k8s.io/client-go/kubernetes/scheme" + cu "kmodules.xyz/client-go/client" + addonapi "kubestash.dev/apimachinery/apis/addons/v1alpha1" + coreapi "kubestash.dev/apimachinery/apis/core/v1alpha1" + storageapi "kubestash.dev/apimachinery/apis/storage/v1alpha1" + ctrl "sigs.k8s.io/controller-runtime" + "sigs.k8s.io/controller-runtime/pkg/client" +) + +func NewUncachedClient() (client.Client, error) { + cfg, err := ctrl.GetConfig() + if err != nil { + return nil, fmt.Errorf("failed to get Kubernetes config. Reason: %w", err) + } + + return cu.NewUncachedClient( + cfg, + clientsetscheme.AddToScheme, + storageapi.AddToScheme, + coreapi.AddToScheme, + addonapi.AddToScheme, + vsapi.AddToScheme, + ) +} + +func GetTmpVolumeAndMount() (core.Volume, core.VolumeMount) { + vol := core.Volume{ + Name: apis.TempDirVolumeName, + VolumeSource: core.VolumeSource{ + EmptyDir: &core.EmptyDirVolumeSource{}, + }, + } + mnt := core.VolumeMount{ + Name: apis.TempDirVolumeName, + MountPath: apis.TempDirMountPath, + } + + return vol, mnt +} + +func ResolveWithInputs(obj interface{}, inputs map[string]string) error { + // convert to JSON, apply replacements and convert back to struct + jsonObj, err := json.Marshal(obj) + if err != nil { + return err + } + resolved, err := envsubst.EvalMap(string(jsonObj), inputs) + if err != nil { + return err + } + return json.Unmarshal([]byte(resolved), obj) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 9e02393e..b2463e90 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -822,13 +822,15 @@ kmodules.xyz/offshoot-api/api/v1 # kmodules.xyz/prober v0.25.0 ## explicit; go 1.18 kmodules.xyz/prober/api/v1 -# kubestash.dev/apimachinery v0.2.0 +# kubestash.dev/apimachinery v0.2.1-0.20231213061518-17b1436fc227 ## explicit; go 1.20 kubestash.dev/apimachinery/apis +kubestash.dev/apimachinery/apis/addons/v1alpha1 kubestash.dev/apimachinery/apis/config/v1alpha1 kubestash.dev/apimachinery/apis/core/v1alpha1 kubestash.dev/apimachinery/apis/storage/v1alpha1 kubestash.dev/apimachinery/crds +kubestash.dev/apimachinery/pkg kubestash.dev/apimachinery/pkg/restic # sigs.k8s.io/controller-runtime v0.13.1 ## explicit; go 1.17