From e3d8e79d9569f3e328facac7978ed3c2ad786eb7 Mon Sep 17 00:00:00 2001 From: Qi Wang Date: Mon, 11 Mar 2019 12:14:29 -0400 Subject: move formats pkg to and vendor from buildah Signed-off-by: Qi Wang --- .../containers/buildah/imagebuildah/build.go | 2 +- .../buildah/imagebuildah/chroot_symlink.go | 10 +- .../containers/buildah/pkg/formats/formats.go | 171 +++++++++++++++++++++ .../containers/buildah/pkg/formats/templates.go | 78 ++++++++++ vendor/github.com/containers/buildah/run.go | 90 ++++++----- 5 files changed, 308 insertions(+), 43 deletions(-) create mode 100644 vendor/github.com/containers/buildah/pkg/formats/formats.go create mode 100644 vendor/github.com/containers/buildah/pkg/formats/templates.go (limited to 'vendor/github.com') diff --git a/vendor/github.com/containers/buildah/imagebuildah/build.go b/vendor/github.com/containers/buildah/imagebuildah/build.go index d69eab52f..4f0ffac1c 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/build.go +++ b/vendor/github.com/containers/buildah/imagebuildah/build.go @@ -293,7 +293,7 @@ func (b *Executor) Preserve(path string) error { // Try and resolve the symlink (if one exists) // Set archivedPath and path based on whether a symlink is found or not - if symLink, err := ResolveSymLink(b.mountPoint, path); err == nil { + if symLink, err := resolveSymlink(b.mountPoint, path); err == nil { archivedPath = filepath.Join(b.mountPoint, symLink) path = symLink } else { diff --git a/vendor/github.com/containers/buildah/imagebuildah/chroot_symlink.go b/vendor/github.com/containers/buildah/imagebuildah/chroot_symlink.go index 6feedf6a5..86bf7653b 100644 --- a/vendor/github.com/containers/buildah/imagebuildah/chroot_symlink.go +++ b/vendor/github.com/containers/buildah/imagebuildah/chroot_symlink.go @@ -24,9 +24,7 @@ func init() { reexec.Register(symlinkModifiedTime, resolveSymlinkTimeModified) } -// main() for grandparent subprocess. Its main job is to shuttle stdio back -// and forth, managing a pseudo-terminal if we want one, for our child, the -// parent subprocess. +// main() for resolveSymlink()'s subprocess. func resolveChrootedSymlinks() { status := 0 flag.Parse() @@ -57,9 +55,9 @@ func resolveChrootedSymlinks() { os.Exit(status) } -// ResolveSymLink (in the grandparent process) resolves any symlink in filename +// resolveSymlink uses a child subprocess to resolve any symlinks in filename // in the context of rootdir. -func ResolveSymLink(rootdir, filename string) (string, error) { +func resolveSymlink(rootdir, filename string) (string, error) { // The child process expects a chroot and one path that // will be consulted relative to the chroot directory and evaluated // for any symbolic links present. @@ -253,7 +251,7 @@ func hasSymlink(path string) (bool, string, error) { } // if the symlink points to a relative path, prepend the path till now to the resolved path if !filepath.IsAbs(targetDir) { - targetDir = filepath.Join(path, targetDir) + targetDir = filepath.Join(filepath.Dir(path), targetDir) } // run filepath.Clean to remove the ".." from relative paths return true, filepath.Clean(targetDir), nil diff --git a/vendor/github.com/containers/buildah/pkg/formats/formats.go b/vendor/github.com/containers/buildah/pkg/formats/formats.go new file mode 100644 index 000000000..37f9b8a20 --- /dev/null +++ b/vendor/github.com/containers/buildah/pkg/formats/formats.go @@ -0,0 +1,171 @@ +package formats + +import ( + "bytes" + "encoding/json" + "fmt" + "io" + "os" + "strings" + "text/tabwriter" + "text/template" + + "github.com/ghodss/yaml" + "github.com/pkg/errors" + "golang.org/x/crypto/ssh/terminal" +) + +const ( + // JSONString const to save on duplicate variable names + JSONString = "json" + // IDString const to save on duplicates for Go templates + IDString = "{{.ID}}" + + parsingErrorStr = "Template parsing error" +) + +// Writer interface for outputs +type Writer interface { + Out() error +} + +// JSONStructArray for JSON output +type JSONStructArray struct { + Output []interface{} +} + +// StdoutTemplateArray for Go template output +type StdoutTemplateArray struct { + Output []interface{} + Template string + Fields map[string]string +} + +// JSONStruct for JSON output +type JSONStruct struct { + Output interface{} +} + +// StdoutTemplate for Go template output +type StdoutTemplate struct { + Output interface{} + Template string + Fields map[string]string +} + +// YAMLStruct for YAML output +type YAMLStruct struct { + Output interface{} +} + +func setJSONFormatEncoder(isTerminal bool, w io.Writer) *json.Encoder { + enc := json.NewEncoder(w) + enc.SetIndent("", " ") + if isTerminal { + enc.SetEscapeHTML(false) + } + return enc +} + +// Out method for JSON Arrays +func (j JSONStructArray) Out() error { + buf := bytes.NewBuffer(nil) + enc := setJSONFormatEncoder(terminal.IsTerminal(int(os.Stdout.Fd())), buf) + if err := enc.Encode(j.Output); err != nil { + return err + } + data := buf.Bytes() + + // JSON returns a byte array with a literal null [110 117 108 108] in it + // if it is passed empty data. We used bytes.Compare to see if that is + // the case. + if diff := bytes.Compare(data, []byte("null")); diff == 0 { + data = []byte("[]") + } + + // If the we did get NULL back, we should spit out {} which is + // at least valid JSON for the consumer. + fmt.Printf("%s", data) + humanNewLine() + return nil +} + +// Out method for Go templates +func (t StdoutTemplateArray) Out() error { + w := tabwriter.NewWriter(os.Stdout, 0, 0, 3, ' ', 0) + if strings.HasPrefix(t.Template, "table") { + // replace any spaces with tabs in template so that tabwriter can align it + t.Template = strings.Replace(strings.TrimSpace(t.Template[5:]), " ", "\t", -1) + headerTmpl, err := template.New("header").Funcs(headerFunctions).Parse(t.Template) + if err != nil { + return errors.Wrapf(err, parsingErrorStr) + } + err = headerTmpl.Execute(w, t.Fields) + if err != nil { + return err + } + fmt.Fprintln(w, "") + } + t.Template = strings.Replace(t.Template, " ", "\t", -1) + tmpl, err := template.New("image").Funcs(basicFunctions).Parse(t.Template) + if err != nil { + return errors.Wrapf(err, parsingErrorStr) + } + for i, raw := range t.Output { + basicTmpl := tmpl.Funcs(basicFunctions) + if err := basicTmpl.Execute(w, raw); err != nil { + return errors.Wrapf(err, parsingErrorStr) + } + if i != len(t.Output)-1 { + fmt.Fprintln(w, "") + continue + } + } + fmt.Fprintln(w, "") + return w.Flush() +} + +// Out method for JSON struct +func (j JSONStruct) Out() error { + data, err := json.MarshalIndent(j.Output, "", " ") + if err != nil { + return err + } + fmt.Printf("%s", data) + humanNewLine() + return nil +} + +//Out method for Go templates +func (t StdoutTemplate) Out() error { + tmpl, err := template.New("image").Parse(t.Template) + if err != nil { + return errors.Wrapf(err, "template parsing error") + } + err = tmpl.Execute(os.Stdout, t.Output) + if err != nil { + return err + } + humanNewLine() + return nil +} + +// Out method for YAML +func (y YAMLStruct) Out() error { + var buf []byte + var err error + buf, err = yaml.Marshal(y.Output) + if err != nil { + return err + } + fmt.Printf("%s", string(buf)) + humanNewLine() + return nil +} + +// humanNewLine prints a new line at the end of the output only if stdout is the terminal +func humanNewLine() { + if terminal.IsTerminal(int(os.Stdout.Fd())) { + fmt.Println() + } +} diff --git a/vendor/github.com/containers/buildah/pkg/formats/templates.go b/vendor/github.com/containers/buildah/pkg/formats/templates.go new file mode 100644 index 000000000..c2582552a --- /dev/null +++ b/vendor/github.com/containers/buildah/pkg/formats/templates.go @@ -0,0 +1,78 @@ +package formats + +import ( + "bytes" + "encoding/json" + "strings" + "text/template" +) + +// basicFunctions are the set of initial +// functions provided to every template. +var basicFunctions = template.FuncMap{ + "json": func(v interface{}) string { + buf := &bytes.Buffer{} + enc := json.NewEncoder(buf) + enc.SetEscapeHTML(false) + _ = enc.Encode(v) + // Remove the trailing new line added by the encoder + return strings.TrimSpace(buf.String()) + }, + "split": strings.Split, + "join": strings.Join, + "title": strings.Title, + "lower": strings.ToLower, + "upper": strings.ToUpper, + "pad": padWithSpace, + "truncate": truncateWithLength, +} + +// HeaderFunctions are used to created headers of a table. +// This is a replacement of basicFunctions for header generation +// because we want the header to remain intact. +// Some functions like `split` are irrelevant so not added. +var headerFunctions = template.FuncMap{ + "json": func(v string) string { + return v + }, + "title": func(v string) string { + return v + }, + "lower": func(v string) string { + return v + }, + "upper": func(v string) string { + return v + }, + "truncate": func(v string, l int) string { + return v + }, +} + +// Parse creates a new anonymous template with the basic functions +// and parses the given format. +func Parse(format string) (*template.Template, error) { + return NewParse("", format) +} + +// NewParse creates a new tagged template with the basic functions +// and parses the given format. +func NewParse(tag, format string) (*template.Template, error) { + return template.New(tag).Funcs(basicFunctions).Parse(format) +} + +// padWithSpace adds whitespace to the input if the input is non-empty +func padWithSpace(source string, prefix, suffix int) string { + if source == "" { + return source + } + return strings.Repeat(" ", prefix) + source + strings.Repeat(" ", suffix) +} + +// truncateWithLength truncates the source string up to the length provided by the input +func truncateWithLength(source string, length int) string { + if len(source) < length { + return source + } + return source[:length] +} diff --git a/vendor/github.com/containers/buildah/run.go b/vendor/github.com/containers/buildah/run.go index 4d6d28380..f56ce30b1 100644 --- a/vendor/github.com/containers/buildah/run.go +++ b/vendor/github.com/containers/buildah/run.go @@ -1,7 +1,6 @@ package buildah import ( - "bufio" "bytes" "encoding/json" "fmt" @@ -272,36 +271,6 @@ func addRlimits(ulimit []string, g *generate.Generator) error { return nil } -func addHosts(hosts []string, w io.Writer) error { - buf := bufio.NewWriter(w) - for _, host := range hosts { - values := strings.SplitN(host, ":", 2) - if len(values) != 2 { - return errors.Errorf("unable to parse host entry %q: incorrect format", host) - } - if values[0] == "" { - return errors.Errorf("hostname in host entry %q is empty", host) - } - if values[1] == "" { - return errors.Errorf("IP address in host entry %q is empty", host) - } - fmt.Fprintf(buf, "%s\t%s\n", values[1], values[0]) - } - return buf.Flush() -} - -func addHostsToFile(hosts []string, filename string) error { - if len(hosts) == 0 { - return nil - } - file, err := os.OpenFile(filename, os.O_APPEND|os.O_WRONLY, os.ModeAppend) - if err != nil { - return errors.Wrapf(err, "error creating hosts file %q", filename) - } - defer file.Close() - return addHosts(hosts, file) -} - func addCommonOptsToSpec(commonOpts *CommonBuildOptions, g *generate.Generator) error { // Resources - CPU if commonOpts.CPUPeriod != 0 { @@ -638,6 +607,59 @@ func (b *Builder) addNetworkConfig(rdir, hostPath string, chownOpts *idtools.IDP return cfile, nil } +// generateHosts creates a containers hosts file +func (b *Builder) generateHosts(rdir, hostname string, addHosts []string, chownOpts *idtools.IDPair) (string, error) { + hostPath := "/etc/hosts" + stat, err := os.Stat(hostPath) + if err != nil { + return "", errors.Wrapf(err, "error statting %q for container %q", hostPath, b.ContainerID) + } + + hosts := bytes.NewBufferString("# Generated by Buildah\n") + orig, err := ioutil.ReadFile(hostPath) + if err != nil { + return "", errors.Wrapf(err, "unable to read %s", hostPath) + } + hosts.Write(orig) + for _, host := range addHosts { + // verify the host format + values := strings.SplitN(host, ":", 2) + if len(values) != 2 { + return "", errors.Errorf("unable to parse host entry %q: incorrect format", host) + } + if values[0] == "" { + return "", errors.Errorf("hostname in host entry %q is empty", host) + } + if values[1] == "" { + return "", errors.Errorf("IP address in host entry %q is empty", host) + } + hosts.Write([]byte(fmt.Sprintf("%s\t%s\n", values[1], values[0]))) + } + + if hostname != "" { + hosts.Write([]byte(fmt.Sprintf("127.0.0.1 %s\n", hostname))) + hosts.Write([]byte(fmt.Sprintf("::1 %s\n", hostname))) + } + cfile := filepath.Join(rdir, filepath.Base(hostPath)) + if err = ioutils.AtomicWriteFile(cfile, hosts.Bytes(), stat.Mode().Perm()); err != nil { + return "", errors.Wrapf(err, "error writing /etc/hosts into the container") + } + uid := int(stat.Sys().(*syscall.Stat_t).Uid) + gid := int(stat.Sys().(*syscall.Stat_t).Gid) + if chownOpts != nil { + uid = chownOpts.UID + gid = chownOpts.GID + } + if err = os.Chown(cfile, uid, gid); err != nil { + return "", errors.Wrapf(err, "error chowning file %q for container %q", cfile, b.ContainerID) + } + if err := label.Relabel(cfile, b.MountLabel, false); err != nil { + return "", errors.Wrapf(err, "error relabeling %q in container %q", cfile, b.ContainerID) + } + + return cfile, nil +} + func setupMaskedPaths(g *generate.Generator) { for _, mp := range []string{ "/proc/acpi", @@ -1081,15 +1103,11 @@ func (b *Builder) Run(command []string, options RunOptions) error { volumes := b.Volumes() if !contains(volumes, "/etc/hosts") { - hostFile, err := b.addNetworkConfig(path, "/etc/hosts", rootIDPair) + hostFile, err := b.generateHosts(path, spec.Hostname, b.CommonBuildOpts.AddHost, rootIDPair) if err != nil { return err } bindFiles["/etc/hosts"] = hostFile - - if err := addHostsToFile(b.CommonBuildOpts.AddHost, hostFile); err != nil { - return err - } } if !contains(volumes, "/etc/resolv.conf") { -- cgit v1.2.3-54-g00ecf