From 25312bb5b4e2bd4e122703006fc85bbf080e5e7c Mon Sep 17 00:00:00 2001 From: baude Date: Fri, 1 May 2020 12:57:41 -0500 Subject: v2 system subcommand add system df, info, load, renumber, and migrate Refactor for specialized libpod engines add the ability to prune images, volumes, containers, and pods Signed-off-by: baude --- pkg/domain/entities/engine.go | 9 ++ pkg/domain/entities/engine_container.go | 1 + pkg/domain/entities/engine_system.go | 14 +++ pkg/domain/entities/system.go | 57 +++++++++ pkg/domain/infra/abi/runtime.go | 5 + pkg/domain/infra/abi/system.go | 177 ++++++++++++++++++++++++++++ pkg/domain/infra/runtime_abi.go | 31 +++++ pkg/domain/infra/runtime_abi_unsupported.go | 14 +++ pkg/domain/infra/runtime_image_proxy.go | 21 ---- pkg/domain/infra/runtime_proxy.go | 8 ++ pkg/domain/infra/tunnel/system.go | 6 +- 11 files changed, 320 insertions(+), 23 deletions(-) create mode 100644 pkg/domain/entities/engine_system.go create mode 100644 pkg/domain/infra/runtime_abi_unsupported.go delete mode 100644 pkg/domain/infra/runtime_image_proxy.go (limited to 'pkg') diff --git a/pkg/domain/entities/engine.go b/pkg/domain/entities/engine.go index f45218d14..265c9f36f 100644 --- a/pkg/domain/entities/engine.go +++ b/pkg/domain/entities/engine.go @@ -12,9 +12,18 @@ import ( // EngineMode is the connection type podman is using to access libpod type EngineMode string +// EngineSetup calls out whether a "normal" or specialized engine should be created +type EngineSetup string + const ( ABIMode = EngineMode("abi") TunnelMode = EngineMode("tunnel") + + MigrateMode = EngineSetup("migrate") + NoFDsMode = EngineSetup("disablefds") + NormalMode = EngineSetup("normal") + RenumberMode = EngineSetup("renumber") + ResetMode = EngineSetup("reset") ) // Convert EngineMode to String diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 734f72e5f..98e4d841f 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -65,6 +65,7 @@ type ContainerEngine interface { PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error) SetupRootless(ctx context.Context, cmd *cobra.Command) error Shutdown(ctx context.Context) + SystemDf(ctx context.Context, options SystemDfOptions) (*SystemDfReport, error) VarlinkService(ctx context.Context, opts ServiceOptions) error VolumeCreate(ctx context.Context, opts VolumeCreateOptions) (*IdOrNameResponse, error) VolumeInspect(ctx context.Context, namesOrIds []string, opts VolumeInspectOptions) ([]*VolumeInspectReport, error) diff --git a/pkg/domain/entities/engine_system.go b/pkg/domain/entities/engine_system.go new file mode 100644 index 000000000..e2000f5cb --- /dev/null +++ b/pkg/domain/entities/engine_system.go @@ -0,0 +1,14 @@ +package entities + +import ( + "context" + + "github.com/spf13/pflag" +) + +type SystemEngine interface { + Renumber(ctx context.Context, flags *pflag.FlagSet, config *PodmanConfig) error + Migrate(ctx context.Context, flags *pflag.FlagSet, config *PodmanConfig, options SystemMigrateOptions) error + Reset(ctx context.Context, options SystemResetOptions) error + Shutdown(ctx context.Context) +} diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go index de93a382f..c62f40025 100644 --- a/pkg/domain/entities/system.go +++ b/pkg/domain/entities/system.go @@ -26,3 +26,60 @@ type SystemPruneReport struct { *ImagePruneReport VolumePruneReport []*VolumePruneReport } + +// SystemMigrateOptions describes the options needed for the +// cli to migrate runtimes of containers +type SystemMigrateOptions struct { + NewRuntime string +} + +// SystemDfOptions describes the options for getting df information +type SystemDfOptions struct { + Format string + Verbose bool +} + +// SystemDfReport describes the response for df information +type SystemDfReport struct { + Images []*SystemDfImageReport + Containers []*SystemDfContainerReport + Volumes []*SystemDfVolumeReport +} + +// SystemDfImageReport describes an image for use with df +type SystemDfImageReport struct { + Repository string + Tag string + ImageID string + Created time.Time + Size int64 + SharedSize int64 + UniqueSize int64 + Containers int +} + +// SystemDfContainerReport describes a container for use with df +type SystemDfContainerReport struct { + ContainerID string + Image string + Command []string + LocalVolumes int + Size int64 + RWSize int64 + Created time.Time + Status string + Names string +} + +// SystemDfVolumeReport describes a volume and its size +type SystemDfVolumeReport struct { + VolumeName string + Links int + Size int64 +} + +// SystemResetOptions describes the options for resetting your +// container runtime storage, etc +type SystemResetOptions struct { + Force bool +} diff --git a/pkg/domain/infra/abi/runtime.go b/pkg/domain/infra/abi/runtime.go index fba422d8e..b9020e9a5 100644 --- a/pkg/domain/infra/abi/runtime.go +++ b/pkg/domain/infra/abi/runtime.go @@ -16,4 +16,9 @@ type ContainerEngine struct { Libpod *libpod.Runtime } +// Container-related runtime linked against libpod library +type SystemEngine struct { + Libpod *libpod.Runtime +} + var shutdownSync sync.Once diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index ab1b282d8..692fcfa0f 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -5,6 +5,7 @@ import ( "fmt" "io/ioutil" "os" + "path/filepath" "strconv" "syscall" @@ -18,9 +19,11 @@ import ( iopodmanAPI "github.com/containers/libpod/pkg/varlinkapi" "github.com/containers/libpod/utils" "github.com/containers/libpod/version" + "github.com/docker/distribution/reference" "github.com/pkg/errors" "github.com/sirupsen/logrus" "github.com/spf13/cobra" + "github.com/spf13/pflag" "github.com/varlink/go/varlink" ) @@ -213,3 +216,177 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys } return systemPruneReport, nil } + +func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.SystemDfOptions) (*entities.SystemDfReport, error) { + var ( + dfImages []*entities.SystemDfImageReport + dfContainers []*entities.SystemDfContainerReport + dfVolumes []*entities.SystemDfVolumeReport + runningContainers []string + ) + + // Get Images and iterate them + imgs, err := ic.Libpod.ImageRuntime().GetImages() + if err != nil { + return nil, err + } + for _, i := range imgs { + var sharedSize uint64 + cons, err := i.Containers() + if err != nil { + return nil, err + } + imageSize, err := i.Size(ctx) + if err != nil { + return nil, err + } + uniqueSize := *imageSize + + parent, err := i.GetParent(ctx) + if err != nil { + return nil, err + } + if parent != nil { + parentSize, err := parent.Size(ctx) + if err != nil { + return nil, err + } + uniqueSize = *parentSize - *imageSize + sharedSize = *imageSize - uniqueSize + } + var name, repository, tag string + for _, n := range i.Names() { + if len(n) > 0 { + name = n + break + } + } + + named, err := reference.ParseNormalizedNamed(name) + if err != nil { + return nil, err + } + repository = named.Name() + if tagged, isTagged := named.(reference.NamedTagged); isTagged { + tag = tagged.Tag() + } + + report := entities.SystemDfImageReport{ + Repository: repository, + Tag: tag, + ImageID: i.ID(), + Created: i.Created(), + Size: int64(*imageSize), + SharedSize: int64(sharedSize), + UniqueSize: int64(uniqueSize), + Containers: len(cons), + } + dfImages = append(dfImages, &report) + } + + // GetContainers and iterate them + cons, err := ic.Libpod.GetAllContainers() + if err != nil { + return nil, err + } + for _, c := range cons { + iid, _ := c.Image() + conSize, err := c.RootFsSize() + if err != nil { + return nil, err + } + state, err := c.State() + if err != nil { + return nil, err + } + rwsize, err := c.RWSize() + if err != nil { + return nil, err + } + report := entities.SystemDfContainerReport{ + ContainerID: c.ID(), + Image: iid, + Command: c.Command(), + LocalVolumes: len(c.UserVolumes()), + RWSize: rwsize, + Size: conSize, + Created: c.CreatedTime(), + Status: state.String(), + Names: c.Name(), + } + dfContainers = append(dfContainers, &report) + } + + // Get volumes and iterate them + vols, err := ic.Libpod.GetAllVolumes() + if err != nil { + return nil, err + } + + running, err := ic.Libpod.GetRunningContainers() + if err != nil { + return nil, err + } + for _, c := range running { + runningContainers = append(runningContainers, c.ID()) + } + + for _, v := range vols { + var consInUse int + volSize, err := sizeOfPath(v.MountPoint()) + if err != nil { + return nil, err + } + inUse, err := v.VolumesInUse() + if err != nil { + return nil, err + } + for _, viu := range inUse { + if util.StringInSlice(viu, runningContainers) { + consInUse += 1 + } + } + report := entities.SystemDfVolumeReport{ + VolumeName: v.Name(), + Links: consInUse, + Size: volSize, + } + dfVolumes = append(dfVolumes, &report) + } + return &entities.SystemDfReport{ + Images: dfImages, + Containers: dfContainers, + Volumes: dfVolumes, + }, nil +} + +// sizeOfPath determines the file usage of a given path. it was called volumeSize in v1 +// and now is made to be generic and take a path instead of a libpod volume +func sizeOfPath(path string) (int64, error) { + var size int64 + err := filepath.Walk(path, func(path string, info os.FileInfo, err error) error { + if err == nil && !info.IsDir() { + size += info.Size() + } + return err + }) + return size, err +} + +func (se *SystemEngine) Reset(ctx context.Context, options entities.SystemResetOptions) error { + return se.Libpod.Reset(ctx) +} + +func (se *SystemEngine) Renumber(ctx context.Context, flags *pflag.FlagSet, config *entities.PodmanConfig) error { + return nil +} + +func (s SystemEngine) Migrate(ctx context.Context, flags *pflag.FlagSet, config *entities.PodmanConfig, options entities.SystemMigrateOptions) error { + return nil +} + +func (s SystemEngine) Shutdown(ctx context.Context) { + if err := s.Libpod.Shutdown(false); err != nil { + logrus.Error(err) + } +} diff --git a/pkg/domain/infra/runtime_abi.go b/pkg/domain/infra/runtime_abi.go index 7aa6986a7..67c1cd534 100644 --- a/pkg/domain/infra/runtime_abi.go +++ b/pkg/domain/infra/runtime_abi.go @@ -6,8 +6,10 @@ import ( "context" "fmt" + "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/bindings" "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/domain/infra/abi" "github.com/containers/libpod/pkg/domain/infra/tunnel" ) @@ -36,3 +38,32 @@ func NewImageEngine(facts *entities.PodmanConfig) (entities.ImageEngine, error) } return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode) } + +// NewSystemEngine factory provides a libpod runtime for specialized system operations +func NewSystemEngine(setup entities.EngineSetup, facts *entities.PodmanConfig) (entities.SystemEngine, error) { + switch facts.EngineMode { + case entities.ABIMode: + var r *libpod.Runtime + var err error + switch setup { + case entities.NormalMode: + r, err = GetRuntime(context.Background(), facts.FlagSet, facts) + case entities.RenumberMode: + r, err = GetRuntimeRenumber(context.Background(), facts.FlagSet, facts) + case entities.ResetMode: + r, err = GetRuntimeRenumber(context.Background(), facts.FlagSet, facts) + case entities.MigrateMode: + name, flagErr := facts.FlagSet.GetString("new-runtime") + if flagErr != nil { + return nil, flagErr + } + r, err = GetRuntimeMigrate(context.Background(), facts.FlagSet, facts, name) + case entities.NoFDsMode: + r, err = GetRuntimeDisableFDs(context.Background(), facts.FlagSet, facts) + } + return &abi.SystemEngine{Libpod: r}, err + case entities.TunnelMode: + return nil, fmt.Errorf("tunnel system runtime not supported") + } + return nil, fmt.Errorf("runtime mode '%v' is not supported", facts.EngineMode) +} diff --git a/pkg/domain/infra/runtime_abi_unsupported.go b/pkg/domain/infra/runtime_abi_unsupported.go new file mode 100644 index 000000000..c4e25e990 --- /dev/null +++ b/pkg/domain/infra/runtime_abi_unsupported.go @@ -0,0 +1,14 @@ +// +build !ABISupport + +package infra + +import ( + "errors" + + "github.com/containers/libpod/pkg/domain/entities" +) + +// NewSystemEngine factory provides a libpod runtime for specialized system operations +func NewSystemEngine(setup entities.EngineSetup, facts *entities.PodmanConfig) (entities.SystemEngine, error) { + return nil, errors.New("not implemented") +} diff --git a/pkg/domain/infra/runtime_image_proxy.go b/pkg/domain/infra/runtime_image_proxy.go deleted file mode 100644 index ea5d0e6f2..000000000 --- a/pkg/domain/infra/runtime_image_proxy.go +++ /dev/null @@ -1,21 +0,0 @@ -// +build ABISupport - -package infra - -import ( - "context" - - "github.com/containers/libpod/pkg/domain/entities" - "github.com/containers/libpod/pkg/domain/infra/abi" - "github.com/spf13/pflag" -) - -// ContainerEngine Image Proxy will be EOL'ed after podman is separated from libpod repo - -func NewLibpodImageRuntime(flags *pflag.FlagSet, opts *entities.PodmanConfig) (entities.ImageEngine, error) { - r, err := GetRuntime(context.Background(), flags, opts) - if err != nil { - return nil, err - } - return &abi.ImageEngine{Libpod: r}, nil -} diff --git a/pkg/domain/infra/runtime_proxy.go b/pkg/domain/infra/runtime_proxy.go index 41193fd89..e7002e20f 100644 --- a/pkg/domain/infra/runtime_proxy.go +++ b/pkg/domain/infra/runtime_proxy.go @@ -19,3 +19,11 @@ func NewLibpodRuntime(flags *flag.FlagSet, opts *entities.PodmanConfig) (entitie } return &abi.ContainerEngine{Libpod: r}, nil } + +func NewLibpodImageRuntime(flags *flag.FlagSet, opts *entities.PodmanConfig) (entities.ImageEngine, error) { + r, err := GetRuntime(context.Background(), flags, opts) + if err != nil { + return nil, err + } + return &abi.ImageEngine{Libpod: r}, nil +} diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go index 18cb6c75a..448fbed1f 100644 --- a/pkg/domain/infra/tunnel/system.go +++ b/pkg/domain/infra/tunnel/system.go @@ -3,7 +3,6 @@ package tunnel import ( "context" "errors" - "fmt" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/bindings/system" @@ -25,6 +24,9 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) // SystemPrune prunes unused data from the system. func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.SystemPruneOptions) (*entities.SystemPruneReport, error) { - fmt.Println("in tunnel") return system.Prune(ic.ClientCxt, &options.All, &options.Volume) } + +func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.SystemDfOptions) (*entities.SystemDfReport, error) { + panic(errors.New("system df is not supported on remote clients")) +} -- cgit v1.2.3-54-g00ecf