aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorbaude <bbaude@redhat.com>2018-01-19 08:51:59 -0600
committerAtomic Bot <atomic-devel@projectatomic.io>2018-01-19 15:42:25 +0000
commita4701b56311d5d934543e2b4306b08baa844ec3f (patch)
treebcad38f7cf02a37107bfa61854a01eb58bf9868f
parent1710acd18a4630ef704c66bf0cdae76dd658776a (diff)
downloadpodman-a4701b56311d5d934543e2b4306b08baa844ec3f.tar.gz
podman-a4701b56311d5d934543e2b4306b08baa844ec3f.tar.bz2
podman-a4701b56311d5d934543e2b4306b08baa844ec3f.zip
Add --dns-search, --dns-opt, --dns-server and --add-host.
Each of these options are destructive in nature, meaning if the user adds one of them, all current ones are removed from the produced resolv.conf. * dns-server allows the user to specify dns servers. * dns-opt allows the user to specify special resolv.conf options * dns-search allows the user to specify search domains The add-host option is not destructive and truly just adds the host to /etc/hosts. Signed-off-by: baude <bbaude@redhat.com> Closes: #231 Approved by: mheon
-rw-r--r--cmd/podman/create.go14
-rw-r--r--cmd/podman/spec.go13
-rw-r--r--libpod/container_api.go17
-rw-r--r--libpod/container_internal.go135
-rw-r--r--libpod/options.go53
5 files changed, 214 insertions, 18 deletions
diff --git a/cmd/podman/create.go b/cmd/podman/create.go
index 262be129c..55425638f 100644
--- a/cmd/podman/create.go
+++ b/cmd/podman/create.go
@@ -83,6 +83,7 @@ type createConfig struct {
Env map[string]string //env
ExposedPorts map[nat.Port]struct{}
GroupAdd []uint32 // group-add
+ HostAdd []string //add-host
Hostname string //hostname
Image string
ImageID string
@@ -560,6 +561,18 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime, imageName string,
networkMode = c.String("net")
}
+ // Verify the additional hosts are in correct format
+ for _, host := range c.StringSlice("add-host") {
+ if _, err := validateExtraHost(host); err != nil {
+ return nil, err
+ }
+ }
+
+ // Check for . and dns-search domains
+ if libpod.StringInSlice(".", c.StringSlice("dns-search")) && len(c.StringSlice("dns-search")) > 1 {
+ return nil, errors.Errorf("cannot pass additional search domains when also specifying '.'")
+ }
+
config := &createConfig{
Runtime: runtime,
CapAdd: c.StringSlice("cap-add"),
@@ -576,6 +589,7 @@ func parseCreateOpts(c *cli.Context, runtime *libpod.Runtime, imageName string,
ExposedPorts: ports,
GroupAdd: groupAdd,
Hostname: c.String("hostname"),
+ HostAdd: c.StringSlice("add-host"),
Image: imageName,
ImageID: imageID,
Interactive: c.Bool("interactive"),
diff --git a/cmd/podman/spec.go b/cmd/podman/spec.go
index 59ea5685a..152d1740c 100644
--- a/cmd/podman/spec.go
+++ b/cmd/podman/spec.go
@@ -584,6 +584,19 @@ func (c *createConfig) GetContainerCreateOptions() ([]libpod.CtrCreateOption, er
options = append(options, libpod.WithStopSignal(c.StopSignal))
options = append(options, libpod.WithStopTimeout(c.StopTimeout))
+ if len(c.DNSSearch) > 0 {
+ options = append(options, libpod.WithDNSSearch(c.DNSSearch))
+ }
+ if len(c.DNSServers) > 0 {
+ options = append(options, libpod.WithDNS(c.DNSServers))
+ }
+ if len(c.DNSOpt) > 0 {
+ options = append(options, libpod.WithDNSOption(c.DNSOpt))
+ }
+ if len(c.HostAdd) > 0 {
+ options = append(options, libpod.WithHosts(c.HostAdd))
+ }
+
return options, nil
}
diff --git a/libpod/container_api.go b/libpod/container_api.go
index 9a4f50079..be7fd76bc 100644
--- a/libpod/container_api.go
+++ b/libpod/container_api.go
@@ -79,22 +79,15 @@ func (c *Container) Init() (err error) {
}
// Copy /etc/resolv.conf to the container's rundir
- resolvPath := "/etc/resolv.conf"
-
- // Check if the host system is using system resolve and if so
- // copy its resolv.conf
- _, err = os.Stat("/run/systemd/resolve/resolv.conf")
- if err == nil {
- resolvPath = "/run/systemd/resolve/resolv.conf"
- }
- runDirResolv, err := c.copyHostFileToRundir(resolvPath)
+ runDirResolv, err := c.generateResolvConf()
if err != nil {
- return errors.Wrapf(err, "unable to copy resolv.conf to ", runDirResolv)
+ return err
}
+
// Copy /etc/hosts to the container's rundir
- runDirHosts, err := c.copyHostFileToRundir("/etc/hosts")
+ runDirHosts, err := c.generateHosts()
if err != nil {
- return errors.Wrapf(err, "unable to copy /etc/hosts to ", runDirHosts)
+ return errors.Wrapf(err, "unable to copy /etc/hosts to container space")
}
// Save OCI spec to disk
diff --git a/libpod/container_internal.go b/libpod/container_internal.go
index a9db43b08..c052f61fe 100644
--- a/libpod/container_internal.go
+++ b/libpod/container_internal.go
@@ -3,8 +3,10 @@ package libpod
import (
"fmt"
"io"
+ "io/ioutil"
"os"
"path/filepath"
+ "strings"
"syscall"
"time"
@@ -13,7 +15,6 @@ import (
"github.com/docker/docker/pkg/mount"
"github.com/docker/docker/pkg/namesgenerator"
"github.com/docker/docker/pkg/stringid"
- "github.com/mrunalp/fileutils"
spec "github.com/opencontainers/runtime-spec/specs-go"
"github.com/opencontainers/selinux/go-selinux/label"
"github.com/pkg/errors"
@@ -373,11 +374,18 @@ func (c *Container) cleanupStorage() error {
return c.save()
}
-// copyHostFileToRundir copies the provided file to the runtimedir
-func (c *Container) copyHostFileToRundir(sourcePath string) (string, error) {
- destFileName := filepath.Join(c.state.RunDir, filepath.Base(sourcePath))
- if err := fileutils.CopyFile(sourcePath, destFileName); err != nil {
- return "", err
+// WriteStringToRundir copies the provided file to the runtimedir
+func (c *Container) WriteStringToRundir(destFile, output string) (string, error) {
+ destFileName := filepath.Join(c.state.RunDir, destFile)
+ f, err := os.Create(destFileName)
+ if err != nil {
+ return "", errors.Wrapf(err, "unable to create %s", destFileName)
+ }
+
+ defer f.Close()
+ _, err = f.WriteString(output)
+ if err != nil {
+ return "", errors.Wrapf(err, "unable to write %s", destFileName)
}
// Relabel runDirResolv for the container
if err := label.Relabel(destFileName, c.config.MountLabel, false); err != nil {
@@ -385,3 +393,118 @@ func (c *Container) copyHostFileToRundir(sourcePath string) (string, error) {
}
return destFileName, nil
}
+
+type resolv struct {
+ nameServers []string
+ searchDomains []string
+ options []string
+}
+
+// generateResolvConf generates a containers resolv.conf
+func (c *Container) generateResolvConf() (string, error) {
+ // Copy /etc/resolv.conf to the container's rundir
+ resolvPath := "/etc/resolv.conf"
+
+ // Check if the host system is using system resolve and if so
+ // copy its resolv.conf
+ if _, err := os.Stat("/run/systemd/resolve/resolv.conf"); err == nil {
+ resolvPath = "/run/systemd/resolve/resolv.conf"
+ }
+ orig, err := ioutil.ReadFile(resolvPath)
+ if err != nil {
+ return "", errors.Wrapf(err, "unable to read %s", resolvPath)
+ }
+ if len(c.config.DNSServer) == 0 && len(c.config.DNSSearch) == 0 && len(c.config.DNSOption) == 0 {
+ return c.WriteStringToRundir("resolv.conf", fmt.Sprintf("%s", orig))
+ }
+
+ // Read and organize the hosts /etc/resolv.conf
+ resolv := createResolv(string(orig[:]))
+
+ // Populate the resolv struct with user's dns search domains
+ if len(c.config.DNSSearch) > 0 {
+ resolv.searchDomains = nil
+ // The . character means the user doesnt want any search domains in the container
+ if !StringInSlice(".", c.config.DNSSearch) {
+ resolv.searchDomains = append(resolv.searchDomains, c.Config().DNSSearch...)
+ }
+ }
+
+ // Populate the resolv struct with user's dns servers
+ if len(c.config.DNSServer) > 0 {
+ resolv.nameServers = nil
+ for _, i := range c.config.DNSServer {
+ resolv.nameServers = append(resolv.nameServers, i.String())
+ }
+ }
+
+ // Populate the resolve struct with the users dns options
+ if len(c.config.DNSOption) > 0 {
+ resolv.options = nil
+ resolv.options = append(resolv.options, c.Config().DNSOption...)
+ }
+ return c.WriteStringToRundir("resolv.conf", resolv.ToString())
+}
+
+// createResolv creates a resolv struct from an input string
+func createResolv(input string) resolv {
+ var resolv resolv
+ for _, line := range strings.Split(input, "\n") {
+ if strings.HasPrefix(line, "search") {
+ fields := strings.Fields(line)
+ if len(fields) < 2 {
+ logrus.Debugf("invalid resolv.conf line %s", line)
+ continue
+ }
+ resolv.searchDomains = append(resolv.searchDomains, fields[1:]...)
+ } else if strings.HasPrefix(line, "nameserver") {
+ fields := strings.Fields(line)
+ if len(fields) < 2 {
+ logrus.Debugf("invalid resolv.conf line %s", line)
+ continue
+ }
+ resolv.nameServers = append(resolv.nameServers, fields[1])
+ } else if strings.HasPrefix(line, "options") {
+ fields := strings.Fields(line)
+ if len(fields) < 2 {
+ logrus.Debugf("invalid resolv.conf line %s", line)
+ continue
+ }
+ resolv.options = append(resolv.options, fields[1:]...)
+ }
+ }
+ return resolv
+}
+
+//ToString returns a resolv struct in the form of a resolv.conf
+func (r resolv) ToString() string {
+ var result string
+ // Populate the output string with search domains
+ result += fmt.Sprintf("search %s\n", strings.Join(r.searchDomains, " "))
+ // Populate the output string with name servers
+ for _, i := range r.nameServers {
+ result += fmt.Sprintf("nameserver %s\n", i)
+ }
+ // Populate the output string with dns options
+ for _, i := range r.options {
+ result += fmt.Sprintf("options %s\n", i)
+ }
+ return result
+}
+
+// generateHosts creates a containers hosts file
+func (c *Container) generateHosts() (string, error) {
+ orig, err := ioutil.ReadFile("/etc/hosts")
+ if err != nil {
+ return "", errors.Wrapf(err, "unable to read /etc/hosts")
+ }
+ hosts := string(orig)
+ if len(c.config.HostAdd) > 0 {
+ for _, host := range c.config.HostAdd {
+ // the host format has already been verified at this point
+ fields := strings.Split(host, ":")
+ hosts += fmt.Sprintf("%s %s\n", fields[0], fields[1])
+ }
+ }
+ return c.WriteStringToRundir("hosts", hosts)
+}
diff --git a/libpod/options.go b/libpod/options.go
index ca4d104df..63a72628f 100644
--- a/libpod/options.go
+++ b/libpod/options.go
@@ -1,6 +1,7 @@
package libpod
import (
+ "net"
"path/filepath"
"regexp"
"syscall"
@@ -641,3 +642,55 @@ func WithPodLabels(labels map[string]string) PodCreateOption {
return nil
}
}
+
+// WithDNSSearch sets the additional search domains of a container
+func WithDNSSearch(searchDomains []string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return ErrCtrFinalized
+ }
+ ctr.config.DNSSearch = searchDomains
+ return nil
+ }
+}
+
+// WithDNS sets additional name servers for the container
+func WithDNS(dnsServers []string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return ErrCtrFinalized
+ }
+ var dns []net.IP
+ for _, i := range dnsServers {
+ result := net.ParseIP(i)
+ if result == nil {
+ return errors.Wrapf(ErrInvalidArg, "invalid IP address %s", i)
+ }
+ dns = append(dns, result)
+ }
+ ctr.config.DNSServer = dns
+ return nil
+ }
+}
+
+// WithDNSOption sets addition dns options for the container
+func WithDNSOption(dnsOptions []string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return ErrCtrFinalized
+ }
+ ctr.config.DNSOption = dnsOptions
+ return nil
+ }
+}
+
+// WithHosts sets additional host:IP for the hosts file
+func WithHosts(hosts []string) CtrCreateOption {
+ return func(ctr *Container) error {
+ if ctr.valid {
+ return ErrCtrFinalized
+ }
+ ctr.config.HostAdd = hosts
+ return nil
+ }
+}