mirror of https://github.com/containers/podman.git
add --ip to podman play kube
Add a new --ip flag to podman play kube. This is used to specify a static IP address which should be used for the pod. This option can be specified several times because play kube can create more than one pod. Fixes #8442 Signed-off-by: Paul Holzinger <paul.holzinger@web.de>
This commit is contained in:
parent
b074e80351
commit
d7292dbf27
|
@ -65,6 +65,10 @@ func init() {
|
||||||
flags.StringVar(&kubeOptions.Network, networkFlagName, "", "Connect pod to CNI network(s)")
|
flags.StringVar(&kubeOptions.Network, networkFlagName, "", "Connect pod to CNI network(s)")
|
||||||
_ = kubeCmd.RegisterFlagCompletionFunc(networkFlagName, common.AutocompleteNetworkFlag)
|
_ = kubeCmd.RegisterFlagCompletionFunc(networkFlagName, common.AutocompleteNetworkFlag)
|
||||||
|
|
||||||
|
staticIPFlagName := "ip"
|
||||||
|
flags.IPSliceVar(&kubeOptions.StaticIPs, staticIPFlagName, nil, "Static IP addresses to assign to the pods")
|
||||||
|
_ = kubeCmd.RegisterFlagCompletionFunc(staticIPFlagName, completion.AutocompleteNone)
|
||||||
|
|
||||||
logDriverFlagName := "log-driver"
|
logDriverFlagName := "log-driver"
|
||||||
flags.StringVar(&kubeOptions.LogDriver, logDriverFlagName, "", "Logging driver for the container")
|
flags.StringVar(&kubeOptions.LogDriver, logDriverFlagName, "", "Logging driver for the container")
|
||||||
_ = kubeCmd.RegisterFlagCompletionFunc(logDriverFlagName, common.AutocompleteLogDriver)
|
_ = kubeCmd.RegisterFlagCompletionFunc(logDriverFlagName, common.AutocompleteLogDriver)
|
||||||
|
|
|
@ -62,6 +62,10 @@ The [username[:password]] to use to authenticate with the registry if required.
|
||||||
If one or both values are not supplied, a command line prompt will appear and the
|
If one or both values are not supplied, a command line prompt will appear and the
|
||||||
value can be entered. The password is entered without echo.
|
value can be entered. The password is entered without echo.
|
||||||
|
|
||||||
|
#### **\-\-ip**=*IP address*
|
||||||
|
|
||||||
|
Assign a static ip address to the pod. This option can be specified several times when play kube creates more than one pod.
|
||||||
|
|
||||||
#### **\-\-log-driver**=driver
|
#### **\-\-log-driver**=driver
|
||||||
|
|
||||||
Set logging driver for all created containers.
|
Set logging driver for all created containers.
|
||||||
|
|
|
@ -3,6 +3,7 @@ package libpod
|
||||||
import (
|
import (
|
||||||
"io"
|
"io"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
|
"net"
|
||||||
"net/http"
|
"net/http"
|
||||||
"os"
|
"os"
|
||||||
|
|
||||||
|
@ -20,10 +21,11 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
|
||||||
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
runtime := r.Context().Value("runtime").(*libpod.Runtime)
|
||||||
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
decoder := r.Context().Value("decoder").(*schema.Decoder)
|
||||||
query := struct {
|
query := struct {
|
||||||
Network string `schema:"network"`
|
Network string `schema:"network"`
|
||||||
TLSVerify bool `schema:"tlsVerify"`
|
TLSVerify bool `schema:"tlsVerify"`
|
||||||
LogDriver string `schema:"logDriver"`
|
LogDriver string `schema:"logDriver"`
|
||||||
Start bool `schema:"start"`
|
Start bool `schema:"start"`
|
||||||
|
StaticIPs []string `schema:"staticIPs"`
|
||||||
}{
|
}{
|
||||||
TLSVerify: true,
|
TLSVerify: true,
|
||||||
Start: true,
|
Start: true,
|
||||||
|
@ -35,6 +37,17 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
staticIPs := make([]net.IP, 0, len(query.StaticIPs))
|
||||||
|
for _, ipString := range query.StaticIPs {
|
||||||
|
ip := net.ParseIP(ipString)
|
||||||
|
if ip == nil {
|
||||||
|
utils.Error(w, http.StatusText(http.StatusBadRequest), http.StatusBadRequest,
|
||||||
|
errors.Errorf("Invalid IP address %s", ipString))
|
||||||
|
return
|
||||||
|
}
|
||||||
|
staticIPs = append(staticIPs, ip)
|
||||||
|
}
|
||||||
|
|
||||||
// Fetch the K8s YAML file from the body, and copy it to a temp file.
|
// Fetch the K8s YAML file from the body, and copy it to a temp file.
|
||||||
tmpfile, err := ioutil.TempFile("", "libpod-play-kube.yml")
|
tmpfile, err := ioutil.TempFile("", "libpod-play-kube.yml")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
|
@ -71,6 +84,7 @@ func PlayKube(w http.ResponseWriter, r *http.Request) {
|
||||||
Network: query.Network,
|
Network: query.Network,
|
||||||
Quiet: true,
|
Quiet: true,
|
||||||
LogDriver: query.LogDriver,
|
LogDriver: query.LogDriver,
|
||||||
|
StaticIPs: staticIPs,
|
||||||
}
|
}
|
||||||
if _, found := r.URL.Query()["tlsVerify"]; found {
|
if _, found := r.URL.Query()["tlsVerify"]; found {
|
||||||
options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
|
options.SkipTLSVerify = types.NewOptionalBool(!query.TLSVerify)
|
||||||
|
|
|
@ -34,6 +34,12 @@ func (s *APIServer) registerPlayHandlers(r *mux.Router) error {
|
||||||
// type: boolean
|
// type: boolean
|
||||||
// default: true
|
// default: true
|
||||||
// description: Start the pod after creating it.
|
// description: Start the pod after creating it.
|
||||||
|
// - in: query
|
||||||
|
// name: staticIPs
|
||||||
|
// type: array
|
||||||
|
// description: Static IPs used for the pods.
|
||||||
|
// items:
|
||||||
|
// type: string
|
||||||
// - in: body
|
// - in: body
|
||||||
// name: request
|
// name: request
|
||||||
// description: Kubernetes YAML file.
|
// description: Kubernetes YAML file.
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
package play
|
package play
|
||||||
|
|
||||||
|
import "net"
|
||||||
|
|
||||||
//go:generate go run ../generator/generator.go KubeOptions
|
//go:generate go run ../generator/generator.go KubeOptions
|
||||||
// KubeOptions are optional options for replaying kube YAML files
|
// KubeOptions are optional options for replaying kube YAML files
|
||||||
type KubeOptions struct {
|
type KubeOptions struct {
|
||||||
|
@ -23,6 +25,8 @@ type KubeOptions struct {
|
||||||
// SeccompProfileRoot - path to a directory containing seccomp
|
// SeccompProfileRoot - path to a directory containing seccomp
|
||||||
// profiles.
|
// profiles.
|
||||||
SeccompProfileRoot *string
|
SeccompProfileRoot *string
|
||||||
|
// StaticIPs - Static IP address used by the pod(s).
|
||||||
|
StaticIPs *[]net.IP
|
||||||
// ConfigMaps - slice of pathnames to kubernetes configmap YAMLs.
|
// ConfigMaps - slice of pathnames to kubernetes configmap YAMLs.
|
||||||
ConfigMaps *[]string
|
ConfigMaps *[]string
|
||||||
// LogDriver for the container. For example: journald
|
// LogDriver for the container. For example: journald
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
package play
|
package play
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"net"
|
||||||
"net/url"
|
"net/url"
|
||||||
|
|
||||||
"github.com/containers/podman/v3/pkg/bindings/internal/util"
|
"github.com/containers/podman/v3/pkg/bindings/internal/util"
|
||||||
|
@ -164,6 +165,22 @@ func (o *KubeOptions) GetSeccompProfileRoot() string {
|
||||||
return *o.SeccompProfileRoot
|
return *o.SeccompProfileRoot
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// WithStaticIPs
|
||||||
|
func (o *KubeOptions) WithStaticIPs(value []net.IP) *KubeOptions {
|
||||||
|
v := &value
|
||||||
|
o.StaticIPs = v
|
||||||
|
return o
|
||||||
|
}
|
||||||
|
|
||||||
|
// GetStaticIPs
|
||||||
|
func (o *KubeOptions) GetStaticIPs() []net.IP {
|
||||||
|
var staticIPs []net.IP
|
||||||
|
if o.StaticIPs == nil {
|
||||||
|
return staticIPs
|
||||||
|
}
|
||||||
|
return *o.StaticIPs
|
||||||
|
}
|
||||||
|
|
||||||
// WithConfigMaps
|
// WithConfigMaps
|
||||||
func (o *KubeOptions) WithConfigMaps(value []string) *KubeOptions {
|
func (o *KubeOptions) WithConfigMaps(value []string) *KubeOptions {
|
||||||
v := &value
|
v := &value
|
||||||
|
|
|
@ -1,6 +1,10 @@
|
||||||
package entities
|
package entities
|
||||||
|
|
||||||
import "github.com/containers/image/v5/types"
|
import (
|
||||||
|
"net"
|
||||||
|
|
||||||
|
"github.com/containers/image/v5/types"
|
||||||
|
)
|
||||||
|
|
||||||
// PlayKubeOptions controls playing kube YAML files.
|
// PlayKubeOptions controls playing kube YAML files.
|
||||||
type PlayKubeOptions struct {
|
type PlayKubeOptions struct {
|
||||||
|
@ -24,6 +28,8 @@ type PlayKubeOptions struct {
|
||||||
// SeccompProfileRoot - path to a directory containing seccomp
|
// SeccompProfileRoot - path to a directory containing seccomp
|
||||||
// profiles.
|
// profiles.
|
||||||
SeccompProfileRoot string
|
SeccompProfileRoot string
|
||||||
|
// StaticIPs - Static IP address used by the pod(s).
|
||||||
|
StaticIPs []net.IP
|
||||||
// ConfigMaps - slice of pathnames to kubernetes configmap YAMLs.
|
// ConfigMaps - slice of pathnames to kubernetes configmap YAMLs.
|
||||||
ConfigMaps []string
|
ConfigMaps []string
|
||||||
// LogDriver for the container. For example: journald
|
// LogDriver for the container. For example: journald
|
||||||
|
|
|
@ -16,6 +16,7 @@ import (
|
||||||
"github.com/containers/podman/v3/libpod/define"
|
"github.com/containers/podman/v3/libpod/define"
|
||||||
"github.com/containers/podman/v3/libpod/image"
|
"github.com/containers/podman/v3/libpod/image"
|
||||||
"github.com/containers/podman/v3/pkg/domain/entities"
|
"github.com/containers/podman/v3/pkg/domain/entities"
|
||||||
|
"github.com/containers/podman/v3/pkg/specgen"
|
||||||
"github.com/containers/podman/v3/pkg/specgen/generate"
|
"github.com/containers/podman/v3/pkg/specgen/generate"
|
||||||
"github.com/containers/podman/v3/pkg/specgen/generate/kube"
|
"github.com/containers/podman/v3/pkg/specgen/generate/kube"
|
||||||
"github.com/containers/podman/v3/pkg/util"
|
"github.com/containers/podman/v3/pkg/util"
|
||||||
|
@ -50,6 +51,8 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
|
||||||
return nil, errors.Wrapf(err, "unable to sort kube kinds in %q", path)
|
return nil, errors.Wrapf(err, "unable to sort kube kinds in %q", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ipIndex := 0
|
||||||
|
|
||||||
// create pod on each document if it is a pod or deployment
|
// create pod on each document if it is a pod or deployment
|
||||||
// any other kube kind will be skipped
|
// any other kube kind will be skipped
|
||||||
for _, document := range documentList {
|
for _, document := range documentList {
|
||||||
|
@ -70,7 +73,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
|
||||||
podTemplateSpec.ObjectMeta = podYAML.ObjectMeta
|
podTemplateSpec.ObjectMeta = podYAML.ObjectMeta
|
||||||
podTemplateSpec.Spec = podYAML.Spec
|
podTemplateSpec.Spec = podYAML.Spec
|
||||||
|
|
||||||
r, err := ic.playKubePod(ctx, podTemplateSpec.ObjectMeta.Name, &podTemplateSpec, options)
|
r, err := ic.playKubePod(ctx, podTemplateSpec.ObjectMeta.Name, &podTemplateSpec, options, &ipIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -84,7 +87,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
|
||||||
return nil, errors.Wrapf(err, "unable to read YAML %q as Kube Deployment", path)
|
return nil, errors.Wrapf(err, "unable to read YAML %q as Kube Deployment", path)
|
||||||
}
|
}
|
||||||
|
|
||||||
r, err := ic.playKubeDeployment(ctx, &deploymentYAML, options)
|
r, err := ic.playKubeDeployment(ctx, &deploymentYAML, options, &ipIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
@ -118,7 +121,7 @@ func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, options en
|
||||||
return report, nil
|
return report, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
|
func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAML *v1apps.Deployment, options entities.PlayKubeOptions, ipIndex *int) (*entities.PlayKubeReport, error) {
|
||||||
var (
|
var (
|
||||||
deploymentName string
|
deploymentName string
|
||||||
podSpec v1.PodTemplateSpec
|
podSpec v1.PodTemplateSpec
|
||||||
|
@ -140,7 +143,7 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM
|
||||||
// create "replicas" number of pods
|
// create "replicas" number of pods
|
||||||
for i = 0; i < numReplicas; i++ {
|
for i = 0; i < numReplicas; i++ {
|
||||||
podName := fmt.Sprintf("%s-pod-%d", deploymentName, i)
|
podName := fmt.Sprintf("%s-pod-%d", deploymentName, i)
|
||||||
podReport, err := ic.playKubePod(ctx, podName, &podSpec, options)
|
podReport, err := ic.playKubePod(ctx, podName, &podSpec, options, ipIndex)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, errors.Wrapf(err, "error encountered while bringing up pod %s", podName)
|
return nil, errors.Wrapf(err, "error encountered while bringing up pod %s", podName)
|
||||||
}
|
}
|
||||||
|
@ -149,7 +152,7 @@ func (ic *ContainerEngine) playKubeDeployment(ctx context.Context, deploymentYAM
|
||||||
return &report, nil
|
return &report, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
|
func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec, options entities.PlayKubeOptions, ipIndex *int) (*entities.PlayKubeReport, error) {
|
||||||
var (
|
var (
|
||||||
registryCreds *types.DockerAuthConfig
|
registryCreds *types.DockerAuthConfig
|
||||||
writer io.Writer
|
writer io.Writer
|
||||||
|
@ -190,9 +193,17 @@ func (ic *ContainerEngine) playKubePod(ctx context.Context, podName string, podY
|
||||||
// networks.
|
// networks.
|
||||||
networks := strings.Split(options.Network, ",")
|
networks := strings.Split(options.Network, ",")
|
||||||
logrus.Debugf("Pod joining CNI networks: %v", networks)
|
logrus.Debugf("Pod joining CNI networks: %v", networks)
|
||||||
|
p.NetNS.NSMode = specgen.Bridge
|
||||||
p.CNINetworks = append(p.CNINetworks, networks...)
|
p.CNINetworks = append(p.CNINetworks, networks...)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if len(options.StaticIPs) > *ipIndex {
|
||||||
|
p.StaticIP = &options.StaticIPs[*ipIndex]
|
||||||
|
*ipIndex++
|
||||||
|
} else if len(options.StaticIPs) > 0 {
|
||||||
|
// only warn if the user has set at least one ip ip
|
||||||
|
logrus.Warn("No more static ips left using a random one")
|
||||||
|
}
|
||||||
|
|
||||||
// Create the Pod
|
// Create the Pod
|
||||||
pod, err := generate.MakePod(p, ic.Libpod)
|
pod, err := generate.MakePod(p, ic.Libpod)
|
||||||
|
|
|
@ -11,7 +11,7 @@ import (
|
||||||
func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, opts entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
|
func (ic *ContainerEngine) PlayKube(ctx context.Context, path string, opts entities.PlayKubeOptions) (*entities.PlayKubeReport, error) {
|
||||||
options := new(play.KubeOptions).WithAuthfile(opts.Authfile).WithUsername(opts.Username).WithPassword(opts.Password)
|
options := new(play.KubeOptions).WithAuthfile(opts.Authfile).WithUsername(opts.Username).WithPassword(opts.Password)
|
||||||
options.WithCertDir(opts.CertDir).WithQuiet(opts.Quiet).WithSignaturePolicy(opts.SignaturePolicy).WithConfigMaps(opts.ConfigMaps)
|
options.WithCertDir(opts.CertDir).WithQuiet(opts.Quiet).WithSignaturePolicy(opts.SignaturePolicy).WithConfigMaps(opts.ConfigMaps)
|
||||||
options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Network).WithSeccompProfileRoot(opts.SeccompProfileRoot)
|
options.WithLogDriver(opts.LogDriver).WithNetwork(opts.Network).WithSeccompProfileRoot(opts.SeccompProfileRoot).WithStaticIPs(opts.StaticIPs)
|
||||||
|
|
||||||
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
|
if s := opts.SkipTLSVerify; s != types.OptionalBoolUndefined {
|
||||||
options.WithSkipTLSVerify(s == types.OptionalBoolTrue)
|
options.WithSkipTLSVerify(s == types.OptionalBoolTrue)
|
||||||
|
|
|
@ -12,6 +12,7 @@ import (
|
||||||
|
|
||||||
"github.com/containers/podman/v3/pkg/util"
|
"github.com/containers/podman/v3/pkg/util"
|
||||||
. "github.com/containers/podman/v3/test/utils"
|
. "github.com/containers/podman/v3/test/utils"
|
||||||
|
"github.com/containers/storage/pkg/stringid"
|
||||||
. "github.com/onsi/ginkgo"
|
. "github.com/onsi/ginkgo"
|
||||||
. "github.com/onsi/gomega"
|
. "github.com/onsi/gomega"
|
||||||
"github.com/opencontainers/selinux/go-selinux"
|
"github.com/opencontainers/selinux/go-selinux"
|
||||||
|
@ -1716,6 +1717,38 @@ spec:
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
|
It("podman play kube --ip", func() {
|
||||||
|
var i, numReplicas int32
|
||||||
|
numReplicas = 3
|
||||||
|
deployment := getDeployment(withReplicas(numReplicas))
|
||||||
|
err := generateKubeYaml("deployment", deployment, kubeYaml)
|
||||||
|
Expect(err).To(BeNil())
|
||||||
|
|
||||||
|
net := "playkube" + stringid.GenerateNonCryptoID()
|
||||||
|
session := podmanTest.Podman([]string{"network", "create", "--subnet", "10.25.31.0/24", net})
|
||||||
|
session.WaitWithDefaultTimeout()
|
||||||
|
defer podmanTest.removeCNINetwork(net)
|
||||||
|
Expect(session.ExitCode()).To(BeZero())
|
||||||
|
|
||||||
|
ips := []string{"10.25.31.5", "10.25.31.10", "10.25.31.15"}
|
||||||
|
playArgs := []string{"play", "kube", "--network", net}
|
||||||
|
for _, ip := range ips {
|
||||||
|
playArgs = append(playArgs, "--ip", ip)
|
||||||
|
}
|
||||||
|
|
||||||
|
kube := podmanTest.Podman(append(playArgs, kubeYaml))
|
||||||
|
kube.WaitWithDefaultTimeout()
|
||||||
|
Expect(kube.ExitCode()).To(Equal(0))
|
||||||
|
|
||||||
|
podNames := getPodNamesInDeployment(deployment)
|
||||||
|
for i = 0; i < numReplicas; i++ {
|
||||||
|
inspect := podmanTest.Podman([]string{"inspect", getCtrNameInPod(&podNames[i]), "--format", "{{ .NetworkSettings.Networks." + net + ".IPAddress }}"})
|
||||||
|
inspect.WaitWithDefaultTimeout()
|
||||||
|
Expect(inspect.ExitCode()).To(Equal(0))
|
||||||
|
Expect(inspect.OutputToString()).To(Equal(ips[i]))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
It("podman play kube test with network portbindings", func() {
|
It("podman play kube test with network portbindings", func() {
|
||||||
ip := "127.0.0.100"
|
ip := "127.0.0.100"
|
||||||
port := "5000"
|
port := "5000"
|
||||||
|
|
Loading…
Reference in New Issue