github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/diagnose/diagnose.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 diagnose
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  	"io/ioutil"
    24  	"time"
    25  
    26  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/build"
    27  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker"
    28  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/filemon"
    29  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
    30  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/output"
    31  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
    32  	"github.com/GoogleContainerTools/skaffold/pkg/skaffold/sync"
    33  	timeutil "github.com/GoogleContainerTools/skaffold/pkg/skaffold/util/time"
    34  )
    35  
    36  type Config interface {
    37  	docker.Config
    38  
    39  	GetPipelines() []latest.Pipeline
    40  	Artifacts() []*latest.Artifact
    41  }
    42  
    43  func CheckArtifacts(ctx context.Context, cfg Config, out io.Writer) error {
    44  	for _, p := range cfg.GetPipelines() {
    45  		for _, artifact := range p.Build.Artifacts {
    46  			output.Default.Fprintf(out, "\n%s: %s\n", typeOfArtifact(artifact), artifact.ImageName)
    47  
    48  			if artifact.DockerArtifact != nil {
    49  				size, err := sizeOfDockerContext(ctx, artifact, cfg)
    50  				if err != nil {
    51  					return fmt.Errorf("computing the size of the Docker context: %w", err)
    52  				}
    53  
    54  				fmt.Fprintf(out, " - Size of the context: %vbytes\n", size)
    55  			}
    56  
    57  			timeDeps1, deps, err := timeToListDependencies(ctx, artifact, cfg)
    58  			if err != nil {
    59  				return fmt.Errorf("listing artifact dependencies: %w", err)
    60  			}
    61  			timeDeps2, _, err := timeToListDependencies(ctx, artifact, cfg)
    62  			if err != nil {
    63  				return fmt.Errorf("listing artifact dependencies: %w", err)
    64  			}
    65  
    66  			fmt.Fprintln(out, " - Dependencies:", len(deps), "files")
    67  			fmt.Fprintf(out, " - Time to list dependencies: %v (2nd time: %v)\n", timeDeps1, timeDeps2)
    68  
    69  			// Only check sync map if inferred sync is configured on artifact
    70  			if artifact.Sync != nil && len(artifact.Sync.Infer) > 0 {
    71  				timeSyncMap1, err := timeToConstructSyncMap(ctx, artifact, cfg)
    72  				if err != nil {
    73  					if _, isNotSupported := err.(build.ErrSyncMapNotSupported); !isNotSupported {
    74  						return fmt.Errorf("constructing inferred sync map: %w", err)
    75  					}
    76  				}
    77  				timeSyncMap2, err := timeToConstructSyncMap(ctx, artifact, cfg)
    78  				if err != nil {
    79  					if _, isNotSupported := err.(build.ErrSyncMapNotSupported); !isNotSupported {
    80  						return fmt.Errorf("constructing inferred sync map: %w", err)
    81  					}
    82  				} else {
    83  					fmt.Fprintf(out, " - Time to construct sync map: %v (2nd time: %v)\n", timeSyncMap1, timeSyncMap2)
    84  				}
    85  			}
    86  
    87  			timeMTimes1, err := timeToComputeMTimes(deps)
    88  			if err != nil {
    89  				return fmt.Errorf("computing modTimes: %w", err)
    90  			}
    91  			timeMTimes2, err := timeToComputeMTimes(deps)
    92  			if err != nil {
    93  				return fmt.Errorf("computing modTimes: %w", err)
    94  			}
    95  
    96  			fmt.Fprintf(out, " - Time to compute mTimes on dependencies: %v (2nd time: %v)\n", timeMTimes1, timeMTimes2)
    97  		}
    98  	}
    99  	return nil
   100  }
   101  
   102  func typeOfArtifact(a *latest.Artifact) string {
   103  	switch {
   104  	case a.DockerArtifact != nil:
   105  		return "Docker artifact"
   106  	case a.BazelArtifact != nil:
   107  		return "Bazel artifact"
   108  	case a.JibArtifact != nil:
   109  		return "Jib artifact"
   110  	case a.KanikoArtifact != nil:
   111  		return "Kaniko artifact"
   112  	case a.CustomArtifact != nil:
   113  		return "Custom artifact"
   114  	case a.BuildpackArtifact != nil:
   115  		return "Buildpack artifact"
   116  	case a.KoArtifact != nil:
   117  		return "Ko artifact"
   118  	default:
   119  		panic("Unknown artifact")
   120  	}
   121  }
   122  
   123  func timeToListDependencies(ctx context.Context, a *latest.Artifact, cfg Config) (string, []string, error) {
   124  	start := time.Now()
   125  	g := graph.ToArtifactGraph(cfg.Artifacts())
   126  	sourceDependencies := graph.NewSourceDependenciesCache(cfg, nil, g)
   127  	paths, err := sourceDependencies.SingleArtifactDependencies(ctx, a)
   128  	return timeutil.Humanize(time.Since(start)), paths, err
   129  }
   130  
   131  func timeToConstructSyncMap(ctx context.Context, a *latest.Artifact, cfg docker.Config) (string, error) {
   132  	start := time.Now()
   133  	_, err := sync.SyncMap(ctx, a, cfg)
   134  	return timeutil.Humanize(time.Since(start)), err
   135  }
   136  
   137  func timeToComputeMTimes(deps []string) (string, error) {
   138  	start := time.Now()
   139  
   140  	if _, err := filemon.Stat(func() ([]string, error) { return deps, nil }); err != nil {
   141  		return "nil", fmt.Errorf("computing modTimes: %w", err)
   142  	}
   143  	return timeutil.Humanize(time.Since(start)), nil
   144  }
   145  
   146  func sizeOfDockerContext(ctx context.Context, a *latest.Artifact, cfg docker.Config) (int64, error) {
   147  	buildCtx, buildCtxWriter := io.Pipe()
   148  	go func() {
   149  		err := docker.CreateDockerTarContext(ctx, buildCtxWriter, docker.NewBuildConfig(
   150  			a.Workspace, a.ImageName, a.DockerArtifact.DockerfilePath, nil), cfg)
   151  		if err != nil {
   152  			buildCtxWriter.CloseWithError(fmt.Errorf("creating docker context: %w", err))
   153  			return
   154  		}
   155  		buildCtxWriter.Close()
   156  	}()
   157  
   158  	return io.Copy(ioutil.Discard, buildCtx)
   159  }