Beispiel #1
0
func TestReplaceLastFrom(t *testing.T) {
	tests := []struct {
		original string
		image    string
		want     string
	}{
		{
			original: `# no FROM instruction`,
			image:    "centos",
			want:     ``,
		},
		{
			original: `FROM scratch
# FROM busybox
RUN echo "hello world"
`,
			image: "centos",
			want: `FROM centos
RUN echo "hello world"
`,
		},
		{
			original: `FROM scratch
FROM busybox
RUN echo "hello world"
`,
			image: "centos",
			want: `FROM scratch
FROM centos
RUN echo "hello world"
`,
		},
	}
	for i, test := range tests {
		got, err := parser.Parse(strings.NewReader(test.original))
		if err != nil {
			t.Errorf("test[%d]: %v", i, err)
			continue
		}
		want, err := parser.Parse(strings.NewReader(test.want))
		if err != nil {
			t.Errorf("test[%d]: %v", i, err)
			continue
		}
		replaceLastFrom(got, test.image)
		if !bytes.Equal(dockerfile.ParseTreeToDockerfile(got), dockerfile.ParseTreeToDockerfile(want)) {
			t.Errorf("test[%d]: replaceLastFrom(node, %+v) = %+v; want %+v", i, test.image, got, want)
			t.Logf("resulting Dockerfile:\n%s", dockerfile.ParseTreeToDockerfile(got))
		}
	}
}
Beispiel #2
0
// FromImage updates the builder to use the provided image (resetting RunConfig
// and recording the image environment), and updates the node with any ONBUILD
// statements extracted from the parent image.
func (b *Builder) FromImage(image *docker.Image, node *parser.Node) error {
	SplitChildren(node, command.From)

	b.RunConfig = *image.Config
	b.Env = b.RunConfig.Env
	b.RunConfig.Env = nil

	// Check to see if we have a default PATH, note that windows won't
	// have one as its set by HCS
	if runtime.GOOS != "windows" && !hasEnvName(b.Env, "PATH") {
		b.RunConfig.Env = append(b.RunConfig.Env, "PATH="+defaultPathEnv)
	}

	// Join the image onbuild statements into node
	if image.Config == nil || len(image.Config.OnBuild) == 0 {
		return nil
	}
	extra, err := parser.Parse(bytes.NewBufferString(strings.Join(image.Config.OnBuild, "\n")))
	if err != nil {
		return err
	}
	for _, child := range extra.Children {
		switch strings.ToUpper(child.Value) {
		case "ONBUILD":
			return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
		case "MAINTAINER", "FROM":
			return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", child.Value)
		}
	}
	node.Children = append(extra.Children, node.Children...)
	// Since we've processed the OnBuild statements, clear them from the runconfig state.
	b.RunConfig.OnBuild = nil
	return nil
}
Beispiel #3
0
// NewBuilder creates a new Dockerfile builder from an optional dockerfile and a Config.
// If dockerfile is nil, the Dockerfile specified by Config.DockerfileName,
// will be read from the Context passed to Build().
func NewBuilder(clientCtx context.Context, config *types.ImageBuildOptions, backend builder.Backend, buildContext builder.Context, dockerfile io.ReadCloser) (b *Builder, err error) {
	if config == nil {
		config = new(types.ImageBuildOptions)
	}
	if config.BuildArgs == nil {
		config.BuildArgs = make(map[string]string)
	}
	ctx, cancel := context.WithCancel(clientCtx)
	b = &Builder{
		clientCtx:        ctx,
		cancel:           cancel,
		options:          config,
		Stdout:           os.Stdout,
		Stderr:           os.Stderr,
		docker:           backend,
		context:          buildContext,
		runConfig:        new(container.Config),
		tmpContainers:    map[string]struct{}{},
		id:               stringid.GenerateNonCryptoID(),
		allowedBuildArgs: make(map[string]bool),
	}
	if dockerfile != nil {
		b.dockerfile, err = parser.Parse(dockerfile)
		if err != nil {
			return nil, err
		}
	}

	return b, nil
}
Beispiel #4
0
// BuildFromConfig builds directly from `changes`, treating it as if it were the contents of a Dockerfile
// It will:
// - Call parse.Parse() to get an AST root for the concatenated Dockerfile entries.
// - Do build by calling builder.dispatch() to call all entries' handling routines
//
// BuildFromConfig is used by the /commit endpoint, with the changes
// coming from the query parameter of the same name.
//
// TODO: Remove?
func BuildFromConfig(config *container.Config, changes []string) (*container.Config, error) {
	ast, err := parser.Parse(bytes.NewBufferString(strings.Join(changes, "\n")))
	if err != nil {
		return nil, err
	}

	// ensure that the commands are valid
	for _, n := range ast.Children {
		if !validCommitCommands[n.Value] {
			return nil, fmt.Errorf("%s is not a valid change command", n.Value)
		}
	}

	b, err := NewBuilder(context.Background(), nil, nil, nil, nil)
	if err != nil {
		return nil, err
	}
	b.runConfig = config
	b.Stdout = ioutil.Discard
	b.Stderr = ioutil.Discard
	b.disableCommit = true

	for i, n := range ast.Children {
		if err := b.dispatch(i, n); err != nil {
			return nil, err
		}
	}

	return b.runConfig, nil
}
Beispiel #5
0
func main() {
	var f *os.File
	var err error

	if len(os.Args) < 2 {
		fmt.Println("please supply filename(s)")
		os.Exit(1)
	}

	for _, fn := range os.Args[1:] {
		f, err = os.Open(fn)
		if err != nil {
			panic(err)
		}

		d := parser.Directive{LookingForDirectives: true}
		parser.SetEscapeToken(parser.DefaultEscapeToken, &d)

		ast, err := parser.Parse(f, &d)
		if err != nil {
			panic(err)
		} else {
			fmt.Println(ast.Dump())
		}
	}
}
// TestNextValuesOnbuild tests calling nextValues with ONBUILD instructions as
// input.
func TestNextValuesOnbuild(t *testing.T) {
	testCases := map[string][]string{
		`ONBUILD ADD . /app/src`:             {".", "/app/src"},
		`ONBUILD RUN echo "Hello universe!"`: {`echo "Hello universe!"`},
	}
	for original, want := range testCases {
		node, err := parser.Parse(strings.NewReader(original))
		if err != nil {
			t.Fatalf("parse error: %s: %v", original, err)
		}
		if len(node.Children) != 1 {
			t.Fatalf("unexpected number of children in test case: %s", original)
		}
		// The Docker parser always wrap instructions in a root node.
		// Look at the node representing the instruction following
		// ONBUILD, the one and only one in each test case.
		node = node.Children[0].Next
		if node == nil || len(node.Children) != 1 {
			t.Fatalf("unexpected number of children in ONBUILD instruction of test case: %s", original)
		}
		node = node.Children[0]
		if got := nextValues(node); !reflect.DeepEqual(got, want) {
			t.Errorf("nextValues(%+v) = %#v; want %#v", node, got, want)
		}
	}
}
Beispiel #7
0
// NewBuilder creates a new Dockerfile builder from an optional dockerfile and a Config.
// If dockerfile is nil, the Dockerfile specified by Config.DockerfileName,
// will be read from the Context passed to Build().
func NewBuilder(config *Config, docker builder.Backend, context builder.Context, dockerfile io.ReadCloser) (b *Builder, err error) {
	if config == nil {
		config = new(Config)
	}
	if config.BuildArgs == nil {
		config.BuildArgs = make(map[string]string)
	}
	b = &Builder{
		Config:           config,
		Stdout:           os.Stdout,
		Stderr:           os.Stderr,
		docker:           docker,
		context:          context,
		runConfig:        new(container.Config),
		tmpContainers:    map[string]struct{}{},
		cancelled:        make(chan struct{}),
		id:               stringid.GenerateNonCryptoID(),
		allowedBuildArgs: make(map[string]bool),
	}
	if dockerfile != nil {
		b.dockerfile, err = parser.Parse(dockerfile)
		if err != nil {
			return nil, err
		}
	}

	return b, nil
}
Beispiel #8
0
func TestRun(t *testing.T) {
	f, err := os.Open("dockerclient/testdata/Dockerfile.add")
	if err != nil {
		t.Fatal(err)
	}
	node, err := parser.Parse(f)
	if err != nil {
		t.Fatal(err)
	}
	b := NewBuilder()
	from, err := b.From(node)
	if err != nil {
		t.Fatal(err)
	}
	if from != "busybox" {
		t.Fatalf("unexpected from: %s", from)
	}
	for _, child := range node.Children {
		step := b.Step()
		if err := step.Resolve(child); err != nil {
			t.Fatal(err)
		}
		if err := b.Run(step, LogExecutor); err != nil {
			t.Fatal(err)
		}
	}
	t.Logf("config: %#v", b.Config())
	t.Logf(node.Dump())
}
Beispiel #9
0
func get_label_value(dockerfile_name string, label_name string) (label_value string, err error) {
	f, err := os.Open(dockerfile_name)
	if err != nil {
		return
	}
	defer f.Close()
	ast, err := parser.Parse(f)
	if err != nil {
		return
	}
	// root node only has children and represents the entire file
	for _, n := range ast.Children {
		if n.Value == "label" {
			// found a label statement, see if it has our label
			for nn := n.Next; nn != nil; nn = nn.Next.Next {
				if nn.Value == label_name {
					nn = nn.Next
					label_value, err = strconv.Unquote(strings.Replace(nn.Value, "$ {", "${", -1))
					return
				}
			}
		}
	}
	return "", nil
}
Beispiel #10
0
//GuessFromDockerfile will guess some information from a Dockerfile file
//guessing means to get all the LABELs and process them somehow
func GuessFromDockerfile(filename string) (map[string]string, string, error) {
	dockerfileContent, err := ioutil.ReadFile(filename)

	if err != nil {
		jww.ERROR.Println("failed to read the Dockerfile")
		return nil, "", err
	}

	// lets parse the Dockerfile
	ast, err := parser.Parse(bytes.NewReader(dockerfileContent))

	if err != nil {
		jww.FATAL.Println("Dockerfile parse error")
		return nil, "", err
	}

	var result map[string]string
	result = make(map[string]string)

	for _, s := range guessFromLabels(ast) {
		result[strings.Replace(s.Key, "\"", "", -1)] = strings.Replace(s.Value, "\"", "", -1)
	}

	for key, value := range result {
		jww.DEBUG.Printf("result: LABEL: %s;\t VALUE: %s\n", key, value)
	}

	// if --experimental is true, we print all snippets to STDOUT
	resultingNulecule := ""
	if viper.GetBool("Experimental") {
		resultingNulecule = snippetsFromLabelsMap(result)
	}

	return result, resultingNulecule, err
}
// TestNextValues tests calling nextValues with multiple valid combinations of
// input.
func TestNextValues(t *testing.T) {
	testCases := map[string][]string{
		`FROM busybox:latest`:           {"busybox:latest"},
		`MAINTAINER [email protected]`: {"*****@*****.**"},
		`LABEL version=1.0`:             {"version", "1.0"},
		`EXPOSE 8080`:                   {"8080"},
		`VOLUME /var/run/www`:           {"/var/run/www"},
		`ENV PATH=/bin`:                 {"PATH", "/bin"},
		`ADD file /home/`:               {"file", "/home/"},
		`COPY dir/ /tmp/`:               {"dir/", "/tmp/"},
		`RUN echo "Hello world!"`:       {`echo "Hello world!"`},
		`ENTRYPOINT /bin/sh`:            {"/bin/sh"},
		`CMD ["-c", "env"]`:             {"-c", "env"},
		`USER 1001`:                     {"1001"},
		`WORKDIR /home`:                 {"/home"},
	}
	for original, want := range testCases {
		node, err := parser.Parse(strings.NewReader(original))
		if err != nil {
			t.Fatalf("parse error: %s: %v", original, err)
		}
		if len(node.Children) != 1 {
			t.Fatalf("unexpected number of children in test case: %s", original)
		}
		// The Docker parser always wrap instructions in a root node.
		// Look at the node representing the first instruction, the one
		// and only one in each test case.
		node = node.Children[0]
		if got := nextValues(node); !reflect.DeepEqual(got, want) {
			t.Errorf("nextValues(%+v) = %#v; want %#v", node, got, want)
		}
	}
}
Beispiel #12
0
func (b *Builder) parseDockerfile() error {
	f, err := b.context.Open(b.options.Dockerfile)
	if err != nil {
		if os.IsNotExist(err) {
			return fmt.Errorf("Cannot locate specified Dockerfile: %s", b.options.Dockerfile)
		}
		return err
	}
	defer f.Close()
	if f, ok := f.(*os.File); ok {
		// ignoring error because Open already succeeded
		fi, err := f.Stat()
		if err != nil {
			return fmt.Errorf("Unexpected error reading Dockerfile: %v", err)
		}
		if fi.Size() == 0 {
			return fmt.Errorf("The Dockerfile (%s) cannot be empty", b.options.Dockerfile)
		}
	}
	b.dockerfile, err = parser.Parse(f, &b.directive)
	if err != nil {
		return err
	}

	return nil
}
Beispiel #13
0
func executeTestCase(t *testing.T, testCase dispatchTestCase) {
	contextDir, cleanup := createTestTempDir(t, "", "builder-dockerfile-test")
	defer cleanup()

	for filename, content := range testCase.files {
		createTestTempFile(t, contextDir, filename, content, 0777)
	}

	tarStream, err := archive.Tar(contextDir, archive.Uncompressed)

	if err != nil {
		t.Fatalf("Error when creating tar stream: %s", err)
	}

	defer func() {
		if err = tarStream.Close(); err != nil {
			t.Fatalf("Error when closing tar stream: %s", err)
		}
	}()

	context, err := builder.MakeTarSumContext(tarStream)

	if err != nil {
		t.Fatalf("Error when creating tar context: %s", err)
	}

	defer func() {
		if err = context.Close(); err != nil {
			t.Fatalf("Error when closing tar context: %s", err)
		}
	}()

	r := strings.NewReader(testCase.dockerfile)
	d := parser.Directive{}
	parser.SetEscapeToken(parser.DefaultEscapeToken, &d)
	n, err := parser.Parse(r, &d)

	if err != nil {
		t.Fatalf("Error when parsing Dockerfile: %s", err)
	}

	config := &container.Config{}
	options := &types.ImageBuildOptions{}

	b := &Builder{runConfig: config, options: options, Stdout: ioutil.Discard, context: context}

	err = b.dispatch(0, n.Children[0])

	if err == nil {
		t.Fatalf("No error when executing test %s", testCase.name)
	}

	if !strings.Contains(err.Error(), testCase.expectedError) {
		t.Fatalf("Wrong error message. Should be \"%s\". Got \"%s\"", testCase.expectedError, err.Error())
	}

}
Beispiel #14
0
func NewDockerfile(contents string) (Dockerfile, error) {
	if len(contents) == 0 {
		return nil, errors.New("Dockerfile is empty")
	}
	node, err := parser.Parse(strings.NewReader(contents))
	if err != nil {
		return nil, err
	}
	return dockerfileContents{node, contents}, nil
}
Beispiel #15
0
// DockerfileRead reads a Dockerfile as io.Reader
func DockerfileRead(input io.Reader) (*Dockerfile, error) {
	dockerfile := Dockerfile{}

	root, err := parser.Parse(input)
	if err != nil {
		return nil, err
	}
	dockerfile.root = root

	return &dockerfile, nil
}
Beispiel #16
0
func parseDockerfile(dockerfilePath string) (*parser.Node, error) {
	f, err := os.Open(dockerfilePath)
	defer f.Close()
	if err != nil {
		return nil, err
	}

	// Parse the Dockerfile.
	node, err := parser.Parse(f)
	if err != nil {
		return nil, err
	}
	return node, nil
}
Beispiel #17
0
// readDockerfile reads a Dockerfile from the current context.
func (b *Builder) readDockerfile() error {
	// If no -f was specified then look for 'Dockerfile'. If we can't find
	// that then look for 'dockerfile'.  If neither are found then default
	// back to 'Dockerfile' and use that in the error message.
	if b.DockerfileName == "" {
		b.DockerfileName = api.DefaultDockerfileName
		if _, _, err := b.context.Stat(b.DockerfileName); os.IsNotExist(err) {
			lowercase := strings.ToLower(b.DockerfileName)
			if _, _, err := b.context.Stat(lowercase); err == nil {
				b.DockerfileName = lowercase
			}
		}
	}

	f, err := b.context.Open(b.DockerfileName)
	if err != nil {
		if os.IsNotExist(err) {
			return fmt.Errorf("Cannot locate specified Dockerfile: %s", b.DockerfileName)
		}
		return err
	}
	if f, ok := f.(*os.File); ok {
		// ignoring error because Open already succeeded
		fi, err := f.Stat()
		if err != nil {
			return fmt.Errorf("Unexpected error reading Dockerfile: %v", err)
		}
		if fi.Size() == 0 {
			return fmt.Errorf("The Dockerfile (%s) cannot be empty", b.DockerfileName)
		}
	}
	b.dockerfile, err = parser.Parse(f)
	f.Close()
	if err != nil {
		return err
	}

	// After the Dockerfile has been parsed, we need to check the .dockerignore
	// file for either "Dockerfile" or ".dockerignore", and if either are
	// present then erase them from the build context. These files should never
	// have been sent from the client but we did send them to make sure that
	// we had the Dockerfile to actually parse, and then we also need the
	// .dockerignore file to know whether either file should be removed.
	// Note that this assumes the Dockerfile has been read into memory and
	// is now safe to be removed.
	if dockerIgnore, ok := b.context.(builder.DockerIgnoreContext); ok {
		dockerIgnore.Process([]string{b.DockerfileName})
	}
	return nil
}
// TestExposedPorts tests calling exposedPorts with multiple valid combinations
// of input.
func TestExposedPorts(t *testing.T) {
	testCases := map[string]struct {
		in   string
		want [][]string
	}{
		"empty Dockerfile": {
			in:   ``,
			want: nil,
		},
		"EXPOSE missing argument": {
			in:   `EXPOSE`,
			want: nil,
		},
		"EXPOSE no FROM": {
			in:   `EXPOSE 8080`,
			want: nil,
		},
		"single EXPOSE after FROM": {
			in: `FROM centos:7
		EXPOSE 8080`,
			want: [][]string{{"8080"}},
		},
		"multiple EXPOSE and FROM": {
			in: `# EXPOSE before FROM should be ignore
EXPOSE 777
FROM busybox
EXPOSE 8080
COPY . /boot
FROM rhel
# no EXPOSE instruction
FROM centos:7
EXPOSE 8000
EXPOSE 9090 9091
`,
			want: [][]string{{"8080"}, nil, {"8000", "9090", "9091"}},
		},
	}
	for name, tc := range testCases {
		node, err := parser.Parse(strings.NewReader(tc.in))
		if err != nil {
			t.Errorf("%s: parse error: %v", name, err)
			continue
		}
		got := exposedPorts(node)
		if !reflect.DeepEqual(got, tc.want) {
			t.Errorf("exposedPorts: %s: got %#v; want %#v", name, got, tc.want)
		}
	}
}
Beispiel #19
0
// FromDockerfile generates an ImageRef from a given name, directory, and context path.
// The directory and context path will be joined and the resulting path should be a
// Dockerfile from where the image's ports will be extracted.
func (g *imageRefGenerator) FromDockerfile(name string, dir string, context string) (*ImageRef, error) {
	// Look for Dockerfile in repository
	file, err := os.Open(filepath.Join(dir, context, "Dockerfile"))
	if err != nil {
		return nil, err
	}
	defer file.Close()
	node, err := parser.Parse(file)
	if err != nil {
		return nil, err
	}
	ports := dockerfile.LastExposedPorts(node)

	return g.FromNameAndPorts(name, ports)
}
// TestInsertInstructionsPosOutOfRange tests calling InsertInstructions with
// invalid values for the pos argument.
func TestInsertInstructionsPosOutOfRange(t *testing.T) {
	original := `FROM busybox
ENV PATH=/bin
`
	node, err := parser.Parse(strings.NewReader(original))
	if err != nil {
		t.Fatalf("parse error: %v", err)
	}
	for _, pos := range []int{-1, 3, 4} {
		err := InsertInstructions(node, pos, "")
		if err == nil {
			t.Errorf("InsertInstructions(node, %d, \"\"): got nil; want error", pos)
		}
	}
}
Beispiel #21
0
// InsertInstructions inserts instructions starting from the pos-th child of
// node, moving other children as necessary. The instructions should be valid
// Dockerfile instructions. InsertInstructions mutates node in-place, and the
// final state of node is equivalent to what parser.Parse would return if the
// original Dockerfile represented by node contained the instructions at the
// specified position pos. If the returned error is non-nil, node is guaranteed
// to be unchanged.
func InsertInstructions(node *parser.Node, pos int, instructions string) error {
	if node == nil {
		return fmt.Errorf("cannot insert instructions in a nil node")
	}
	if pos < 0 || pos > len(node.Children) {
		return fmt.Errorf("pos %d out of range [0, %d]", pos, len(node.Children)-1)
	}
	newChild, err := parser.Parse(strings.NewReader(instructions))
	if err != nil {
		return err
	}
	// InsertVector pattern (https://github.com/golang/go/wiki/SliceTricks)
	node.Children = append(node.Children[:pos], append(newChild.Children, node.Children[pos:]...)...)
	return nil
}
Beispiel #22
0
func (b *Builder) processImageFrom(img builder.Image) error {
	b.image = img.ID()

	if img.Config != nil {
		b.runConfig = img.Config()
	}

	// The default path will be blank on Windows (set by HCS)
	if len(b.runConfig.Env) == 0 && system.DefaultPathEnv != "" {
		b.runConfig.Env = append(b.runConfig.Env, "PATH="+system.DefaultPathEnv)
	}

	// Process ONBUILD triggers if they exist
	if nTriggers := len(b.runConfig.OnBuild); nTriggers != 0 {
		word := "trigger"
		if nTriggers > 1 {
			word = "triggers"
		}
		fmt.Fprintf(b.Stderr, "# Executing %d build %s...\n", nTriggers, word)
	}

	// Copy the ONBUILD triggers, and remove them from the config, since the config will be committed.
	onBuildTriggers := b.runConfig.OnBuild
	b.runConfig.OnBuild = []string{}

	// parse the ONBUILD triggers by invoking the parser
	for _, step := range onBuildTriggers {
		ast, err := parser.Parse(strings.NewReader(step))
		if err != nil {
			return err
		}

		for i, n := range ast.Children {
			switch strings.ToUpper(n.Value) {
			case "ONBUILD":
				return fmt.Errorf("Chaining ONBUILD via `ONBUILD ONBUILD` isn't allowed")
			case "MAINTAINER", "FROM":
				return fmt.Errorf("%s isn't allowed as an ONBUILD trigger", n.Value)
			}

			if err := b.dispatch(i, n); err != nil {
				return err
			}
		}
	}

	return nil
}
// TestInsertInstructionsUnparseable tests calling InsertInstructions with
// instructions that the Docker parser cannot handle.
func TestInsertInstructionsUnparseable(t *testing.T) {
	original := `FROM busybox
ENV PATH=/bin
`
	node, err := parser.Parse(strings.NewReader(original))
	if err != nil {
		t.Fatalf("parse error: %v", err)
	}
	for name, instructions := range map[string]string{
		"env without value": `ENV PATH`,
		"nested json":       `CMD [ "echo", [ "nested json" ] ]`,
	} {
		err = InsertInstructions(node, 1, instructions)
		if err == nil {
			t.Errorf("InsertInstructions: %s: got nil; want error", name)
		}
	}
}
Beispiel #24
0
// this replaces the FROM field in the Dockerfile to one with the previous step's unique name
// it stores the parsed result Dockefile in uniqueSessionName file
func (b *Builder) replaceFromField(step *Step) error {
	b.Conf.Logger.Noticef("Parsing and converting '%s'", step.Dockerfile)

	rwc, err := os.Open(path.Join(b.Conf.Workdir, step.Dockerfile))
	if err != nil {
		return err
	}
	defer rwc.Close()

	d := parser.Directive{LookingForDirectives: true}
	parser.SetEscapeToken(parser.DefaultEscapeToken, &d)
	node, err := parser.Parse(rwc, &d)
	if err != nil {
		return err
	}

	for _, child := range node.Children {
		if child.Value == "from" {
			// found it. is it from anyone we know?
			if child.Next == nil {
				return errors.New("invalid Dockerfile. No valid FROM found")
			}

			imageName := child.Next.Value
			found, err := step.Manifest.FindStepByName(imageName)
			if err != nil {
				return err
			}

			if found != nil {
				child.Next.Value = b.uniqueStepName(found)
			}
		}
	}

	// did it have any effect?
	b.Conf.Logger.Debugf("Writing the new Dockerfile into %s", step.Dockerfile+".generated")
	err = ioutil.WriteFile(b.uniqueDockerfile(step), []byte(dumpDockerfile(node)), 0644)
	if err != nil {
		return err
	}

	return nil
}
Beispiel #25
0
// NewBuilder creates a new Dockerfile builder from an optional dockerfile and a Config.
// If dockerfile is nil, the Dockerfile specified by Config.DockerfileName,
// will be read from the Context passed to Build().
func NewBuilder(d *daemon.Daemon, config *Config, docker builder.Docker, context builder.Context, dockerfile io.ReadCloser) (b *Builder, err error) {
	if config == nil {
		config = new(Config)
	}
	if config.BuildArgs == nil {
		config.BuildArgs = make(map[string]string)
	}
	hyper, err := GetDaemon()
	if err != nil {
		glog.Error(err.Error())
		return nil, err
	}
	vmId := "buildervm-" + rand.RandStr(10, "number")
	defer func() {
		glog.V(1).Infof("Kill VM(%s)...", vmId)
		hyper.KillVm(vmId)
	}()

	b = &Builder{
		Config:           config,
		Daemon:           d,
		Name:             vmId,
		Hyperdaemon:      hyper,
		Stdout:           os.Stdout,
		Stderr:           os.Stderr,
		docker:           docker,
		context:          context,
		runConfig:        new(runconfig.Config),
		tmpContainers:    map[string]struct{}{},
		tmpPods:          map[string]struct{}{},
		cancelled:        make(chan struct{}),
		id:               stringid.GenerateNonCryptoID(),
		allowedBuildArgs: make(map[string]bool),
	}
	if dockerfile != nil {
		b.dockerfile, err = parser.Parse(dockerfile)
		if err != nil {
			return nil, err
		}
	}

	return b, nil
}
Beispiel #26
0
func TestBuildProcessLabels(t *testing.T) {
	dockerfile := "FROM scratch"
	d := parser.Directive{}
	parser.SetEscapeToken(parser.DefaultEscapeToken, &d)
	n, err := parser.Parse(strings.NewReader(dockerfile), &d)
	if err != nil {
		t.Fatalf("Error when parsing Dockerfile: %s", err)
	}

	options := &types.ImageBuildOptions{
		Labels: map[string]string{
			"org.e": "cli-e",
			"org.d": "cli-d",
			"org.c": "cli-c",
			"org.b": "cli-b",
			"org.a": "cli-a",
		},
	}
	b := &Builder{
		runConfig:  &container.Config{},
		options:    options,
		directive:  d,
		dockerfile: n,
	}
	err = b.processLabels()
	if err != nil {
		t.Fatalf("Error when processing labels: %s", err)
	}

	expected := []string{
		"FROM scratch",
		`LABEL "org.a"='cli-a' "org.b"='cli-b' "org.c"='cli-c' "org.d"='cli-d' "org.e"='cli-e'`,
	}
	if len(b.dockerfile.Children) != 2 {
		t.Fatalf("Expect 2, got %d", len(b.dockerfile.Children))
	}
	for i, v := range b.dockerfile.Children {
		if v.Original != expected[i] {
			t.Fatalf("Expect '%s' for %dth children, got, '%s'", expected[i], i, v.Original)
		}
	}
}
Beispiel #27
0
// replaceLastFrom changes the last FROM instruction of node to point to the
// base image.
func replaceLastFrom(node *parser.Node, image string) error {
	if node == nil {
		return nil
	}
	for i := len(node.Children) - 1; i >= 0; i-- {
		child := node.Children[i]
		if child != nil && child.Value == dockercmd.From {
			from, err := dockerfile.From(image)
			if err != nil {
				return err
			}
			fromTree, err := parser.Parse(strings.NewReader(from))
			if err != nil {
				return err
			}
			node.Children[i] = fromTree.Children[0]
			return nil
		}
	}
	return nil
}
Beispiel #28
0
// NewBuilder creates a new Dockerfile builder from an optional dockerfile and a Config.
// If dockerfile is nil, the Dockerfile specified by Config.DockerfileName,
// will be read from the Context passed to Build().
func NewBuilder(clientCtx context.Context, config *types.ImageBuildOptions, backend builder.Backend, buildContext builder.Context, dockerfile io.ReadCloser) (b *Builder, err error) {
	if config == nil {
		config = new(types.ImageBuildOptions)
	}
	if config.BuildArgs == nil {
		config.BuildArgs = make(map[string]string)
	}
	ctx, cancel := context.WithCancel(clientCtx)
	b = &Builder{
		clientCtx:        ctx,
		cancel:           cancel,
		options:          config,
		Stdout:           os.Stdout,
		Stderr:           os.Stderr,
		docker:           backend,
		context:          buildContext,
		runConfig:        new(container.Config),
		tmpContainers:    map[string]struct{}{},
		id:               stringid.GenerateNonCryptoID(),
		allowedBuildArgs: make(map[string]bool),
		directive: parser.Directive{
			EscapeSeen:           false,
			LookingForDirectives: true,
		},
	}
	if icb, ok := backend.(builder.ImageCacheBuilder); ok {
		b.imageCache = icb.MakeImageCache(config.CacheFrom)
	}

	parser.SetEscapeToken(parser.DefaultEscapeToken, &b.directive) // Assume the default token for escape

	if dockerfile != nil {
		b.dockerfile, err = parser.Parse(dockerfile, &b.directive)
		if err != nil {
			return nil, err
		}
	}

	return b, nil
}
Beispiel #29
0
func main() {
	var f *os.File
	var err error

	if len(os.Args) < 2 {
		fmt.Println("please supply filename(s)")
		os.Exit(1)
	}

	for _, fn := range os.Args[1:] {
		f, err = os.Open(fn)
		if err != nil {
			panic(err)
		}

		ast, err := parser.Parse(f)
		if err != nil {
			panic(err)
		} else {
			fmt.Println(ast.Dump())
		}
	}
}
// TestFindAll tests calling FindAll with multiple values of cmd.
func TestFindAll(t *testing.T) {
	instructions := `FROM scratch
LABEL version=1.0
FROM busybox
ENV PATH=/bin
`
	node, err := parser.Parse(strings.NewReader(instructions))
	if err != nil {
		t.Fatalf("parse error: %v", err)
	}
	for cmd, want := range map[string][]int{
		command.From:       {0, 2},
		command.Label:      {1},
		command.Env:        {3},
		command.Maintainer: nil,
		"UnknownCommand":   nil,
	} {
		got := FindAll(node, cmd)
		if !reflect.DeepEqual(got, want) {
			t.Errorf("FindAll(node, %q) = %#v; want %#v", cmd, got, want)
		}
	}
}