mirror of
https://github.com/containers/podman.git
synced 2025-06-21 17:38:12 +08:00
Merge pull request #6092 from vrothberg/v2-kube
add {generate,play} kube
This commit is contained in:
@ -534,10 +534,13 @@ func FillOutSpecGen(s *specgen.SpecGenerator, c *ContainerCLIOpts, args []string
|
|||||||
case "label":
|
case "label":
|
||||||
// TODO selinux opts and label opts are the same thing
|
// TODO selinux opts and label opts are the same thing
|
||||||
s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1])
|
s.ContainerSecurityConfig.SelinuxOpts = append(s.ContainerSecurityConfig.SelinuxOpts, con[1])
|
||||||
|
s.Annotations[define.InspectAnnotationLabel] = con[1]
|
||||||
case "apparmor":
|
case "apparmor":
|
||||||
s.ContainerSecurityConfig.ApparmorProfile = con[1]
|
s.ContainerSecurityConfig.ApparmorProfile = con[1]
|
||||||
|
s.Annotations[define.InspectAnnotationApparmor] = con[1]
|
||||||
case "seccomp":
|
case "seccomp":
|
||||||
s.SeccompProfilePath = con[1]
|
s.SeccompProfilePath = con[1]
|
||||||
|
s.Annotations[define.InspectAnnotationSeccomp] = con[1]
|
||||||
default:
|
default:
|
||||||
return fmt.Errorf("invalid --security-opt 2: %q", opt)
|
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() {
|
func init() {
|
||||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
Mode: []entities.EngineMode{entities.ABIMode},
|
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
||||||
Command: generateCmd,
|
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() {
|
func init() {
|
||||||
registry.Commands = append(registry.Commands, registry.CliCommand{
|
registry.Commands = append(registry.Commands, registry.CliCommand{
|
||||||
Mode: []entities.EngineMode{entities.ABIMode, entities.TunnelMode},
|
Mode: []entities.EngineMode{entities.ABIMode},
|
||||||
Command: systemdCmd,
|
Command: systemdCmd,
|
||||||
Parent: generateCmd,
|
Parent: generateCmd,
|
||||||
})
|
})
|
||||||
|
@ -10,6 +10,7 @@ import (
|
|||||||
_ "github.com/containers/libpod/cmd/podman/images"
|
_ "github.com/containers/libpod/cmd/podman/images"
|
||||||
_ "github.com/containers/libpod/cmd/podman/manifest"
|
_ "github.com/containers/libpod/cmd/podman/manifest"
|
||||||
_ "github.com/containers/libpod/cmd/podman/networks"
|
_ "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/pods"
|
||||||
"github.com/containers/libpod/cmd/podman/registry"
|
"github.com/containers/libpod/cmd/podman/registry"
|
||||||
_ "github.com/containers/libpod/cmd/podman/system"
|
_ "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/cmd/podman/validate"
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
"github.com/containers/libpod/pkg/errorhandling"
|
"github.com/containers/libpod/pkg/errorhandling"
|
||||||
|
createconfig "github.com/containers/libpod/pkg/spec"
|
||||||
"github.com/containers/libpod/pkg/specgen"
|
"github.com/containers/libpod/pkg/specgen"
|
||||||
"github.com/containers/libpod/pkg/util"
|
"github.com/containers/libpod/pkg/util"
|
||||||
"github.com/pkg/errors"
|
"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.Name, "name", "n", "", "Assign a name to the pod")
|
||||||
flags.StringVarP(&createOptions.Hostname, "hostname", "", "", "Set a hostname 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(&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 {
|
func create(cmd *cobra.Command, args []string) error {
|
||||||
|
@ -1221,5 +1221,5 @@ func (c *Container) AutoRemove() bool {
|
|||||||
if spec.Annotations == nil {
|
if spec.Annotations == nil {
|
||||||
return false
|
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"
|
"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.
|
// inspectLocked inspects a container for low-level information.
|
||||||
// The caller must held c.lock.
|
// The caller must held c.lock.
|
||||||
func (c *Container) inspectLocked(size bool) (*define.InspectContainerData, error) {
|
func (c *Container) inspectLocked(size bool) (*define.InspectContainerData, error) {
|
||||||
@ -452,26 +385,26 @@ func (c *Container) generateInspectContainerHostConfig(ctrSpec *spec.Spec, named
|
|||||||
|
|
||||||
// Annotations
|
// Annotations
|
||||||
if ctrSpec.Annotations != nil {
|
if ctrSpec.Annotations != nil {
|
||||||
hostConfig.ContainerIDFile = ctrSpec.Annotations[InspectAnnotationCIDFile]
|
hostConfig.ContainerIDFile = ctrSpec.Annotations[define.InspectAnnotationCIDFile]
|
||||||
if ctrSpec.Annotations[InspectAnnotationAutoremove] == InspectResponseTrue {
|
if ctrSpec.Annotations[define.InspectAnnotationAutoremove] == define.InspectResponseTrue {
|
||||||
hostConfig.AutoRemove = true
|
hostConfig.AutoRemove = true
|
||||||
}
|
}
|
||||||
if ctrs, ok := ctrSpec.Annotations[InspectAnnotationVolumesFrom]; ok {
|
if ctrs, ok := ctrSpec.Annotations[define.InspectAnnotationVolumesFrom]; ok {
|
||||||
hostConfig.VolumesFrom = strings.Split(ctrs, ",")
|
hostConfig.VolumesFrom = strings.Split(ctrs, ",")
|
||||||
}
|
}
|
||||||
if ctrSpec.Annotations[InspectAnnotationPrivileged] == InspectResponseTrue {
|
if ctrSpec.Annotations[define.InspectAnnotationPrivileged] == define.InspectResponseTrue {
|
||||||
hostConfig.Privileged = true
|
hostConfig.Privileged = true
|
||||||
}
|
}
|
||||||
if ctrSpec.Annotations[InspectAnnotationInit] == InspectResponseTrue {
|
if ctrSpec.Annotations[define.InspectAnnotationInit] == define.InspectResponseTrue {
|
||||||
hostConfig.Init = true
|
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))
|
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))
|
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))
|
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
|
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 {
|
if len(opts) == 2 {
|
||||||
switch opts[0] {
|
switch opts[0] {
|
||||||
case "type":
|
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
|
Body handlers.LibpodImagesRemoveReport
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// PlayKube response
|
||||||
|
// swagger:response DocsLibpodPlayKubeResponse
|
||||||
|
type swagLibpodPlayKubeResponse struct {
|
||||||
|
// in:body
|
||||||
|
Body entities.PlayKubeReport
|
||||||
|
}
|
||||||
|
|
||||||
// Delete response
|
// Delete response
|
||||||
// swagger:response DocsImageDeleteResponse
|
// swagger:response DocsImageDeleteResponse
|
||||||
type swagImageDeleteResponse struct {
|
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.registerDistributionHandlers,
|
||||||
server.registerEventsHandlers,
|
server.registerEventsHandlers,
|
||||||
server.registerExecHandlers,
|
server.registerExecHandlers,
|
||||||
|
server.registerGenerateHandlers,
|
||||||
server.registerHealthCheckHandlers,
|
server.registerHealthCheckHandlers,
|
||||||
server.registerImagesHandlers,
|
server.registerImagesHandlers,
|
||||||
server.registerInfoHandlers,
|
server.registerInfoHandlers,
|
||||||
server.registerManifestHandlers,
|
server.registerManifestHandlers,
|
||||||
server.registerMonitorHandlers,
|
server.registerMonitorHandlers,
|
||||||
server.registerPingHandlers,
|
server.registerPingHandlers,
|
||||||
|
server.registerPlayHandlers,
|
||||||
server.registerPluginsHandlers,
|
server.registerPluginsHandlers,
|
||||||
server.registerPodsHandlers,
|
server.registerPodsHandlers,
|
||||||
server.RegisterSwaggerHandlers,
|
server.RegisterSwaggerHandlers,
|
||||||
|
@ -1,4 +1,32 @@
|
|||||||
package generate
|
package generate
|
||||||
|
|
||||||
func GenerateKube() {}
|
import (
|
||||||
func GenerateSystemd() {}
|
"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
|
package play
|
||||||
|
|
||||||
import "github.com/containers/libpod/pkg/bindings"
|
import (
|
||||||
|
"context"
|
||||||
|
"net/http"
|
||||||
|
"net/url"
|
||||||
|
"os"
|
||||||
|
"strconv"
|
||||||
|
|
||||||
func PlayKube() error {
|
"github.com/containers/image/v5/types"
|
||||||
return bindings.ErrNotImplemented
|
"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)
|
ContainerWait(ctx context.Context, namesOrIds []string, options WaitOptions) ([]WaitReport, error)
|
||||||
Events(ctx context.Context, opts EventsOptions) error
|
Events(ctx context.Context, opts EventsOptions) error
|
||||||
GenerateSystemd(ctx context.Context, nameOrID string, opts GenerateSystemdOptions) (*GenerateSystemdReport, 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)
|
SystemPrune(ctx context.Context, options SystemPruneOptions) (*SystemPruneReport, error)
|
||||||
HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error)
|
HealthCheckRun(ctx context.Context, nameOrId string, options HealthCheckOptions) (*define.HealthCheckResults, error)
|
||||||
Info(ctx context.Context) (*define.Info, 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)
|
NetworkInspect(ctx context.Context, namesOrIds []string, options NetworkInspectOptions) ([]NetworkInspectReport, error)
|
||||||
NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, error)
|
NetworkList(ctx context.Context, options NetworkListOptions) ([]*NetworkListReport, error)
|
||||||
NetworkRm(ctx context.Context, namesOrIds []string, options NetworkRmOptions) ([]*NetworkRmReport, 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)
|
PodCreate(ctx context.Context, opts PodCreateOptions) (*PodCreateReport, error)
|
||||||
PodExists(ctx context.Context, nameOrId string) (*BoolReport, error)
|
PodExists(ctx context.Context, nameOrId string) (*BoolReport, error)
|
||||||
PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error)
|
PodInspect(ctx context.Context, options PodInspectOptions) (*PodInspectReport, error)
|
||||||
|
@ -1,5 +1,7 @@
|
|||||||
package entities
|
package entities
|
||||||
|
|
||||||
|
import "io"
|
||||||
|
|
||||||
// GenerateSystemdOptions control the generation of systemd unit files.
|
// GenerateSystemdOptions control the generation of systemd unit files.
|
||||||
type GenerateSystemdOptions struct {
|
type GenerateSystemdOptions struct {
|
||||||
// Files - generate files instead of printing to stdout.
|
// Files - generate files instead of printing to stdout.
|
||||||
@ -20,3 +22,15 @@ type GenerateSystemdReport struct {
|
|||||||
// entire content.
|
// entire content.
|
||||||
Output string
|
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
|
package abi
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"bytes"
|
||||||
"context"
|
"context"
|
||||||
"fmt"
|
"fmt"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/libpod/define"
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
"github.com/containers/libpod/pkg/systemd/generate"
|
"github.com/containers/libpod/pkg/systemd/generate"
|
||||||
|
"github.com/ghodss/yaml"
|
||||||
"github.com/pkg/errors"
|
"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) {
|
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)
|
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 (
|
import (
|
||||||
"context"
|
"context"
|
||||||
|
|
||||||
|
"github.com/containers/libpod/pkg/bindings/generate"
|
||||||
"github.com/containers/libpod/pkg/domain/entities"
|
"github.com/containers/libpod/pkg/domain/entities"
|
||||||
"github.com/pkg/errors"
|
"github.com/pkg/errors"
|
||||||
)
|
)
|
||||||
@ -10,3 +11,7 @@ import (
|
|||||||
func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) {
|
func (ic *ContainerEngine) GenerateSystemd(ctx context.Context, nameOrID string, options entities.GenerateSystemdOptions) (*entities.GenerateSystemdReport, error) {
|
||||||
return nil, errors.New("not implemented for tunnel")
|
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"
|
"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.
|
// ToCreateOptions converts the input to a slice of container create options.
|
||||||
func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserConfig) ([]libpod.CtrCreateOption, error) {
|
func (c *NetworkConfig) ToCreateOptions(runtime *libpod.Runtime, userns *UserConfig) ([]libpod.CtrCreateOption, error) {
|
||||||
var portBindings []ocicni.PortMapping
|
var portBindings []ocicni.PortMapping
|
||||||
@ -154,9 +158,9 @@ func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if c.PublishAll {
|
if c.PublishAll {
|
||||||
g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue
|
g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue
|
||||||
} else {
|
} else {
|
||||||
g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse
|
g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containers/common/pkg/capabilities"
|
"github.com/containers/common/pkg/capabilities"
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/libpod/define"
|
||||||
"github.com/containers/libpod/pkg/util"
|
"github.com/containers/libpod/pkg/util"
|
||||||
"github.com/opencontainers/runtime-tools/generate"
|
"github.com/opencontainers/runtime-tools/generate"
|
||||||
"github.com/opencontainers/selinux/go-selinux/label"
|
"github.com/opencontainers/selinux/go-selinux/label"
|
||||||
@ -184,11 +185,11 @@ func (c *SecurityConfig) ConfigureGenerator(g *generate.Generator, user *UserCon
|
|||||||
}
|
}
|
||||||
switch splitOpt[0] {
|
switch splitOpt[0] {
|
||||||
case "label":
|
case "label":
|
||||||
configSpec.Annotations[libpod.InspectAnnotationLabel] = splitOpt[1]
|
configSpec.Annotations[define.InspectAnnotationLabel] = splitOpt[1]
|
||||||
case "seccomp":
|
case "seccomp":
|
||||||
configSpec.Annotations[libpod.InspectAnnotationSeccomp] = splitOpt[1]
|
configSpec.Annotations[define.InspectAnnotationSeccomp] = splitOpt[1]
|
||||||
case "apparmor":
|
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"
|
cconfig "github.com/containers/common/pkg/config"
|
||||||
"github.com/containers/common/pkg/sysinfo"
|
"github.com/containers/common/pkg/sysinfo"
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/libpod/define"
|
||||||
"github.com/containers/libpod/pkg/cgroups"
|
"github.com/containers/libpod/pkg/cgroups"
|
||||||
"github.com/containers/libpod/pkg/env"
|
"github.com/containers/libpod/pkg/env"
|
||||||
"github.com/containers/libpod/pkg/rootless"
|
"github.com/containers/libpod/pkg/rootless"
|
||||||
@ -436,29 +437,29 @@ func (config *CreateConfig) createConfigToOCISpec(runtime *libpod.Runtime, userM
|
|||||||
}
|
}
|
||||||
|
|
||||||
if config.CidFile != "" {
|
if config.CidFile != "" {
|
||||||
configSpec.Annotations[libpod.InspectAnnotationCIDFile] = config.CidFile
|
configSpec.Annotations[define.InspectAnnotationCIDFile] = config.CidFile
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Rm {
|
if config.Rm {
|
||||||
configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseTrue
|
configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue
|
||||||
} else {
|
} else {
|
||||||
configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseFalse
|
configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(config.VolumesFrom) > 0 {
|
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 {
|
if config.Security.Privileged {
|
||||||
configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseTrue
|
configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue
|
||||||
} else {
|
} else {
|
||||||
configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseFalse
|
configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseFalse
|
||||||
}
|
}
|
||||||
|
|
||||||
if config.Init {
|
if config.Init {
|
||||||
configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseTrue
|
configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseTrue
|
||||||
} else {
|
} else {
|
||||||
configSpec.Annotations[libpod.InspectAnnotationInit] = libpod.InspectResponseFalse
|
configSpec.Annotations[define.InspectAnnotationInit] = define.InspectResponseFalse
|
||||||
}
|
}
|
||||||
|
|
||||||
return configSpec, nil
|
return configSpec, nil
|
||||||
|
@ -438,9 +438,9 @@ func specConfigureNamespaces(s *specgen.SpecGenerator, g *generate.Generator, rt
|
|||||||
g.Config.Annotations = make(map[string]string)
|
g.Config.Annotations = make(map[string]string)
|
||||||
}
|
}
|
||||||
if s.PublishExposedPorts {
|
if s.PublishExposedPorts {
|
||||||
g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseTrue
|
g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue
|
||||||
} else {
|
} else {
|
||||||
g.Config.Annotations[libpod.InspectAnnotationPublishAll] = libpod.InspectResponseFalse
|
g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@ -6,6 +6,7 @@ import (
|
|||||||
|
|
||||||
"github.com/containers/common/pkg/config"
|
"github.com/containers/common/pkg/config"
|
||||||
"github.com/containers/libpod/libpod"
|
"github.com/containers/libpod/libpod"
|
||||||
|
"github.com/containers/libpod/libpod/define"
|
||||||
"github.com/containers/libpod/libpod/image"
|
"github.com/containers/libpod/libpod/image"
|
||||||
"github.com/containers/libpod/pkg/rootless"
|
"github.com/containers/libpod/pkg/rootless"
|
||||||
"github.com/containers/libpod/pkg/specgen"
|
"github.com/containers/libpod/pkg/specgen"
|
||||||
@ -327,19 +328,19 @@ func SpecGenToOCI(ctx context.Context, s *specgen.SpecGenerator, rt *libpod.Runt
|
|||||||
//}
|
//}
|
||||||
|
|
||||||
if s.Remove {
|
if s.Remove {
|
||||||
configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseTrue
|
configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseTrue
|
||||||
} else {
|
} else {
|
||||||
configSpec.Annotations[libpod.InspectAnnotationAutoremove] = libpod.InspectResponseFalse
|
configSpec.Annotations[define.InspectAnnotationAutoremove] = define.InspectResponseFalse
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(s.VolumesFrom) > 0 {
|
if len(s.VolumesFrom) > 0 {
|
||||||
configSpec.Annotations[libpod.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ",")
|
configSpec.Annotations[define.InspectAnnotationVolumesFrom] = strings.Join(s.VolumesFrom, ",")
|
||||||
}
|
}
|
||||||
|
|
||||||
if s.Privileged {
|
if s.Privileged {
|
||||||
configSpec.Annotations[libpod.InspectAnnotationPrivileged] = libpod.InspectResponseTrue
|
configSpec.Annotations[define.InspectAnnotationPrivileged] = define.InspectResponseTrue
|
||||||
} else {
|
} 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
|
// 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() {
|
BeforeEach(func() {
|
||||||
Skip(v2fail)
|
|
||||||
tempdir, err = CreateTempDirInTempDir()
|
tempdir, err = CreateTempDirInTempDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
@ -217,7 +217,6 @@ var _ = Describe("Podman generate kube", func() {
|
|||||||
)
|
)
|
||||||
|
|
||||||
BeforeEach(func() {
|
BeforeEach(func() {
|
||||||
Skip(v2fail)
|
|
||||||
tempdir, err = CreateTempDirInTempDir()
|
tempdir, err = CreateTempDirInTempDir()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
|
Reference in New Issue
Block a user