package app import ( "context" "fmt" "coopcloud.tech/abra/cli/internal" "coopcloud.tech/abra/pkg/autocomplete" "coopcloud.tech/abra/pkg/client" "coopcloud.tech/abra/pkg/config" containerPkg "coopcloud.tech/abra/pkg/container" "coopcloud.tech/abra/pkg/recipe" "coopcloud.tech/abra/pkg/service" "coopcloud.tech/abra/pkg/upstream/container" "github.com/docker/cli/cli/command" "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/filters" "github.com/sirupsen/logrus" "github.com/urfave/cli" ) var snapshot string var snapshotFlag = &cli.StringFlag{ Name: "snapshot, s", Usage: "Lists specific snapshot", Destination: &snapshot, } var includePath string var includePathFlag = &cli.StringFlag{ Name: "path, p", Usage: "Include path", Destination: &includePath, } var resticRepo string var resticRepoFlag = &cli.StringFlag{ Name: "repo, r", Usage: "Restic repository", Destination: &resticRepo, } var appBackupListCommand = cli.Command{ Name: "list", Aliases: []string{"ls"}, Flags: []cli.Flag{ internal.DebugFlag, internal.OfflineFlag, snapshotFlag, includePathFlag, }, Before: internal.SubCommandBefore, Usage: "List all backups", BashComplete: autocomplete.AppNameComplete, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) if err := recipe.EnsureExists(app.Recipe); err != nil { logrus.Fatal(err) } if !internal.Chaos { if err := recipe.EnsureIsClean(app.Recipe); err != nil { logrus.Fatal(err) } if !internal.Offline { if err := recipe.EnsureUpToDate(app.Recipe); err != nil { logrus.Fatal(err) } } if err := recipe.EnsureLatest(app.Recipe); err != nil { logrus.Fatal(err) } } cl, err := client.New(app.Server) if err != nil { logrus.Fatal(err) } chosenService, err := service.GetServiceByLabel(context.Background(), cl, config.BackupbotLabel, internal.NoInput) if err != nil { logrus.Fatal(err) } logrus.Debugf("retrieved %s as backup enabled service", chosenService.Spec.Name) filters := filters.NewArgs() filters.Add("name", chosenService.Spec.Name) targetContainer, err := containerPkg.GetContainer( context.Background(), cl, filters, internal.NoInput, ) if err != nil { logrus.Fatal(err) } execEnv := []string{fmt.Sprintf("SERVICE=%s", app.Domain)} if snapshot != "" { logrus.Debugf("including SNAPSHOT=%s in backupbot exec invocation", snapshot) execEnv = append(execEnv, fmt.Sprintf("SNAPSHOT=%s", snapshot)) } if includePath != "" { logrus.Debugf("including INCLUDE_PATH=%s in backupbot exec invocation", includePath) execEnv = append(execEnv, fmt.Sprintf("INCLUDE_PATH=%s", includePath)) } execBackupListOpts := types.ExecConfig{ AttachStderr: true, AttachStdin: true, AttachStdout: true, Cmd: []string{"/usr/bin/backup", "--", "ls"}, Detach: false, Env: execEnv, Tty: true, } logrus.Debugf("running backup list on %s with exec config %v", targetContainer.ID, execBackupListOpts) // FIXME: avoid instantiating a new CLI dcli, err := command.NewDockerCli() if err != nil { logrus.Fatal(err) } if err := container.RunExec(dcli, cl, targetContainer.ID, &execBackupListOpts); err != nil { logrus.Fatal(err) } return nil }, } var appBackupDownloadCommand = cli.Command{ Name: "download", Aliases: []string{"d"}, Flags: []cli.Flag{ internal.DebugFlag, internal.OfflineFlag, snapshotFlag, includePathFlag, }, Before: internal.SubCommandBefore, Usage: "Download a backup", BashComplete: autocomplete.AppNameComplete, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) if err := recipe.EnsureExists(app.Recipe); err != nil { logrus.Fatal(err) } if !internal.Chaos { if err := recipe.EnsureIsClean(app.Recipe); err != nil { logrus.Fatal(err) } if !internal.Offline { if err := recipe.EnsureUpToDate(app.Recipe); err != nil { logrus.Fatal(err) } } if err := recipe.EnsureLatest(app.Recipe); err != nil { logrus.Fatal(err) } } cl, err := client.New(app.Server) if err != nil { logrus.Fatal(err) } chosenService, err := service.GetServiceByLabel(context.Background(), cl, config.BackupbotLabel, internal.NoInput) if err != nil { logrus.Fatal(err) } logrus.Debugf("retrieved %s as backup enabled service", chosenService.Spec.Name) filters := filters.NewArgs() filters.Add("name", chosenService.Spec.Name) targetContainer, err := containerPkg.GetContainer( context.Background(), cl, filters, internal.NoInput, ) if err != nil { logrus.Fatal(err) } execEnv := []string{fmt.Sprintf("SERVICE=%s", app.Domain)} if snapshot != "" { logrus.Debugf("including SNAPSHOT=%s in backupbot exec invocation", snapshot) execEnv = append(execEnv, fmt.Sprintf("SNAPSHOT=%s", snapshot)) } if includePath != "" { logrus.Debugf("including INCLUDE_PATH=%s in backupbot exec invocation", includePath) execEnv = append(execEnv, fmt.Sprintf("INCLUDE_PATH=%s", includePath)) } execBackupListOpts := types.ExecConfig{ AttachStderr: true, AttachStdin: true, AttachStdout: true, Cmd: []string{"/usr/bin/backup", "--", "download"}, Detach: false, Env: execEnv, Tty: true, } logrus.Debugf("running backup list on %s with exec config %v", targetContainer.ID, execBackupListOpts) // FIXME: avoid instantiating a new CLI dcli, err := command.NewDockerCli() if err != nil { logrus.Fatal(err) } if err := container.RunExec(dcli, cl, targetContainer.ID, &execBackupListOpts); err != nil { logrus.Fatal(err) } return nil }, } var appBackupCreateCommand = cli.Command{ Name: "create", Aliases: []string{"c"}, Flags: []cli.Flag{ internal.DebugFlag, internal.OfflineFlag, resticRepoFlag, }, Before: internal.SubCommandBefore, Usage: "Create a new backup", BashComplete: autocomplete.AppNameComplete, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) if err := recipe.EnsureExists(app.Recipe); err != nil { logrus.Fatal(err) } if !internal.Chaos { if err := recipe.EnsureIsClean(app.Recipe); err != nil { logrus.Fatal(err) } if !internal.Offline { if err := recipe.EnsureUpToDate(app.Recipe); err != nil { logrus.Fatal(err) } } if err := recipe.EnsureLatest(app.Recipe); err != nil { logrus.Fatal(err) } } cl, err := client.New(app.Server) if err != nil { logrus.Fatal(err) } chosenService, err := service.GetServiceByLabel(context.Background(), cl, config.BackupbotLabel, internal.NoInput) if err != nil { logrus.Fatal(err) } logrus.Debugf("retrieved %s as backup enabled service", chosenService.Spec.Name) filters := filters.NewArgs() filters.Add("name", chosenService.Spec.Name) targetContainer, err := containerPkg.GetContainer( context.Background(), cl, filters, internal.NoInput, ) if err != nil { logrus.Fatal(err) } execEnv := []string{fmt.Sprintf("SERVICE=%s", app.Domain)} if resticRepo != "" { logrus.Debugf("including RESTIC_REPO=%s in backupbot exec invocation", resticRepo) execEnv = append(execEnv, fmt.Sprintf("RESTIC_REPO=%s", resticRepo)) } execBackupListOpts := types.ExecConfig{ AttachStderr: true, AttachStdin: true, AttachStdout: true, Cmd: []string{"/usr/bin/backup", "--", "create"}, Detach: false, Env: execEnv, Tty: true, } logrus.Debugf("running backup list on %s with exec config %v", targetContainer.ID, execBackupListOpts) // FIXME: avoid instantiating a new CLI dcli, err := command.NewDockerCli() if err != nil { logrus.Fatal(err) } if err := container.RunExec(dcli, cl, targetContainer.ID, &execBackupListOpts); err != nil { logrus.Fatal(err) } return nil }, } var appBackupSnapshotsCommand = cli.Command{ Name: "snapshots", Aliases: []string{"s"}, Flags: []cli.Flag{ internal.DebugFlag, internal.OfflineFlag, snapshotFlag, }, Before: internal.SubCommandBefore, Usage: "List backup snapshots", BashComplete: autocomplete.AppNameComplete, Action: func(c *cli.Context) error { app := internal.ValidateApp(c) if err := recipe.EnsureExists(app.Recipe); err != nil { logrus.Fatal(err) } if !internal.Chaos { if err := recipe.EnsureIsClean(app.Recipe); err != nil { logrus.Fatal(err) } if !internal.Offline { if err := recipe.EnsureUpToDate(app.Recipe); err != nil { logrus.Fatal(err) } } if err := recipe.EnsureLatest(app.Recipe); err != nil { logrus.Fatal(err) } } cl, err := client.New(app.Server) if err != nil { logrus.Fatal(err) } chosenService, err := service.GetServiceByLabel(context.Background(), cl, config.BackupbotLabel, internal.NoInput) if err != nil { logrus.Fatal(err) } logrus.Debugf("retrieved %s as backup enabled service", chosenService.Spec.Name) filters := filters.NewArgs() filters.Add("name", chosenService.Spec.Name) targetContainer, err := containerPkg.GetContainer( context.Background(), cl, filters, internal.NoInput, ) if err != nil { logrus.Fatal(err) } execEnv := []string{fmt.Sprintf("SERVICE=%s", app.Domain)} if snapshot != "" { logrus.Debugf("including SNAPSHOT=%s in backupbot exec invocation", snapshot) execEnv = append(execEnv, fmt.Sprintf("SNAPSHOT=%s", snapshot)) } execBackupListOpts := types.ExecConfig{ AttachStderr: true, AttachStdin: true, AttachStdout: true, Cmd: []string{"/usr/bin/backup", "--", "snapshots"}, Detach: false, Env: execEnv, Tty: true, } logrus.Debugf("running backup list on %s with exec config %v", targetContainer.ID, execBackupListOpts) // FIXME: avoid instantiating a new CLI dcli, err := command.NewDockerCli() if err != nil { logrus.Fatal(err) } if err := container.RunExec(dcli, cl, targetContainer.ID, &execBackupListOpts); err != nil { logrus.Fatal(err) } return nil }, } var appBackupCommand = cli.Command{ Name: "backup", Aliases: []string{"b"}, Usage: "Manage app backups", ArgsUsage: "", Subcommands: []cli.Command{ appBackupListCommand, appBackupSnapshotsCommand, appBackupDownloadCommand, appBackupCreateCommand, }, }