mirror of
https://github.com/containers/podman.git
synced 2025-06-19 00:06:43 +08:00

Add the `podman generate kube` and `podman play kube` command. The code has largely been copied from Podman v1 but restructured to not leak the K8s core API into the (remote) client. Both commands are added in the same commit to allow for enabling the tests at the same time. Move some exports from `cmd/podman/common` to the appropriate places in the backend to avoid circular dependencies. Move definitions of label annotations to `libpod/define` and set the security-opt labels in the frontend to make kube tests pass. Implement rest endpoints, bindings and the tunnel interface. Signed-off-by: Valentin Rothberg <rothberg@redhat.com>
464 lines
14 KiB
Go
464 lines
14 KiB
Go
package createconfig
|
|
|
|
import (
|
|
"net"
|
|
"os"
|
|
"strconv"
|
|
"strings"
|
|
|
|
"github.com/containers/libpod/libpod"
|
|
"github.com/containers/libpod/libpod/define"
|
|
"github.com/containers/libpod/pkg/cgroups"
|
|
"github.com/cri-o/ocicni/pkg/ocicni"
|
|
"github.com/docker/go-connections/nat"
|
|
spec "github.com/opencontainers/runtime-spec/specs-go"
|
|
"github.com/opencontainers/runtime-tools/generate"
|
|
"github.com/pkg/errors"
|
|
"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
|
|
var err error
|
|
if len(c.PortBindings) > 0 {
|
|
portBindings, err = NatToOCIPortBindings(c.PortBindings)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "unable to create port bindings")
|
|
}
|
|
}
|
|
|
|
options := make([]libpod.CtrCreateOption, 0)
|
|
userNetworks := c.NetMode.UserDefined()
|
|
networks := make([]string, 0)
|
|
|
|
if IsPod(userNetworks) {
|
|
userNetworks = ""
|
|
}
|
|
if userNetworks != "" {
|
|
for _, netName := range strings.Split(userNetworks, ",") {
|
|
if netName == "" {
|
|
return nil, errors.Errorf("container networks %q invalid", userNetworks)
|
|
}
|
|
networks = append(networks, netName)
|
|
}
|
|
}
|
|
|
|
switch {
|
|
case c.NetMode.IsNS():
|
|
ns := c.NetMode.NS()
|
|
if ns == "" {
|
|
return nil, errors.Errorf("invalid empty user-defined network namespace")
|
|
}
|
|
_, err := os.Stat(ns)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
case c.NetMode.IsContainer():
|
|
connectedCtr, err := runtime.LookupContainer(c.NetMode.Container())
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "container %q not found", c.NetMode.Container())
|
|
}
|
|
options = append(options, libpod.WithNetNSFrom(connectedCtr))
|
|
case !c.NetMode.IsHost() && !c.NetMode.IsNone():
|
|
postConfigureNetNS := userns.getPostConfigureNetNS()
|
|
options = append(options, libpod.WithNetNS(portBindings, postConfigureNetNS, string(c.NetMode), networks))
|
|
}
|
|
|
|
if len(c.DNSSearch) > 0 {
|
|
options = append(options, libpod.WithDNSSearch(c.DNSSearch))
|
|
}
|
|
if len(c.DNSServers) > 0 {
|
|
if len(c.DNSServers) == 1 && strings.ToLower(c.DNSServers[0]) == "none" {
|
|
options = append(options, libpod.WithUseImageResolvConf())
|
|
} else {
|
|
options = append(options, libpod.WithDNS(c.DNSServers))
|
|
}
|
|
}
|
|
if len(c.DNSOpt) > 0 {
|
|
options = append(options, libpod.WithDNSOption(c.DNSOpt))
|
|
}
|
|
if c.IPAddress != "" {
|
|
ip := net.ParseIP(c.IPAddress)
|
|
if ip == nil {
|
|
return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as IP address", c.IPAddress)
|
|
} else if ip.To4() == nil {
|
|
return nil, errors.Wrapf(define.ErrInvalidArg, "%s is not an IPv4 address", c.IPAddress)
|
|
}
|
|
options = append(options, libpod.WithStaticIP(ip))
|
|
}
|
|
|
|
if c.MacAddress != "" {
|
|
mac, err := net.ParseMAC(c.MacAddress)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(define.ErrInvalidArg, "cannot parse %s as MAC address: %v", c.MacAddress, err)
|
|
}
|
|
options = append(options, libpod.WithStaticMAC(mac))
|
|
}
|
|
|
|
return options, nil
|
|
}
|
|
|
|
// ConfigureGenerator configures the generator based according to the current
|
|
// state of the NetworkConfig.
|
|
func (c *NetworkConfig) ConfigureGenerator(g *generate.Generator) error {
|
|
netMode := c.NetMode
|
|
netCtr := netMode.Container()
|
|
switch {
|
|
case netMode.IsHost():
|
|
logrus.Debug("Using host netmode")
|
|
if err := g.RemoveLinuxNamespace(string(spec.NetworkNamespace)); err != nil {
|
|
return err
|
|
}
|
|
case netMode.IsNone():
|
|
logrus.Debug("Using none netmode")
|
|
case netMode.IsBridge():
|
|
logrus.Debug("Using bridge netmode")
|
|
case netCtr != "":
|
|
logrus.Debugf("using container %s netmode", netCtr)
|
|
case IsNS(string(netMode)):
|
|
logrus.Debug("Using ns netmode")
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.NetworkNamespace), NS(string(netMode))); err != nil {
|
|
return err
|
|
}
|
|
case IsPod(string(netMode)):
|
|
logrus.Debug("Using pod netmode, unless pod is not sharing")
|
|
case netMode.IsSlirp4netns():
|
|
logrus.Debug("Using slirp4netns netmode")
|
|
case netMode.IsUserDefined():
|
|
logrus.Debug("Using user defined netmode")
|
|
default:
|
|
return errors.Errorf("unknown network mode")
|
|
}
|
|
|
|
if c.HTTPProxy {
|
|
for _, envSpec := range []string{
|
|
"http_proxy",
|
|
"HTTP_PROXY",
|
|
"https_proxy",
|
|
"HTTPS_PROXY",
|
|
"ftp_proxy",
|
|
"FTP_PROXY",
|
|
"no_proxy",
|
|
"NO_PROXY",
|
|
} {
|
|
envVal := os.Getenv(envSpec)
|
|
if envVal != "" {
|
|
g.AddProcessEnv(envSpec, envVal)
|
|
}
|
|
}
|
|
}
|
|
|
|
if g.Config.Annotations == nil {
|
|
g.Config.Annotations = make(map[string]string)
|
|
}
|
|
|
|
if c.PublishAll {
|
|
g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseTrue
|
|
} else {
|
|
g.Config.Annotations[define.InspectAnnotationPublishAll] = define.InspectResponseFalse
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// NatToOCIPortBindings iterates a nat.portmap slice and creates []ocicni portmapping slice
|
|
func NatToOCIPortBindings(ports nat.PortMap) ([]ocicni.PortMapping, error) {
|
|
var portBindings []ocicni.PortMapping
|
|
for containerPb, hostPb := range ports {
|
|
var pm ocicni.PortMapping
|
|
pm.ContainerPort = int32(containerPb.Int())
|
|
for _, i := range hostPb {
|
|
var hostPort int
|
|
var err error
|
|
pm.HostIP = i.HostIP
|
|
if i.HostPort == "" {
|
|
hostPort = containerPb.Int()
|
|
} else {
|
|
hostPort, err = strconv.Atoi(i.HostPort)
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "unable to convert host port to integer")
|
|
}
|
|
}
|
|
|
|
pm.HostPort = int32(hostPort)
|
|
pm.Protocol = containerPb.Proto()
|
|
portBindings = append(portBindings, pm)
|
|
}
|
|
}
|
|
return portBindings, nil
|
|
}
|
|
|
|
// ToCreateOptions converts the input to container create options.
|
|
func (c *CgroupConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
|
|
options := make([]libpod.CtrCreateOption, 0)
|
|
if c.CgroupMode.IsNS() {
|
|
ns := c.CgroupMode.NS()
|
|
if ns == "" {
|
|
return nil, errors.Errorf("invalid empty user-defined network namespace")
|
|
}
|
|
_, err := os.Stat(ns)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
} else if c.CgroupMode.IsContainer() {
|
|
connectedCtr, err := runtime.LookupContainer(c.CgroupMode.Container())
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "container %q not found", c.CgroupMode.Container())
|
|
}
|
|
options = append(options, libpod.WithCgroupNSFrom(connectedCtr))
|
|
}
|
|
|
|
if c.CgroupParent != "" {
|
|
options = append(options, libpod.WithCgroupParent(c.CgroupParent))
|
|
}
|
|
|
|
if c.Cgroups != "" {
|
|
options = append(options, libpod.WithCgroupsMode(c.Cgroups))
|
|
}
|
|
|
|
return options, nil
|
|
}
|
|
|
|
// ToCreateOptions converts the input to container create options.
|
|
func (c *UserConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
|
|
options := make([]libpod.CtrCreateOption, 0)
|
|
switch {
|
|
case c.UsernsMode.IsNS():
|
|
ns := c.UsernsMode.NS()
|
|
if ns == "" {
|
|
return nil, errors.Errorf("invalid empty user-defined user namespace")
|
|
}
|
|
_, err := os.Stat(ns)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
options = append(options, libpod.WithIDMappings(*c.IDMappings))
|
|
case c.UsernsMode.IsContainer():
|
|
connectedCtr, err := runtime.LookupContainer(c.UsernsMode.Container())
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "container %q not found", c.UsernsMode.Container())
|
|
}
|
|
options = append(options, libpod.WithUserNSFrom(connectedCtr))
|
|
default:
|
|
options = append(options, libpod.WithIDMappings(*c.IDMappings))
|
|
}
|
|
|
|
options = append(options, libpod.WithUser(c.User))
|
|
options = append(options, libpod.WithGroups(c.GroupAdd))
|
|
|
|
return options, nil
|
|
}
|
|
|
|
// ConfigureGenerator configures the generator according to the current state
|
|
// of the UserConfig.
|
|
func (c *UserConfig) ConfigureGenerator(g *generate.Generator) error {
|
|
if IsNS(string(c.UsernsMode)) {
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), NS(string(c.UsernsMode))); err != nil {
|
|
return err
|
|
}
|
|
// runc complains if no mapping is specified, even if we join another ns. So provide a dummy mapping
|
|
g.AddLinuxUIDMapping(uint32(0), uint32(0), uint32(1))
|
|
g.AddLinuxGIDMapping(uint32(0), uint32(0), uint32(1))
|
|
}
|
|
|
|
if (len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0) && !c.UsernsMode.IsHost() {
|
|
if err := g.AddOrReplaceLinuxNamespace(string(spec.UserNamespace), ""); err != nil {
|
|
return err
|
|
}
|
|
}
|
|
for _, uidmap := range c.IDMappings.UIDMap {
|
|
g.AddLinuxUIDMapping(uint32(uidmap.HostID), uint32(uidmap.ContainerID), uint32(uidmap.Size))
|
|
}
|
|
for _, gidmap := range c.IDMappings.GIDMap {
|
|
g.AddLinuxGIDMapping(uint32(gidmap.HostID), uint32(gidmap.ContainerID), uint32(gidmap.Size))
|
|
}
|
|
return nil
|
|
}
|
|
|
|
func (c *UserConfig) getPostConfigureNetNS() bool {
|
|
hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || c.UsernsMode.IsAuto() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
|
|
postConfigureNetNS := hasUserns && !c.UsernsMode.IsHost()
|
|
return postConfigureNetNS
|
|
}
|
|
|
|
// InNS returns true if the UserConfig indicates to be in a dedicated user
|
|
// namespace.
|
|
func (c *UserConfig) InNS(isRootless bool) bool {
|
|
hasUserns := c.UsernsMode.IsContainer() || c.UsernsMode.IsNS() || c.UsernsMode.IsAuto() || len(c.IDMappings.UIDMap) > 0 || len(c.IDMappings.GIDMap) > 0
|
|
return isRootless || (hasUserns && !c.UsernsMode.IsHost())
|
|
}
|
|
|
|
// ToCreateOptions converts the input to container create options.
|
|
func (c *IpcConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
|
|
options := make([]libpod.CtrCreateOption, 0)
|
|
if c.IpcMode.IsHost() {
|
|
options = append(options, libpod.WithShmDir("/dev/shm"))
|
|
} else if c.IpcMode.IsContainer() {
|
|
connectedCtr, err := runtime.LookupContainer(c.IpcMode.Container())
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "container %q not found", c.IpcMode.Container())
|
|
}
|
|
|
|
options = append(options, libpod.WithIPCNSFrom(connectedCtr))
|
|
options = append(options, libpod.WithShmDir(connectedCtr.ShmDir()))
|
|
}
|
|
|
|
return options, nil
|
|
}
|
|
|
|
// ConfigureGenerator configures the generator according to the current state
|
|
// of the IpcConfig.
|
|
func (c *IpcConfig) ConfigureGenerator(g *generate.Generator) error {
|
|
ipcMode := c.IpcMode
|
|
if IsNS(string(ipcMode)) {
|
|
return g.AddOrReplaceLinuxNamespace(string(spec.IPCNamespace), NS(string(ipcMode)))
|
|
}
|
|
if ipcMode.IsHost() {
|
|
return g.RemoveLinuxNamespace(string(spec.IPCNamespace))
|
|
}
|
|
if ipcCtr := ipcMode.Container(); ipcCtr != "" {
|
|
logrus.Debugf("Using container %s ipcmode", ipcCtr)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// ConfigureGenerator configures the generator according to the current state
|
|
// of the CgroupConfig.
|
|
func (c *CgroupConfig) ConfigureGenerator(g *generate.Generator) error {
|
|
cgroupMode := c.CgroupMode
|
|
if cgroupMode.IsDefaultValue() {
|
|
// If the value is not specified, default to "private" on cgroups v2 and "host" on cgroups v1.
|
|
unified, err := cgroups.IsCgroup2UnifiedMode()
|
|
if err != nil {
|
|
return err
|
|
}
|
|
if unified {
|
|
cgroupMode = "private"
|
|
} else {
|
|
cgroupMode = "host"
|
|
}
|
|
}
|
|
if cgroupMode.IsNS() {
|
|
return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), NS(string(cgroupMode)))
|
|
}
|
|
if cgroupMode.IsHost() {
|
|
return g.RemoveLinuxNamespace(string(spec.CgroupNamespace))
|
|
}
|
|
if cgroupMode.IsPrivate() {
|
|
return g.AddOrReplaceLinuxNamespace(string(spec.CgroupNamespace), "")
|
|
}
|
|
if cgCtr := cgroupMode.Container(); cgCtr != "" {
|
|
logrus.Debugf("Using container %s cgroup mode", cgCtr)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ToCreateOptions converts the input to container create options.
|
|
func (c *PidConfig) ToCreateOptions(runtime *libpod.Runtime) ([]libpod.CtrCreateOption, error) {
|
|
options := make([]libpod.CtrCreateOption, 0)
|
|
if c.PidMode.IsContainer() {
|
|
connectedCtr, err := runtime.LookupContainer(c.PidMode.Container())
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "container %q not found", c.PidMode.Container())
|
|
}
|
|
|
|
options = append(options, libpod.WithPIDNSFrom(connectedCtr))
|
|
}
|
|
|
|
return options, nil
|
|
}
|
|
|
|
// ConfigureGenerator configures the generator according to the current state
|
|
// of the PidConfig.
|
|
func (c *PidConfig) ConfigureGenerator(g *generate.Generator) error {
|
|
pidMode := c.PidMode
|
|
if IsNS(string(pidMode)) {
|
|
return g.AddOrReplaceLinuxNamespace(string(spec.PIDNamespace), NS(string(pidMode)))
|
|
}
|
|
if pidMode.IsHost() {
|
|
return g.RemoveLinuxNamespace(string(spec.PIDNamespace))
|
|
}
|
|
if pidCtr := pidMode.Container(); pidCtr != "" {
|
|
logrus.Debugf("using container %s pidmode", pidCtr)
|
|
}
|
|
if IsPod(string(pidMode)) {
|
|
logrus.Debug("using pod pidmode")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// ToCreateOptions converts the input to container create options.
|
|
func (c *UtsConfig) ToCreateOptions(runtime *libpod.Runtime, pod *libpod.Pod) ([]libpod.CtrCreateOption, error) {
|
|
options := make([]libpod.CtrCreateOption, 0)
|
|
if IsPod(string(c.UtsMode)) {
|
|
options = append(options, libpod.WithUTSNSFromPod(pod))
|
|
}
|
|
if c.UtsMode.IsContainer() {
|
|
connectedCtr, err := runtime.LookupContainer(c.UtsMode.Container())
|
|
if err != nil {
|
|
return nil, errors.Wrapf(err, "container %q not found", c.UtsMode.Container())
|
|
}
|
|
|
|
options = append(options, libpod.WithUTSNSFrom(connectedCtr))
|
|
}
|
|
if c.NoHosts {
|
|
options = append(options, libpod.WithUseImageHosts())
|
|
}
|
|
if len(c.HostAdd) > 0 && !c.NoHosts {
|
|
options = append(options, libpod.WithHosts(c.HostAdd))
|
|
}
|
|
|
|
return options, nil
|
|
}
|
|
|
|
// ConfigureGenerator configures the generator according to the current state
|
|
// of the UtsConfig.
|
|
func (c *UtsConfig) ConfigureGenerator(g *generate.Generator, net *NetworkConfig, runtime *libpod.Runtime) error {
|
|
hostname := c.Hostname
|
|
utsCtrID := c.UtsMode.Container()
|
|
var err error
|
|
if hostname == "" {
|
|
switch {
|
|
case utsCtrID != "":
|
|
utsCtr, err := runtime.LookupContainer(utsCtrID)
|
|
if err != nil {
|
|
return errors.Wrapf(err, "unable to retrieve hostname from dependency container %s", utsCtrID)
|
|
}
|
|
hostname = utsCtr.Hostname()
|
|
case net.NetMode.IsHost() || c.UtsMode.IsHost():
|
|
hostname, err = os.Hostname()
|
|
if err != nil {
|
|
return errors.Wrap(err, "unable to retrieve hostname of the host")
|
|
}
|
|
default:
|
|
logrus.Debug("No hostname set; container's hostname will default to runtime default")
|
|
}
|
|
}
|
|
g.RemoveHostname()
|
|
if c.Hostname != "" || !c.UtsMode.IsHost() {
|
|
// Set the hostname in the OCI configuration only
|
|
// if specified by the user or if we are creating
|
|
// a new UTS namespace.
|
|
g.SetHostname(hostname)
|
|
}
|
|
g.AddProcessEnv("HOSTNAME", hostname)
|
|
|
|
utsMode := c.UtsMode
|
|
if IsNS(string(utsMode)) {
|
|
return g.AddOrReplaceLinuxNamespace(string(spec.UTSNamespace), NS(string(utsMode)))
|
|
}
|
|
if utsMode.IsHost() {
|
|
return g.RemoveLinuxNamespace(string(spec.UTSNamespace))
|
|
}
|
|
if utsCtr := utsMode.Container(); utsCtr != "" {
|
|
logrus.Debugf("using container %s utsmode", utsCtr)
|
|
}
|
|
return nil
|
|
}
|