Files
diun/cmd/image.go

222 lines
6.4 KiB
Go

package main
import (
"context"
"fmt"
"os"
"sort"
"strings"
"time"
"unicode"
survey "github.com/AlecAivazis/survey/v2"
"github.com/crazy-max/diun/v4/pb"
units "github.com/docker/go-units"
"github.com/jedib0t/go-pretty/v6/table"
"github.com/tidwall/pretty"
"google.golang.org/grpc"
"google.golang.org/grpc/credentials/insecure"
"google.golang.org/protobuf/encoding/protojson"
)
// ImageCmd holds image command
type ImageCmd struct {
List ImageListCmd `cmd:"" default:"1" help:"List images in database."`
Inspect ImageInspectCmd `cmd:"" help:"Display information of an image in database."`
Remove ImageRemoveCmd `cmd:"" help:"Remove an image manifest from database."`
Prune ImagePruneCmd `cmd:"" help:"Remove all manifests from the database."`
}
// ImageListCmd holds image list command
type ImageListCmd struct {
Raw bool `name:"raw" default:"false" help:"JSON output."`
GRPCAuthority string `name:"grpc-authority" default:"127.0.0.1:42286" help:"Link to Diun gRPC server."`
}
func (s *ImageListCmd) Run(_ *Context) error {
conn, err := grpc.NewClient(s.GRPCAuthority, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return err
}
defer conn.Close()
imageSvc := pb.NewImageServiceClient(conn)
il, err := imageSvc.ImageList(context.Background(), &pb.ImageListRequest{})
if err != nil {
return err
}
sort.Slice(il.Images, func(i, j int) bool {
return strings.Map(unicode.ToUpper, il.Images[i].Name) < strings.Map(unicode.ToUpper, il.Images[j].Name)
})
if s.Raw {
b, _ := protojson.Marshal(il)
fmt.Println(string(pretty.Pretty(b)))
return nil
}
if len(il.Images) == 0 {
fmt.Println("No image found in the database")
return nil
}
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t.AppendHeader(table.Row{"Name", "Manifests Count", "Latest Tag", "Latest Created", "Latest Digest"})
for _, image := range il.Images {
t.AppendRow(table.Row{image.Name, image.ManifestsCount, image.Latest.Tag, image.Latest.Created.AsTime().Format(time.RFC3339), image.Latest.Digest})
}
t.AppendFooter(table.Row{"Total", len(il.Images)})
t.Render()
return nil
}
// ImageInspectCmd holds image inspect command
type ImageInspectCmd struct {
Image string `name:"image" required:"" help:"Image to inspect."`
Raw bool `name:"raw" default:"false" help:"JSON output."`
GRPCAuthority string `name:"grpc-authority" default:"127.0.0.1:42286" help:"Link to Diun gRPC server."`
}
func (s *ImageInspectCmd) Run(_ *Context) error {
conn, err := grpc.NewClient(s.GRPCAuthority, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return err
}
defer conn.Close()
imageSvc := pb.NewImageServiceClient(conn)
ii, err := imageSvc.ImageInspect(context.Background(), &pb.ImageInspectRequest{
Name: s.Image,
})
if err != nil {
return err
}
sort.Slice(ii.Image.Manifests, func(i, j int) bool {
return ii.Image.Manifests[i].Created.AsTime().After(ii.Image.Manifests[j].Created.AsTime())
})
if s.Raw {
b, _ := protojson.Marshal(ii)
fmt.Println(string(pretty.Pretty(b)))
return nil
}
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t.AppendHeader(table.Row{"Tag", "Created", "Digest"})
for _, image := range ii.Image.Manifests {
t.AppendRow(table.Row{image.Tag, image.Created.AsTime().Format(time.RFC3339), image.Digest})
}
t.AppendFooter(table.Row{"Total", len(ii.Image.Manifests)})
t.Render()
return nil
}
// ImageRemoveCmd holds image remove command
type ImageRemoveCmd struct {
Image string `name:"image" required:"" help:"Image to remove."`
GRPCAuthority string `name:"grpc-authority" default:"127.0.0.1:42286" help:"Link to Diun gRPC server."`
}
func (s *ImageRemoveCmd) Run(_ *Context) error {
conn, err := grpc.NewClient(s.GRPCAuthority, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return err
}
defer conn.Close()
imageSvc := pb.NewImageServiceClient(conn)
removed, err := imageSvc.ImageRemove(context.Background(), &pb.ImageRemoveRequest{
Name: s.Image,
})
if err != nil {
return err
}
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t.AppendHeader(table.Row{"Tag", "Created", "Digest", "Size"})
var totalSize int64
for _, image := range removed.Manifests {
t.AppendRow(table.Row{image.Tag, image.Created.AsTime().Format(time.RFC3339), image.Digest, units.HumanSize(float64(image.Size))})
totalSize += image.Size
}
t.AppendFooter(table.Row{"Total", fmt.Sprintf("%d (%s)", len(removed.Manifests), units.HumanSize(float64(totalSize)))})
t.Render()
return nil
}
// ImagePruneCmd holds image prune command
type ImagePruneCmd struct {
// All bool `name:"all' default:"false help:"Remove all manifests from the database."`
// Filter string `name:"filter help:"Provide filter values (e.g., until=24h)."`
Force bool `name:"force" default:"false" help:"Do not prompt for confirmation."`
GRPCAuthority string `name:"grpc-authority" default:"127.0.0.1:42286" help:"Link to Diun gRPC server."`
}
const (
pruneAllWarning = `This will remove all manifests from the database. Are you sure you want to continue?`
)
func (s *ImagePruneCmd) Run(_ *Context) error {
conn, err := grpc.NewClient(s.GRPCAuthority, grpc.WithTransportCredentials(insecure.NewCredentials()))
if err != nil {
return err
}
defer conn.Close()
imageSvc := pb.NewImageServiceClient(conn)
if !s.Force {
var confirmed bool
prompt := &survey.Confirm{
Message: pruneAllWarning,
}
if err := survey.AskOne(prompt, &confirmed); err != nil {
return err
}
if !confirmed {
return nil
}
}
removed, err := imageSvc.ImagePrune(context.Background(), &pb.ImagePruneRequest{
//All: s.All,
//Filter: s.Filter,
})
if err != nil {
return err
}
if len(removed.Images) == 0 {
fmt.Println("Nothing to be removed from the database")
return nil
}
t := table.NewWriter()
t.SetOutputMirror(os.Stdout)
t.AppendHeader(table.Row{"Tag", "Created", "Digest", "Size"})
var totalSize int64
var totalManifest int
for _, image := range removed.Images {
for _, manifest := range image.Manifests {
t.AppendRow(table.Row{manifest.Tag, manifest.Created.AsTime().Format(time.RFC3339), manifest.Digest, units.HumanSize(float64(manifest.Size))})
totalSize += manifest.Size
}
totalManifest += len(image.Manifests)
}
t.AppendFooter(table.Row{"Total", fmt.Sprintf("%d (%s)", totalManifest, units.HumanSize(float64(totalSize)))})
t.Render()
return nil
}