github.com/GoogleContainerTools/skaffold@v1.39.18/pkg/skaffold/build/build_problems.go (about) 1 /* 2 Copyright 2020 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 build 18 19 import ( 20 "context" 21 "fmt" 22 "regexp" 23 24 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/config" 25 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants" 26 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/docker" 27 sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors" 28 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/output/log" 29 "github.com/GoogleContainerTools/skaffold/proto/v1" 30 ) 31 32 const ( 33 PushImageErr = "could not push image" 34 // Error Prefix matches error thrown by Docker 35 // See https://github.com/moby/moby/blob/master/client/errors.go#L18 36 dockerConnectionFailed = ".*(Cannot connect to the Docker daemon.*) Is" 37 38 // See https://github.com/moby/moby/blob/master/client/errors.go#L20 39 // `docker build: error during connect: Post \"https://127.0.0.1:32770/v1.24/build?buildargs=: globalRepo canceled` 40 buildCancelled = ".*context canceled.*" 41 ) 42 43 var ( 44 unknownProjectErr = fmt.Sprintf(".*(%s.* unknown: Project.*)", PushImageErr) 45 nilSuggestions = func(cfg interface{}) []*proto.Suggestion { return nil } 46 // for testing 47 getConfigForCurrentContext = config.GetConfigForCurrentKubectx 48 problems = []sErrors.Problem{ 49 { 50 Regexp: re(fmt.Sprintf(".*%s.* denied: .*", PushImageErr)), 51 ErrCode: proto.StatusCode_BUILD_PUSH_ACCESS_DENIED, 52 Description: func(err error) string { 53 log.Entry(context.TODO()).Tracef("error building %s", err) 54 return "Build Failed. No push access to specified image repository" 55 }, 56 Suggestion: suggestBuildPushAccessDeniedAction, 57 }, 58 { 59 Regexp: re(buildCancelled), 60 ErrCode: proto.StatusCode_BUILD_CANCELLED, 61 Description: func(error) string { 62 return "Build Cancelled" 63 }, 64 Suggestion: nilSuggestions, 65 }, 66 { 67 Regexp: re(unknownProjectErr), 68 Description: func(err error) string { 69 log.Entry(context.TODO()).Tracef("error building %s", err) 70 matchExp := re(unknownProjectErr) 71 if match := matchExp.FindStringSubmatch(err.Error()); len(match) >= 2 { 72 return fmt.Sprintf("Build Failed. %s", match[1]) 73 } 74 return fmt.Sprintf("Build Failed due to %s", err) 75 }, 76 ErrCode: proto.StatusCode_BUILD_PROJECT_NOT_FOUND, 77 Suggestion: func(interface{}) []*proto.Suggestion { 78 return []*proto.Suggestion{{ 79 SuggestionCode: proto.SuggestionCode_CHECK_GCLOUD_PROJECT, 80 Action: "Check your GCR project", 81 }} 82 }, 83 }, 84 { 85 Regexp: re(dockerConnectionFailed), 86 ErrCode: proto.StatusCode_BUILD_DOCKER_DAEMON_NOT_RUNNING, 87 Description: func(err error) string { 88 log.Entry(context.TODO()).Tracef("error building %s", err) 89 matchExp := re(dockerConnectionFailed) 90 if match := matchExp.FindStringSubmatch(err.Error()); len(match) >= 2 { 91 return fmt.Sprintf("Build Failed. %s", match[1]) 92 } 93 return "Build Failed. Could not connect to Docker daemon" 94 }, 95 Suggestion: func(interface{}) []*proto.Suggestion { 96 return []*proto.Suggestion{{ 97 SuggestionCode: proto.SuggestionCode_CHECK_DOCKER_RUNNING, 98 Action: "Check if docker is running", 99 }} 100 }, 101 }, 102 } 103 ) 104 105 // re is a shortcut around regexp.MustCompile 106 func re(s string) *regexp.Regexp { 107 return regexp.MustCompile(s) 108 } 109 110 func init() { 111 sErrors.AddPhaseProblems(constants.Build, problems) 112 } 113 114 func suggestBuildPushAccessDeniedAction(cfg interface{}) []*proto.Suggestion { 115 buildCfg, ok := cfg.(Config) 116 if !ok { 117 return nil 118 } 119 if defaultRepo := buildCfg.DefaultRepo(); defaultRepo != nil { 120 suggestions := []*proto.Suggestion{{ 121 SuggestionCode: proto.SuggestionCode_CHECK_DEFAULT_REPO, 122 Action: "Check your `--default-repo` value", 123 }} 124 return append(suggestions, makeAuthSuggestionsForRepo(*defaultRepo)) 125 } 126 127 // check if global repo is set 128 if gCfg, err := getConfigForCurrentContext(buildCfg.GlobalConfig()); err == nil { 129 if defaultRepo := gCfg.DefaultRepo; defaultRepo != "" { 130 suggestions := []*proto.Suggestion{{ 131 SuggestionCode: proto.SuggestionCode_CHECK_DEFAULT_REPO_GLOBAL_CONFIG, 132 Action: "Check your default-repo setting in skaffold config", 133 }} 134 return append(suggestions, makeAuthSuggestionsForRepo(defaultRepo)) 135 } 136 } 137 138 return []*proto.Suggestion{{ 139 SuggestionCode: proto.SuggestionCode_ADD_DEFAULT_REPO, 140 Action: "Try running with `--default-repo` flag", 141 }} 142 } 143 144 func makeAuthSuggestionsForRepo(repo string) *proto.Suggestion { 145 // parse off the registry component; should have already been validated so unlikely to fail 146 if ref, _ := docker.ParseReference(repo); ref != nil { 147 repo = ref.Domain 148 } 149 150 if re(`(.+\.)?gcr\.io`).MatchString(repo) || re(`.+-docker\.pkg\.dev`).MatchString(repo) { 151 return &proto.Suggestion{ 152 SuggestionCode: proto.SuggestionCode_GCLOUD_DOCKER_AUTH_CONFIGURE, 153 Action: fmt.Sprintf("try `gcloud auth configure-docker%s`", withSpace(repo)), 154 } 155 } 156 return &proto.Suggestion{ 157 SuggestionCode: proto.SuggestionCode_DOCKER_AUTH_CONFIGURE, 158 Action: fmt.Sprintf("try `docker login%s`", withSpace(repo)), 159 } 160 } 161 162 // withSpace returns the given value with a space prepended when not empty. 163 func withSpace(value string) string { 164 if len(value) > 0 { 165 return " " + value 166 } 167 return value 168 }