diff options
Diffstat (limited to 'pkg/domain')
23 files changed, 541 insertions, 130 deletions
diff --git a/pkg/domain/entities/auto-update.go b/pkg/domain/entities/auto-update.go index aef8fc46b..c51158816 100644 --- a/pkg/domain/entities/auto-update.go +++ b/pkg/domain/entities/auto-update.go @@ -1,5 +1,11 @@ package entities +// AutoUpdateOptions are the options for running auto-update. +type AutoUpdateOptions struct { + // Authfile to use when contacting registries. + Authfile string +} + // AutoUpdateReport contains the results from running auto-update. type AutoUpdateReport struct { // Units - the restarted systemd units during auto-update. diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index e5330e1ab..8d85a9b23 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -170,7 +170,7 @@ type CheckpointOptions struct { IgnoreRootFS bool Keep bool Latest bool - LeaveRuninng bool + LeaveRunning bool TCPEstablished bool } @@ -242,7 +242,6 @@ type ExecOptions struct { Latest bool PreserveFDs uint Privileged bool - Streams define.AttachStreams Tty bool User string WorkDir string @@ -311,6 +310,7 @@ type ContainerRunReport struct { // cleanup command type ContainerCleanupOptions struct { All bool + Exec string Latest bool Remove bool RemoveImage bool diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 719ac3f9e..3d5161745 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -10,7 +10,7 @@ import ( ) type ContainerEngine interface { - AutoUpdate(ctx context.Context) (*AutoUpdateReport, []error) + AutoUpdate(ctx context.Context, options AutoUpdateOptions) (*AutoUpdateReport, []error) 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) @@ -19,7 +19,8 @@ type ContainerEngine interface { ContainerCp(ctx context.Context, source, dest string, options ContainerCpOptions) (*ContainerCpReport, 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) + ContainerExec(ctx context.Context, nameOrId string, options ExecOptions, streams define.AttachStreams) (int, error) + ContainerExecDetached(ctx context.Context, nameOrID string, options ExecOptions) (string, error) ContainerExists(ctx context.Context, nameOrId string) (*BoolReport, error) ContainerExport(ctx context.Context, nameOrId string, options ContainerExportOptions) error ContainerInit(ctx context.Context, namesOrIds []string, options ContainerInitOptions) ([]*ContainerInitReport, error) diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go index ffa71abd6..7d7099838 100644 --- a/pkg/domain/entities/engine_image.go +++ b/pkg/domain/entities/engine_image.go @@ -34,4 +34,5 @@ type ImageEngine interface { ManifestAnnotate(ctx context.Context, names []string, opts ManifestAnnotateOptions) (string, error) ManifestRemove(ctx context.Context, names []string) (string, error) ManifestPush(ctx context.Context, names []string, manifestPushOpts ManifestPushOptions) error + Sign(ctx context.Context, names []string, options SignOptions) (*SignReport, error) } diff --git a/pkg/domain/entities/engine_system.go b/pkg/domain/entities/engine_system.go index e2000f5cb..a0ecfe9ea 100644 --- a/pkg/domain/entities/engine_system.go +++ b/pkg/domain/entities/engine_system.go @@ -9,6 +9,6 @@ import ( type SystemEngine interface { Renumber(ctx context.Context, flags *pflag.FlagSet, config *PodmanConfig) error Migrate(ctx context.Context, flags *pflag.FlagSet, config *PodmanConfig, options SystemMigrateOptions) error - Reset(ctx context.Context, options SystemResetOptions) error + Reset(ctx context.Context) error Shutdown(ctx context.Context) } diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index e116a90b9..0f909ab37 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -1,7 +1,6 @@ package entities import ( - "net/url" "time" "github.com/containers/image/v5/manifest" @@ -221,15 +220,13 @@ type ImageSearchReport struct { // Image List Options type ImageListOptions struct { - All bool `json:"all" schema:"all"` - Filter []string `json:"Filter,omitempty"` - Filters url.Values `json:"filters" schema:"filters"` + All bool `json:"all" schema:"all"` + Filter []string `json:"Filter,omitempty"` } type ImagePruneOptions struct { - All bool `json:"all" schema:"all"` - Filter []string `json:"filter" schema:"filter"` - Filters url.Values `json:"filters" schema:"filters"` + All bool `json:"all" schema:"all"` + Filter []string `json:"filter" schema:"filter"` } type ImagePruneReport struct { @@ -309,3 +306,13 @@ type SetTrustOptions struct { PubKeysFile []string Type string } + +// SignOptions describes input options for the CLI signing +type SignOptions struct { + Directory string + SignBy string + CertDir string +} + +// SignReport describes the result of signing +type SignReport struct{} diff --git a/pkg/domain/entities/network.go b/pkg/domain/entities/network.go index cffd40899..9beeeb042 100644 --- a/pkg/domain/entities/network.go +++ b/pkg/domain/entities/network.go @@ -10,6 +10,7 @@ import ( type NetworkListOptions struct { Format string Quiet bool + Filter string } // NetworkListReport describes the results from listing networks @@ -19,6 +20,7 @@ type NetworkListReport struct { // NetworkInspectOptions describes options for inspect networks type NetworkInspectOptions struct { + Format string } // NetworkInspectReport describes the results from inspect networks @@ -36,6 +38,7 @@ type NetworkRmReport struct { } // NetworkCreateOptions describes options to create a network +// swagger:model NetworkCreateOptions type NetworkCreateOptions struct { DisableDNS bool Driver string diff --git a/pkg/domain/infra/abi/auto-update.go b/pkg/domain/infra/abi/auto-update.go index aa20664b4..9fcc451fd 100644 --- a/pkg/domain/infra/abi/auto-update.go +++ b/pkg/domain/infra/abi/auto-update.go @@ -7,7 +7,11 @@ import ( "github.com/containers/libpod/pkg/domain/entities" ) -func (ic *ContainerEngine) AutoUpdate(ctx context.Context) (*entities.AutoUpdateReport, []error) { - units, failures := autoupdate.AutoUpdate(ic.Libpod) +func (ic *ContainerEngine) AutoUpdate(ctx context.Context, options entities.AutoUpdateOptions) (*entities.AutoUpdateReport, []error) { + // Convert the entities options to the autoupdate ones. We can't use + // them in the entities package as low-level packages must not leak + // into the remote client. + autoOpts := autoupdate.Options{Authfile: options.Authfile} + units, failures := autoupdate.AutoUpdate(ic.Libpod, autoOpts) return &entities.AutoUpdateReport{Units: units}, failures } diff --git a/pkg/domain/infra/abi/containers.go b/pkg/domain/infra/abi/containers.go index 249e8147c..b4e38ca23 100644 --- a/pkg/domain/infra/abi/containers.go +++ b/pkg/domain/infra/abi/containers.go @@ -434,6 +434,7 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ TCPEstablished: options.TCPEstablished, TargetFile: options.Export, IgnoreRootfs: options.IgnoreRootFS, + KeepRunning: options.LeaveRunning, } if options.All { @@ -535,7 +536,22 @@ func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, return nil } -func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) { +func makeExecConfig(options entities.ExecOptions) *libpod.ExecConfig { + execConfig := new(libpod.ExecConfig) + execConfig.Command = options.Cmd + execConfig.Terminal = options.Tty + execConfig.Privileged = options.Privileged + execConfig.Environment = options.Envs + execConfig.User = options.User + execConfig.WorkDir = options.WorkDir + execConfig.DetachKeys = &options.DetachKeys + execConfig.PreserveFDs = options.PreserveFDs + execConfig.AttachStdin = options.Interactive + + return execConfig +} + +func checkExecPreserveFDs(options entities.ExecOptions) (int, error) { ec := define.ExecErrorCodeGeneric if options.PreserveFDs > 0 { entries, err := ioutil.ReadDir("/proc/self/fd") @@ -558,15 +574,66 @@ func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, o } } } + return ec, nil +} + +func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions, streams define.AttachStreams) (int, error) { + ec, err := checkExecPreserveFDs(options) + if err != nil { + return ec, err + } ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod) if err != nil { return ec, err } ctr := ctrs[0] - ec, err = terminal.ExecAttachCtr(ctx, ctr, options.Tty, options.Privileged, options.Envs, options.Cmd, options.User, options.WorkDir, &options.Streams, options.PreserveFDs, options.DetachKeys) + + execConfig := makeExecConfig(options) + + ec, err = terminal.ExecAttachCtr(ctx, ctr, execConfig, &streams) return define.TranslateExecErrorToExitCode(ec, err), err } +func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrId string, options entities.ExecOptions) (string, error) { + _, err := checkExecPreserveFDs(options) + if err != nil { + return "", err + } + ctrs, err := getContainersByContext(false, options.Latest, []string{nameOrId}, ic.Libpod) + if err != nil { + return "", err + } + ctr := ctrs[0] + + execConfig := makeExecConfig(options) + + // Make an exit command + storageConfig := ic.Libpod.StorageConfig() + runtimeConfig, err := ic.Libpod.GetConfig() + if err != nil { + return "", errors.Wrapf(err, "error retrieving Libpod configuration to build exec exit command") + } + podmanPath, err := os.Executable() + if err != nil { + return "", errors.Wrapf(err, "error retrieving executable to build exec exit command") + } + // TODO: Add some ability to toggle syslog + exitCommandArgs := generate.CreateExitCommandArgs(storageConfig, runtimeConfig, podmanPath, false, true, true) + execConfig.ExitCommand = exitCommandArgs + + // Create and start the exec session + id, err := ctr.ExecCreate(execConfig) + if err != nil { + return "", err + } + + // TODO: we should try and retrieve exit code if this fails. + if err := ctr.ExecStart(id); err != nil { + return "", err + } + return id, nil +} + func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { var reports []*entities.ContainerStartReport var exitCode = define.ExecErrorCodeGeneric @@ -835,6 +902,20 @@ func (ic *ContainerEngine) ContainerCleanup(ctx context.Context, namesOrIds []st for _, ctr := range ctrs { var err error report := entities.ContainerCleanupReport{Id: ctr.ID()} + + if options.Exec != "" { + if options.Remove { + if err := ctr.ExecRemove(options.Exec, false); err != nil { + return nil, err + } + } else { + if err := ctr.ExecCleanup(options.Exec); err != nil { + return nil, err + } + } + return []*entities.ContainerCleanupReport{}, nil + } + if options.Remove { err = ic.Libpod.RemoveContainer(ctx, ctr, false, true) if err != nil { diff --git a/pkg/domain/infra/abi/healthcheck.go b/pkg/domain/infra/abi/healthcheck.go index 351bf4f7e..4e925ef56 100644 --- a/pkg/domain/infra/abi/healthcheck.go +++ b/pkg/domain/infra/abi/healthcheck.go @@ -3,7 +3,6 @@ package abi import ( "context" - "github.com/containers/libpod/libpod" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/domain/entities" ) @@ -13,9 +12,9 @@ func (ic *ContainerEngine) HealthCheckRun(ctx context.Context, nameOrId string, if err != nil { return nil, err } - hcStatus := "unhealthy" - if status == libpod.HealthCheckSuccess { - hcStatus = "healthy" + hcStatus := define.HealthCheckUnhealthy + if status == define.HealthCheckSuccess { + hcStatus = define.HealthCheckHealthy } report := define.HealthCheckResults{ Status: hcStatus, diff --git a/pkg/domain/infra/abi/images.go b/pkg/domain/infra/abi/images.go index 7ab5131f0..6e774df8e 100644 --- a/pkg/domain/infra/abi/images.go +++ b/pkg/domain/infra/abi/images.go @@ -4,14 +4,22 @@ import ( "context" "fmt" "io" + "io/ioutil" + "net/url" "os" + "path/filepath" + "strconv" "strings" + "github.com/containers/libpod/pkg/rootless" + "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/signature" + "github.com/containers/image/v5/transports" "github.com/containers/image/v5/transports/alltransports" "github.com/containers/image/v5/types" "github.com/containers/libpod/libpod/define" @@ -19,6 +27,7 @@ import ( libpodImage "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/domain/entities" domainUtils "github.com/containers/libpod/pkg/domain/utils" + "github.com/containers/libpod/pkg/trust" "github.com/containers/libpod/pkg/util" "github.com/containers/storage" imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" @@ -26,6 +35,9 @@ import ( "github.com/sirupsen/logrus" ) +// SignatureStoreDir defines default directory to store signatures +const SignatureStoreDir = "/var/lib/containers/sigstore" + func (ir *ImageEngine) Exists(_ context.Context, nameOrId string) (*entities.BoolReport, error) { _, err := ir.Libpod.ImageRuntime().NewFromLocal(nameOrId) if err != nil && errors.Cause(err) != define.ErrNoSuchImage { @@ -549,3 +561,145 @@ func (ir *ImageEngine) Shutdown(_ context.Context) { _ = ir.Libpod.Shutdown(false) }) } + +func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entities.SignOptions) (*entities.SignReport, error) { + dockerRegistryOptions := image.DockerRegistryOptions{ + DockerCertPath: options.CertDir, + } + + mech, err := signature.NewGPGSigningMechanism() + if err != nil { + return nil, errors.Wrap(err, "error initializing GPG") + } + defer mech.Close() + if err := mech.SupportsSigning(); err != nil { + return nil, errors.Wrap(err, "signing is not supported") + } + sc := ir.Libpod.SystemContext() + sc.DockerCertPath = options.CertDir + + systemRegistriesDirPath := trust.RegistriesDirPath(sc) + registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath) + if err != nil { + return nil, errors.Wrapf(err, "error reading registry configuration") + } + + for _, signimage := range names { + var sigStoreDir string + srcRef, err := alltransports.ParseImageName(signimage) + if err != nil { + return nil, errors.Wrapf(err, "error parsing image name") + } + rawSource, err := srcRef.NewImageSource(ctx, sc) + if err != nil { + return nil, errors.Wrapf(err, "error getting image source") + } + err = rawSource.Close() + if err != nil { + logrus.Errorf("unable to close new image source %q", err) + } + getManifest, _, err := rawSource.GetManifest(ctx, nil) + if err != nil { + return nil, errors.Wrapf(err, "error getting getManifest") + } + dockerReference := rawSource.Reference().DockerReference() + if dockerReference == nil { + return nil, errors.Errorf("cannot determine canonical Docker reference for destination %s", transports.ImageName(rawSource.Reference())) + } + + // create the signstore file + rtc, err := ir.Libpod.GetConfig() + if err != nil { + return nil, err + } + newImage, err := ir.Libpod.ImageRuntime().New(ctx, signimage, rtc.Engine.SignaturePolicyPath, "", os.Stderr, &dockerRegistryOptions, image.SigningOptions{SignBy: options.SignBy}, nil, util.PullImageMissing) + if err != nil { + return nil, errors.Wrapf(err, "error pulling image %s", signimage) + } + if sigStoreDir == "" { + if rootless.IsRootless() { + sigStoreDir = filepath.Join(filepath.Dir(ir.Libpod.StorageConfig().GraphRoot), "sigstore") + } else { + registryInfo := trust.HaveMatchRegistry(rawSource.Reference().DockerReference().String(), registryConfigs) + if registryInfo != nil { + if sigStoreDir = registryInfo.SigStoreStaging; sigStoreDir == "" { + sigStoreDir = registryInfo.SigStore + + } + } + } + } + sigStoreDir, err = isValidSigStoreDir(sigStoreDir) + if err != nil { + return nil, errors.Wrapf(err, "invalid signature storage %s", sigStoreDir) + } + repos, err := newImage.RepoDigests() + if err != nil { + return nil, errors.Wrapf(err, "error calculating repo digests for %s", signimage) + } + if len(repos) == 0 { + logrus.Errorf("no repodigests associated with the image %s", signimage) + continue + } + + // create signature + newSig, err := signature.SignDockerManifest(getManifest, dockerReference.String(), mech, options.SignBy) + if err != nil { + return nil, errors.Wrapf(err, "error creating new signature") + } + + trimmedDigest := strings.TrimPrefix(repos[0], strings.Split(repos[0], "/")[0]) + sigStoreDir = filepath.Join(sigStoreDir, strings.Replace(trimmedDigest, ":", "=", 1)) + if err := os.MkdirAll(sigStoreDir, 0751); err != nil { + // The directory is allowed to exist + if !os.IsExist(err) { + logrus.Errorf("error creating directory %s: %s", sigStoreDir, err) + continue + } + } + sigFilename, err := getSigFilename(sigStoreDir) + if err != nil { + logrus.Errorf("error creating sigstore file: %v", err) + continue + } + err = ioutil.WriteFile(filepath.Join(sigStoreDir, sigFilename), newSig, 0644) + if err != nil { + logrus.Errorf("error storing signature for %s", rawSource.Reference().DockerReference().String()) + continue + } + } + return nil, nil +} + +func getSigFilename(sigStoreDirPath string) (string, error) { + sigFileSuffix := 1 + sigFiles, err := ioutil.ReadDir(sigStoreDirPath) + if err != nil { + return "", err + } + sigFilenames := make(map[string]bool) + for _, file := range sigFiles { + sigFilenames[file.Name()] = true + } + for { + sigFilename := "signature-" + strconv.Itoa(sigFileSuffix) + if _, exists := sigFilenames[sigFilename]; !exists { + return sigFilename, nil + } + sigFileSuffix++ + } +} + +func isValidSigStoreDir(sigStoreDir string) (string, error) { + writeURIs := map[string]bool{"file": true} + url, err := url.Parse(sigStoreDir) + if err != nil { + return sigStoreDir, errors.Wrapf(err, "invalid directory %s", sigStoreDir) + } + _, exists := writeURIs[url.Scheme] + if !exists { + return sigStoreDir, errors.Errorf("writing to %s is not supported. Use a supported scheme", sigStoreDir) + } + sigStoreDir = url.Path + return sigStoreDir, nil +} diff --git a/pkg/domain/infra/abi/images_list.go b/pkg/domain/infra/abi/images_list.go index c559e250c..3034e36ec 100644 --- a/pkg/domain/infra/abi/images_list.go +++ b/pkg/domain/infra/abi/images_list.go @@ -13,14 +13,7 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) err error ) - // TODO: Future work support for domain.Filters - // filters := utils.ToLibpodFilters(opts.Filters) - - if len(opts.Filter) > 0 { - images, err = ir.Libpod.ImageRuntime().GetImagesWithFilters(opts.Filter) - } else { - images, err = ir.Libpod.ImageRuntime().GetImages() - } + images, err = ir.Libpod.ImageRuntime().GetImagesWithFilters(opts.Filter) if err != nil { return nil, err } @@ -40,9 +33,18 @@ func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) } } } else { - repoTags, _ = img.RepoTags() - if len(repoTags) == 0 { - continue + repoTags, err = img.RepoTags() + if err != nil { + return nil, err + } + if len(img.Names()) == 0 { + parent, err := img.IsParent(ctx) + if err != nil { + return nil, err + } + if parent { + continue + } } } diff --git a/pkg/domain/infra/abi/network.go b/pkg/domain/infra/abi/network.go index 5c39b5374..8e3515824 100644 --- a/pkg/domain/infra/abi/network.go +++ b/pkg/domain/infra/abi/network.go @@ -6,7 +6,9 @@ import ( "fmt" "io/ioutil" "path/filepath" + "strings" + "github.com/containernetworking/cni/libcni" cniversion "github.com/containernetworking/cni/pkg/version" "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/domain/entities" @@ -15,32 +17,32 @@ import ( "github.com/pkg/errors" ) -func getCNIConfDir(r *libpod.Runtime) (string, error) { - config, err := r.GetConfig() - if err != nil { - return "", err - } - configPath := config.Network.NetworkConfigDir - - if len(config.Network.NetworkConfigDir) < 1 { - configPath = network.CNIConfigDir - } - return configPath, nil -} - func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) { var reports []*entities.NetworkListReport - cniConfigPath, err := getCNIConfDir(ic.Libpod) + + config, err := ic.Libpod.GetConfig() if err != nil { return nil, err } - networks, err := network.LoadCNIConfsFromDir(cniConfigPath) + + networks, err := network.LoadCNIConfsFromDir(network.GetCNIConfDir(config)) if err != nil { return nil, err } + var tokens []string + // tokenize the networkListOptions.Filter in key=value. + if len(options.Filter) > 0 { + tokens = strings.Split(options.Filter, "=") + if len(tokens) != 2 { + return nil, fmt.Errorf("invalid filter syntax : %s", options.Filter) + } + } + for _, n := range networks { - reports = append(reports, &entities.NetworkListReport{NetworkConfigList: n}) + if ifPassesFilterTest(n, tokens) { + reports = append(reports, &entities.NetworkListReport{NetworkConfigList: n}) + } } return reports, nil } @@ -49,8 +51,14 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri var ( rawCNINetworks []entities.NetworkInspectReport ) + + config, err := ic.Libpod.GetConfig() + if err != nil { + return nil, err + } + for _, name := range namesOrIds { - rawList, err := network.InspectNetwork(name) + rawList, err := network.InspectNetwork(config, name) if err != nil { return nil, err } @@ -61,6 +69,12 @@ func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []stri func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { var reports []*entities.NetworkRmReport + + config, err := ic.Libpod.GetConfig() + if err != nil { + return nil, err + } + for _, name := range namesOrIds { report := entities.NetworkRmReport{Name: name} containers, err := ic.Libpod.GetAllContainers() @@ -80,7 +94,7 @@ func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, o } } } - if err := network.RemoveNetwork(name); err != nil { + if err := network.RemoveNetwork(config, name); err != nil { report.Err = err } reports = append(reports, &report) @@ -117,10 +131,10 @@ func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreate // if range is provided, make sure it is "in" network if subnet.IP != nil { // if network is provided, does it conflict with existing CNI or live networks - err = network.ValidateUserNetworkIsAvailable(subnet) + err = network.ValidateUserNetworkIsAvailable(runtimeConfig, subnet) } else { // if no network is provided, figure out network - subnet, err = network.GetFreeNetwork() + subnet, err = network.GetFreeNetwork(runtimeConfig) } if err != nil { return "", err @@ -158,13 +172,13 @@ func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreate return "", errors.Errorf("the ip range %s does not fall within the subnet range %s", options.Range.String(), subnet.String()) } } - bridgeDeviceName, err := network.GetFreeDeviceName() + bridgeDeviceName, err := network.GetFreeDeviceName(runtimeConfig) if err != nil { return "", err } if len(name) > 0 { - netNames, err := network.GetNetworkNamesFromFileSystem() + netNames, err := network.GetNetworkNamesFromFileSystem(runtimeConfig) if err != nil { return "", err } @@ -205,11 +219,7 @@ func createBridge(r *libpod.Runtime, name string, options entities.NetworkCreate if err != nil { return "", err } - cniConfigPath, err := getCNIConfDir(r) - if err != nil { - return "", err - } - cniPathName := filepath.Join(cniConfigPath, fmt.Sprintf("%s.conflist", name)) + cniPathName := filepath.Join(network.GetCNIConfDir(runtimeConfig), fmt.Sprintf("%s.conflist", name)) err = ioutil.WriteFile(cniPathName, b, 0644) return cniPathName, err } @@ -222,12 +232,18 @@ func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreat if err != nil { return "", err } + + config, err := r.GetConfig() + if err != nil { + return "", err + } + // Make sure the host-device exists if !util.StringInSlice(options.MacVLAN, liveNetNames) { return "", errors.Errorf("failed to find network interface %q", options.MacVLAN) } if len(name) > 0 { - netNames, err := network.GetNetworkNamesFromFileSystem() + netNames, err := network.GetNetworkNamesFromFileSystem(config) if err != nil { return "", err } @@ -235,7 +251,7 @@ func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreat return "", errors.Errorf("the network name %s is already used", name) } } else { - name, err = network.GetFreeDeviceName() + name, err = network.GetFreeDeviceName(config) if err != nil { return "", err } @@ -248,11 +264,29 @@ func createMacVLAN(r *libpod.Runtime, name string, options entities.NetworkCreat if err != nil { return "", err } - cniConfigPath, err := getCNIConfDir(r) - if err != nil { - return "", err - } - cniPathName := filepath.Join(cniConfigPath, fmt.Sprintf("%s.conflist", name)) + cniPathName := filepath.Join(network.GetCNIConfDir(config), fmt.Sprintf("%s.conflist", name)) err = ioutil.WriteFile(cniPathName, b, 0644) return cniPathName, err } + +func ifPassesFilterTest(netconf *libcni.NetworkConfigList, filter []string) bool { + result := false + if len(filter) == 0 { + // No filter, so pass + return true + } + switch strings.ToLower(filter[0]) { + case "name": + if filter[1] == netconf.Name { + result = true + } + case "plugin": + plugins := network.GetCNIPlugins(netconf) + if strings.Contains(plugins, filter[1]) { + result = true + } + default: + result = false + } + return result +} diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index d701d65de..af2ec5f7b 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -375,7 +375,7 @@ func sizeOfPath(path string) (int64, error) { return size, err } -func (se *SystemEngine) Reset(ctx context.Context, options entities.SystemResetOptions) error { +func (se *SystemEngine) Reset(ctx context.Context) error { return se.Libpod.Reset(ctx) } diff --git a/pkg/domain/infra/abi/terminal/terminal_linux.go b/pkg/domain/infra/abi/terminal/terminal_linux.go index 15701342f..8d9cdde03 100644 --- a/pkg/domain/infra/abi/terminal/terminal_linux.go +++ b/pkg/domain/infra/abi/terminal/terminal_linux.go @@ -15,13 +15,13 @@ import ( ) // ExecAttachCtr execs and attaches to a container -func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged bool, env map[string]string, cmd []string, user, workDir string, streams *define.AttachStreams, preserveFDs uint, detachKeys string) (int, error) { +func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, execConfig *libpod.ExecConfig, streams *define.AttachStreams) (int, error) { resize := make(chan remotecommand.TerminalSize) haveTerminal := terminal.IsTerminal(int(os.Stdin.Fd())) // Check if we are attached to a terminal. If we are, generate resize // events, and set the terminal to raw mode - if haveTerminal && tty { + if haveTerminal && execConfig.Terminal { cancel, oldTermState, err := handleTerminalAttach(ctx, resize) if err != nil { return -1, err @@ -34,16 +34,6 @@ func ExecAttachCtr(ctx context.Context, ctr *libpod.Container, tty, privileged b }() } - execConfig := new(libpod.ExecConfig) - execConfig.Command = cmd - execConfig.Terminal = tty - execConfig.Privileged = privileged - execConfig.Environment = env - execConfig.User = user - execConfig.WorkDir = workDir - execConfig.DetachKeys = &detachKeys - execConfig.PreserveFDs = preserveFDs - return ctr.Exec(execConfig, streams, resize) } diff --git a/pkg/domain/infra/tunnel/auto-update.go b/pkg/domain/infra/tunnel/auto-update.go index fac033050..5c2dd360d 100644 --- a/pkg/domain/infra/tunnel/auto-update.go +++ b/pkg/domain/infra/tunnel/auto-update.go @@ -7,6 +7,6 @@ import ( "github.com/pkg/errors" ) -func (ic *ContainerEngine) AutoUpdate(ctx context.Context) (*entities.AutoUpdateReport, []error) { +func (ic *ContainerEngine) AutoUpdate(ctx context.Context, options entities.AutoUpdateOptions) (*entities.AutoUpdateReport, []error) { return nil, []error{errors.New("not implemented")} } diff --git a/pkg/domain/infra/tunnel/containers.go b/pkg/domain/infra/tunnel/containers.go index 49a3069d6..30c4a8359 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -4,14 +4,17 @@ import ( "context" "io" "os" + "strings" "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" "github.com/containers/libpod/pkg/bindings/containers" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/specgen" "github.com/pkg/errors" + "github.com/sirupsen/logrus" ) func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string, image string, args []string, options entities.ContainerRunlabelOptions) error { @@ -84,10 +87,25 @@ func (ic *ContainerEngine) ContainerStop(ctx context.Context, namesOrIds []strin } for _, c := range ctrs { report := entities.StopReport{Id: c.ID} - report.Err = containers.Stop(ic.ClientCxt, c.ID, &options.Timeout) - // TODO we need to associate errors returned by http with common - // define.errors so that we can equity tests. this will allow output - // to be the same as the native client + if err = containers.Stop(ic.ClientCxt, c.ID, &options.Timeout); err != nil { + // These first two are considered non-fatal under the right conditions + if errors.Cause(err).Error() == define.ErrCtrStopped.Error() { + logrus.Debugf("Container %s is already stopped", c.ID) + reports = append(reports, &report) + continue + } else if options.All && errors.Cause(err).Error() == define.ErrCtrStateInvalid.Error() { + logrus.Debugf("Container %s is not running, could not stop", c.ID) + reports = append(reports, &report) + continue + } + + // TODO we need to associate errors returned by http with common + // define.errors so that we can equity tests. this will allow output + // to be the same as the native client + report.Err = err + reports = append(reports, &report) + continue + } reports = append(reports, &report) } return reports, nil @@ -267,7 +285,7 @@ func (ic *ContainerEngine) ContainerCheckpoint(ctx context.Context, namesOrIds [ } } for _, c := range ctrs { - report, err := containers.Checkpoint(ic.ClientCxt, c.ID, &options.Keep, &options.LeaveRuninng, &options.TCPEstablished, &options.IgnoreRootFS, &options.Export) + report, err := containers.Checkpoint(ic.ClientCxt, c.ID, &options.Keep, &options.LeaveRunning, &options.TCPEstablished, &options.IgnoreRootFS, &options.Export) if err != nil { reports = append(reports, &entities.CheckpointReport{Id: c.ID, Err: err}) } @@ -324,15 +342,59 @@ func (ic *ContainerEngine) ContainerLogs(ctx context.Context, containers []strin } func (ic *ContainerEngine) ContainerAttach(ctx context.Context, nameOrId string, options entities.AttachOptions) error { - return errors.New("not implemented") + return containers.Attach(ic.ClientCxt, nameOrId, &options.DetachKeys, nil, bindings.PTrue, options.Stdin, options.Stdout, options.Stderr, nil) } -func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions) (int, error) { +func (ic *ContainerEngine) ContainerExec(ctx context.Context, nameOrId string, options entities.ExecOptions, streams define.AttachStreams) (int, error) { return 125, errors.New("not implemented") } +func (ic *ContainerEngine) ContainerExecDetached(ctx context.Context, nameOrID string, options entities.ExecOptions) (string, error) { + return "", errors.New("not implemented") +} + +func startAndAttach(ic *ContainerEngine, name string, detachKeys *string, input, output, errput *os.File) error { //nolint + attachErr := make(chan error) + attachReady := make(chan bool) + go func() { + err := containers.Attach(ic.ClientCxt, name, detachKeys, bindings.PFalse, bindings.PTrue, input, output, errput, attachReady) + attachErr <- err + }() + // Wait for the attach to actually happen before starting + // the container. + <-attachReady + if err := containers.Start(ic.ClientCxt, name, detachKeys); err != nil { + return err + } + return <-attachErr +} + func (ic *ContainerEngine) ContainerStart(ctx context.Context, namesOrIds []string, options entities.ContainerStartOptions) ([]*entities.ContainerStartReport, error) { - return nil, errors.New("not implemented") + var reports []*entities.ContainerStartReport + for _, name := range namesOrIds { + report := entities.ContainerStartReport{ + Id: name, + RawInput: name, + ExitCode: 125, + } + if options.Attach { + report.Err = startAndAttach(ic, name, &options.DetachKeys, options.Stdin, options.Stdout, options.Stderr) + if report.Err == nil { + exitCode, err := containers.Wait(ic.ClientCxt, name, nil) + if err == nil { + report.ExitCode = int(exitCode) + } + } else { + report.ExitCode = define.ExitCode(report.Err) + } + reports = append(reports, &report) + return reports, nil + } + report.Err = containers.Start(ic.ClientCxt, name, &options.DetachKeys) + report.ExitCode = define.ExitCode(report.Err) + reports = append(reports, &report) + } + return reports, nil } func (ic *ContainerEngine) ContainerList(ctx context.Context, options entities.ContainerListOptions) ([]entities.ListContainer, error) { @@ -340,7 +402,30 @@ 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") + if opts.Rm { + logrus.Info("the remote client does not support --rm yet") + } + con, err := containers.CreateWithSpec(ic.ClientCxt, opts.Spec) + if err != nil { + return nil, err + } + report := entities.ContainerRunReport{Id: con.ID} + // Attach + if !opts.Detach { + err = startAndAttach(ic, con.ID, &opts.DetachKeys, opts.InputStream, opts.OutputStream, opts.ErrorStream) + if err == nil { + exitCode, err := containers.Wait(ic.ClientCxt, con.ID, nil) + if err == nil { + report.ExitCode = int(exitCode) + } + } + } else { + err = containers.Start(ic.ClientCxt, con.ID, nil) + } + if err != nil { + report.ExitCode = define.ExitCode(err) + } + return &report, err } func (ic *ContainerEngine) ContainerDiff(ctx context.Context, nameOrId string, _ entities.DiffOptions) (*entities.DiffReport, error) { @@ -360,6 +445,11 @@ func (ic *ContainerEngine) ContainerInit(ctx context.Context, namesOrIds []strin } for _, ctr := range ctrs { err := containers.ContainerInit(ic.ClientCxt, ctr.ID) + // When using all, it is NOT considered an error if a container + // has already been init'd. + if err != nil && options.All && strings.Contains(errors.Cause(err).Error(), define.ErrCtrStateInvalid.Error()) { + err = nil + } reports = append(reports, &entities.ContainerInitReport{ Err: err, Id: ctr.ID, @@ -381,7 +471,29 @@ func (ic *ContainerEngine) Config(_ context.Context) (*config.Config, error) { } func (ic *ContainerEngine) ContainerPort(ctx context.Context, nameOrId string, options entities.ContainerPortOptions) ([]*entities.ContainerPortReport, error) { - return nil, errors.New("not implemented") + var ( + reports []*entities.ContainerPortReport + namesOrIds []string + ) + if len(nameOrId) > 0 { + namesOrIds = append(namesOrIds, nameOrId) + } + ctrs, err := getContainersByContext(ic.ClientCxt, options.All, namesOrIds) + if err != nil { + return nil, err + } + for _, con := range ctrs { + if con.State != define.ContainerStateRunning.String() { + continue + } + if len(con.Ports) > 0 { + reports = append(reports, &entities.ContainerPortReport{ + Id: con.ID, + Ports: con.Ports, + }) + } + } + return reports, nil } func (ic *ContainerEngine) ContainerCp(ctx context.Context, source, dest string, options entities.ContainerCpOptions) (*entities.ContainerCpReport, error) { diff --git a/pkg/domain/infra/tunnel/helpers.go b/pkg/domain/infra/tunnel/helpers.go index 682d60d6a..862c7a5d6 100644 --- a/pkg/domain/infra/tunnel/helpers.go +++ b/pkg/domain/infra/tunnel/helpers.go @@ -20,7 +20,7 @@ func getContainersByContext(contextWithConnection context.Context, all bool, nam if all && len(namesOrIds) > 0 { return nil, errors.New("cannot lookup containers and all") } - c, err := containers.List(contextWithConnection, nil, &bindings.PTrue, nil, nil, nil, &bindings.PTrue) + c, err := containers.List(contextWithConnection, nil, bindings.PTrue, nil, nil, nil, bindings.PTrue) if err != nil { return nil, err } @@ -37,7 +37,7 @@ func getContainersByContext(contextWithConnection context.Context, all bool, nam } } if !found { - return nil, errors.Errorf("unable to find container %q", id) + return nil, errors.Wrapf(define.ErrNoSuchCtr, "unable to find container %q", id) } } return cons, nil diff --git a/pkg/domain/infra/tunnel/images.go b/pkg/domain/infra/tunnel/images.go index 00893194c..3d5626c45 100644 --- a/pkg/domain/infra/tunnel/images.go +++ b/pkg/domain/infra/tunnel/images.go @@ -4,6 +4,7 @@ import ( "context" "io/ioutil" "os" + "strings" "github.com/containers/common/pkg/config" "github.com/containers/image/v5/docker/reference" @@ -25,8 +26,13 @@ func (ir *ImageEngine) Remove(ctx context.Context, imagesArg []string, opts enti } func (ir *ImageEngine) List(ctx context.Context, opts entities.ImageListOptions) ([]*entities.ImageSummary, error) { - images, err := images.List(ir.ClientCxt, &opts.All, opts.Filters) + filters := make(map[string][]string, len(opts.Filter)) + for _, filter := range opts.Filter { + f := strings.Split(filter, "=") + filters[f[0]] = f[1:] + } + images, err := images.List(ir.ClientCxt, &opts.All, filters) if err != nil { return nil, err } @@ -61,7 +67,13 @@ func (ir *ImageEngine) History(ctx context.Context, nameOrId string, opts entiti } func (ir *ImageEngine) Prune(ctx context.Context, opts entities.ImagePruneOptions) (*entities.ImagePruneReport, error) { - results, err := images.Prune(ir.ClientCxt, &opts.All, opts.Filters) + filters := make(map[string][]string, len(opts.Filter)) + for _, filter := range opts.Filter { + f := strings.Split(filter, "=") + filters[f[0]] = f[1:] + } + + results, err := images.Prune(ir.ClientCxt, &opts.All, filters) if err != nil { return nil, err } @@ -112,7 +124,7 @@ func (ir *ImageEngine) Tag(ctx context.Context, nameOrId string, tags []string, func (ir *ImageEngine) Untag(ctx context.Context, nameOrId string, tags []string, options entities.ImageUntagOptions) error { // Remove all tags if none are provided if len(tags) == 0 { - newImage, err := images.GetImage(ir.ClientCxt, nameOrId, &bindings.PFalse) + newImage, err := images.GetImage(ir.ClientCxt, nameOrId, bindings.PFalse) if err != nil { return err } @@ -190,7 +202,6 @@ func (ir *ImageEngine) Save(ctx context.Context, nameOrId string, tags []string, f *os.File err error ) - switch options.Format { case "oci-dir", "docker-dir": f, err = ioutil.TempFile("", "podman_save") @@ -258,9 +269,13 @@ func (ir *ImageEngine) Build(ctx context.Context, containerFiles []string, opts } func (ir *ImageEngine) Tree(ctx context.Context, nameOrId string, opts entities.ImageTreeOptions) (*entities.ImageTreeReport, error) { - return nil, errors.New("not implemented yet") + return images.Tree(ir.ClientCxt, nameOrId, &opts.WhatRequires) } // Shutdown Libpod engine func (ir *ImageEngine) Shutdown(_ context.Context) { } + +func (ir *ImageEngine) Sign(ctx context.Context, names []string, options entities.SignOptions) (*entities.SignReport, error) { + return nil, errors.New("not implemented yet") +} diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go index 9c1f5349a..beac378fe 100644 --- a/pkg/domain/infra/tunnel/manifest.go +++ b/pkg/domain/infra/tunnel/manifest.go @@ -57,46 +57,21 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAd } manifestAddOpts.Annotation = annotations } - listID, err := manifests.Add(ctx, opts.Images[1], manifestAddOpts) + listID, err := manifests.Add(ir.ClientCxt, opts.Images[1], manifestAddOpts) if err != nil { return listID, errors.Wrapf(err, "error adding to manifest list %s", opts.Images[1]) } return listID, nil } -// FIXME There is no endpoint for annotate and therefor this code is currently invalid // ManifestAnnotate updates an entry of the manifest list func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opts entities.ManifestAnnotateOptions) (string, error) { return "", errors.New("not implemented") - // manifestAnnotateOpts := image.ManifestAnnotateOpts{ - // Arch: opts.Arch, - // Features: opts.Features, - // OS: opts.OS, - // OSFeatures: opts.OSFeatures, - // OSVersion: opts.OSVersion, - // Variant: opts.Variant, - // } - // if len(opts.Annotation) > 0 { - // annotations := make(map[string]string) - // for _, annotationSpec := range opts.Annotation { - // spec := strings.SplitN(annotationSpec, "=", 2) - // if len(spec) != 2 { - // return "", errors.Errorf("no value given for annotation %q", spec[0]) - // } - // annotations[spec[0]] = spec[1] - // } - // manifestAnnotateOpts.Annotation = annotations - // } - // updatedListID, err := manifests.Annotate(ctx, names[0], names[1], manifestAnnotateOpts) - // if err != nil { - // return updatedListID, errors.Wrapf(err, "error annotating %s of manifest list %s", names[1], names[0]) - // } - // return fmt.Sprintf("%s :%s", updatedListID, names[1]), nil } // ManifestRemove removes the digest from manifest list func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (string, error) { - updatedListID, err := manifests.Remove(ctx, names[0], names[1]) + updatedListID, err := manifests.Remove(ir.ClientCxt, names[0], names[1]) if err != nil { return updatedListID, errors.Wrapf(err, "error removing from manifest %s", names[0]) } @@ -105,6 +80,6 @@ func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (stri // ManifestPush pushes a manifest list or image index to the destination func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts entities.ManifestPushOptions) error { - _, err := manifests.Push(ctx, names[0], &names[1], &opts.All) + _, err := manifests.Push(ir.ClientCxt, names[0], &names[1], &opts.All) return err } diff --git a/pkg/domain/infra/tunnel/network.go b/pkg/domain/infra/tunnel/network.go index 4ff72dcfc..7725d8257 100644 --- a/pkg/domain/infra/tunnel/network.go +++ b/pkg/domain/infra/tunnel/network.go @@ -2,22 +2,39 @@ package tunnel import ( "context" - "errors" + "github.com/containers/libpod/pkg/bindings/network" "github.com/containers/libpod/pkg/domain/entities" ) func (ic *ContainerEngine) NetworkList(ctx context.Context, options entities.NetworkListOptions) ([]*entities.NetworkListReport, error) { - return nil, errors.New("not implemented") + return network.List(ic.ClientCxt) } func (ic *ContainerEngine) NetworkInspect(ctx context.Context, namesOrIds []string, options entities.NetworkInspectOptions) ([]entities.NetworkInspectReport, error) { - return nil, errors.New("not implemented") + var reports []entities.NetworkInspectReport + for _, name := range namesOrIds { + report, err := network.Inspect(ic.ClientCxt, name) + if err != nil { + return nil, err + } + reports = append(reports, report...) + } + return reports, nil } + func (ic *ContainerEngine) NetworkRm(ctx context.Context, namesOrIds []string, options entities.NetworkRmOptions) ([]*entities.NetworkRmReport, error) { - return nil, errors.New("not implemented") + var reports []*entities.NetworkRmReport + for _, name := range namesOrIds { + report, err := network.Remove(ic.ClientCxt, name, &options.Force) + if err != nil { + report[0].Err = err + } + reports = append(reports, report...) + } + return reports, nil } func (ic *ContainerEngine) NetworkCreate(ctx context.Context, name string, options entities.NetworkCreateOptions) (*entities.NetworkCreateReport, error) { - return nil, errors.New("not implemented") + return network.Create(ic.ClientCxt, options, &name) } diff --git a/pkg/domain/infra/tunnel/runtime.go b/pkg/domain/infra/tunnel/runtime.go index c111f99e9..357e2c390 100644 --- a/pkg/domain/infra/tunnel/runtime.go +++ b/pkg/domain/infra/tunnel/runtime.go @@ -13,3 +13,8 @@ type ImageEngine struct { type ContainerEngine struct { ClientCxt context.Context } + +// Container-related runtime using an ssh-tunnel to utilize Podman service +type SystemEngine struct { + ClientCxt context.Context +} diff --git a/pkg/domain/infra/tunnel/system.go b/pkg/domain/infra/tunnel/system.go index dafada805..829af31f6 100644 --- a/pkg/domain/infra/tunnel/system.go +++ b/pkg/domain/infra/tunnel/system.go @@ -27,8 +27,13 @@ func (ic *ContainerEngine) SystemPrune(ctx context.Context, options entities.Sys return system.Prune(ic.ClientCxt, &options.All, &options.Volume) } +// Reset removes all storage +func (ic *SystemEngine) Reset(ctx context.Context) error { + return system.Reset(ic.ClientCxt) +} + func (ic *ContainerEngine) SystemDf(ctx context.Context, options entities.SystemDfOptions) (*entities.SystemDfReport, error) { - panic(errors.New("system df is not supported on remote clients")) + return system.DiskUsage(ic.ClientCxt) } func (ic *ContainerEngine) Unshare(ctx context.Context, args []string) error { |