summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.cirrus.yml26
-rwxr-xr-xAPI.md24
-rw-r--r--CONTRIBUTING.md4
-rw-r--r--Makefile5
-rw-r--r--cmd/podman/create.go63
-rw-r--r--cmd/podman/generate_kube.go1
-rw-r--r--cmd/podman/image.go1
-rw-r--r--cmd/podman/main.go1
-rw-r--r--cmd/podman/play.go23
-rw-r--r--cmd/podman/play_kube.go245
-rw-r--r--cmd/podman/trust.go293
-rw-r--r--cmd/podman/varlink/io.podman.varlink12
-rw-r--r--completions/bash/podman37
-rw-r--r--contrib/gate/Dockerfile2
-rw-r--r--docs/podman-generate-kube.1.md2
-rw-r--r--docs/podman-image-trust.1.md81
-rw-r--r--docs/podman-image.1.md1
-rw-r--r--docs/podman-play-kube.1.md78
-rw-r--r--docs/podman-play.1.md20
-rw-r--r--docs/tutorials/podman_tutorial.md6
-rw-r--r--libpod/image/utils.go22
-rw-r--r--libpod/kube.go34
-rw-r--r--libpod/runtime.go5
-rw-r--r--pkg/trust/trust.go250
-rw-r--r--test/e2e/trust_test.go72
-rw-r--r--test/install/Dockerfile.Fedora4
26 files changed, 1267 insertions, 45 deletions
diff --git a/.cirrus.yml b/.cirrus.yml
index f18e82653..7afd8f0b3 100644
--- a/.cirrus.yml
+++ b/.cirrus.yml
@@ -22,6 +22,7 @@ env:
# Save a little typing (path relative to $CIRRUS_WORKING_DIR)
SCRIPT_BASE: "./contrib/cirrus"
PACKER_BASE: "./contrib/cirrus/packer"
+ CIRRUS_CLONE_DEPTH: 200
####
#### Variables for composing new cache-images (used in PR testing) from
@@ -97,11 +98,36 @@ gating_task:
- '/usr/local/bin/entrypoint.sh lint'
+build_each_commit_task:
+
+ depends_on:
+ - "gating"
+
+ # $CIRRUS_BASE_BRANCH is only set when testing a PR
+ only_if: $CIRRUS_BRANCH != 'master'
+
+ gce_instance:
+ image_project: "libpod-218412"
+ zone: "us-central1-a" # Required by Cirrus for the time being
+ cpu: 2
+ memory: "4Gb"
+ disk: 40
+ matrix:
+ image_name: "fedora-29-libpod-0c954a67"
+
+ timeout_in: 20m
+
+ script:
+ - $SCRIPT_BASE/setup_environment.sh
+ - git fetch --depth $CIRRUS_CLONE_DEPTH origin $CIRRUS_BASE_BRANCH
+ - env GOPATH=/var/tmp/go/ make build-all-new-commits GIT_BASE_BRANCH=origin/$CIRRUS_BASE_BRANCH
+
# This task does the unit and integration testing for every platform
testing_task:
depends_on:
- "gating"
+ - "build_each_commit"
gce_instance:
image_project: "libpod-218412"
diff --git a/API.md b/API.md
index 7bc866d98..51787496c 100755
--- a/API.md
+++ b/API.md
@@ -31,6 +31,10 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func ExportImage(name: string, destination: string, compress: bool, tags: []string) string](#ExportImage)
+[func GenerateKube() NotImplemented](#GenerateKube)
+
+[func GenerateKubeService() NotImplemented](#GenerateKubeService)
+
[func GetAttachSockets(name: string) Sockets](#GetAttachSockets)
[func GetContainer(name: string) ListContainerData](#GetContainer)
@@ -99,6 +103,8 @@ in the [API.md](https://github.com/containers/libpod/blob/master/API.md) file in
[func RenameContainer() NotImplemented](#RenameContainer)
+[func ReplayKube() NotImplemented](#ReplayKube)
+
[func ResizeContainerTty() NotImplemented](#ResizeContainerTty)
[func RestartContainer(name: string, timeout: int) string](#RestartContainer)
@@ -358,6 +364,18 @@ a booleon option to force compression. It also takes in a string array of tags
tags of the same image to a tarball (each tag should be of the form <image>:<tag>). Upon completion, the ID
of the image is returned. If the image cannot be found in local storage, an [ImageNotFound](#ImageNotFound)
error will be returned. See also [ImportImage](ImportImage).
+### <a name="GenerateKube"></a>func GenerateKube
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method GenerateKube() [NotImplemented](#NotImplemented)</div>
+GenerateKube generates a Kubernetes v1 Pod description of a Podman container or pod
+and its containers. The description is in YAML. See also [ReplayKube](ReplayKube).
+### <a name="GenerateKubeService"></a>func GenerateKubeService
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method GenerateKubeService() [NotImplemented](#NotImplemented)</div>
+GenerateKubeService generates a Kubernetes v1 Service description of a Podman container or pod
+and its containers. The description is in YAML. See also [GenerateKube](GenerateKube).
### <a name="GetAttachSockets"></a>func GetAttachSockets
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
@@ -808,6 +826,12 @@ $ varlink call -m unix:/run/podman/io.podman/io.podman.RemovePod '{"name": "62f4
method RenameContainer() [NotImplemented](#NotImplemented)</div>
This method has not be implemented yet.
+### <a name="ReplayKube"></a>func ReplayKube
+<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
+
+method ReplayKube() [NotImplemented](#NotImplemented)</div>
+ReplayKube recreates a pod and its containers based on a Kubernetes v1 Pod description (in YAML)
+like that created by GenerateKube. See also [GenerateKube](GenerateKube).
### <a name="ResizeContainerTty"></a>func ResizeContainerTty
<div style="background-color: #E8E8E8; padding: 15px; margin: 10px; border-radius: 10px;">
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 8e921dcf3..32ed94ad4 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -192,6 +192,8 @@ from the repository root, with the command:
sudo podman build -t quay.io/libpod/gate:latest -f contrib/gate/Dockerfile .
```
+***N/B:*** **don't miss the dot (.) at the end, it's really important**
+
The container executes 'make' by default, on a copy of the repository.
This avoids changing or leaving build artifacts in your working directory.
Execution does not require any special permissions from the host. However,
@@ -199,7 +201,7 @@ the repository root must be bind-mounted into the container at
'/usr/src/libpod'. For example, running `make lint` is done (from
the repository root) with the command:
-``sudo podman run -it --rm -v $PWD:/usr/src/libpod:z quay.io/libpod/gate:latest lint``
+``sudo podman run -it --rm -v $PWD:/usr/src/libpod:ro --security-opt label=disable quay.io/libpod/gate:latest lint``
### Integration Tests
diff --git a/Makefile b/Makefile
index b8f1eff93..c423d82f2 100644
--- a/Makefile
+++ b/Makefile
@@ -5,6 +5,7 @@ HEAD ?= HEAD
CHANGELOG_BASE ?= HEAD~
CHANGELOG_TARGET ?= HEAD
PROJECT := github.com/containers/libpod
+GIT_BASE_BRANCH ?= origin/master
GIT_BRANCH ?= $(shell git rev-parse --abbrev-ref HEAD 2>/dev/null)
GIT_BRANCH_CLEAN ?= $(shell echo $(GIT_BRANCH) | sed -e "s/[^[:alnum:]]/-/g")
LIBPOD_IMAGE ?= libpod_dev$(if $(GIT_BRANCH_CLEAN),:$(GIT_BRANCH_CLEAN))
@@ -351,6 +352,10 @@ API.md: cmd/podman/varlink/io.podman.varlink
validate: gofmt .gitvalidation
+build-all-new-commits:
+ # Validate that all the commits build on top of $(GIT_BASE_BRANCH)
+ git rebase $(GIT_BASE_BRANCH) -x make
+
.PHONY: \
.gopathok \
binaries \
diff --git a/cmd/podman/create.go b/cmd/podman/create.go
index 870eb28d6..2b31a6423 100644
--- a/cmd/podman/create.go
+++ b/cmd/podman/create.go
@@ -146,37 +146,10 @@ func createContainer(c *cli.Context, runtime *libpod.Runtime) (*libpod.Container
return nil, nil, err
}
- runtimeSpec, err := cc.CreateConfigToOCISpec(createConfig)
- if err != nil {
- return nil, nil, err
- }
-
- options, err := createConfig.GetContainerCreateOptions(runtime)
- if err != nil {
- return nil, nil, err
- }
-
- became, ret, err := joinOrCreateRootlessUserNamespace(createConfig, runtime)
+ ctr, err := createContainerFromCreateConfig(runtime, createConfig, ctx)
if err != nil {
return nil, nil, err
}
- if became {
- os.Exit(ret)
- }
-
- ctr, err := runtime.NewContainer(ctx, runtimeSpec, options...)
- if err != nil {
- return nil, nil, err
- }
-
- createConfigJSON, err := json.Marshal(createConfig)
- if err != nil {
- return nil, nil, err
- }
- if err := ctr.AddArtifact("create-config", createConfigJSON); err != nil {
- return nil, nil, err
- }
-
if cidFile != nil {
_, err = cidFile.WriteString(ctr.ID())
if err != nil {
@@ -913,3 +886,37 @@ func joinOrCreateRootlessUserNamespace(createConfig *cc.CreateConfig, runtime *l
}
return rootless.BecomeRootInUserNS()
}
+
+func createContainerFromCreateConfig(r *libpod.Runtime, createConfig *cc.CreateConfig, ctx context.Context) (*libpod.Container, error) {
+ runtimeSpec, err := cc.CreateConfigToOCISpec(createConfig)
+ if err != nil {
+ return nil, err
+ }
+
+ options, err := createConfig.GetContainerCreateOptions(r)
+ if err != nil {
+ return nil, err
+ }
+
+ became, ret, err := joinOrCreateRootlessUserNamespace(createConfig, r)
+ if err != nil {
+ return nil, err
+ }
+ if became {
+ os.Exit(ret)
+ }
+
+ ctr, err := r.NewContainer(ctx, runtimeSpec, options...)
+ if err != nil {
+ return nil, err
+ }
+
+ createConfigJSON, err := json.Marshal(createConfig)
+ if err != nil {
+ return nil, err
+ }
+ if err := ctr.AddArtifact("create-config", createConfigJSON); err != nil {
+ return nil, err
+ }
+ return ctr, nil
+}
diff --git a/cmd/podman/generate_kube.go b/cmd/podman/generate_kube.go
index 8f2f0de32..f63bd431b 100644
--- a/cmd/podman/generate_kube.go
+++ b/cmd/podman/generate_kube.go
@@ -33,7 +33,6 @@ var (
}
)
-// generateKubeYAMLCmdgenerates or replays kube
func generateKubeYAMLCmd(c *cli.Context) error {
var (
podYAML *v1.Pod
diff --git a/cmd/podman/image.go b/cmd/podman/image.go
index 95af36df5..e978b9cf5 100644
--- a/cmd/podman/image.go
+++ b/cmd/podman/image.go
@@ -19,6 +19,7 @@ var (
rmImageCommand,
saveCommand,
tagCommand,
+ trustCommand,
}
imageDescription = "Manage images"
diff --git a/cmd/podman/main.go b/cmd/podman/main.go
index 2db6c5dec..f47a75761 100644
--- a/cmd/podman/main.go
+++ b/cmd/podman/main.go
@@ -90,6 +90,7 @@ func main() {
portCommand,
pullCommand,
pushCommand,
+ playCommand,
restartCommand,
rmCommand,
rmiCommand,
diff --git a/cmd/podman/play.go b/cmd/podman/play.go
new file mode 100644
index 000000000..adbab3480
--- /dev/null
+++ b/cmd/podman/play.go
@@ -0,0 +1,23 @@
+package main
+
+import (
+ "github.com/urfave/cli"
+)
+
+var (
+ playSubCommands = []cli.Command{
+ playKubeCommand,
+ }
+
+ playDescription = "Play a pod and its containers from a structured file."
+ playCommand = cli.Command{
+ Name: "play",
+ Usage: "play a container or pod",
+ Description: playDescription,
+ ArgsUsage: "",
+ Subcommands: playSubCommands,
+ UseShortOptionHandling: true,
+ OnUsageError: usageErrorHandler,
+ Hidden: true,
+ }
+)
diff --git a/cmd/podman/play_kube.go b/cmd/podman/play_kube.go
new file mode 100644
index 000000000..f165c5f0f
--- /dev/null
+++ b/cmd/podman/play_kube.go
@@ -0,0 +1,245 @@
+package main
+
+import (
+ "fmt"
+ "io"
+ "io/ioutil"
+ "os"
+ "strings"
+
+ "github.com/containers/image/types"
+ "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/cmd/podman/shared"
+ "github.com/containers/libpod/libpod"
+ image2 "github.com/containers/libpod/libpod/image"
+ ns "github.com/containers/libpod/pkg/namespaces"
+ "github.com/containers/libpod/pkg/rootless"
+ "github.com/containers/libpod/pkg/spec"
+ "github.com/containers/storage"
+ "github.com/cri-o/ocicni/pkg/ocicni"
+ "github.com/ghodss/yaml"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "github.com/urfave/cli"
+ "k8s.io/api/core/v1"
+)
+
+var (
+ playKubeFlags = []cli.Flag{
+ cli.StringFlag{
+ Name: "authfile",
+ Usage: "Path of the authentication file. Default is ${XDG_RUNTIME_DIR}/containers/auth.json. Use REGISTRY_AUTH_FILE environment variable to override. ",
+ },
+ cli.StringFlag{
+ Name: "cert-dir",
+ Usage: "`pathname` of a directory containing TLS certificates and keys",
+ },
+ cli.StringFlag{
+ Name: "creds",
+ Usage: "`credentials` (USERNAME:PASSWORD) to use for authenticating to a registry",
+ },
+ cli.BoolFlag{
+ Name: "quiet, q",
+ Usage: "Suppress output information when pulling images",
+ },
+ cli.StringFlag{
+ Name: "signature-policy",
+ Usage: "`pathname` of signature policy file (not usually used)",
+ },
+ cli.BoolTFlag{
+ Name: "tls-verify",
+ Usage: "require HTTPS and verify certificates when contacting registries (default: true)",
+ },
+ }
+ playKubeDescription = "Play a Pod and its containers based on a Kubrernetes YAML"
+ playKubeCommand = cli.Command{
+ Name: "kube",
+ Usage: "Play a pod based on Kubernetes YAML",
+ Description: playKubeDescription,
+ Action: playKubeYAMLCmd,
+ Flags: sortFlags(playKubeFlags),
+ ArgsUsage: "kubernetes YAML file",
+ UseShortOptionHandling: true,
+ OnUsageError: usageErrorHandler,
+ }
+)
+
+func playKubeYAMLCmd(c *cli.Context) error {
+ var (
+ podOptions []libpod.PodCreateOption
+ podYAML v1.Pod
+ registryCreds *types.DockerAuthConfig
+ containers []*libpod.Container
+ writer io.Writer
+ )
+
+ ctx := getContext()
+ if rootless.IsRootless() {
+ return errors.Wrapf(libpod.ErrNotImplemented, "rootless users")
+ }
+ args := c.Args()
+ if len(args) > 1 {
+ return errors.New("you can only play one kubernetes file at a time")
+ }
+ if len(args) < 1 {
+ return errors.New("you must supply at least one file")
+ }
+
+ runtime, err := libpodruntime.GetRuntime(c)
+ if err != nil {
+ return errors.Wrapf(err, "could not get runtime")
+ }
+ defer runtime.Shutdown(false)
+
+ content, err := ioutil.ReadFile(args[0])
+ if err != nil {
+ return err
+ }
+
+ if err := yaml.Unmarshal(content, &podYAML); err != nil {
+ return errors.Wrapf(err, "unable to read %s as YAML", args[0])
+ }
+
+ podOptions = append(podOptions, libpod.WithInfraContainer())
+ podOptions = append(podOptions, libpod.WithPodName(podYAML.ObjectMeta.Name))
+ // TODO for now we just used the default kernel namespaces; we need to add/subtract this from yaml
+
+ nsOptions, err := shared.GetNamespaceOptions(strings.Split(DefaultKernelNamespaces, ","))
+ if err != nil {
+ return err
+ }
+ podOptions = append(podOptions, nsOptions...)
+ podPorts := getPodPorts(podYAML.Spec.Containers)
+ podOptions = append(podOptions, libpod.WithInfraContainerPorts(podPorts))
+
+ // Create the Pod
+ pod, err := runtime.NewPod(ctx, podOptions...)
+ if err != nil {
+ return err
+ }
+ // Print the Pod's ID
+ fmt.Println(pod.ID())
+
+ podInfraID, err := pod.InfraContainerID()
+ if err != nil {
+ return err
+ }
+
+ namespaces := map[string]string{
+ // Disabled during code review per mheon
+ //"pid": fmt.Sprintf("container:%s", podInfraID),
+ "net": fmt.Sprintf("container:%s", podInfraID),
+ "user": fmt.Sprintf("container:%s", podInfraID),
+ "ipc": fmt.Sprintf("container:%s", podInfraID),
+ "uts": fmt.Sprintf("container:%s", podInfraID),
+ }
+ if !c.Bool("quiet") {
+ writer = os.Stderr
+ }
+
+ dockerRegistryOptions := image2.DockerRegistryOptions{
+ DockerRegistryCreds: registryCreds,
+ DockerCertPath: c.String("cert-dir"),
+ }
+ if c.IsSet("tls-verify") {
+ dockerRegistryOptions.DockerInsecureSkipTLSVerify = types.NewOptionalBool(!c.BoolT("tls-verify"))
+ }
+
+ for _, container := range podYAML.Spec.Containers {
+ newImage, err := runtime.ImageRuntime().New(ctx, container.Image, c.String("signature-policy"), c.String("authfile"), writer, &dockerRegistryOptions, image2.SigningOptions{}, false)
+ if err != nil {
+ return err
+ }
+ createConfig := kubeContainerToCreateConfig(container, runtime, newImage, namespaces)
+ if err != nil {
+ return err
+ }
+ ctr, err := createContainerFromCreateConfig(runtime, createConfig, ctx)
+ if err != nil {
+ return err
+ }
+ containers = append(containers, ctr)
+ }
+
+ // start the containers
+ for _, ctr := range containers {
+ if err := ctr.Start(ctx); err != nil {
+ // Making this a hard failure here to avoid a mess
+ // the other containers are in created status
+ return err
+ }
+ fmt.Println(ctr.ID())
+ }
+
+ return nil
+}
+
+// getPodPorts converts a slice of kube container descriptions to an
+// array of ocicni portmapping descriptions usable in libpod
+func getPodPorts(containers []v1.Container) []ocicni.PortMapping {
+ var infraPorts []ocicni.PortMapping
+ for _, container := range containers {
+ for _, p := range container.Ports {
+ portBinding := ocicni.PortMapping{
+ HostPort: p.HostPort,
+ ContainerPort: p.ContainerPort,
+ Protocol: strings.ToLower(string(p.Protocol)),
+ }
+ if p.HostIP != "" {
+ logrus.Debug("HostIP on port bindings is not supported")
+ }
+ infraPorts = append(infraPorts, portBinding)
+ }
+ }
+ return infraPorts
+}
+
+// kubeContainerToCreateConfig takes a v1.Container and returns a createconfig describing a container
+func kubeContainerToCreateConfig(containerYAML v1.Container, runtime *libpod.Runtime, newImage *image2.Image, namespaces map[string]string) *createconfig.CreateConfig {
+ var (
+ containerConfig createconfig.CreateConfig
+ envs map[string]string
+ )
+
+ containerConfig.Runtime = runtime
+ containerConfig.Image = containerYAML.Image
+ containerConfig.ImageID = newImage.ID()
+ containerConfig.Name = containerYAML.Name
+ containerConfig.Tty = containerYAML.TTY
+ containerConfig.WorkDir = containerYAML.WorkingDir
+ if containerYAML.SecurityContext.ReadOnlyRootFilesystem != nil {
+ containerConfig.ReadOnlyRootfs = *containerYAML.SecurityContext.ReadOnlyRootFilesystem
+ }
+ if containerYAML.SecurityContext.Privileged != nil {
+ containerConfig.Privileged = *containerYAML.SecurityContext.Privileged
+ }
+
+ if containerYAML.SecurityContext.AllowPrivilegeEscalation != nil {
+ containerConfig.NoNewPrivs = !*containerYAML.SecurityContext.AllowPrivilegeEscalation
+ }
+
+ containerConfig.Command = containerYAML.Command
+ containerConfig.StopSignal = 15
+
+ // If the user does not pass in ID mappings, just set to basics
+ if containerConfig.IDMappings == nil {
+ containerConfig.IDMappings = &storage.IDMappingOptions{}
+ }
+
+ containerConfig.NetMode = ns.NetworkMode(namespaces["net"])
+ containerConfig.IpcMode = ns.IpcMode(namespaces["ipc"])
+ containerConfig.UtsMode = ns.UTSMode(namespaces["uts"])
+ // disabled in code review per mheon
+ //containerConfig.PidMode = ns.PidMode(namespaces["pid"])
+ containerConfig.UsernsMode = ns.UsernsMode(namespaces["user"])
+
+ if len(containerYAML.Env) > 0 {
+ envs = make(map[string]string)
+ }
+ // Environment Variables
+ for _, e := range containerYAML.Env {
+ envs[e.Name] = e.Value
+ }
+ containerConfig.Env = envs
+ return &containerConfig
+}
diff --git a/cmd/podman/trust.go b/cmd/podman/trust.go
new file mode 100644
index 000000000..7c404cd3f
--- /dev/null
+++ b/cmd/podman/trust.go
@@ -0,0 +1,293 @@
+package main
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "sort"
+
+ "github.com/containers/image/types"
+ "github.com/containers/libpod/cmd/podman/formats"
+ "github.com/containers/libpod/cmd/podman/libpodruntime"
+ "github.com/containers/libpod/libpod/image"
+ "github.com/containers/libpod/pkg/trust"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ "github.com/urfave/cli"
+)
+
+var (
+ setTrustFlags = []cli.Flag{
+ cli.StringFlag{
+ Name: "type, t",
+ Usage: "Trust type, accept values: signedBy(default), accept, reject.",
+ Value: "signedBy",
+ },
+ cli.StringSliceFlag{
+ Name: "pubkeysfile, f",
+ Usage: `Path of installed public key(s) to trust for TARGET.
+ Absolute path to keys is added to policy.json. May
+ used multiple times to define multiple public keys.
+ File(s) must exist before using this command.`,
+ },
+ cli.StringFlag{
+ Name: "policypath",
+ Hidden: true,
+ },
+ }
+ showTrustFlags = []cli.Flag{
+ cli.BoolFlag{
+ Name: "raw",
+ Usage: "Output raw policy file",
+ },
+ cli.BoolFlag{
+ Name: "json, j",
+ Usage: "Output as json",
+ },
+ cli.StringFlag{
+ Name: "policypath",
+ Hidden: true,
+ },
+ cli.StringFlag{
+ Name: "registrypath",
+ Hidden: true,
+ },
+ }
+
+ setTrustDescription = "Set default trust policy or add a new trust policy for a registry"
+ setTrustCommand = cli.Command{
+ Name: "set",
+ Usage: "Set default trust policy or a new trust policy for a registry",
+ Description: setTrustDescription,
+ Flags: sortFlags(setTrustFlags),
+ ArgsUsage: "default | REGISTRY[/REPOSITORY]",
+ Action: setTrustCmd,
+ OnUsageError: usageErrorHandler,
+ }
+
+ showTrustDescription = "Display trust policy for the system"
+ showTrustCommand = cli.Command{
+ Name: "show",
+ Usage: "Display trust policy for the system",
+ Description: showTrustDescription,
+ Flags: sortFlags(showTrustFlags),
+ Action: showTrustCmd,
+ ArgsUsage: "",
+ UseShortOptionHandling: true,
+ OnUsageError: usageErrorHandler,
+ }
+
+ trustSubCommands = []cli.Command{
+ setTrustCommand,
+ showTrustCommand,
+ }
+
+ trustDescription = fmt.Sprintf(`Manages the trust policy of the host system. (%s)
+ Trust policy describes a registry scope that must be signed by public keys.`, getDefaultPolicyPath())
+ trustCommand = cli.Command{
+ Name: "trust",
+ Usage: "Manage container image trust policy",
+ Description: trustDescription,
+ ArgsUsage: "{set,show} ...",
+ Subcommands: trustSubCommands,
+ OnUsageError: usageErrorHandler,
+ }
+)
+
+func showTrustCmd(c *cli.Context) error {
+ runtime, err := libpodruntime.GetRuntime(c)
+ if err != nil {
+ return errors.Wrapf(err, "could not create runtime")
+ }
+
+ var (
+ policyPath string
+ systemRegistriesDirPath string
+ )
+ if c.IsSet("policypath") {
+ policyPath = c.String("policypath")
+ } else {
+ policyPath = trust.DefaultPolicyPath(runtime.SystemContext())
+ }
+ policyContent, err := ioutil.ReadFile(policyPath)
+ if err != nil {
+ return errors.Wrapf(err, "unable to read %s", policyPath)
+ }
+ if c.IsSet("registrypath") {
+ systemRegistriesDirPath = c.String("registrypath")
+ } else {
+ systemRegistriesDirPath = trust.RegistriesDirPath(runtime.SystemContext())
+ }
+
+ if c.Bool("raw") {
+ _, err := os.Stdout.Write(policyContent)
+ if err != nil {
+ return errors.Wrap(err, "could not read trust policies")
+ }
+ return nil
+ }
+
+ var policyContentStruct trust.PolicyContent
+ if err := json.Unmarshal(policyContent, &policyContentStruct); err != nil {
+ return errors.Errorf("could not read trust policies")
+ }
+ policyJSON, err := trust.GetPolicyJSON(policyContentStruct, systemRegistriesDirPath)
+ if err != nil {
+ return errors.Wrapf(err, "error reading registry config file")
+ }
+ if c.Bool("json") {
+ var outjson interface{}
+ outjson = policyJSON
+ out := formats.JSONStruct{Output: outjson}
+ return formats.Writer(out).Out()
+ }
+
+ sortedRepos := sortPolicyJSONKey(policyJSON)
+ type policydefault struct {
+ Repo string
+ Trusttype string
+ GPGid string
+ Sigstore string
+ }
+ var policyoutput []policydefault
+ for _, repo := range sortedRepos {
+ repoval := policyJSON[repo]
+ var defaultstruct policydefault
+ defaultstruct.Repo = repo
+ if repoval["type"] != nil {
+ defaultstruct.Trusttype = trustTypeDescription(repoval["type"].(string))
+ }
+ if repoval["keys"] != nil && len(repoval["keys"].([]string)) > 0 {
+ defaultstruct.GPGid = trust.GetGPGId(repoval["keys"].([]string))
+ }
+ if repoval["sigstore"] != nil {
+ defaultstruct.Sigstore = repoval["sigstore"].(string)
+ }
+ policyoutput = append(policyoutput, defaultstruct)
+ }
+ var output []interface{}
+ for _, ele := range policyoutput {
+ output = append(output, interface{}(ele))
+ }
+ out := formats.StdoutTemplateArray{Output: output, Template: "{{.Repo}}\t{{.Trusttype}}\t{{.GPGid}}\t{{.Sigstore}}"}
+ return formats.Writer(out).Out()
+}
+
+func setTrustCmd(c *cli.Context) error {
+ runtime, err := libpodruntime.GetRuntime(c)
+ if err != nil {
+ return errors.Wrapf(err, "could not create runtime")
+ }
+
+ args := c.Args()
+ if len(args) != 1 {
+ return errors.Errorf("default or a registry name must be specified")
+ }
+ valid, err := image.IsValidImageURI(args[0])
+ if err != nil || !valid {
+ return errors.Wrapf(err, "invalid image uri %s", args[0])
+ }
+
+ trusttype := c.String("type")
+ if !isValidTrustType(trusttype) {
+ return errors.Errorf("invalid choice: %s (choose from 'accept', 'reject', 'signedBy')", trusttype)
+ }
+ if trusttype == "accept" {
+ trusttype = "insecureAcceptAnything"
+ }
+
+ pubkeysfile := c.StringSlice("pubkeysfile")
+ if len(pubkeysfile) == 0 && trusttype == "signedBy" {
+ return errors.Errorf("At least one public key must be defined for type 'signedBy'")
+ }
+
+ var policyPath string
+ if c.IsSet("policypath") {
+ policyPath = c.String("policypath")
+ } else {
+ policyPath = trust.DefaultPolicyPath(runtime.SystemContext())
+ }
+ var policyContentStruct trust.PolicyContent
+ _, 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")
+ }
+ }
+ var newReposContent []trust.RepoContent
+ 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 {
+ exists := false
+ for transport, transportval := range policyContentStruct.Transports {
+ _, exists = transportval[args[0]]
+ if exists {
+ policyContentStruct.Transports[transport][args[0]] = newReposContent
+ break
+ }
+ }
+ if !exists {
+ 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")
+ }
+ err = ioutil.WriteFile(policyPath, data, 0644)
+ if err != nil {
+ return errors.Wrapf(err, "error setting trust policy")
+ }
+ return 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
+}
+
+func sortPolicyJSONKey(m map[string]map[string]interface{}) []string {
+ keys := make([]string, len(m))
+ i := 0
+ for k := range m {
+ keys[i] = k
+ i++
+ }
+ sort.Strings(keys)
+ return keys
+}
+
+func isValidTrustType(t string) bool {
+ if t == "accept" || t == "insecureAcceptAnything" || t == "reject" || t == "signedBy" {
+ return true
+ }
+ return false
+}
+
+func getDefaultPolicyPath() string {
+ return trust.DefaultPolicyPath(&types.SystemContext{})
+}
diff --git a/cmd/podman/varlink/io.podman.varlink b/cmd/podman/varlink/io.podman.varlink
index 376bbc950..c1b7c703a 100644
--- a/cmd/podman/varlink/io.podman.varlink
+++ b/cmd/podman/varlink/io.podman.varlink
@@ -1016,6 +1016,18 @@ method UnmountContainer(name: string, force: bool) -> ()
# This function is not implemented yet.
method ListContainerPorts(name: string) -> (notimplemented: NotImplemented)
+# GenerateKube generates a Kubernetes v1 Pod description of a Podman container or pod
+# and its containers. The description is in YAML. See also [ReplayKube](ReplayKube).
+method GenerateKube() -> (notimplemented: NotImplemented)
+
+# GenerateKubeService generates a Kubernetes v1 Service description of a Podman container or pod
+# and its containers. The description is in YAML. See also [GenerateKube](GenerateKube).
+method GenerateKubeService() -> (notimplemented: NotImplemented)
+
+# ReplayKube recreates a pod and its containers based on a Kubernetes v1 Pod description (in YAML)
+# like that created by GenerateKube. See also [GenerateKube](GenerateKube).
+method ReplayKube() -> (notimplemented: NotImplemented)
+
# ImageNotFound means the image could not be found by the provided name or ID in local storage.
error ImageNotFound (name: string)
diff --git a/completions/bash/podman b/completions/bash/podman
index 4702ae0e0..2ce70a1f5 100644
--- a/completions/bash/podman
+++ b/completions/bash/podman
@@ -895,6 +895,26 @@ _podman_generate() {
;;
esac
}
+
+_podman_play() {
+ local boolean_options="
+ --help
+ -h
+ "
+ subcommands="
+ kube
+ "
+ __podman_subcommands "$subcommands $aliases" && return
+
+ case "$cur" in
+ -*)
+ COMPREPLY=( $( compgen -W "--help" -- "$cur" ) )
+ ;;
+ *)
+ COMPREPLY=( $( compgen -W "$subcommands" -- "$cur" ) )
+ ;;
+ esac
+}
_podman_container() {
local boolean_options="
--help
@@ -2247,6 +2267,22 @@ _podman_generate_kube() {
--service
"
+_podman_play_kube() {
+ local options_with_args="
+ --authfile
+ --cert-dir
+ --creds
+ --signature-policy
+ "
+
+ local boolean_options="
+ -h
+ --help
+ --quiet
+ -q
+ --tls-verify
+ "
+
_podman_container_runlabel() {
local options_with_args="
--authfile
@@ -2750,6 +2786,7 @@ _podman_podman() {
ps
pull
push
+ play
restart
rm
rmi
diff --git a/contrib/gate/Dockerfile b/contrib/gate/Dockerfile
index 0c0e4aaf9..f9b57a6da 100644
--- a/contrib/gate/Dockerfile
+++ b/contrib/gate/Dockerfile
@@ -1,4 +1,4 @@
-FROM fedora:28
+FROM fedora:29
RUN dnf -y install \
atomic-registries \
btrfs-progs-devel \
diff --git a/docs/podman-generate-kube.1.md b/docs/podman-generate-kube.1.md
index 396f69615..5236f23fe 100644
--- a/docs/podman-generate-kube.1.md
+++ b/docs/podman-generate-kube.1.md
@@ -145,7 +145,7 @@ status:
```
## SEE ALSO
-podman(1), podman-container, podman-pod
+podman(1), podman-container, podman-pod, podman-play
# HISTORY
Decemeber 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
diff --git a/docs/podman-image-trust.1.md b/docs/podman-image-trust.1.md
new file mode 100644
index 000000000..24209698c
--- /dev/null
+++ b/docs/podman-image-trust.1.md
@@ -0,0 +1,81 @@
+% podman-image-trust "1"
+
+# NAME
+podman\-trust - Manage container image trust policy
+
+
+# SYNOPSIS
+**podman image trust set|show**
+[**-h**|**--help**]
+[**-j**|**--json**]
+[**--raw**]
+[**-f**|**--pubkeysfile** KEY1 [**f**|**--pubkeysfile** KEY2,...]]
+[**-t**|**--type** signedBy|accept|reject]
+REGISTRY[/REPOSITORY]
+
+# DESCRIPTION
+Manages the trust policy of the host system. Trust policy describes
+a registry scope (registry and/or repository) that must be signed by public keys. Trust
+is defined in **/etc/containers/policy.json**. Trust is enforced when a user attempts to pull
+an image from a registry.
+
+Trust scope is evaluated by most specific to least specific. In other words, policy may
+be defined for an entire registry, but refined for a particular repository in that
+registry. See below for examples.
+
+Trust **type** provides a way to whitelist ("accept") or blacklist
+("reject") registries.
+
+Trust may be updated using the command **podman image trust set** for an existing trust scope.
+
+# OPTIONS
+**-h** **--help**
+ Print usage statement.
+
+**-f** **--pubkeysfile**
+ A path to an exported public key on the local system. Key paths
+ will be referenced in policy.json. Any path may be used but path
+ **/etc/pki/containers** is recommended. Option may be used multiple times to
+ require an image be sigend by multiple keys. One of **--pubkeys** or
+ **--pubkeysfile** is required for **signedBy** type.
+
+**-t** **--type**
+ The trust type for this policy entry. Accepted values:
+ **signedBy** (default): Require signatures with corresponding list of
+ public keys
+ **accept**: do not require any signatures for this
+ registry scope
+ **reject**: do not accept images for this registry scope
+
+# show OPTIONS
+
+**--raw**
+ Output trust policy file as raw JSON
+
+**-j** **--json**
+ Output trust as JSON for machine parsing
+
+# EXAMPLES
+
+Accept all unsigned images from a registry
+
+ podman image trust set --type accept docker.io
+
+Modify default trust policy
+
+ podman image trust set -t reject default
+
+Display system trust policy
+
+ podman image trust show
+
+Display trust policy file
+
+ podman image trust show --raw
+
+Display trust as JSON
+
+ podman image trust show --json
+
+# HISTORY
+December 2018, originally compiled by Qi Wang (qiwan at redhat dot com)
diff --git a/docs/podman-image.1.md b/docs/podman-image.1.md
index 8b812af11..8aa7cee64 100644
--- a/docs/podman-image.1.md
+++ b/docs/podman-image.1.md
@@ -26,6 +26,7 @@ The image command allows you to manage images
| rm | [podman-rm(1)](podman-rmi.1.md) | Removes one or more locally stored images. |
| save | [podman-save(1)](podman-save.1.md) | Save an image to docker-archive or oci. |
| tag | [podman-tag(1)](podman-tag.1.md) | Add an additional name to a local image. |
+| trust | [podman-image-trust(1)](podman-image-trust.1.md) | Manage container image trust policy.
## SEE ALSO
podman
diff --git a/docs/podman-play-kube.1.md b/docs/podman-play-kube.1.md
new file mode 100644
index 000000000..3fd9746a5
--- /dev/null
+++ b/docs/podman-play-kube.1.md
@@ -0,0 +1,78 @@
+% podman-play-kube Podman Man Pages
+% Brent Baude
+% December 2018
+# NAME
+podman-play-kube - Create pods and containers based on Kubernetes YAML
+
+# SYNOPSIS
+**podman play kube **
+[**-h**|**--help**]
+[**--authfile**]
+[**--cert-dir**]
+[**--creds**]
+[***-q** | **--quiet**]
+[**--signature-policy**]
+[**--tls-verify**]
+kubernetes_input.yml
+
+# DESCRIPTION
+**podman play kube** will read in a structured file of Kubernetes YAML. It will then recreate
+the pod and containers described in the YAML. The containers within the pod are then started and
+the ID of the new Pod is output.
+
+Ideally the input file would be one created by Podman. This would guarantee a smooth import and expected results.
+
+# OPTIONS:
+
+**--authfile**
+
+Path of the authentication file. Default is ${XDG_RUNTIME\_DIR}/containers/auth.json, which is set using `podman login`.
+If the authorization state is not found there, $HOME/.docker/config.json is checked, which is set using `docker login`.
+
+Note: You can also override the default path of the authentication file by setting the REGISTRY\_AUTH\_FILE
+environment variable. `export REGISTRY_AUTH_FILE=path`
+
+**--cert-dir** *path*
+
+Use certificates at *path* (\*.crt, \*.cert, \*.key) to connect to the registry.
+Default certificates directory is _/etc/containers/certs.d_.
+
+**--creds**
+
+The [username[:password]] to use to authenticate with the registry if required.
+If one or both values are not supplied, a command line prompt will appear and the
+value can be entered. The password is entered without echo.
+
+**--quiet, -q**
+
+Suppress output information when pulling images
+
+**--signature-policy="PATHNAME"**
+
+Pathname of a signature policy file to use. It is not recommended that this
+option be used, as the default behavior of using the system-wide default policy
+(frequently */etc/containers/policy.json*) is most often preferred.
+
+**--tls-verify**
+
+Require HTTPS and verify certificates when contacting registries (default: true). If explicitly set to true,
+then TLS verification will be used. If set to false, then TLS verification will not be used. If not specified,
+TLS verification will be used unless the target registry is listed as an insecure registry in registries.conf.
+
+**--help**, **-h**
+
+Print usage statement
+
+## Examples ##
+
+Recreate the pod and containers as described in a file called `demo.yml`
+```
+$ podman play kube demo.yml
+52182811df2b1e73f36476003a66ec872101ea59034ac0d4d3a7b40903b955a6
+```
+
+## SEE ALSO
+podman(1), podman-container(1), podman-pod(1), podman-generate(1), podman-play(1)
+
+# HISTORY
+Decemeber 2018, Originally compiled by Brent Baude (bbaude at redhat dot com)
diff --git a/docs/podman-play.1.md b/docs/podman-play.1.md
new file mode 100644
index 000000000..c703c1455
--- /dev/null
+++ b/docs/podman-play.1.md
@@ -0,0 +1,20 @@
+% podman-play(1)
+
+## NAME
+podman\-container - play pods and containers based on a structured input file
+
+## SYNOPSIS
+**podman play** *subcommand*
+
+## DESCRIPTION
+The play command will recreate pods and containers based on the input from a structured (like YAML)
+file input. Containers will be automatically started.
+
+## COMMANDS
+
+| Command | Man Page | Description |
+| ------- | --------------------------------------------------- | ---------------------------------------------------------------------------- |
+| kube | [podman-play-kube(1)](podman-play-kube.1.md) | Recreate pods and containers based on Kubernetes YAML.
+
+## SEE ALSO
+podman, podman-pod(1), podman-container(1), podman-generate(1), podman-play(1), podman-play-kube(1)
diff --git a/docs/tutorials/podman_tutorial.md b/docs/tutorials/podman_tutorial.md
index 659973b28..f8332c820 100644
--- a/docs/tutorials/podman_tutorial.md
+++ b/docs/tutorials/podman_tutorial.md
@@ -84,6 +84,12 @@ cd $GOPATH/src/github.com/containernetworking/plugins
sudo mkdir -p /usr/libexec/cni
sudo cp bin/* /usr/libexec/cni
```
+#### Installing CNI config
+Add a most basic network config
+```console
+mkdir -p /etc/cni/net.d
+curl -qsSL https://raw.githubusercontent.com/containers/libpod/master/cni/87-podman-bridge.conflist | tee /etc/cni/net.d/99-loopback.conf
+```
#### Installing runc
```console
git clone https://github.com/opencontainers/runc.git $GOPATH/src/github.com/opencontainers/runc
diff --git a/libpod/image/utils.go b/libpod/image/utils.go
index 9a75ca6dc..b944de1bb 100644
--- a/libpod/image/utils.go
+++ b/libpod/image/utils.go
@@ -2,6 +2,8 @@ package image
import (
"io"
+ "net/url"
+ "regexp"
"strings"
cp "github.com/containers/image/copy"
@@ -117,3 +119,23 @@ func GetAdditionalTags(images []string) ([]reference.NamedTagged, error) {
}
return allTags, nil
}
+
+// IsValidImageURI checks if image name has valid format
+func IsValidImageURI(imguri string) (bool, error) {
+ uri := "http://" + imguri
+ u, err := url.Parse(uri)
+ if err != nil {
+ return false, errors.Wrapf(err, "invalid image uri: %s", imguri)
+ }
+ reg := regexp.MustCompile(`^[a-zA-Z0-9-_\.]+\/?:?[0-9]*[a-z0-9-\/:]*$`)
+ ret := reg.FindAllString(u.Host, -1)
+ if len(ret) == 0 {
+ return false, errors.Wrapf(err, "invalid image uri: %s", imguri)
+ }
+ reg = regexp.MustCompile(`^[a-z0-9-:\./]*$`)
+ ret = reg.FindAllString(u.Fragment, -1)
+ if len(ret) == 0 {
+ return false, errors.Wrapf(err, "invalid image uri: %s", imguri)
+ }
+ return true, nil
+}
diff --git a/libpod/kube.go b/libpod/kube.go
index c164ca0c5..f34805e39 100644
--- a/libpod/kube.go
+++ b/libpod/kube.go
@@ -30,7 +30,10 @@ func (c *Container) GenerateForKube() (*v1.Pod, error) {
// one v1.Pod description
func (p *Pod) GenerateForKube() (*v1.Pod, []v1.ServicePort, error) {
// Generate the v1.Pod yaml description
- var servicePorts []v1.ServicePort
+ var (
+ servicePorts []v1.ServicePort
+ ports []v1.ContainerPort
+ )
allContainers, err := p.allContainers()
if err != nil {
@@ -51,13 +54,13 @@ func (p *Pod) GenerateForKube() (*v1.Pod, []v1.ServicePort, error) {
return nil, servicePorts, err
}
- ports, err := ocicniPortMappingToContainerPort(infraContainer.config.PortMappings)
+ ports, err = ocicniPortMappingToContainerPort(infraContainer.config.PortMappings)
if err != nil {
return nil, servicePorts, err
}
servicePorts = containerPortsToServicePorts(ports)
}
- pod, err := p.podWithContainers(allContainers)
+ pod, err := p.podWithContainers(allContainers, ports)
return pod, servicePorts, err
}
@@ -124,18 +127,27 @@ func containersToServicePorts(containers []v1.Container) []v1.ServicePort {
return sps
}
-func (p *Pod) podWithContainers(containers []*Container) (*v1.Pod, error) {
- var podContainers []v1.Container
+func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPort) (*v1.Pod, error) {
+ var (
+ podContainers []v1.Container
+ )
+ first := true
for _, ctr := range containers {
- result, err := containerToV1Container(ctr)
- if err != nil {
- return nil, err
- }
if !ctr.IsInfra() {
+ result, err := containerToV1Container(ctr)
+ if err != nil {
+ return nil, err
+ }
+ // We add the original port declarations from the libpod infra container
+ // to the first kubernetes container description because otherwise we loose
+ // the original container/port bindings.
+ if first && len(ports) > 0 {
+ result.Ports = ports
+ first = false
+ }
podContainers = append(podContainers, result)
}
}
-
return addContainersToPodObject(podContainers, p.Name()), nil
}
@@ -150,7 +162,7 @@ func addContainersToPodObject(containers []v1.Container, podName string) *v1.Pod
labels["app"] = removeUnderscores(podName)
om := v12.ObjectMeta{
// The name of the pod is container_name-libpod
- Name: fmt.Sprintf("%s-libpod", removeUnderscores(podName)),
+ Name: fmt.Sprintf("%s", removeUnderscores(podName)),
Labels: labels,
// CreationTimestamp seems to be required, so adding it; in doing so, the timestamp
// will reflect time this is run (not container create time) because the conversion
diff --git a/libpod/runtime.go b/libpod/runtime.go
index 82473aae9..2dfebf565 100644
--- a/libpod/runtime.go
+++ b/libpod/runtime.go
@@ -877,3 +877,8 @@ func (r *Runtime) generateName() (string, error) {
func (r *Runtime) ImageRuntime() *image.Runtime {
return r.imageRuntime
}
+
+// SystemContext returns the imagecontext
+func (r *Runtime) SystemContext() *types.SystemContext {
+ return r.imageContext
+}
diff --git a/pkg/trust/trust.go b/pkg/trust/trust.go
new file mode 100644
index 000000000..efc760364
--- /dev/null
+++ b/pkg/trust/trust.go
@@ -0,0 +1,250 @@
+package trust
+
+import (
+ "bufio"
+ "encoding/base64"
+ "encoding/json"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "unsafe"
+
+ "github.com/containers/image/types"
+ "github.com/pkg/errors"
+ "github.com/sirupsen/logrus"
+ yaml "gopkg.in/yaml.v2"
+)
+
+// PolicyContent struct for policy.json file
+type PolicyContent struct {
+ Default []RepoContent `json:"default"`
+ Transports TransportsContent `json:"transports"`
+}
+
+// RepoContent struct used under each repo
+type RepoContent struct {
+ Type string `json:"type"`
+ KeyType string `json:"keyType,omitempty"`
+ KeyPath string `json:"keyPath,omitempty"`
+ KeyData string `json:"keyData,omitempty"`
+ SignedIdentity json.RawMessage `json:"signedIdentity,omitempty"`
+}
+
+// RepoMap map repo name to policycontent for each repo
+type RepoMap map[string][]RepoContent
+
+// TransportsContent struct for content under "transports"
+type TransportsContent map[string]RepoMap
+
+// RegistryConfiguration is one of the files in registriesDirPath configuring lookaside locations, or the result of merging them all.
+// NOTE: Keep this in sync with docs/registries.d.md!
+type RegistryConfiguration struct {
+ DefaultDocker *RegistryNamespace `json:"default-docker"`
+ // The key is a namespace, using fully-expanded Docker reference format or parent namespaces (per dockerReference.PolicyConfiguration*),
+ Docker map[string]RegistryNamespace `json:"docker"`
+}
+
+// RegistryNamespace defines lookaside locations for a single namespace.
+type RegistryNamespace struct {
+ SigStore string `json:"sigstore"` // For reading, and if SigStoreStaging is not present, for writing.
+ SigStoreStaging string `json:"sigstore-staging"` // For writing only.
+}
+
+// DefaultPolicyPath returns a path to the default policy of the system.
+func DefaultPolicyPath(sys *types.SystemContext) string {
+ systemDefaultPolicyPath := "/etc/containers/policy.json"
+ if sys != nil {
+ if sys.SignaturePolicyPath != "" {
+ return sys.SignaturePolicyPath
+ }
+ if sys.RootForImplicitAbsolutePaths != "" {
+ return filepath.Join(sys.RootForImplicitAbsolutePaths, systemDefaultPolicyPath)
+ }
+ }
+ return systemDefaultPolicyPath
+}
+
+// RegistriesDirPath returns a path to registries.d
+func RegistriesDirPath(sys *types.SystemContext) string {
+ systemRegistriesDirPath := "/etc/containers/registries.d"
+ if sys != nil {
+ if sys.RegistriesDirPath != "" {
+ return sys.RegistriesDirPath
+ }
+ if sys.RootForImplicitAbsolutePaths != "" {
+ return filepath.Join(sys.RootForImplicitAbsolutePaths, systemRegistriesDirPath)
+ }
+ }
+ return systemRegistriesDirPath
+}
+
+// LoadAndMergeConfig loads configuration files in dirPath
+func LoadAndMergeConfig(dirPath string) (*RegistryConfiguration, error) {
+ mergedConfig := RegistryConfiguration{Docker: map[string]RegistryNamespace{}}
+ dockerDefaultMergedFrom := ""
+ nsMergedFrom := map[string]string{}
+
+ dir, err := os.Open(dirPath)
+ if err != nil {
+ if os.IsNotExist(err) {
+ return &mergedConfig, nil
+ }
+ return nil, err
+ }
+ configNames, err := dir.Readdirnames(0)
+ if err != nil {
+ return nil, err
+ }
+ for _, configName := range configNames {
+ if !strings.HasSuffix(configName, ".yaml") {
+ continue
+ }
+ configPath := filepath.Join(dirPath, configName)
+ configBytes, err := ioutil.ReadFile(configPath)
+ if err != nil {
+ return nil, err
+ }
+ var config RegistryConfiguration
+ err = yaml.Unmarshal(configBytes, &config)
+ if err != nil {
+ return nil, errors.Wrapf(err, "Error parsing %s", configPath)
+ }
+ if config.DefaultDocker != nil {
+ if mergedConfig.DefaultDocker != nil {
+ return nil, errors.Errorf(`Error parsing signature storage configuration: "default-docker" defined both in "%s" and "%s"`,
+ dockerDefaultMergedFrom, configPath)
+ }
+ mergedConfig.DefaultDocker = config.DefaultDocker
+ dockerDefaultMergedFrom = configPath
+ }
+ for nsName, nsConfig := range config.Docker { // includes config.Docker == nil
+ if _, ok := mergedConfig.Docker[nsName]; ok {
+ return nil, errors.Errorf(`Error parsing signature storage configuration: "docker" namespace "%s" defined both in "%s" and "%s"`,
+ nsName, nsMergedFrom[nsName], configPath)
+ }
+ mergedConfig.Docker[nsName] = nsConfig
+ nsMergedFrom[nsName] = configPath
+ }
+ }
+ return &mergedConfig, nil
+}
+
+// HaveMatchRegistry checks if trust settings for the registry have been configed in yaml file
+func HaveMatchRegistry(key string, registryConfigs *RegistryConfiguration) *RegistryNamespace {
+ searchKey := key
+ if !strings.Contains(searchKey, "/") {
+ val, exists := registryConfigs.Docker[searchKey]
+ if exists {
+ return &val
+ }
+ }
+ for range strings.Split(key, "/") {
+ val, exists := registryConfigs.Docker[searchKey]
+ if exists {
+ return &val
+ }
+ if strings.Contains(searchKey, "/") {
+ searchKey = searchKey[:strings.LastIndex(searchKey, "/")]
+ }
+ }
+ return nil
+}
+
+// CreateTmpFile creates a temp file under dir and writes the content into it
+func CreateTmpFile(dir, pattern string, content []byte) (string, error) {
+ tmpfile, err := ioutil.TempFile(dir, pattern)
+ if err != nil {
+ return "", err
+ }
+ defer tmpfile.Close()
+
+ if _, err := tmpfile.Write(content); err != nil {
+ return "", err
+
+ }
+ return tmpfile.Name(), nil
+}
+
+// GetGPGId return GPG identity, either bracketed <email> or ID string
+// comma separated if more than one key
+func GetGPGId(keys []string) string {
+ for _, k := range keys {
+ if _, err := os.Stat(k); err != nil {
+ decodeKey, err := base64.StdEncoding.DecodeString(k)
+ if err != nil {
+ logrus.Warnf("error decoding key data")
+ continue
+ }
+ tmpfileName, err := CreateTmpFile("/run/", "", decodeKey)
+ if err != nil {
+ logrus.Warnf("error creating key date temp file %s", err)
+ }
+ defer os.Remove(tmpfileName)
+ k = tmpfileName
+ }
+ cmd := exec.Command("gpg2", "--with-colons", k)
+ results, err := cmd.Output()
+ if err != nil {
+ logrus.Warnf("error get key identity: %s", err)
+ continue
+ }
+ resultsStr := *(*string)(unsafe.Pointer(&results))
+ scanner := bufio.NewScanner(strings.NewReader(resultsStr))
+ var parseduids []string
+ for scanner.Scan() {
+ line := scanner.Text()
+ if strings.HasPrefix(line, "uid:") || strings.HasPrefix(line, "pub:") {
+ uid := strings.Split(line, ":")[9]
+ if uid == "" {
+ continue
+ }
+ parseduid := uid
+ if strings.Contains(uid, "<") && strings.Contains(uid, ">") {
+ parseduid = strings.SplitN(strings.SplitAfterN(uid, "<", 2)[1], ">", 2)[0]
+ }
+ parseduids = append(parseduids, parseduid)
+ }
+ }
+ return strings.Join(parseduids, ",")
+ }
+ return ""
+}
+
+// GetPolicyJSON return the struct to show policy.json in json format
+func GetPolicyJSON(policyContentStruct PolicyContent, systemRegistriesDirPath string) (map[string]map[string]interface{}, error) {
+ registryConfigs, err := LoadAndMergeConfig(systemRegistriesDirPath)
+ if err != nil {
+ return nil, err
+ }
+
+ policyJSON := make(map[string]map[string]interface{})
+ if len(policyContentStruct.Default) > 0 {
+ policyJSON["* (default)"] = make(map[string]interface{})
+ policyJSON["* (default)"]["type"] = policyContentStruct.Default[0].Type
+ }
+ for transname, transval := range policyContentStruct.Transports {
+ for repo, repoval := range transval {
+ policyJSON[repo] = make(map[string]interface{})
+ policyJSON[repo]["type"] = repoval[0].Type
+ policyJSON[repo]["transport"] = transname
+ for _, repoele := range repoval {
+ keyarr := []string{}
+ if len(repoele.KeyPath) > 0 {
+ keyarr = append(keyarr, repoele.KeyPath)
+ }
+ if len(repoele.KeyData) > 0 {
+ keyarr = append(keyarr, string(repoele.KeyData))
+ }
+ policyJSON[repo]["keys"] = keyarr
+ }
+ policyJSON[repo]["sigstore"] = ""
+ registryNamespace := HaveMatchRegistry(repo, registryConfigs)
+ if registryNamespace != nil {
+ policyJSON[repo]["sigstore"] = registryNamespace.SigStore
+ }
+ }
+ }
+ return policyJSON, nil
+}
diff --git a/test/e2e/trust_test.go b/test/e2e/trust_test.go
new file mode 100644
index 000000000..bbf09eca4
--- /dev/null
+++ b/test/e2e/trust_test.go
@@ -0,0 +1,72 @@
+package integration
+
+import (
+ "encoding/json"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "path/filepath"
+
+ . "github.com/containers/libpod/test/utils"
+ . "github.com/onsi/ginkgo"
+ . "github.com/onsi/gomega"
+)
+
+var _ = Describe("Podman trust", func() {
+ var (
+ tempdir string
+ err error
+ podmanTest *PodmanTestIntegration
+ )
+
+ BeforeEach(func() {
+ tempdir, err = CreateTempDirInTempDir()
+ if err != nil {
+ os.Exit(1)
+ }
+ podmanTest = PodmanTestCreate(tempdir)
+ podmanTest.RestoreAllArtifacts()
+ })
+
+ AfterEach(func() {
+ podmanTest.Cleanup()
+ f := CurrentGinkgoTestDescription()
+ timedResult := fmt.Sprintf("Test: %s completed in %f seconds", f.TestText, f.Duration.Seconds())
+ GinkgoWriter.Write([]byte(timedResult))
+ })
+
+ It("podman image trust show", func() {
+ path, err := os.Getwd()
+ if err != nil {
+ os.Exit(1)
+ }
+ session := podmanTest.Podman([]string{"image", "trust", "show", "--registrypath", filepath.Dir(path), "--policypath", filepath.Join(filepath.Dir(path), "policy.json")})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ outArray := session.OutputToStringArray()
+ Expect(len(outArray)).To(Equal(3))
+ Expect(outArray[0]).Should(ContainSubstring("accept"))
+ Expect(outArray[1]).Should(ContainSubstring("reject"))
+ Expect(outArray[2]).Should(ContainSubstring("signed"))
+ })
+
+ It("podman image trust set", func() {
+ path, err := os.Getwd()
+ if err != nil {
+ os.Exit(1)
+ }
+ session := podmanTest.Podman([]string{"image", "trust", "set", "--policypath", filepath.Join(filepath.Dir(path), "trust_set_test.json"), "-t", "accept", "default"})
+ session.WaitWithDefaultTimeout()
+ Expect(session.ExitCode()).To(Equal(0))
+ var teststruct map[string][]map[string]string
+ policyContent, err := ioutil.ReadFile(filepath.Join(filepath.Dir(path), "trust_set_test.json"))
+ if err != nil {
+ os.Exit(1)
+ }
+ err = json.Unmarshal(policyContent, &teststruct)
+ if err != nil {
+ os.Exit(1)
+ }
+ Expect(teststruct["default"][0]["type"]).To(Equal("insecureAcceptAnything"))
+ })
+})
diff --git a/test/install/Dockerfile.Fedora b/test/install/Dockerfile.Fedora
index 188e60328..3a7b472de 100644
--- a/test/install/Dockerfile.Fedora
+++ b/test/install/Dockerfile.Fedora
@@ -1,3 +1,3 @@
-FROM registry.fedoraproject.org/fedora:28
+FROM registry.fedoraproject.org/fedora:29
-RUN dnf install -y rpms/noarch/* rpms/x86_64/* \ No newline at end of file
+RUN dnf install -y rpms/noarch/* rpms/x86_64/*