Skip to content

Commit 4bca105

Browse files
author
TQ Berg
committed
Add support for yaml-sequences of k8s objects
1 parent 8d013ec commit 4bca105

File tree

4 files changed

+240
-2
lines changed

4 files changed

+240
-2
lines changed

fixtures/list_invalid.yaml

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
apiVersion: v1
2+
kind: List
3+
items:
4+
- apiVersion: v1
5+
kind: Service
6+
metadata:
7+
name: redis-master
8+
labels:
9+
app: redis
10+
tier: backend
11+
role: master
12+
spec:
13+
ports:
14+
# the port that this service should serve on
15+
- port: 6379
16+
targetPort: 6379
17+
selector:
18+
app: redis
19+
tier: backend
20+
role: master
21+
- apiVersion: v1
22+
kind: ReplicationController
23+
metadata:
24+
name: "bob"
25+
spec:
26+
replicas: asd"
27+
selector:
28+
app: nginx
29+
templates:
30+
metadata:
31+
name: nginx
32+
labels:
33+
app: nginx
34+
spec:
35+
containers:
36+
- name: nginx
37+
image: nginx
38+
ports:
39+
- containerPort: 80

fixtures/list_valid.yaml

Lines changed: 174 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,174 @@
1+
apiVersion: v1
2+
kind: List
3+
items:
4+
- apiVersion: v1
5+
kind: Service
6+
metadata:
7+
name: redis-master
8+
labels:
9+
app: redis
10+
tier: backend
11+
role: master
12+
spec:
13+
ports:
14+
# the port that this service should serve on
15+
- port: 6379
16+
targetPort: 6379
17+
selector:
18+
app: redis
19+
tier: backend
20+
role: master
21+
- apiVersion: v1
22+
kind: ReplicationController
23+
metadata:
24+
name: redis-master
25+
# these labels can be applied automatically
26+
# from the labels in the pod template if not set
27+
labels:
28+
app: redis
29+
role: master
30+
tier: backend
31+
spec:
32+
# this replicas value is default
33+
# modify it according to your case
34+
replicas: 1
35+
# selector can be applied automatically
36+
# from the labels in the pod template if not set
37+
# selector:
38+
# app: guestbook
39+
# role: master
40+
# tier: backend
41+
template:
42+
metadata:
43+
labels:
44+
app: redis
45+
role: master
46+
tier: backend
47+
spec:
48+
containers:
49+
- name: master
50+
image: redis
51+
resources:
52+
requests:
53+
cpu: 100m
54+
memory: 100Mi
55+
ports:
56+
- containerPort: 6379
57+
- apiVersion: v1
58+
kind: Service
59+
metadata:
60+
name: redis-slave
61+
labels:
62+
app: redis
63+
tier: backend
64+
role: slave
65+
spec:
66+
ports:
67+
# the port that this service should serve on
68+
- port: 6379
69+
selector:
70+
app: redis
71+
tier: backend
72+
role: slave
73+
- apiVersion: v1
74+
kind: ReplicationController
75+
metadata:
76+
name: redis-slave
77+
# these labels can be applied automatically
78+
# from the labels in the pod template if not set
79+
labels:
80+
app: redis
81+
role: slave
82+
tier: backend
83+
spec:
84+
# this replicas value is default
85+
# modify it according to your case
86+
replicas: 2
87+
# selector can be applied automatically
88+
# from the labels in the pod template if not set
89+
# selector:
90+
# app: guestbook
91+
# role: slave
92+
# tier: backend
93+
template:
94+
metadata:
95+
labels:
96+
app: redis
97+
role: slave
98+
tier: backend
99+
spec:
100+
containers:
101+
- name: slave
102+
image: gcr.io/google_samples/gb-redisslave:v1
103+
resources:
104+
requests:
105+
cpu: 100m
106+
memory: 100Mi
107+
env:
108+
- name: GET_HOSTS_FROM
109+
value: dns
110+
# If your cluster config does not include a dns service, then to
111+
# instead access an environment variable to find the master
112+
# service's host, comment out the 'value: dns' line above, and
113+
# uncomment the line below.
114+
# value: env
115+
ports:
116+
- containerPort: 6379
117+
- apiVersion: v1
118+
kind: Service
119+
metadata:
120+
name: frontend
121+
labels:
122+
app: guestbook
123+
tier: frontend
124+
spec:
125+
# if your cluster supports it, uncomment the following to automatically create
126+
# an external load-balanced IP for the frontend service.
127+
# type: LoadBalancer
128+
ports:
129+
# the port that this service should serve on
130+
- port: 80
131+
selector:
132+
app: guestbook
133+
tier: frontend
134+
- apiVersion: v1
135+
kind: ReplicationController
136+
metadata:
137+
name: frontend
138+
# these labels can be applied automatically
139+
# from the labels in the pod template if not set
140+
labels:
141+
app: guestbook
142+
tier: frontend
143+
spec:
144+
# this replicas value is default
145+
# modify it according to your case
146+
replicas: 3
147+
# selector can be applied automatically
148+
# from the labels in the pod template if not set
149+
# selector:
150+
# app: guestbook
151+
# tier: frontend
152+
template:
153+
metadata:
154+
labels:
155+
app: guestbook
156+
tier: frontend
157+
spec:
158+
containers:
159+
- name: php-redis
160+
image: gcr.io/google_samples/gb-frontend:v3
161+
resources:
162+
requests:
163+
cpu: 100m
164+
memory: 100Mi
165+
env:
166+
- name: GET_HOSTS_FROM
167+
value: dns
168+
# If your cluster config does not include a dns service, then to
169+
# instead access environment variables to find service host
170+
# info, comment out the 'value: dns' line above, and uncomment the
171+
# line below.
172+
# value: env
173+
ports:
174+
- containerPort: 80

