Skip to content

Commit 82fc28e

Browse files
author
Mengqi Yu
authored
Surface failure information from pod evaluator (#3105)
1 parent 3205565 commit 82fc28e

File tree

3 files changed

+82
-11
lines changed

3 files changed

+82
-11
lines changed

porch/func/internal/podevaluator.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,10 @@ func (pe *podEvaluator) EvaluateFunction(ctx context.Context, req *evaluator.Eva
134134
if err != nil {
135135
return nil, fmt.Errorf("unable to evaluate %v with pod evaluator: %w", req.Image, err)
136136
}
137+
// Log stderr when the function succeeded. If the function fails, stderr will be surfaced to the users.
138+
if len(resp.Log) > 0 {
139+
klog.Warningf("evaluating %v succeeded, but stderr is: %v", req.Image, string(resp.Log))
140+
}
137141
return resp, nil
138142
}
139143

@@ -581,7 +585,7 @@ func (pm *podManager) podIpIfRunningAndReady(ctx context.Context, podKey client.
581585
}
582586
return false, nil
583587
}); e != nil {
584-
return "", fmt.Errorf("error when waiting the pod to be ready: %w", e)
588+
return "", fmt.Errorf("error occured when waiting the pod to be ready. If the error is caused by timeout, you may want to examine the pods in namespace %q. Error: %w", pm.namespace, e)
585589
}
586590
return pod.Status.PodIP, nil
587591
}

