diff options
Diffstat (limited to 'pkg')
26 files changed, 791 insertions, 100 deletions
diff --git a/pkg/bindings/manifests/manifests.go b/pkg/bindings/manifests/manifests.go index b85169410..3e0ef0325 100644 --- a/pkg/bindings/manifests/manifests.go +++ b/pkg/bindings/manifests/manifests.go @@ -125,23 +125,24 @@ func Push(ctx context.Context, name string, destination *string, all *bool) (str return idr.ID, response.Process(&idr) } +// There is NO annotate endpoint. this binding could never work // Annotate updates the image configuration of a given manifest list -func Annotate(ctx context.Context, name, digest string, options image.ManifestAnnotateOpts) (string, error) { - var idr handlers.IDResponse - conn, err := bindings.GetClient(ctx) - if err != nil { - return "", err - } - params := url.Values{} - params.Set("digest", digest) - optionsString, err := jsoniter.MarshalToString(options) - if err != nil { - return "", err - } - stringReader := strings.NewReader(optionsString) - response, err := conn.DoRequest(stringReader, http.MethodPost, "/manifests/%s/annotate", params, name) - if err != nil { - return "", err - } - return idr.ID, response.Process(&idr) -} +//func Annotate(ctx context.Context, name, digest string, options image.ManifestAnnotateOpts) (string, error) { +// var idr handlers.IDResponse +// conn, err := bindings.GetClient(ctx) +// if err != nil { +// return "", err +// } +// params := url.Values{} +// params.Set("digest", digest) +// optionsString, err := jsoniter.MarshalToString(options) +// if err != nil { +// return "", err +// } +// stringReader := strings.NewReader(optionsString) +// response, err := conn.DoRequest(stringReader, http.MethodPost, "/manifests/%s/annotate", params, name) +// if err != nil { +// return "", err +// } +// return idr.ID, response.Process(&idr) +//} diff --git a/pkg/bindings/test/containers_test.go b/pkg/bindings/test/containers_test.go index c79d89b73..f40d8ce46 100644 --- a/pkg/bindings/test/containers_test.go +++ b/pkg/bindings/test/containers_test.go @@ -531,7 +531,7 @@ var _ = Describe("Podman containers ", func() { Expect(err).ToNot(BeNil()) }) - It("podman prune stoped containers", func() { + It("podman prune stopped containers", func() { // Start and stop a container to enter in exited state. var name = "top" _, err := bt.RunTopContainer(&name, &bindings.PFalse, nil) @@ -546,7 +546,7 @@ var _ = Describe("Podman containers ", func() { Expect(len(pruneResponse.ID)).To(Equal(1)) }) - It("podman prune stoped containers with filters", func() { + It("podman prune stopped containers with filters", func() { // Start and stop a container to enter in exited state. var name = "top" _, err := bt.RunTopContainer(&name, &bindings.PFalse, nil) diff --git a/pkg/bindings/test/manifests_test.go b/pkg/bindings/test/manifests_test.go index ddb75865c..71d626b7b 100644 --- a/pkg/bindings/test/manifests_test.go +++ b/pkg/bindings/test/manifests_test.go @@ -118,25 +118,27 @@ var _ = Describe("Podman containers ", func() { Expect(len(data.Manifests)).To(BeZero()) }) - It("annotate manifest", func() { - id, err := manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{}, nil) - Expect(err).To(BeNil()) - opts := image.ManifestAddOpts{Images: []string{"docker.io/library/alpine:latest"}} - - _, err = manifests.Add(bt.conn, id, opts) - Expect(err).To(BeNil()) - data, err := manifests.Inspect(bt.conn, id) - Expect(err).To(BeNil()) - Expect(len(data.Manifests)).To(BeNumerically("==", 1)) - digest := data.Manifests[0].Digest.String() - annoOpts := image.ManifestAnnotateOpts{OS: "foo"} - _, err = manifests.Annotate(bt.conn, id, digest, annoOpts) - Expect(err).To(BeNil()) - list, err := manifests.Inspect(bt.conn, id) - Expect(err).To(BeNil()) - Expect(len(list.Manifests)).To(BeNumerically("==", 1)) - Expect(list.Manifests[0].Platform.OS).To(Equal("foo")) - }) + // There is NO annotate endpoint, this could never work.:w + + //It("annotate manifest", func() { + // id, err := manifests.Create(bt.conn, []string{"quay.io/libpod/foobar:latest"}, []string{}, nil) + // Expect(err).To(BeNil()) + // opts := image.ManifestAddOpts{Images: []string{"docker.io/library/alpine:latest"}} + // + // _, err = manifests.Add(bt.conn, id, opts) + // Expect(err).To(BeNil()) + // data, err := manifests.Inspect(bt.conn, id) + // Expect(err).To(BeNil()) + // Expect(len(data.Manifests)).To(BeNumerically("==", 1)) + // digest := data.Manifests[0].Digest.String() + // annoOpts := image.ManifestAnnotateOpts{OS: "foo"} + // _, err = manifests.Annotate(bt.conn, id, digest, annoOpts) + // Expect(err).To(BeNil()) + // list, err := manifests.Inspect(bt.conn, id) + // Expect(err).To(BeNil()) + // Expect(len(list.Manifests)).To(BeNumerically("==", 1)) + // Expect(list.Manifests[0].Platform.OS).To(Equal("foo")) + //}) It("push manifest", func() { Skip("TODO") diff --git a/pkg/bindings/test/pods_test.go b/pkg/bindings/test/pods_test.go index 8a0b9c7a6..49bbfa246 100644 --- a/pkg/bindings/test/pods_test.go +++ b/pkg/bindings/test/pods_test.go @@ -57,6 +57,11 @@ var _ = Describe("Podman pods", func() { podSummary, err := pods.List(bt.conn, nil) Expect(err).To(BeNil()) Expect(len(podSummary)).To(Equal(1)) + + // Start the pod + _, err = pods.Start(bt.conn, newpod) + Expect(err).To(BeNil()) + // Adding an alpine container to the existing pod _, err = bt.RunTopContainer(nil, &bindings.PTrue, &newpod) Expect(err).To(BeNil()) @@ -83,6 +88,11 @@ var _ = Describe("Podman pods", func() { It("List pods with filters", func() { newpod2 := "newpod2" bt.Podcreate(&newpod2) + + // Start the pod + _, err = pods.Start(bt.conn, newpod) + Expect(err).To(BeNil()) + _, err = bt.RunTopContainer(nil, &bindings.PTrue, &newpod) Expect(err).To(BeNil()) diff --git a/pkg/bindings/test/system_test.go b/pkg/bindings/test/system_test.go index 87e6d56dc..62ea32377 100644 --- a/pkg/bindings/test/system_test.go +++ b/pkg/bindings/test/system_test.go @@ -3,7 +3,6 @@ package test_bindings import ( "time" - "github.com/containers/libpod/pkg/api/handlers" "github.com/containers/libpod/pkg/bindings" "github.com/containers/libpod/pkg/bindings/containers" "github.com/containers/libpod/pkg/bindings/pods" @@ -39,8 +38,8 @@ var _ = Describe("Podman system", func() { }) It("podman events", func() { - eChan := make(chan handlers.Event, 1) - var messages []handlers.Event + eChan := make(chan entities.Event, 1) + var messages []entities.Event cancelChan := make(chan bool, 1) go func() { for e := range eChan { diff --git a/pkg/domain/entities/auto-update.go b/pkg/domain/entities/auto-update.go new file mode 100644 index 000000000..aef8fc46b --- /dev/null +++ b/pkg/domain/entities/auto-update.go @@ -0,0 +1,7 @@ +package entities + +// AutoUpdateReport contains the results from running auto-update. +type AutoUpdateReport struct { + // Units - the restarted systemd units during auto-update. + Units []string +} diff --git a/pkg/domain/entities/containers.go b/pkg/domain/entities/containers.go index 071eff2fc..e5330e1ab 100644 --- a/pkg/domain/entities/containers.go +++ b/pkg/domain/entities/containers.go @@ -6,11 +6,49 @@ import ( "os" "time" + "github.com/containers/image/v5/types" "github.com/containers/libpod/libpod/define" "github.com/containers/libpod/pkg/specgen" "github.com/cri-o/ocicni/pkg/ocicni" ) +// ContainerRunlabelOptions are the options to execute container-runlabel. +type ContainerRunlabelOptions struct { + // Authfile - path to an authentication file. + Authfile string + // CertDir - path to a directory containing TLS certifications and + // keys. + CertDir string + // Credentials - `user:password` to use when pulling an image. + Credentials string + // Display - do not execute but print the command. + Display bool + // Replace - replace an existing container with a new one from the + // image. + Replace bool + // Name - use this name when executing the runlabel container. + Name string + // Optional1 - fist optional parameter for install. + Optional1 string + // Optional2 - second optional parameter for install. + Optional2 string + // Optional3 - third optional parameter for install. + Optional3 string + // Pull - pull the specified image if it's not in the local storage. + Pull bool + // Quiet - suppress output when pulling images. + Quiet bool + // SignaturePolicy - path to a signature-policy file. + SignaturePolicy string + // SkipTLSVerify - skip HTTPS and certificate verifications when + // contacting registries. + SkipTLSVerify types.OptionalBool +} + +// ContainerRunlabelReport contains the results from executing container-runlabel. +type ContainerRunlabelReport struct { +} + type WaitOptions struct { Condition define.ContainerStatus Interval time.Duration diff --git a/pkg/domain/entities/engine_container.go b/pkg/domain/entities/engine_container.go index 1bfac4514..7c93e6802 100644 --- a/pkg/domain/entities/engine_container.go +++ b/pkg/domain/entities/engine_container.go @@ -10,6 +10,7 @@ import ( ) type ContainerEngine interface { + AutoUpdate(ctx context.Context) (*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) @@ -34,6 +35,7 @@ type ContainerEngine interface { ContainerRestore(ctx context.Context, namesOrIds []string, options RestoreOptions) ([]*RestoreReport, error) ContainerRm(ctx context.Context, namesOrIds []string, options RmOptions) ([]*RmReport, error) ContainerRun(ctx context.Context, opts ContainerRunOptions) (*ContainerRunReport, error) + ContainerRunlabel(ctx context.Context, label string, image string, args []string, opts ContainerRunlabelOptions) error ContainerStart(ctx context.Context, namesOrIds []string, options ContainerStartOptions) ([]*ContainerStartReport, error) ContainerStats(ctx context.Context, namesOrIds []string, options ContainerStatsOptions) error ContainerStop(ctx context.Context, namesOrIds []string, options StopOptions) ([]*StopReport, error) diff --git a/pkg/domain/entities/engine_image.go b/pkg/domain/entities/engine_image.go index c46ba815a..ffa71abd6 100644 --- a/pkg/domain/entities/engine_image.go +++ b/pkg/domain/entities/engine_image.go @@ -22,6 +22,8 @@ type ImageEngine interface { Remove(ctx context.Context, images []string, opts ImageRemoveOptions) (*ImageRemoveReport, []error) Save(ctx context.Context, nameOrId string, tags []string, options ImageSaveOptions) error Search(ctx context.Context, term string, opts ImageSearchOptions) ([]ImageSearchReport, error) + SetTrust(ctx context.Context, args []string, options SetTrustOptions) error + ShowTrust(ctx context.Context, args []string, options ShowTrustOptions) (*ShowTrustReport, error) Shutdown(ctx context.Context) Tag(ctx context.Context, nameOrId string, tags []string, options ImageTagOptions) error Tree(ctx context.Context, nameOrId string, options ImageTreeOptions) (*ImageTreeReport, error) @@ -30,4 +32,6 @@ type ImageEngine interface { ManifestInspect(ctx context.Context, name string) ([]byte, error) ManifestAdd(ctx context.Context, opts ManifestAddOptions) (string, error) 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 } diff --git a/pkg/domain/entities/images.go b/pkg/domain/entities/images.go index 74f27e25f..e116a90b9 100644 --- a/pkg/domain/entities/images.go +++ b/pkg/domain/entities/images.go @@ -7,6 +7,7 @@ import ( "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/types" "github.com/containers/libpod/pkg/inspect" + "github.com/containers/libpod/pkg/trust" docker "github.com/docker/docker/api/types" "github.com/docker/docker/api/types/container" "github.com/opencontainers/go-digest" @@ -285,3 +286,26 @@ type ImageTreeOptions struct { type ImageTreeReport struct { Tree string // TODO: Refactor move presentation work out of server } + +// ShowTrustOptions are the cli options for showing trust +type ShowTrustOptions struct { + JSON bool + PolicyPath string + Raw bool + RegistryPath string +} + +// ShowTrustReport describes the results of show trust +type ShowTrustReport struct { + Raw []byte + SystemRegistriesDirPath string + JSONOutput []byte + Policies []*trust.TrustPolicy +} + +// SetTrustOptions describes the CLI options for setting trust +type SetTrustOptions struct { + PolicyPath string + PubKeysFile []string + Type string +} diff --git a/pkg/domain/entities/manifest.go b/pkg/domain/entities/manifest.go index d92b1dc9b..273052bb9 100644 --- a/pkg/domain/entities/manifest.go +++ b/pkg/domain/entities/manifest.go @@ -24,3 +24,8 @@ type ManifestAnnotateOptions struct { OSVersion string `json:"os_version" schema:"os_version"` Variant string `json:"variant" schema:"variant"` } + +type ManifestPushOptions struct { + Purge, Quiet, All, TlsVerify, RemoveSignatures bool + Authfile, CertDir, Creds, DigestFile, Format, SignBy string +} diff --git a/pkg/domain/infra/abi/auto-update.go b/pkg/domain/infra/abi/auto-update.go new file mode 100644 index 000000000..aa20664b4 --- /dev/null +++ b/pkg/domain/infra/abi/auto-update.go @@ -0,0 +1,13 @@ +package abi + +import ( + "context" + + "github.com/containers/libpod/pkg/autoupdate" + "github.com/containers/libpod/pkg/domain/entities" +) + +func (ic *ContainerEngine) AutoUpdate(ctx context.Context) (*entities.AutoUpdateReport, []error) { + units, failures := autoupdate.AutoUpdate(ic.Libpod) + return &entities.AutoUpdateReport{Units: units}, failures +} diff --git a/pkg/domain/infra/abi/containers_runlabel.go b/pkg/domain/infra/abi/containers_runlabel.go new file mode 100644 index 000000000..41f4444d5 --- /dev/null +++ b/pkg/domain/infra/abi/containers_runlabel.go @@ -0,0 +1,280 @@ +package abi + +import ( + "context" + "fmt" + "os" + "path/filepath" + "strings" + + "github.com/containers/image/v5/types" + "github.com/containers/libpod/libpod/define" + "github.com/containers/libpod/libpod/image" + "github.com/containers/libpod/pkg/domain/entities" + envLib "github.com/containers/libpod/pkg/env" + "github.com/containers/libpod/pkg/util" + "github.com/containers/libpod/utils" + "github.com/google/shlex" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string, imageRef string, args []string, options entities.ContainerRunlabelOptions) error { + // First, get the image and pull it if needed. + img, err := ic.runlabelImage(ctx, label, imageRef, options) + if err != nil { + return err + } + // Extract the runlabel from the image. + runlabel, err := img.GetLabel(ctx, label) + if err != nil { + return err + } + + cmd, env, err := generateRunlabelCommand(runlabel, img, args, options) + if err != nil { + return err + } + + stdErr := os.Stderr + stdOut := os.Stdout + stdIn := os.Stdin + if options.Quiet { + stdErr = nil + stdOut = nil + stdIn = nil + } + + // If container already exists && --replace given -- Nuke it + if options.Replace { + for i, entry := range cmd { + if entry == "--name" { + name := cmd[i+1] + ctr, err := ic.Libpod.LookupContainer(name) + if err != nil { + if errors.Cause(err) != define.ErrNoSuchCtr { + logrus.Debugf("Error occurred searching for container %s: %s", name, err.Error()) + return err + } + } else { + logrus.Debugf("Runlabel --replace option given. Container %s will be deleted. The new container will be named %s", ctr.ID(), name) + if err := ic.Libpod.RemoveContainer(ctx, ctr, true, false); err != nil { + return err + } + } + break + } + } + } + + return utils.ExecCmdWithStdStreams(stdIn, stdOut, stdErr, env, cmd[0], cmd[1:]...) +} + +// runlabelImage returns an image based on the specified image AND options. +func (ic *ContainerEngine) runlabelImage(ctx context.Context, label string, imageRef string, options entities.ContainerRunlabelOptions) (*image.Image, error) { + // First, look up the image locally. If we get an error and requested + // to pull, fallthrough and pull it. + img, err := ic.Libpod.ImageRuntime().NewFromLocal(imageRef) + switch { + case err == nil: + return img, nil + case !options.Pull: + return nil, err + default: + // Fallthrough and pull! + } + + // Parse credentials if specified. + var credentials *types.DockerAuthConfig + if options.Credentials != "" { + credentials, err = util.ParseRegistryCreds(options.Credentials) + if err != nil { + return nil, err + } + } + + // Suppress pull progress bars if requested. + pullOutput := os.Stdout + if options.Quiet { + pullOutput = nil // c/image/copy takes care of the rest + } + + // Pull the image. + dockerRegistryOptions := image.DockerRegistryOptions{ + DockerCertPath: options.CertDir, + DockerInsecureSkipTLSVerify: options.SkipTLSVerify, + DockerRegistryCreds: credentials, + } + + return ic.Libpod.ImageRuntime().New(ctx, imageRef, options.SignaturePolicy, options.Authfile, pullOutput, &dockerRegistryOptions, image.SigningOptions{}, &label, util.PullImageMissing) +} + +// generateRunlabelCommand generates the to-be-executed command as a string +// slice along with a base environment. +func generateRunlabelCommand(runlabel string, img *image.Image, args []string, options entities.ContainerRunlabelOptions) ([]string, []string, error) { + var ( + err error + name, imageName string + globalOpts string + cmd, env []string + ) + + // TODO: How do we get global opts as done in v1? + + // Extract the imageName (or ID). + imgNames := img.Names() + if len(imgNames) == 0 { + imageName = img.ID() + } else { + imageName = imgNames[0] + } + + // Use the user-specified name or extract one from the image. + if options.Name != "" { + name = options.Name + } else { + name, err = image.GetImageBaseName(imageName) + if err != nil { + return nil, nil, err + } + } + + // Append the user-specified arguments to the runlabel (command). + if len(args) > 0 { + runlabel = fmt.Sprintf("%s %s", runlabel, strings.Join(args, " ")) + } + + cmd, err = generateCommand(runlabel, imageName, name, globalOpts) + if err != nil { + return nil, nil, err + } + + env = generateRunEnvironment(name, imageName, options) + env = append(env, "PODMAN_RUNLABEL_NESTED=1") + envmap, err := envLib.ParseSlice(env) + if err != nil { + return nil, nil, err + } + + envmapper := func(k string) string { + switch k { + case "OPT1": + return envmap["OPT1"] + case "OPT2": + return envmap["OPT2"] + case "OPT3": + return envmap["OPT3"] + case "PWD": + // I would prefer to use os.getenv but it appears PWD is not in the os env list. + d, err := os.Getwd() + if err != nil { + logrus.Error("unable to determine current working directory") + return "" + } + return d + } + return "" + } + newS := os.Expand(strings.Join(cmd, " "), envmapper) + cmd, err = shlex.Split(newS) + if err != nil { + return nil, nil, err + } + return cmd, env, nil +} + +// generateCommand takes a label (string) and converts it to an executable command +func generateCommand(command, imageName, name, globalOpts string) ([]string, error) { + var ( + newCommand []string + ) + if name == "" { + name = imageName + } + + cmd, err := shlex.Split(command) + if err != nil { + return nil, err + } + + prog, err := substituteCommand(cmd[0]) + if err != nil { + return nil, err + } + newCommand = append(newCommand, prog) + + for _, arg := range cmd[1:] { + var newArg string + switch arg { + case "IMAGE": + newArg = imageName + case "$IMAGE": + newArg = imageName + case "IMAGE=IMAGE": + newArg = fmt.Sprintf("IMAGE=%s", imageName) + case "IMAGE=$IMAGE": + newArg = fmt.Sprintf("IMAGE=%s", imageName) + case "NAME": + newArg = name + case "NAME=NAME": + newArg = fmt.Sprintf("NAME=%s", name) + case "NAME=$NAME": + newArg = fmt.Sprintf("NAME=%s", name) + case "$NAME": + newArg = name + case "$GLOBAL_OPTS": + newArg = globalOpts + default: + newArg = arg + } + newCommand = append(newCommand, newArg) + } + return newCommand, nil +} + +// GenerateRunEnvironment merges the current environment variables with optional +// environment variables provided by the user +func generateRunEnvironment(name, imageName string, options entities.ContainerRunlabelOptions) []string { + newEnv := os.Environ() + if options.Optional1 != "" { + newEnv = append(newEnv, fmt.Sprintf("OPT1=%s", options.Optional1)) + } + if options.Optional2 != "" { + newEnv = append(newEnv, fmt.Sprintf("OPT2=%s", options.Optional2)) + } + if options.Optional3 != "" { + newEnv = append(newEnv, fmt.Sprintf("OPT3=%s", options.Optional3)) + } + return newEnv +} + +func substituteCommand(cmd string) (string, error) { + var ( + newCommand string + ) + + // Replace cmd with "/proc/self/exe" if "podman" or "docker" is being + // used. If "/usr/bin/docker" is provided, we also sub in podman. + // Otherwise, leave the command unchanged. + if cmd == "podman" || filepath.Base(cmd) == "docker" { + newCommand = "/proc/self/exe" + } else { + newCommand = cmd + } + + // If cmd is an absolute or relative path, check if the file exists. + // Throw an error if it doesn't exist. + if strings.Contains(newCommand, "/") || strings.HasPrefix(newCommand, ".") { + res, err := filepath.Abs(newCommand) + if err != nil { + return "", err + } + if _, err := os.Stat(res); !os.IsNotExist(err) { + return res, nil + } else if err != nil { + return "", err + } + } + + return newCommand, nil +} diff --git a/pkg/domain/infra/abi/manifest.go b/pkg/domain/infra/abi/manifest.go index 812507f0a..fca34dda2 100644 --- a/pkg/domain/infra/abi/manifest.go +++ b/pkg/domain/infra/abi/manifest.go @@ -6,15 +6,21 @@ import ( "context" "encoding/json" "fmt" + "io/ioutil" + "os" "strings" + "github.com/containers/buildah/manifests" buildahUtil "github.com/containers/buildah/util" + cp "github.com/containers/image/v5/copy" "github.com/containers/image/v5/docker" + "github.com/containers/image/v5/manifest" "github.com/containers/image/v5/transports/alltransports" libpodImage "github.com/containers/libpod/libpod/image" "github.com/containers/libpod/pkg/domain/entities" "github.com/containers/libpod/pkg/util" "github.com/opencontainers/go-digest" + imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1" "github.com/pkg/errors" ) @@ -137,3 +143,68 @@ func (ir *ImageEngine) ManifestAnnotate(ctx context.Context, names []string, opt } return "", err } + +// ManifestRemove removes specified digest from the specified manifest list +func (ir *ImageEngine) ManifestRemove(ctx context.Context, names []string) (string, error) { + instanceDigest, err := digest.Parse(names[1]) + if err != nil { + return "", errors.Errorf(`invalid image digest "%s": %v`, names[1], err) + } + listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0]) + if err != nil { + return "", errors.Wrapf(err, "error retriving local image from image name %s", names[0]) + } + updatedListID, err := listImage.RemoveManifest(instanceDigest) + if err == nil { + return fmt.Sprintf("%s :%s\n", updatedListID, instanceDigest.String()), nil + } + return "", err +} + +// ManifestPush pushes a manifest list or image index to the destination +func (ir *ImageEngine) ManifestPush(ctx context.Context, names []string, opts entities.ManifestPushOptions) error { + listImage, err := ir.Libpod.ImageRuntime().NewFromLocal(names[0]) + if err != nil { + return errors.Wrapf(err, "error retriving local image from image name %s", names[0]) + } + dest, err := alltransports.ParseImageName(names[1]) + if err != nil { + return err + } + var manifestType string + if opts.Format != "" { + switch opts.Format { + case "oci": + manifestType = imgspecv1.MediaTypeImageManifest + case "v2s2", "docker": + manifestType = manifest.DockerV2Schema2MediaType + default: + return errors.Errorf("unknown format %q. Choose on of the supported formats: 'oci' or 'v2s2'", opts.Format) + } + } + options := manifests.PushOptions{ + Store: ir.Libpod.GetStore(), + SystemContext: ir.Libpod.SystemContext(), + ImageListSelection: cp.CopySpecificImages, + Instances: nil, + RemoveSignatures: opts.RemoveSignatures, + SignBy: opts.SignBy, + ManifestType: manifestType, + } + if opts.All { + options.ImageListSelection = cp.CopyAllImages + } + if !opts.Quiet { + options.ReportWriter = os.Stderr + } + digest, err := listImage.PushManifest(dest, options) + if err == nil && opts.Purge { + _, err = ir.Libpod.GetStore().DeleteImage(listImage.ID(), true) + } + if opts.DigestFile != "" { + if err = ioutil.WriteFile(opts.DigestFile, []byte(digest.String()), 0644); err != nil { + return buildahUtil.GetFailureCause(err, errors.Wrapf(err, "failed to write digest to file %q", opts.DigestFile)) + } + } + return err +} diff --git a/pkg/domain/infra/abi/system.go b/pkg/domain/infra/abi/system.go index 692fcfa0f..24c62465f 100644 --- a/pkg/domain/infra/abi/system.go +++ b/pkg/domain/infra/abi/system.go @@ -86,6 +86,7 @@ func (ic *ContainerEngine) SetupRootless(_ context.Context, cmd *cobra.Command) } } } + return nil } pausePidPath, err := util.GetRootlessPauseProcessPidPath() diff --git a/pkg/domain/infra/abi/trust.go b/pkg/domain/infra/abi/trust.go new file mode 100644 index 000000000..5b89c91d9 --- /dev/null +++ b/pkg/domain/infra/abi/trust.go @@ -0,0 +1,171 @@ +package abi + +import ( + "context" + "encoding/json" + "io/ioutil" + "os" + "strings" + + "github.com/containers/libpod/pkg/domain/entities" + "github.com/containers/libpod/pkg/trust" + "github.com/pkg/errors" + "github.com/sirupsen/logrus" +) + +func (ir *ImageEngine) ShowTrust(ctx context.Context, args []string, options entities.ShowTrustOptions) (*entities.ShowTrustReport, error) { + var ( + err error + report entities.ShowTrustReport + ) + policyPath := trust.DefaultPolicyPath(ir.Libpod.SystemContext()) + if len(options.PolicyPath) > 0 { + policyPath = options.PolicyPath + } + report.Raw, err = ioutil.ReadFile(policyPath) + if err != nil { + return nil, errors.Wrapf(err, "unable to read %s", policyPath) + } + if options.Raw { + return &report, nil + } + report.SystemRegistriesDirPath = trust.RegistriesDirPath(ir.Libpod.SystemContext()) + if len(options.RegistryPath) > 0 { + report.SystemRegistriesDirPath = options.RegistryPath + } + policyContentStruct, err := trust.GetPolicy(policyPath) + if err != nil { + return nil, errors.Wrapf(err, "could not read trust policies") + } + report.Policies, err = getPolicyShowOutput(policyContentStruct, report.SystemRegistriesDirPath) + if err != nil { + return nil, errors.Wrapf(err, "could not show trust policies") + } + return &report, nil +} + +func (ir *ImageEngine) SetTrust(ctx context.Context, args []string, options entities.SetTrustOptions) error { + var ( + policyContentStruct trust.PolicyContent + newReposContent []trust.RepoContent + ) + trustType := options.Type + if trustType == "accept" { + trustType = "insecureAcceptAnything" + } + + pubkeysfile := options.PubKeysFile + if len(pubkeysfile) == 0 && trustType == "signedBy" { + return errors.Errorf("At least one public key must be defined for type 'signedBy'") + } + + policyPath := trust.DefaultPolicyPath(ir.Libpod.SystemContext()) + if len(options.PolicyPath) > 0 { + policyPath = options.PolicyPath + } + _, err := os.Stat(policyPath) + if !os.IsNotExist(err) { + policyContent, err := ioutil.ReadFile(policyPath) + if err != nil { + return errors.Wrapf(err, "unable to read %s", policyPath) + } + if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil { + return errors.Errorf("could not read trust policies") + } + } + if len(pubkeysfile) != 0 { + for _, filepath := range pubkeysfile { + newReposContent = append(newReposContent, trust.RepoContent{Type: trustType, KeyType: "GPGKeys", KeyPath: filepath}) + } + } else { + newReposContent = append(newReposContent, trust.RepoContent{Type: trustType}) + } + if args[0] == "default" { + policyContentStruct.Default = newReposContent + } else { + if len(policyContentStruct.Default) == 0 { + return errors.Errorf("Default trust policy must be set.") + } + registryExists := false + for transport, transportval := range policyContentStruct.Transports { + _, registryExists = transportval[args[0]] + if registryExists { + policyContentStruct.Transports[transport][args[0]] = newReposContent + break + } + } + if !registryExists { + if policyContentStruct.Transports == nil { + policyContentStruct.Transports = make(map[string]trust.RepoMap) + } + if policyContentStruct.Transports["docker"] == nil { + policyContentStruct.Transports["docker"] = make(map[string][]trust.RepoContent) + } + policyContentStruct.Transports["docker"][args[0]] = append(policyContentStruct.Transports["docker"][args[0]], newReposContent...) + } + } + + data, err := json.MarshalIndent(policyContentStruct, "", " ") + if err != nil { + return errors.Wrapf(err, "error setting trust policy") + } + return ioutil.WriteFile(policyPath, data, 0644) +} + +func getPolicyShowOutput(policyContentStruct trust.PolicyContent, systemRegistriesDirPath string) ([]*trust.TrustPolicy, error) { + var output []*trust.TrustPolicy + + registryConfigs, err := trust.LoadAndMergeConfig(systemRegistriesDirPath) + if err != nil { + return nil, err + } + + if len(policyContentStruct.Default) > 0 { + defaultPolicyStruct := trust.TrustPolicy{ + Name: "* (default)", + RepoName: "default", + Type: trustTypeDescription(policyContentStruct.Default[0].Type), + } + output = append(output, &defaultPolicyStruct) + } + for _, transval := range policyContentStruct.Transports { + for repo, repoval := range transval { + tempTrustShowOutput := trust.TrustPolicy{ + Name: repo, + RepoName: repo, + Type: repoval[0].Type, + } + // TODO - keyarr is not used and I don't know its intent; commenting out for now for someone to fix later + //keyarr := []string{} + uids := []string{} + for _, repoele := range repoval { + if len(repoele.KeyPath) > 0 { + //keyarr = append(keyarr, repoele.KeyPath) + uids = append(uids, trust.GetGPGIdFromKeyPath(repoele.KeyPath)...) + } + if len(repoele.KeyData) > 0 { + //keyarr = append(keyarr, string(repoele.KeyData)) + uids = append(uids, trust.GetGPGIdFromKeyData(repoele.KeyData)...) + } + } + tempTrustShowOutput.GPGId = strings.Join(uids, ", ") + + registryNamespace := trust.HaveMatchRegistry(repo, registryConfigs) + if registryNamespace != nil { + tempTrustShowOutput.SignatureStore = registryNamespace.SigStore + } + output = append(output, &tempTrustShowOutput) + } + } + return output, nil +} + +var typeDescription = map[string]string{"insecureAcceptAnything": "accept", "signedBy": "signed", "reject": "reject"} + +func trustTypeDescription(trustType string) string { + trustDescription, exist := typeDescription[trustType] + if !exist { + logrus.Warnf("invalid trust type %s", trustType) + } + return trustDescription +} diff --git a/pkg/domain/infra/tunnel/auto-update.go b/pkg/domain/infra/tunnel/auto-update.go new file mode 100644 index 000000000..fac033050 --- /dev/null +++ b/pkg/domain/infra/tunnel/auto-update.go @@ -0,0 +1,12 @@ +package tunnel + +import ( + "context" + + "github.com/containers/libpod/pkg/domain/entities" + "github.com/pkg/errors" +) + +func (ic *ContainerEngine) AutoUpdate(ctx context.Context) (*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 227b660f7..49a3069d6 100644 --- a/pkg/domain/infra/tunnel/containers.go +++ b/pkg/domain/infra/tunnel/containers.go @@ -14,6 +14,10 @@ import ( "github.com/pkg/errors" ) +func (ic *ContainerEngine) ContainerRunlabel(ctx context.Context, label string, image string, args []string, options entities.ContainerRunlabelOptions) error { + return errors.New("not implemented") +} + func (ic *ContainerEngine) ContainerExists(ctx context.Context, nameOrId string) (*entities.BoolReport, error) { exists, err := containers.Exists(ic.ClientCxt, nameOrId) return &entities.BoolReport{Value: exists}, err diff --git a/pkg/domain/infra/tunnel/manifest.go b/pkg/domain/infra/tunnel/manifest.go index 3d3196019..9c1f5349a 100644 --- a/pkg/domain/infra/tunnel/manifest.go +++ b/pkg/domain/infra/tunnel/manifest.go @@ -64,30 +64,47 @@ func (ir *ImageEngine) ManifestAdd(ctx context.Context, opts entities.ManifestAd 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) { - 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) + 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]) if err != nil { - return updatedListID, errors.Wrapf(err, "error annotating %s of manifest list %s", names[1], names[0]) + return updatedListID, errors.Wrapf(err, "error removing from manifest %s", names[0]) } - return fmt.Sprintf("%s :%s", updatedListID, names[1]), nil + return fmt.Sprintf("%s :%s\n", updatedListID, names[1]), nil +} + +// 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) + return err } diff --git a/pkg/domain/infra/tunnel/trust.go b/pkg/domain/infra/tunnel/trust.go new file mode 100644 index 000000000..a976bfdc2 --- /dev/null +++ b/pkg/domain/infra/tunnel/trust.go @@ -0,0 +1,16 @@ +package tunnel + +import ( + "context" + "errors" + + "github.com/containers/libpod/pkg/domain/entities" +) + +func (ir *ImageEngine) ShowTrust(ctx context.Context, args []string, options entities.ShowTrustOptions) (*entities.ShowTrustReport, error) { + return nil, errors.New("not implemented") +} + +func (ir *ImageEngine) SetTrust(ctx context.Context, args []string, options entities.SetTrustOptions) error { + return errors.New("not implemented") +} diff --git a/pkg/specgen/generate/container.go b/pkg/specgen/generate/container.go index 92a2b4d35..a217125f4 100644 --- a/pkg/specgen/generate/container.go +++ b/pkg/specgen/generate/container.go @@ -9,6 +9,7 @@ import ( envLib "github.com/containers/libpod/pkg/env" "github.com/containers/libpod/pkg/signal" "github.com/containers/libpod/pkg/specgen" + "github.com/pkg/errors" "golang.org/x/sys/unix" ) @@ -41,31 +42,37 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat if err != nil { return err } - sig, err := signal.ParseSignalNameOrNumber(stopSignal) - if err != nil { - return err + if stopSignal != "" { + sig, err := signal.ParseSignalNameOrNumber(stopSignal) + if err != nil { + return err + } + s.StopSignal = &sig } - s.StopSignal = &sig + } + + rtc, err := r.GetConfig() + if err != nil { + return err + } + // Get Default Environment + defaultEnvs, err := envLib.ParseSlice(rtc.Containers.Env) + if err != nil { + return errors.Wrap(err, "Env fields in containers.conf failed to parse") } // Image envs from the image if they don't exist - // already - env, err := newImage.Env(ctx) + // already, overriding the default environments + imageEnvs, err := newImage.Env(ctx) if err != nil { return err } - if len(env) > 0 { - envs, err := envLib.ParseSlice(env) - if err != nil { - return err - } - for k, v := range envs { - if _, exists := s.Env[k]; !exists { - s.Env[v] = k - } - } + envs, err := envLib.ParseSlice(imageEnvs) + if err != nil { + return errors.Wrap(err, "Env fields from image failed to parse") } + s.Env = envLib.Join(envLib.Join(defaultEnvs, envs), s.Env) labels, err := newImage.Labels(ctx) if err != nil { @@ -73,6 +80,9 @@ func CompleteSpec(ctx context.Context, r *libpod.Runtime, s *specgen.SpecGenerat } // labels from the image that dont exist already + if len(labels) > 0 && s.Labels == nil { + s.Labels = make(map[string]string) + } for k, v := range labels { if _, exists := s.Labels[k]; !exists { s.Labels[k] = v diff --git a/pkg/specgen/generate/pod_create.go b/pkg/specgen/generate/pod_create.go index df5775f8b..cd2d69cfb 100644 --- a/pkg/specgen/generate/pod_create.go +++ b/pkg/specgen/generate/pod_create.go @@ -5,6 +5,7 @@ import ( "github.com/containers/libpod/libpod" "github.com/containers/libpod/pkg/specgen" + "github.com/pkg/errors" "github.com/sirupsen/logrus" ) @@ -68,15 +69,17 @@ func createPodOptions(p *specgen.PodSpecGenerator) ([]libpod.PodCreateOption, er if p.NoManageResolvConf { options = append(options, libpod.WithPodUseImageResolvConf()) } + if len(p.CNINetworks) > 0 { + options = append(options, libpod.WithPodNetworks(p.CNINetworks)) + } switch p.NetNS.NSMode { - case specgen.Bridge: + case specgen.Bridge, specgen.Default, "": logrus.Debugf("Pod using default network mode") case specgen.Host: logrus.Debugf("Pod will use host networking") options = append(options, libpod.WithPodHostNetwork()) default: - logrus.Debugf("Pod joining CNI networks: %v", p.CNINetworks) - options = append(options, libpod.WithPodNetworks(p.CNINetworks)) + return nil, errors.Errorf("pods presently do not support network mode %s", p.NetNS.NSMode) } if p.NoManageHosts { diff --git a/pkg/specgen/pod_validate.go b/pkg/specgen/pod_validate.go index 98d59549e..640447e71 100644 --- a/pkg/specgen/pod_validate.go +++ b/pkg/specgen/pod_validate.go @@ -1,7 +1,6 @@ package specgen import ( - "github.com/containers/libpod/pkg/rootless" "github.com/containers/libpod/pkg/util" "github.com/pkg/errors" ) @@ -33,12 +32,12 @@ func (p *PodSpecGenerator) Validate() error { } // PodNetworkConfig - if err := p.NetNS.validate(); err != nil { + if err := validateNetNS(&p.NetNS); err != nil { return err } if p.NoInfra { - if p.NetNS.NSMode == NoNetwork { - return errors.New("NoInfra and a none network cannot be used toegther") + if p.NetNS.NSMode != Default && p.NetNS.NSMode != "" { + return errors.New("NoInfra and network modes cannot be used toegther") } if p.StaticIP != nil { return exclusivePodOptions("NoInfra", "StaticIP") @@ -85,18 +84,7 @@ func (p *PodSpecGenerator) Validate() error { return exclusivePodOptions("NoManageHosts", "HostAdd") } - if err := p.NetNS.validate(); err != nil { - return err - } - // Set Defaults - if p.NetNS.Value == "" { - if rootless.IsRootless() { - p.NetNS.NSMode = Slirp - } else { - p.NetNS.NSMode = Bridge - } - } if len(p.InfraImage) < 1 { p.InfraImage = containerConfig.Engine.InfraImage } diff --git a/pkg/specgen/podspecgen.go b/pkg/specgen/podspecgen.go index 682f3f215..11976233a 100644 --- a/pkg/specgen/podspecgen.go +++ b/pkg/specgen/podspecgen.go @@ -54,7 +54,7 @@ type PodNetworkConfig struct { // namespace. This network will, by default, be shared with all // containers in the pod. // Cannot be set to FromContainer and FromPod. - // Setting this to anything except "" conflicts with NoInfra=true. + // Setting this to anything except default conflicts with NoInfra=true. // Defaults to Bridge as root and Slirp as rootless. // Mandatory. NetNS Namespace `json:"netns,omitempty"` diff --git a/pkg/specgen/specgen.go b/pkg/specgen/specgen.go index 4ad6dd6fb..bb01a5d14 100644 --- a/pkg/specgen/specgen.go +++ b/pkg/specgen/specgen.go @@ -47,6 +47,7 @@ type ContainerBasicConfig struct { // Optional. Env map[string]string `json:"env,omitempty"` // Terminal is whether the container will create a PTY. + // Optional. Terminal bool `json:"terminal,omitempty"` // Stdin is whether the container will keep its STDIN open. Stdin bool `json:"stdin,omitempty"` diff --git a/pkg/trust/config.go b/pkg/trust/config.go new file mode 100644 index 000000000..0bafc722b --- /dev/null +++ b/pkg/trust/config.go @@ -0,0 +1,12 @@ +package trust + +// Trust Policy describes a basic trust policy configuration +type TrustPolicy struct { + Name string `json:"name"` + RepoName string `json:"repo_name,omitempty"` + Keys []string `json:"keys,omitempty"` + SignatureStore string `json:"sigstore"` + Transport string `json:"transport"` + Type string `json:"type"` + GPGId string `json:"gpg_id,omitempty"` +} |