mirror of
https://github.com/containers/podman.git
synced 2025-06-19 16:33:24 +08:00
add {generate,play} kube
Add the `podman generate kube` and `podman play kube` command. The code has largely been copied from Podman v1 but restructured to not leak the K8s core API into the (remote) client. Both commands are added in the same commit to allow for enabling the tests at the same time. Move some exports from `cmd/podman/common` to the appropriate places in the backend to avoid circular dependencies. Move definitions of label annotations to `libpod/define` and set the security-opt labels in the frontend to make kube tests pass. Implement rest endpoints, bindings and the tunnel interface. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
This commit is contained in:
@ -534,10 +534,13 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
|
||||
case "label":
|
||||
// TODO selinux opts and label opts are the same thing
|
||||
s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1])
|
||||
s.Annotations[define.InspectAnnotationLabel] = con[1]
|
||||
case "apparmor":
|
||||
s.ContainerSecurityConfig.ApparmorProfile = con[1]
|
||||
s.Annotations[define.InspectAnnotationApparmor] = con[1]
|
||||
case "seccomp":
|
||||
s.SeccompProfilePath = con[1]
|
||||
s.Annotations[define.InspectAnnotationSeccomp] = con[1]
|
||||
default:
|
||||
return fmt.Errorf("invalid --security-opt 2: %q", opt)
|
||||
}
|
||||
|
@ -1,3 +0,0 @@
|
||||
package common
|
||||
|
||||
var DefaultKernelNamespaces = "cgroup,ipc,net,uts"
|
@ -22,7 +22,7 @@ var (
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Mode: []entities.EngineMode{entities.ABIMode},
|
||||
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||
Command: generateCmd,
|
||||
})
|
||||
}
|
||||
|
68
cmd/podman/generate/kube.go
Normal file
68
cmd/podman/generate/kube.go
Normal file
@ -0,0 +1,68 @@
|
||||
package pods
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
|
||||
"github.com/containers/libpod/cmd/podman/registry"
|
||||
"github.com/containers/libpod/cmd/podman/utils"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
kubeOptions = entities.GenerateKubeOptions{}
|
||||
kubeFile = ""
|
||||
kubeDescription = `Command generates Kubernetes pod and service YAML (v1 specification) from a Podman container or pod.
|
||||
|
||||
Whether the input is for a container or pod, Podman will always generate the specification as a pod.`
|
||||
|
||||
kubeCmd = &cobra.Command{
|
||||
Use: "kube [flags] CONTAINER | POD",
|
||||
Short: "Generate Kubernetes YAML from a container or pod.",
|
||||
Long: kubeDescription,
|
||||
RunE: kube,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Example: `podman generate kube ctrID
|
||||
podman generate kube podID
|
||||
podman generate kube --service podID`,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||
Command: kubeCmd,
|
||||
Parent: generateCmd,
|
||||
})
|
||||
flags := kubeCmd.Flags()
|
||||
flags.BoolVarP(&kubeOptions.Service, "service", "s", false, "Generate YAML for a Kubernetes service object")
|
||||
flags.StringVarP(&kubeFile, "filename", "f", "", "Write output to the specified path")
|
||||
flags.SetNormalizeFunc(utils.AliasFlags)
|
||||
}
|
||||
|
||||
func kube(cmd *cobra.Command, args []string) error {
|
||||
report, err := registry.ContainerEngine().GenerateKube(registry.GetContext(), args[0], kubeOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
content, err := ioutil.ReadAll(report.Reader)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if cmd.Flags().Changed("filename") {
|
||||
if _, err := os.Stat(kubeFile); err == nil {
|
||||
return errors.Errorf("cannot write to %q", kubeFile)
|
||||
}
|
||||
if err := ioutil.WriteFile(kubeFile, content, 0644); err != nil {
|
||||
return errors.Wrapf(err, "cannot write to %q", kubeFile)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
fmt.Println(string(content))
|
||||
return nil
|
||||
}
|
@ -29,7 +29,7 @@ var (
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||
Mode: []entities.EngineMode{entities.ABIMode},
|
||||
Command: systemdCmd,
|
||||
Parent: generateCmd,
|
||||
})
|
||||
|
@ -10,6 +10,7 @@ import (
|
||||
_ "github.com/containers/libpod/cmd/podman/images"
|
||||
_ "github.com/containers/libpod/cmd/podman/manifest"
|
||||
_ "github.com/containers/libpod/cmd/podman/networks"
|
||||
_ "github.com/containers/libpod/cmd/podman/play"
|
||||
_ "github.com/containers/libpod/cmd/podman/pods"
|
||||
"github.com/containers/libpod/cmd/podman/registry"
|
||||
_ "github.com/containers/libpod/cmd/podman/system"
|
||||
|
101
cmd/podman/play/kube.go
Normal file
101
cmd/podman/play/kube.go
Normal file
@ -0,0 +1,101 @@
|
||||
package pods
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/containers/common/pkg/auth"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/libpod/cmd/podman/registry"
|
||||
"github.com/containers/libpod/cmd/podman/utils"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
// playKubeOptionsWrapper allows for separating CLI-only fields from API-only
|
||||
// fields.
|
||||
type playKubeOptionsWrapper struct {
|
||||
entities.PlayKubeOptions
|
||||
|
||||
TLSVerifyCLI bool
|
||||
}
|
||||
|
||||
var (
|
||||
// https://kubernetes.io/docs/reference/command-line-tools-reference/kubelet/
|
||||
defaultSeccompRoot = "/var/lib/kubelet/seccomp"
|
||||
kubeOptions = playKubeOptionsWrapper{}
|
||||
kubeDescription = `Command reads in a structured file of Kubernetes YAML.
|
||||
|
||||
It creates 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.`
|
||||
|
||||
kubeCmd = &cobra.Command{
|
||||
Use: "kube [flags] KUBEFILE",
|
||||
Short: "Play a pod based on Kubernetes YAML.",
|
||||
Long: kubeDescription,
|
||||
RunE: kube,
|
||||
Args: cobra.ExactArgs(1),
|
||||
Example: `podman play kube nginx.yml
|
||||
podman play kube --creds user:password --seccomp-profile-root /custom/path apache.yml`,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||
Command: kubeCmd,
|
||||
Parent: playCmd,
|
||||
})
|
||||
|
||||
flags := kubeCmd.Flags()
|
||||
flags.SetNormalizeFunc(utils.AliasFlags)
|
||||
flags.StringVar(&kubeOptions.Credentials, "creds", "", "`Credentials` (USERNAME:PASSWORD) to use for authenticating to a registry")
|
||||
flags.StringVar(&kubeOptions.Network, "network", "", "Connect pod to CNI network(s)")
|
||||
flags.BoolVarP(&kubeOptions.Quiet, "quiet", "q", false, "Suppress output information when pulling images")
|
||||
if !registry.IsRemote() {
|
||||
flags.StringVar(&kubeOptions.Authfile, "authfile", auth.GetDefaultAuthFile(), "Path of the authentication file. Use REGISTRY_AUTH_FILE environment variable to override")
|
||||
flags.StringVar(&kubeOptions.CertDir, "cert-dir", "", "`Pathname` of a directory containing TLS certificates and keys")
|
||||
flags.BoolVar(&kubeOptions.TLSVerifyCLI, "tls-verify", true, "Require HTTPS and verify certificates when contacting registries")
|
||||
flags.StringVar(&kubeOptions.SignaturePolicy, "signature-policy", "", "`Pathname` of signature policy file (not usually used)")
|
||||
flags.StringVar(&kubeOptions.SeccompProfileRoot, "seccomp-profile-root", defaultSeccompRoot, "Directory path for seccomp profiles")
|
||||
}
|
||||
}
|
||||
|
||||
func kube(cmd *cobra.Command, args []string) error {
|
||||
// TLS verification in c/image is controlled via a `types.OptionalBool`
|
||||
// which allows for distinguishing among set-true, set-false, unspecified
|
||||
// which is important to implement a sane way of dealing with defaults of
|
||||
// boolean CLI flags.
|
||||
if cmd.Flags().Changed("tls-verify") {
|
||||
kubeOptions.SkipTLSVerify = types.NewOptionalBool(!kubeOptions.TLSVerifyCLI)
|
||||
}
|
||||
if kubeOptions.Authfile != "" {
|
||||
if _, err := os.Stat(kubeOptions.Authfile); err != nil {
|
||||
return errors.Wrapf(err, "error getting authfile %s", kubeOptions.Authfile)
|
||||
}
|
||||
}
|
||||
|
||||
report, err := registry.ContainerEngine().PlayKube(registry.GetContext(), args[0], kubeOptions.PlayKubeOptions)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, l := range report.Logs {
|
||||
fmt.Fprintf(os.Stderr, l)
|
||||
}
|
||||
|
||||
fmt.Printf("Pod:\n%s\n", report.Pod)
|
||||
switch len(report.Containers) {
|
||||
case 0:
|
||||
return nil
|
||||
case 1:
|
||||
fmt.Printf("Container:\n")
|
||||
default:
|
||||
fmt.Printf("Containers:\n")
|
||||
}
|
||||
for _, ctr := range report.Containers {
|
||||
fmt.Println(ctr)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
26
cmd/podman/play/play.go
Normal file
26
cmd/podman/play/play.go
Normal file
@ -0,0 +1,26 @@
|
||||
package pods
|
||||
|
||||
import (
|
||||
"github.com/containers/libpod/cmd/podman/registry"
|
||||
"github.com/containers/libpod/cmd/podman/validate"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/spf13/cobra"
|
||||
)
|
||||
|
||||
var (
|
||||
// Command: podman _play_
|
||||
playCmd = &cobra.Command{
|
||||
Use: "play",
|
||||
Short: "Play a pod and its containers from a structured file.",
|
||||
Long: "Play structured data (e.g., Kubernetes pod or service yaml) based on containers and pods.",
|
||||
TraverseChildren: true,
|
||||
RunE: validate.SubCommandExists,
|
||||
}
|
||||
)
|
||||
|
||||
func init() {
|
||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||
Command: playCmd,
|
||||
})
|
||||
}
|
@ -12,6 +12,7 @@ import (
|
||||
"github.com/containers/libpod/cmd/podman/validate"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/errorhandling"
|
||||
createconfig "github.com/containers/libpod/pkg/spec"
|
||||
"github.com/containers/libpod/pkg/specgen"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/pkg/errors"
|
||||
@ -57,7 +58,7 @@ func init() {
|
||||
flags.StringVarP(&createOptions.Name, "name", "n", "", "Assign a name to the pod")
|
||||
flags.StringVarP(&createOptions.Hostname, "hostname", "", "", "Set a hostname to the pod")
|
||||
flags.StringVar(&podIDFile, "pod-id-file", "", "Write the pod ID to the file")
|
||||
flags.StringVar(&share, "share", common.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share")
|
||||
flags.StringVar(&share, "share", createconfig.DefaultKernelNamespaces, "A comma delimited list of kernel namespaces the pod will share")
|
||||
}
|
||||
|
||||
func create(cmd *cobra.Command, args []string) error {
|
||||
|
@ -1221,5 +1221,5 @@ func (c *Container) AutoRemove() bool {
|
||||
if spec.Annotations == nil {
|
||||
return false
|
||||
}
|
||||
return c.Spec().Annotations[InspectAnnotationAutoremove] == InspectResponseTrue
|
||||
return c.Spec().Annotations[define.InspectAnnotationAutoremove] == define.InspectResponseTrue
|
||||
}
|
||||
|
@ -16,73 +16,6 @@ import (
|
||||
"github.com/syndtr/gocapability/capability"
|
||||
)
|
||||
|
||||
const (
|
||||
// InspectAnnotationCIDFile is used by Inspect to determine if a
|
||||
// container ID file was created for the container.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationCIDFile = "io.podman.annotations.cid-file"
|
||||
// InspectAnnotationAutoremove is used by Inspect to determine if a
|
||||
// container will be automatically removed on exit.
|
||||
// If an annotation with this key is found in the OCI spec and is one of
|
||||
// the two supported boolean values (InspectResponseTrue and
|
||||
// InspectResponseFalse) it will be used in the output of Inspect().
|
||||
InspectAnnotationAutoremove = "io.podman.annotations.autoremove"
|
||||
// InspectAnnotationVolumesFrom is used by Inspect to identify
|
||||
// containers whose volumes are are being used by this container.
|
||||
// It is expected to be a comma-separated list of container names and/or
|
||||
// IDs.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationVolumesFrom = "io.podman.annotations.volumes-from"
|
||||
// InspectAnnotationPrivileged is used by Inspect to identify containers
|
||||
// which are privileged (IE, running with elevated privileges).
|
||||
// It is expected to be a boolean, populated by one of
|
||||
// InspectResponseTrue or InspectResponseFalse.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationPrivileged = "io.podman.annotations.privileged"
|
||||
// InspectAnnotationPublishAll is used by Inspect to identify containers
|
||||
// which have all the ports from their image published.
|
||||
// It is expected to be a boolean, populated by one of
|
||||
// InspectResponseTrue or InspectResponseFalse.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationPublishAll = "io.podman.annotations.publish-all"
|
||||
// InspectAnnotationInit is used by Inspect to identify containers that
|
||||
// mount an init binary in.
|
||||
// It is expected to be a boolean, populated by one of
|
||||
// InspectResponseTrue or InspectResponseFalse.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationInit = "io.podman.annotations.init"
|
||||
// InspectAnnotationLabel is used by Inspect to identify containers with
|
||||
// special SELinux-related settings. It is used to populate the output
|
||||
// of the SecurityOpt setting.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationLabel = "io.podman.annotations.label"
|
||||
// InspectAnnotationSeccomp is used by Inspect to identify containers
|
||||
// with special Seccomp-related settings. It is used to populate the
|
||||
// output of the SecurityOpt setting in Inspect.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationSeccomp = "io.podman.annotations.seccomp"
|
||||
// InspectAnnotationApparmor is used by Inspect to identify containers
|
||||
// with special Apparmor-related settings. It is used to populate the
|
||||
// output of the SecurityOpt setting.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationApparmor = "io.podman.annotations.apparmor"
|
||||
|
||||
// InspectResponseTrue is a boolean True response for an inspect
|
||||
// annotation.
|
||||
InspectResponseTrue = "TRUE"
|
||||
// InspectResponseFalse is a boolean False response for an inspect
|
||||
// annotation.
|
||||
InspectResponseFalse = "FALSE"
|
||||
)
|
||||
|
||||
// inspectLocked inspects a container for low-level information.
|
||||
// The caller must held c.lock.
|
||||
func (c *Container) inspectLocked(size bool) (*define.InspectContainerData, error) {
|
||||
@ -452,26 +385,26 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
|
||||
|
||||
// Annotations
|
||||
if ctrSpec.Annotations != nil {
|
||||
hostConfig.ContainerIDFile = ctrSpec.Annotations[InspectAnnotationCIDFile]
|
||||
if ctrSpec.Annotations[InspectAnnotationAutoremove] == InspectResponseTrue {
|
||||
hostConfig.ContainerIDFile = ctrSpec.Annotations[define.InspectAnnotationCIDFile]
|
||||
if ctrSpec.Annotations[define.InspectAnnotationAutoremove] == define.InspectResponseTrue {
|
||||
hostConfig.AutoRemove = true
|
||||
}
|
||||
if ctrs, ok := ctrSpec.Annotations[InspectAnnotationVolumesFrom]; ok {
|
||||
if ctrs, ok := ctrSpec.Annotations[define.InspectAnnotationVolumesFrom]; ok {
|
||||
hostConfig.VolumesFrom = strings.Split(ctrs, ",")
|
||||
}
|
||||
if ctrSpec.Annotations[InspectAnnotationPrivileged] == InspectResponseTrue {
|
||||
if ctrSpec.Annotations[define.InspectAnnotationPrivileged] == define.InspectResponseTrue {
|
||||
hostConfig.Privileged = true
|
||||
}
|
||||
if ctrSpec.Annotations[InspectAnnotationInit] == InspectResponseTrue {
|
||||
if ctrSpec.Annotations[define.InspectAnnotationInit] == define.InspectResponseTrue {
|
||||
hostConfig.Init = true
|
||||
}
|
||||
if label, ok := ctrSpec.Annotations[InspectAnnotationLabel]; ok {
|
||||
if label, ok := ctrSpec.Annotations[define.InspectAnnotationLabel]; ok {
|
||||
hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("label=%s", label))
|
||||
}
|
||||
if seccomp, ok := ctrSpec.Annotations[InspectAnnotationSeccomp]; ok {
|
||||
if seccomp, ok := ctrSpec.Annotations[define.InspectAnnotationSeccomp]; ok {
|
||||
hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("seccomp=%s", seccomp))
|
||||
}
|
||||
if apparmor, ok := ctrSpec.Annotations[InspectAnnotationApparmor]; ok {
|
||||
if apparmor, ok := ctrSpec.Annotations[define.InspectAnnotationApparmor]; ok {
|
||||
hostConfig.SecurityOpt = append(hostConfig.SecurityOpt, fmt.Sprintf("apparmor=%s", apparmor))
|
||||
}
|
||||
}
|
||||
|
68
libpod/define/annotations.go
Normal file
68
libpod/define/annotations.go
Normal file
@ -0,0 +1,68 @@
|
||||
package define
|
||||
|
||||
const (
|
||||
// InspectAnnotationCIDFile is used by Inspect to determine if a
|
||||
// container ID file was created for the container.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationCIDFile = "io.podman.annotations.cid-file"
|
||||
// InspectAnnotationAutoremove is used by Inspect to determine if a
|
||||
// container will be automatically removed on exit.
|
||||
// If an annotation with this key is found in the OCI spec and is one of
|
||||
// the two supported boolean values (InspectResponseTrue and
|
||||
// InspectResponseFalse) it will be used in the output of Inspect().
|
||||
InspectAnnotationAutoremove = "io.podman.annotations.autoremove"
|
||||
// InspectAnnotationVolumesFrom is used by Inspect to identify
|
||||
// containers whose volumes are are being used by this container.
|
||||
// It is expected to be a comma-separated list of container names and/or
|
||||
// IDs.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationVolumesFrom = "io.podman.annotations.volumes-from"
|
||||
// InspectAnnotationPrivileged is used by Inspect to identify containers
|
||||
// which are privileged (IE, running with elevated privileges).
|
||||
// It is expected to be a boolean, populated by one of
|
||||
// InspectResponseTrue or InspectResponseFalse.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationPrivileged = "io.podman.annotations.privileged"
|
||||
// InspectAnnotationPublishAll is used by Inspect to identify containers
|
||||
// which have all the ports from their image published.
|
||||
// It is expected to be a boolean, populated by one of
|
||||
// InspectResponseTrue or InspectResponseFalse.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationPublishAll = "io.podman.annotations.publish-all"
|
||||
// InspectAnnotationInit is used by Inspect to identify containers that
|
||||
// mount an init binary in.
|
||||
// It is expected to be a boolean, populated by one of
|
||||
// InspectResponseTrue or InspectResponseFalse.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationInit = "io.podman.annotations.init"
|
||||
// InspectAnnotationLabel is used by Inspect to identify containers with
|
||||
// special SELinux-related settings. It is used to populate the output
|
||||
// of the SecurityOpt setting.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationLabel = "io.podman.annotations.label"
|
||||
// InspectAnnotationSeccomp is used by Inspect to identify containers
|
||||
// with special Seccomp-related settings. It is used to populate the
|
||||
// output of the SecurityOpt setting in Inspect.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationSeccomp = "io.podman.annotations.seccomp"
|
||||
// InspectAnnotationApparmor is used by Inspect to identify containers
|
||||
// with special Apparmor-related settings. It is used to populate the
|
||||
// output of the SecurityOpt setting.
|
||||
// If an annotation with this key is found in the OCI spec, it will be
|
||||
// used in the output of Inspect().
|
||||
InspectAnnotationApparmor = "io.podman.annotations.apparmor"
|
||||
|
||||
// InspectResponseTrue is a boolean True response for an inspect
|
||||
// annotation.
|
||||
InspectResponseTrue = "TRUE"
|
||||
// InspectResponseFalse is a boolean False response for an inspect
|
||||
// annotation.
|
||||
InspectResponseFalse = "FALSE"
|
||||
)
|
@ -469,7 +469,7 @@ func generateKubeSecurityContext(c *Container) (*v1.SecurityContext, error) {
|
||||
}
|
||||
|
||||
var selinuxOpts v1.SELinuxOptions
|
||||
opts := strings.SplitN(c.config.Spec.Annotations[InspectAnnotationLabel], ":", 2)
|
||||
opts := strings.SplitN(c.config.Spec.Annotations[define.InspectAnnotationLabel], ":", 2)
|
||||
if len(opts) == 2 {
|
||||
switch opts[0] {
|
||||
case "type":
|
||||
|
38
pkg/api/handlers/libpod/generate.go
Normal file
38
pkg/api/handlers/libpod/generate.go
Normal file
@ -0,0 +1,38 @@
|
||||
package libpod
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/pkg/api/handlers/utils"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/domain/infra/abi"
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func GenerateKube(w http.ResponseWriter, r *http.Request) {
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
query := struct {
|
||||
Service bool `schema:"service"`
|
||||
}{
|
||||
// Defaults would go here.
|
||||
}
|
||||
|
||||
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
|
||||
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
|
||||
return
|
||||
}
|
||||
|
||||
containerEngine := abi.ContainerEngine{Libpod: runtime}
|
||||
options := entities.GenerateKubeOptions{Service: query.Service}
|
||||
report, err := containerEngine.GenerateKube(r.Context(), utils.GetName(r), options)
|
||||
if err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error generating YAML"))
|
||||
return
|
||||
}
|
||||
|
||||
utils.WriteResponse(w, http.StatusOK, report.Reader)
|
||||
}
|
64
pkg/api/handlers/libpod/play.go
Normal file
64
pkg/api/handlers/libpod/play.go
Normal file
@ -0,0 +1,64 @@
|
||||
package libpod
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/pkg/api/handlers/utils"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/domain/infra/abi"
|
||||
"github.com/gorilla/schema"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
func PlayKube(w http.ResponseWriter, r *http.Request) {
|
||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||
query := struct {
|
||||
Network string `schema:"reference"`
|
||||
TLSVerify bool `schema:"tlsVerify"`
|
||||
}{
|
||||
TLSVerify: true,
|
||||
}
|
||||
|
||||
if err := decoder.Decode(&query, r.URL.Query()); err != nil {
|
||||
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
|
||||
errors.Wrapf(err, "failed to parse parameters for %s", r.URL.String()))
|
||||
return
|
||||
}
|
||||
|
||||
// Fetch the K8s YAML file from the body, and copy it to a temp file.
|
||||
tmpfile, err := ioutil.TempFile("", "libpod-play-kube.yml")
|
||||
if err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to create tempfile"))
|
||||
return
|
||||
}
|
||||
defer os.Remove(tmpfile.Name())
|
||||
if _, err := io.Copy(tmpfile, r.Body); err != nil && err != io.EOF {
|
||||
tmpfile.Close()
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "unable to write archive to temporary file"))
|
||||
return
|
||||
}
|
||||
if err := tmpfile.Close(); err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error closing temporary file"))
|
||||
return
|
||||
}
|
||||
|
||||
containerEngine := abi.ContainerEngine{Libpod: runtime}
|
||||
options := entities.PlayKubeOptions{Network: query.Network, Quiet: true}
|
||||
if _, found := r.URL.Query()["tlsVerify"]; found {
|
||||
options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
|
||||
}
|
||||
|
||||
report, err := containerEngine.PlayKube(r.Context(), tmpfile.Name(), options)
|
||||
if err != nil {
|
||||
utils.Error(w, "Something went wrong.", http.StatusInternalServerError, errors.Wrap(err, "error playing YAML file"))
|
||||
return
|
||||
}
|
||||
|
||||
utils.WriteResponse(w, http.StatusOK, report)
|
||||
}
|
@ -56,6 +56,13 @@ type swagLibpodImagesRemoveResponse struct {
|
||||
Body handlers.LibpodImagesRemoveReport
|
||||
}
|
||||
|
||||
// PlayKube response
|
||||
// swagger:response DocsLibpodPlayKubeResponse
|
||||
type swagLibpodPlayKubeResponse struct {
|
||||
// in:body
|
||||
Body entities.PlayKubeReport
|
||||
}
|
||||
|
||||
// Delete response
|
||||
// swagger:response DocsImageDeleteResponse
|
||||
type swagImageDeleteResponse struct {
|
||||
|
41
pkg/api/server/register_generate.go
Normal file
41
pkg/api/server/register_generate.go
Normal file
@ -0,0 +1,41 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/containers/libpod/pkg/api/handlers/libpod"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func (s *APIServer) registerGenerateHandlers(r *mux.Router) error {
|
||||
// swagger:operation GET /libpod/generate/{name:.*}/kube libpod libpodGenerateKube
|
||||
// ---
|
||||
// tags:
|
||||
// - containers
|
||||
// - pods
|
||||
// summary: Play a Kubernetes YAML file.
|
||||
// description: Create and run pods based on a Kubernetes YAML file (pod or service kind).
|
||||
// parameters:
|
||||
// - in: path
|
||||
// name: name:.*
|
||||
// type: string
|
||||
// required: true
|
||||
// description: Name or ID of the container or pod.
|
||||
// - in: query
|
||||
// name: service
|
||||
// type: boolean
|
||||
// default: false
|
||||
// description: Generate YAML for a Kubernetes service object.
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
// 200:
|
||||
// description: no error
|
||||
// schema:
|
||||
// type: string
|
||||
// format: binary
|
||||
// 500:
|
||||
// $ref: "#/responses/InternalError"
|
||||
r.HandleFunc(VersionedPath("/libpod/generate/{name:.*}/kube"), s.APIHandler(libpod.GenerateKube)).Methods(http.MethodGet)
|
||||
return nil
|
||||
}
|
42
pkg/api/server/register_play.go
Normal file
42
pkg/api/server/register_play.go
Normal file
@ -0,0 +1,42 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
|
||||
"github.com/containers/libpod/pkg/api/handlers/libpod"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func (s *APIServer) registerPlayHandlers(r *mux.Router) error {
|
||||
// swagger:operation POST /libpod/play/kube libpod libpodPlayKube
|
||||
// ---
|
||||
// tags:
|
||||
// - containers
|
||||
// - pods
|
||||
// summary: Play a Kubernetes YAML file.
|
||||
// description: Create and run pods based on a Kubernetes YAML file (pod or service kind).
|
||||
// parameters:
|
||||
// - in: query
|
||||
// name: network
|
||||
// type: string
|
||||
// description: Connect the pod to this network.
|
||||
// - in: query
|
||||
// name: tlsVerify
|
||||
// type: boolean
|
||||
// default: true
|
||||
// description: Require HTTPS and verify signatures when contating registries.
|
||||
// - in: body
|
||||
// name: request
|
||||
// description: Kubernetes YAML file.
|
||||
// schema:
|
||||
// type: string
|
||||
// produces:
|
||||
// - application/json
|
||||
// responses:
|
||||
// 200:
|
||||
// $ref: "#/responses/DocsLibpodPlayKubeResponse"
|
||||
// 500:
|
||||
// $ref: "#/responses/InternalError"
|
||||
r.HandleFunc(VersionedPath("/libpod/play/kube"), s.APIHandler(libpod.PlayKube)).Methods(http.MethodPost)
|
||||
return nil
|
||||
}
|
@ -98,12 +98,14 @@ func newServer(runtime *libpod.Runtime, duration time.Duration, listener *net.Li
|
||||
server.registerDistributionHandlers,
|
||||
server.registerEventsHandlers,
|
||||
server.registerExecHandlers,
|
||||
server.registerGenerateHandlers,
|
||||
server.registerHealthCheckHandlers,
|
||||
server.registerImagesHandlers,
|
||||
server.registerInfoHandlers,
|
||||
server.registerManifestHandlers,
|
||||
server.registerMonitorHandlers,
|
||||
server.registerPingHandlers,
|
||||
server.registerPlayHandlers,
|
||||
server.registerPluginsHandlers,
|
||||
server.registerPodsHandlers,
|
||||
server.RegisterSwaggerHandlers,
|
||||
|
@ -1,4 +1,32 @@
|
||||
package generate
|
||||
|
||||
func GenerateKube() {}
|
||||
func GenerateSystemd() {}
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"strconv"
|
||||
|
||||
"github.com/containers/libpod/pkg/bindings"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
)
|
||||
|
||||
func GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
params := url.Values{}
|
||||
params.Set("service", strconv.FormatBool(options.Service))
|
||||
|
||||
response, err := conn.DoRequest(nil, http.MethodGet, "/generate/%s/kube", params, nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if response.StatusCode == http.StatusOK {
|
||||
return &entities.GenerateKubeReport{Reader: response.Body}, nil
|
||||
}
|
||||
|
||||
// Unpack the error.
|
||||
return nil, response.Process(nil)
|
||||
}
|
||||
|
@ -1,7 +1,43 @@
|
||||
package play
|
||||
|
||||
import "github.com/containers/libpod/pkg/bindings"
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/url"
|
||||
"os"
|
||||
"strconv"
|
||||
|
||||
func PlayKube() error {
|
||||
return bindings.ErrNotImplemented
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/libpod/pkg/bindings"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
)
|
||||
|
||||
func PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
|
||||
var report entities.PlayKubeReport
|
||||
conn, err := bindings.GetClient(ctx)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
f, err := os.Open(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
params := url.Values{}
|
||||
params.Set("network", options.Network)
|
||||
if options.SkipTLSVerify != types.OptionalBoolUndefined {
|
||||
params.Set("tlsVerify", strconv.FormatBool(options.SkipTLSVerify == types.OptionalBoolTrue))
|
||||
}
|
||||
|
||||
response, err := conn.DoRequest(f, http.MethodPost, "/play/kube", params)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := response.Process(&report); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &report, nil
|
||||
}
|
||||
|
@ -43,6 +43,7 @@ type ContainerEngine interface {
|
||||
ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
|
||||
Events(ctx context.Context, opts EventsOptions) error
|
||||
GenerateSystemd(ctx context.Context, nameOrID string, opts GenerateSystemdOptions) (*GenerateSystemdReport, error)
|
||||
GenerateKube(ctx context.Context, nameOrID string, opts GenerateKubeOptions) (*GenerateKubeReport, error)
|
||||
SystemPrune(ctx context.Context, options SystemPruneOptions) (*SystemPruneReport, error)
|
||||
HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error)
|
||||
Info(ctx context.Context) (*define.Info, error)
|
||||
@ -50,6 +51,7 @@ type ContainerEngine interface {
|
||||
NetworkInspect(ctx context.Context, namesOrIds []string, options NetworkInspectOptions) ([]NetworkInspectReport, error)
|
||||
NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, error)
|
||||
NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, error)
|
||||
PlayKube(ctx context.Context, path string, opts PlayKubeOptions) (*PlayKubeReport, error)
|
||||
PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error)
|
||||
PodExists(ctx context.Context, nameOrId string) (*BoolReport, error)
|
||||
PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error)
|
||||
|
@ -1,5 +1,7 @@
|
||||
package entities
|
||||
|
||||
import "io"
|
||||
|
||||
// GenerateSystemdOptions control the generation of systemd unit files.
|
||||
type GenerateSystemdOptions struct {
|
||||
// Files - generate files instead of printing to stdout.
|
||||
@ -20,3 +22,15 @@ type GenerateSystemdReport struct {
|
||||
// entire content.
|
||||
Output string
|
||||
}
|
||||
|
||||
// GenerateKubeOptions control the generation of Kubernetes YAML files.
|
||||
type GenerateKubeOptions struct {
|
||||
// Service - generate YAML for a Kubernetes _service_ object.
|
||||
Service bool
|
||||
}
|
||||
|
||||
// GenerateKubeReport
|
||||
type GenerateKubeReport struct {
|
||||
// Reader - the io.Reader to reader the generated YAML file.
|
||||
Reader io.Reader
|
||||
}
|
||||
|
36
pkg/domain/entities/play.go
Normal file
36
pkg/domain/entities/play.go
Normal file
@ -0,0 +1,36 @@
|
||||
package entities
|
||||
|
||||
import "github.com/containers/image/v5/types"
|
||||
|
||||
// PlayKubeOptions controls playing kube YAML files.
|
||||
type PlayKubeOptions struct {
|
||||
// Authfile - path to an authentication file.
|
||||
Authfile string
|
||||
// CertDir - to a directory containing TLS certifications and keys.
|
||||
CertDir string
|
||||
// Credentials - `username:password` for authentication against a
|
||||
// container registry.
|
||||
Credentials string
|
||||
// Network - name of the CNI network to connect to.
|
||||
Network string
|
||||
// Quiet - suppress output when pulling images.
|
||||
Quiet bool
|
||||
// SignaturePolicy - path to a signature-policy file.
|
||||
SignaturePolicy string
|
||||
// SkipTLSVerify - skip https and certificate validation when
|
||||
// contacting container registries.
|
||||
SkipTLSVerify types.OptionalBool
|
||||
// SeccompProfileRoot - path to a directory containing seccomp
|
||||
// profiles.
|
||||
SeccompProfileRoot string
|
||||
}
|
||||
|
||||
// PlayKubeReport contains the results of running play kube.
|
||||
type PlayKubeReport struct {
|
||||
// Pod - the ID of the created pod.
|
||||
Pod string
|
||||
// Containers - the IDs of the containers running in the created pod.
|
||||
Containers []string
|
||||
// Logs - non-fatal erros and log messages while processing.
|
||||
Logs []string
|
||||
}
|
@ -1,14 +1,18 @@
|
||||
package abi
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"context"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/containers/libpod/pkg/systemd/generate"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/pkg/errors"
|
||||
k8sAPI "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) {
|
||||
@ -172,3 +176,84 @@ func generateServiceName(ctr *libpod.Container, pod *libpod.Pod, options entitie
|
||||
}
|
||||
return ctrName, fmt.Sprintf("%s-%s", kind, name)
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
|
||||
var (
|
||||
pod *libpod.Pod
|
||||
podYAML *k8sAPI.Pod
|
||||
err error
|
||||
ctr *libpod.Container
|
||||
servicePorts []k8sAPI.ServicePort
|
||||
serviceYAML k8sAPI.Service
|
||||
)
|
||||
// Get the container in question.
|
||||
ctr, err = ic.Libpod.LookupContainer(nameOrID)
|
||||
if err != nil {
|
||||
pod, err = ic.Libpod.LookupPod(nameOrID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
podYAML, servicePorts, err = pod.GenerateForKube()
|
||||
} else {
|
||||
if len(ctr.Dependencies()) > 0 {
|
||||
return nil, errors.Wrapf(define.ErrNotImplemented, "containers with dependencies")
|
||||
}
|
||||
podYAML, err = ctr.GenerateForKube()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if options.Service {
|
||||
serviceYAML = libpod.GenerateKubeServiceFromV1Pod(podYAML, servicePorts)
|
||||
}
|
||||
|
||||
content, err := generateKubeOutput(podYAML, &serviceYAML)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &entities.GenerateKubeReport{Reader: bytes.NewReader(content)}, nil
|
||||
}
|
||||
|
||||
func generateKubeOutput(podYAML *k8sAPI.Pod, serviceYAML *k8sAPI.Service) ([]byte, error) {
|
||||
var (
|
||||
output []byte
|
||||
marshalledPod []byte
|
||||
marshalledService []byte
|
||||
err error
|
||||
)
|
||||
|
||||
marshalledPod, err = yaml.Marshal(podYAML)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if serviceYAML != nil {
|
||||
marshalledService, err = yaml.Marshal(serviceYAML)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
header := `# Generation of Kubernetes YAML is still under development!
|
||||
#
|
||||
# Save the output of this file and use kubectl create -f to import
|
||||
# it into Kubernetes.
|
||||
#
|
||||
# Created with podman-%s
|
||||
`
|
||||
podmanVersion, err := define.GetVersion()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
output = append(output, []byte(fmt.Sprintf(header, podmanVersion.Version))...)
|
||||
output = append(output, marshalledPod...)
|
||||
if serviceYAML != nil {
|
||||
output = append(output, []byte("---\n")...)
|
||||
output = append(output, marshalledService...)
|
||||
}
|
||||
|
||||
return output, nil
|
||||
}
|
||||
|
544
pkg/domain/infra/abi/play.go
Normal file
544
pkg/domain/infra/abi/play.go
Normal file
@ -0,0 +1,544 @@
|
||||
package abi
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/containers/buildah/pkg/parse"
|
||||
"github.com/containers/image/v5/types"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
ann "github.com/containers/libpod/pkg/annotations"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
envLib "github.com/containers/libpod/pkg/env"
|
||||
ns "github.com/containers/libpod/pkg/namespaces"
|
||||
createconfig "github.com/containers/libpod/pkg/spec"
|
||||
"github.com/containers/libpod/pkg/specgen/generate"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/containers/storage"
|
||||
"github.com/cri-o/ocicni/pkg/ocicni"
|
||||
"github.com/docker/distribution/reference"
|
||||
"github.com/ghodss/yaml"
|
||||
"github.com/pkg/errors"
|
||||
"github.com/sirupsen/logrus"
|
||||
v1 "k8s.io/api/core/v1"
|
||||
)
|
||||
|
||||
const (
|
||||
// https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
|
||||
kubeDirectoryPermission = 0755
|
||||
// https://kubernetes.io/docs/concepts/storage/volumes/#hostpath
|
||||
kubeFilePermission = 0644
|
||||
)
|
||||
|
||||
func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
|
||||
var (
|
||||
containers []*libpod.Container
|
||||
pod *libpod.Pod
|
||||
podOptions []libpod.PodCreateOption
|
||||
podYAML v1.Pod
|
||||
registryCreds *types.DockerAuthConfig
|
||||
writer io.Writer
|
||||
report entities.PlayKubeReport
|
||||
)
|
||||
|
||||
content, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := yaml.Unmarshal(content, &podYAML); err != nil {
|
||||
return nil, errors.Wrapf(err, "unable to read %q as YAML", path)
|
||||
}
|
||||
|
||||
if podYAML.Kind != "Pod" {
|
||||
return nil, errors.Errorf("invalid YAML kind: %q. Pod is the only supported Kubernetes YAML kind", podYAML.Kind)
|
||||
}
|
||||
|
||||
// check for name collision between pod and container
|
||||
podName := podYAML.ObjectMeta.Name
|
||||
if podName == "" {
|
||||
return nil, errors.Errorf("pod does not have a name")
|
||||
}
|
||||
for _, n := range podYAML.Spec.Containers {
|
||||
if n.Name == podName {
|
||||
report.Logs = append(report.Logs,
|
||||
fmt.Sprintf("a container exists with the same name (%q) as the pod in your YAML file; changing pod name to %s_pod\n", podName, podName))
|
||||
podName = fmt.Sprintf("%s_pod", podName)
|
||||
}
|
||||
}
|
||||
|
||||
podOptions = append(podOptions, libpod.WithInfraContainer())
|
||||
podOptions = append(podOptions, libpod.WithPodName(podName))
|
||||
// TODO for now we just used the default kernel namespaces; we need to add/subtract this from yaml
|
||||
|
||||
hostname := podYAML.Spec.Hostname
|
||||
if hostname == "" {
|
||||
hostname = podName
|
||||
}
|
||||
podOptions = append(podOptions, libpod.WithPodHostname(hostname))
|
||||
|
||||
if podYAML.Spec.HostNetwork {
|
||||
podOptions = append(podOptions, libpod.WithPodHostNetwork())
|
||||
}
|
||||
|
||||
nsOptions, err := generate.GetNamespaceOptions(strings.Split(createconfig.DefaultKernelNamespaces, ","))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
podOptions = append(podOptions, nsOptions...)
|
||||
podPorts := getPodPorts(podYAML.Spec.Containers)
|
||||
podOptions = append(podOptions, libpod.WithInfraContainerPorts(podPorts))
|
||||
|
||||
if options.Network != "" {
|
||||
switch strings.ToLower(options.Network) {
|
||||
case "bridge", "host":
|
||||
return nil, errors.Errorf("invalid value passed to --network: bridge or host networking must be configured in YAML")
|
||||
case "":
|
||||
return nil, errors.Errorf("invalid value passed to --network: must provide a comma-separated list of CNI networks")
|
||||
default:
|
||||
// We'll assume this is a comma-separated list of CNI
|
||||
// networks.
|
||||
networks := strings.Split(options.Network, ",")
|
||||
logrus.Debugf("Pod joining CNI networks: %v", networks)
|
||||
podOptions = append(podOptions, libpod.WithPodNetworks(networks))
|
||||
}
|
||||
}
|
||||
|
||||
// Create the Pod
|
||||
pod, err = ic.Libpod.NewPod(ctx, podOptions...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
podInfraID, err := pod.InfraContainerID()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hasUserns := false
|
||||
if podInfraID != "" {
|
||||
podCtr, err := ic.Libpod.GetContainer(podInfraID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
mappings, err := podCtr.IDMappings()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
hasUserns = len(mappings.UIDMap) > 0
|
||||
}
|
||||
|
||||
namespaces := map[string]string{
|
||||
// Disabled during code review per mheon
|
||||
//"pid": fmt.Sprintf("container:%s", podInfraID),
|
||||
"net": fmt.Sprintf("container:%s", podInfraID),
|
||||
"ipc": fmt.Sprintf("container:%s", podInfraID),
|
||||
"uts": fmt.Sprintf("container:%s", podInfraID),
|
||||
}
|
||||
if hasUserns {
|
||||
namespaces["user"] = fmt.Sprintf("container:%s", podInfraID)
|
||||
}
|
||||
if !options.Quiet {
|
||||
writer = os.Stderr
|
||||
}
|
||||
|
||||
dockerRegistryOptions := image.DockerRegistryOptions{
|
||||
DockerRegistryCreds: registryCreds,
|
||||
DockerCertPath: options.CertDir,
|
||||
DockerInsecureSkipTLSVerify: options.SkipTLSVerify,
|
||||
}
|
||||
|
||||
// map from name to mount point
|
||||
volumes := make(map[string]string)
|
||||
for _, volume := range podYAML.Spec.Volumes {
|
||||
hostPath := volume.VolumeSource.HostPath
|
||||
if hostPath == nil {
|
||||
return nil, errors.Errorf("HostPath is currently the only supported VolumeSource")
|
||||
}
|
||||
if hostPath.Type != nil {
|
||||
switch *hostPath.Type {
|
||||
case v1.HostPathDirectoryOrCreate:
|
||||
if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) {
|
||||
if err := os.Mkdir(hostPath.Path, kubeDirectoryPermission); err != nil {
|
||||
return nil, errors.Errorf("Error creating HostPath %s at %s", volume.Name, hostPath.Path)
|
||||
}
|
||||
}
|
||||
// Label a newly created volume
|
||||
if err := libpod.LabelVolumePath(hostPath.Path); err != nil {
|
||||
return nil, errors.Wrapf(err, "Error giving %s a label", hostPath.Path)
|
||||
}
|
||||
case v1.HostPathFileOrCreate:
|
||||
if _, err := os.Stat(hostPath.Path); os.IsNotExist(err) {
|
||||
f, err := os.OpenFile(hostPath.Path, os.O_RDONLY|os.O_CREATE, kubeFilePermission)
|
||||
if err != nil {
|
||||
return nil, errors.Errorf("Error creating HostPath %s at %s", volume.Name, hostPath.Path)
|
||||
}
|
||||
if err := f.Close(); err != nil {
|
||||
logrus.Warnf("Error in closing newly created HostPath file: %v", err)
|
||||
}
|
||||
}
|
||||
// unconditionally label a newly created volume
|
||||
if err := libpod.LabelVolumePath(hostPath.Path); err != nil {
|
||||
return nil, errors.Wrapf(err, "Error giving %s a label", hostPath.Path)
|
||||
}
|
||||
case v1.HostPathDirectory:
|
||||
case v1.HostPathFile:
|
||||
case v1.HostPathUnset:
|
||||
// do nothing here because we will verify the path exists in validateVolumeHostDir
|
||||
break
|
||||
default:
|
||||
return nil, errors.Errorf("Directories are the only supported HostPath type")
|
||||
}
|
||||
}
|
||||
|
||||
if err := parse.ValidateVolumeHostDir(hostPath.Path); err != nil {
|
||||
return nil, errors.Wrapf(err, "Error in parsing HostPath in YAML")
|
||||
}
|
||||
volumes[volume.Name] = hostPath.Path
|
||||
}
|
||||
|
||||
seccompPaths, err := initializeSeccompPaths(podYAML.ObjectMeta.Annotations, options.SeccompProfileRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
for _, container := range podYAML.Spec.Containers {
|
||||
pullPolicy := util.PullImageMissing
|
||||
if len(container.ImagePullPolicy) > 0 {
|
||||
pullPolicy, err = util.ValidatePullType(string(container.ImagePullPolicy))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
named, err := reference.ParseNormalizedNamed(container.Image)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// In kube, if the image is tagged with latest, it should always pull
|
||||
if tagged, isTagged := named.(reference.NamedTagged); isTagged {
|
||||
if tagged.Tag() == image.LatestTag {
|
||||
pullPolicy = util.PullImageAlways
|
||||
}
|
||||
}
|
||||
newImage, err := ic.Libpod.ImageRuntime().New(ctx, container.Image, options.SignaturePolicy, options.Authfile, writer, &dockerRegistryOptions, image.SigningOptions{}, nil, pullPolicy)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
conf, err := kubeContainerToCreateConfig(ctx, container, ic.Libpod, newImage, namespaces, volumes, pod.ID(), podInfraID, seccompPaths)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ctr, err := createconfig.CreateContainerFromCreateConfig(ic.Libpod, conf, ctx, pod)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containers = append(containers, ctr)
|
||||
}
|
||||
|
||||
// start the containers
|
||||
for _, ctr := range containers {
|
||||
if err := ctr.Start(ctx, true); err != nil {
|
||||
// Making this a hard failure here to avoid a mess
|
||||
// the other containers are in created status
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
report.Pod = pod.ID()
|
||||
for _, ctr := range containers {
|
||||
report.Containers = append(report.Containers, ctr.ID())
|
||||
}
|
||||
|
||||
return &report, 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 {
|
||||
if p.HostPort != 0 && p.ContainerPort == 0 {
|
||||
p.ContainerPort = p.HostPort
|
||||
}
|
||||
if p.Protocol == "" {
|
||||
p.Protocol = "tcp"
|
||||
}
|
||||
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")
|
||||
}
|
||||
// only hostPort is utilized in podman context, all container ports
|
||||
// are accessible inside the shared network namespace
|
||||
if p.HostPort != 0 {
|
||||
infraPorts = append(infraPorts, portBinding)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
return infraPorts
|
||||
}
|
||||
|
||||
func setupSecurityContext(securityConfig *createconfig.SecurityConfig, userConfig *createconfig.UserConfig, containerYAML v1.Container) {
|
||||
if containerYAML.SecurityContext == nil {
|
||||
return
|
||||
}
|
||||
if containerYAML.SecurityContext.ReadOnlyRootFilesystem != nil {
|
||||
securityConfig.ReadOnlyRootfs = *containerYAML.SecurityContext.ReadOnlyRootFilesystem
|
||||
}
|
||||
if containerYAML.SecurityContext.Privileged != nil {
|
||||
securityConfig.Privileged = *containerYAML.SecurityContext.Privileged
|
||||
}
|
||||
|
||||
if containerYAML.SecurityContext.AllowPrivilegeEscalation != nil {
|
||||
securityConfig.NoNewPrivs = !*containerYAML.SecurityContext.AllowPrivilegeEscalation
|
||||
}
|
||||
|
||||
if seopt := containerYAML.SecurityContext.SELinuxOptions; seopt != nil {
|
||||
if seopt.User != "" {
|
||||
securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=user:%s", seopt.User))
|
||||
securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("user:%s", seopt.User))
|
||||
}
|
||||
if seopt.Role != "" {
|
||||
securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=role:%s", seopt.Role))
|
||||
securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("role:%s", seopt.Role))
|
||||
}
|
||||
if seopt.Type != "" {
|
||||
securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=type:%s", seopt.Type))
|
||||
securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("type:%s", seopt.Type))
|
||||
}
|
||||
if seopt.Level != "" {
|
||||
securityConfig.SecurityOpts = append(securityConfig.SecurityOpts, fmt.Sprintf("label=level:%s", seopt.Level))
|
||||
securityConfig.LabelOpts = append(securityConfig.LabelOpts, fmt.Sprintf("level:%s", seopt.Level))
|
||||
}
|
||||
}
|
||||
if caps := containerYAML.SecurityContext.Capabilities; caps != nil {
|
||||
for _, capability := range caps.Add {
|
||||
securityConfig.CapAdd = append(securityConfig.CapAdd, string(capability))
|
||||
}
|
||||
for _, capability := range caps.Drop {
|
||||
securityConfig.CapDrop = append(securityConfig.CapDrop, string(capability))
|
||||
}
|
||||
}
|
||||
if containerYAML.SecurityContext.RunAsUser != nil {
|
||||
userConfig.User = fmt.Sprintf("%d", *containerYAML.SecurityContext.RunAsUser)
|
||||
}
|
||||
if containerYAML.SecurityContext.RunAsGroup != nil {
|
||||
if userConfig.User == "" {
|
||||
userConfig.User = "0"
|
||||
}
|
||||
userConfig.User = fmt.Sprintf("%s:%d", userConfig.User, *containerYAML.SecurityContext.RunAsGroup)
|
||||
}
|
||||
}
|
||||
|
||||
// kubeContainerToCreateConfig takes a v1.Container and returns a createconfig describing a container
|
||||
func kubeContainerToCreateConfig(ctx context.Context, containerYAML v1.Container, runtime *libpod.Runtime, newImage *image.Image, namespaces map[string]string, volumes map[string]string, podID, infraID string, seccompPaths *kubeSeccompPaths) (*createconfig.CreateConfig, error) {
|
||||
var (
|
||||
containerConfig createconfig.CreateConfig
|
||||
pidConfig createconfig.PidConfig
|
||||
networkConfig createconfig.NetworkConfig
|
||||
cgroupConfig createconfig.CgroupConfig
|
||||
utsConfig createconfig.UtsConfig
|
||||
ipcConfig createconfig.IpcConfig
|
||||
userConfig createconfig.UserConfig
|
||||
securityConfig createconfig.SecurityConfig
|
||||
)
|
||||
|
||||
// The default for MemorySwappiness is -1, not 0
|
||||
containerConfig.Resources.MemorySwappiness = -1
|
||||
|
||||
containerConfig.Image = containerYAML.Image
|
||||
containerConfig.ImageID = newImage.ID()
|
||||
containerConfig.Name = containerYAML.Name
|
||||
containerConfig.Tty = containerYAML.TTY
|
||||
|
||||
containerConfig.Pod = podID
|
||||
|
||||
imageData, _ := newImage.Inspect(ctx)
|
||||
|
||||
userConfig.User = "0"
|
||||
if imageData != nil {
|
||||
userConfig.User = imageData.Config.User
|
||||
}
|
||||
|
||||
setupSecurityContext(&securityConfig, &userConfig, containerYAML)
|
||||
|
||||
securityConfig.SeccompProfilePath = seccompPaths.findForContainer(containerConfig.Name)
|
||||
|
||||
containerConfig.Command = []string{}
|
||||
if imageData != nil && imageData.Config != nil {
|
||||
containerConfig.Command = append(containerConfig.Command, imageData.Config.Entrypoint...)
|
||||
}
|
||||
if len(containerYAML.Command) != 0 {
|
||||
containerConfig.Command = append(containerConfig.Command, containerYAML.Command...)
|
||||
} else if imageData != nil && imageData.Config != nil {
|
||||
containerConfig.Command = append(containerConfig.Command, imageData.Config.Cmd...)
|
||||
}
|
||||
if imageData != nil && len(containerConfig.Command) == 0 {
|
||||
return nil, errors.Errorf("No command specified in container YAML or as CMD or ENTRYPOINT in this image for %s", containerConfig.Name)
|
||||
}
|
||||
|
||||
containerConfig.UserCommand = containerConfig.Command
|
||||
|
||||
containerConfig.StopSignal = 15
|
||||
|
||||
containerConfig.WorkDir = "/"
|
||||
if imageData != nil {
|
||||
// FIXME,
|
||||
// we are currently ignoring imageData.Config.ExposedPorts
|
||||
containerConfig.BuiltinImgVolumes = imageData.Config.Volumes
|
||||
if imageData.Config.WorkingDir != "" {
|
||||
containerConfig.WorkDir = imageData.Config.WorkingDir
|
||||
}
|
||||
containerConfig.Labels = imageData.Config.Labels
|
||||
if imageData.Config.StopSignal != "" {
|
||||
stopSignal, err := util.ParseSignal(imageData.Config.StopSignal)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
containerConfig.StopSignal = stopSignal
|
||||
}
|
||||
}
|
||||
|
||||
if containerYAML.WorkingDir != "" {
|
||||
containerConfig.WorkDir = containerYAML.WorkingDir
|
||||
}
|
||||
// If the user does not pass in ID mappings, just set to basics
|
||||
if userConfig.IDMappings == nil {
|
||||
userConfig.IDMappings = &storage.IDMappingOptions{}
|
||||
}
|
||||
|
||||
networkConfig.NetMode = ns.NetworkMode(namespaces["net"])
|
||||
ipcConfig.IpcMode = ns.IpcMode(namespaces["ipc"])
|
||||
utsConfig.UtsMode = ns.UTSMode(namespaces["uts"])
|
||||
// disabled in code review per mheon
|
||||
//containerConfig.PidMode = ns.PidMode(namespaces["pid"])
|
||||
userConfig.UsernsMode = ns.UsernsMode(namespaces["user"])
|
||||
if len(containerConfig.WorkDir) == 0 {
|
||||
containerConfig.WorkDir = "/"
|
||||
}
|
||||
|
||||
containerConfig.Pid = pidConfig
|
||||
containerConfig.Network = networkConfig
|
||||
containerConfig.Uts = utsConfig
|
||||
containerConfig.Ipc = ipcConfig
|
||||
containerConfig.Cgroup = cgroupConfig
|
||||
containerConfig.User = userConfig
|
||||
containerConfig.Security = securityConfig
|
||||
|
||||
annotations := make(map[string]string)
|
||||
if infraID != "" {
|
||||
annotations[ann.SandboxID] = infraID
|
||||
annotations[ann.ContainerType] = ann.ContainerTypeContainer
|
||||
}
|
||||
containerConfig.Annotations = annotations
|
||||
|
||||
// Environment Variables
|
||||
envs := map[string]string{}
|
||||
if imageData != nil {
|
||||
imageEnv, err := envLib.ParseSlice(imageData.Config.Env)
|
||||
if err != nil {
|
||||
return nil, errors.Wrap(err, "error parsing image environment variables")
|
||||
}
|
||||
envs = imageEnv
|
||||
}
|
||||
for _, e := range containerYAML.Env {
|
||||
envs[e.Name] = e.Value
|
||||
}
|
||||
containerConfig.Env = envs
|
||||
|
||||
for _, volume := range containerYAML.VolumeMounts {
|
||||
hostPath, exists := volumes[volume.Name]
|
||||
if !exists {
|
||||
return nil, errors.Errorf("Volume mount %s specified for container but not configured in volumes", volume.Name)
|
||||
}
|
||||
if err := parse.ValidateVolumeCtrDir(volume.MountPath); err != nil {
|
||||
return nil, errors.Wrapf(err, "error in parsing MountPath")
|
||||
}
|
||||
containerConfig.Volumes = append(containerConfig.Volumes, fmt.Sprintf("%s:%s", hostPath, volume.MountPath))
|
||||
}
|
||||
return &containerConfig, nil
|
||||
}
|
||||
|
||||
// kubeSeccompPaths holds information about a pod YAML's seccomp configuration
|
||||
// it holds both container and pod seccomp paths
|
||||
type kubeSeccompPaths struct {
|
||||
containerPaths map[string]string
|
||||
podPath string
|
||||
}
|
||||
|
||||
// findForContainer checks whether a container has a seccomp path configured for it
|
||||
// if not, it returns the podPath, which should always have a value
|
||||
func (k *kubeSeccompPaths) findForContainer(ctrName string) string {
|
||||
if path, ok := k.containerPaths[ctrName]; ok {
|
||||
return path
|
||||
}
|
||||
return k.podPath
|
||||
}
|
||||
|
||||
// initializeSeccompPaths takes annotations from the pod object metadata and finds annotations pertaining to seccomp
|
||||
// it parses both pod and container level
|
||||
// if the annotation is of the form "localhost/%s", the seccomp profile will be set to profileRoot/%s
|
||||
func initializeSeccompPaths(annotations map[string]string, profileRoot string) (*kubeSeccompPaths, error) {
|
||||
seccompPaths := &kubeSeccompPaths{containerPaths: make(map[string]string)}
|
||||
var err error
|
||||
if annotations != nil {
|
||||
for annKeyValue, seccomp := range annotations {
|
||||
// check if it is prefaced with container.seccomp.security.alpha.kubernetes.io/
|
||||
prefixAndCtr := strings.Split(annKeyValue, "/")
|
||||
if prefixAndCtr[0]+"/" != v1.SeccompContainerAnnotationKeyPrefix {
|
||||
continue
|
||||
} else if len(prefixAndCtr) != 2 {
|
||||
// this could be caused by a user inputting either of
|
||||
// container.seccomp.security.alpha.kubernetes.io{,/}
|
||||
// both of which are invalid
|
||||
return nil, errors.Errorf("Invalid seccomp path: %s", prefixAndCtr[0])
|
||||
}
|
||||
|
||||
path, err := verifySeccompPath(seccomp, profileRoot)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seccompPaths.containerPaths[prefixAndCtr[1]] = path
|
||||
}
|
||||
|
||||
podSeccomp, ok := annotations[v1.SeccompPodAnnotationKey]
|
||||
if ok {
|
||||
seccompPaths.podPath, err = verifySeccompPath(podSeccomp, profileRoot)
|
||||
} else {
|
||||
seccompPaths.podPath, err = libpod.DefaultSeccompPath()
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return seccompPaths, nil
|
||||
}
|
||||
|
||||
// verifySeccompPath takes a path and checks whether it is a default, unconfined, or a path
|
||||
// the available options are parsed as defined in https://kubernetes.io/docs/concepts/policy/pod-security-policy/#seccomp
|
||||
func verifySeccompPath(path string, profileRoot string) (string, error) {
|
||||
switch path {
|
||||
case v1.DeprecatedSeccompProfileDockerDefault:
|
||||
fallthrough
|
||||
case v1.SeccompProfileRuntimeDefault:
|
||||
return libpod.DefaultSeccompPath()
|
||||
case "unconfined":
|
||||
return path, nil
|
||||
default:
|
||||
parts := strings.Split(path, "/")
|
||||
if parts[0] == "localhost" {
|
||||
return filepath.Join(profileRoot, parts[1]), nil
|
||||
}
|
||||
return "", errors.Errorf("invalid seccomp path: %s", path)
|
||||
}
|
||||
}
|
@ -3,6 +3,7 @@ package tunnel
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containers/libpod/pkg/bindings/generate"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
@ -10,3 +11,7 @@ import (
|
||||
func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) {
|
||||
return nil, errors.New("not implemented for tunnel")
|
||||
}
|
||||
|
||||
func (ic *ContainerEngine) GenerateKube(ctx context.Context, nameOrID string, options entities.GenerateKubeOptions) (*entities.GenerateKubeReport, error) {
|
||||
return generate.GenerateKube(ic.ClientCxt, nameOrID, options)
|
||||
}
|
||||
|
12
pkg/domain/infra/tunnel/play.go
Normal file
12
pkg/domain/infra/tunnel/play.go
Normal file
@ -0,0 +1,12 @@
|
||||
package tunnel
|
||||
|
||||
import (
|
||||
"context"
|
||||
|
||||
"github.com/containers/libpod/pkg/bindings/play"
|
||||
"github.com/containers/libpod/pkg/domain/entities"
|
||||
)
|
||||
|
||||
func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
|
||||
return play.PlayKube(ic.ClientCxt, path, options)
|
||||
}
|
@ -17,6 +17,10 @@ import (
|
||||
"github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// DefaultKernelNamespaces is a comma-separated list of default kernel
|
||||
// namespaces.
|
||||
const DefaultKernelNamespaces = "cgroup,ipc,net,uts"
|
||||
|
||||
// ToCreateOptions converts the input to a slice of container create options.
|
||||
func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserConfig) ([]libpod.CtrCreateOption, error) {
|
||||
var portBindings []ocicni.PortMapping
|
||||
@ -154,9 +158,9 @@ func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error {
|
||||
}
|
||||
|
||||
if c.PublishAll {
|
||||
g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue
|
||||
g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue
|
||||
} else {
|
||||
g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse
|
||||
g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/containers/common/pkg/capabilities"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/pkg/util"
|
||||
"github.com/opencontainers/runtime-tools/generate"
|
||||
"github.com/opencontainers/selinux/go-selinux/label"
|
||||
@ -184,11 +185,11 @@ func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserCon
|
||||
}
|
||||
switch splitOpt[0] {
|
||||
case "label":
|
||||
configSpec.Annotations[libpod.InspectAnnotationLabel] = splitOpt[1]
|
||||
configSpec.Annotations[define.InspectAnnotationLabel] = splitOpt[1]
|
||||
case "seccomp":
|
||||
configSpec.Annotations[libpod.InspectAnnotationSeccomp] = splitOpt[1]
|
||||
configSpec.Annotations[define.InspectAnnotationSeccomp] = splitOpt[1]
|
||||
case "apparmor":
|
||||
configSpec.Annotations[libpod.InspectAnnotationApparmor] = splitOpt[1]
|
||||
configSpec.Annotations[define.InspectAnnotationApparmor] = splitOpt[1]
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -7,6 +7,7 @@ import (
|
||||
cconfig "github.com/containers/common/pkg/config"
|
||||
"github.com/containers/common/pkg/sysinfo"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/pkg/cgroups"
|
||||
"github.com/containers/libpod/pkg/env"
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
@ -436,29 +437,29 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
|
||||
}
|
||||
|
||||
if config.CidFile != "" {
|
||||
configSpec.Annotations[libpod.InspectAnnotationCIDFile] = config.CidFile
|
||||
configSpec.Annotations[define.InspectAnnotationCIDFile] = config.CidFile
|
||||
}
|
||||
|
||||
if config.Rm {
|
||||
configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseTrue
|
||||
configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue
|
||||
} else {
|
||||
configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseFalse
|
||||
configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse
|
||||
}
|
||||
|
||||
if len(config.VolumesFrom) > 0 {
|
||||
configSpec.Annotations[libpod.InspectAnnotationVolumesFrom] = strings.Join(config.VolumesFrom, ",")
|
||||
configSpec.Annotations[define.InspectAnnotationVolumesFrom] = strings.Join(config.VolumesFrom, ",")
|
||||
}
|
||||
|
||||
if config.Security.Privileged {
|
||||
configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseTrue
|
||||
configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue
|
||||
} else {
|
||||
configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseFalse
|
||||
configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseFalse
|
||||
}
|
||||
|
||||
if config.Init {
|
||||
configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseTrue
|
||||
configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseTrue
|
||||
} else {
|
||||
configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseFalse
|
||||
configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseFalse
|
||||
}
|
||||
|
||||
return configSpec, nil
|
||||
|
@ -438,9 +438,9 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt
|
||||
g.Config.Annotations = make(map[string]string)
|
||||
}
|
||||
if s.PublishExposedPorts {
|
||||
g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue
|
||||
g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue
|
||||
} else {
|
||||
g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse
|
||||
g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse
|
||||
}
|
||||
|
||||
return nil
|
||||
|
@ -6,6 +6,7 @@ import (
|
||||
|
||||
"github.com/containers/common/pkg/config"
|
||||
"github.com/containers/libpod/libpod"
|
||||
"github.com/containers/libpod/libpod/define"
|
||||
"github.com/containers/libpod/libpod/image"
|
||||
"github.com/containers/libpod/pkg/rootless"
|
||||
"github.com/containers/libpod/pkg/specgen"
|
||||
@ -327,19 +328,19 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
|
||||
//}
|
||||
|
||||
if s.Remove {
|
||||
configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseTrue
|
||||
configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue
|
||||
} else {
|
||||
configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseFalse
|
||||
configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse
|
||||
}
|
||||
|
||||
if len(s.VolumesFrom) > 0 {
|
||||
configSpec.Annotations[libpod.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ",")
|
||||
configSpec.Annotations[define.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ",")
|
||||
}
|
||||
|
||||
if s.Privileged {
|
||||
configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseTrue
|
||||
configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue
|
||||
} else {
|
||||
configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseFalse
|
||||
configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseFalse
|
||||
}
|
||||
|
||||
// TODO Init might not make it into the specgen and therefore is not available here. We should deal
|
||||
|
@ -21,7 +21,6 @@ var _ = Describe("Podman generate kube", func() {
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
Skip(v2fail)
|
||||
tempdir, err = CreateTempDirInTempDir()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
|
@ -217,7 +217,6 @@ var _ = Describe("Podman generate kube", func() {
|
||||
)
|
||||
|
||||
BeforeEach(func() {
|
||||
Skip(v2fail)
|
||||
tempdir, err = CreateTempDirInTempDir()
|
||||
if err != nil {
|
||||
os.Exit(1)
|
||||
|
Reference in New Issue
Block a user