Skip to content

Commit 3d43066

Browse files
authored
Merge pull request #6086 from thaJeztah/fork_resolvconf
executor/oci: use fork of libnetwork/resolvconf
2 parents 0c01580 + 4e1e0fe commit 3d43066

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

41 files changed

+859
-791
lines changed
Lines changed: 111 additions & 150 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,3 @@
1-
// FIXME(thaJeztah): remove once we are a module; the go:build directive prevents go from downgrading language version to go1.16:
2-
//go:build go1.23
3-
41
// Package resolvconf is used to generate a container's /etc/resolv.conf file.
52
//
63
// Constructor Load and Parse read a resolv.conf file from the filesystem or
@@ -21,19 +18,15 @@ import (
2118
"bufio"
2219
"bytes"
2320
"context"
24-
"fmt"
2521
"io"
26-
"io/fs"
2722
"net/netip"
2823
"os"
24+
"slices"
2925
"strconv"
3026
"strings"
31-
"text/template"
3227

33-
"github.com/containerd/log"
34-
"github.com/moby/sys/atomicwriter"
35-
"github.com/opencontainers/go-digest"
36-
"github.com/pkg/errors"
28+
"github.com/moby/buildkit/errdefs"
29+
"github.com/moby/buildkit/util/bklog"
3730
)
3831

