Skip to content

Commit

Permalink
Schema and strict validation radixconfig (#95)
Browse files Browse the repository at this point in the history
* 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://
  • Loading branch information
Richard87 authored Jun 3, 2024
1 parent 466e58f commit 103a074
Show file tree
Hide file tree
Showing 8 changed files with 266 additions and 136 deletions.
20 changes: 8 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,15 +24,15 @@ jobs:
uses: docker/setup-buildx-action@v3

- name: Set up Go
uses: actions/setup-go@v4
uses: actions/setup-go@v5
with:
go-version: '1.21'
go-version-file: 'go.mod'

- name: Run GoReleaser
uses: goreleaser/goreleaser-action@v5
with:
distribution: goreleaser
version: v1.21.2
version: v1.26.2
args: build --clean --single-target --snapshot
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand All @@ -44,14 +44,10 @@ jobs:
- uses: actions/checkout@v4
with:
fetch-depth: 2
- uses: actions/setup-go@v4
- uses: actions/setup-go@v5
with:
go-version: '1.21'
- name: Install dependencies
run: go mod download
- name: Install GolangCI Lint
run: curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2

go-version-file: 'go.mod'
- name: golangci-lint
run: golangci-lint run --timeout=30m --max-same-issues=0 --out-format=github-actions

uses: golangci/golangci-lint-action@v4
with:
version: v1.58.2
15 changes: 15 additions & 0 deletions .golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
run:
timeout: 30m

linters:
enable:
- errcheck
- gosimple
- govet
- ineffassign
- staticcheck
- unused
- zerologlint

issues:
max-same-issues: 0
2 changes: 1 addition & 1 deletion Dockerfile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
FROM golang:1.21-alpine3.18 as builder
FROM golang:1.22-alpine3.19 as builder

ENV GO111MODULE=on

Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,15 @@ lint: bootstrap

HAS_SWAGGER := $(shell command -v swagger;)
HAS_GOLANGCI_LINT := $(shell command -v golangci-lint;)
HAS_GORELEASER := $(shell command -v goreleaser;)

bootstrap:
ifndef HAS_SWAGGER
go install github.com/go-swagger/go-swagger/cmd/[email protected]
endif
ifndef HAS_GOLANGCI_LINT
curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin v1.55.2
go install github.com/golangci/golangci-lint/cmd/[email protected]
endif
ifndef HAS_GORELEASER
go install github.com/goreleaser/[email protected]
endif
108 changes: 95 additions & 13 deletions cmd/validateRadixConfig.go
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,9 @@ import (
"github.com/equinor/radix-cli/pkg/flagnames"
radixv1 "github.com/equinor/radix-operator/pkg/apis/radix/v1"
"github.com/equinor/radix-operator/pkg/apis/radixvalidators"
"github.com/equinor/radix-operator/pkg/apis/utils"
"github.com/pkg/errors"
"github.com/santhosh-tekuri/jsonschema/v5"
_ "github.com/santhosh-tekuri/jsonschema/v5/httploader"
"github.com/spf13/cobra"
"sigs.k8s.io/yaml"
)
Expand All @@ -33,46 +34,126 @@ var validateRadixConfigCmd = &cobra.Command{
Use: "radix-config",
Short: "Validate radixconfig.yaml",
Long: `Check radixconfig.yaml for structural and logical errors`,
RunE: func(cmd *cobra.Command, args []string) error {

cmd.SilenceUsage = true

Run: func(cmd *cobra.Command, args []string) {
radixconfig, err := cmd.Flags().GetString(flagnames.ConfigFile)
if err != nil {
return err
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}

printfile, err := cmd.Flags().GetBool(flagnames.Print)
if err != nil {
return err
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}

schema, err := cmd.Flags().GetString(flagnames.Schema)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}

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

ra, err := utils.GetRadixApplicationFromFile(radixconfig)
raw, err := os.ReadFile(radixconfig)
if err != nil {
return fmt.Errorf("RadixConfig is invalid: %w", err)
fmt.Fprintf(os.Stderr, "failed to read file: %v\n", err)
os.Exit(1)
}

ra, err := unmarshalRadixApplication(raw)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}

if printfile {
err = printRA(ra)
if err != nil {
return err
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}
}

validationErrors, err := validateSchema(raw, schema)
if err != nil {
fmt.Fprintln(os.Stderr, err.Error())
os.Exit(1)
}

err = radixvalidators.IsRadixApplicationValid(ra)
if err != nil {
return fmt.Errorf("RadixConfig is invalid:\n%w", err)
validationErrors = append(validationErrors, err)
}

err = strictUnmarshalValidation(raw)
if err != nil {
validationErrors = append(validationErrors, err)
}

if len(validationErrors) > 0 {
for _, err := range validationErrors {
fmt.Fprintf(os.Stderr, " - %s\n", err)
}

fmt.Fprintln(os.Stderr, "RadixConfig is invalid")
os.Exit(2)
}

fmt.Fprintln(os.Stderr, "RadixConfig is valid")
return nil
},
}

