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
This commit is contained in:
parent
1710acd18a
commit
a4701b5631
|
|
@ -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"),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -79,22 +79,15 @@ func (c *Container) Init() (err error) {
|
|||
}
|
||||
|
||||
// Copy /etc/resolv.conf to the container's rundir
|
||||
resolvPath := "/etc/resolv.conf"
|
||||
runDirResolv, err := c.generateResolvConf()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// 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)
|
||||
if err != nil {
|
||||
return errors.Wrapf(err, "unable to copy resolv.conf to ", runDirResolv)
|
||||
}
|
||||
// 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
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue