Skip to content

Add Cluster Internal Service Error code along with runcontext #5491

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 22, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 28 additions & 14 deletions docs/content/en/api/skaffold.swagger.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions docs/content/en/docs/references/api/grpc.md
Original file line number Diff line number Diff line change
Expand Up @@ -982,6 +982,7 @@ For Cancelled Error code, use range 800 to 850.<br>
| DEPLOY_MANIFEST_WRITE_ERR | 1020 | Error writing hydrated kubernetes manifests. |
| DEPLOY_PARSE_MANIFEST_IMAGES_ERR | 1021 | Error getting images from a kubernetes manifest. |
| DEPLOY_HELM_CREATE_NS_NOT_AVAILABLE | 1022 | Helm config `createNamespace` not available |
| DEPLOY_CLUSTER_INTERNAL_SYSTEM_ERR | 1023 | Kubernetes cluster reported an internal system error |
| TEST_USER_CONFIG_ERR | 1101 | Error expanding paths |
| TEST_CST_USER_ERR | 1102 | Error running container-structure-test |
| TEST_IMG_PULL_ERR | 1103 | Unable to docker pull image |
Expand Down
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ require (
github.com/opencontainers/go-digest v1.0.0
github.com/opencontainers/image-spec v1.0.1
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
github.com/pkg/errors v0.9.1 // indirect
github.com/rakyll/statik v0.1.7
github.com/rjeczalik/notify v0.9.3-0.20201210012515-e2a77dcc14cf
github.com/russross/blackfriday/v2 v2.0.1
Expand Down
30 changes: 10 additions & 20 deletions pkg/skaffold/deploy/deploy_problems.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,49 +21,39 @@ import (
"regexp"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
deployerr "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/error"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/types"
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
"github.com/GoogleContainerTools/skaffold/proto/v1"
)

var (
clusterConnectionErr = regexp.MustCompile("(?i).*unable to connect.*: Get (.*)")
)

func suggestDeployFailedAction(cfg interface{}) []*proto.Suggestion {
deployCfg, ok := cfg.(types.Config)
if !ok {
return nil
}
kCtx := deployCfg.GetKubeContext()
isMinikube := deployCfg.MinikubeProfile() != ""

if isMinikube {
command := "minikube status"
if deployCfg.GetKubeContext() != "minikube" {
command = fmt.Sprintf("minikube status -p %s", kCtx)
if deployCfg.MinikubeProfile() != "" {
return []*proto.Suggestion{
deployerr.CheckMinikubeStatusSuggestion(deployCfg),
}
return []*proto.Suggestion{{
SuggestionCode: proto.SuggestionCode_CHECK_MINIKUBE_STATUS,
Action: fmt.Sprintf("Check if minikube is running using %q command and try again.", command),
}}
}

return []*proto.Suggestion{{
SuggestionCode: proto.SuggestionCode_CHECK_CLUSTER_CONNECTION,
Action: "Check your connection for the cluster",
}}
}

// re is a shortcut around regexp.MustCompile
func re(s string) *regexp.Regexp {
return regexp.MustCompile(s)
}

func init() {
sErrors.AddPhaseProblems(constants.Deploy, []sErrors.Problem{
{
Regexp: re("(?i).*unable to connect.*: Get (.*)"),
Regexp: clusterConnectionErr,
ErrCode: proto.StatusCode_DEPLOY_CLUSTER_CONNECTION_ERR,
Description: func(err error) string {
matchExp := re("(?i).*unable to connect.*Get (.*)")
if match := matchExp.FindStringSubmatch(err.Error()); len(match) >= 2 {
if match := clusterConnectionErr.FindStringSubmatch(err.Error()); len(match) >= 2 {
return fmt.Sprintf("Deploy Failed. Could not connect to cluster due to %s", match[1])
}
return "Deploy Failed. Could not connect to cluster."
Expand Down
55 changes: 40 additions & 15 deletions pkg/skaffold/deploy/deploy_problems_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,12 @@ limitations under the License.
package deploy

import (
"fmt"
"testing"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/config"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest"
"github.com/GoogleContainerTools/skaffold/proto/v1"
"github.com/GoogleContainerTools/skaffold/testutil"
Expand All @@ -29,34 +32,54 @@ func TestSuggestDeployFailedAction(t *testing.T) {
tests := []struct {
description string
context string
err error
isMinikube bool
expected []*proto.Suggestion
expected string
expectedAE *proto.ActionableErr
}{
{
description: "minikube status",
context: "minikube",
isMinikube: true,
expected: []*proto.Suggestion{{
SuggestionCode: proto.SuggestionCode_CHECK_MINIKUBE_STATUS,
Action: "Check if minikube is running using \"minikube status\" command and try again.",
}},
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"),
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.",
expectedAE: &proto.ActionableErr{
ErrCode: proto.StatusCode_DEPLOY_CLUSTER_CONNECTION_ERR,
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",
Suggestions: []*proto.Suggestion{{
SuggestionCode: proto.SuggestionCode_CHECK_MINIKUBE_STATUS,
Action: "Check if minikube is running using \"minikube status\" command and try again",
}},
},
},
{
description: "minikube status named ctx",
context: "test_cluster",
isMinikube: true,
expected: []*proto.Suggestion{{
SuggestionCode: proto.SuggestionCode_CHECK_MINIKUBE_STATUS,
Action: "Check if minikube is running using \"minikube status -p test_cluster\" command and try again.",
}},
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"),
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.",
expectedAE: &proto.ActionableErr{
ErrCode: proto.StatusCode_DEPLOY_CLUSTER_CONNECTION_ERR,
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",
Suggestions: []*proto.Suggestion{{
SuggestionCode: proto.SuggestionCode_CHECK_MINIKUBE_STATUS,
Action: "Check if minikube is running using \"minikube status -p test_cluster\" command and try again",
}},
},
},
{
description: "gke cluster",
context: "gke_test",
expected: []*proto.Suggestion{{
SuggestionCode: proto.SuggestionCode_CHECK_CLUSTER_CONNECTION,
Action: "Check your connection for the cluster",
}},
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"),
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.",
expectedAE: &proto.ActionableErr{
ErrCode: proto.StatusCode_DEPLOY_CLUSTER_CONNECTION_ERR,
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",
Suggestions: []*proto.Suggestion{{
SuggestionCode: proto.SuggestionCode_CHECK_CLUSTER_CONNECTION,
Action: "Check your connection for the cluster",
}},
},
},
}
for _, test := range tests {
Expand All @@ -65,8 +88,10 @@ func TestSuggestDeployFailedAction(t *testing.T) {
if test.isMinikube {
cfg.minikube = test.context
}
actual := suggestDeployFailedAction(cfg)
t.CheckDeepEqual(test.expected, actual)
actual := sErrors.ShowAIError(&cfg, test.err)
t.CheckDeepEqual(test.expected, actual.Error())
actualAE := sErrors.ActionableErr(&cfg, constants.Deploy, test.err)
t.CheckDeepEqual(test.expectedAE, actualAE)
})
}
}
Expand Down
70 changes: 68 additions & 2 deletions pkg/skaffold/deploy/error/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,26 @@ package error

import (
"fmt"
"regexp"
"strings"

"github.com/GoogleContainerTools/skaffold/pkg/skaffold/constants"
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/types"
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
"github.com/GoogleContainerTools/skaffold/proto/v1"
)

const (
executableNotFound = "executable file not found"
notFound = "%s not found"
executableNotFound = "executable file not found"
notFound = "%s not found"
defaultMinikubeProfile = "minikube"
)

var (
clusterInternalSystemErr = regexp.MustCompile(".*Internal Server Error")

// for testing
internalSystemErrSuggestion = internalSystemErrSuggestionFunc
)

// DebugHelperRetrieveErr is thrown when debug helpers could not be retrieved.
Expand Down Expand Up @@ -57,3 +68,58 @@ func MissingToolErr(toolName string, err error) string {
}
return err.Error()
}

func UserError(err error, sc proto.StatusCode) error {
if sErrors.IsSkaffoldErr(err) {
return err
}
if clusterInternalSystemErr.MatchString(err.Error()) {
return sErrors.NewProblem(
func(err error) string {
return fmt.Sprintf("Deploy Failed. %v", err)
},
proto.StatusCode_DEPLOY_CLUSTER_INTERNAL_SYSTEM_ERR,
internalSystemErrSuggestion,
err)
}
return sErrors.NewError(err,
proto.ActionableErr{
Message: err.Error(),
ErrCode: sc,
})
}

func CheckMinikubeStatusSuggestion(cfg types.Config) *proto.Suggestion {
return &proto.Suggestion{
SuggestionCode: proto.SuggestionCode_CHECK_MINIKUBE_STATUS,
Action: fmt.Sprintf("Check if minikube is running using %q command and try again",
getMinikubeStatusCommand(cfg.GetKubeContext())),
}
}

func getMinikubeStatusCommand(p string) string {
if p == defaultMinikubeProfile {
return "minikube status"
}
return fmt.Sprintf("minikube status -p %s", p)
}

func internalSystemErrSuggestionFunc(cfg interface{}) []*proto.Suggestion {
deployCfg, ok := cfg.(types.Config)
if !ok {
return nil
}
if deployCfg.MinikubeProfile() != "" {
return []*proto.Suggestion{
CheckMinikubeStatusSuggestion(deployCfg),
{
SuggestionCode: proto.SuggestionCode_OPEN_ISSUE,
// TODO: show tip to run minikube logs command and attach logs.
Action: fmt.Sprintf("open an issue at %s", constants.GithubIssueLink),
}}
}
return []*proto.Suggestion{{
SuggestionCode: proto.SuggestionCode_OPEN_ISSUE,
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),
}}
}
72 changes: 72 additions & 0 deletions pkg/skaffold/deploy/error/errors_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*
Copyright 2021 The Skaffold Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package error

import (
"fmt"
"testing"

sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
"github.com/GoogleContainerTools/skaffold/proto/v1"
"github.com/GoogleContainerTools/skaffold/testutil"
)

func TestUserError(t *testing.T) {
tests := []struct {
description string
statusCode proto.StatusCode
expected proto.StatusCode
expectedErr string
err error
}{
{
description: "internal system error",
err: fmt.Errorf("Error: (Internal Server Error: the server is currently unable to handle the request)"),
statusCode: proto.StatusCode_DEPLOY_KUSTOMIZE_USER_ERR,
expected: proto.StatusCode_DEPLOY_CLUSTER_INTERNAL_SYSTEM_ERR,
expectedErr: "Deploy Failed. Error: (Internal Server Error: the server is currently unable to handle the request)." +
" Something went wrong.",
},
{
description: "not an internal system err",
err: fmt.Errorf("helm tiller not running"),
statusCode: proto.StatusCode_DEPLOY_HELM_USER_ERR,
expected: proto.StatusCode_DEPLOY_HELM_USER_ERR,
expectedErr: "helm tiller not running",
},
}
for _, test := range tests {
testutil.Run(t, test.description, func(t *testutil.T) {
t.Override(&internalSystemErrSuggestion, func(_ interface{}) []*proto.Suggestion {
return []*proto.Suggestion{{
Action: "Something went wrong",
}}
})
actual := UserError(test.err, test.statusCode)
switch actualType := actual.(type) {
case sErrors.ErrDef:
t.CheckDeepEqual(test.expected, actualType.StatusCode())
case sErrors.Problem:
t.CheckDeepEqual(test.expected, actualType.ErrCode)
actualErr := sErrors.ShowAIError(nil, actualType)
t.CheckErrorContains(test.expectedErr, actualErr)
default:
t.CheckErrorContains(test.expectedErr, actualType)
}
})
}
}
11 changes: 3 additions & 8 deletions pkg/skaffold/deploy/helm/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ package helm
import (
"fmt"

"github.com/pkg/errors"

deployerr "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/error"
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
"github.com/GoogleContainerTools/skaffold/proto/v1"
Expand Down Expand Up @@ -67,14 +69,7 @@ func helmLabelErr(err error) error {
}

func userErr(prefix string, err error) error {
if sErrors.IsSkaffoldErr(err) {
return err
}
return sErrors.NewError(err,
proto.ActionableErr{
Message: fmt.Sprintf("%s: %s", prefix, err.Error()),
ErrCode: proto.StatusCode_DEPLOY_HELM_USER_ERR,
})
return deployerr.UserError(errors.Wrap(err, prefix), proto.StatusCode_DEPLOY_HELM_USER_ERR)
}

func noMatchingBuild(image string) error {
Expand Down
6 changes: 1 addition & 5 deletions pkg/skaffold/deploy/kubectl/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -90,9 +90,5 @@ func listManifestErr(err error) error {
}

func userErr(err error) error {
return sErrors.NewError(err,
proto.ActionableErr{
Message: err.Error(),
ErrCode: proto.StatusCode_DEPLOY_KUBECTL_USER_ERR,
})
return deployerr.UserError(err, proto.StatusCode_DEPLOY_KUBECTL_USER_ERR)
}
8 changes: 2 additions & 6 deletions pkg/skaffold/deploy/kustomize/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,14 +17,10 @@ limitations under the License.
package kustomize

import (
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
deployerr "github.com/GoogleContainerTools/skaffold/pkg/skaffold/deploy/error"
"github.com/GoogleContainerTools/skaffold/proto/v1"
)

func userErr(err error) error {
return sErrors.NewError(err,
proto.ActionableErr{
Message: err.Error(),
ErrCode: proto.StatusCode_DEPLOY_KUSTOMIZE_USER_ERR,
})
return deployerr.UserError(err, proto.StatusCode_DEPLOY_KUSTOMIZE_USER_ERR)
}
Loading