Merge pull request #9150 from baude/playkubedns

Honor custom DNS in play|generate kube
This commit is contained in:
OpenShift Merge Robot
2021-01-29 14:15:21 -05:00
committed by GitHub
3 changed files with 187 additions and 14 deletions

View File

@ -171,9 +171,10 @@ func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPor
deDupPodVolumes := make(map[string]*v1.Volume) deDupPodVolumes := make(map[string]*v1.Volume)
first := true first := true
podContainers := make([]v1.Container, 0, len(containers)) podContainers := make([]v1.Container, 0, len(containers))
dnsInfo := v1.PodDNSConfig{}
for _, ctr := range containers { for _, ctr := range containers {
if !ctr.IsInfra() { if !ctr.IsInfra() {
ctr, volumes, err := containerToV1Container(ctr) ctr, volumes, _, err := containerToV1Container(ctr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
@ -196,6 +197,22 @@ func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPor
vol := vol vol := vol
deDupPodVolumes[vol.Name] = &vol deDupPodVolumes[vol.Name] = &vol
} }
} else {
_, _, infraDNS, err := containerToV1Container(ctr)
if err != nil {
return nil, err
}
if infraDNS != nil {
if servers := infraDNS.Nameservers; len(servers) > 0 {
dnsInfo.Nameservers = servers
}
if searches := infraDNS.Searches; len(searches) > 0 {
dnsInfo.Searches = searches
}
if options := infraDNS.Options; len(options) > 0 {
dnsInfo.Options = options
}
}
} }
} }
podVolumes := make([]v1.Volume, 0, len(deDupPodVolumes)) podVolumes := make([]v1.Volume, 0, len(deDupPodVolumes))
@ -203,10 +220,10 @@ func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPor
podVolumes = append(podVolumes, *vol) podVolumes = append(podVolumes, *vol)
} }
return addContainersAndVolumesToPodObject(podContainers, podVolumes, p.Name()), nil return addContainersAndVolumesToPodObject(podContainers, podVolumes, p.Name(), &dnsInfo), nil
} }
func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1.Volume, podName string) *v1.Pod { func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1.Volume, podName string, dnsOptions *v1.PodDNSConfig) *v1.Pod {
tm := v12.TypeMeta{ tm := v12.TypeMeta{
Kind: "Pod", Kind: "Pod",
APIVersion: "v1", APIVersion: "v1",
@ -228,6 +245,9 @@ func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1.
Containers: containers, Containers: containers,
Volumes: volumes, Volumes: volumes,
} }
if dnsOptions != nil {
ps.DNSConfig = dnsOptions
}
p := v1.Pod{ p := v1.Pod{
TypeMeta: tm, TypeMeta: tm,
ObjectMeta: om, ObjectMeta: om,
@ -241,32 +261,65 @@ func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1.
func simplePodWithV1Containers(ctrs []*Container) (*v1.Pod, error) { func simplePodWithV1Containers(ctrs []*Container) (*v1.Pod, error) {
kubeCtrs := make([]v1.Container, 0, len(ctrs)) kubeCtrs := make([]v1.Container, 0, len(ctrs))
kubeVolumes := make([]v1.Volume, 0) kubeVolumes := make([]v1.Volume, 0)
podDNS := v1.PodDNSConfig{}
for _, ctr := range ctrs { for _, ctr := range ctrs {
kubeCtr, kubeVols, err := containerToV1Container(ctr) kubeCtr, kubeVols, ctrDNS, err := containerToV1Container(ctr)
if err != nil { if err != nil {
return nil, err return nil, err
} }
kubeCtrs = append(kubeCtrs, kubeCtr) kubeCtrs = append(kubeCtrs, kubeCtr)
kubeVolumes = append(kubeVolumes, kubeVols...) kubeVolumes = append(kubeVolumes, kubeVols...)
}
return addContainersAndVolumesToPodObject(kubeCtrs, kubeVolumes, strings.ReplaceAll(ctrs[0].Name(), "_", "")), nil
// Combine DNS information in sum'd structure
if ctrDNS != nil {
// nameservers
if servers := ctrDNS.Nameservers; servers != nil {
if podDNS.Nameservers == nil {
podDNS.Nameservers = make([]string, 0)
}
for _, s := range servers {
if !util.StringInSlice(s, podDNS.Nameservers) { // only append if it does not exist
podDNS.Nameservers = append(podDNS.Nameservers, s)
}
}
}
// search domains
if domains := ctrDNS.Searches; domains != nil {
if podDNS.Searches == nil {
podDNS.Searches = make([]string, 0)
}
for _, d := range domains {
if !util.StringInSlice(d, podDNS.Searches) { // only append if it does not exist
podDNS.Searches = append(podDNS.Searches, d)
}
}
}
// dns options
if options := ctrDNS.Options; options != nil {
if podDNS.Options == nil {
podDNS.Options = make([]v1.PodDNSConfigOption, 0)
}
podDNS.Options = append(podDNS.Options, options...)
}
} // end if ctrDNS
}
return addContainersAndVolumesToPodObject(kubeCtrs, kubeVolumes, strings.ReplaceAll(ctrs[0].Name(), "_", ""), &podDNS), nil
} }
// containerToV1Container converts information we know about a libpod container // containerToV1Container converts information we know about a libpod container
// to a V1.Container specification. // to a V1.Container specification.
func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) { func containerToV1Container(c *Container) (v1.Container, []v1.Volume, *v1.PodDNSConfig, error) {
kubeContainer := v1.Container{} kubeContainer := v1.Container{}
kubeVolumes := []v1.Volume{} kubeVolumes := []v1.Volume{}
kubeSec, err := generateKubeSecurityContext(c) kubeSec, err := generateKubeSecurityContext(c)
if err != nil { if err != nil {
return kubeContainer, kubeVolumes, err return kubeContainer, kubeVolumes, nil, err
} }
if len(c.config.Spec.Linux.Devices) > 0 { if len(c.config.Spec.Linux.Devices) > 0 {
// TODO Enable when we can support devices and their names // TODO Enable when we can support devices and their names
kubeContainer.VolumeDevices = generateKubeVolumeDeviceFromLinuxDevice(c.Spec().Linux.Devices) kubeContainer.VolumeDevices = generateKubeVolumeDeviceFromLinuxDevice(c.Spec().Linux.Devices)
return kubeContainer, kubeVolumes, errors.Wrapf(define.ErrNotImplemented, "linux devices") return kubeContainer, kubeVolumes, nil, errors.Wrapf(define.ErrNotImplemented, "linux devices")
} }
if len(c.config.UserVolumes) > 0 { if len(c.config.UserVolumes) > 0 {
@ -274,7 +327,7 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) {
// Volume names need to be coordinated "globally" in the kube files. // Volume names need to be coordinated "globally" in the kube files.
volumeMounts, volumes, err := libpodMountsToKubeVolumeMounts(c) volumeMounts, volumes, err := libpodMountsToKubeVolumeMounts(c)
if err != nil { if err != nil {
return kubeContainer, kubeVolumes, err return kubeContainer, kubeVolumes, nil, err
} }
kubeContainer.VolumeMounts = volumeMounts kubeContainer.VolumeMounts = volumeMounts
kubeVolumes = append(kubeVolumes, volumes...) kubeVolumes = append(kubeVolumes, volumes...)
@ -282,16 +335,16 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) {
envVariables, err := libpodEnvVarsToKubeEnvVars(c.config.Spec.Process.Env) envVariables, err := libpodEnvVarsToKubeEnvVars(c.config.Spec.Process.Env)
if err != nil { if err != nil {
return kubeContainer, kubeVolumes, err return kubeContainer, kubeVolumes, nil, err
} }
portmappings, err := c.PortMappings() portmappings, err := c.PortMappings()
if err != nil { if err != nil {
return kubeContainer, kubeVolumes, err return kubeContainer, kubeVolumes, nil, err
} }
ports, err := ocicniPortMappingToContainerPort(portmappings) ports, err := ocicniPortMappingToContainerPort(portmappings)
if err != nil { if err != nil {
return kubeContainer, kubeVolumes, err return kubeContainer, kubeVolumes, nil, err
} }
containerCommands := c.Command() containerCommands := c.Command()
@ -355,7 +408,38 @@ func containerToV1Container(c *Container) (v1.Container, []v1.Volume, error) {
} }
} }
return kubeContainer, kubeVolumes, nil // Obtain the DNS entries from the container
dns := v1.PodDNSConfig{}
// DNS servers
if servers := c.config.DNSServer; len(servers) > 0 {
dnsServers := make([]string, 0)
for _, server := range servers {
dnsServers = append(dnsServers, server.String())
}
dns.Nameservers = dnsServers
}
// DNS search domains
if searches := c.config.DNSSearch; len(searches) > 0 {
dns.Searches = searches
}
// DNS options
if options := c.config.DNSOption; len(options) > 0 {
dnsOptions := make([]v1.PodDNSConfigOption, 0)
for _, option := range options {
// the option can be "k:v" or just "k", no delimiter is required
opts := strings.SplitN(option, ":", 2)
dnsOpt := v1.PodDNSConfigOption{
Name: opts[0],
Value: &opts[1],
}
dnsOptions = append(dnsOptions, dnsOpt)
}
dns.Options = dnsOptions
}
return kubeContainer, kubeVolumes, &dns, nil
} }
// ocicniPortMappingToContainerPort takes an ocicni portmapping and converts // ocicniPortMappingToContainerPort takes an ocicni portmapping and converts

View File

@ -3,6 +3,7 @@ package kube
import ( import (
"context" "context"
"fmt" "fmt"
"net"
"strings" "strings"
"github.com/containers/common/pkg/parse" "github.com/containers/common/pkg/parse"
@ -44,6 +45,31 @@ func ToPodGen(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec)
podPorts := getPodPorts(podYAML.Spec.Containers) podPorts := getPodPorts(podYAML.Spec.Containers)
p.PortMappings = podPorts p.PortMappings = podPorts
if dnsConfig := podYAML.Spec.DNSConfig; dnsConfig != nil {
// name servers
if dnsServers := dnsConfig.Nameservers; len(dnsServers) > 0 {
servers := make([]net.IP, 0)
for _, server := range dnsServers {
servers = append(servers, net.ParseIP(server))
}
p.DNSServer = servers
}
// search domans
if domains := dnsConfig.Searches; len(domains) > 0 {
p.DNSSearch = domains
}
// dns options
if options := dnsConfig.Options; len(options) > 0 {
dnsOptions := make([]string, 0)
for _, opts := range options {
d := opts.Name
if opts.Value != nil {
d += ":" + *opts.Value
}
dnsOptions = append(dnsOptions, d)
}
}
}
return p, nil return p, nil
} }

View File

@ -540,4 +540,67 @@ var _ = Describe("Podman generate kube", func() {
kube.WaitWithDefaultTimeout() kube.WaitWithDefaultTimeout()
Expect(kube.ExitCode()).ToNot(Equal(0)) Expect(kube.ExitCode()).ToNot(Equal(0))
}) })
It("podman generate kube on a container with dns options", func() {
top := podmanTest.Podman([]string{"run", "-dt", "--name", "top", "--dns", "8.8.8.8", "--dns-search", "foobar.com", "--dns-opt", "color:blue", ALPINE, "top"})
top.WaitWithDefaultTimeout()
Expect(top.ExitCode()).To(BeZero())
kube := podmanTest.Podman([]string{"generate", "kube", "top"})
kube.WaitWithDefaultTimeout()
Expect(kube.ExitCode()).To(Equal(0))
pod := new(v1.Pod)
err := yaml.Unmarshal(kube.Out.Contents(), pod)
Expect(err).To(BeNil())
Expect(StringInSlice("8.8.8.8", pod.Spec.DNSConfig.Nameservers)).To(BeTrue())
Expect(StringInSlice("foobar.com", pod.Spec.DNSConfig.Searches)).To(BeTrue())
Expect(len(pod.Spec.DNSConfig.Options)).To(BeNumerically(">", 0))
Expect(pod.Spec.DNSConfig.Options[0].Name).To(Equal("color"))
Expect(*pod.Spec.DNSConfig.Options[0].Value).To(Equal("blue"))
})
It("podman generate kube multiple contianer dns servers and options are cumulative", func() {
top1 := podmanTest.Podman([]string{"run", "-dt", "--name", "top1", "--dns", "8.8.8.8", "--dns-search", "foobar.com", ALPINE, "top"})
top1.WaitWithDefaultTimeout()
Expect(top1.ExitCode()).To(BeZero())
top2 := podmanTest.Podman([]string{"run", "-dt", "--name", "top2", "--dns", "8.7.7.7", "--dns-search", "homer.com", ALPINE, "top"})
top2.WaitWithDefaultTimeout()
Expect(top2.ExitCode()).To(BeZero())
kube := podmanTest.Podman([]string{"generate", "kube", "top1", "top2"})
kube.WaitWithDefaultTimeout()
Expect(kube.ExitCode()).To(Equal(0))
pod := new(v1.Pod)
err := yaml.Unmarshal(kube.Out.Contents(), pod)
Expect(err).To(BeNil())
Expect(StringInSlice("8.8.8.8", pod.Spec.DNSConfig.Nameservers)).To(BeTrue())
Expect(StringInSlice("8.7.7.7", pod.Spec.DNSConfig.Nameservers)).To(BeTrue())
Expect(StringInSlice("foobar.com", pod.Spec.DNSConfig.Searches)).To(BeTrue())
Expect(StringInSlice("homer.com", pod.Spec.DNSConfig.Searches)).To(BeTrue())
})
It("podman generate kube on a pod with dns options", func() {
top := podmanTest.Podman([]string{"run", "--pod", "new:pod1", "-dt", "--name", "top", "--dns", "8.8.8.8", "--dns-search", "foobar.com", "--dns-opt", "color:blue", ALPINE, "top"})
top.WaitWithDefaultTimeout()
Expect(top.ExitCode()).To(BeZero())
kube := podmanTest.Podman([]string{"generate", "kube", "pod1"})
kube.WaitWithDefaultTimeout()
Expect(kube.ExitCode()).To(Equal(0))
pod := new(v1.Pod)
err := yaml.Unmarshal(kube.Out.Contents(), pod)
Expect(err).To(BeNil())
Expect(StringInSlice("8.8.8.8", pod.Spec.DNSConfig.Nameservers)).To(BeTrue())
Expect(StringInSlice("foobar.com", pod.Spec.DNSConfig.Searches)).To(BeTrue())
Expect(len(pod.Spec.DNSConfig.Options)).To(BeNumerically(">", 0))
Expect(pod.Spec.DNSConfig.Options[0].Name).To(Equal("color"))
Expect(*pod.Spec.DNSConfig.Options[0].Value).To(Equal("blue"))
})
}) })