1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163
|
package main
import (
"fmt"
"io/ioutil"
"strings"
"github.com/containers/buildah"
buildahcli "github.com/containers/buildah/pkg/cli"
"github.com/pkg/errors"
"github.com/spf13/cobra"
)
type addCopyResults struct {
addHistory bool
chown string
quiet bool
ignoreFile string
contextdir string
}
func init() {
var (
addDescription = "\n Adds the contents of a file, URL, or directory to a container's working\n directory. If a local file appears to be an archive, its contents are\n extracted and added instead of the archive file itself."
copyDescription = "\n Copies the contents of a file, URL, or directory into a container's working\n directory."
addOpts addCopyResults
copyOpts addCopyResults
)
addCommand := &cobra.Command{
Use: "add",
Short: "Add content to the container",
Long: addDescription,
RunE: func(cmd *cobra.Command, args []string) error {
return addCmd(cmd, args, addOpts)
},
Example: `buildah add containerID '/myapp/app.conf'
buildah add containerID '/myapp/app.conf' '/myapp/app.conf'`,
Args: cobra.MinimumNArgs(1),
}
addCommand.SetUsageTemplate(UsageTemplate())
copyCommand := &cobra.Command{
Use: "copy",
Short: "Copy content into the container",
Long: copyDescription,
RunE: func(cmd *cobra.Command, args []string) error {
return copyCmd(cmd, args, copyOpts)
},
Example: `buildah copy containerID '/myapp/app.conf'
buildah copy containerID '/myapp/app.conf' '/myapp/app.conf'`,
Args: cobra.MinimumNArgs(1),
}
copyCommand.SetUsageTemplate(UsageTemplate())
addFlags := addCommand.Flags()
addFlags.SetInterspersed(false)
addFlags.BoolVar(&addOpts.addHistory, "add-history", false, "add an entry for this operation to the image's history. Use BUILDAH_HISTORY environment variable to override. (default false)")
addFlags.StringVar(&addOpts.chown, "chown", "", "set the user and group ownership of the destination content")
addFlags.StringVar(&addOpts.contextdir, "contextdir", "", "context directory path")
addFlags.StringVar(&addOpts.ignoreFile, "ignorefile", "", "path to .dockerignore file")
addFlags.BoolVarP(&addOpts.quiet, "quiet", "q", false, "don't output a digest of the newly-added/copied content")
// TODO We could avoid some duplication here if need-be; given it is small, leaving as is
copyFlags := copyCommand.Flags()
copyFlags.SetInterspersed(false)
copyFlags.BoolVar(©Opts.addHistory, "add-history", false, "add an entry for this operation to the image's history. Use BUILDAH_HISTORY environment variable to override. (default false)")
copyFlags.StringVar(©Opts.chown, "chown", "", "set the user and group ownership of the destination content")
copyFlags.StringVar(©Opts.ignoreFile, "ignorefile", "", "path to .dockerignore file")
copyFlags.StringVar(©Opts.contextdir, "contextdir", "", "context directory path")
copyFlags.BoolVarP(©Opts.quiet, "quiet", "q", false, "don't output a digest of the newly-added/copied content")
rootCmd.AddCommand(addCommand)
rootCmd.AddCommand(copyCommand)
}
func addAndCopyCmd(c *cobra.Command, args []string, verb string, extractLocalArchives bool, iopts addCopyResults) error {
if len(args) == 0 {
return errors.Errorf("container ID must be specified")
}
name := args[0]
args = Tail(args)
if len(args) == 0 {
return errors.Errorf("src must be specified")
}
if err := buildahcli.VerifyFlagsArgsOrder(args); err != nil {
return err
}
// If list is greater than one, the last item is the destination
dest := ""
size := len(args)
if size > 1 {
dest = args[size-1]
args = args[:size-1]
}
store, err := getStore(c)
if err != nil {
return err
}
builder, err := openBuilder(getContext(), store, name)
if err != nil {
return errors.Wrapf(err, "error reading build container %q", name)
}
builder.ContentDigester.Restart()
options := buildah.AddAndCopyOptions{
Chown: iopts.chown,
ContextDir: iopts.contextdir,
}
if iopts.ignoreFile != "" {
if iopts.contextdir == "" {
return errors.Errorf("--ignore options requires that you specify a context dir using --contextdir")
}
excludes, err := parseDockerignore(iopts.ignoreFile)
if err != nil {
return err
}
options.Excludes = excludes
}
err = builder.Add(dest, extractLocalArchives, options, args...)
if err != nil {
return errors.Wrapf(err, "error adding content to container %q", builder.Container)
}
contentType, digest := builder.ContentDigester.Digest()
if !iopts.quiet {
fmt.Printf("%s\n", digest.Hex())
}
if contentType != "" {
contentType = contentType + ":"
}
conditionallyAddHistory(builder, c, "/bin/sh -c #(nop) %s %s%s", verb, contentType, digest.Hex())
return builder.Save()
}
func addCmd(c *cobra.Command, args []string, iopts addCopyResults) error {
return addAndCopyCmd(c, args, "ADD", true, iopts)
}
func copyCmd(c *cobra.Command, args []string, iopts addCopyResults) error {
return addAndCopyCmd(c, args, "COPY", false, iopts)
}
func parseDockerignore(ignoreFile string) ([]string, error) {
var excludes []string
ignore, err := ioutil.ReadFile(ignoreFile)
if err != nil {
return excludes, err
}
for _, e := range strings.Split(string(ignore), "\n") {
if len(e) == 0 || e[0] == '#' {
continue
}
excludes = append(excludes, e)
}
return excludes, nil
}
|