mirror of
https://github.com/containers/podman.git
synced 2025-06-20 09:03:43 +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":
|
||||
// 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