diff --git a/Makefile b/Makefile index d78f039..561c16b 100644 --- a/Makefile +++ b/Makefile @@ -2,7 +2,7 @@ # Variables ############################################################################## APP_NAME = datasafed -VERSION ?= 0.0.6 +VERSION ?= 0.1.0 GITHUB_PROXY ?= GIT_COMMIT = $(shell git rev-list -1 HEAD) GIT_VERSION = $(shell git describe --always --abbrev=0 --tag) diff --git a/cmd/app.go b/cmd/app.go index 133e59e..5379dc8 100644 --- a/cmd/app.go +++ b/cmd/app.go @@ -4,25 +4,12 @@ import ( "context" "fmt" "os" - "strconv" - "strings" "github.com/spf13/cobra" - "github.com/apecloud/datasafed/pkg/config" + "github.com/apecloud/datasafed/pkg/app" "github.com/apecloud/datasafed/pkg/logging" "github.com/apecloud/datasafed/pkg/storage" - "github.com/apecloud/datasafed/pkg/storage/kopia" - "github.com/apecloud/datasafed/pkg/storage/rclone" -) - -const ( - backendBasePathEnv = "DATASAFED_BACKEND_BASE_PATH" - kopiaRepoRootEnv = "DATASAFED_KOPIA_REPO_ROOT" - kopiaPasswordEnv = "DATASAFED_KOPIA_PASSWORD" - kopiaDisableCacheEnv = "DATASAFED_KOPIA_DISABLE_CACHE" - kopiaMaintenanceEnv = "DATASAFED_KOPIA_MAINTENANCE" - kopiaSafetyEnv = "DATASAFED_KOPIA_SAFETY" ) var ( @@ -37,23 +24,23 @@ var ( doNotInitStorage bool globalStorage storage.Storage appCtx context.Context = context.Background() - onFinishFuncs []func() ) func init() { rootCmd.PersistentPreRunE = func(cmd *cobra.Command, args []string) error { appCtx = logging.WithLogger(appCtx, logging.DefaultLoggerFactory) if !doNotInitStorage { - if err := initStorage(); err != nil { + if err := app.InitGlobalStorage(appCtx, configFile); err != nil { return err } + var err error + globalStorage, err = app.GetGlobalStorage() + exitIfError(err) } return nil } rootCmd.PersistentPostRunE = func(cmd *cobra.Command, args []string) error { - for _, fn := range onFinishFuncs { - fn() - } + app.InvokeFinalizers() return nil } rootCmd.PersistentFlags().StringVarP(&configFile, "conf", "c", @@ -75,65 +62,6 @@ func exitIfError(err error) { } } -func initStorage() error { - if err := config.InitGlobal(configFile); err != nil { - return err - } - - basePath := strings.TrimSpace(os.Getenv(backendBasePathEnv)) - storageConf := config.GetGlobal().GetAll(config.StorageSection) - - if kopiaRoot := strings.TrimSpace(os.Getenv(kopiaRepoRootEnv)); kopiaRoot != "" { - return initKopiaStorage(storageConf, basePath, kopiaRoot) - } else { - st, err := createStorage(storageConf, basePath) - if err != nil { - return err - } - globalStorage = st - return nil - } -} - -func initKopiaStorage(storageConf map[string]string, basePath, kopiaRoot string) error { - underlying, err := createStorage(storageConf, "") - if err != nil { - return err - } - kopia.SetUnderlyingStorage(underlying) - storageConf[kopia.RepoRootKey] = kopiaRoot - storageConf[kopia.PasswordKey] = strings.TrimSpace(os.Getenv(kopiaPasswordEnv)) - storageConf[kopia.DisableCacheKey] = strings.TrimSpace(os.Getenv(kopiaDisableCacheEnv)) - st, err := kopia.New(appCtx, storageConf, basePath) - if err != nil { - return err - } - globalStorage = st - - maintenance := os.Getenv(kopiaMaintenanceEnv) - if ok, _ := strconv.ParseBool(maintenance); ok { - onFinish(func() { - err := kopia.RunMaintenance(appCtx, globalStorage, os.Getenv(kopiaSafetyEnv)) - if err != nil { - fmt.Fprintf(os.Stderr, "RunMaintenance() failed, err: %v\n", err) - } - }) - } - return nil -} - -func createStorage(conf map[string]string, basePath string) (storage.Storage, error) { - cloneConf := make(map[string]string, len(conf)) - for k, v := range conf { - cloneConf[k] = v - } - return rclone.New(appCtx, cloneConf, basePath) -} - -func onFinish(fn func()) { - onFinishFuncs = append(onFinishFuncs, fn) -} - func Execute() { exitIfError(rootCmd.Execute()) } diff --git a/pkg/app/globalstorage.go b/pkg/app/globalstorage.go new file mode 100644 index 0000000..05908a0 --- /dev/null +++ b/pkg/app/globalstorage.go @@ -0,0 +1,90 @@ +package app + +import ( + "context" + "fmt" + "os" + "strconv" + "strings" + + "github.com/apecloud/datasafed/pkg/config" + "github.com/apecloud/datasafed/pkg/storage" + "github.com/apecloud/datasafed/pkg/storage/kopia" + "github.com/apecloud/datasafed/pkg/storage/rclone" +) + +const ( + backendBasePathEnv = "DATASAFED_BACKEND_BASE_PATH" + kopiaRepoRootEnv = "DATASAFED_KOPIA_REPO_ROOT" + kopiaPasswordEnv = "DATASAFED_KOPIA_PASSWORD" + kopiaDisableCacheEnv = "DATASAFED_KOPIA_DISABLE_CACHE" + kopiaMaintenanceEnv = "DATASAFED_KOPIA_MAINTENANCE" + kopiaSafetyEnv = "DATASAFED_KOPIA_SAFETY" +) + +var globalStorage storage.Storage + +func InitGlobalStorage(ctx context.Context, configFile string) error { + if globalStorage != nil { + return fmt.Errorf("already inited") + } + if err := config.InitGlobal(configFile); err != nil { + return err + } + + basePath := strings.TrimSpace(os.Getenv(backendBasePathEnv)) + storageConf := config.GetGlobal().GetAll(config.StorageSection) + + if kopiaRoot := strings.TrimSpace(os.Getenv(kopiaRepoRootEnv)); kopiaRoot != "" { + return initKopiaStorage(ctx, storageConf, basePath, kopiaRoot) + } else { + st, err := createStorage(ctx, storageConf, basePath) + if err != nil { + return err + } + globalStorage = st + return nil + } +} + +func GetGlobalStorage() (storage.Storage, error) { + if globalStorage == nil { + return nil, fmt.Errorf("not inited, call InitGlobalStorage() first") + } + return globalStorage, nil +} + +func initKopiaStorage(ctx context.Context, storageConf map[string]string, basePath, kopiaRoot string) error { + underlying, err := createStorage(ctx, storageConf, "") + if err != nil { + return err + } + kopia.SetUnderlyingStorage(underlying) + storageConf[kopia.RepoRootKey] = kopiaRoot + storageConf[kopia.PasswordKey] = strings.TrimSpace(os.Getenv(kopiaPasswordEnv)) + storageConf[kopia.DisableCacheKey] = strings.TrimSpace(os.Getenv(kopiaDisableCacheEnv)) + st, err := kopia.New(ctx, storageConf, basePath) + if err != nil { + return err + } + globalStorage = st + + maintenance := os.Getenv(kopiaMaintenanceEnv) + if ok, _ := strconv.ParseBool(maintenance); ok { + OnFinalize(func() { + err := kopia.RunMaintenance(ctx, globalStorage, os.Getenv(kopiaSafetyEnv)) + if err != nil { + fmt.Fprintf(os.Stderr, "RunMaintenance() failed, err: %v\n", err) + } + }) + } + return nil +} + +func createStorage(ctx context.Context, conf map[string]string, basePath string) (storage.Storage, error) { + cloneConf := make(map[string]string, len(conf)) + for k, v := range conf { + cloneConf[k] = v + } + return rclone.New(ctx, cloneConf, basePath) +} diff --git a/pkg/app/lifecycle.go b/pkg/app/lifecycle.go new file mode 100644 index 0000000..569373b --- /dev/null +++ b/pkg/app/lifecycle.go @@ -0,0 +1,22 @@ +package app + +import "sync" + +var ( + finalizers []func() + finalizersMutex sync.Mutex +) + +func OnFinalize(f func()) { + finalizersMutex.Lock() + defer finalizersMutex.Unlock() + finalizers = append(finalizers, f) +} + +func InvokeFinalizers() { + finalizersMutex.Lock() + defer finalizersMutex.Unlock() + for _, f := range finalizers { + f() + } +}