func validateSchema(raw []byte, schema string) (validationErrors []error, err error) {
s, err := jsonschema.Compile(schema)
if err != nil {
return nil, fmt.Errorf("failed compiling schema %s: %s", schema, err)
}

var obj interface{}
err = yaml.Unmarshal(raw, &obj)
if err != nil {
return nil, fmt.Errorf("failed to parse file: %v", err)
}

err = s.Validate(obj)
var verr *jsonschema.ValidationError
if errors.As(err, &verr) {
for _, err := range verr.Causes {
validationErrors = append(validationErrors, fmt.Errorf("%s: %s", err.InstanceLocation, err.Message))
}
} else {
return nil, err
}

return validationErrors, nil
}

func strictUnmarshalValidation(raw []byte) error {
radixApp := &radixv1.RadixApplication{}

err := yaml.UnmarshalStrict(raw, radixApp)
if err != nil {
return fmt.Errorf("strict test failed: %v", err)
}

return nil
}
func unmarshalRadixApplication(raw []byte) (*radixv1.RadixApplication, error) {
radixApp := &radixv1.RadixApplication{}

err := yaml.Unmarshal(raw, radixApp)
if err != nil {
return nil, fmt.Errorf("strict test failed: %v", err)
}

return radixApp, nil
}

func printRA(ra *radixv1.RadixApplication) error {
b, err := yaml.Marshal(ra)
if err != nil {
Expand All @@ -87,6 +168,7 @@ func init() {
validateCmd.AddCommand(validateRadixConfigCmd)
validateRadixConfigCmd.Flags().StringP(flagnames.ConfigFile, "f", "radixconfig.yaml", "Name of the radixconfig file. Defaults to radixconfig.yaml in current directory")
validateRadixConfigCmd.Flags().BoolP(flagnames.Print, "p", false, "Print parsed config file")
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")

// Allow but hide token-env flag so radix-github-actions won't interfere
validateRadixConfigCmd.Flags().Bool(flagnames.TokenEnvironment, false, fmt.Sprintf("Take the token from environment variable %s", client.TokenEnvironmentName))
Expand Down
71 changes: 40 additions & 31 deletions go.mod
Original file line number Diff line number Diff line change
@@ -1,22 +1,23 @@
module github.com/equinor/radix-cli

go 1.21
go 1.22.0

toolchain go1.21.0
toolchain go1.22.1

require (
github.com/AzureAD/microsoft-authentication-library-for-go v1.2.1
github.com/equinor/radix-operator v1.51.2
github.com/fatih/color v1.15.0
github.com/equinor/radix-operator v1.55.2
github.com/fatih/color v1.16.0
github.com/go-openapi/errors v0.20.4
github.com/go-openapi/runtime v0.26.2
github.com/go-openapi/strfmt v0.21.8
github.com/go-openapi/swag v0.22.7
github.com/go-openapi/validate v0.22.3
github.com/pkg/errors v0.9.1
github.com/santhosh-tekuri/jsonschema/v5 v5.3.1
github.com/sirupsen/logrus v1.9.3
github.com/spf13/cobra v1.8.0
k8s.io/utils v0.0.0-20240102154912-e7106e64919e
k8s.io/utils v0.0.0-20240310230437-4693a0247e57
sigs.k8s.io/yaml v1.4.0
)

Expand All @@ -27,8 +28,12 @@ require (
github.com/cert-manager/cert-manager v1.14.2 // indirect
github.com/cespare/xxhash/v2 v2.2.0 // indirect
github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
github.com/emicklei/go-restful/v3 v3.11.0 // indirect
github.com/emicklei/go-restful/v3 v3.11.2 // indirect
github.com/equinor/radix-common v1.7.1 // indirect
github.com/evanphx/json-patch v5.8.1+incompatible // indirect
github.com/evanphx/json-patch/v5 v5.9.0 // indirect
github.com/expr-lang/expr v1.15.8 // indirect
github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-openapi/analysis v0.21.4 // indirect
Expand All @@ -37,60 +42,64 @@ require (
github.com/go-openapi/loads v0.21.2 // indirect
github.com/go-openapi/spec v0.20.11 // indirect
github.com/gogo/protobuf v1.3.2 // indirect
github.com/golang-jwt/jwt/v5 v5.0.0 // indirect
github.com/golang-jwt/jwt/v5 v5.2.0 // indirect
github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da // indirect
github.com/golang/mock v1.6.0 // indirect
github.com/golang/protobuf v1.5.3 // indirect
github.com/golang/protobuf v1.5.4 // indirect
github.com/google/gnostic-models v0.6.8 // indirect
github.com/google/go-cmp v0.6.0 // indirect
github.com/google/gofuzz v1.2.0 // indirect
github.com/google/uuid v1.5.0 // indirect
github.com/google/uuid v1.6.0 // indirect
github.com/imdario/mergo v0.3.16 // indirect
github.com/inconshreveable/mousetrap v1.1.0 // indirect
github.com/josharian/intern v1.0.0 // indirect
github.com/json-iterator/go v1.1.12 // indirect
github.com/kedacore/keda/v2 v2.13.1 // indirect
github.com/kylelemons/godebug v1.1.0 // indirect
github.com/mailru/easyjson v0.7.7 // indirect
github.com/mattn/go-colorable v0.1.13 // indirect
github.com/mattn/go-isatty v0.0.19 // indirect
github.com/matttproud/golang_protobuf_extensions/v2 v2.0.0 // indirect
github.com/mattn/go-isatty v0.0.20 // indirect
github.com/mitchellh/mapstructure v1.5.0 // indirect
github.com/modern-go/concurrent v0.0.0-20180306012644-bacd9c7ef1dd // indirect
github.com/modern-go/reflect2 v1.0.2 // indirect
github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
github.com/oklog/ulid v1.3.1 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/pkg/browser v0.0.0-20210911075715-681adbf594b8 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/prometheus-operator/prometheus-operator/pkg/apis/monitoring v0.70.0 // indirect
github.com/prometheus-operator/prometheus-operator/pkg/client v0.70.0 // indirect
github.com/prometheus/client_golang v1.18.0 // indirect
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/client_golang v1.19.0 // indirect
github.com/prometheus/client_model v0.6.1 // indirect
github.com/prometheus/common v0.53.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/robfig/cron/v3 v3.0.1 // indirect
github.com/rs/zerolog v1.32.0 // indirect
github.com/spf13/pflag v1.0.5 // indirect
go.mongodb.org/mongo-driver v1.13.1 // indirect
go.opentelemetry.io/otel v1.21.0 // indirect
go.opentelemetry.io/otel/metric v1.21.0 // indirect
go.opentelemetry.io/otel/trace v1.21.0 // indirect
golang.org/x/crypto v0.17.0 // indirect
golang.org/x/net v0.19.0 // indirect
golang.org/x/oauth2 v0.15.0 // indirect
golang.org/x/sys v0.15.0 // indirect
golang.org/x/term v0.15.0 // indirect
go.opentelemetry.io/otel v1.22.0 // indirect
go.opentelemetry.io/otel/metric v1.22.0 // indirect
go.opentelemetry.io/otel/trace v1.22.0 // indirect
golang.org/x/crypto v0.22.0 // indirect
golang.org/x/exp v0.0.0-20240112132812-db7319d0e0e3 // indirect
golang.org/x/net v0.24.0 // indirect
golang.org/x/oauth2 v0.19.0 // indirect
golang.org/x/sys v0.19.0 // indirect
golang.org/x/term v0.19.0 // indirect
golang.org/x/text v0.14.0 // indirect
golang.org/x/time v0.5.0 // indirect
google.golang.org/appengine v1.6.8 // indirect
gomodules.xyz/jsonpatch/v2 v2.4.0 // indirect
google.golang.org/protobuf v1.33.0 // indirect
gopkg.in/inf.v0 v0.9.1 // indirect
gopkg.in/yaml.v2 v2.4.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
k8s.io/api v0.29.0 // indirect
k8s.io/apiextensions-apiserver v0.29.0 // indirect
k8s.io/apimachinery v0.29.0 // indirect
k8s.io/client-go v0.29.0 // indirect
k8s.io/klog/v2 v2.110.1 // indirect
k8s.io/kube-openapi v0.0.0-20240103051144-eec4567ac022 // indirect
sigs.k8s.io/controller-runtime v0.16.3 // indirect
k8s.io/api v0.30.1 // indirect
k8s.io/apiextensions-apiserver v0.30.1 // indirect
k8s.io/apimachinery v0.30.1 // indirect
k8s.io/client-go v0.30.1 // indirect
k8s.io/klog/v2 v2.120.1 // indirect
k8s.io/kube-openapi v0.0.0-20240228011516-70dd3763d340 // indirect
knative.dev/pkg v0.0.0-20240116073220-b488e7be5902 // indirect
sigs.k8s.io/controller-runtime v0.18.2 // indirect
sigs.k8s.io/gateway-api v1.0.0 // indirect
sigs.k8s.io/json v0.0.0-20221116044647-bc3834ca7abd // indirect
sigs.k8s.io/secrets-store-csi-driver v1.4.0 // indirect
Expand Down
Loading

0 comments on commit 103a074

Please sign in to comment.