porch/func/wrapper-server/main.go

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ import (
2323
"os"
2424
"os/exec"
2525

26+
"github.com/GoogleContainerTools/kpt-functions-sdk/go/fn"
2627
pb "github.com/GoogleContainerTools/kpt/porch/func/evaluator"
2728
"github.com/GoogleContainerTools/kpt/porch/func/healthchecker"
2829
"github.com/spf13/cobra"
@@ -98,15 +99,26 @@ func (e *singleFunctionEvaluator) EvaluateFunction(ctx context.Context, req *pb.
9899

99100
err := cmd.Run()
100101
var exitErr *exec.ExitError
101-
if err != nil && !errors.As(err, &exitErr) {
102-
return nil, status.Errorf(codes.Internal, "Failed to execute function %q: %s (%s)", req.Image, err, stderr.String())
102+
outbytes := stdout.Bytes()
103+
stderrStr := stderr.String()
104+
if err != nil {
105+
if errors.As(err, &exitErr) {
106+
// If the exit code is non-zero, we will try to embed the structured results and content from stderr into the error message.
107+
rl, pe := fn.ParseResourceList(outbytes)
108+
if pe != nil {
109+
// If we can't parse the output resource list, we only surface the content in stderr.
110+
return nil, status.Errorf(codes.Internal, "failed to evaluate function %q with stderr %v", req.Image, stderrStr)
111+
}
112+
return nil, status.Errorf(codes.Internal, "failed to evaluate function %q with structured results: %v and stderr: %v", req.Image, rl.Results.Error(), stderrStr)
113+
} else {
114+
return nil, status.Errorf(codes.Internal, "Failed to execute function %q: %s (%s)", req.Image, err, stderrStr)
115+
}
103116
}
104117

105-
outbytes := stdout.Bytes()
106-
klog.Infof("Evaluated %q: stdout length: %d\nstderr:\n%v", req.Image, len(outbytes), stderr.String())
118+
klog.Infof("Evaluated %q: stdout length: %d\nstderr:\n%v", req.Image, len(outbytes), stderrStr)
107119

108120
return &pb.EvaluateFunctionResponse{
109121
ResourceList: outbytes,
110-
Log: stderr.Bytes(),
122+
Log: []byte(stderrStr),
111123
}, nil
112124
}

porch/test/e2e/e2e_test.go

Lines changed: 60 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1040,6 +1040,11 @@ func (t *PorchSuite) TestPodEvaluator(ctx context.Context) {
10401040
t.Skipf("Skipping due to not having pod evalutor in local mode")
10411041
}
10421042

1043+
const (
1044+
generateFolderImage = "gcr.io/kpt-fn/generate-folders:v0.1.1" // This function is a TS based function.
1045+
setAnnotationsImage = "gcr.io/kpt-fn/set-annotations:v0.1.3" // set-annotations:v0.1.3 is an older version that porch maps it neither to built-in nor exec.
1046+
)
1047+
10431048
// Register the repository as 'git-fn'
10441049
t.registerMainGitRepositoryF(ctx, "git-fn-pod")
10451050

@@ -1070,14 +1075,14 @@ func (t *PorchSuite) TestPodEvaluator(ctx context.Context) {
10701075
{
10711076
Type: "eval",
10721077
Eval: &porchapi.FunctionEvalTaskSpec{
1073-
Image: "gcr.io/kpt-fn/generate-folders:v0.1.1", // This function is a TS based function.
1078+
Image: generateFolderImage,
10741079
},
10751080
},
10761081
// Testing pod evaluator with golang function
10771082
{
10781083
Type: "eval",
10791084
Eval: &porchapi.FunctionEvalTaskSpec{
1080-
Image: "gcr.io/kpt-fn/set-annotations:v0.1.3", // set-annotations:v0.1.3 is an older version that porch maps it neither to built-in nor exec.
1085+
Image: setAnnotationsImage,
10811086
ConfigMap: map[string]string{
10821087
"test-key": "test-val",
10831088
},
@@ -1116,7 +1121,10 @@ func (t *PorchSuite) TestPodEvaluator(ctx context.Context) {
11161121
podList := &coreapi.PodList{}
11171122
t.ListF(ctx, podList, client.InNamespace("porch-fn-system"))
11181123
for _, pod := range podList.Items {
1119-
t.DeleteF(ctx, &pod)
1124+
img := pod.Spec.Containers[0].Image
1125+
if img == generateFolderImage || img == setAnnotationsImage {
1126+
t.DeleteF(ctx, &pod)
1127+
}
11201128
}
11211129

11221130
// Create another Package Revision
@@ -1146,14 +1154,14 @@ func (t *PorchSuite) TestPodEvaluator(ctx context.Context) {
11461154
{
11471155
Type: "eval",
11481156
Eval: &porchapi.FunctionEvalTaskSpec{
1149-
Image: "gcr.io/kpt-fn/generate-folders:v0.1.1", // This function is a TS based function.
1157+
Image: generateFolderImage,
11501158
},
11511159
},
11521160
// Testing pod evaluator with golang function
11531161
{
11541162
Type: "eval",
11551163
Eval: &porchapi.FunctionEvalTaskSpec{
1156-
Image: "gcr.io/kpt-fn/set-annotations:v0.1.3", // set-annotations:v0.1.3 is an older version that porch maps it neither to built-in nor exec.
1164+
Image: setAnnotationsImage,
11571165
ConfigMap: map[string]string{
11581166
"new-test-key": "new-test-val",
11591167
},
@@ -1188,6 +1196,53 @@ func (t *PorchSuite) TestPodEvaluator(ctx context.Context) {
11881196
}
11891197
}
11901198

1199+
func (t *PorchSuite) TestPodEvaluatorWithFailure(ctx context.Context) {
1200+
if t.local {
1201+
t.Skipf("Skipping due to not having pod evalutor in local mode")
1202+
}
1203+
1204+
t.registerMainGitRepositoryF(ctx, "git-fn-pod-failure")
1205+
1206+
// Create Package Revision
1207+
pr := &porchapi.PackageRevision{
1208+
ObjectMeta: metav1.ObjectMeta{
1209+
Namespace: t.namespace,
1210+
},
1211+
Spec: porchapi.PackageRevisionSpec{
1212+
PackageName: "test-fn-pod-bucket",
1213+
Revision: "v1",
1214+
RepositoryName: "git-fn-pod-failure",
1215+
Tasks: []porchapi.Task{
1216+
{
1217+
Type: "clone",
1218+
Clone: &porchapi.PackageCloneTaskSpec{
1219+
Upstream: porchapi.UpstreamPackage{
1220+
Type: "git",
1221+
Git: &porchapi.GitPackage{
1222+
Repo: "https://github.com/GoogleCloudPlatform/blueprints.git",
1223+
Ref: "bucket-blueprint-v0.4.3",
1224+
Directory: "catalog/bucket",
1225+
},
1226+
},
1227+
},
1228+
},
1229+
{
1230+
Type: "eval",
1231+
Eval: &porchapi.FunctionEvalTaskSpec{
1232+
// This function is expect to fail due to not knowing schema for some CRDs.
1233+
Image: "gcr.io/kpt-fn/kubeval:v0.2.0",
1234+
},
1235+
},
1236+
},
1237+
},
1238+
}
1239+
err := t.client.Create(ctx, pr)
1240+
expectedErrMsg := "Validating arbitrary CRDs is not supported"
1241+
if err == nil || !strings.Contains(err.Error(), expectedErrMsg) {
1242+
t.Fatalf("expected the error to contain %q, but got %v", expectedErrMsg, err)
1243+
}
1244+
}
1245+
11911246
func (t *PorchSuite) TestRepositoryError(ctx context.Context) {
11921247
const (
11931248
repositoryName = "repo-with-error"

0 commit comments

Comments
 (0)