Skip to content

Commit 0e506fa

Browse files
authored
Add Cluster Internal Service Error code along with runcontext (#5491)
* add error code * add test * Add UserError and tests for the deploy.UserError * make ci happy * code review changes and retesting the scenario manually * fix lint
1 parent b30b60c commit 0e506fa

File tree

15 files changed

+409
-241
lines changed

15 files changed

+409
-241
lines changed

docs/content/en/api/skaffold.swagger.json

+28-14
Large diffs are not rendered by default.

docs/content/en/docs/references/api/grpc.md

+1
Original file line numberDiff line numberDiff line change
@@ -982,6 +982,7 @@ For Cancelled Error code, use range 800 to 850.<br>
982982
| DEPLOY_MANIFEST_WRITE_ERR | 1020 | Error writing hydrated kubernetes manifests. |
983983
| DEPLOY_PARSE_MANIFEST_IMAGES_ERR | 1021 | Error getting images from a kubernetes manifest. |
984984
| DEPLOY_HELM_CREATE_NS_NOT_AVAILABLE | 1022 | Helm config `createNamespace` not available |
985+
| DEPLOY_CLUSTER_INTERNAL_SYSTEM_ERR | 1023 | Kubernetes cluster reported an internal system error |
985986
| TEST_USER_CONFIG_ERR | 1101 | Error expanding paths |
986987
| TEST_CST_USER_ERR | 1102 | Error running container-structure-test |
987988
| TEST_IMG_PULL_ERR | 1103 | Unable to docker pull image |

go.mod

+1
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ require (
5252
github.com/opencontainers/go-digest v1.0.0
5353
github.com/opencontainers/image-spec v1.0.1
5454
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
55+
github.com/pkg/errors v0.9.1 // indirect
5556
github.com/rakyll/statik v0.1.7
5657
github.com/rjeczalik/notify v0.9.3-0.20201210012515-e2a77dcc14cf
5758
github.com/russross/blackfriday/v2 v2.0.1

pkg/skaffold/deploy/deploy_problems.go

+10-20
Original file line numberDiff line numberDiff line change
@@ -21,49 +21,39 @@ import (
2121
"regexp"
2222

2323
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
24+
deployerr "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/error"
2425
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/types"
2526
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
2627
"github.com/GoogleContainerTools/skaffold/proto/v1"
2728
)
2829

30+
var (
31+
clusterConnectionErr = regexp.MustCompile("(?i).*unable to connect.*: Get (.*)")
32+
)
33+
2934
func suggestDeployFailedAction(cfg interface{}) []*proto.Suggestion {
3035
deployCfg, ok := cfg.(types.Config)
3136
if !ok {
3237
return nil
3338
}
34-
kCtx := deployCfg.GetKubeContext()
35-
isMinikube := deployCfg.MinikubeProfile() != ""
36-
37-
if isMinikube {
38-
command := "minikube status"
39-
if deployCfg.GetKubeContext() != "minikube" {
40-
command = fmt.Sprintf("minikube status -p %s", kCtx)
39+
if deployCfg.MinikubeProfile() != "" {
40+
return []*proto.Suggestion{
41+
deployerr.CheckMinikubeStatusSuggestion(deployCfg),
4142
}
42-
return []*proto.Suggestion{{
43-
SuggestionCode: proto.SuggestionCode_CHECK_MINIKUBE_STATUS,
44-
Action: fmt.Sprintf("Check if minikube is running using %q command and try again.", command),
45-
}}
4643
}
47-
4844
return []*proto.Suggestion{{
4945
SuggestionCode: proto.SuggestionCode_CHECK_CLUSTER_CONNECTION,
5046
Action: "Check your connection for the cluster",
5147
}}
5248
}
5349

54-
// re is a shortcut around regexp.MustCompile
55-
func re(s string) *regexp.Regexp {
56-
return regexp.MustCompile(s)
57-
}
58-
5950
func init() {
6051
sErrors.AddPhaseProblems(constants.Deploy, []sErrors.Problem{
6152
{
62-
Regexp: re("(?i).*unable to connect.*: Get (.*)"),
53+
Regexp: clusterConnectionErr,
6354
ErrCode: proto.StatusCode_DEPLOY_CLUSTER_CONNECTION_ERR,
6455
Description: func(err error) string {
65-
matchExp := re("(?i).*unable to connect.*Get (.*)")
66-
if match := matchExp.FindStringSubmatch(err.Error()); len(match) >= 2 {
56+
if match := clusterConnectionErr.FindStringSubmatch(err.Error()); len(match) >= 2 {
6757
return fmt.Sprintf("Deploy Failed. Could not connect to cluster due to %s", match[1])
6858
}
6959
return "Deploy Failed. Could not connect to cluster."

pkg/skaffold/deploy/deploy_problems_test.go

+40-15
Original file line numberDiff line numberDiff line change
@@ -17,9 +17,12 @@ limitations under the License.
1717
package deploy
1818

1919
import (
20+
"fmt"
2021
"testing"
2122

2223
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
24+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
25+
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
2326
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
2427
"github.com/GoogleContainerTools/skaffold/proto/v1"
2528
"github.com/GoogleContainerTools/skaffold/testutil"
@@ -29,34 +32,54 @@ func TestSuggestDeployFailedAction(t *testing.T) {
2932
tests := []struct {
3033
description string
3134
context string
35+
err error
3236
isMinikube bool
33-
expected []*proto.Suggestion
37+
expected string
38+
expectedAE *proto.ActionableErr
3439
}{
3540
{
3641
description: "minikube status",
3742
context: "minikube",
3843
isMinikube: true,
39-
expected: []*proto.Suggestion{{
40-
SuggestionCode: proto.SuggestionCode_CHECK_MINIKUBE_STATUS,
41-
Action: "Check if minikube is running using \"minikube status\" command and try again.",
42-
}},
44+
err: fmt.Errorf("exiting dev mode because first deploy failed: unable to connect to Kubernetes: Get \"https://192.168.64.3:8443/version?timeout=32s\": net/http: TLS handshake timeout"),
45+
expected: "Deploy Failed. Could not connect to cluster due to \"https://192.168.64.3:8443/version?timeout=32s\": net/http: TLS handshake timeout. Check if minikube is running using \"minikube status\" command and try again.",
46+
expectedAE: &proto.ActionableErr{
47+
ErrCode: proto.StatusCode_DEPLOY_CLUSTER_CONNECTION_ERR,
48+
Message: "exiting dev mode because first deploy failed: unable to connect to Kubernetes: Get \"https://192.168.64.3:8443/version?timeout=32s\": net/http: TLS handshake timeout",
49+
Suggestions: []*proto.Suggestion{{
50+
SuggestionCode: proto.SuggestionCode_CHECK_MINIKUBE_STATUS,
51+
Action: "Check if minikube is running using \"minikube status\" command and try again",
52+
}},
53+
},
4354
},
4455
{
4556
description: "minikube status named ctx",
4657
context: "test_cluster",
4758
isMinikube: true,
48-
expected: []*proto.Suggestion{{
49-
SuggestionCode: proto.SuggestionCode_CHECK_MINIKUBE_STATUS,
50-
Action: "Check if minikube is running using \"minikube status -p test_cluster\" command and try again.",
51-
}},
59+
err: fmt.Errorf("exiting dev mode because first deploy failed: unable to connect to Kubernetes: Get \"https://192.168.64.3:8443/version?timeout=32s\": net/http: TLS handshake timeout"),
60+
expected: "Deploy Failed. Could not connect to cluster due to \"https://192.168.64.3:8443/version?timeout=32s\": net/http: TLS handshake timeout. Check if minikube is running using \"minikube status -p test_cluster\" command and try again.",
61+
expectedAE: &proto.ActionableErr{
62+
ErrCode: proto.StatusCode_DEPLOY_CLUSTER_CONNECTION_ERR,
63+
Message: "exiting dev mode because first deploy failed: unable to connect to Kubernetes: Get \"https://192.168.64.3:8443/version?timeout=32s\": net/http: TLS handshake timeout",
64+
Suggestions: []*proto.Suggestion{{
65+
SuggestionCode: proto.SuggestionCode_CHECK_MINIKUBE_STATUS,
66+
Action: "Check if minikube is running using \"minikube status -p test_cluster\" command and try again",
67+
}},
68+
},
5269
},
5370
{
5471
description: "gke cluster",
5572
context: "gke_test",
56-
expected: []*proto.Suggestion{{
57-
SuggestionCode: proto.SuggestionCode_CHECK_CLUSTER_CONNECTION,
58-
Action: "Check your connection for the cluster",
59-
}},
73+
err: fmt.Errorf("exiting dev mode because first deploy failed: unable to connect to Kubernetes: Get \"https://192.168.64.3:8443/version?timeout=32s\": net/http: TLS handshake timeout"),
74+
expected: "Deploy Failed. Could not connect to cluster due to \"https://192.168.64.3:8443/version?timeout=32s\": net/http: TLS handshake timeout. Check your connection for the cluster.",
75+
expectedAE: &proto.ActionableErr{
76+
ErrCode: proto.StatusCode_DEPLOY_CLUSTER_CONNECTION_ERR,
77+
Message: "exiting dev mode because first deploy failed: unable to connect to Kubernetes: Get \"https://192.168.64.3:8443/version?timeout=32s\": net/http: TLS handshake timeout",
78+
Suggestions: []*proto.Suggestion{{
79+
SuggestionCode: proto.SuggestionCode_CHECK_CLUSTER_CONNECTION,
80+
Action: "Check your connection for the cluster",
81+
}},
82+
},
6083
},
6184
}
6285
for _, test := range tests {
@@ -65,8 +88,10 @@ func TestSuggestDeployFailedAction(t *testing.T) {
6588
if test.isMinikube {
6689
cfg.minikube = test.context
6790
}
68-
actual := suggestDeployFailedAction(cfg)
69-
t.CheckDeepEqual(test.expected, actual)
91+
actual := sErrors.ShowAIError(&cfg, test.err)
92+
t.CheckDeepEqual(test.expected, actual.Error())
93+
actualAE := sErrors.ActionableErr(&cfg, constants.Deploy, test.err)
94+
t.CheckDeepEqual(test.expectedAE, actualAE)
7095
})
7196
}
7297
}

pkg/skaffold/deploy/error/errors.go

+68-2
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,26 @@ package error
1818

1919
import (
2020
"fmt"
21+
"regexp"
2122
"strings"
2223

24+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
25+
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/types"
2326
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
2427
"github.com/GoogleContainerTools/skaffold/proto/v1"
2528
)
2629

2730
const (
28-
executableNotFound = "executable file not found"
29-
notFound = "%s not found"
31+
executableNotFound = "executable file not found"
32+
notFound = "%s not found"
33+
defaultMinikubeProfile = "minikube"
34+
)
35+
36+
var (
37+
clusterInternalSystemErr = regexp.MustCompile(".*Internal Server Error")
38+
39+
// for testing
40+
internalSystemErrSuggestion = internalSystemErrSuggestionFunc
3041
)
3142

3243
// DebugHelperRetrieveErr is thrown when debug helpers could not be retrieved.
@@ -57,3 +68,58 @@ func MissingToolErr(toolName string, err error) string {
5768
}
5869
return err.Error()
5970
}
71+
72+
func UserError(err error, sc proto.StatusCode) error {
73+
if sErrors.IsSkaffoldErr(err) {
74+
return err
75+
}
76+
if clusterInternalSystemErr.MatchString(err.Error()) {
77+
return sErrors.NewProblem(
78+
func(err error) string {
79+
return fmt.Sprintf("Deploy Failed. %v", err)
80+
},
81+
proto.StatusCode_DEPLOY_CLUSTER_INTERNAL_SYSTEM_ERR,
82+
internalSystemErrSuggestion,
83+
err)
84+
}
85+
return sErrors.NewError(err,
86+
proto.ActionableErr{
87+
Message: err.Error(),
88+
ErrCode: sc,
89+
})
90+
}
91+
92+
func CheckMinikubeStatusSuggestion(cfg types.Config) *proto.Suggestion {
93+
return &proto.Suggestion{
94+
SuggestionCode: proto.SuggestionCode_CHECK_MINIKUBE_STATUS,
95+
Action: fmt.Sprintf("Check if minikube is running using %q command and try again",
96+
getMinikubeStatusCommand(cfg.GetKubeContext())),
97+
}
98+
}
99+
100+
func getMinikubeStatusCommand(p string) string {
101+
if p == defaultMinikubeProfile {
102+
return "minikube status"
103+
}
104+
return fmt.Sprintf("minikube status -p %s", p)
105+
}
106+
107+
func internalSystemErrSuggestionFunc(cfg interface{}) []*proto.Suggestion {
108+
deployCfg, ok := cfg.(types.Config)
109+
if !ok {
110+
return nil
111+
}
112+
if deployCfg.MinikubeProfile() != "" {
113+
return []*proto.Suggestion{
114+
CheckMinikubeStatusSuggestion(deployCfg),
115+
{
116+
SuggestionCode: proto.SuggestionCode_OPEN_ISSUE,
117+
// TODO: show tip to run minikube logs command and attach logs.
118+
Action: fmt.Sprintf("open an issue at %s", constants.GithubIssueLink),
119+
}}
120+
}
121+
return []*proto.Suggestion{{
122+
SuggestionCode: proto.SuggestionCode_OPEN_ISSUE,
123+
Action: fmt.Sprintf("Something went wrong with your cluster %q. Try again.\nIf this keeps happening please open an issue at %s", deployCfg.GetKubeContext(), constants.GithubIssueLink),
124+
}}
125+
}
+72
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
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 error
18+
19+
import (
20+
"fmt"
21+
"testing"
22+
23+
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
24+
"github.com/GoogleContainerTools/skaffold/proto/v1"
25+
"github.com/GoogleContainerTools/skaffold/testutil"
26+
)
27+
28+
func TestUserError(t *testing.T) {
29+
tests := []struct {
30+
description string
31+
statusCode proto.StatusCode
32+
expected proto.StatusCode
33+
expectedErr string
34+
err error
35+
}{
36+
{
37+
description: "internal system error",
38+
err: fmt.Errorf("Error: (Internal Server Error: the server is currently unable to handle the request)"),
39+
statusCode: proto.StatusCode_DEPLOY_KUSTOMIZE_USER_ERR,
40+
expected: proto.StatusCode_DEPLOY_CLUSTER_INTERNAL_SYSTEM_ERR,
41+
expectedErr: "Deploy Failed. Error: (Internal Server Error: the server is currently unable to handle the request)." +
42+
" Something went wrong.",
43+
},
44+
{
45+
description: "not an internal system err",
46+
err: fmt.Errorf("helm tiller not running"),
47+
statusCode: proto.StatusCode_DEPLOY_HELM_USER_ERR,
48+
expected: proto.StatusCode_DEPLOY_HELM_USER_ERR,
49+
expectedErr: "helm tiller not running",
50+
},
51+
}
52+
for _, test := range tests {
53+
testutil.Run(t, test.description, func(t *testutil.T) {
54+
t.Override(&internalSystemErrSuggestion, func(_ interface{}) []*proto.Suggestion {
55+
return []*proto.Suggestion{{
56+
Action: "Something went wrong",
57+
}}
58+
})
59+
actual := UserError(test.err, test.statusCode)
60+
switch actualType := actual.(type) {
61+
case sErrors.ErrDef:
62+
t.CheckDeepEqual(test.expected, actualType.StatusCode())
63+
case sErrors.Problem:
64+
t.CheckDeepEqual(test.expected, actualType.ErrCode)
65+
actualErr := sErrors.ShowAIError(nil, actualType)
66+
t.CheckErrorContains(test.expectedErr, actualErr)
67+
default:
68+
t.CheckErrorContains(test.expectedErr, actualType)
69+
}
70+
})
71+
}
72+
}

pkg/skaffold/deploy/helm/errors.go

+3-8
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ package helm
1919
import (
2020
"fmt"
2121

22+
"github.com/pkg/errors"
23+
2224
deployerr "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/error"
2325
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
2426
"github.com/GoogleContainerTools/skaffold/proto/v1"
@@ -67,14 +69,7 @@ func helmLabelErr(err error) error {
6769
}
6870

6971
func userErr(prefix string, err error) error {
70-
if sErrors.IsSkaffoldErr(err) {
71-
return err
72-
}
73-
return sErrors.NewError(err,
74-
proto.ActionableErr{
75-
Message: fmt.Sprintf("%s: %s", prefix, err.Error()),
76-
ErrCode: proto.StatusCode_DEPLOY_HELM_USER_ERR,
77-
})
72+
return deployerr.UserError(errors.Wrap(err, prefix), proto.StatusCode_DEPLOY_HELM_USER_ERR)
7873
}
7974

8075
func noMatchingBuild(image string) error {

pkg/skaffold/deploy/kubectl/errors.go

+1-5
Original file line numberDiff line numberDiff line change
@@ -90,9 +90,5 @@ func listManifestErr(err error) error {
9090
}
9191

9292
func userErr(err error) error {
93-
return sErrors.NewError(err,
94-
proto.ActionableErr{
95-
Message: err.Error(),
96-
ErrCode: proto.StatusCode_DEPLOY_KUBECTL_USER_ERR,
97-
})
93+
return deployerr.UserError(err, proto.StatusCode_DEPLOY_KUBECTL_USER_ERR)
9894
}

pkg/skaffold/deploy/kustomize/errors.go

+2-6
Original file line numberDiff line numberDiff line change
@@ -17,14 +17,10 @@ limitations under the License.
1717
package kustomize
1818

1919
import (
20-
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
20+
deployerr "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/error"
2121
"github.com/GoogleContainerTools/skaffold/proto/v1"
2222
)
2323

2424
func userErr(err error) error {
25-
return sErrors.NewError(err,
26-
proto.ActionableErr{
27-
Message: err.Error(),
28-
ErrCode: proto.StatusCode_DEPLOY_KUSTOMIZE_USER_ERR,
29-
})
25+
return deployerr.UserError(err, proto.StatusCode_DEPLOY_KUSTOMIZE_USER_ERR)
3026
}

0 commit comments

Comments
 (0)