github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/build/gcb/docker.go (about)

     1  /*
     2  Copyright 2019 The Skaffold Authors
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package gcb
    18  
    19  import (
    20  	"errors"
    21  	"fmt"
    22  	"os"
    23  
    24  	cloudbuild "google.golang.org/api/cloudbuild/v1"
    25  
    26  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
    27  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/platform"
    28  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
    29  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util/stringslice"
    30  )
    31  
    32  // dockerBuildSpec lists the build steps required to build a docker image.
    33  func (b *Builder) dockerBuildSpec(a *latest.Artifact, tag string, platforms platform.Matcher) (cloudbuild.Build, error) {
    34  	a = adjustCacheFrom(a, tag)
    35  
    36  	args, err := b.dockerBuildArgs(a, tag, a.Dependencies, platforms)
    37  	if err != nil {
    38  		return cloudbuild.Build{}, err
    39  	}
    40  	steps := b.cacheFromSteps(a.DockerArtifact, platforms)
    41  	buildStep := &cloudbuild.BuildStep{
    42  		Name: b.DockerImage,
    43  		Args: args,
    44  	}
    45  	if platforms.IsNotEmpty() {
    46  		// cross-platform build requires buildkit enabled
    47  		buildStep.Env = append(buildStep.Env, "DOCKER_BUILDKIT=1")
    48  	}
    49  	steps = append(steps, buildStep)
    50  
    51  	return cloudbuild.Build{
    52  		Steps:  steps,
    53  		Images: []string{tag},
    54  	}, nil
    55  }
    56  
    57  // cacheFromSteps pulls images used by `--cache-from`.
    58  func (b *Builder) cacheFromSteps(artifact *latest.DockerArtifact, platforms platform.Matcher) []*cloudbuild.BuildStep {
    59  	var steps []*cloudbuild.BuildStep
    60  	argFmt := "docker pull %s || true"
    61  	if platforms.IsNotEmpty() {
    62  		argFmt = "docker pull --platform " + platforms.String() + " %s || true"
    63  	}
    64  	for _, cacheFrom := range artifact.CacheFrom {
    65  		steps = append(steps, &cloudbuild.BuildStep{
    66  			Name:       b.DockerImage,
    67  			Entrypoint: "sh",
    68  			Args:       []string{"-c", fmt.Sprintf(argFmt, cacheFrom)},
    69  		})
    70  	}
    71  
    72  	return steps
    73  }
    74  
    75  // dockerBuildArgs lists the arguments passed to `docker` to build a given image.
    76  func (b *Builder) dockerBuildArgs(a *latest.Artifact, tag string, deps []*latest.ArtifactDependency, platforms platform.Matcher) ([]string, error) {
    77  	d := a.DockerArtifact
    78  	// TODO(nkubala): remove when buildkit is supported in GCB (#4773)
    79  	if len(d.Secrets) > 0 || d.SSH != "" {
    80  		return nil, errors.New("docker build options, secrets and ssh, are not currently supported in GCB builds")
    81  	}
    82  	requiredImages := docker.ResolveDependencyImages(deps, b.artifactStore, true)
    83  	buildArgs, err := docker.EvalBuildArgs(b.cfg.Mode(), a.Workspace, d.DockerfilePath, d.BuildArgs, requiredImages)
    84  	if err != nil {
    85  		return nil, fmt.Errorf("unable to evaluate build args: %w", err)
    86  	}
    87  
    88  	ba, err := docker.ToCLIBuildArgs(d, buildArgs)
    89  	if err != nil {
    90  		return nil, fmt.Errorf("getting docker build args: %w", err)
    91  	}
    92  
    93  	args := []string{"build", "--tag", tag, "-f", d.DockerfilePath}
    94  	if platforms.IsNotEmpty() {
    95  		args = append(args, "--platform", platforms.String())
    96  	}
    97  	args = append(args, ba...)
    98  	args = append(args, ".")
    99  
   100  	return args, nil
   101  }
   102  
   103  // adjustCacheFrom returns  an artifact where any cache references from the artifactImage is changed to the tagged built image name instead.
   104  func adjustCacheFrom(a *latest.Artifact, artifactTag string) *latest.Artifact {
   105  	if os.Getenv("SKAFFOLD_DISABLE_GCB_CACHE_ADJUSTMENT") != "" {
   106  		// allow this behaviour to be disabled
   107  		return a
   108  	}
   109  
   110  	if !stringslice.Contains(a.DockerArtifact.CacheFrom, a.ImageName) {
   111  		return a
   112  	}
   113  
   114  	cf := make([]string, 0, len(a.DockerArtifact.CacheFrom))
   115  	for _, image := range a.DockerArtifact.CacheFrom {
   116  		if image == a.ImageName {
   117  			cf = append(cf, artifactTag)
   118  		} else {
   119  			cf = append(cf, image)
   120  		}
   121  	}
   122  	copy := *a
   123  	copy.DockerArtifact.CacheFrom = cf
   124  	return &copy
   125  }