diff options
Diffstat (limited to 'pkg/domain')
23 files changed, 1163 insertions, 139 deletions
diff --git a/pkg/domain/entities/container_ps.go b/pkg/domain/entities/container_ps.go index ceafecebc..33f5d0500 100644 --- a/pkg/domain/entities/container_ps.go +++ b/pkg/domain/entities/container_ps.go @@ -4,8 +4,8 @@ import ( "sort" "strings" - "github.com/containers/libpod/cmd/podman/shared" "github.com/containers/libpod/libpod" + "github.com/containers/libpod/pkg/ps/define" "github.com/cri-o/ocicni/pkg/ocicni" "github.com/pkg/errors" ) @@ -48,7 +48,7 @@ type ListContainer struct { // Port mappings Ports []ocicni.PortMapping // Size of the container rootfs. Requires the size boolean to be true - Size *shared.ContainerSize + Size *define.ContainerSize // Time when container started StartedAt int64 // State of container diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 5d302058b..52327a905 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -2,6 +2,7 @@ package entities import ( "io" + "net/url" "os" "time" @@ -172,6 +173,26 @@ type AttachOptions struct { Stderr *os.File } +// ContainerLogsOptions describes the options to extract container logs. +type ContainerLogsOptions struct { + // Show extra details provided to the logs. + Details bool + // Follow the log output. + Follow bool + // Display logs for the latest container only. Ignored on the remote client. + Latest bool + // Show container names in the output. + Names bool + // Show logs since this timestamp. + Since time.Time + // Number of lines to display at the end of the output. + Tail int64 + // Show timestamps in the logs. + Timestamps bool + // Write the logs to Writer. + Writer io.Writer +} + // ExecOptions describes the cli values to exec into // a container type ExecOptions struct { @@ -240,8 +261,83 @@ type ContainerRunOptions struct { } // ContainerRunReport describes the results of running -//a container +// a container type ContainerRunReport struct { ExitCode int Id string } + +// ContainerCleanupOptions are the CLI values for the +// cleanup command +type ContainerCleanupOptions struct { + All bool + Latest bool + Remove bool + RemoveImage bool +} + +// ContainerCleanupReport describes the response from a +// container cleanup +type ContainerCleanupReport struct { + CleanErr error + Id string + RmErr error + RmiErr error +} + +// ContainerInitOptions describes input options +// for the container init cli +type ContainerInitOptions struct { + All bool + Latest bool +} + +// ContainerInitReport describes the results of a +// container init +type ContainerInitReport struct { + Err error + Id string +} + +//ContainerMountOptions describes the input values for mounting containers +// in the CLI +type ContainerMountOptions struct { + All bool + Format string + Latest bool + NoTruncate bool +} + +// ContainerUnmountOptions are the options from the cli for unmounting +type ContainerUnmountOptions struct { + All bool + Force bool + Latest bool +} + +// ContainerMountReport describes the response from container mount +type ContainerMountReport struct { + Err error + Id string + Name string + Path string +} + +// ContainerUnmountReport describes the response from umounting a container +type ContainerUnmountReport struct { + Err error + Id string +} + +// ContainerPruneOptions describes the options needed +// to prune a container from the CLI +type ContainerPruneOptions struct { + Filters url.Values `json:"filters" schema:"filters"` +} + +// ContainerPruneReport describes the results after pruning the +// stopped containers. +type ContainerPruneReport struct { + ID map[string]int64 + Err map[string]error +} diff --git a/pkg/domain/entities/engine.go b/pkg/domain/entities/engine.go index c14348529..3b971a1e8 100644 --- a/pkg/domain/entities/engine.go +++ b/pkg/domain/entities/engine.go @@ -1,13 +1,23 @@ package entities import ( - "os/user" - "path/filepath" + "context" + "fmt" + "io" + "os" + "github.com/containers/buildah/pkg/parse" "github.com/containers/common/pkg/config" + "github.com/containers/common/pkg/sysinfo" + "github.com/containers/libpod/pkg/apparmor" + "github.com/containers/libpod/pkg/cgroups" + "github.com/containers/libpod/pkg/rootless" + "github.com/opencontainers/selinux/go-selinux" + "github.com/opentracing/opentracing-go" "github.com/spf13/pflag" ) +// EngineMode is the connection type podman is using to access libpod type EngineMode string const ( @@ -15,78 +25,243 @@ const ( TunnelMode = EngineMode("tunnel") ) +// Convert EngineMode to String func (m EngineMode) String() string { return string(m) } -type EngineOptions struct { - Uri string - Identities []string - FlagSet *pflag.FlagSet - EngineMode EngineMode - - CGroupManager string - CniConfigDir string - ConmonPath string - DefaultMountsFile string - EventsBackend string - HooksDir []string - MaxWorks int - Namespace string - Root string - Runroot string - Runtime string - StorageDriver string - StorageOpts []string - Syslog bool - Trace bool - NetworkCmdPath string - - Config string - CpuProfile string - LogLevel string - TmpDir string - - RemoteUserName string - RemoteHost string - VarlinkAddress string - ConnectionName string - RemoteConfigFilePath string - Port int - IdentityFile string - IgnoreHosts bool -} - -func NewEngineOptions() (EngineOptions, error) { - u, _ := user.Current() - return EngineOptions{ - CGroupManager: config.SystemdCgroupsManager, - CniConfigDir: "", - Config: "", - ConmonPath: filepath.Join("usr", "bin", "conmon"), - ConnectionName: "", - CpuProfile: "", - DefaultMountsFile: "", - EventsBackend: "", - HooksDir: nil, - IdentityFile: "", - IgnoreHosts: false, - LogLevel: "", - MaxWorks: 0, - Namespace: "", - NetworkCmdPath: "", - Port: 0, - RemoteConfigFilePath: "", - RemoteHost: "", - RemoteUserName: "", - Root: "", - Runroot: filepath.Join("run", "user", u.Uid), - Runtime: "", - StorageDriver: "overlayfs", - StorageOpts: nil, - Syslog: false, - TmpDir: filepath.Join("run", "user", u.Uid, "libpod", "tmp"), - Trace: false, - VarlinkAddress: "", - }, nil +// PodmanConfig combines the defaults and settings from the file system with the +// flags given in os.Args. Some runtime state is also stored here. +type PodmanConfig struct { + *config.Config + *pflag.FlagSet + + CGroupUsage string // rootless code determines Usage message + ConmonPath string // --conmon flag will set Engine.ConmonPath + CpuProfile string // Hidden: Should CPU profile be taken + EngineMode EngineMode // ABI or Tunneling mode + Identities []string // ssh identities for connecting to server + MaxWorks int // maximum number of parallel threads + RuntimePath string // --runtime flag will set Engine.RuntimePath + SpanCloser io.Closer // Close() for tracing object + SpanCtx context.Context // context to use when tracing + Span opentracing.Span // tracing object + Syslog bool // write to StdOut and Syslog, not supported when tunneling + Trace bool // Hidden: Trace execution + Uri string // URI to API Service + + Runroot string + StorageDriver string + StorageOpts []string +} + +// DefaultSecurityOptions: getter for security options from configuration +func (c PodmanConfig) DefaultSecurityOptions() []string { + securityOpts := []string{} + if c.Containers.SeccompProfile != "" && c.Containers.SeccompProfile != parse.SeccompDefaultPath { + securityOpts = append(securityOpts, fmt.Sprintf("seccomp=%s", c.Containers.SeccompProfile)) + } + if apparmor.IsEnabled() && c.Containers.ApparmorProfile != "" { + securityOpts = append(securityOpts, fmt.Sprintf("apparmor=%s", c.Containers.ApparmorProfile)) + } + if selinux.GetEnabled() && !c.Containers.EnableLabeling { + securityOpts = append(securityOpts, fmt.Sprintf("label=%s", selinux.DisableSecOpt()[0])) + } + return securityOpts +} + +// DefaultSysctls +func (c PodmanConfig) DefaultSysctls() []string { + return c.Containers.DefaultSysctls +} + +func (c PodmanConfig) DefaultVolumes() []string { + return c.Containers.Volumes +} + +func (c PodmanConfig) DefaultDevices() []string { + return c.Containers.Devices +} + +func (c PodmanConfig) DefaultDNSServers() []string { + return c.Containers.DNSServers +} + +func (c PodmanConfig) DefaultDNSSearches() []string { + return c.Containers.DNSSearches +} + +func (c PodmanConfig) DefaultDNSOptions() []string { + return c.Containers.DNSOptions +} + +func (c PodmanConfig) DefaultEnv() []string { + return c.Containers.Env +} + +func (c PodmanConfig) DefaultInitPath() string { + return c.Containers.InitPath +} + +func (c PodmanConfig) DefaultIPCNS() string { + return c.Containers.IPCNS } + +func (c PodmanConfig) DefaultPidNS() string { + return c.Containers.PidNS +} + +func (c PodmanConfig) DefaultNetNS() string { + if c.Containers.NetNS == "private" && rootless.IsRootless() { + return "slirp4netns" + } + return c.Containers.NetNS +} + +func (c PodmanConfig) DefaultCgroupNS() string { + return c.Containers.CgroupNS +} + +func (c PodmanConfig) DefaultUTSNS() string { + return c.Containers.UTSNS +} + +func (c PodmanConfig) DefaultShmSize() string { + return c.Containers.ShmSize +} + +func (c PodmanConfig) DefaultUlimits() []string { + return c.Containers.DefaultUlimits +} + +func (c PodmanConfig) DefaultUserNS() string { + if v, found := os.LookupEnv("PODMAN_USERNS"); found { + return v + } + return c.Containers.UserNS +} + +func (c PodmanConfig) DefaultPidsLimit() int64 { + if rootless.IsRootless() { + cgroup2, _ := cgroups.IsCgroup2UnifiedMode() + if cgroup2 { + return c.Containers.PidsLimit + } + } + return sysinfo.GetDefaultPidsLimit() +} + +func (c PodmanConfig) DefaultPidsDescription() string { + return "Tune container pids limit (set 0 for unlimited)" +} + +func (c PodmanConfig) DefaultDetachKeys() string { + return c.Engine.DetachKeys +} + +// TODO: Remove in rootless support PR +// // EngineOptions holds the environment for running the engines +// type EngineOptions struct { +// // Introduced with V2 +// Uri string +// Identities []string +// FlagSet *pflag.FlagSet +// EngineMode EngineMode +// CGroupUsage string +// +// // Introduced with V1 +// CGroupManager string // config.EngineConfig +// CniConfigDir string // config.NetworkConfig.NetworkConfigDir +// ConmonPath string // config.EngineConfig +// DefaultMountsFile string // config.ContainersConfig +// EventsBackend string // config.EngineConfig.EventsLogger +// HooksDir []string // config.EngineConfig +// MaxWorks int +// Namespace string // config.EngineConfig +// Root string // +// Runroot string // config.EngineConfig.StorageConfigRunRootSet?? +// Runtime string // config.EngineConfig.OCIRuntime +// StorageDriver string // config.EngineConfig.StorageConfigGraphDriverNameSet?? +// StorageOpts []string +// Syslog bool +// Trace bool +// NetworkCmdPath string // config.EngineConfig +// +// Config string +// CpuProfile string +// LogLevel string +// TmpDir string // config.EngineConfig +// +// RemoteUserName string // deprecated +// RemoteHost string // deprecated +// VarlinkAddress string // deprecated +// ConnectionName string +// RemoteConfigFilePath string +// Port int // deprecated +// IdentityFile string // deprecated +// IgnoreHosts bool +// } +// +// func NewEngineOptions(opts EngineOptions) (EngineOptions, error) { +// ctnrCfg, err := config.Default() +// if err != nil { +// logrus.Error(err) +// os.Exit(1) +// } +// +// cgroupManager := ctnrCfg.Engine.CgroupManager +// cgroupUsage := `Cgroup manager to use ("cgroupfs"|"systemd")` +// cgroupv2, _ := cgroups.IsCgroup2UnifiedMode() +// cniPluginDir := ctnrCfg.Network.CNIPluginDirs[0] +// +// cfg, err := config.NewConfig("") +// if err != nil { +// logrus.Errorf("Error loading container config %v\n", err) +// os.Exit(1) +// } +// cfg.CheckCgroupsAndAdjustConfig() +// +// if rootless.IsRootless() { +// if !cgroupv2 { +// cgroupManager = "" +// cgroupUsage = "Cgroup manager is not supported in rootless mode" +// } +// cniPluginDir = "" +// } +// +// return EngineOptions{ +// CGroupManager: cgroupManager, +// CGroupUsage: cgroupUsage, +// CniConfigDir: cniPluginDir, +// Config: opts.Config, // TODO: deprecate +// ConmonPath: opts.ConmonPath, +// ConnectionName: opts.ConnectionName, +// CpuProfile: opts.CpuProfile, +// DefaultMountsFile: ctnrCfg.Containers.DefaultMountsFile, +// EngineMode: opts.EngineMode, +// EventsBackend: ctnrCfg.Engine.EventsLogger, +// FlagSet: opts.FlagSet, // TODO: deprecate +// HooksDir: append(ctnrCfg.Engine.HooksDir[:0:0], ctnrCfg.Engine.HooksDir...), +// Identities: append(opts.Identities[:0:0], opts.Identities...), +// IdentityFile: opts.IdentityFile, // TODO: deprecate +// IgnoreHosts: opts.IgnoreHosts, +// LogLevel: opts.LogLevel, +// MaxWorks: opts.MaxWorks, +// Namespace: ctnrCfg.Engine.Namespace, +// NetworkCmdPath: ctnrCfg.Engine.NetworkCmdPath, +// Port: opts.Port, +// RemoteConfigFilePath: opts.RemoteConfigFilePath, +// RemoteHost: opts.RemoteHost, // TODO: deprecate +// RemoteUserName: opts.RemoteUserName, // TODO: deprecate +// Root: opts.Root, +// Runroot: opts.Runroot, +// Runtime: opts.Runtime, +// StorageDriver: opts.StorageDriver, +// StorageOpts: append(opts.StorageOpts[:0:0], opts.StorageOpts...), +// Syslog: opts.Syslog, +// TmpDir: opts.TmpDir, +// Trace: opts.Trace, +// Uri: opts.Uri, +// VarlinkAddress: opts.VarlinkAddress, +// }, nil +// } diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 576ce1658..c3092a98a 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -3,35 +3,46 @@ package entities import ( "context" + "github.com/containers/common/pkg/config" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/specgen" ) type ContainerEngine interface { + Config(ctx context.Context) (*config.Config, error) ContainerAttach(ctx context.Context, nameOrId string, options AttachOptions) error - ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error) ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error) - ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error) + ContainerCleanup(ctx context.Context, namesOrIds []string, options ContainerCleanupOptions) ([]*ContainerCleanupReport, error) + ContainerPrune(ctx context.Context, options ContainerPruneOptions) (*ContainerPruneReport, error) + ContainerCommit(ctx context.Context, nameOrId string, options CommitOptions) (*CommitReport, error) ContainerCreate(ctx context.Context, s *specgen.SpecGenerator) (*ContainerCreateReport, error) + ContainerDiff(ctx context.Context, nameOrId string, options DiffOptions) (*DiffReport, error) ContainerExec(ctx context.Context, nameOrId string, options ExecOptions) (int, error) ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error) - ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error) ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error + ContainerInit(ctx context.Context, namesOrIds []string, options ContainerInitOptions) ([]*ContainerInitReport, error) + ContainerInspect(ctx context.Context, namesOrIds []string, options InspectOptions) ([]*ContainerInspectReport, error) ContainerKill(ctx context.Context, namesOrIds []string, options KillOptions) ([]*KillReport, error) - ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) ContainerList(ctx context.Context, options ContainerListOptions) ([]ListContainer, error) + ContainerMount(ctx context.Context, nameOrIds []string, options ContainerMountOptions) ([]*ContainerMountReport, error) + ContainerPause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) + ContainerLogs(ctx context.Context, containers []string, options ContainerLogsOptions) error ContainerRestart(ctx context.Context, namesOrIds []string, options RestartOptions) ([]*RestartReport, error) + ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error) ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error) - ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error) ContainerRun(ctx context.Context, opts ContainerRunOptions) (*ContainerRunReport, error) + ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error) ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error) ContainerTop(ctx context.Context, options TopOptions) (*StringSliceReport, error) + ContainerUnmount(ctx context.Context, nameOrIds []string, options ContainerUnmountOptions) ([]*ContainerUnmountReport, error) ContainerUnpause(ctx context.Context, namesOrIds []string, options PauseUnPauseOptions) ([]*PauseUnpauseReport, error) ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error) + Events(ctx context.Context, opts EventsOptions) error HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error) - + Info(ctx context.Context) (*define.Info, error) PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error) PodExists(ctx context.Context, nameOrId string) (*BoolReport, error) + PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error) PodKill(ctx context.Context, namesOrIds []string, options PodKillOptions) ([]*PodKillReport, error) PodPause(ctx context.Context, namesOrIds []string, options PodPauseOptions) ([]*PodPauseReport, error) PodPs(ctx context.Context, options PodPSOptions) ([]*ListPodsReport, error) @@ -41,8 +52,8 @@ type ContainerEngine interface { PodStop(ctx context.Context, namesOrIds []string, options PodStopOptions) ([]*PodStopReport, error) PodTop(ctx context.Context, options PodTopOptions) (*StringSliceReport, error) PodUnpause(ctx context.Context, namesOrIds []string, options PodunpauseOptions) ([]*PodUnpauseReport, error) - PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error) - + RestService(ctx context.Context, opts ServiceOptions) 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) VolumeList(ctx context.Context, opts VolumeListOptions) ([]*VolumeListReport, error) diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go index a28bfc548..3a051ab9b 100644 --- a/pkg/domain/entities/engine_image.go +++ b/pkg/domain/entities/engine_image.go @@ -2,20 +2,25 @@ package entities import ( "context" + + "github.com/containers/common/pkg/config" ) type ImageEngine interface { + Config(ctx context.Context) (*config.Config, error) Delete(ctx context.Context, nameOrId []string, opts ImageDeleteOptions) (*ImageDeleteReport, error) + Diff(ctx context.Context, nameOrId string, options DiffOptions) (*DiffReport, error) Exists(ctx context.Context, nameOrId string) (*BoolReport, error) History(ctx context.Context, nameOrId string, opts ImageHistoryOptions) (*ImageHistoryReport, error) + Import(ctx context.Context, opts ImageImportOptions) (*ImageImportReport, error) Inspect(ctx context.Context, names []string, opts InspectOptions) (*ImageInspectReport, error) List(ctx context.Context, opts ImageListOptions) ([]*ImageSummary, error) + Load(ctx context.Context, opts ImageLoadOptions) (*ImageLoadReport, error) Prune(ctx context.Context, opts ImagePruneOptions) (*ImagePruneReport, error) Pull(ctx context.Context, rawImage string, opts ImagePullOptions) (*ImagePullReport, error) - Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error - Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error - Load(ctx context.Context, opts ImageLoadOptions) (*ImageLoadReport, error) - Import(ctx context.Context, opts ImageImportOptions) (*ImageImportReport, error) Push(ctx context.Context, source string, destination string, opts ImagePushOptions) error Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) error + Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error + Untag(ctx context.Context, nameOrId string, tags []string, options ImageUntagOptions) error + Search(ctx context.Context, term string, opts ImageSearchOptions) ([]ImageSearchReport, error) } diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index bc8a34c13..78ebb8805 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -2,6 +2,7 @@ package entities import ( "net/url" + "time" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/types" @@ -99,12 +100,12 @@ type ImageDeleteReport struct { type ImageHistoryOptions struct{} type ImageHistoryLayer struct { - ID string `json:"Id"` - Created int64 `json:",omitempty"` - CreatedBy string `json:",omitempty"` - Tags []string `json:",omitempty"` - Size int64 `json:",omitempty"` - Comment string `json:",omitempty"` + ID string `json:"id"` + Created time.Time `json:"created,omitempty"` + CreatedBy string `json:",omitempty"` + Tags []string `json:"tags,omitempty"` + Size int64 `json:"size"` + Comment string `json:"comment,omitempty"` } type ImageHistoryReport struct { @@ -181,6 +182,37 @@ type ImagePushOptions struct { TLSVerify types.OptionalBool } +// ImageSearchOptions are the arguments for searching images. +type ImageSearchOptions struct { + // Authfile is the path to the authentication file. Ignored for remote + // calls. + Authfile string + // Filters for the search results. + Filters []string + // Limit the number of results. + Limit int + // NoTrunc will not truncate the output. + NoTrunc bool + // TLSVerify to enable/disable HTTPS and certificate verification. + TLSVerify types.OptionalBool +} + +// ImageSearchReport is the response from searching images. +type ImageSearchReport struct { + // Index is the image index (e.g., "docker.io" or "quay.io") + Index string + // Name is the canoncical name of the image (e.g., "docker.io/library/alpine"). + Name string + // Description of the image. + Description string + // Stars is the number of stars of the image. + Stars int + // Official indicates if it's an official image. + Official string + // Automated indicates if the image was created by an automated build. + Automated string +} + type ImageListOptions struct { All bool `json:"all" schema:"all"` Filter []string `json:"Filter,omitempty"` diff --git a/pkg/domain/entities/pods.go b/pkg/domain/entities/pods.go index cd2e79961..9ca8ff43c 100644 --- a/pkg/domain/entities/pods.go +++ b/pkg/domain/entities/pods.go @@ -1,6 +1,7 @@ package entities import ( + "strings" "time" "github.com/containers/libpod/libpod" @@ -121,7 +122,9 @@ func (p PodCreateOptions) ToPodSpecGen(s *specgen.PodSpecGenerator) { s.Hostname = p.Hostname s.Labels = p.Labels s.NoInfra = !p.Infra - s.InfraCommand = []string{p.InfraCommand} + if len(p.InfraCommand) > 0 { + s.InfraCommand = strings.Split(p.InfraCommand, " ") + } s.InfraImage = p.InfraImage s.SharedNamespaces = p.Share diff --git a/pkg/domain/entities/system.go b/pkg/domain/entities/system.go new file mode 100644 index 000000000..3ddc04293 --- /dev/null +++ b/pkg/domain/entities/system.go @@ -0,0 +1,14 @@ +package entities + +import ( + "time" + + "github.com/spf13/cobra" +) + +// ServiceOptions provides the input for starting an API Service +type ServiceOptions struct { + URI string // Path to unix domain socket service should listen on + Timeout time.Duration // duration of inactivity the service should wait before shutting down + Command *cobra.Command // CLI command provided. Used in V1 code +} diff --git a/pkg/domain/entities/types.go b/pkg/domain/entities/types.go index dd7aaa07f..91ae00764 100644 --- a/pkg/domain/entities/types.go +++ b/pkg/domain/entities/types.go @@ -3,7 +3,9 @@ package entities import ( "net" + "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/pkg/specgen" + "github.com/containers/storage/pkg/archive" "github.com/cri-o/ocicni/pkg/ocicni" ) @@ -49,3 +51,24 @@ type InspectOptions struct { Latest bool `json:",omitempty"` Size bool `json:",omitempty"` } + +// All API and CLI diff commands and diff sub-commands use the same options +type DiffOptions struct { + Format string `json:",omitempty"` // CLI only + Latest bool `json:",omitempty"` // API and CLI, only supported by containers + Archive bool `json:",omitempty"` // CLI only +} + +// DiffReport provides changes for object +type DiffReport struct { + Changes []archive.Change +} + +type EventsOptions struct { + FromStart bool + EventChan chan *events.Event + Filter []string + Stream bool + Since string + Until string +} diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 828ee56f0..4279fb756 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -4,23 +4,32 @@ package abi import ( "context" + "fmt" "io/ioutil" + "os" "strconv" "strings" + "sync" + + lpfilters "github.com/containers/libpod/libpod/filters" "github.com/containers/buildah" + "github.com/containers/common/pkg/config" "github.com/containers/image/v5/manifest" "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/events" "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/libpod/logs" "github.com/containers/libpod/pkg/checkpoint" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/domain/infra/abi/terminal" "github.com/containers/libpod/pkg/ps" + "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/signal" "github.com/containers/libpod/pkg/specgen" "github.com/containers/libpod/pkg/specgen/generate" + "github.com/containers/storage" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -166,6 +175,28 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin return reports, nil } +func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) (*entities.ContainerPruneReport, error) { + var filterFuncs []libpod.ContainerFilter + for k, v := range options.Filters { + for _, val := range v { + generatedFunc, err := lpfilters.GenerateContainerFilterFuncs(k, val, ic.Libpod) + if err != nil { + return nil, err + } + filterFuncs = append(filterFuncs, generatedFunc) + } + } + prunedContainers, pruneErrors, err := ic.Libpod.PruneContainers(filterFuncs) + if err != nil { + return nil, err + } + report := entities.ContainerPruneReport{ + ID: prunedContainers, + Err: pruneErrors, + } + return &report, nil +} + func (ic *ContainerEngine) ContainerKill(ctx context.Context, namesOrIds []string, options entities.KillOptions) ([]*entities.KillReport, error) { var ( reports []*entities.KillReport @@ -599,9 +630,9 @@ func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []stri ExitCode: 125, } if err := ctr.Start(ctx, ctr.PodID() != ""); err != nil { - //if lastError != nil { + // if lastError != nil { // fmt.Fprintln(os.Stderr, lastError) - //} + // } report.Err = err if errors.Cause(err) == define.ErrWillDeadlock { report.Err = errors.Wrapf(err, "please run 'podman system renumber' to resolve deadlocks") @@ -623,6 +654,19 @@ func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.C return ps.GetContainerLists(ic.Libpod, options) } +// ContainerDiff provides changes to given container +func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrId string, opts entities.DiffOptions) (*entities.DiffReport, error) { + if opts.Latest { + ctnr, err := ic.Libpod.GetLatestContainer() + if err != nil { + return nil, errors.Wrap(err, "unable to get latest container") + } + nameOrId = ctnr.ID() + } + changes, err := ic.Libpod.GetDiff("", nameOrId) + return &entities.DiffReport{Changes: changes}, err +} + func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { var ( joinPod bool @@ -696,3 +740,186 @@ func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.Conta } return &report, nil } + +func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error { + if options.Writer == nil { + return errors.New("no io.Writer set for container logs") + } + + var wg sync.WaitGroup + + ctrs, err := getContainersByContext(false, options.Latest, containers, ic.Libpod) + if err != nil { + return err + } + + logOpts := &logs.LogOptions{ + Multi: len(ctrs) > 1, + Details: options.Details, + Follow: options.Follow, + Since: options.Since, + Tail: options.Tail, + Timestamps: options.Timestamps, + UseName: options.Names, + WaitGroup: &wg, + } + + chSize := len(ctrs) * int(options.Tail) + if chSize <= 0 { + chSize = 1 + } + logChannel := make(chan *logs.LogLine, chSize) + + if err := ic.Libpod.Log(ctrs, logOpts, logChannel); err != nil { + return err + } + + go func() { + wg.Wait() + close(logChannel) + }() + + for line := range logChannel { + fmt.Fprintln(options.Writer, line.String(logOpts)) + } + + return nil +} + +func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []string, options entities.ContainerCleanupOptions) ([]*entities.ContainerCleanupReport, error) { + var reports []*entities.ContainerCleanupReport + ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } + for _, ctr := range ctrs { + var err error + report := entities.ContainerCleanupReport{Id: ctr.ID()} + if options.Remove { + err = ic.Libpod.RemoveContainer(ctx, ctr, false, true) + if err != nil { + report.RmErr = errors.Wrapf(err, "failed to cleanup and remove container %v", ctr.ID()) + } + } else { + err := ctr.Cleanup(ctx) + if err != nil { + report.CleanErr = errors.Wrapf(err, "failed to cleanup container %v", ctr.ID()) + } + } + + if options.RemoveImage { + _, imageName := ctr.Image() + ctrImage, err := ic.Libpod.ImageRuntime().NewFromLocal(imageName) + if err != nil { + report.RmiErr = err + reports = append(reports, &report) + continue + } + _, err = ic.Libpod.RemoveImage(ctx, ctrImage, false) + report.RmiErr = err + } + reports = append(reports, &report) + } + return reports, nil +} + +func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []string, options entities.ContainerInitOptions) ([]*entities.ContainerInitReport, error) { + var reports []*entities.ContainerInitReport + ctrs, err := getContainersByContext(options.All, options.Latest, namesOrIds, ic.Libpod) + if err != nil { + return nil, err + } + for _, ctr := range ctrs { + report := entities.ContainerInitReport{Id: ctr.ID()} + report.Err = ctr.Init(ctx) + reports = append(reports, &report) + } + return reports, nil +} + +func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIds []string, options entities.ContainerMountOptions) ([]*entities.ContainerMountReport, error) { + if os.Geteuid() != 0 { + if driver := ic.Libpod.StorageConfig().GraphDriverName; driver != "vfs" { + // Do not allow to mount a graphdriver that is not vfs if we are creating the userns as part + // of the mount command. + return nil, fmt.Errorf("cannot mount using driver %s in rootless mode", driver) + } + + became, ret, err := rootless.BecomeRootInUserNS("") + if err != nil { + return nil, err + } + if became { + os.Exit(ret) + } + } + var reports []*entities.ContainerMountReport + ctrs, err := getContainersByContext(options.All, options.Latest, nameOrIds, ic.Libpod) + if err != nil { + return nil, err + } + for _, ctr := range ctrs { + report := entities.ContainerMountReport{Id: ctr.ID()} + report.Path, report.Err = ctr.Mount() + reports = append(reports, &report) + } + if len(reports) > 0 { + return reports, nil + } + + // No containers were passed, so we send back what is mounted + ctrs, err = getContainersByContext(true, false, []string{}, ic.Libpod) + if err != nil { + return nil, err + } + for _, ctr := range ctrs { + mounted, path, err := ctr.Mounted() + if err != nil { + return nil, err + } + + if mounted { + reports = append(reports, &entities.ContainerMountReport{ + Id: ctr.ID(), + Name: ctr.Name(), + Path: path, + }) + } + } + return reports, nil +} + +func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIds []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) { + var reports []*entities.ContainerUnmountReport + ctrs, err := getContainersByContext(options.All, options.Latest, nameOrIds, ic.Libpod) + if err != nil { + return nil, err + } + for _, ctr := range ctrs { + state, err := ctr.State() + if err != nil { + logrus.Debugf("Error umounting container %s state: %s", ctr.ID(), err.Error()) + continue + } + if state == define.ContainerStateRunning { + logrus.Debugf("Error umounting container %s, is running", ctr.ID()) + continue + } + + report := entities.ContainerUnmountReport{Id: ctr.ID()} + if err := ctr.Unmount(options.Force); err != nil { + if options.All && errors.Cause(err) == storage.ErrLayerNotMounted { + logrus.Debugf("Error umounting container %s, storage.ErrLayerNotMounted", ctr.ID()) + continue + } + report.Err = errors.Wrapf(err, "error unmounting container %s", ctr.ID()) + } + reports = append(reports, &report) + } + return reports, nil +} + +// GetConfig returns a copy of the configuration used by the runtime +func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) { + return ic.Libpod.GetConfig() +} diff --git a/pkg/domain/infra/abi/events.go b/pkg/domain/infra/abi/events.go new file mode 100644 index 000000000..9540a5b96 --- /dev/null +++ b/pkg/domain/infra/abi/events.go @@ -0,0 +1,18 @@ +//+build ABISupport + +package abi + +import ( + "context" + + "github.com/containers/libpod/libpod/events" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/sirupsen/logrus" +) + +func (ic *ContainerEngine) Events(ctx context.Context, opts entities.EventsOptions) error { + readOpts := events.ReadOptions{FromStart: opts.FromStart, Stream: opts.Stream, Filters: opts.Filter, EventChannel: opts.EventChan, Since: opts.Since, Until: opts.Until} + err := ic.Libpod.Events(readOpts) + logrus.Error(err) + return err +} diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 9d706a112..9467c14d4 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -9,12 +9,14 @@ import ( "os" "strings" + "github.com/containers/common/pkg/config" "github.com/containers/image/v5/docker" dockerarchive "github.com/containers/image/v5/docker/archive" "github.com/containers/image/v5/docker/reference" "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" + "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/libpod/image" libpodImage "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/domain/entities" @@ -27,10 +29,11 @@ import ( ) func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.BoolReport, error) { - if _, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId); err != nil { - return &entities.BoolReport{}, nil + _, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) + if err != nil && errors.Cause(err) != define.ErrNoSuchImage { + return nil, err } - return &entities.BoolReport{Value: true}, nil + return &entities.BoolReport{Value: err == nil}, nil } func (ir *ImageEngine) Delete(ctx context.Context, nameOrId []string, opts entities.ImageDeleteOptions) (*entities.ImageDeleteReport, error) { @@ -43,7 +46,9 @@ func (ir *ImageEngine) Delete(ctx context.Context, nameOrId []string, opts entit if err != nil { return &report, errors.Wrapf(err, "unable to query local images") } - + if len(targets) == 0 { + return &report, nil + } if len(targets) > 0 && len(targets) == len(previousTargets) { return &report, errors.New("unable to delete all images; re-run the rmi command again.") } @@ -140,7 +145,7 @@ func (ir *ImageEngine) History(ctx context.Context, nameOrId string, opts entiti func ToDomainHistoryLayer(layer *libpodImage.History) entities.ImageHistoryLayer { l := entities.ImageHistoryLayer{} l.ID = layer.ID - l.Created = layer.Created.Unix() + l.Created = *layer.Created l.CreatedBy = layer.CreatedBy copy(l.Tags, layer.Tags) l.Size = layer.Size @@ -392,8 +397,10 @@ func (ir *ImageEngine) Load(ctx context.Context, opts entities.ImageLoadOptions) if err != nil { return nil, errors.Wrap(err, "image loaded but no additional tags were created") } - if err := newImage.TagImage(opts.Name); err != nil { - return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName) + if len(opts.Name) > 0 { + if err := newImage.TagImage(fmt.Sprintf("%s:%s", opts.Name, opts.Tag)); err != nil { + return nil, errors.Wrapf(err, "error adding %q to image %q", opts.Name, newImage.InputName) + } } return &entities.ImageLoadReport{Name: name}, nil } @@ -413,3 +420,51 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, } return newImage.Save(ctx, nameOrId, options.Format, options.Output, tags, options.Quiet, options.Compress) } + +func (ir *ImageEngine) Diff(_ context.Context, nameOrId string, _ entities.DiffOptions) (*entities.DiffReport, error) { + changes, err := ir.Libpod.GetDiff("", nameOrId) + if err != nil { + return nil, err + } + return &entities.DiffReport{Changes: changes}, nil +} + +func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) { + filter, err := image.ParseSearchFilter(opts.Filters) + if err != nil { + return nil, err + } + + searchOpts := image.SearchOptions{ + Authfile: opts.Authfile, + Filter: *filter, + Limit: opts.Limit, + NoTrunc: opts.NoTrunc, + InsecureSkipTLSVerify: opts.TLSVerify, + } + + searchResults, err := image.SearchImages(term, searchOpts) + if err != nil { + return nil, err + } + + // Convert from image.SearchResults to entities.ImageSearchReport. We don't + // want to leak any low-level packages into the remote client, which + // requires converting. + reports := make([]entities.ImageSearchReport, len(searchResults)) + for i := range searchResults { + reports[i].Index = searchResults[i].Index + reports[i].Name = searchResults[i].Name + reports[i].Description = searchResults[i].Index + reports[i].Stars = searchResults[i].Stars + reports[i].Official = searchResults[i].Official + reports[i].Automated = searchResults[i].Automated + } + + return reports, nil +} + +// GetConfig returns a copy of the configuration used by the runtime +func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) { + return ir.Libpod.GetConfig() +} diff --git a/pkg/domain/infra/abi/pods.go b/pkg/domain/infra/abi/pods.go index c3e5d59bc..bb637de3e 100644 --- a/pkg/domain/infra/abi/pods.go +++ b/pkg/domain/infra/abi/pods.go @@ -5,13 +5,13 @@ package abi import ( "context" - lpfilters "github.com/containers/libpod/libpod/filters" - "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" + lpfilters "github.com/containers/libpod/libpod/filters" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/signal" "github.com/containers/libpod/pkg/specgen" + "github.com/containers/libpod/pkg/specgen/generate" "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -246,7 +246,7 @@ func (ic *ContainerEngine) PodRm(ctx context.Context, namesOrIds []string, optio func (ic *ContainerEngine) PodCreate(ctx context.Context, opts entities.PodCreateOptions) (*entities.PodCreateReport, error) { podSpec := specgen.NewPodSpecGenerator() opts.ToPodSpecGen(podSpec) - pod, err := podSpec.MakePod(ic.Libpod) + pod, err := generate.MakePod(podSpec, ic.Libpod) if err != nil { return nil, err } diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go new file mode 100644 index 000000000..10872144b --- /dev/null +++ b/pkg/domain/infra/abi/system.go @@ -0,0 +1,244 @@ +// +build ABISupport + +package abi + +import ( + "context" + "fmt" + "io/ioutil" + "net" + "os" + "strconv" + "strings" + "syscall" + + "github.com/containers/common/pkg/config" + "github.com/containers/libpod/libpod/define" + api "github.com/containers/libpod/pkg/api/server" + "github.com/containers/libpod/pkg/cgroups" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/rootless" + "github.com/containers/libpod/pkg/util" + iopodman "github.com/containers/libpod/pkg/varlink" + iopodmanAPI "github.com/containers/libpod/pkg/varlinkapi" + "github.com/containers/libpod/utils" + "github.com/containers/libpod/version" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" + "github.com/spf13/cobra" + "github.com/varlink/go/varlink" +) + +func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) { + return ic.Libpod.Info() +} + +func (ic *ContainerEngine) RestService(_ context.Context, opts entities.ServiceOptions) error { + var ( + listener net.Listener + err error + ) + + if opts.URI != "" { + fields := strings.Split(opts.URI, ":") + if len(fields) == 1 { + return errors.Errorf("%s is an invalid socket destination", opts.URI) + } + address := strings.Join(fields[1:], ":") + listener, err = net.Listen(fields[0], address) + if err != nil { + return errors.Wrapf(err, "unable to create socket %s", opts.URI) + } + } + + server, err := api.NewServerWithSettings(ic.Libpod, opts.Timeout, &listener) + if err != nil { + return err + } + defer func() { + if err := server.Shutdown(); err != nil { + logrus.Warnf("Error when stopping API service: %s", err) + } + }() + + err = server.Serve() + _ = listener.Close() + return err +} + +func (ic *ContainerEngine) VarlinkService(_ context.Context, opts entities.ServiceOptions) error { + var varlinkInterfaces = []*iopodman.VarlinkInterface{ + iopodmanAPI.New(opts.Command, ic.Libpod), + } + + service, err := varlink.NewService( + "Atomic", + "podman", + version.Version, + "https://github.com/containers/libpod", + ) + if err != nil { + return errors.Wrapf(err, "unable to create new varlink service") + } + + for _, i := range varlinkInterfaces { + if err := service.RegisterInterface(i); err != nil { + return errors.Errorf("unable to register varlink interface %v", i) + } + } + + // Run the varlink server at the given address + if err = service.Listen(opts.URI, opts.Timeout); err != nil { + switch err.(type) { + case varlink.ServiceTimeoutError: + logrus.Infof("varlink service expired (use --timeout to increase session time beyond %s ms, 0 means never timeout)", opts.Timeout.String()) + return nil + default: + return errors.Wrapf(err, "unable to start varlink service") + } + } + return nil +} + +func (ic *ContainerEngine) SetupRootless(cmd *cobra.Command) error { + // do it only after podman has already re-execed and running with uid==0. + if os.Geteuid() == 0 { + ownsCgroup, err := cgroups.UserOwnsCurrentSystemdCgroup() + if err != nil { + logrus.Warnf("Failed to detect the owner for the current cgroup: %v", err) + } + if !ownsCgroup { + conf, err := ic.Config(context.Background()) + if err != nil { + return err + } + unitName := fmt.Sprintf("podman-%d.scope", os.Getpid()) + if err := utils.RunUnderSystemdScope(os.Getpid(), "user.slice", unitName); err != nil { + if conf.Engine.CgroupManager == config.SystemdCgroupsManager { + logrus.Warnf("Failed to add podman to systemd sandbox cgroup: %v", err) + } else { + logrus.Debugf("Failed to add podman to systemd sandbox cgroup: %v", err) + } + } + } + } + + if !executeCommandInUserNS(cmd) { + return nil + } + + pausePidPath, err := util.GetRootlessPauseProcessPidPath() + if err != nil { + return errors.Wrapf(err, "could not get pause process pid file path") + } + + became, ret, err := rootless.TryJoinPauseProcess(pausePidPath) + if err != nil { + return err + } + if became { + os.Exit(ret) + } + + // if there is no pid file, try to join existing containers, and create a pause process. + ctrs, err := ic.Libpod.GetRunningContainers() + if err != nil { + logrus.WithError(err).Fatal("") + } + + paths := []string{} + for _, ctr := range ctrs { + paths = append(paths, ctr.Config().ConmonPidFile) + } + + became, ret, err = rootless.TryJoinFromFilePaths(pausePidPath, true, paths) + if err := movePauseProcessToScope(); err != nil { + conf, err := ic.Config(context.Background()) + if err != nil { + return err + } + if conf.Engine.CgroupManager == config.SystemdCgroupsManager { + logrus.Warnf("Failed to add pause process to systemd sandbox cgroup: %v", err) + } else { + logrus.Debugf("Failed to add pause process to systemd sandbox cgroup: %v", err) + } + } + if err != nil { + logrus.WithError(err).Fatal("") + } + if became { + os.Exit(ret) + } + return nil +} + +// Most podman commands when run in rootless mode, need to be executed in the +// users usernamespace. This function is updated with a list of commands that +// should NOT be run within the user namespace. +func executeCommandInUserNS(cmd *cobra.Command) bool { + return os.Geteuid() == 0 + // if os.Geteuid() == 0 { + // return false + // } + // switch cmd { + // case _migrateCommand, + // _mountCommand, + // _renumberCommand, + // _searchCommand, + // _versionCommand: + // return false + // } + // return true +} + +func movePauseProcessToScope() error { + pausePidPath, err := util.GetRootlessPauseProcessPidPath() + if err != nil { + return errors.Wrapf(err, "could not get pause process pid file path") + } + + data, err := ioutil.ReadFile(pausePidPath) + if err != nil { + return errors.Wrapf(err, "cannot read pause pid file") + } + pid, err := strconv.ParseUint(string(data), 10, 0) + if err != nil { + return errors.Wrapf(err, "cannot parse pid file %s", pausePidPath) + } + + return utils.RunUnderSystemdScope(int(pid), "user.slice", "podman-pause.scope") +} + +func setRLimits() error { // nolint:deadcode,unused + rlimits := new(syscall.Rlimit) + rlimits.Cur = 1048576 + rlimits.Max = 1048576 + if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil { + if err := syscall.Getrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil { + return errors.Wrapf(err, "error getting rlimits") + } + rlimits.Cur = rlimits.Max + if err := syscall.Setrlimit(syscall.RLIMIT_NOFILE, rlimits); err != nil { + return errors.Wrapf(err, "error setting new rlimits") + } + } + return nil +} + +func setUMask() { // nolint:deadcode,unused + // Be sure we can create directories with 0755 mode. + syscall.Umask(0022) +} + +// checkInput can be used to verify any of the globalopt values +func checkInput() error { // nolint:deadcode,unused + return nil +} + +// func getCNIPluginsDir() string { +// if rootless.IsRootless() { +// return "" +// } +// +// return registry.PodmanOptions.Network.CNIPluginDirs[0] +// } diff --git a/pkg/domain/infra/runtime_abi.go b/pkg/domain/infra/runtime_abi.go index f11026571..0dbcf2ad2 100644 --- a/pkg/domain/infra/runtime_abi.go +++ b/pkg/domain/infra/runtime_abi.go @@ -12,7 +12,7 @@ import ( ) // NewContainerEngine factory provides a libpod runtime for container-related operations -func NewContainerEngine(facts entities.EngineOptions) (entities.ContainerEngine, error) { +func NewContainerEngine(facts entities.PodmanConfig) (entities.ContainerEngine, error) { switch facts.EngineMode { case entities.ABIMode: r, err := NewLibpodRuntime(facts.FlagSet, facts) @@ -25,7 +25,7 @@ func NewContainerEngine(facts entities.EngineOptions) (entities.ContainerEngine, } // NewContainerEngine factory provides a libpod runtime for image-related operations -func NewImageEngine(facts entities.EngineOptions) (entities.ImageEngine, error) { +func NewImageEngine(facts entities.PodmanConfig) (entities.ImageEngine, error) { switch facts.EngineMode { case entities.ABIMode: r, err := NewLibpodImageRuntime(facts.FlagSet, facts) diff --git a/pkg/domain/infra/runtime_image_proxy.go b/pkg/domain/infra/runtime_image_proxy.go index befc66b9a..45c5425a3 100644 --- a/pkg/domain/infra/runtime_image_proxy.go +++ b/pkg/domain/infra/runtime_image_proxy.go @@ -12,7 +12,7 @@ import ( // ContainerEngine Image Proxy will be EOL'ed after podmanV2 is separated from libpod repo -func NewLibpodImageRuntime(flags *pflag.FlagSet, opts entities.EngineOptions) (entities.ImageEngine, error) { +func NewLibpodImageRuntime(flags *pflag.FlagSet, opts entities.PodmanConfig) (entities.ImageEngine, error) { r, err := GetRuntime(context.Background(), flags, opts) if err != nil { return nil, err diff --git a/pkg/domain/infra/runtime_libpod.go b/pkg/domain/infra/runtime_libpod.go index d59759707..6b0ac4852 100644 --- a/pkg/domain/infra/runtime_libpod.go +++ b/pkg/domain/infra/runtime_libpod.go @@ -1,3 +1,5 @@ +// build: ABISupport + package infra import ( @@ -22,68 +24,70 @@ type engineOpts struct { migrate bool noStore bool withFDS bool - flags entities.EngineOptions + config entities.PodmanConfig } // GetRuntimeMigrate gets a libpod runtime that will perform a migration of existing containers -func GetRuntimeMigrate(ctx context.Context, fs *flag.FlagSet, ef entities.EngineOptions, newRuntime string) (*libpod.Runtime, error) { +func GetRuntimeMigrate(ctx context.Context, fs *flag.FlagSet, cfg entities.PodmanConfig, newRuntime string) (*libpod.Runtime, error) { return getRuntime(ctx, fs, &engineOpts{ name: newRuntime, renumber: false, migrate: true, noStore: false, withFDS: true, - flags: ef, + config: cfg, }) } // GetRuntimeDisableFDs gets a libpod runtime that will disable sd notify -func GetRuntimeDisableFDs(ctx context.Context, fs *flag.FlagSet, ef entities.EngineOptions) (*libpod.Runtime, error) { +func GetRuntimeDisableFDs(ctx context.Context, fs *flag.FlagSet, cfg entities.PodmanConfig) (*libpod.Runtime, error) { return getRuntime(ctx, fs, &engineOpts{ renumber: false, migrate: false, noStore: false, withFDS: false, - flags: ef, + config: cfg, }) } // GetRuntimeRenumber gets a libpod runtime that will perform a lock renumber -func GetRuntimeRenumber(ctx context.Context, fs *flag.FlagSet, ef entities.EngineOptions) (*libpod.Runtime, error) { +func GetRuntimeRenumber(ctx context.Context, fs *flag.FlagSet, cfg entities.PodmanConfig) (*libpod.Runtime, error) { return getRuntime(ctx, fs, &engineOpts{ renumber: true, migrate: false, noStore: false, withFDS: true, - flags: ef, + config: cfg, }) } // GetRuntime generates a new libpod runtime configured by command line options -func GetRuntime(ctx context.Context, flags *flag.FlagSet, ef entities.EngineOptions) (*libpod.Runtime, error) { +func GetRuntime(ctx context.Context, flags *flag.FlagSet, cfg entities.PodmanConfig) (*libpod.Runtime, error) { return getRuntime(ctx, flags, &engineOpts{ renumber: false, migrate: false, noStore: false, withFDS: true, - flags: ef, + config: cfg, }) } // GetRuntimeNoStore generates a new libpod runtime configured by command line options -func GetRuntimeNoStore(ctx context.Context, fs *flag.FlagSet, ef entities.EngineOptions) (*libpod.Runtime, error) { +func GetRuntimeNoStore(ctx context.Context, fs *flag.FlagSet, cfg entities.PodmanConfig) (*libpod.Runtime, error) { return getRuntime(ctx, fs, &engineOpts{ renumber: false, migrate: false, noStore: true, withFDS: true, - flags: ef, + config: cfg, }) } func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpod.Runtime, error) { options := []libpod.RuntimeOption{} storageOpts := storage.StoreOptions{} + cfg := opts.config + storageSet := false uidmapFlag := fs.Lookup("uidmap") @@ -109,25 +113,25 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo if fs.Changed("root") { storageSet = true - storageOpts.GraphRoot = opts.flags.Root + storageOpts.GraphRoot = cfg.Engine.StaticDir } if fs.Changed("runroot") { storageSet = true - storageOpts.RunRoot = opts.flags.Runroot + storageOpts.RunRoot = cfg.Runroot } if len(storageOpts.RunRoot) > 50 { return nil, errors.New("the specified runroot is longer than 50 characters") } if fs.Changed("storage-driver") { storageSet = true - storageOpts.GraphDriverName = opts.flags.StorageDriver + storageOpts.GraphDriverName = cfg.StorageDriver // Overriding the default storage driver caused GraphDriverOptions from storage.conf to be ignored storageOpts.GraphDriverOptions = []string{} } // This should always be checked after storage-driver is checked - if len(opts.flags.StorageOpts) > 0 { + if len(cfg.StorageOpts) > 0 { storageSet = true - storageOpts.GraphDriverOptions = opts.flags.StorageOpts + storageOpts.GraphDriverOptions = cfg.StorageOpts } if opts.migrate { options = append(options, libpod.WithMigrate()) @@ -151,30 +155,30 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo // TODO CLI flags for image config? // TODO CLI flag for signature policy? - if len(opts.flags.Namespace) > 0 { - options = append(options, libpod.WithNamespace(opts.flags.Namespace)) + if len(cfg.Engine.Namespace) > 0 { + options = append(options, libpod.WithNamespace(cfg.Engine.Namespace)) } if fs.Changed("runtime") { - options = append(options, libpod.WithOCIRuntime(opts.flags.Runtime)) + options = append(options, libpod.WithOCIRuntime(cfg.Engine.OCIRuntime)) } if fs.Changed("conmon") { - options = append(options, libpod.WithConmonPath(opts.flags.ConmonPath)) + options = append(options, libpod.WithConmonPath(cfg.ConmonPath)) } if fs.Changed("tmpdir") { - options = append(options, libpod.WithTmpDir(opts.flags.TmpDir)) + options = append(options, libpod.WithTmpDir(cfg.Engine.TmpDir)) } if fs.Changed("network-cmd-path") { - options = append(options, libpod.WithNetworkCmdPath(opts.flags.NetworkCmdPath)) + options = append(options, libpod.WithNetworkCmdPath(cfg.Engine.NetworkCmdPath)) } if fs.Changed("events-backend") { - options = append(options, libpod.WithEventsLogger(opts.flags.EventsBackend)) + options = append(options, libpod.WithEventsLogger(cfg.Engine.EventsLogger)) } if fs.Changed("cgroup-manager") { - options = append(options, libpod.WithCgroupManager(opts.flags.CGroupManager)) + options = append(options, libpod.WithCgroupManager(cfg.Engine.CgroupManager)) } else { unified, err := cgroups.IsCgroup2UnifiedMode() if err != nil { @@ -189,13 +193,13 @@ func getRuntime(ctx context.Context, fs *flag.FlagSet, opts *engineOpts) (*libpo // TODO flag to set libpod tmp dir? if fs.Changed("cni-config-dir") { - options = append(options, libpod.WithCNIConfigDir(opts.flags.CniConfigDir)) + options = append(options, libpod.WithCNIConfigDir(cfg.Network.NetworkConfigDir)) } if fs.Changed("default-mounts-file") { - options = append(options, libpod.WithDefaultMountsFile(opts.flags.DefaultMountsFile)) + options = append(options, libpod.WithDefaultMountsFile(cfg.Containers.DefaultMountsFile)) } if fs.Changed("hooks-dir") { - options = append(options, libpod.WithHooksDir(opts.flags.HooksDir...)) + options = append(options, libpod.WithHooksDir(cfg.Engine.HooksDir...)) } // TODO flag to set CNI plugins dir? diff --git a/pkg/domain/infra/runtime_proxy.go b/pkg/domain/infra/runtime_proxy.go index 2e38c74b9..18f716ea0 100644 --- a/pkg/domain/infra/runtime_proxy.go +++ b/pkg/domain/infra/runtime_proxy.go @@ -12,7 +12,7 @@ import ( // ContainerEngine Proxy will be EOL'ed after podmanV2 is separated from libpod repo -func NewLibpodRuntime(flags *flag.FlagSet, opts entities.EngineOptions) (entities.ContainerEngine, error) { +func NewLibpodRuntime(flags *flag.FlagSet, opts entities.PodmanConfig) (entities.ContainerEngine, error) { r, err := GetRuntime(context.Background(), flags, opts) if err != nil { return nil, err diff --git a/pkg/domain/infra/runtime_tunnel.go b/pkg/domain/infra/runtime_tunnel.go index dc04b4e53..129fdeb2c 100644 --- a/pkg/domain/infra/runtime_tunnel.go +++ b/pkg/domain/infra/runtime_tunnel.go @@ -11,7 +11,7 @@ import ( "github.com/containers/libpod/pkg/domain/infra/tunnel" ) -func NewContainerEngine(facts entities.EngineOptions) (entities.ContainerEngine, error) { +func NewContainerEngine(facts entities.PodmanConfig) (entities.ContainerEngine, error) { switch facts.EngineMode { case entities.ABIMode: return nil, fmt.Errorf("direct runtime not supported") @@ -23,7 +23,7 @@ func NewContainerEngine(facts entities.EngineOptions) (entities.ContainerEngine, } // NewImageEngine factory provides a libpod runtime for image-related operations -func NewImageEngine(facts entities.EngineOptions) (entities.ImageEngine, error) { +func NewImageEngine(facts entities.PodmanConfig) (entities.ImageEngine, error) { switch facts.EngineMode { case entities.ABIMode: return nil, fmt.Errorf("direct image runtime not supported") diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index e96200c5b..679bb371b 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -5,6 +5,7 @@ import ( "io" "os" + "github.com/containers/common/pkg/config" "github.com/containers/image/v5/docker/reference" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/bindings/containers" @@ -145,6 +146,10 @@ func (ic *ContainerEngine) ContainerRm(ctx context.Context, namesOrIds []string, return reports, nil } +func (ic *ContainerEngine) ContainerPrune(ctx context.Context, options entities.ContainerPruneOptions) (*entities.ContainerPruneReport, error) { + return containers.Prune(ic.ClientCxt, options.Filters) +} + func (ic *ContainerEngine) ContainerInspect(ctx context.Context, namesOrIds []string, options entities.InspectOptions) ([]*entities.ContainerInspectReport, error) { var ( reports []*entities.ContainerInspectReport @@ -305,6 +310,11 @@ func (ic *ContainerEngine) ContainerCreate(ctx context.Context, s *specgen.SpecG return &entities.ContainerCreateReport{Id: response.ID}, nil } +func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []string, options entities.ContainerLogsOptions) error { + // The endpoint is not ready yet and requires some more work. + return errors.New("not implemented yet") +} + func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error { return errors.New("not implemented") } @@ -324,3 +334,40 @@ func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.C func (ic *ContainerEngine) ContainerRun(ctx context.Context, opts entities.ContainerRunOptions) (*entities.ContainerRunReport, error) { return nil, errors.New("not implemented") } + +func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrId string, _ entities.DiffOptions) (*entities.DiffReport, error) { + changes, err := containers.Diff(ic.ClientCxt, nameOrId) + return &entities.DiffReport{Changes: changes}, err +} + +func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []string, options entities.ContainerCleanupOptions) ([]*entities.ContainerCleanupReport, error) { + return nil, errors.New("not implemented") +} + +func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []string, options entities.ContainerInitOptions) ([]*entities.ContainerInitReport, error) { + var reports []*entities.ContainerInitReport + ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds) + if err != nil { + return nil, err + } + for _, ctr := range ctrs { + err := containers.ContainerInit(ic.ClientCxt, ctr.ID) + reports = append(reports, &entities.ContainerInitReport{ + Err: err, + Id: ctr.ID, + }) + } + return reports, nil +} + +func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIds []string, options entities.ContainerMountOptions) ([]*entities.ContainerMountReport, error) { + return nil, errors.New("mounting containers is not supported for remote clients") +} + +func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIds []string, options entities.ContainerUnmountOptions) ([]*entities.ContainerUnmountReport, error) { + return nil, errors.New("unmounting containers is not supported for remote clients") +} + +func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) { + return config.Default() +} diff --git a/pkg/domain/infra/tunnel/events.go b/pkg/domain/infra/tunnel/events.go new file mode 100644 index 000000000..46d88341a --- /dev/null +++ b/pkg/domain/infra/tunnel/events.go @@ -0,0 +1,31 @@ +package tunnel + +import ( + "context" + "strings" + + "github.com/containers/libpod/pkg/api/handlers" + "github.com/containers/libpod/pkg/bindings/system" + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" +) + +func (ic *ContainerEngine) Events(ctx context.Context, opts entities.EventsOptions) error { + filters := make(map[string][]string) + if len(opts.Filter) > 0 { + for _, filter := range opts.Filter { + split := strings.Split(filter, "=") + if len(split) < 2 { + return errors.Errorf("invalid filter %q", filter) + } + filters[split[0]] = append(filters[split[0]], strings.Join(split[1:], "=")) + } + } + binChan := make(chan handlers.Event) + go func() { + for e := range binChan { + opts.EventChan <- e.ToLibpodEvent() + } + }() + return system.Events(ic.ClientCxt, binChan, nil, &opts.Since, &opts.Until, filters) +} diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 516914a68..7d40e0327 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -5,6 +5,7 @@ import ( "io/ioutil" "os" + "github.com/containers/common/pkg/config" "github.com/containers/image/v5/docker/reference" images "github.com/containers/libpod/pkg/bindings/images" "github.com/containers/libpod/pkg/domain/entities" @@ -241,3 +242,20 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, } return utils2.UntarToFileSystem(options.Output, f, nil) } + +// Diff reports the changes to the given image +func (ir *ImageEngine) Diff(ctx context.Context, nameOrId string, _ entities.DiffOptions) (*entities.DiffReport, error) { + changes, err := images.Diff(ir.ClientCxt, nameOrId) + if err != nil { + return nil, err + } + return &entities.DiffReport{Changes: changes}, nil +} + +func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.ImageSearchOptions) ([]entities.ImageSearchReport, error) { + return images.Search(ir.ClientCxt, term, opts) +} + +func (ir *ImageEngine) Config(_ context.Context) (*config.Config, error) { + return config.Default() +} diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go index 5bafef1fe..7c7a55c05 100644 --- a/pkg/domain/infra/tunnel/system.go +++ b/pkg/domain/infra/tunnel/system.go @@ -1 +1,22 @@ package tunnel + +import ( + "context" + "errors" + + "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/pkg/bindings/system" + "github.com/containers/libpod/pkg/domain/entities" +) + +func (ic *ContainerEngine) Info(ctx context.Context) (*define.Info, error) { + return system.Info(ic.ClientCxt) +} + +func (ic *ContainerEngine) RestService(_ context.Context, _ entities.ServiceOptions) error { + panic(errors.New("rest service is not supported when tunneling")) +} + +func (ic *ContainerEngine) VarlinkService(_ context.Context, _ entities.ServiceOptions) error { + panic(errors.New("varlink service is not supported when tunneling")) +} |