3932
// Fallback nameservers, to use if none can be obtained from the host or command
@@ -70,7 +63,7 @@ type ExtDNSEntry struct {
7063

7164
func (ed ExtDNSEntry) String() string {
7265
if ed.HostLoopback {
73-
return fmt.Sprintf("host(%s)", ed.Addr)
66+
return "host(" + ed.Addr.String() + ")"
7467
}
7568
return ed.Addr.String()
7669
}
@@ -119,7 +112,7 @@ func Parse(reader io.Reader, path string) (ResolvConf, error) {
119112
rc.processLine(scanner.Text())
120113
}
121114
if err := scanner.Err(); err != nil {
122-
return ResolvConf{}, errSystem{err}
115+
return ResolvConf{}, errdefs.Internal(err)
123116
}
124117
if _, ok := rc.Option("ndots"); ok {
125118
rc.md.NDotsFrom = "host"
@@ -141,7 +134,7 @@ func (rc *ResolvConf) SetHeader(c string) {
141134

142135
// NameServers returns addresses used in nameserver directives.
143136
func (rc *ResolvConf) NameServers() []netip.Addr {
144-
return append([]netip.Addr(nil), rc.nameServers...)
137+
return slices.Clone(rc.nameServers)
145138
}
146139

147140
// OverrideNameServers replaces the current set of nameservers.
@@ -152,7 +145,7 @@ func (rc *ResolvConf) OverrideNameServers(nameServers []netip.Addr) {
152145

153146
// Search returns the current DNS search domains.
154147
func (rc *ResolvConf) Search() []string {
155-
return append([]string(nil), rc.search...)
148+
return slices.Clone(rc.search)
156149
}
157150

158151
// OverrideSearch replaces the current DNS search domains.
@@ -169,7 +162,7 @@ func (rc *ResolvConf) OverrideSearch(search []string) {
169162

170163
// Options returns the current options.
171164
func (rc *ResolvConf) Options() []string {
172-
return append([]string(nil), rc.options...)
165+
return slices.Clone(rc.options)
173166
}
174167

175168
// Option finds the last option named search, and returns (value, true) if
@@ -181,7 +174,7 @@ func (rc *ResolvConf) Options() []string {
181174
// Option("ndots") -> ("1", true)
182175
// Option("edns0") -> ("", true)
183176
func (rc *ResolvConf) Option(search string) (string, bool) {
184-
for i := len(rc.options) - 1; i >= 0; i -= 1 {
177+
for i := len(rc.options) - 1; i >= 0; i-- {
185178
k, v, _ := strings.Cut(rc.options[i], ":")
186179
if k == search {
187180
return v, true
@@ -192,7 +185,7 @@ func (rc *ResolvConf) Option(search string) (string, bool) {
192185

193186
// OverrideOptions replaces the current DNS options.
194187
func (rc *ResolvConf) OverrideOptions(options []string) {
195-
rc.options = append([]string(nil), options...)
188+
rc.options = slices.Clone(options)
196189
rc.md.NDotsFrom = ""
197190
if _, exists := rc.Option("ndots"); exists {
198191
rc.md.NDotsFrom = "override"
@@ -227,7 +220,7 @@ func (rc *ResolvConf) TransformForLegacyNw(ipv6 bool) {
227220
}
228221
rc.nameServers = filtered
229222
if len(rc.nameServers) == 0 {
230-
log.G(context.TODO()).Info("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers")
223+
bklog.G(context.TODO()).Info("No non-localhost DNS nameservers are left in resolv.conf. Using default external servers")
231224
rc.nameServers = defaultNSAddrs(ipv6)
232225
rc.md.Warnings = append(rc.md.Warnings, "Used default nameservers.")
233226
}
@@ -283,145 +276,123 @@ func (rc *ResolvConf) TransformForIntNS(
283276
if len(rc.md.ExtNameServers) == 0 {
284277
rc.md.Warnings = append(rc.md.Warnings, "NO EXTERNAL NAMESERVERS DEFINED")
285278
}
286-
return append([]ExtDNSEntry(nil), rc.md.ExtNameServers...), nil
279+
return slices.Clone(rc.md.ExtNameServers), nil
287280
}
288281

289282
// Generate returns content suitable for writing to a resolv.conf file. If comments
290283
// is true, the file will include header information if supplied, and a trailing
291284
// comment that describes how the file was constructed and lists external resolvers.
292285
func (rc *ResolvConf) Generate(comments bool) ([]byte, error) {
293-
s := struct {
294-
Md *metadata
295-
NameServers []netip.Addr
296-
Search []string
297-
Options []string
298-
Other []string
299-
Overrides []string
300-
Comments bool
301-
}{
302-
Md: &rc.md,
303-
NameServers: rc.nameServers,
304-
Search: rc.search,
305-
Options: rc.options,
306-
Other: rc.other,
307-
Comments: comments,
308-
}
309-
if rc.md.NSOverride {
310-
s.Overrides = append(s.Overrides, "nameservers")
311-
}
312-
if rc.md.SearchOverride {
313-
s.Overrides = append(s.Overrides, "search")
314-
}
315-
if rc.md.OptionsOverride {
316-
s.Overrides = append(s.Overrides, "options")
317-
}
318-
319-
const templateText = `{{if .Comments}}{{with .Md.Header}}{{.}}
320-
321-
{{end}}{{end}}{{range .NameServers -}}
322-
nameserver {{.}}
323-
{{end}}{{with .Search -}}
324-
search {{join . " "}}
325-
{{end}}{{with .Options -}}
326-
options {{join . " "}}
327-
{{end}}{{with .Other -}}
328-
{{join . "\n"}}
329-
{{end}}{{if .Comments}}
330-
# Based on host file: '{{.Md.SourcePath}}'{{with .Md.Transform}} ({{.}}){{end}}
331-
{{range .Md.Warnings -}}
332-
# {{.}}
333-
{{end -}}
334-
{{with .Md.ExtNameServers -}}
335-
# ExtServers: {{.}}
336-
{{end -}}
337-
{{with .Md.InvalidNSs -}}
338-
# Invalid nameservers: {{.}}
339-
{{end -}}
340-
# Overrides: {{.Overrides}}
341-
{{with .Md.NDotsFrom -}}
342-
# Option ndots from: {{.}}
343-
{{end -}}
344-
{{end -}}
345-
`
346-
347-
funcs := template.FuncMap{"join": strings.Join}
348-
var buf bytes.Buffer
349-
templ, err := template.New("summary").Funcs(funcs).Parse(templateText)
350-
if err != nil {
351-
return nil, errSystem{err}
286+
var b bytes.Buffer
287+
b.Grow(512) // estimated size for a regular resolv.conf we produce.
288+
289+
if comments && rc.md.Header != "" {
290+
b.WriteString(rc.md.Header + "\n")
291+
b.WriteByte('\n')
292+
}
293+
for _, ns := range rc.nameServers {
294+
b.WriteString("nameserver ")
295+
b.WriteString(ns.String())
296+
b.WriteByte('\n')
297+
}
298+
if len(rc.search) > 0 {
299+
b.WriteString("search ")
300+
for i, s := range rc.search {
301+
if i > 0 {
302+
b.WriteByte(' ')
303+
}
304+
b.WriteString(s)
305+
}
306+
b.WriteByte('\n')
352307
}
353-
if err := templ.Execute(&buf, s); err != nil {
354-
return nil, errSystem{err}
308+
if len(rc.options) > 0 {
309+
b.WriteString("options ")
310+
for i, s := range rc.options {
311+
if i > 0 {
312+
b.WriteByte(' ')
313+
}
314+
b.WriteString(s)
315+
}
316+
b.WriteByte('\n')
355317
}
356-
return buf.Bytes(), nil
357-
}
358-
359-
// WriteFile generates content and writes it to path. If hashPath is non-zero, it
360-
// also writes a file containing a hash of the content, to enable UserModified()
361-
// to determine whether the file has been modified.
362-
func (rc *ResolvConf) WriteFile(path, hashPath string, perm os.FileMode) error {
363-
content, err := rc.Generate(true)
364-
if err != nil {
365-
return err
318+
for _, o := range rc.other {
319+
b.WriteString(o)
320+
b.WriteByte('\n')
366321
}
367322

368-
// Write the resolv.conf file - it's bind-mounted into the container, so can't
369-
// move a temp file into place, just have to truncate and write it.
370-
if err := os.WriteFile(path, content, perm); err != nil {
371-
return errSystem{err}
372-
}
323+
if comments {
324+
b.WriteByte('\n')
325+
b.WriteString("# Based on host file: '" + rc.md.SourcePath + "'")
326+
if rc.md.Transform != "" {
327+
b.WriteString(" (" + rc.md.Transform + ")")
328+
}
329+
b.WriteByte('\n')
330+
for _, w := range rc.md.Warnings {
331+
b.WriteString("# ")
332+
b.WriteString(w)
333+
b.WriteByte('\n')
334+
}
335+
if len(rc.md.ExtNameServers) > 0 {
336+
b.WriteString("# ExtServers: [")
337+
for i, ext := range rc.md.ExtNameServers {
338+
if i > 0 {
339+
b.WriteByte(' ')
340+
}
341+
b.WriteString(ext.String())
342+
}
343+
b.WriteByte(']')
344+
b.WriteByte('\n')
345+
}
346+
if len(rc.md.InvalidNSs) > 0 {
347+
b.WriteString("# Invalid nameservers: [")
348+
for i, ext := range rc.md.InvalidNSs {
349+
if i > 0 {
350+
b.WriteByte(' ')
351+
}
352+
b.WriteString(ext)
353+
}
354+
b.WriteByte(']')
355+
b.WriteByte('\n')
356+
}
373357

374-
// Write the hash file.
375-
if hashPath != "" {
376-
hashFile, err := atomicwriter.New(hashPath, perm)
377-
if err != nil {
378-
return errSystem{err}
358+
b.WriteString("# Overrides: [")
359+
var overrides int
360+
if rc.md.NSOverride {
361+
b.WriteString("nameservers")
362+
overrides++
363+
}
364+
if rc.md.SearchOverride {
365+
if overrides > 0 {
366+
b.WriteByte(' ')
367+
}
368+
b.WriteString("search")
369+
overrides++
379370
}
380-
defer hashFile.Close()
371+
if rc.md.OptionsOverride {
372+
if overrides > 0 {
373+
b.WriteByte(' ')
374+
}
375+
b.WriteString("options")
376+
}
377+
b.WriteByte(']')
378+
b.WriteByte('\n')
381379

382-
if _, err = hashFile.Write([]byte(digest.FromBytes(content))); err != nil {
383-
return err
380+
if rc.md.NDotsFrom != "" {
381+
b.WriteString("# Option ndots from: " + rc.md.NDotsFrom + "\n")
384382
}
385383
}
386384

387-
return nil
385+
return b.Bytes(), nil
388386
}
389387

390-
// UserModified can be used to determine whether the resolv.conf file has been
391-
// modified since it was generated. It returns false with no error if the file
392-
// matches the hash, true with no error if the file no longer matches the hash,
393-
// and false with an error if the result cannot be determined.
394-
func UserModified(rcPath, rcHashPath string) (bool, error) {
395-
currRCHash, err := os.ReadFile(rcHashPath)
396-
if err != nil {
397-
// If the hash file doesn't exist, can only assume it hasn't been written
398-
// yet (so, the user hasn't modified the file it hashes).
399-
if errors.Is(err, fs.ErrNotExist) {
400-
return false, nil
401-
}
402-
return false, errors.Wrapf(err, "failed to read hash file %s", rcHashPath)
403-
}
404-
expected, err := digest.Parse(string(currRCHash))
405-
if err != nil {
406-
return false, errors.Wrapf(err, "failed to parse hash file %s", rcHashPath)
407-
}
408-
v := expected.Verifier()
409-
currRC, err := os.Open(rcPath)
410-
if err != nil {
411-
return false, errors.Wrapf(err, "failed to open %s to check for modifications", rcPath)
412-
}
413-
defer currRC.Close()
414-
if _, err := io.Copy(v, currRC); err != nil {
415-
return false, errors.Wrapf(err, "failed to hash %s to check for modifications", rcPath)
388+
func (rc *ResolvConf) processLine(line string) {
389+
// Strip blank lines and comments.
390+
if line == "" || line[0] == '#' || line[0] == ';' {
391+
return
416392
}
417-
return !v.Verified(), nil
418-
}
419393

420-
func (rc *ResolvConf) processLine(line string) {
421394
fields := strings.Fields(line)
422-
423-
// Strip blank lines and comments.
424-
if len(fields) == 0 || fields[0][0] == '#' || fields[0][0] == ';' {
395+
if len(fields) == 0 {
425396
return
426397
}
427398

@@ -470,8 +441,11 @@ func defaultNSAddrs(ipv6 bool) []netip.Addr {
470441
func removeInvalidNDots(options []string) []string {
471442
n := 0
472443
for _, opt := range options {
473-
k, v, _ := strings.Cut(opt, ":")
444+
k, v, hasSep := strings.Cut(opt, ":")
474445
if k == "ndots" {
446+
if !hasSep || v == "" {
447+
continue
448+
}
475449
ndots, err := strconv.Atoi(v)
476450
if err != nil || ndots < 0 {
477451
continue
@@ -483,16 +457,3 @@ func removeInvalidNDots(options []string) []string {
483457
clear(options[n:]) // Zero out the obsolete elements, for GC.
484458
return options[:n]
485459
}
486-
487-
// errSystem implements [github.com/docker/docker/errdefs.ErrSystem].
488-
//
489-
// We don't use the errdefs helpers here, because the resolvconf package
490-
// is imported in BuildKit, and this is the only location that used the
491-
// errdefs package outside of the client.
492-
type errSystem struct{ error }
493-
494-
func (errSystem) System() {}
495-
496-
func (e errSystem) Unwrap() error {
497-
return e.error
498-
}
Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import (
55
"net/netip"
66
"sync"
77

8-
"github.com/containerd/log"
8+
"github.com/moby/buildkit/util/bklog"
99
)
1010

1111
const (
@@ -49,7 +49,7 @@ func Path() string {
4949
ns := rc.nameServers
5050
if len(ns) == 1 && ns[0] == netip.MustParseAddr("127.0.0.53") {
5151
pathAfterSystemdDetection = alternatePath
52-
log.G(context.TODO()).Infof("detected 127.0.0.53 nameserver, assuming systemd-resolved, so using resolv.conf: %s", alternatePath)
52+
bklog.G(context.TODO()).Infof("detected 127.0.0.53 nameserver, assuming systemd-resolved, so using resolv.conf: %s", alternatePath)
5353
}
5454
})
5555
return pathAfterSystemdDetection

0 commit comments

Comments
 (0)