Merge pull request #5812 from jwhonce/wip/options

Add support for the global flags and config files
This commit is contained in:
OpenShift Merge Robot
2020-04-15 01:48:52 +02:00
committed by GitHub
22 changed files with 683 additions and 188 deletions

View File

@ -31,6 +31,9 @@ var (
Args: func(cmd *cobra.Command, args []string) error {
return parse.CheckAllLatestAndCIDFile(cmd, args, true, false)
},
Annotations: map[string]string{
registry.RootRequired: "true",
},
}
)

View File

@ -46,7 +46,7 @@ func init() {
flags.StringArrayVarP(&stopOptions.CIDFiles, "cidfile", "", nil, "Read the container ID from the file")
flags.BoolVarP(&stopOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.UintVarP(&stopTimeout, "time", "t", defaultContainerConfig.Engine.StopTimeout, "Seconds to wait for stop before killing the container")
if registry.EngineOptions.EngineMode == entities.ABIMode {
if registry.PodmanOptions.EngineMode == entities.ABIMode {
_ = flags.MarkHidden("latest")
_ = flags.MarkHidden("cidfile")
_ = flags.MarkHidden("ignore")

View File

@ -44,7 +44,7 @@ func init() {
flags.DurationVarP(&waitOptions.Interval, "interval", "i", time.Duration(250), "Milliseconds to wait before polling for completion")
flags.BoolVarP(&waitOptions.Latest, "latest", "l", false, "Act on the latest container podman is aware of")
flags.StringVar(&waitCondition, "condition", "stopped", "Condition to wait on")
if registry.EngineOptions.EngineMode == entities.ABIMode {
if registry.PodmanOptions.EngineMode == entities.ABIMode {
// TODO: This is the same as V1. We could skip creating the flag altogether in V2...
_ = flags.MarkHidden("latest")
}

View File

@ -3,8 +3,6 @@ package main
import (
"os"
"reflect"
"runtime"
"strings"
_ "github.com/containers/libpod/cmd/podmanV2/containers"
_ "github.com/containers/libpod/cmd/podmanV2/healthcheck"
@ -14,36 +12,13 @@ import (
"github.com/containers/libpod/cmd/podmanV2/registry"
_ "github.com/containers/libpod/cmd/podmanV2/system"
_ "github.com/containers/libpod/cmd/podmanV2/volumes"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/storage/pkg/reexec"
"github.com/sirupsen/logrus"
)
func init() {
if err := libpod.SetXdgDirs(); err != nil {
logrus.Errorf(err.Error())
os.Exit(1)
}
switch runtime.GOOS {
case "darwin":
fallthrough
case "windows":
registry.EngineOptions.EngineMode = entities.TunnelMode
case "linux":
registry.EngineOptions.EngineMode = entities.ABIMode
default:
logrus.Errorf("%s is not a supported OS", runtime.GOOS)
os.Exit(1)
}
// TODO: Is there a Cobra way to "peek" at os.Args?
for _, v := range os.Args {
if strings.HasPrefix(v, "--remote") {
registry.EngineOptions.EngineMode = entities.TunnelMode
}
}
// This is the bootstrap configuration, if user gives
// CLI flags parts of this configuration may be overwritten
registry.PodmanOptions = registry.NewPodmanConfig()
}
func main() {
@ -53,7 +28,7 @@ func main() {
return
}
for _, c := range registry.Commands {
if Contains(registry.EngineOptions.EngineMode, c.Mode) {
if Contains(registry.PodmanOptions.EngineMode, c.Mode) {
parent := rootCmd
if c.Parent != nil {
parent = c.Parent

View File

@ -0,0 +1,59 @@
package registry
import (
"os"
"runtime"
"strings"
"github.com/containers/common/pkg/config"
"github.com/containers/libpod/libpod"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/sirupsen/logrus"
)
const (
RootRequired = "RootRequired"
)
var (
PodmanOptions entities.PodmanConfig
)
// NewPodmanConfig creates a PodmanConfig from the environment
func NewPodmanConfig() entities.PodmanConfig {
if err := libpod.SetXdgDirs(); err != nil {
logrus.Errorf(err.Error())
os.Exit(1)
}
var mode entities.EngineMode
switch runtime.GOOS {
case "darwin":
fallthrough
case "windows":
mode = entities.TunnelMode
case "linux":
mode = entities.ABIMode
default:
logrus.Errorf("%s is not a supported OS", runtime.GOOS)
os.Exit(1)
}
// cobra.Execute() may not be called yet, so we peek at os.Args.
for _, v := range os.Args {
// Prefix checking works because of how default EngineMode's
// have been defined.
if strings.HasPrefix(v, "--remote=") {
mode = entities.TunnelMode
}
}
// FIXME: for rootless, where to get the path
// TODO:
cfg, err := config.NewConfig("")
if err != nil {
logrus.Error("Failed to obtain podman configuration")
os.Exit(1)
}
return entities.PodmanConfig{Config: cfg, EngineMode: mode}
}

View File

@ -29,8 +29,9 @@ var (
exitCode = ExecErrorCodeGeneric
imageEngine entities.ImageEngine
Commands []CliCommand
EngineOptions entities.EngineOptions
// Commands holds the cobra.Commands to present to the user, including
// parent if not a child of "root"
Commands []CliCommand
)
func SetExitCode(code int) {
@ -83,8 +84,8 @@ func ImageEngine() entities.ImageEngine {
// NewImageEngine is a wrapper for building an ImageEngine to be used for PreRunE functions
func NewImageEngine(cmd *cobra.Command, args []string) (entities.ImageEngine, error) {
if imageEngine == nil {
EngineOptions.FlagSet = cmd.Flags()
engine, err := infra.NewImageEngine(EngineOptions)
PodmanOptions.FlagSet = cmd.Flags()
engine, err := infra.NewImageEngine(PodmanOptions)
if err != nil {
return nil, err
}
@ -100,8 +101,8 @@ func ContainerEngine() entities.ContainerEngine {
// NewContainerEngine is a wrapper for building an ContainerEngine to be used for PreRunE functions
func NewContainerEngine(cmd *cobra.Command, args []string) (entities.ContainerEngine, error) {
if containerEngine == nil {
EngineOptions.FlagSet = cmd.Flags()
engine, err := infra.NewContainerEngine(EngineOptions)
PodmanOptions.FlagSet = cmd.Flags()
engine, err := infra.NewContainerEngine(PodmanOptions)
if err != nil {
return nil, err
}
@ -125,24 +126,17 @@ func IdOrLatestArgs(cmd *cobra.Command, args []string) error {
return nil
}
type podmanContextKey string
var podmanFactsKey = podmanContextKey("engineOptions")
func NewOptions(ctx context.Context, facts *entities.EngineOptions) context.Context {
return context.WithValue(ctx, podmanFactsKey, facts)
}
func Options(cmd *cobra.Command) (*entities.EngineOptions, error) {
if f, ok := cmd.Context().Value(podmanFactsKey).(*entities.EngineOptions); ok {
return f, errors.New("Command Context ")
}
return nil, nil
}
func GetContext() context.Context {
if cliCtx == nil {
cliCtx = context.TODO()
cliCtx = context.Background()
}
return cliCtx
}
type ContextOptionsKey string
const PodmanOptionsKey ContextOptionsKey = "PodmanOptions"
func GetContextWithOptions() context.Context {
return context.WithValue(GetContext(), PodmanOptionsKey, PodmanOptions)
}

View File

@ -5,5 +5,5 @@ import (
)
func IsRemote() bool {
return EngineOptions.EngineMode == entities.TunnelMode
return PodmanOptions.EngineMode == entities.TunnelMode
}

View File

@ -1,29 +1,37 @@
package main
import (
"context"
"fmt"
"log/syslog"
"os"
"path"
"runtime/pprof"
"github.com/containers/libpod/cmd/podmanV2/registry"
"github.com/containers/libpod/pkg/domain/entities"
"github.com/containers/libpod/pkg/rootless"
"github.com/containers/libpod/pkg/tracing"
"github.com/containers/libpod/version"
"github.com/opentracing/opentracing-go"
"github.com/pkg/errors"
"github.com/sirupsen/logrus"
logrusSyslog "github.com/sirupsen/logrus/hooks/syslog"
"github.com/spf13/cobra"
"github.com/spf13/pflag"
)
var (
rootCmd = &cobra.Command{
Use: path.Base(os.Args[0]),
Long: "Manage pods, containers and images",
SilenceUsage: true,
SilenceErrors: true,
TraverseChildren: true,
PersistentPreRunE: preRunE,
RunE: registry.SubCommandExists,
Version: version.Version,
Use: path.Base(os.Args[0]),
Long: "Manage pods, containers and images",
SilenceUsage: true,
SilenceErrors: true,
TraverseChildren: true,
PersistentPreRunE: preRunE,
RunE: registry.SubCommandExists,
PersistentPostRunE: postRunE,
Version: version.Version,
}
logLevels = entities.NewStringSet("debug", "info", "warn", "error", "fatal", "panic")
@ -32,30 +40,73 @@ var (
)
func init() {
// Override default --help information of `--version` global flag}
var dummyVersion bool
// TODO had to disable shorthand -v for version due to -v rm with volume
rootCmd.PersistentFlags().BoolVar(&dummyVersion, "version", false, "Version of Podman")
rootCmd.PersistentFlags().StringVarP(&registry.EngineOptions.Uri, "remote", "r", "", "URL to access Podman service")
rootCmd.PersistentFlags().StringSliceVar(&registry.EngineOptions.Identities, "identity", []string{}, "path to SSH identity file")
rootCmd.PersistentFlags().StringVar(&logLevel, "log-level", "error", fmt.Sprintf("Log messages above specified level (%s)", logLevels.String()))
rootCmd.PersistentFlags().BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)")
cobra.OnInitialize(
logging,
rootlessHook,
loggingHook,
syslogHook,
)
rootFlags(registry.PodmanOptions, rootCmd.PersistentFlags())
}
func preRunE(cmd *cobra.Command, args []string) error {
func Execute() {
if err := rootCmd.ExecuteContext(registry.GetContextWithOptions()); err != nil {
logrus.Error(err)
} else if registry.GetExitCode() == registry.ExecErrorCodeGeneric {
// The exitCode modified from registry.ExecErrorCodeGeneric,
// indicates an application
// running inside of a container failed, as opposed to the
// podman command failed. Must exit with that exit code
// otherwise command exited correctly.
registry.SetExitCode(0)
}
os.Exit(registry.GetExitCode())
}
func preRunE(cmd *cobra.Command, _ []string) error {
// Update PodmanOptions now that we "know" more
// TODO: pass in path overriding configuration file
registry.PodmanOptions = registry.NewPodmanConfig()
cmd.SetHelpTemplate(registry.HelpTemplate())
cmd.SetUsageTemplate(registry.UsageTemplate())
if cmd.Flag("cpu_profile").Changed {
f, err := os.Create(registry.PodmanOptions.CpuProfile)
if err != nil {
return errors.Wrapf(err, "unable to create cpu profiling file %s",
registry.PodmanOptions.CpuProfile)
}
if err := pprof.StartCPUProfile(f); err != nil {
return err
}
}
if cmd.Flag("trace").Changed {
tracer, closer := tracing.Init("podman")
opentracing.SetGlobalTracer(tracer)
registry.PodmanOptions.SpanCloser = closer
registry.PodmanOptions.Span = tracer.StartSpan("before-context")
registry.PodmanOptions.SpanCtx = opentracing.ContextWithSpan(context.Background(), registry.PodmanOptions.Span)
}
return nil
}
func logging() {
func postRunE(cmd *cobra.Command, args []string) error {
if cmd.Flag("cpu-profile").Changed {
pprof.StopCPUProfile()
}
if cmd.Flag("trace").Changed {
registry.PodmanOptions.Span.Finish()
registry.PodmanOptions.SpanCloser.Close()
}
return nil
}
func loggingHook() {
if !logLevels.Contains(logLevel) {
fmt.Fprintf(os.Stderr, "Log Level \"%s\" is not supported, choose from: %s\n", logLevel, logLevels.String())
logrus.Errorf("Log Level \"%s\" is not supported, choose from: %s", logLevel, logLevels.String())
os.Exit(1)
}
@ -83,17 +134,68 @@ func syslogHook() {
}
}
func Execute() {
o := registry.NewOptions(rootCmd.Context(), &registry.EngineOptions)
if err := rootCmd.ExecuteContext(o); err != nil {
fmt.Fprintln(os.Stderr, "Error:", err.Error())
} else if registry.GetExitCode() == registry.ExecErrorCodeGeneric {
// The exitCode modified from registry.ExecErrorCodeGeneric,
// indicates an application
// running inside of a container failed, as opposed to the
// podman command failed. Must exit with that exit code
// otherwise command exited correctly.
registry.SetExitCode(0)
func rootlessHook() {
if rootless.IsRootless() {
logrus.Error("rootless mode is currently not supported. Support will return ASAP.")
}
os.Exit(registry.GetExitCode())
// ce, err := registry.NewContainerEngine(rootCmd, []string{})
// if err != nil {
// logrus.WithError(err).Fatal("failed to obtain container engine")
// }
// ce.SetupRootLess(rootCmd)
}
func rootFlags(opts entities.PodmanConfig, flags *pflag.FlagSet) {
// V2 flags
flags.StringVarP(&opts.Uri, "remote", "r", "", "URL to access Podman service")
flags.StringSliceVar(&opts.Identities, "identity", []string{}, "path to SSH identity file")
// Override default --help information of `--version` global flag
// TODO: restore -v option for version without breaking -v for volumes
var dummyVersion bool
flags.BoolVar(&dummyVersion, "version", false, "Version of Podman")
cfg := opts.Config
flags.StringVar(&cfg.Engine.CgroupManager, "cgroup-manager", cfg.Engine.CgroupManager, opts.CGroupUsage)
flags.StringVar(&opts.CpuProfile, "cpu-profile", "", "Path for the cpu profiling results")
flags.StringVar(&opts.ConmonPath, "conmon", "", "Path of the conmon binary")
flags.StringVar(&cfg.Engine.NetworkCmdPath, "network-cmd-path", cfg.Engine.NetworkCmdPath, "Path to the command for configuring the network")
flags.StringVar(&cfg.Network.NetworkConfigDir, "cni-config-dir", cfg.Network.NetworkConfigDir, "Path of the configuration directory for CNI networks")
flags.StringVar(&cfg.Containers.DefaultMountsFile, "default-mounts-file", cfg.Containers.DefaultMountsFile, "Path to default mounts file")
flags.StringVar(&cfg.Engine.EventsLogger, "events-backend", cfg.Engine.EventsLogger, `Events backend to use ("file"|"journald"|"none")`)
flags.StringSliceVar(&cfg.Engine.HooksDir, "hooks-dir", cfg.Engine.HooksDir, "Set the OCI hooks directory path (may be set multiple times)")
flags.IntVar(&opts.MaxWorks, "max-workers", 0, "The maximum number of workers for parallel operations")
flags.StringVar(&cfg.Engine.Namespace, "namespace", cfg.Engine.Namespace, "Set the libpod namespace, used to create separate views of the containers and pods on the system")
flags.StringVar(&cfg.Engine.StaticDir, "root", "", "Path to the root directory in which data, including images, is stored")
flags.StringVar(&opts.Runroot, "runroot", "", "Path to the 'run directory' where all state information is stored")
flags.StringVar(&opts.RuntimePath, "runtime", "", "Path to the OCI-compatible binary used to run containers, default is /usr/bin/runc")
// -s is deprecated due to conflict with -s on subcommands
flags.StringVar(&opts.StorageDriver, "storage-driver", "", "Select which storage driver is used to manage storage of images and containers (default is overlay)")
flags.StringArrayVar(&opts.StorageOpts, "storage-opt", []string{}, "Used to pass an option to the storage driver")
flags.StringVar(&opts.Engine.TmpDir, "tmpdir", "", "Path to the tmp directory for libpod state content.\n\nNote: use the environment variable 'TMPDIR' to change the temporary storage location for container images, '/var/tmp'.\n")
flags.BoolVar(&opts.Trace, "trace", false, "Enable opentracing output (default false)")
// Override default --help information of `--help` global flag
var dummyHelp bool
flags.BoolVar(&dummyHelp, "help", false, "Help for podman")
flags.StringVar(&logLevel, "log-level", logLevel, fmt.Sprintf("Log messages above specified level (%s)", logLevels.String()))
// Hide these flags for both ABI and Tunneling
for _, f := range []string{
"cpu-profile",
"default-mounts-file",
"max-workers",
"trace",
} {
if err := flags.MarkHidden(f); err != nil {
logrus.Warnf("unable to mark %s flag as hidden", f)
}
}
// Only create these flags for ABI connections
if !registry.IsRemote() {
flags.BoolVar(&useSyslog, "syslog", false, "Output logging information to syslog as well as the console (default false)")
}
}

View File

@ -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
// 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
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
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
Config string
CpuProfile string
LogLevel string
TmpDir string
RemoteUserName string
RemoteHost string
VarlinkAddress string
ConnectionName string
RemoteConfigFilePath string
Port int
IdentityFile string
IgnoreHosts bool
Runroot string
StorageDriver string
StorageOpts []string
}
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
// 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
// }

View File

@ -3,11 +3,13 @@ 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
ContainerCheckpoint(ctx context.Context, namesOrIds []string, options CheckpointOptions) ([]*CheckpointReport, error)
ContainerCleanup(ctx context.Context, namesOrIds []string, options ContainerCleanupOptions) ([]*ContainerCleanupReport, error)

View File

@ -2,9 +2,12 @@ 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)

View File

@ -12,6 +12,7 @@ import (
"sync"
"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"
@ -893,3 +894,8 @@ func (ic *ContainerEngine) ContainerUnmount(ctx context.Context, nameOrIds []str
}
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()
}

View File

@ -9,6 +9,7 @@ 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"
@ -460,3 +461,8 @@ func (ir *ImageEngine) Search(ctx context.Context, term string, opts entities.Im
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()
}

View File

@ -4,17 +4,28 @@ 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"
)
@ -88,3 +99,146 @@ func (ic *ContainerEngine) VarlinkService(_ context.Context, opts entities.Servi
}
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]
// }

View File

@ -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)

View File

@ -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

View File

@ -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.RuntimePath))
}
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?

View File

@ -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

View File

@ -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")

View File

@ -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"
@ -362,3 +363,7 @@ func (ic *ContainerEngine) ContainerMount(ctx context.Context, nameOrIds []strin
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()
}

View File

@ -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"
@ -254,3 +255,7 @@ func (ir *ImageEngine) Diff(ctx context.Context, nameOrId string, _ entities.Dif
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()
}

View File

@ -516,6 +516,8 @@ func ParseInputTime(inputTime string) (time.Time, error) {
}
// GetGlobalOpts checks all global flags and generates the command string
// FIXME: Port input to config.Config
// TODO: Is there a "better" way to reverse values to flags? This seems brittle.
func GetGlobalOpts(c *cliconfig.RunlabelValues) string {
globalFlags := map[string]bool{
"cgroup-manager": true, "cni-config-dir": true, "conmon": true, "default-mounts-file": true,