Skip to content

Commit dd168ed

Browse files
authored
Merge pull request #19 from garethr/stdin-support
Support input over stdin for validating config files
2 parents b863eb4 + 4400068 commit dd168ed

File tree

3 files changed

+78
-20
lines changed

3 files changed

+78
-20
lines changed

README.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,18 @@ $ echo $?
1919
1
2020
```
2121

22+
Alternatively kubeval can also take input via `stdin` which can make using
23+
it as part of an automated pipeline easier.
24+
25+
```
26+
$ cat my-invalid-rc.yaml | kubeval
27+
The document my-invalid-rc.yaml contains an invalid ReplicationController
28+
--> spec.replicas: Invalid type. Expected: integer, given: string
29+
$ echo $?
30+
1
31+
```
32+
33+
2234
## Why?
2335

2436
* If you're writing Kubernetes configuration files by hand it is useful

acceptance.bats

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@
66
[ "$output" = "The document fixtures/valid.yaml contains a valid ReplicationController" ]
77
}
88

9+
@test "Pass when parsing a valid Kubernetes config YAML file on stdin" {
10+
run bash -c "cat fixtures/valid.yaml | kubeval"
11+
[ "$status" -eq 0 ]
12+
[ "$output" = "The document stdin contains a valid ReplicationController" ]
13+
}
14+
915
@test "Pass when parsing a valid Kubernetes config JSON file" {
1016
run kubeval fixtures/valid.json
1117
[ "$status" -eq 0 ]
@@ -46,6 +52,11 @@
4652
[ "$status" -eq 1 ]
4753
}
4854

55+
@test "Fail when parsing an invalid Kubernetes config file on stdin" {
56+
run bash -c "cat fixtures/invalid.yaml | kubeval"
57+
[ "$status" -eq 1 ]
58+
}
59+
4960
@test "Return relevant error for non-existent file" {
5061
run kubeval fixtures/not-here
5162
[ "$status" -eq 1 ]

cmd/root.go

Lines changed: 55 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,12 @@
11
package cmd
22

33
import (
4+
"bufio"
5+
"bytes"
46
"io/ioutil"
57
"os"
68
"path/filepath"
9+
"runtime"
710

811
"github.com/spf13/cobra"
912

@@ -21,34 +24,51 @@ var RootCmd = &cobra.Command{
2124
printVersion()
2225
os.Exit(0)
2326
}
24-
if len(args) < 1 {
25-
log.Error("You must pass at least one file as an argument")
26-
os.Exit(1)
27-
}
2827
success := true
29-
for _, fileName := range args {
30-
filePath, _ := filepath.Abs(fileName)
31-
fileContents, err := ioutil.ReadFile(filePath)
32-
if err != nil {
33-
log.Error("Could not open file", fileName)
28+
windowsStdinIssue := false
29+
stat, err := os.Stdin.Stat()
30+
if err != nil {
31+
// Stat() will return an error on Windows in both Powershell and
32+
// console until go1.9 when nothing is passed on stdin.
33+
// See https://github.com/golang/go/issues/14853.
34+
if runtime.GOOS != "windows" {
35+
log.Error(err)
3436
os.Exit(1)
37+
} else {
38+
windowsStdinIssue = true
3539
}
36-
results, err := kubeval.Validate(fileContents, fileName)
40+
}
41+
// We detect whether we have anything on stdin to process
42+
if !windowsStdinIssue && ((stat.Mode() & os.ModeCharDevice) == 0) {
43+
var buffer bytes.Buffer
44+
scanner := bufio.NewScanner(os.Stdin)
45+
for scanner.Scan() {
46+
buffer.WriteString(scanner.Text() + "\n")
47+
}
48+
results, err := kubeval.Validate(buffer.Bytes(), "stdin")
3749
if err != nil {
3850
log.Error(err)
3951
os.Exit(1)
4052
}
41-
42-
for _, result := range results {
43-
if len(result.Errors) > 0 {
44-
success = false
45-
log.Warn("The document", result.FileName, "contains an invalid", result.Kind)
46-
for _, desc := range result.Errors {
47-
log.Info("--->", desc)
48-
}
49-
} else {
50-
log.Success("The document", result.FileName, "contains a valid", result.Kind)
53+
success = logResults(results, success)
54+
} else {
55+
if len(args) < 1 {
56+
log.Error("You must pass at least one file as an argument")
57+
os.Exit(1)
58+
}
59+
for _, fileName := range args {
60+
filePath, _ := filepath.Abs(fileName)
61+
fileContents, err := ioutil.ReadFile(filePath)
62+
if err != nil {
63+
log.Error("Could not open file", fileName)
64+
os.Exit(1)
65+
}
66+
results, err := kubeval.Validate(fileContents, fileName)
67+
if err != nil {
68+
log.Error(err)
69+
os.Exit(1)
5170
}
71+
success = logResults(results, success)
5272
}
5373
}
5474
if !success {
@@ -57,6 +77,21 @@ var RootCmd = &cobra.Command{
5777
},
5878
}
5979

80+
func logResults(results []kubeval.ValidationResult, success bool) bool {
81+
for _, result := range results {
82+
if len(result.Errors) > 0 {
83+
success = false
84+
log.Warn("The document", result.FileName, "contains an invalid", result.Kind)
85+
for _, desc := range result.Errors {
86+
log.Info("--->", desc)
87+
}
88+
} else {
89+
log.Success("The document", result.FileName, "contains a valid", result.Kind)
90+
}
91+
}
92+
return success
93+
}
94+
6095
// Execute adds all child commands to the root command sets flags appropriately.
6196
// This is called by main.main(). It only needs to happen once to the rootCmd.
6297
func Execute() {

0 commit comments

Comments
 (0)