diff --git a/docs/content/en/schemas/v1beta10.json b/docs/content/en/schemas/v1beta10.json index 572139fc19c..9b9bdb18d29 100755 --- a/docs/content/en/schemas/v1beta10.json +++ b/docs/content/en/schemas/v1beta10.json @@ -595,6 +595,11 @@ }, "CustomDependencies": { "properties": { + "dockerfile": { + "$ref": "#/definitions/DockerfileDependency", + "description": "should be set if the artifact is built from a Dockerfile, from which skaffold can determine dependencies.", + "x-intellij-html-description": "should be set if the artifact is built from a Dockerfile, from which skaffold can determine dependencies." + }, "ignore": { "items": { "type": "string" @@ -615,12 +620,13 @@ } }, "preferredOrder": [ + "dockerfile", "paths", "ignore" ], "additionalProperties": false, - "description": "*alpha* used to specify dependencies for an artifact built by a custom build script.", - "x-intellij-html-description": "alpha used to specify dependencies for an artifact built by a custom build script." + "description": "*alpha* used to specify dependencies for an artifact built by a custom build script. Either `dockerfile` or `paths` should be specified for file watching to work as expected.", + "x-intellij-html-description": "alpha used to specify dependencies for an artifact built by a custom build script. Either dockerfile or paths should be specified for file watching to work as expected." }, "DateTimeTagger": { "properties": { @@ -767,6 +773,40 @@ "description": "contains information about the docker `config.json` to mount.", "x-intellij-html-description": "contains information about the docker config.json to mount." }, + "DockerfileDependency": { + "properties": { + "buildArgs": { + "additionalProperties": { + "type": "string" + }, + "type": "object", + "description": "arguments passed to the docker build. It also accepts environment variables via the go template syntax.", + "x-intellij-html-description": "arguments passed to the docker build. It also accepts environment variables via the go template syntax.", + "default": "{}", + "examples": [ + "{\"key1\": \"value1\", \"key2\": \"value2\", \"key3\": \"{{.ENV_VARIABLE}}\"}" + ] + }, + "path": { + "type": "string", + "description": "locates the Dockerfile relative to workspace.", + "x-intellij-html-description": "locates the Dockerfile relative to workspace." + }, + "target": { + "type": "string", + "description": "Dockerfile target name to build.", + "x-intellij-html-description": "Dockerfile target name to build." + } + }, + "preferredOrder": [ + "path", + "target", + "buildArgs" + ], + "additionalProperties": false, + "description": "*alpha* used to specify a custom build artifact that is built from a Dockerfile. This allows skaffold to determine dependencies from the Dockerfile.", + "x-intellij-html-description": "alpha used to specify a custom build artifact that is built from a Dockerfile. This allows skaffold to determine dependencies from the Dockerfile." + }, "EnvTemplateTagger": { "required": [ "template" diff --git a/pkg/skaffold/build/custom/dependencies.go b/pkg/skaffold/build/custom/dependencies.go index 56fdf65e4c2..2143ae87724 100644 --- a/pkg/skaffold/build/custom/dependencies.go +++ b/pkg/skaffold/build/custom/dependencies.go @@ -26,15 +26,24 @@ import ( ) // GetDependencies returns dependencies listed for a custom artifact -func GetDependencies(ctx context.Context, workspace string, a *latest.CustomArtifact) ([]string, error) { - files, err := docker.WalkWorkspace(workspace, a.Dependencies.Ignore, a.Dependencies.Paths) - if err != nil { - return nil, errors.Wrapf(err, "walking workspace %s", workspace) - } - var dependencies []string - for file := range files { - dependencies = append(dependencies, file) +func GetDependencies(ctx context.Context, workspace string, a *latest.CustomArtifact, insecureRegistries map[string]bool) ([]string, error) { + + switch { + case a.Dependencies.Dockerfile != nil: + dockerfile := a.Dependencies.Dockerfile + return docker.GetDependencies(ctx, workspace, dockerfile.Path, dockerfile.BuildArgs, insecureRegistries) + + default: + files, err := docker.WalkWorkspace(workspace, a.Dependencies.Ignore, a.Dependencies.Paths) + if err != nil { + return nil, errors.Wrapf(err, "walking workspace %s", workspace) + } + var dependencies []string + for file := range files { + dependencies = append(dependencies, file) + } + sort.Strings(dependencies) + return dependencies, nil } - sort.Strings(dependencies) - return dependencies, nil + } diff --git a/pkg/skaffold/build/custom/dependencies_test.go b/pkg/skaffold/build/custom/dependencies_test.go index 35f0a4d0043..1fd2850f50c 100644 --- a/pkg/skaffold/build/custom/dependencies_test.go +++ b/pkg/skaffold/build/custom/dependencies_test.go @@ -25,7 +25,40 @@ import ( "github.com/GoogleContainerTools/skaffold/testutil" ) -func TestGetDependencies(t *testing.T) { +func TestGetDependenciesDockerfile(t *testing.T) { + tmpDir, cleanup := testutil.NewTempDir(t) + defer cleanup() + + // Directory structure: + // foo + // bar + // - baz + // file + // Dockerfile + tmpDir.Write("foo", "") + tmpDir.Write("bar", "") + tmpDir.Mkdir("baz") + tmpDir.Write("baz/file", "") + tmpDir.Write("Dockerfile", "FROM scratch \n ARG file \n COPY $file baz/file .") + + customArtifact := &latest.CustomArtifact{ + Dependencies: &latest.CustomDependencies{ + Dockerfile: &latest.DockerfileDependency{ + Path: "Dockerfile", + BuildArgs: map[string]*string{ + "file": stringPointer("foo"), + }, + }, + }, + } + + expected := []string{"Dockerfile", "baz/file", "foo"} + deps, err := GetDependencies(context.Background(), tmpDir.Root(), customArtifact, nil) + + testutil.CheckErrorAndDeepEqual(t, false, err, expected, deps) +} + +func TestGetDependenciesPaths(t *testing.T) { tmpDir, cleanup := testutil.NewTempDir(t) defer cleanup() @@ -66,9 +99,13 @@ func TestGetDependencies(t *testing.T) { Paths: test.paths, Ignore: test.ignore, }, - }) + }, nil) testutil.CheckErrorAndDeepEqual(t, false, err, test.expected, deps) }) } } + +func stringPointer(s string) *string { + return &s +} diff --git a/pkg/skaffold/build/local/local.go b/pkg/skaffold/build/local/local.go index 7644f96749c..49aca07b5c6 100644 --- a/pkg/skaffold/build/local/local.go +++ b/pkg/skaffold/build/local/local.go @@ -124,7 +124,7 @@ func (b *Builder) DependenciesForArtifact(ctx context.Context, a *latest.Artifac paths, err = jib.GetDependenciesGradle(ctx, a.Workspace, a.JibGradleArtifact) case a.CustomArtifact != nil: - paths, err = custom.GetDependencies(ctx, a.Workspace, a.CustomArtifact) + paths, err = custom.GetDependencies(ctx, a.Workspace, a.CustomArtifact, b.insecureRegistries) default: return nil, fmt.Errorf("undefined artifact type: %+v", a.ArtifactType) diff --git a/pkg/skaffold/schema/latest/config.go b/pkg/skaffold/schema/latest/config.go index df7d6522a39..44649b8f365 100644 --- a/pkg/skaffold/schema/latest/config.go +++ b/pkg/skaffold/schema/latest/config.go @@ -566,7 +566,10 @@ type CustomArtifact struct { } // CustomDependencies *alpha* is used to specify dependencies for an artifact built by a custom build script. +// Either `dockerfile` or `paths` should be specified for file watching to work as expected. type CustomDependencies struct { + // Dockerfile should be set if the artifact is built from a Dockerfile, from which skaffold can determine dependencies. + Dockerfile *DockerfileDependency `yaml:"dockerfile,omitempty" yamltags:"oneOf=dependency"` // Paths should be set to the file dependencies for this artifact, so that the skaffold file watcher knows when to rebuild and perform file synchronization. Paths []string `yaml:"paths,omitempty" yamltags:"oneOf=dependency"` // Ignore specifies the paths that should be ignored by skaffold's file watcher. If a file exists in both `paths` and in `ignore`, it will be ignored, and will be excluded from both rebuilds and file synchronization. @@ -574,6 +577,20 @@ type CustomDependencies struct { Ignore []string `yaml:"ignore,omitempty"` } +// DockerfileDependency *alpha* is used to specify a custom build artifact that is built from a Dockerfile. This allows skaffold to determine dependencies from the Dockerfile. +type DockerfileDependency struct { + // Path locates the Dockerfile relative to workspace. + Path string `yaml:"path,omitempty"` + + // Target is the Dockerfile target name to build. + Target string `yaml:"target,omitempty"` + + // BuildArgs are arguments passed to the docker build. + // It also accepts environment variables via the go template syntax. + // For example: `{"key1": "value1", "key2": "value2", "key3": "{{.ENV_VARIABLE}}"}`. + BuildArgs map[string]*string `yaml:"buildArgs,omitempty"` +} + // KanikoArtifact *alpha* describes an artifact built from a Dockerfile, // with kaniko. type KanikoArtifact struct {