github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/graph/dependencies.go (about)

     1  /*
     2  Copyright 2021 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 graph
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  
    23  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/bazel"
    24  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/buildpacks"
    25  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/custom"
    26  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/jib"
    27  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/ko"
    28  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build/misc"
    29  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
    30  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/instrumentation"
    31  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
    32  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
    33  )
    34  
    35  // for testing
    36  var getDependenciesFunc = sourceDependenciesForArtifact
    37  
    38  // SourceDependenciesCache provides an interface to evaluate and cache the source dependencies for artifacts.
    39  type SourceDependenciesCache interface {
    40  	// TransitiveArtifactDependencies returns the source dependencies for the target artifact, including the source dependencies from all other artifacts that are in the transitive closure of its artifact dependencies.
    41  	// The result (even if an error) is cached so that the function is evaluated only once for every artifact. The cache is reset before the start of the next devloop.
    42  	TransitiveArtifactDependencies(ctx context.Context, a *latest.Artifact) ([]string, error)
    43  	// SingleArtifactDependencies returns the source dependencies for only the target artifact.
    44  	// The result (even if an error) is cached so that the function is evaluated only once for every artifact. The cache is reset before the start of the next devloop.
    45  	SingleArtifactDependencies(ctx context.Context, a *latest.Artifact) ([]string, error)
    46  	// Reset removes the cached source dependencies for all artifacts
    47  	Reset()
    48  }
    49  
    50  func NewSourceDependenciesCache(cfg docker.Config, r docker.ArtifactResolver, g ArtifactGraph) SourceDependenciesCache {
    51  	return &dependencyResolverImpl{cfg: cfg, artifactResolver: r, artifactGraph: g, cache: util.NewSyncStore()}
    52  }
    53  
    54  type dependencyResolverImpl struct {
    55  	cfg              docker.Config
    56  	artifactResolver docker.ArtifactResolver
    57  	artifactGraph    ArtifactGraph
    58  	cache            *util.SyncStore
    59  }
    60  
    61  func (r *dependencyResolverImpl) TransitiveArtifactDependencies(ctx context.Context, a *latest.Artifact) ([]string, error) {
    62  	ctx, endTrace := instrumentation.StartTrace(ctx, "TransitiveArtifactDependencies", map[string]string{
    63  		"ArtifactName": instrumentation.PII(a.ImageName),
    64  	})
    65  	defer endTrace()
    66  
    67  	deps, err := r.SingleArtifactDependencies(ctx, a)
    68  	if err != nil {
    69  		endTrace(instrumentation.TraceEndError(err))
    70  		return nil, err
    71  	}
    72  	for _, ad := range a.Dependencies {
    73  		d, err := r.TransitiveArtifactDependencies(ctx, r.artifactGraph[ad.ImageName])
    74  		if err != nil {
    75  			endTrace(instrumentation.TraceEndError(err))
    76  			return nil, err
    77  		}
    78  		deps = append(deps, d...)
    79  	}
    80  	return deps, nil
    81  }
    82  
    83  func (r *dependencyResolverImpl) SingleArtifactDependencies(ctx context.Context, a *latest.Artifact) ([]string, error) {
    84  	ctx, endTrace := instrumentation.StartTrace(ctx, "SingleArtifactDependencies", map[string]string{
    85  		"ArtifactName": instrumentation.PII(a.ImageName),
    86  	})
    87  	defer endTrace()
    88  
    89  	res := r.cache.Exec(a.ImageName, func() interface{} {
    90  		d, e := getDependenciesFunc(ctx, a, r.cfg, r.artifactResolver)
    91  		if e != nil {
    92  			return e
    93  		}
    94  		return d
    95  	})
    96  	if err, ok := res.(error); ok {
    97  		endTrace(instrumentation.TraceEndError(err))
    98  		return nil, err
    99  	}
   100  	return res.([]string), nil
   101  }
   102  
   103  func (r *dependencyResolverImpl) Reset() {
   104  	r.cache = util.NewSyncStore()
   105  }
   106  
   107  // sourceDependenciesForArtifact returns the build dependencies for the current artifact.
   108  func sourceDependenciesForArtifact(ctx context.Context, a *latest.Artifact, cfg docker.Config, r docker.ArtifactResolver) ([]string, error) {
   109  	var (
   110  		paths []string
   111  		err   error
   112  	)
   113  
   114  	switch {
   115  	case a.DockerArtifact != nil:
   116  		// Required artifacts cannot be resolved when `ResolveDependencyImages` runs prior to a completed build sequence (like `skaffold build` or the first iteration of `skaffold dev`).
   117  		// However it only affects the behavior for Dockerfiles with ONBUILD instructions, and there's no functional change even for those scenarios.
   118  		// For single build scenarios like `build` and `run`, it is called for the cache hash calculations which are already handled in `artifactHasher`.
   119  		// For `dev` it will succeed on the first dev loop and list any additional dependencies found from the base artifact's ONBUILD instructions as a file added instead of modified (see `filemon.Events`)
   120  		deps := docker.ResolveDependencyImages(a.Dependencies, r, false)
   121  		args, evalErr := docker.EvalBuildArgs(cfg.Mode(), a.Workspace, a.DockerArtifact.DockerfilePath, a.DockerArtifact.BuildArgs, deps)
   122  		if evalErr != nil {
   123  			return nil, fmt.Errorf("unable to evaluate build args: %w", evalErr)
   124  		}
   125  		paths, err = docker.GetDependencies(ctx, docker.NewBuildConfig(a.Workspace, a.ImageName, a.DockerArtifact.DockerfilePath, args), cfg)
   126  
   127  	case a.KanikoArtifact != nil:
   128  		deps := docker.ResolveDependencyImages(a.Dependencies, r, false)
   129  		args, evalErr := docker.EvalBuildArgs(cfg.Mode(), a.Workspace, a.KanikoArtifact.DockerfilePath, a.KanikoArtifact.BuildArgs, deps)
   130  		if evalErr != nil {
   131  			return nil, fmt.Errorf("unable to evaluate build args: %w", evalErr)
   132  		}
   133  		paths, err = docker.GetDependencies(ctx, docker.NewBuildConfig(a.Workspace, a.ImageName, a.KanikoArtifact.DockerfilePath, args), cfg)
   134  
   135  	case a.BazelArtifact != nil:
   136  		paths, err = bazel.GetDependencies(ctx, a.Workspace, a.BazelArtifact)
   137  
   138  	case a.JibArtifact != nil:
   139  		paths, err = jib.GetDependencies(ctx, a.Workspace, a.JibArtifact)
   140  
   141  	case a.CustomArtifact != nil:
   142  		paths, err = custom.GetDependencies(ctx, a.Workspace, a.ImageName, a.CustomArtifact, cfg)
   143  
   144  	case a.BuildpackArtifact != nil:
   145  		paths, err = buildpacks.GetDependencies(ctx, a.Workspace, a.BuildpackArtifact)
   146  
   147  	case a.KoArtifact != nil:
   148  		paths, err = ko.GetDependencies(ctx, a.Workspace, a.KoArtifact)
   149  
   150  	default:
   151  		return nil, fmt.Errorf("unexpected artifact type %q:\n%s", misc.ArtifactType(a), misc.FormatArtifact(a))
   152  	}
   153  
   154  	if err != nil {
   155  		return nil, err
   156  	}
   157  
   158  	return util.AbsolutePaths(a.Workspace, paths), nil
   159  }