Skip to content

Commit 0da1e20

Browse files
authored
cli: rpkg push surface renderstatus (#3645)
1 parent 971cb4c commit 0da1e20

File tree

13 files changed

+153
-88
lines changed

13 files changed

+153
-88
lines changed

commands/alpha/rpkg/push/command.go

Lines changed: 78 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@ import (
2323
"os"
2424
"path"
2525
"path/filepath"
26+
"strings"
2627

2728
"github.com/GoogleContainerTools/kpt/internal/docs/generated/rpkgdocs"
2829
"github.com/GoogleContainerTools/kpt/internal/errors"
30+
"github.com/GoogleContainerTools/kpt/internal/fnruntime"
2931
"github.com/GoogleContainerTools/kpt/internal/printer"
3032
"github.com/GoogleContainerTools/kpt/internal/util/porch"
3133
porchapi "github.com/GoogleContainerTools/kpt/porch/api/porch/v1alpha1"
@@ -117,7 +119,7 @@ func (r *runner) runE(cmd *cobra.Command, args []string) error {
117119
return errors.E(op, err)
118120
}
119121

120-
if err := r.client.Update(r.ctx, &porchapi.PackageRevisionResources{
122+
pkgResources := porchapi.PackageRevisionResources{
121123
TypeMeta: metav1.TypeMeta{
122124
Kind: "PackageRevisionResources",
123125
APIVersion: porchapi.SchemeGroupVersion.Identifier(),
@@ -129,12 +131,86 @@ func (r *runner) runE(cmd *cobra.Command, args []string) error {
129131
Spec: porchapi.PackageRevisionResourcesSpec{
130132
Resources: resources,
131133
},
132-
}); err != nil {
134+
}
135+
if err := r.client.Update(r.ctx, &pkgResources); err != nil {
133136
return errors.E(op, err)
134137
}
138+
rs := pkgResources.Status.RenderStatus
139+
if rs.Err != "" {
140+
r.printer.Printf("Package is updated, but failed to render the package.\n")
141+
r.printer.Printf("Error: %s\n", rs.Err)
142+
}
143+
if len(rs.Result.Items) > 0 {
144+
for _, result := range rs.Result.Items {
145+
r.printer.Printf("[RUNNING] %q \n", result.Image)
146+
printOpt := printer.NewOpt()
147+
if result.ExitCode != 0 {
148+
r.printer.OptPrintf(printOpt, "[FAIL] %q\n", result.Image)
149+
} else {
150+
r.printer.OptPrintf(printOpt, "[PASS] %q\n", result.Image)
151+
}
152+
r.printFnResult(result, printOpt)
153+
}
154+
}
135155
return nil
136156
}
137157

158+
// printFnResult prints given function result in a user friendly
159+
// format on kpt CLI.
160+
func (r *runner) printFnResult(fnResult *porchapi.Result, opt *printer.Options) {
161+
if len(fnResult.Results) > 0 {
162+
// function returned structured results
163+
var lines []string
164+
for _, item := range fnResult.Results {
165+
lines = append(lines, str(item))
166+
}
167+
ri := &fnruntime.MultiLineFormatter{
168+
Title: "Results",
169+
Lines: lines,
170+
TruncateOutput: printer.TruncateOutput,
171+
}
172+
r.printer.OptPrintf(opt, "%s", ri.String())
173+
}
174+
}
175+
176+
// String provides a human-readable message for the result item
177+
func str(i porchapi.ResultItem) string {
178+
identifier := i.ResourceRef
179+
var idStringList []string
180+
if identifier != nil {
181+
if identifier.APIVersion != "" {
182+
idStringList = append(idStringList, identifier.APIVersion)
183+
}
184+
if identifier.Kind != "" {
185+
idStringList = append(idStringList, identifier.Kind)
186+
}
187+
if identifier.Namespace != "" {
188+
idStringList = append(idStringList, identifier.Namespace)
189+
}
190+
if identifier.Name != "" {
191+
idStringList = append(idStringList, identifier.Name)
192+
}
193+
}
194+
formatString := "[%s]"
195+
severity := i.Severity
196+
// We default Severity to Info when converting a result to a message.
197+
if i.Severity == "" {
198+
severity = "info"
199+
}
200+
list := []interface{}{severity}
201+
if len(idStringList) > 0 {
202+
formatString += " %s"
203+
list = append(list, strings.Join(idStringList, "/"))
204+
}
205+
if i.Field != nil {
206+
formatString += " %s"
207+
list = append(list, i.Field.Path)
208+
}
209+
formatString += ": %s"
210+
list = append(list, i.Message)
211+
return fmt.Sprintf(formatString, list...)
212+
}
213+
138214
func readFromDir(dir string) (map[string]string, error) {
139215
resources := map[string]string{}
140216
if err := filepath.Walk(dir, func(path string, info fs.FileInfo, err error) error {

go.mod

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ module github.com/GoogleContainerTools/kpt
33
go 1.18
44

55
require (
6-
github.com/GoogleContainerTools/kpt/porch/api v0.0.0-20221027171429-622c0141f3d7
6+
github.com/GoogleContainerTools/kpt/porch/api v0.0.0-20221028161857-aa271f292cc0
77
github.com/bytecodealliance/wasmtime-go v0.39.0
88
github.com/cpuguy83/go-md2man/v2 v2.0.2
99
github.com/go-errors/errors v1.4.2

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,8 @@ github.com/BurntSushi/xgb v0.0.0-20160522181843-27f122750802/go.mod h1:IVnqGOEym
8787
github.com/Djarvur/go-err113 v0.0.0-20210108212216-aea10b59be24/go.mod h1:4UJr5HIiMZrwgkSPdsjy2uOQExX/WEILpIrO9UPGuXs=
8888
github.com/GoogleContainerTools/kpt/porch/api v0.0.0-20221027171429-622c0141f3d7 h1:WKfyM9TDvHaGJ0n98rO/U7qkPL8wAWPvLKQYNT7TNvY=
8989
github.com/GoogleContainerTools/kpt/porch/api v0.0.0-20221027171429-622c0141f3d7/go.mod h1:ASrhnLAL4ahTuiUJyepqcpVRXIoRMJyDs8/eSxwhgZM=
90+
github.com/GoogleContainerTools/kpt/porch/api v0.0.0-20221028161857-aa271f292cc0 h1:Nay/s1StXHUKyIxerpXb8o0hZUkRjrbteLO6ardI26Y=
91+
github.com/GoogleContainerTools/kpt/porch/api v0.0.0-20221028161857-aa271f292cc0/go.mod h1:ASrhnLAL4ahTuiUJyepqcpVRXIoRMJyDs8/eSxwhgZM=
9092
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd h1:sjQovDkwrZp8u+gxLtPgKGjk5hCxuy2hrRejBTA9xFU=
9193
github.com/MakeNowJust/heredoc v0.0.0-20170808103936-bb23615498cd/go.mod h1:64YHyfSL2R96J44Nlwm39UHepQbyR5q10x7iYa1ks2E=
9294
github.com/Masterminds/goutils v1.1.0/go.mod h1:8cTjp+g8YejhMuvIA5y2vz3BpJxksy863GQaJW2MFNU=

internal/fnruntime/fnerrors.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ type ExecError struct {
4545
func (fe *ExecError) String() string {
4646
var b strings.Builder
4747

48-
errLines := &multiLineFormatter{
48+
errLines := &MultiLineFormatter{
4949
Title: "Stderr",
5050
Lines: strings.Split(fe.Stderr, "\n"),
5151
UseQuote: true,

internal/fnruntime/runner.go

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -295,10 +295,15 @@ func (fr *FunctionRunner) do(input []*yaml.RNode) (output []*yaml.RNode, err err
295295
}
296296
if err != nil {
297297
var execErr *ExecError
298+
// set exitCode to non-zero by default in case of an error.
299+
// It will be overridden with appropriate exitCode if the function runtime returns execError.
300+
// builtinruntime and podEvaluator function runtime do not return execError so having
301+
// a default is important.
302+
fnResult.ExitCode = 1
303+
fr.fnResults.ExitCode = 1
298304
if goerrors.As(err, &execErr) {
299305
fnResult.ExitCode = execErr.ExitCode
300306
fnResult.Stderr = execErr.Stderr
301-
fr.fnResults.ExitCode = 1
302307
}
303308
// accumulate the results
304309
fr.fnResults.Items = append(fr.fnResults.Items, *fnResult)
@@ -427,7 +432,7 @@ func printFnResult(ctx context.Context, fnResult *fnresult.Result, opt *printer.
427432
for _, item := range fnResult.Results {
428433
lines = append(lines, item.String())
429434
}
430-
ri := &multiLineFormatter{
435+
ri := &MultiLineFormatter{
431436
Title: "Results",
432437
Lines: lines,
433438
TruncateOutput: printer.TruncateOutput,
@@ -448,7 +453,7 @@ func printFnExecErr(ctx context.Context, fnErr *ExecError) {
448453
func printFnStderr(ctx context.Context, stdErr string) {
449454
pr := printer.FromContextOrDie(ctx)
450455
if len(stdErr) > 0 {
451-
errLines := &multiLineFormatter{
456+
errLines := &MultiLineFormatter{
452457
Title: "Stderr",
453458
Lines: strings.Split(stdErr, "\n"),
454459
UseQuote: true,
@@ -492,9 +497,9 @@ func enforcePathInvariants(nodes []*yaml.RNode) error {
492497
return nil
493498
}
494499

495-
// multiLineFormatter knows how to format multiple lines in pretty format
500+
// MultiLineFormatter knows how to format multiple lines in pretty format
496501
// that can be displayed to an end user.
497-
type multiLineFormatter struct {
502+
type MultiLineFormatter struct {
498503
// Title under which lines need to be printed
499504
Title string
500505

@@ -512,7 +517,7 @@ type multiLineFormatter struct {
512517
}
513518

514519
// String returns multiline string.
515-
func (ri *multiLineFormatter) String() string {
520+
func (ri *MultiLineFormatter) String() string {
516521
if ri.MaxLines == 0 {
517522
ri.MaxLines = FnExecErrorTruncateLines
518523
}

internal/fnruntime/runner_test.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -101,13 +101,13 @@ data: {foo: bar}
101101

102102
func TestMultilineFormatter(t *testing.T) {
103103
type testcase struct {
104-
ml *multiLineFormatter
104+
ml *MultiLineFormatter
105105
expected string
106106
}
107107

108108
testcases := map[string]testcase{
109109
"multiline should format lines and truncate": {
110-
ml: &multiLineFormatter{
110+
ml: &MultiLineFormatter{
111111
Title: "Results",
112112
Lines: []string{
113113
"line-1",
@@ -127,7 +127,7 @@ func TestMultilineFormatter(t *testing.T) {
127127
`,
128128
},
129129
"multiline should format without truncate": {
130-
ml: &multiLineFormatter{
130+
ml: &MultiLineFormatter{
131131
Title: "Results",
132132
Lines: []string{
133133
"line-1",

porch/api/generated/clientset/versioned/fake/register.go

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

porch/api/generated/clientset/versioned/scheme/register.go

Lines changed: 7 additions & 7 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

porch/api/porch/types_packagerevisions.go

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -347,27 +347,6 @@ const (
347347
ConditionUnknown ConditionStatus = "Unknown"
348348
)
349349

350-
// Result contains the structured result from an individual function
351-
type Result struct {
352-
// Image is the full name of the image that generates this result
353-
// Image and Exec are mutually exclusive
354-
Image string `json:"image,omitempty"`
355-
// ExecPath is the the absolute os-specific path to the executable file
356-
// If user provides an executable file with commands, ExecPath should
357-
// contain the entire input string.
358-
ExecPath string `json:"exec,omitempty"`
359-
// TODO(droot): This is required for making structured results subpackage aware.
360-
// Enable this once test harness supports filepath based assertions.
361-
// Pkg is OS specific Absolute path to the package.
362-
// Pkg string `yaml:"pkg,omitempty"`
363-
// Stderr is the content in function stderr
364-
Stderr string `json:"stderr,omitempty"`
365-
// ExitCode is the exit code from running the function
366-
ExitCode int `json:"exitCode"`
367-
// Results is the list of results for the function
368-
Results []ResultItem `json:"results,omitempty"`
369-
}
370-
371350
const (
372351
// Deprecated: prefer ResultListGVK
373352
ResultListKind = "FunctionResultList"
@@ -380,7 +359,6 @@ const (
380359
)
381360

382361
// ResultList contains aggregated results from multiple functions
383-
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
384362
type ResultList struct {
385363
metav1.TypeMeta `json:",inline"`
386364
metav1.ObjectMeta `json:"metadata,omitempty"`
@@ -390,6 +368,27 @@ type ResultList struct {
390368
Items []*Result `json:"items,omitempty"`
391369
}
392370

371+
// Result contains the structured result from an individual function
372+
type Result struct {
373+
// Image is the full name of the image that generates this result
374+
// Image and Exec are mutually exclusive
375+
Image string `json:"image,omitempty"`
376+
// ExecPath is the the absolute os-specific path to the executable file
377+
// If user provides an executable file with commands, ExecPath should
378+
// contain the entire input string.
379+
ExecPath string `json:"exec,omitempty"`
380+
// TODO(droot): This is required for making structured results subpackage aware.
381+
// Enable this once test harness supports filepath based assertions.
382+
// Pkg is OS specific Absolute path to the package.
383+
// Pkg string `yaml:"pkg,omitempty"`
384+
// Stderr is the content in function stderr
385+
Stderr string `json:"stderr,omitempty"`
386+
// ExitCode is the exit code from running the function
387+
ExitCode int `json:"exitCode"`
388+
// Results is the list of results for the function
389+
Results []ResultItem `json:"results,omitempty"`
390+
}
391+
393392
// ResultItem defines a validation result
394393
type ResultItem struct {
395394
// Message is a human readable message. This field is required.

porch/api/porch/v1alpha1/types_packagerevisions.go

Lines changed: 21 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -358,27 +358,6 @@ const (
358358
ConditionUnknown ConditionStatus = "Unknown"
359359
)
360360

361-
// Result contains the structured result from an individual function
362-
type Result struct {
363-
// Image is the full name of the image that generates this result
364-
// Image and Exec are mutually exclusive
365-
Image string `json:"image,omitempty"`
366-
// ExecPath is the the absolute os-specific path to the executable file
367-
// If user provides an executable file with commands, ExecPath should
368-
// contain the entire input string.
369-
ExecPath string `json:"exec,omitempty"`
370-
// TODO(droot): This is required for making structured results subpackage aware.
371-
// Enable this once test harness supports filepath based assertions.
372-
// Pkg is OS specific Absolute path to the package.
373-
// Pkg string `yaml:"pkg,omitempty"`
374-
// Stderr is the content in function stderr
375-
Stderr string `json:"stderr,omitempty"`
376-
// ExitCode is the exit code from running the function
377-
ExitCode int `json:"exitCode"`
378-
// Results is the list of results for the function
379-
Results []ResultItem `json:"results,omitempty"`
380-
}
381-
382361
const (
383362
// Deprecated: prefer ResultListGVK
384363
ResultListKind = "FunctionResultList"
@@ -391,7 +370,6 @@ const (
391370
)
392371

393372
// ResultList contains aggregated results from multiple functions
394-
// +k8s:deepcopy-gen:interfaces=k8s.io/apimachinery/pkg/runtime.Object
395373
type ResultList struct {
396374
metav1.TypeMeta `json:",inline"`
397375
metav1.ObjectMeta `json:"metadata,omitempty"`
@@ -401,6 +379,27 @@ type ResultList struct {
401379
Items []*Result `json:"items,omitempty"`
402380
}
403381

382+
// Result contains the structured result from an individual function
383+
type Result struct {
384+
// Image is the full name of the image that generates this result
385+
// Image and Exec are mutually exclusive
386+
Image string `json:"image,omitempty"`
387+
// ExecPath is the the absolute os-specific path to the executable file
388+
// If user provides an executable file with commands, ExecPath should
389+
// contain the entire input string.
390+
ExecPath string `json:"exec,omitempty"`
391+
// TODO(droot): This is required for making structured results subpackage aware.
392+
// Enable this once test harness supports filepath based assertions.
393+
// Pkg is OS specific Absolute path to the package.
394+
// Pkg string `yaml:"pkg,omitempty"`
395+
// Stderr is the content in function stderr
396+
Stderr string `json:"stderr,omitempty"`
397+
// ExitCode is the exit code from running the function
398+
ExitCode int `json:"exitCode"`
399+
// Results is the list of results for the function
400+
Results []ResultItem `json:"results,omitempty"`
401+
}
402+
404403
// ResultItem defines a validation result
405404
type ResultItem struct {
406405
// Message is a human readable message. This field is required.

0 commit comments

Comments
 (0)