123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413 |
- /*
- Copyright 2017 The Kubernetes Authors.
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
- http://www.apache.org/licenses/LICENSE-2.0
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
- */
- package validation
- import (
- "path/filepath"
- . "github.com/onsi/ginkgo"
- . "github.com/onsi/gomega"
- utilerrors "k8s.io/apimachinery/pkg/util/errors"
- "k8s.io/kube-openapi/pkg/util/proto/validation"
- // This dependency is needed to register API types.
- "k8s.io/kube-openapi/pkg/util/proto/testing"
- "k8s.io/kubernetes/pkg/kubectl/cmd/util/openapi"
- )
- var fakeSchema = testing.Fake{Path: filepath.Join("..", "..", "..", "..", "..", "..", "api", "openapi-spec", "swagger.json")}
- var _ = Describe("resource validation using OpenAPI Schema", func() {
- var validator *SchemaValidation
- BeforeEach(func() {
- s, err := fakeSchema.OpenAPISchema()
- Expect(err).To(BeNil())
- resources, err := openapi.NewOpenAPIData(s)
- Expect(err).To(BeNil())
- validator = NewSchemaValidation(resources)
- Expect(validator).ToNot(BeNil())
- })
- It("finds Deployment in Schema and validates it", func() {
- err := validator.ValidateBytes([]byte(`
- apiVersion: apps/v1
- kind: Deployment
- metadata:
- labels:
- name: redis-master
- name: name
- spec:
- replicas: 1
- selector:
- matchLabels:
- app: redis
- template:
- metadata:
- labels:
- app: redis
- spec:
- containers:
- - image: redis
- name: redis
- `))
- Expect(err).To(BeNil())
- })
- It("validates a valid pod", func() {
- err := validator.ValidateBytes([]byte(`
- apiVersion: v1
- kind: Pod
- metadata:
- labels:
- name: redis-master
- name: name
- spec:
- containers:
- - args:
- - this
- - is
- - an
- - ok
- - command
- image: gcr.io/fake_project/fake_image:fake_tag
- name: master
- `))
- Expect(err).To(BeNil())
- })
- It("finds invalid command (string instead of []string) in Json Pod", func() {
- err := validator.ValidateBytes([]byte(`
- {
- "kind": "Pod",
- "apiVersion": "v1",
- "metadata": {
- "name": "name",
- "labels": {
- "name": "redis-master"
- }
- },
- "spec": {
- "containers": [
- {
- "name": "master",
- "image": "gcr.io/fake_project/fake_image:fake_tag",
- "args": "this is a bad command"
- }
- ]
- }
- }
- `))
- Expect(err).To(Equal(utilerrors.NewAggregate([]error{
- validation.ValidationError{
- Path: "Pod.spec.containers[0].args",
- Err: validation.InvalidTypeError{
- Path: "io.k8s.api.core.v1.Container.args",
- Expected: "array",
- Actual: "string",
- },
- },
- })))
- })
- It("fails because hostPort is string instead of int", func() {
- err := validator.ValidateBytes([]byte(`
- {
- "kind": "Pod",
- "apiVersion": "v1",
- "metadata": {
- "name": "apache-php",
- "labels": {
- "name": "apache-php"
- }
- },
- "spec": {
- "volumes": [{
- "name": "shared-disk"
- }],
- "containers": [
- {
- "name": "apache-php",
- "image": "gcr.io/fake_project/fake_image:fake_tag",
- "ports": [
- {
- "name": "apache",
- "hostPort": "13380",
- "containerPort": 80,
- "protocol": "TCP"
- }
- ],
- "volumeMounts": [
- {
- "name": "shared-disk",
- "mountPath": "/var/www/html"
- }
- ]
- }
- ]
- }
- }
- `))
- Expect(err).To(Equal(utilerrors.NewAggregate([]error{
- validation.ValidationError{
- Path: "Pod.spec.containers[0].ports[0].hostPort",
- Err: validation.InvalidTypeError{
- Path: "io.k8s.api.core.v1.ContainerPort.hostPort",
- Expected: "integer",
- Actual: "string",
- },
- },
- })))
- })
- It("fails because volume is not an array of object", func() {
- err := validator.ValidateBytes([]byte(`
- {
- "kind": "Pod",
- "apiVersion": "v1",
- "metadata": {
- "name": "apache-php",
- "labels": {
- "name": "apache-php"
- }
- },
- "spec": {
- "volumes": [
- "name": "shared-disk"
- ],
- "containers": [
- {
- "name": "apache-php",
- "image": "gcr.io/fake_project/fake_image:fake_tag",
- "ports": [
- {
- "name": "apache",
- "hostPort": 13380,
- "containerPort": 80,
- "protocol": "TCP"
- }
- ],
- "volumeMounts": [
- {
- "name": "shared-disk",
- "mountPath": "/var/www/html"
- }
- ]
- }
- ]
- }
- }
- `))
- Expect(err.Error()).To(Equal("invalid character ':' after array element"))
- })
- It("fails because some string lists have empty strings", func() {
- err := validator.ValidateBytes([]byte(`
- apiVersion: v1
- kind: Pod
- metadata:
- labels:
- name: redis-master
- name: name
- spec:
- containers:
- - image: gcr.io/fake_project/fake_image:fake_tag
- name: master
- args:
- -
- command:
- -
- `))
- Expect(err).To(Equal(utilerrors.NewAggregate([]error{
- validation.ValidationError{
- Path: "Pod.spec.containers[0].args",
- Err: validation.InvalidObjectTypeError{
- Path: "Pod.spec.containers[0].args[0]",
- Type: "nil",
- },
- },
- validation.ValidationError{
- Path: "Pod.spec.containers[0].command",
- Err: validation.InvalidObjectTypeError{
- Path: "Pod.spec.containers[0].command[0]",
- Type: "nil",
- },
- },
- })))
- })
- It("fails if required fields are missing", func() {
- err := validator.ValidateBytes([]byte(`
- apiVersion: v1
- kind: Pod
- metadata:
- labels:
- name: redis-master
- name: name
- spec:
- containers:
- - command: ["my", "command"]
- `))
- Expect(err).To(Equal(utilerrors.NewAggregate([]error{
- validation.ValidationError{
- Path: "Pod.spec.containers[0]",
- Err: validation.MissingRequiredFieldError{
- Path: "io.k8s.api.core.v1.Container",
- Field: "name",
- },
- },
- })))
- })
- It("fails if required fields are empty", func() {
- err := validator.ValidateBytes([]byte(`
- apiVersion: v1
- kind: Pod
- metadata:
- labels:
- name: redis-master
- name: name
- spec:
- containers:
- - image:
- name:
- `))
- Expect(err).To(Equal(utilerrors.NewAggregate([]error{
- validation.ValidationError{
- Path: "Pod.spec.containers[0]",
- Err: validation.MissingRequiredFieldError{
- Path: "io.k8s.api.core.v1.Container",
- Field: "name",
- },
- },
- })))
- })
- It("is fine with empty non-mandatory fields", func() {
- err := validator.ValidateBytes([]byte(`
- apiVersion: v1
- kind: Pod
- metadata:
- labels:
- name: redis-master
- name: name
- spec:
- containers:
- - image: image
- name: name
- command:
- `))
- Expect(err).To(BeNil())
- })
- It("can validate lists", func() {
- err := validator.ValidateBytes([]byte(`
- apiVersion: v1
- kind: List
- items:
- - apiVersion: v1
- kind: Pod
- metadata:
- labels:
- name: redis-master
- name: name
- spec:
- containers:
- - name: name
- `))
- Expect(err).To(BeNil())
- })
- It("fails because apiVersion is not provided", func() {
- err := validator.ValidateBytes([]byte(`
- kind: Pod
- metadata:
- name: name
- spec:
- containers:
- - name: name
- image: image
- `))
- Expect(err.Error()).To(Equal("apiVersion not set"))
- })
- It("fails because apiVersion type is not string and kind is not provided", func() {
- err := validator.ValidateBytes([]byte(`
- apiVersion: 1
- metadata:
- name: name
- spec:
- containers:
- - name: name
- image: image
- `))
- Expect(err.Error()).To(Equal("[apiVersion isn't string type, kind not set]"))
- })
- It("fails because List first item is missing kind and second item is missing apiVersion", func() {
- err := validator.ValidateBytes([]byte(`
- apiVersion: v1
- kind: List
- items:
- - apiVersion: v1
- metadata:
- name: name
- spec:
- replicas: 1
- template:
- metadata:
- labels:
- name: name
- spec:
- containers:
- - name: name
- image: image
- - kind: Service
- metadata:
- name: name
- spec:
- type: NodePort
- ports:
- - port: 123
- targetPort: 1234
- name: name
- selector:
- name: name
- `))
- Expect(err.Error()).To(Equal("[kind not set, apiVersion not set]"))
- })
- It("is fine with crd resource with List as a suffix kind name, which may not be a list of resources", func() {
- err := validator.ValidateBytes([]byte(`
- apiVersion: fake.com/v1
- kind: FakeList
- metadata:
- name: fake
- spec:
- foo: bar
- `))
- Expect(err).To(BeNil())
- })
- })
|