Skip to content

Commit 103a074

Browse files
authored
Schema and strict validation radixconfig (#95)
* validate against crd and strictly * cleanup, use jsonschema directly * update go.mod * updated operator * update golangci-lint, goreleaser, remove shorthand flag, add comment about local file and file://
1 parent 466e58f commit 103a074

File tree

8 files changed

+266
-136
lines changed

8 files changed

+266
-136
lines changed

.github/workflows/ci.yml

+8-12
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,15 @@ jobs:
2424
uses: docker/setup-buildx-action@v3
2525

2626
- name: Set up Go
27-
uses: actions/setup-go@v4
27+
uses: actions/setup-go@v5
2828
with:
29-
go-version: '1.21'
29+
go-version-file: 'go.mod'
3030

3131
- name: Run GoReleaser
3232
uses: goreleaser/goreleaser-action@v5
3333
with:
3434
distribution: goreleaser
35-
version: v1.21.2
35+
version: v1.26.2
3636
args: build --clean --single-target --snapshot
3737
env:
3838
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
@@ -44,14 +44,10 @@ jobs:
4444
- uses: actions/checkout@v4
4545
with:
4646
fetch-depth: 2
47-
- uses: actions/setup-go@v4
47+
- uses: actions/setup-go@v5
4848
with:
49-
go-version: '1.21'
50-
- name: Install dependencies
51-
run: go mod download
52-
- name: Install GolangCI Lint
53-
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2
54-
49+
go-version-file: 'go.mod'
5550
- name: golangci-lint
56-
run: golangci-lint run --timeout=30m --max-same-issues=0 --out-format=github-actions
57-
51+
uses: golangci/golangci-lint-action@v4
52+
with:
53+
version: v1.58.2

.golangci.yaml

+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
run:
2+
timeout: 30m
3+
4+
linters:
5+
enable:
6+
- errcheck
7+
- gosimple
8+
- govet
9+
- ineffassign
10+
- staticcheck
11+
- unused
12+
- zerologlint
13+
14+
issues:
15+
max-same-issues: 0

Dockerfile

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
FROM golang:1.21-alpine3.18 as builder
1+
FROM golang:1.22-alpine3.19 as builder
22

33
ENV GO111MODULE=on
44

Makefile

+5-1
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,15 @@ lint: bootstrap
1818

1919
HAS_SWAGGER := $(shell command -v swagger;)
2020
HAS_GOLANGCI_LINT := $(shell command -v golangci-lint;)
21+
HAS_GORELEASER := $(shell command -v goreleaser;)
2122

2223
bootstrap:
2324
ifndef HAS_SWAGGER
2425
go install github.com/go-swagger/go-swagger/cmd/[email protected]
2526
endif
2627
ifndef HAS_GOLANGCI_LINT
27-
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2
28+
go install github.com/golangci/golangci-lint/cmd/[email protected]
29+
endif
30+
ifndef HAS_GORELEASER
31+
go install github.com/goreleaser/[email protected]
2832
endif

cmd/validateRadixConfig.go

+95-13
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,9 @@ import (
2222
"github.com/equinor/radix-cli/pkg/flagnames"
2323
radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1"
2424
"github.com/equinor/radix-operator/pkg/apis/radixvalidators"
25-
"github.com/equinor/radix-operator/pkg/apis/utils"
2625
"github.com/pkg/errors"
26+
"github.com/santhosh-tekuri/jsonschema/v5"
27+
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
2728
"github.com/spf13/cobra"
2829
"sigs.k8s.io/yaml"
2930
)
@@ -33,46 +34,126 @@ var validateRadixConfigCmd = &cobra.Command{
3334
Use: "radix-config",
3435
Short: "Validate radixconfig.yaml",
3536
Long: `Check radixconfig.yaml for structural and logical errors`,
36-
RunE: func(cmd *cobra.Command, args []string) error {
37-
38-
cmd.SilenceUsage = true
39-
37+
Run: func(cmd *cobra.Command, args []string) {
4038
radixconfig, err := cmd.Flags().GetString(flagnames.ConfigFile)
4139
if err != nil {
42-
return err
40+
fmt.Fprintln(os.Stderr, err.Error())
41+
os.Exit(1)
4342
}
4443

4544
printfile, err := cmd.Flags().GetBool(flagnames.Print)
4645
if err != nil {
47-
return err
46+
fmt.Fprintln(os.Stderr, err.Error())
47+
os.Exit(1)
48+
}
49+
50+
schema, err := cmd.Flags().GetString(flagnames.Schema)
51+
if err != nil {
52+
fmt.Fprintln(os.Stderr, err.Error())
53+
os.Exit(1)
4854
}
4955

56+
fmt.Fprintf(os.Stderr, "Validating %s\n", radixconfig)
5057
if _, err := os.Stat(radixconfig); errors.Is(err, os.ErrNotExist) {
51-
return fmt.Errorf("RadixConfig file not found: %s", radixconfig)
58+
fmt.Fprintln(os.Stderr, "RadixConfig file not found")
59+
os.Exit(1)
5260
}
5361

54-
ra, err := utils.GetRadixApplicationFromFile(radixconfig)
62+
raw, err := os.ReadFile(radixconfig)
5563
if err != nil {
56-
return fmt.Errorf("RadixConfig is invalid: %w", err)
64+
fmt.Fprintf(os.Stderr, "failed to read file: %v\n", err)
65+
os.Exit(1)
66+
}
67+
68+
ra, err := unmarshalRadixApplication(raw)
69+
if err != nil {
70+
fmt.Fprintln(os.Stderr, err.Error())
71+
os.Exit(1)
5772
}
5873

5974
if printfile {
6075
err = printRA(ra)
6176
if err != nil {
62-
return err
77+
fmt.Fprintln(os.Stderr, err.Error())
78+
os.Exit(1)
6379
}
6480
}
6581

82+
validationErrors, err := validateSchema(raw, schema)
83+
if err != nil {
84+
fmt.Fprintln(os.Stderr, err.Error())
85+
os.Exit(1)
86+
}
87+
6688
err = radixvalidators.IsRadixApplicationValid(ra)
6789
if err != nil {
68-
return fmt.Errorf("RadixConfig is invalid:\n%w", err)
90+
validationErrors = append(validationErrors, err)
91+
}
92+
93+
err = strictUnmarshalValidation(raw)
94+
if err != nil {
95+
validationErrors = append(validationErrors, err)
96+
}
97+
98+
if len(validationErrors) > 0 {
99+
for _, err := range validationErrors {
100+
fmt.Fprintf(os.Stderr, " - %s\n", err)
101+
}
102+
103+
fmt.Fprintln(os.Stderr, "RadixConfig is invalid")
104+
os.Exit(2)
69105
}
70106

71107
fmt.Fprintln(os.Stderr, "RadixConfig is valid")
72-
return nil
73108
},
74109
}
75110

111+
func validateSchema(raw []byte, schema string) (validationErrors []error, err error) {
112+
s, err := jsonschema.Compile(schema)
113+
if err != nil {
114+
return nil, fmt.Errorf("failed compiling schema %s: %s", schema, err)
115+
}
116+
117+
var obj interface{}
118+
err = yaml.Unmarshal(raw, &obj)
119+
if err != nil {
120+
return nil, fmt.Errorf("failed to parse file: %v", err)
121+
}
122+
123+
err = s.Validate(obj)
124+
var verr *jsonschema.ValidationError
125+
if errors.As(err, &verr) {
126+
for _, err := range verr.Causes {
127+
validationErrors = append(validationErrors, fmt.Errorf("%s: %s", err.InstanceLocation, err.Message))
128+
}
129+
} else {
130+
return nil, err
131+
}
132+
133+
return validationErrors, nil
134+
}
135+
136+
func strictUnmarshalValidation(raw []byte) error {
137+
radixApp := &radixv1.RadixApplication{}
138+
139+
err := yaml.UnmarshalStrict(raw, radixApp)
140+
if err != nil {
141+
return fmt.Errorf("strict test failed: %v", err)
142+
}
143+
144+
return nil
145+
}
146+
func unmarshalRadixApplication(raw []byte) (*radixv1.RadixApplication, error) {
147+
radixApp := &radixv1.RadixApplication{}
148+
149+
err := yaml.Unmarshal(raw, radixApp)
150+
if err != nil {
151+
return nil, fmt.Errorf("strict test failed: %v", err)
152+
}
153+
154+
return radixApp, nil
155+
}
156+
76157
func printRA(ra *radixv1.RadixApplication) error {
77158
b, err := yaml.Marshal(ra)
78159
if err != nil {
@@ -87,6 +168,7 @@ func init() {
87168
validateCmd.AddCommand(validateRadixConfigCmd)
88169
validateRadixConfigCmd.Flags().StringP(flagnames.ConfigFile, "f", "radixconfig.yaml", "Name of the radixconfig file. Defaults to radixconfig.yaml in current directory")
89170
validateRadixConfigCmd.Flags().BoolP(flagnames.Print, "p", false, "Print parsed config file")
171+
validateRadixConfigCmd.Flags().String(flagnames.Schema, "https://raw.githubusercontent.com/equinor/radix-operator/release/json-schema/radixapplication.json", "Validate against schema. http://, file:// or path is supported")
90172

91173
// Allow but hide token-env flag so radix-github-actions won't interfere
92174
validateRadixConfigCmd.Flags().Bool(flagnames.TokenEnvironment, false, fmt.Sprintf("Take the token from environment variable %s", client.TokenEnvironmentName))

go.mod

+40-31
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,23 @@
11
module github.com/equinor/radix-cli
22

3-
go 1.21
3+
go 1.22.0
44

5-
toolchain go1.21.0
5+
toolchain go1.22.1
66

77
require (
88
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1
9-
github.com/equinor/radix-operator v1.51.2
10-
github.com/fatih/color v1.15.0
9+
github.com/equinor/radix-operator v1.55.2
10+
github.com/fatih/color v1.16.0
1111
github.com/go-openapi/errors v0.20.4
1212
github.com/go-openapi/runtime v0.26.2
1313
github.com/go-openapi/strfmt v0.21.8
1414
github.com/go-openapi/swag v0.22.7
1515
github.com/go-openapi/validate v0.22.3
1616
github.com/pkg/errors v0.9.1
17+
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
1718
github.com/sirupsen/logrus v1.9.3
1819
github.com/spf13/cobra v1.8.0
19-
k8s.io/utils v0.0.0-20240102154912-e7106e64919e
20+
k8s.io/utils v0.0.0-20240310230437-4693a0247e57
2021
sigs.k8s.io/yaml v1.4.0
2122
)
2223

@@ -27,8 +28,12 @@ require (
2728
github.com/cert-manager/cert-manager v1.14.2 // indirect
2829
github.com/cespare/xxhash/v2 v2.2.0 // indirect
2930
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
30-
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
31+
github.com/emicklei/go-restful/v3 v3.11.2 // indirect
3132
github.com/equinor/radix-common v1.7.1 // indirect
33+
github.com/evanphx/json-patch v5.8.1+incompatible // indirect
34+
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
35+
github.com/expr-lang/expr v1.15.8 // indirect
36+
github.com/fsnotify/fsnotify v1.7.0 // indirect
3237
github.com/go-logr/logr v1.4.1 // indirect
3338
github.com/go-logr/stdr v1.2.2 // indirect
3439
github.com/go-openapi/analysis v0.21.4 // indirect
@@ -37,60 +42,64 @@ require (
3742
github.com/go-openapi/loads v0.21.2 // indirect
3843
github.com/go-openapi/spec v0.20.11 // indirect
3944
github.com/gogo/protobuf v1.3.2 // indirect
40-
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
45+
github.com/golang-jwt/jwt/v5 v5.2.0 // indirect
46+
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
4147
github.com/golang/mock v1.6.0 // indirect
42-
github.com/golang/protobuf v1.5.3 // indirect
48+
github.com/golang/protobuf v1.5.4 // indirect
4349
github.com/google/gnostic-models v0.6.8 // indirect
4450
github.com/google/go-cmp v0.6.0 // indirect
4551
github.com/google/gofuzz v1.2.0 // indirect
46-
github.com/google/uuid v1.5.0 // indirect
52+
github.com/google/uuid v1.6.0 // indirect
4753
github.com/imdario/mergo v0.3.16 // indirect
4854
github.com/inconshreveable/mousetrap v1.1.0 // indirect
4955
github.com/josharian/intern v1.0.0 // indirect
5056
github.com/json-iterator/go v1.1.12 // indirect
57+
github.com/kedacore/keda/v2 v2.13.1 // indirect
5158
github.com/kylelemons/godebug v1.1.0 // indirect
5259
github.com/mailru/easyjson v0.7.7 // indirect
5360
github.com/mattn/go-colorable v0.1.13 // indirect
54-
github.com/mattn/go-isatty v0.0.19 // indirect
55-
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
61+
github.com/mattn/go-isatty v0.0.20 // indirect
5662
github.com/mitchellh/mapstructure v1.5.0 // indirect
5763
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
5864
github.com/modern-go/reflect2 v1.0.2 // indirect
5965
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
6066
github.com/oklog/ulid v1.3.1 // indirect
6167
github.com/opentracing/opentracing-go v1.2.0 // indirect
62-
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
68+
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
6369
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.70.0 // indirect
6470
github.com/prometheus-operator/prometheus-operator/pkg/client v0.70.0 // indirect
65-
github.com/prometheus/client_golang v1.18.0 // indirect
66-
github.com/prometheus/client_model v0.5.0 // indirect
67-
github.com/prometheus/common v0.45.0 // indirect
71+
github.com/prometheus/client_golang v1.19.0 // indirect
72+
github.com/prometheus/client_model v0.6.1 // indirect
73+
github.com/prometheus/common v0.53.0 // indirect
6874
github.com/prometheus/procfs v0.12.0 // indirect
75+
github.com/robfig/cron/v3 v3.0.1 // indirect
6976
github.com/rs/zerolog v1.32.0 // indirect
7077
github.com/spf13/pflag v1.0.5 // indirect
7178
go.mongodb.org/mongo-driver v1.13.1 // indirect
72-
go.opentelemetry.io/otel v1.21.0 // indirect
73-
go.opentelemetry.io/otel/metric v1.21.0 // indirect
74-
go.opentelemetry.io/otel/trace v1.21.0 // indirect
75-
golang.org/x/crypto v0.17.0 // indirect
76-
golang.org/x/net v0.19.0 // indirect
77-
golang.org/x/oauth2 v0.15.0 // indirect
78-
golang.org/x/sys v0.15.0 // indirect
79-
golang.org/x/term v0.15.0 // indirect
79+
go.opentelemetry.io/otel v1.22.0 // indirect
80+
go.opentelemetry.io/otel/metric v1.22.0 // indirect
81+
go.opentelemetry.io/otel/trace v1.22.0 // indirect
82+
golang.org/x/crypto v0.22.0 // indirect
83+
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
84+
golang.org/x/net v0.24.0 // indirect
85+
golang.org/x/oauth2 v0.19.0 // indirect
86+
golang.org/x/sys v0.19.0 // indirect
87+
golang.org/x/term v0.19.0 // indirect
8088
golang.org/x/text v0.14.0 // indirect
8189
golang.org/x/time v0.5.0 // indirect
82-
google.golang.org/appengine v1.6.8 // indirect
90+
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
8391
google.golang.org/protobuf v1.33.0 // indirect
8492
gopkg.in/inf.v0 v0.9.1 // indirect
8593
gopkg.in/yaml.v2 v2.4.0 // indirect
8694
gopkg.in/yaml.v3 v3.0.1 // indirect
87-
k8s.io/api v0.29.0 // indirect
88-
k8s.io/apiextensions-apiserver v0.29.0 // indirect
89-
k8s.io/apimachinery v0.29.0 // indirect
90-
k8s.io/client-go v0.29.0 // indirect
91-
k8s.io/klog/v2 v2.110.1 // indirect
92-
k8s.io/kube-openapi v0.0.0-20240103051144-eec4567ac022 // indirect
93-
sigs.k8s.io/controller-runtime v0.16.3 // indirect
95+
k8s.io/api v0.30.1 // indirect
96+
k8s.io/apiextensions-apiserver v0.30.1 // indirect
97+
k8s.io/apimachinery v0.30.1 // indirect
98+
k8s.io/client-go v0.30.1 // indirect
99+
k8s.io/klog/v2 v2.120.1 // indirect
100+
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
101+
knative.dev/pkg v0.0.0-20240116073220-b488e7be5902 // indirect
102+
sigs.k8s.io/controller-runtime v0.18.2 // indirect
94103
sigs.k8s.io/gateway-api v1.0.0 // indirect
95104
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
96105
sigs.k8s.io/secrets-store-csi-driver v1.4.0 // indirect

0 commit comments

Comments
 (0)