Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: exit early on invalid k8s version flag #210

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .github/workflows/main.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,9 @@ jobs:
with:
fetch-depth: 0 # https://github.com/goreleaser/goreleaser-action/issues/56

- uses: docker/setup-qemu-action@v2
- uses: docker/setup-buildx-action@v2

- name: goreleaser
run: |
echo "${{ github.token }}" | docker login https://ghcr.io -u ${GITHUB_ACTOR} --password-stdin
Expand Down
48 changes: 44 additions & 4 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,23 +32,63 @@ archives:

dockers:
- image_templates:
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:latest'
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:{{ .Tag }}'
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:{{ .Tag }}-amd64'
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:latest-amd64'
dockerfile: Dockerfile
use: buildx
build_flag_templates:
- "--pull"
- "--platform=linux/amd64"
goos: linux
goarch: amd64
- image_templates:
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:latest-alpine'
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:{{ .Tag }}-alpine'
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:{{ .Tag }}-arm64'
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:latest-arm64'
dockerfile: Dockerfile
use: buildx
build_flag_templates:
- "--pull"
- "--platform=linux/arm64"
goos: linux
goarch: arm64
- image_templates:
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:{{ .Tag }}-amd64-alpine'
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:latest-amd64-alpine'
dockerfile: Dockerfile-alpine
use: buildx
build_flag_templates:
- "--pull"
- "--platform=linux/amd64"
goos: linux
goarch: amd64
- image_templates:
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:{{ .Tag }}-arm64-alpine'
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:latest-arm64-alpine'
dockerfile: Dockerfile-alpine
use: buildx
build_flag_templates:
- "--pull"
- "--platform=linux/arm64"
goos: linux
goarch: arm64

docker_manifests:
- name_template: 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:{{ .Tag }}'
image_templates:
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:{{ .Tag }}-amd64'
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:{{ .Tag }}-arm64'
- name_template: 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:latest'
image_templates:
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:latest-amd64'
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:latest-arm64'
- name_template: 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:latest-alpine'
image_templates:
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:latest-amd64-alpine'
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:latest-arm64-alpine'
- name_template: 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:{{ .Tag }}-alpine'
image_templates:
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:{{ .Tag }}-amd64-alpine'
- 'ghcr.io/{{.Env.GIT_OWNER}}/kubeconform:{{ .Tag }}-arm64-alpine'

checksum:
name_template: 'CHECKSUMS'
Expand Down
8 changes: 4 additions & 4 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -8,20 +8,20 @@ local-test:
go test -race ./...

local-build:
go build -o bin/ ./...
go build -buildvcs=false -o bin/ ./...

local-build-static:
CGO_ENABLED=0 GOFLAGS=-mod=vendor GOOS=linux GOARCH=amd64 GO111MODULE=on go build -trimpath -tags=netgo -ldflags "-extldflags=\"-static\"" -a -o bin/ ./...

# These only used for development. Release artifacts and docker images are produced by goreleaser.
docker-test:
docker run -t -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform golang:1.17 make local-test
docker run -t -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform golang:1.20 make local-test

docker-build:
docker run -t -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform golang:1.17 make local-build
docker run -t -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform golang:1.20 make local-build

docker-build-static:
docker run -t -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform golang:1.17 make local-build-static
docker run -t -v $$PWD:/go/src/github.com/yannh/kubeconform -w /go/src/github.com/yannh/kubeconform golang:1.20 make local-build-static

