diff --git a/api/volume_types.go b/api/volume_types.go index a3703ca5a8..695d440d06 100644 --- a/api/volume_types.go +++ b/api/volume_types.go @@ -47,4 +47,5 @@ type VolumeSnapshot struct { Size int `json:"size"` Digest string `json:"digest"` CreatedAt time.Time `json:"created_at"` + Status string `json:"status"` } diff --git a/flaps/flaps_volumes.go b/flaps/flaps_volumes.go index c05ef5e64e..53a5cac968 100644 --- a/flaps/flaps_volumes.go +++ b/flaps/flaps_volumes.go @@ -87,6 +87,17 @@ func (f *Client) GetVolumeSnapshots(ctx context.Context, volumeId string) ([]api return out, nil } +func (f *Client) CreateVolumeSnapshot(ctx context.Context, volumeId string) error { + err := f.sendRequestVolumes( + ctx, http.MethodPost, fmt.Sprintf("/%s/snapshots", volumeId), + nil, nil, nil, + ) + if err != nil { + return fmt.Errorf("failed to snapshot %s: %w", volumeId, err) + } + return nil +} + type ExtendVolumeRequest struct { SizeGB int `json:"size_gb"` } diff --git a/internal/command/volumes/create.go b/internal/command/volumes/create.go index c5f0fb43c6..98fb66fbe2 100644 --- a/internal/command/volumes/create.go +++ b/internal/command/volumes/create.go @@ -35,7 +35,6 @@ func newCreate() *cobra.Command { command.RequireSession, command.RequireAppName, ) - cmd.Args = cobra.ExactArgs(1) flag.Add(cmd, diff --git a/internal/command/volumes/snapshots/create.go b/internal/command/volumes/snapshots/create.go new file mode 100644 index 0000000000..1b77506a55 --- /dev/null +++ b/internal/command/volumes/snapshots/create.go @@ -0,0 +1,60 @@ +package snapshots + +import ( + "context" + "fmt" + + "github.com/spf13/cobra" + + "github.com/superfly/flyctl/client" + "github.com/superfly/flyctl/flaps" + "github.com/superfly/flyctl/internal/appconfig" + "github.com/superfly/flyctl/internal/command" + "github.com/superfly/flyctl/internal/flag" +) + +func newCreate() *cobra.Command { + const ( + short = "Snapshot a volume" + long = "Snapshot a volume\n" + usage = "create " + ) + + cmd := command.New(usage, short, long, create, command.RequireSession) + + cmd.Aliases = []string{"create"} + + cmd.Args = cobra.ExactArgs(1) + + flag.Add(cmd, flag.JSONOutput()) + return cmd +} + +func create(ctx context.Context) error { + var client = client.FromContext(ctx).API() + + volumeId := flag.FirstArg(ctx) + + appName := appconfig.NameFromContext(ctx) + if appName == "" { + n, err := client.GetAppNameFromVolume(ctx, volumeId) + if err != nil { + return err + } + appName = *n + } + + flapsClient, err := flaps.NewFromAppName(ctx, appName) + if err != nil { + return err + } + + err = flapsClient.CreateVolumeSnapshot(ctx, volumeId) + if err != nil { + return err + } + + fmt.Printf("Scheduled to snapshot volume %s\n", volumeId) + + return nil +} diff --git a/internal/command/volumes/snapshots/list.go b/internal/command/volumes/snapshots/list.go index c441937b55..67898ae568 100644 --- a/internal/command/volumes/snapshots/list.go +++ b/internal/command/volumes/snapshots/list.go @@ -5,6 +5,7 @@ import ( "fmt" "sort" "strconv" + "time" "github.com/dustin/go-humanize" "github.com/spf13/cobra" @@ -40,6 +41,13 @@ func newList() *cobra.Command { return cmd } +func timeToString(t time.Time) string { + if t.IsZero() { + return "" + } + return humanize.Time(t) +} + func runList(ctx context.Context) error { var ( io = iostreams.FromContext(ctx) @@ -84,12 +92,18 @@ func runList(ctx context.Context) error { rows := make([][]string, 0, len(snapshots)) for _, snapshot := range snapshots { + id := snapshot.ID + if id == "" { + id = "(pending)" + } + rows = append(rows, []string{ - snapshot.ID, + id, + snapshot.Status, strconv.Itoa(snapshot.Size), - humanize.Time(snapshot.CreatedAt), + timeToString(snapshot.CreatedAt), }) } - return render.Table(io.Out, "Snapshots", rows, "ID", "Size", "Created At") + return render.Table(io.Out, "Snapshots", rows, "ID", "Status", "Size", "Created At") } diff --git a/internal/command/volumes/snapshots/snapshots.go b/internal/command/volumes/snapshots/snapshots.go index 6b078e5fc9..b3729c41db 100644 --- a/internal/command/volumes/snapshots/snapshots.go +++ b/internal/command/volumes/snapshots/snapshots.go @@ -22,6 +22,7 @@ func New() *cobra.Command { snapshots.AddCommand( newList(), + newCreate(), ) return snapshots