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 }