Skip to content

Commit

Permalink
Merge pull request #19 from garethr/stdin-support
Browse files Browse the repository at this point in the history
Support input over stdin for validating config files
  • Loading branch information
garethr authored Aug 2, 2017
2 parents b863eb4 + 4400068 commit dd168ed
Show file tree
Hide file tree
Showing 3 changed files with 78 additions and 20 deletions.
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,18 @@ $ echo $?
1
```

Alternatively kubeval can also take input via `stdin` which can make using
it as part of an automated pipeline easier.

```
$ cat my-invalid-rc.yaml | kubeval
The document my-invalid-rc.yaml contains an invalid ReplicationController
--> spec.replicas: Invalid type. Expected: integer, given: string
$ echo $?
1
```


## Why?

* If you're writing Kubernetes configuration files by hand it is useful
Expand Down
11 changes: 11 additions & 0 deletions acceptance.bats
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@
[ "$output" = "The document fixtures/valid.yaml contains a valid ReplicationController" ]
}

@test "Pass when parsing a valid Kubernetes config YAML file on stdin" {
run bash -c "cat fixtures/valid.yaml | kubeval"
[ "$status" -eq 0 ]
[ "$output" = "The document stdin contains a valid ReplicationController" ]
}

@test "Pass when parsing a valid Kubernetes config JSON file" {
run kubeval fixtures/valid.json
[ "$status" -eq 0 ]
Expand Down Expand Up @@ -46,6 +52,11 @@
[ "$status" -eq 1 ]
}

@test "Fail when parsing an invalid Kubernetes config file on stdin" {
run bash -c "cat fixtures/invalid.yaml | kubeval"
[ "$status" -eq 1 ]
}

@test "Return relevant error for non-existent file" {
run kubeval fixtures/not-here
[ "$status" -eq 1 ]
Expand Down
75 changes: 55 additions & 20 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package cmd

import (
"bufio"
"bytes"
"io/ioutil"
"os"
"path/filepath"
"runtime"

"github.com/spf13/cobra"

Expand All @@ -21,34 +24,51 @@ var RootCmd = &cobra.Command{
printVersion()
os.Exit(0)
}
if len(args) < 1 {
log.Error("You must pass at least one file as an argument")
os.Exit(1)
}
success := true
for _, fileName := range args {
filePath, _ := filepath.Abs(fileName)
fileContents, err := ioutil.ReadFile(filePath)
if err != nil {
log.Error("Could not open file", fileName)
windowsStdinIssue := false
stat, err := os.Stdin.Stat()
if err != nil {
// Stat() will return an error on Windows in both Powershell and
// console until go1.9 when nothing is passed on stdin.
// See https://github.com/golang/go/issues/14853.
if runtime.GOOS != "windows" {
log.Error(err)
os.Exit(1)
} else {
windowsStdinIssue = true
}
results, err := kubeval.Validate(fileContents, fileName)
}
// We detect whether we have anything on stdin to process
if !windowsStdinIssue && ((stat.Mode() & os.ModeCharDevice) == 0) {
var buffer bytes.Buffer
scanner := bufio.NewScanner(os.Stdin)
for scanner.Scan() {
buffer.WriteString(scanner.Text() + "\n")
}
results, err := kubeval.Validate(buffer.Bytes(), "stdin")
if err != nil {
log.Error(err)
os.Exit(1)
}

for _, result := range results {
if len(result.Errors) > 0 {
success = false
log.Warn("The document", result.FileName, "contains an invalid", result.Kind)
for _, desc := range result.Errors {
log.Info("--->", desc)
}
} else {
log.Success("The document", result.FileName, "contains a valid", result.Kind)
success = logResults(results, success)
} else {
if len(args) < 1 {
log.Error("You must pass at least one file as an argument")
os.Exit(1)
}
for _, fileName := range args {
filePath, _ := filepath.Abs(fileName)
fileContents, err := ioutil.ReadFile(filePath)
if err != nil {
log.Error("Could not open file", fileName)
os.Exit(1)
}
results, err := kubeval.Validate(fileContents, fileName)
if err != nil {
log.Error(err)
os.Exit(1)
}
success = logResults(results, success)
}
}
if !success {
Expand All @@ -57,6 +77,21 @@ var RootCmd = &cobra.Command{
},
}

func logResults(results []kubeval.ValidationResult, success bool) bool {
for _, result := range results {
if len(result.Errors) > 0 {
success = false
log.Warn("The document", result.FileName, "contains an invalid", result.Kind)
for _, desc := range result.Errors {
log.Info("--->", desc)
}
} else {
log.Success("The document", result.FileName, "contains a valid", result.Kind)
}
}
return success
}

// Execute adds all child commands to the root command sets flags appropriately.
// This is called by main.main(). It only needs to happen once to the rootCmd.
func Execute() {
Expand Down

0 comments on commit dd168ed

Please sign in to comment.