Skip to content

Commit d1dbfc8

Browse files
bundle validate: new alpha flag --output=<text|json-alpha1> to format results (#3011)
1 parent aa944d6 commit d1dbfc8

File tree

5 files changed

+541
-22
lines changed

5 files changed

+541
-22
lines changed
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
# entries is a list of entries to include in
2+
# release notes and/or the migration guide
3+
entries:
4+
- description: >
5+
Add new hidden alpha flag `--output` to print the result of `operator-sdk bundle validate` in JSON format to stdout. Logs are printed to stderr.
6+
7+
# kind is one of:
8+
# - addition
9+
# - change
10+
# - deprecation
11+
# - removal
12+
# - bugfix
13+
kind: "addition"
14+
15+
# Is this a breaking change?
16+
breaking: false
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
// Copyright 2020 The Operator-SDK Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package internal
16+
17+
import (
18+
"io"
19+
20+
log "github.com/sirupsen/logrus"
21+
)
22+
23+
// NewLoggerTo returns a logger that writes logs to w.
24+
func NewLoggerTo(w io.Writer) *log.Logger {
25+
logger := log.New()
26+
logger.SetOutput(w)
27+
return logger
28+
}
Lines changed: 166 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,166 @@
1+
// Copyright 2020 The Operator-SDK Authors
2+
//
3+
// Licensed under the Apache License, Version 2.0 (the "License");
4+
// you may not use this file except in compliance with the License.
5+
// You may obtain a copy of the License at
6+
//
7+
// http://www.apache.org/licenses/LICENSE-2.0
8+
//
9+
// Unless required by applicable law or agreed to in writing, software
10+
// distributed under the License is distributed on an "AS IS" BASIS,
11+
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
// See the License for the specific language governing permissions and
13+
// limitations under the License.
14+
15+
package internal
16+
17+
import (
18+
"encoding/json"
19+
"errors"
20+
"fmt"
21+
"os"
22+
23+
registrybundle "github.com/operator-framework/operator-registry/pkg/lib/bundle"
24+
"github.com/sirupsen/logrus"
25+
log "github.com/sirupsen/logrus"
26+
)
27+
28+
const (
29+
JSONAlpha1 = "json-alpha1"
30+
Text = "text"
31+
)
32+
33+
// Result represents the final result
34+
type Result struct {
35+
Passed bool `json:"passed"`
36+
Outputs []output `json:"outputs"`
37+
}
38+
39+
// output represents the logs which are used to return the final result in the JSON format
40+
type output struct {
41+
Type string `json:"type"`
42+
Message string `json:"message"`
43+
}
44+
45+
// NewResult return a new result object which starts with passed == true since has no errors
46+
func NewResult() Result {
47+
return Result{Passed: true}
48+
}
49+
50+
// AddInfo will add a log to the result with the Info Level
51+
func (o *Result) AddInfo(msg string) {
52+
o.Outputs = append(o.Outputs, output{
53+
Type: logrus.InfoLevel.String(),
54+
Message: msg,
55+
})
56+
}
57+
58+
// AddError will add a log to the result with the Error Level
59+
func (o *Result) AddError(err error) {
60+
verr := registrybundle.ValidationError{}
61+
if errors.As(err, &verr) {
62+
for _, valErr := range verr.Errors {
63+
o.Outputs = append(o.Outputs, output{
64+
Type: logrus.ErrorLevel.String(),
65+
Message: valErr.Error(),
66+
})
67+
}
68+
} else {
69+
o.Outputs = append(o.Outputs, output{
70+
Type: logrus.ErrorLevel.String(),
71+
Message: err.Error(),
72+
})
73+
}
74+
o.Passed = false
75+
}
76+
77+
// AddWarn will add a log to the result with the Warn Level
78+
func (o *Result) AddWarn(err error) {
79+
o.Outputs = append(o.Outputs, output{
80+
Type: logrus.WarnLevel.String(),
81+
Message: err.Error(),
82+
})
83+
}
84+
85+
// printText will print the output in human readable format
86+
func (o *Result) printText(logger *logrus.Entry) error {
87+
for _, obj := range o.Outputs {
88+
lvl, err := logrus.ParseLevel(obj.Type)
89+
if err != nil {
90+
return err
91+
}
92+
switch lvl {
93+
case logrus.InfoLevel:
94+
logger.Info(obj.Message)
95+
case logrus.WarnLevel:
96+
logger.Warn(obj.Message)
97+
case logrus.ErrorLevel:
98+
logger.Error(obj.Message)
99+
default:
100+
return fmt.Errorf("unknown output level %q", obj.Type)
101+
}
102+
}
103+
104+
return nil
105+
}
106+
107+
// printJSON will print the output in JSON format
108+
func (o *Result) printJSON() error {
109+
prettyJSON, err := json.MarshalIndent(o, "", " ")
110+
if err != nil {
111+
return fmt.Errorf("error marshaling JSON output: %v", err)
112+
}
113+
fmt.Printf("%s\n", string(prettyJSON))
114+
return nil
115+
}
116+
117+
// prepare should be used when writing an Result to a non-log writer.
118+
// it will ensure that the passed boolean will properly set in the case of the setters were not properly used
119+
func (o *Result) prepare() error {
120+
o.Passed = true
121+
for i, obj := range o.Outputs {
122+
lvl, err := logrus.ParseLevel(obj.Type)
123+
if err != nil {
124+
return err
125+
}
126+
if o.Passed && lvl == logrus.ErrorLevel {
127+
o.Passed = false
128+
}
129+
lvlBytes, _ := lvl.MarshalText()
130+
o.Outputs[i].Type = string(lvlBytes)
131+
}
132+
return nil
133+
}
134+
135+
// PrintWithFormat prints output to w in format, and exits if some object in output
136+
// is not in a passing state.
137+
func (o *Result) PrintWithFormat(format string) (err error) {
138+
// the prepare will ensure the result data if the setters were not used
139+
if err = o.prepare(); err != nil {
140+
return fmt.Errorf("error to prepare output: %v", err)
141+
}
142+
143+
printf := o.getPrintFuncFormat(format)
144+
if err = printf(*o); err == nil && !o.Passed {
145+
os.Exit(1) // Exit with error when any Error type was added
146+
}
147+
return err
148+
}
149+
150+
// getPrintFuncFormat returns a function that writes an Result to w in a given
151+
// format, defaulting to "text" if format is not recognized.
152+
func (o *Result) getPrintFuncFormat(format string) func(Result) error {
153+
// PrintWithFormat output in desired format.
154+
switch format {
155+
case JSONAlpha1:
156+
return func(o Result) error {
157+
return o.printJSON()
158+
}
159+
}
160+
161+
// Address all to the Stdout when the type is not JSON
162+
logger := log.NewEntry(NewLoggerTo(os.Stdout))
163+
return func(o Result) error {
164+
return o.printText(logger)
165+
}
166+
}

0 commit comments

Comments
 (0)