Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Honor custom DNS in play|generate kube #9150

Merged
merged 1 commit into from
Jan 29, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
112 changes: 98 additions & 14 deletions libpod/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -171,9 +171,10 @@ func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPor
deDupPodVolumes := make(map[string]*v1.Volume)
first := true
podContainers := make([]v1.Container, 0, len(containers))
dnsInfo := v1.PodDNSConfig{}
for _, ctr := range containers {
if !ctr.IsInfra() {
ctr, volumes, err := containerToV1Container(ctr)
ctr, volumes, _, err := containerToV1Container(ctr)
if err != nil {
return nil, err
}
Expand All @@ -196,17 +197,33 @@ func (p *Pod) podWithContainers(containers []*Container, ports []v1.ContainerPor
vol := 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))
for _, vol := range deDupPodVolumes {
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{
Kind: "Pod",
APIVersion: "v1",
Expand All @@ -228,6 +245,9 @@ func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1.
Containers: containers,
Volumes: volumes,
}
if dnsOptions != nil {
ps.DNSConfig = dnsOptions
}
p := v1.Pod{
TypeMeta: tm,
ObjectMeta: om,
Expand All @@ -241,57 +261,90 @@ func addContainersAndVolumesToPodObject(containers []v1.Container, volumes []v1.
func simplePodWithV1Containers(ctrs []*Container) (*v1.Pod, error) {
kubeCtrs := make([]v1.Container, 0, len(ctrs))
kubeVolumes := make([]v1.Volume, 0)
podDNS := v1.PodDNSConfig{}
for _, ctr := range ctrs {
kubeCtr, kubeVols, err := containerToV1Container(ctr)
kubeCtr, kubeVols, ctrDNS, err := containerToV1Container(ctr)
if err != nil {
return nil, err
}
kubeCtrs = append(kubeCtrs, kubeCtr)
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
// 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{}
kubeVolumes := []v1.Volume{}
kubeSec, err := generateKubeSecurityContext(c)
if err != nil {
return kubeContainer, kubeVolumes, err
return kubeContainer, kubeVolumes, nil, err
}

if len(c.config.Spec.Linux.Devices) > 0 {
// TODO Enable when we can support devices and their names
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 {
// TODO When we until we can resolve what the volume name should be, this is disabled
// Volume names need to be coordinated "globally" in the kube files.
volumeMounts, volumes, err := libpodMountsToKubeVolumeMounts(c)
if err != nil {
return kubeContainer, kubeVolumes, err
return kubeContainer, kubeVolumes, nil, err
}
kubeContainer.VolumeMounts = volumeMounts
kubeVolumes = append(kubeVolumes, volumes...)
}

envVariables, err := libpodEnvVarsToKubeEnvVars(c.config.Spec.Process.Env)
if err != nil {
return kubeContainer, kubeVolumes, err
return kubeContainer, kubeVolumes, nil, err
}

portmappings, err := c.PortMappings()
if err != nil {
return kubeContainer, kubeVolumes, err
return kubeContainer, kubeVolumes, nil, err
}
ports, err := ocicniPortMappingToContainerPort(portmappings)
if err != nil {
return kubeContainer, kubeVolumes, err
return kubeContainer, kubeVolumes, nil, err
}

containerCommands := c.Command()
Expand Down Expand Up @@ -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
Expand Down
26 changes: 26 additions & 0 deletions pkg/specgen/generate/kube/kube.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package kube
import (
"context"
"fmt"
"net"
"strings"

"github.com/containers/common/pkg/parse"
Expand Down Expand Up @@ -44,6 +45,31 @@ func ToPodGen(ctx context.Context, podName string, podYAML *v1.PodTemplateSpec)
podPorts := getPodPorts(podYAML.Spec.Containers)
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
}

Expand Down
63 changes: 63 additions & 0 deletions test/e2e/generate_kube_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -540,4 +540,67 @@ var _ = Describe("Podman generate kube", func() {
kube.WaitWithDefaultTimeout()
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"))
})
})