kubeval/kubeval.go

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -241,7 +241,25 @@ func ValidateWithCache(input []byte, schemaCache map[string]*gojsonschema.Schema
241241
return results, nil
242242
}
243243

244-
bits := bytes.Split(input, []byte(detectLineBreak(input)+"---"+detectLineBreak(input)))
244+
list := struct {
245+
Version string
246+
Kind string
247+
Items []interface{}
248+
}{}
249+
250+
unmarshalErr := yaml.Unmarshal(input, &list)
251+
isYamlList := unmarshalErr == nil && list.Items != nil && len(list.Items) > 0
252+
253+
var bits [][]byte
254+
if isYamlList {
255+
bits = make([][]byte, len(list.Items))
256+
for i, item := range list.Items {
257+
b, _ := yaml.Marshal(item)
258+
bits[i] = b
259+
}
260+
} else {
261+
bits = bytes.Split(input, []byte(detectLineBreak(input)+"---"+detectLineBreak(input)))
262+
}
245263

246264
var errors *multierror.Error
247265

kubeval/kubeval_test.go

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,7 @@ func TestValidateValidInputs(t *testing.T) {
3434
"extra_property.yaml",
3535
"full_domain_group.yaml",
3636
"unconventional_keys.yaml",
37+
"list_valid.yaml",
3738
}
3839
for _, test := range tests {
3940
filePath, _ := filepath.Abs("../fixtures/" + test)
@@ -60,6 +61,7 @@ func TestValidateValidInputsWithCache(t *testing.T) {
6061
"extra_property.yaml",
6162
"full_domain_group.yaml",
6263
"unconventional_keys.yaml",
64+
"list_valid.yaml",
6365
}
6466
schemaCache := make(map[string]*gojsonschema.Schema, 0)
6567

@@ -147,14 +149,19 @@ func TestValidateInputsWithErrors(t *testing.T) {
147149
var tests = []string{
148150
"invalid.yaml",
149151
"multi_invalid.yaml",
152+
"list_invalid.yaml",
150153
}
151154
for _, test := range tests {
152155
filePath, _ := filepath.Abs("../fixtures/" + test)
153156
fileContents, _ := ioutil.ReadFile(filePath)
154157
config := NewDefaultConfig()
155158
config.FileName = test
156159
results, _ := Validate(fileContents, config)
157-
if len(results[0].Errors) == 0 {
160+
errorCount := 0
161+
for _, result := range results {
162+
errorCount += len(result.Errors)
163+
}
164+
if errorCount == 0 {
158165
t.Errorf("Validate should not pass when testing invalid configuration in " + test)
159166
}
160167
}

0 commit comments

Comments
 (0)