Skip to content

Commit ccca995

Browse files
committed
[v3] Add proto for render error code and suggestions.
1 parent 4bd76de commit ccca995

File tree

10 files changed

+487
-237
lines changed

10 files changed

+487
-237
lines changed

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

+120-31
Large diffs are not rendered by default.

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

+11
Original file line numberDiff line numberDiff line change
@@ -844,6 +844,7 @@ Enum indicating deploy tools used
844844
| HELM | 1 | Helm Deployer |
845845
| KUSTOMIZE | 2 | Kustomize Deployer |
846846
| KUBECTL | 3 | Kubectl Deployer |
847+
| KPT | 4 | kpt Deployer |
847848

848849

849850

@@ -878,6 +879,7 @@ For Cancelled Error code, use range 800 to 850.<br>
878879
| OK | 0 | A default status code for events that do not have an associated phase. Typically seen with the DevEndEvent event on success. |
879880
| STATUSCHECK_SUCCESS | 200 | Status Check Success |
880881
| BUILD_SUCCESS | 201 | Build Success |
882+
| RENDER_SUCCESS | 204 | Render Success |
881883
| DEPLOY_SUCCESS | 202 | Deploy Success |
882884
| TEST_SUCCESS | 203 | Test Success |
883885
| BUILD_PUSH_ACCESS_DENIED | 101 | Build error due to push access denied |
@@ -1010,10 +1012,15 @@ For Cancelled Error code, use range 800 to 850.<br>
10101012
| CONFIG_MULTI_IMPORT_PROFILE_CONFLICT_ERR | 1211 | Same config imported at least twice with different set of profiles |
10111013
| CONFIG_PROFILES_NOT_FOUND_ERR | 1212 | Profile selection did not match known profile names |
10121014
| CONFIG_UNKNOWN_API_VERSION_ERR | 1213 | Config API version not found |
1015+
| CONFIG_UNKNOWN_VALIDATOR | 1214 | The validator is not supported in skaffold-managed mode (not whitelisted). |
1016+
| CONFIG_UNKNOWN_MUTATOR | 1215 | The mutator is not supported in skaffold-managed mode (not whitelisted). |
10131017
| INSPECT_UNKNOWN_ERR | 1301 | Catch-all `skaffold inspect` command error |
10141018
| INSPECT_BUILD_ENV_ALREADY_EXISTS_ERR | 1302 | Trying to add new build environment that already exists |
10151019
| INSPECT_BUILD_ENV_INCORRECT_TYPE_ERR | 1303 | Trying to modify build environment that doesn't exist |
10161020
| INSPECT_PROFILE_NOT_FOUND_ERR | 1304 | Trying to modify a profile that doesn't exist |
1021+
| KPTFILE_INVALID_YAML_ERR | 1401 | Kptfile errors The Kptfile is not a valid yaml file |
1022+
| KPTFILE_INVALID_SCHEMA_ERR | 1402 | The Kptfile is not a valid API schema |
1023+
| KPTFILE_INIT_ERR | 1403 | The Kptfile cannot be created via `kpt pkg init` or `kpt live init`. |
10171024

10181025

10191026

@@ -1066,6 +1073,8 @@ Enum for Suggestion codes
10661073
| UNPAUSE_MINIKUBE | 502 | Minikube is paused: use `minikube unpause` |
10671074
| RUN_DOCKER_PULL | 551 | Run Docker pull for the image with v1 manifest and try again. |
10681075
| SET_RENDER_FLAG_OFFLINE_FALSE | 600 | Rerun with correct offline flag value. |
1076+
| KPTFILE_MANUAL_INIT | 601 | Manually run `kpt pkg init` or `kpt live init` |
1077+
| KPTFILE_CHECK_YAML | 602 | Check if the Kptfile is correct. |
10691078
| CONFIG_CHECK_FILE_PATH | 700 | Check configuration file path |
10701079
| CONFIG_CHECK_DEPENDENCY_DEFINITION | 701 | Check dependency config definition |
10711080
| CONFIG_CHANGE_NAMES | 702 | Change config name to avoid duplicates |
@@ -1074,6 +1083,8 @@ Enum for Suggestion codes
10741083
| CONFIG_CHECK_DEPENDENCY_PROFILES_SELECTION | 705 | Check active profile selection for dependency config |
10751084
| CONFIG_CHECK_PROFILE_SELECTION | 706 | Check profile selection flag |
10761085
| CONFIG_FIX_API_VERSION | 707 | Fix config API version or upgrade the skaffold binary |
1086+
| CONFIG_WHITELISTED_VALIDATORS | 708 | Only the whitelisted validators are acceptable in skaffold-managed mode. |
1087+
| CONFIG_WHITELISTED_MUTATORS | 709 | Only the whitelisted validators are acceptable in skaffold-managed mode. |
10771088
| INSPECT_USE_MODIFY_OR_NEW_PROFILE | 800 | Create new build env in a profile instead, or use the 'modify' command |
10781089
| INSPECT_USE_ADD_BUILD_ENV | 801 | Check profile selection, or use the 'add' command instead |
10791090
| INSPECT_CHECK_INPUT_PROFILE | 802 | Check profile flag value |