build-bats:
docker build -t bats -f Dockerfile.bats .
Expand Down
10 changes: 7 additions & 3 deletions Readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ sys 0m1,069s
* [Usage examples](#Usage-examples)
* [Proxy support](#Proxy-support)
* [Overriding schemas location](#Overriding-schemas-location)
* [CustomResourceDefinition (CRD) Support](#CustomResourceDefinition-(CRD)-Support)
* [CustomResourceDefinition (CRD) Support](#CustomResourceDefinition-CRD-Support)
* [OpenShift schema Support](#OpenShift-schema-Support)
* [Integrating Kubeconform in the CI](#Integrating-Kubeconform-in-the-CI)
* [Github Workflow](#Github-Workflow)
Expand Down Expand Up @@ -256,12 +256,16 @@ $ python ./scripts/openapi2jsonschema.py https://raw.githubusercontent.com/aws/a
JSON schema written to trainingjob_v1.json
```

By default, the file name output format is `{kind}_{version}`. The `FILENAME_FORMAT` environment variable can be used to change the output file name (Available variables: `kind`, `group`, `version`):
By default, the file name output format is `{kind}_{version}`. The `FILENAME_FORMAT` environment variable can be used to change the output file name (Available variables: `kind`, `group`, `fullgroup`, `version`):

```
$ export FILENAME_FORMAT='{kind}-{group}-{version}'
$ ./scripts/openapi2jsonschema.py https://raw.githubusercontent.com/aws/amazon-sagemaker-operator-for-k8s/master/config/crd/bases/sagemaker.aws.amazon.com_trainingjobs.yaml
JSON schema written to trainingjob-sagemaker-v1.json

$ export FILENAME_FORMAT='{kind}-{fullgroup}-{version}'
$ ./scripts/openapi2jsonschema.py https://raw.githubusercontent.com/aws/amazon-sagemaker-operator-for-k8s/master/config/crd/bases/sagemaker.aws.amazon.com_trainingjobs.yaml
JSON schema written to trainingjob-sagemaker.aws.amazon.com-v1.json
```

After converting your CRDs to JSON schema files, you can use `kubeconform` to validate your CRs against them:
Expand Down Expand Up @@ -304,7 +308,7 @@ jobs:
- name: login to Github Packages
run: echo "${{ github.token }}" | docker login https://ghcr.io -u ${GITHUB_ACTOR} --password-stdin
- uses: actions/checkout@v2
- uses: docker://ghcr.io/yannh/kubeconform:master
- uses: docker://ghcr.io/yannh/kubeconform:latest
with:
entrypoint: '/kubeconform'
args: "-summary -output json kubeconfigs/"
Expand Down
6 changes: 6 additions & 0 deletions acceptance.bats
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,12 @@ resetCacheFolder() {
[ "$status" -eq 1 ]
}

@test "Fail early when passing a non valid -kubernetes-version" {
run bin/kubeconform -kubernetes-version 1.25 fixtures/valid.yaml
[ "${lines[0]}" == 'invalid value "1.25" for flag -kubernetes-version: 1.25 is not a valid version. Valid values are "master" (default) or full version x.y.z (e.g. "1.27.2")' ]
[ "$status" -eq 1 ]
}

@test "Pass with a valid input when validating against openshift manifests" {
run bin/kubeconform -kubernetes-version 3.8.0 -schema-location 'https://raw.githubusercontent.com/garethr/openshift-json-schema/master/{{ .NormalizedKubernetesVersion }}-standalone{{ .StrictSuffix }}/{{ .ResourceKind }}.json' -summary fixtures/valid.yaml
[ "$status" -eq 0 ]
Expand Down
2 changes: 1 addition & 1 deletion cmd/kubeconform/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ func kubeconform(cfg config.Config) int {
SkipTLS: cfg.SkipTLS,
SkipKinds: cfg.SkipKinds,
RejectKinds: cfg.RejectKinds,
KubernetesVersion: cfg.KubernetesVersion,
KubernetesVersion: cfg.KubernetesVersion.String(),
Strict: cfg.Strict,
IgnoreMissingSchemas: cfg.IgnoreMissingSchemas,
})
Expand Down
23 changes: 21 additions & 2 deletions pkg/config/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"bytes"
"flag"
"fmt"
"regexp"
"strings"
)

Expand All @@ -15,7 +16,7 @@ type Config struct {
Help bool `yaml:"help" json:"help"`
IgnoreFilenamePatterns []string `yaml:"ignoreFilenamePatterns" json:"ignoreFilenamePatterns"`
IgnoreMissingSchemas bool `yaml:"ignoreMissingSchemas" json:"ignoreMissingSchemas"`
KubernetesVersion string `yaml:"kubernetesVersion" json:"kubernetesVersion"`
KubernetesVersion k8sVersionValue `yaml:"kubernetesVersion" json:"kubernetesVersion"`
NumberOfWorkers int `yaml:"numberOfWorkers" json:"numberOfWorkers"`
OutputFormat string `yaml:"output" json:"output"`
RejectKinds map[string]struct{} `yaml:"reject" json:"reject"`
Expand All @@ -39,6 +40,24 @@ func (ap *arrayParam) Set(value string) error {
return nil
}

type k8sVersionValue string

func (kv *k8sVersionValue) String() string {
return string(*kv)
}

func (kv k8sVersionValue) MarshalText() ([]byte, error) {
return []byte(kv), nil
}

func (kv *k8sVersionValue) UnmarshalText(v []byte) error {
if ok, _ := regexp.MatchString(`^(master|\d+\.\d+\.\d+)$`, string(v)); ok != true {
return fmt.Errorf("%v is not a valid version. Valid values are \"master\" (default) or full version x.y.z (e.g. \"1.27.2\")", string(v))
}
*kv = k8sVersionValue(v)
return nil
}

func splitCSV(csvStr string) map[string]struct{} {
splitValues := strings.Split(csvStr, ",")
valuesMap := map[string]struct{}{}
Expand All @@ -63,7 +82,7 @@ func FromFlags(progName string, args []string) (Config, string, error) {
c := Config{}
c.Files = []string{}

flags.StringVar(&c.KubernetesVersion, "kubernetes-version", "master", "version of Kubernetes to validate against, e.g.: 1.18.0")
flags.TextVar(&c.KubernetesVersion, "kubernetes-version", k8sVersionValue("master"), "version of Kubernetes to validate against, e.g.: 1.18.0")
flags.Var(&schemaLocationsParam, "schema-location", "override schemas location search path (can be specified multiple times)")
flags.StringVar(&skipKindsCSV, "skip", "", "comma-separated list of kinds or GVKs to ignore")
flags.StringVar(&rejectKindsCSV, "reject", "", "comma-separated list of kinds or GVKs to reject")
Expand Down
31 changes: 31 additions & 0 deletions pkg/config/config_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -138,3 +138,34 @@ func TestFromFlags(t *testing.T) {
}
}
}

func TestKubernetesVersionFlag(t *testing.T) {
for _, testCase := range []struct {
k8sVersion string
expectError bool
}{
{
"master",
false,
},
{
"1.2.3",
false,
},
{
"1.2",
true,
},
{
"foo",
true,
},
} {
_, _, err := FromFlags("kubeconform", []string{"-kubernetes-version", testCase.k8sVersion})
if testCase.expectError && err == nil {
t.Errorf("expected error for kubernetes version flag %s", testCase.k8sVersion)
} else if !testCase.expectError && err != nil {
t.Errorf("unexpected error for kubernetes version flag %s: %s", testCase.k8sVersion, err)
}
}
}
2 changes: 1 addition & 1 deletion pkg/output/output.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ func New(w io.Writer, outputFormat string, printSummary, isStdin, verbose bool)
case outputFormat == "text":
return textOutput(w, printSummary, isStdin, verbose), nil
default:
return nil, fmt.Errorf("`outputFormat` must be 'json', 'tap' or 'text'")
return nil, fmt.Errorf("'outputFormat' must be 'json', 'junit', 'pretty', 'tap' or 'text'")
}
}
3 changes: 3 additions & 0 deletions scripts/openapi2jsonschema.py
Original file line number Diff line number Diff line change
Expand Up @@ -155,6 +155,7 @@ def construct_value(load, node):
filename = filename_format.format(
kind=y["spec"]["names"]["kind"],
group=y["spec"]["group"].split(".")[0],
fullgroup=y["spec"]["group"],
version=version["name"],
).lower() + ".json"

Expand All @@ -164,6 +165,7 @@ def construct_value(load, node):
filename = filename_format.format(
kind=y["spec"]["names"]["kind"],
group=y["spec"]["group"].split(".")[0],
fullgroup=y["spec"]["group"],
version=version["name"],
).lower() + ".json"

Expand All @@ -173,6 +175,7 @@ def construct_value(load, node):
filename = filename_format.format(
kind=y["spec"]["names"]["kind"],
group=y["spec"]["group"].split(".")[0],
fullgroup=y["spec"]["group"],
version=y["spec"]["version"],
).lower() + ".json"

Expand Down