The provider currently attempts to parse your Dockerfile in case there are any obviously broken syntax errors. However, Dockerfiles can use custom syntaxes which are implemented as their own images. Buildkit detects `# syntax=` directives, pulls the image, and uses it to parse. It doesn't make sense for us to attempt to replicate that behavior, so when we detect a custom syntax we will simply disable this validation. If there are syntax errors the user will discover them after the build is attempted. Fixes https://github.com/pulumi/pulumi-docker-build/issues/300
123 lines
3.0 KiB
Go
123 lines
3.0 KiB
Go
// Copyright 2024, Pulumi Corporation.
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License");
|
|
// you may not use this file except in compliance with the License.
|
|
// You may obtain a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS,
|
|
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
// See the License for the specific language governing permissions and
|
|
// limitations under the License.
|
|
|
|
package internal
|
|
|
|
import (
|
|
"bytes"
|
|
"errors"
|
|
"io"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
buildx "github.com/docker/buildx/build"
|
|
"github.com/moby/buildkit/frontend/dockerfile/instructions"
|
|
"github.com/moby/buildkit/frontend/dockerfile/parser"
|
|
|
|
"github.com/pulumi/pulumi-go-provider/infer"
|
|
)
|
|
|
|
// Dockerfile references a local, remote, or inline Dockerfile.
|
|
type Dockerfile struct {
|
|
Location string `pulumi:"location,optional"`
|
|
Inline string `pulumi:"inline,optional"`
|
|
}
|
|
|
|
// Annotate sets docstrings on Dockerfile.
|
|
func (d *Dockerfile) Annotate(a infer.Annotator) {
|
|
a.Describe(&d.Location, dedent(`
|
|
Location of the Dockerfile to use.
|
|
|
|
Can be a relative or absolute path to a local file, or a remote URL.
|
|
|
|
Defaults to "${context.location}/Dockerfile" if context is on-disk.
|
|
|
|
Conflicts with "inline".
|
|
`))
|
|
a.Describe(&d.Inline, dedent(`
|
|
Raw Dockerfile contents.
|
|
|
|
Conflicts with "location".
|
|
|
|
Equivalent to invoking Docker with "-f -".
|
|
`))
|
|
}
|
|
|
|
func (d *Dockerfile) validate(preview bool, c *Context) error {
|
|
if d.Location != "" && d.Inline != "" {
|
|
return newCheckFailure(
|
|
errors.New(`only specify "file" or "inline", not both`),
|
|
"dockerfile",
|
|
)
|
|
}
|
|
|
|
if d.Location != "" {
|
|
if buildx.IsRemoteURL(d.Location) {
|
|
return nil
|
|
}
|
|
abs, err := filepath.Abs(d.Location)
|
|
if err != nil {
|
|
return err
|
|
}
|
|
f, err := os.Open(filepath.Clean(abs))
|
|
if err != nil {
|
|
return newCheckFailure(err, "dockerfile.location")
|
|
}
|
|
if err := parseDockerfile(f); err != nil {
|
|
return newCheckFailure(err, "dockerfile.location")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if d.Inline != "" {
|
|
err := parseDockerfile(strings.NewReader(d.Inline))
|
|
if err != nil {
|
|
return newCheckFailure(err, "dockerfile.inline")
|
|
}
|
|
return nil
|
|
}
|
|
|
|
if !preview && c != nil && !buildx.IsRemoteURL(c.Location) {
|
|
return newCheckFailure(errors.New("missing 'location' or 'inline'"), "dockerfile")
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func parseDockerfile(r io.Reader) error {
|
|
df, _ := io.ReadAll(r)
|
|
syntax, _, _, _ := parser.DetectSyntax(df)
|
|
if syntax == "" {
|
|
syntax = os.Getenv("BUILDKIT_SYNTAX")
|
|
}
|
|
|
|
// Disable validation if this uses a custom syntax.
|
|
if syntax != "" && syntax != "docker/dockerfile:1" {
|
|
return nil
|
|
}
|
|
|
|
parsed, err := parser.Parse(bytes.NewReader(df))
|
|
if err != nil {
|
|
return newCheckFailure(err, "dockerfile")
|
|
}
|
|
|
|
_, _, err = instructions.Parse(parsed.AST, nil)
|
|
if err != nil {
|
|
return newCheckFailure(err, "dockerfile")
|
|
}
|
|
|
|
return nil
|
|
}
|