pkg/skaffold/render/renderer/renderer.go

+25-6
Original file line numberDiff line numberDiff line change
@@ -27,13 +27,15 @@ import (
2727
"github.com/sirupsen/logrus"
2828
"gopkg.in/yaml.v2"
2929

30+
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
3031
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/graph"
3132
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/kubernetes/manifest"
3233
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/generate"
3334
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/kptfile"
3435
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/validate"
3536
latestV2 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v2"
3637
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/util"
38+
"github.com/GoogleContainerTools/skaffold/proto/v1"
3739
)
3840

3941
const (
@@ -97,9 +99,17 @@ func (r *SkaffoldRenderer) prepareHydrationDir(ctx context.Context) error {
9799
if _, err := os.Stat(kptFilePath); os.IsNotExist(err) {
98100
cmd := exec.CommandContext(ctx, "kpt", "pkg", "init", r.hydrationDir)
99101
if _, err := util.RunCmdOut(cmd); err != nil {
100-
// TODO: user error. need manual init
101-
return fmt.Errorf("unable to initialize kpt directory in %v, please manually run `kpt pkg init %v`",
102-
kptFilePath, kptFilePath)
102+
return sErrors.NewError(err,
103+
proto.ActionableErr{
104+
Message: fmt.Sprintf("unable to initialize Kptfile in %v", r.hydrationDir),
105+
ErrCode: proto.StatusCode_KPTFILE_INIT_ERR,
106+
Suggestions: []*proto.Suggestion{
107+
{
108+
SuggestionCode: proto.SuggestionCode_KPTFILE_MANUAL_INIT,
109+
Action: fmt.Sprintf("please manually run `kpt pkg init %v`", r.hydrationDir),
110+
},
111+
},
112+
})
103113
}
104114
}
105115
return nil
@@ -135,9 +145,18 @@ func (r *SkaffoldRenderer) Render(ctx context.Context, out io.Writer, builds []g
135145
defer file.Close()
136146
kfConfig := &kptfile.KptFile{}
137147
if err := yaml.NewDecoder(file).Decode(&kfConfig); err != nil {
138-
// TODO: user error.
139-
return fmt.Errorf("unable to parse %v: %w, please check if the kptfile is updated to new apiVersion > v1alpha2",
140-
kptFilePath, err)
148+
return sErrors.NewError(err,
149+
proto.ActionableErr{
150+
Message: fmt.Sprintf("unable to parse Kptfile in %v", r.hydrationDir),
151+
ErrCode: proto.StatusCode_KPTFILE_INVALID_YAML_ERR,
152+
Suggestions: []*proto.Suggestion{
153+
{
154+
SuggestionCode: proto.SuggestionCode_KPTFILE_CHECK_YAML,
155+
Action: fmt.Sprintf("please check if the Kptfile is correct and " +
156+
"the `apiVersion` is greater than `v1alpha2`"),
157+
},
158+
},
159+
})
141160
}
142161
if kfConfig.Pipeline == nil {
143162
kfConfig.Pipeline = &kptfile.Pipeline{}

pkg/skaffold/render/renderer/renderer_test.go

+16
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ package renderer
1818
import (
1919
"bytes"
2020
"context"
21+
"errors"
2122
"fmt"
2223
"path/filepath"
2324
"testing"
@@ -149,3 +150,18 @@ pipeline:
149150
})
150151
}
151152
}
153+
func TestRender_UserErr(t *testing.T) {
154+
testutil.Run(t, "", func(t *testutil.T) {
155+
r, err := NewSkaffoldRenderer(&latestV2.RenderConfig{
156+
Generate: &latestV2.Generate{Manifests: []string{"pod.yaml"}},
157+
Validate: &[]latestV2.Validator{{Name: "kubeval"}},
158+
}, "")
159+
t.CheckNoError(err)
160+
fakeCmd := testutil.CmdRunOutErr(fmt.Sprintf("kpt pkg init %v", DefaultHydrationDir), "",
161+
errors.New("fake err"))
162+
t.Override(&util.DefaultExecCommand, fakeCmd)
163+
err = r.Render(context.Background(), &bytes.Buffer{}, []graph.Artifact{{ImageName: "leeroy-web",
164+
Tag: "leeroy-web:v1"}})
165+
t.CheckContains("please manually run `kpt pkg init", err.Error())
166+
})
167+
}

pkg/skaffold/render/validate/validate.go

+23-6
Original file line numberDiff line numberDiff line change
@@ -18,23 +18,40 @@ package validate
1818
import (
1919
"fmt"
2020

21+
sErrors "github.com/GoogleContainerTools/skaffold/pkg/skaffold/errors"
2122
"github.com/GoogleContainerTools/skaffold/pkg/skaffold/render/kptfile"
2223
latestV2 "github.com/GoogleContainerTools/skaffold/pkg/skaffold/schema/latest/v2"
24+
"github.com/GoogleContainerTools/skaffold/proto/v1"
2325
)
2426

25-
var validatorWhitelist = map[string]kptfile.Function{
26-
"kubeval": {Image: "gcr.io/kpt-fn/kubeval:v0.1"},
27-
// TODO: Add conftest validator in kpt catalog.
28-
}
27+
var (
28+
WhitelistedValidators = []string{"kubeval"}
29+
validatorWhitelist = map[string]kptfile.Function{
30+
"kubeval": {Image: "gcr.io/kpt-fn/kubeval:v0.1"},
31+
// TODO: Add conftest validator in kpt catalog.
32+
}
33+
)
2934

3035
// NewValidator instantiates a Validator object.
3136
func NewValidator(config []latestV2.Validator) (*Validator, error) {
3237
var fns []kptfile.Function
3338
for _, c := range config {
3439
fn, ok := validatorWhitelist[c.Name]
3540
if !ok {
36-
// TODO: kpt user error
37-
return nil, fmt.Errorf("unsupported validator %v", c.Name)
41+
// TODO: Add links to explain "skaffold-managed mode" and "kpt-managed mode".
42+
return nil, sErrors.NewErrorWithStatusCode(
43+
proto.ActionableErr{
44+
Message: fmt.Sprintf("unsupported validator %q", c.Name),
45+
ErrCode: proto.StatusCode_CONFIG_UNKNOWN_VALIDATOR,
46+
Suggestions: []*proto.Suggestion{
47+
{
48+
SuggestionCode: proto.SuggestionCode_CONFIG_WHITELISTED_VALIDATORS,
49+
Action: fmt.Sprintf(
50+
"please only use the following validators in skaffold-managed mode: %v. "+
51+
"to use custom validators, please use kpt-managed mode.", WhitelistedValidators),
52+
},
53+
},
54+
})
3855
}
3956
fns = append(fns, fn)
4057
}

pkg/skaffold/render/validate/validate_test.go

+11-12
Original file line numberDiff line numberDiff line change
@@ -22,36 +22,35 @@ import (
2222
"github.com/GoogleContainerTools/skaffold/testutil"
2323
)
2424

25-
func TestValidatorInit(t *testing.T) {
25+
func TestNewValidator(t *testing.T) {
2626
tests := []struct {
2727
description string
2828
config []latestV2.Validator
29-
shouldErr bool
3029
}{
3130
{
3231
description: "no validation",
3332
config: []latestV2.Validator{},
34-
shouldErr: false,
3533
},
3634
{
3735
description: "kubeval validator",
3836
config: []latestV2.Validator{
3937
{Name: "kubeval"},
4038
},
41-
shouldErr: false,
42-
},
43-
{
44-
description: "invalid validator",
45-
config: []latestV2.Validator{
46-
{Name: "bad-validator"},
47-
},
48-
shouldErr: true,
4939
},
5040
}
5141
for _, test := range tests {
5242
testutil.Run(t, test.description, func(t *testutil.T) {
5343
_, err := NewValidator(test.config)
54-
t.CheckError(test.shouldErr, err)
44+
t.CheckNoError(err)
5545
})
5646
}
5747
}
48+
49+
func TestNewValidator_Error(t *testing.T) {
50+
testutil.Run(t, "", func(t *testutil.T) {
51+
_, err := NewValidator([]latestV2.Validator{
52+
{Name: "bad-validator"},
53+
})
54+
t.CheckContains(`unsupported validator "bad-validator". please only use the`, err.Error())
55+
})
56+
}

0 commit comments

Comments
 (0)