Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support dependencies between build artifacts - 1 #4828

Merged
merged 27 commits into from
Oct 9, 2020
Merged
Show file tree
Hide file tree
Changes from 26 commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
38ba49b
Implements sections of the `artifact-dependencies` design:
gsquared94 Sep 28, 2020
409e292
Fix UT
gsquared94 Sep 28, 2020
ff4f49c
define `countingSemaphore`
gsquared94 Sep 29, 2020
b988840
rename functions
gsquared94 Sep 29, 2020
2b67c82
rework comments
gsquared94 Sep 29, 2020
5c6cb5f
add more comments
gsquared94 Sep 29, 2020
790764d
@briandealwis' commit.
gsquared94 Sep 30, 2020
7a96903
fix checks and linters
gsquared94 Sep 30, 2020
045a0db
merge from master
gsquared94 Oct 1, 2020
df8da8d
change doc.
gsquared94 Oct 1, 2020
6febb97
change doc.
gsquared94 Oct 1, 2020
40bda84
wrap all builds in an error group
gsquared94 Oct 5, 2020
e0b8112
Rework build result store and logger.
gsquared94 Oct 5, 2020
f2559cc
fix nodes when some artifacts are cached
gsquared94 Oct 5, 2020
7debf3e
Update pkg/skaffold/build/model.go
gsquared94 Oct 5, 2020
31e96cf
Update pkg/skaffold/build/model.go
gsquared94 Oct 5, 2020
014579b
Update pkg/skaffold/build/model.go
gsquared94 Oct 5, 2020
dfe0c47
make `logAggregator` cancellable
gsquared94 Oct 6, 2020
24b341b
address PR comments
gsquared94 Oct 6, 2020
259b733
Fix race condition of messages channel getting closed on cancellation…
gsquared94 Oct 6, 2020
9e0112c
Update pkg/skaffold/build/scheduler.go
gsquared94 Oct 6, 2020
6fdab4c
Update pkg/skaffold/build/scheduler.go
gsquared94 Oct 6, 2020
b1f9e2d
Add integration test.
gsquared94 Oct 7, 2020
beef8d1
rename structs
gsquared94 Oct 8, 2020
cc8ba73
Improve logs; fix prune cancellation
gsquared94 Oct 8, 2020
a6336df
disable dependencies for `skaffold build --cache-artifacts=true` and …
gsquared94 Oct 9, 2020
8ac4e67
Add issue ids for TODOs
gsquared94 Oct 9, 2020
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion cmd/skaffold/app/cmd/build.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ package cmd
import (
"bytes"
"context"
"errors"
"fmt"
"io"
"io/ioutil"
Expand Down Expand Up @@ -67,7 +68,13 @@ func doBuild(ctx context.Context, out io.Writer) error {
}

return withRunner(ctx, func(r runner.Runner, config *latest.SkaffoldConfig) error {
bRes, err := r.BuildAndTest(ctx, buildOut, targetArtifacts(opts, config))
ar := targetArtifacts(opts, config)

// TODO: Remove this block after fixing artifact cache logic for artifacts with dependencies
gsquared94 marked this conversation as resolved.
Show resolved Hide resolved
if err := failForArtifactDependenciesWithCacheEnabled(ar, opts.CacheArtifacts); err != nil {
return err
}
bRes, err := r.BuildAndTest(ctx, buildOut, ar)

if quietFlag || buildOutputFlag != "" {
cmdOut := flags.BuildOutput{Builds: bRes}
Expand Down Expand Up @@ -104,3 +111,15 @@ func targetArtifacts(opts config.SkaffoldOptions, cfg *latest.SkaffoldConfig) []

return targetArtifacts
}

func failForArtifactDependenciesWithCacheEnabled(artifacts []*latest.Artifact, cacheEnabled bool) error {
if !cacheEnabled {
return nil
}
for _, a := range artifacts {
if len(a.Dependencies) > 0 {
return errors.New("defining dependencies between artifacts is not yet supported for `skaffold build` with cache enabled. Run with `--cache-artifacts=false` flag")
}
}
return nil
}
13 changes: 13 additions & 0 deletions cmd/skaffold/app/cmd/dev.go
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,10 @@ func runDev(ctx context.Context, out io.Writer) error {
return nil
default:
err := withRunner(ctx, func(r runner.Runner, config *latest.SkaffoldConfig) error {
// TODO: Remove this block after fixing devloop rebuild logic for artifacts with dependencies
gsquared94 marked this conversation as resolved.
Show resolved Hide resolved
if err := checkForArtifactDependencies(config.Build.Artifacts); err != nil {
return err
}
err := r.Dev(ctx, out, config.Build.Artifacts)

if r.HasDeployed() {
Expand Down Expand Up @@ -91,3 +95,12 @@ func runDev(ctx context.Context, out io.Writer) error {
}
}
}

func checkForArtifactDependencies(artifacts []*latest.Artifact) error {
for _, a := range artifacts {
if len(a.Dependencies) > 0 {
return errors.New("defining dependencies between artifacts is not yet supported for `skaffold dev` and `skaffold debug`")
}
}
return nil
}
89 changes: 88 additions & 1 deletion docs/content/en/schemas/v2beta9.json
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,14 @@
"gcr.io/k8s-skaffold/example"
]
},
"requires": {
"items": {
"$ref": "#/definitions/ArtifactDependency"
},
"type": "array",
"description": "describes build artifacts that this artifact depends on.",
"x-intellij-html-description": "describes build artifacts that this artifact depends on."
},
"sync": {
"$ref": "#/definitions/Sync",
"description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.",
Expand All @@ -74,7 +82,8 @@
"preferredOrder": [
"image",
"context",
"sync"
"sync",
"requires"
],
"additionalProperties": false
},
Expand All @@ -99,6 +108,14 @@
"gcr.io/k8s-skaffold/example"
]
},
"requires": {
"items": {
"$ref": "#/definitions/ArtifactDependency"
},
"type": "array",
"description": "describes build artifacts that this artifact depends on.",
"x-intellij-html-description": "describes build artifacts that this artifact depends on."
},
"sync": {
"$ref": "#/definitions/Sync",
"description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.",
Expand All @@ -110,6 +127,7 @@
"image",
"context",
"sync",
"requires",
"docker"
],
"additionalProperties": false
Expand All @@ -135,6 +153,14 @@
"gcr.io/k8s-skaffold/example"
]
},
"requires": {
"items": {
"$ref": "#/definitions/ArtifactDependency"
},
"type": "array",
"description": "describes build artifacts that this artifact depends on.",
"x-intellij-html-description": "describes build artifacts that this artifact depends on."
},
"sync": {
"$ref": "#/definitions/Sync",
"description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.",
Expand All @@ -146,6 +172,7 @@
"image",
"context",
"sync",
"requires",
"bazel"
],
"additionalProperties": false
Expand All @@ -171,6 +198,14 @@
"description": "builds images using the [Jib plugins for Maven or Gradle](https://github.com/GoogleContainerTools/jib/).",
"x-intellij-html-description": "builds images using the <a href=\"https://github.com/GoogleContainerTools/jib/\">Jib plugins for Maven or Gradle</a>."
},
"requires": {
"items": {
"$ref": "#/definitions/ArtifactDependency"
},
"type": "array",
"description": "describes build artifacts that this artifact depends on.",
"x-intellij-html-description": "describes build artifacts that this artifact depends on."
},
"sync": {
"$ref": "#/definitions/Sync",
"description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.",
Expand All @@ -182,6 +217,7 @@
"image",
"context",
"sync",
"requires",
"jib"
],
"additionalProperties": false
Expand All @@ -207,6 +243,14 @@
"description": "builds images using [kaniko](https://github.com/GoogleContainerTools/kaniko).",
"x-intellij-html-description": "builds images using <a href=\"https://github.com/GoogleContainerTools/kaniko\">kaniko</a>."
},
"requires": {
"items": {
"$ref": "#/definitions/ArtifactDependency"
},
"type": "array",
"description": "describes build artifacts that this artifact depends on.",
"x-intellij-html-description": "describes build artifacts that this artifact depends on."
},
"sync": {
"$ref": "#/definitions/Sync",
"description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.",
Expand All @@ -218,6 +262,7 @@
"image",
"context",
"sync",
"requires",
"kaniko"
],
"additionalProperties": false
Expand All @@ -243,6 +288,14 @@
"gcr.io/k8s-skaffold/example"
]
},
"requires": {
"items": {
"$ref": "#/definitions/ArtifactDependency"
},
"type": "array",
"description": "describes build artifacts that this artifact depends on.",
"x-intellij-html-description": "describes build artifacts that this artifact depends on."
},
"sync": {
"$ref": "#/definitions/Sync",
"description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.",
Expand All @@ -254,6 +307,7 @@
"image",
"context",
"sync",
"requires",
"buildpacks"
],
"additionalProperties": false
Expand All @@ -279,6 +333,14 @@
"gcr.io/k8s-skaffold/example"
]
},
"requires": {
"items": {
"$ref": "#/definitions/ArtifactDependency"
},
"type": "array",
"description": "describes build artifacts that this artifact depends on.",
"x-intellij-html-description": "describes build artifacts that this artifact depends on."
},
"sync": {
"$ref": "#/definitions/Sync",
"description": "*beta* local files synced to pods instead of triggering an image build when modified. If no files are listed, sync all the files and infer the destination.",
Expand All @@ -290,6 +352,7 @@
"image",
"context",
"sync",
"requires",
"custom"
],
"additionalProperties": false
Expand All @@ -298,6 +361,30 @@
"description": "items that need to be built, along with the context in which they should be built.",
"x-intellij-html-description": "items that need to be built, along with the context in which they should be built."
},
"ArtifactDependency": {
"required": [
"image"
],
"properties": {
"alias": {
"type": "string",
"description": "a token that is replaced with the image reference in the builder definition files. For example, the `docker` builder will use the alias as a build-arg key. Defaults to the value of `image`.",
"x-intellij-html-description": "a token that is replaced with the image reference in the builder definition files. For example, the <code>docker</code> builder will use the alias as a build-arg key. Defaults to the value of <code>image</code>."
},
"image": {
"type": "string",
"description": "a reference to an artifact's image name.",
"x-intellij-html-description": "a reference to an artifact's image name."
}
},
"preferredOrder": [
"image",
"alias"
],
"additionalProperties": false,
"description": "describes a specific build dependency for an artifact.",
"x-intellij-html-description": "describes a specific build dependency for an artifact."
},
"Auto": {
"description": "cannot be customized.",
"x-intellij-html-description": "cannot be customized."
Expand Down
114 changes: 114 additions & 0 deletions integration/build_dependencies_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
/*
Copyright 2020 The Skaffold Authors

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 integration

import (
"strings"
"testing"

"github.com/sirupsen/logrus"

"github.com/GoogleContainerTools/skaffold/integration/skaffold"
)

func TestBuild_WithDependencies(t *testing.T) {
tejal29 marked this conversation as resolved.
Show resolved Hide resolved
MarkIntegrationTest(t, CanRunWithoutGcp)

tests := []struct {
description string
args []string
cacheEnabled bool
failure string
}{
{
description: "default concurrency=1",
},
{
description: "concurrency=0",
args: []string{"-p", "concurrency-0"},
},
{
description: "concurrency=3",
args: []string{"-p", "concurrency-3"},
},
{
description: "invalid dependency",
args: []string{"-p", "invalid-dependency"},
failure: `invalid skaffold config: unknown build dependency "image5" for artifact "image1"`,
},
{
description: "circular dependency",
args: []string{"-p", "circular-dependency"},
failure: `invalid skaffold config: cycle detected in build dependencies involving "image1"`,
},
{
description: "build failure with concurrency=1",
args: []string{"-p", "failed-dependency"},
failure: `unable to stream build output: The command '/bin/sh -c [ "${FAIL}" == "0" ] || false' returned a non-zero code: 1`,
},
{
description: "build failure with concurrency=0",
args: []string{"-p", "failed-dependency", "-p", "concurrency-0"},
failure: `unable to stream build output: The command '/bin/sh -c [ "${FAIL}" == "0" ] || false' returned a non-zero code: 1`,
},
{
description: "build failure with cache-artifacts=true",
cacheEnabled: true,
failure: "defining dependencies between artifacts is not yet supported for `skaffold build` with cache enabled. Run with `--cache-artifacts=false` flag",
},
}

for _, test := range tests {
t.Run(test.description, func(t *testing.T) {
if test.cacheEnabled {
test.args = append(test.args, "--cache-artifacts=true")
} else {
test.args = append(test.args, "--cache-artifacts=false")
}

if test.failure == "" {
// Run without artifact caching
skaffold.Build(test.args...).InDir("testdata/build-dependencies").RunOrFail(t)
checkImagesExist(t)
} else {
if out, err := skaffold.Build(test.args...).InDir("testdata/build-dependencies").RunWithCombinedOutput(t); err == nil {
t.Fatal("expected build to fail")
} else if !strings.Contains(string(out), test.failure) {
logrus.Info("build output: ", string(out))
t.Fatalf("build failed but for wrong reason")
}
}
})
}
}

func TestDev_WithDependencies(t *testing.T) {
tejal29 marked this conversation as resolved.
Show resolved Hide resolved
MarkIntegrationTest(t, CanRunWithoutGcp)
if out, err := skaffold.Dev().InDir("testdata/build-dependencies").RunWithCombinedOutput(t); err == nil {
t.Fatal("expected build to fail")
} else if !strings.Contains(string(out), "defining dependencies between artifacts is not yet supported for `skaffold dev` and `skaffold debug`") {
logrus.Info("dev output: ", string(out))
t.Fatalf("dev failed but for wrong reason")
}
}

func checkImagesExist(t *testing.T) {
checkImageExists(t, "gcr.io/k8s-skaffold/image1:latest")
checkImageExists(t, "gcr.io/k8s-skaffold/image2:latest")
checkImageExists(t, "gcr.io/k8s-skaffold/image3:latest")
checkImageExists(t, "gcr.io/k8s-skaffold/image4:latest")
}
15 changes: 15 additions & 0 deletions integration/testdata/build-dependencies/app1/Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
FROM busybox

# SLEEP is to simulate build time
ARG SLEEP=0
# FAIL=1 will cause the build to fail
ARG FAIL=0
COPY foo /foo

ENV SLEEP_TIMEOUT=${SLEEP}
ENV FAIL=${FAIL}
RUN echo "sleep ${SLEEP_TIMEOUT}"
RUN sleep ${SLEEP_TIMEOUT}
RUN [ "${FAIL}" == "0" ] || false

CMD while true; do cat /foo; sleep 1; done
Empty file.
Loading