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

Feature: diff/sync commands - read from stdin - resolves #8 #11

Closed
Closed
3 changes: 2 additions & 1 deletion cmd/diff.go
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ that will be created or updated or deleted.
func init() {
rootCmd.AddCommand(diffCmd)
diffCmd.Flags().StringVarP(&diffCmdKongStateFile,
"state", "s", "kong.yaml", "file containing Kong's configuration.")
"state", "s", "kong.yaml", "file containing Kong's configuration. "+
"Use '-' to read from stdin.")
diffCmd.Flags().BoolVar(&dumpConfig.SkipConsumers, "skip-consumers",
false, "do not diff consumers or "+
"any plugins associated with consumers")
Expand Down
3 changes: 2 additions & 1 deletion cmd/sync.go
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@ to get Kong's state in sync with the input state.`,
func init() {
rootCmd.AddCommand(syncCmd)
syncCmd.Flags().StringVarP(&syncCmdKongStateFile,
"state", "s", "kong.yaml", "file containing Kong's configuration.")
"state", "s", "kong.yaml", "file containing Kong's configuration. "+
"Use '-' to read from stdin.")
syncCmd.Flags().BoolVar(&dumpConfig.SkipConsumers, "skip-consumers",
false, "do not diff consumers or "+
"any plugins associated with consumers")
Expand Down
9 changes: 8 additions & 1 deletion file/reader.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package file
import (
"fmt"
"io/ioutil"
"os"
"strconv"

"github.com/hbagdi/deck/counter"
Expand Down Expand Up @@ -223,7 +224,13 @@ func GetStateFromFile(filename string) (*state.KongState, error) {
func readFile(kongStateFile string) (*fileStructure, error) {

var s fileStructure
b, err := ioutil.ReadFile(kongStateFile)
var b []byte
var err error
if kongStateFile == "-" {
b, err = ioutil.ReadAll(os.Stdin)
} else {
b, err = ioutil.ReadFile(kongStateFile)
}
if err != nil {
return nil, err
}
Expand Down
78 changes: 78 additions & 0 deletions file/reader_test.go
Original file line number Diff line number Diff line change
@@ -1,8 +1,13 @@
package file

import (
"bytes"
"io/ioutil"
"os"
"reflect"
"testing"

"github.com/stretchr/testify/assert"
)

func Test_ensureJSON(t *testing.T) {
Expand Down Expand Up @@ -32,3 +37,76 @@ func Test_ensureJSON(t *testing.T) {
})
}
}

func TestReadKongStateFromStdinFailsToParseText(t *testing.T) {
var filename = "-"
assert := assert.New(t)
assert.Equal("-", filename)

var content bytes.Buffer
content.Write([]byte("hunter2\n"))

tmpfile, err := ioutil.TempFile("", "example")
if err != nil {
panic(err)
}
defer os.Remove(tmpfile.Name())

if _, err := tmpfile.Write(content.Bytes()); err != nil {
panic(err)
}

if _, err := tmpfile.Seek(0, 0); err != nil {
panic(err)
}

oldStdin := os.Stdin
defer func() { os.Stdin = oldStdin }() // Restore original Stdin

os.Stdin = tmpfile

ks, err := GetStateFromFile(filename)
assert.NotNil(err)
assert.Empty(ks)
}

func TestReadKongStateFromStdin(t *testing.T) {
var filename = "-"
assert := assert.New(t)
assert.Equal("-", filename)

var content bytes.Buffer
content.Write([]byte("services:\n- host: test.com\n name: test service\n"))

tmpfile, err := ioutil.TempFile("", "example")
if err != nil {
panic(err)
}
defer os.Remove(tmpfile.Name())

if _, err := tmpfile.Write(content.Bytes()); err != nil {
panic(err)
}

if _, err := tmpfile.Seek(0, 0); err != nil {
panic(err)
}

oldStdin := os.Stdin
defer func() { os.Stdin = oldStdin }() // Restore original Stdin

os.Stdin = tmpfile

ks, err := GetStateFromFile(filename)
assert.NotNil(ks)
assert.Nil(err)

services, err := ks.Services.GetAll()
if err != nil {
panic(err)
}
assert.Equal("test.com", *services[0].Host)
assert.NotEqual("not.the.same.as.test.com", *services[0].Host)
assert.Equal("test service", *services[0].Name)
assert.NotEqual("not the same as 'test service'", *services[0].Name)
}
7 changes: 6 additions & 1 deletion file/writer.go
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package file

import (
"fmt"
"io/ioutil"
"sort"
"strings"
Expand Down Expand Up @@ -165,7 +166,11 @@ func KongStateToFile(kongState *state.KongState, filename string) error {
})

c, err := yaml.Marshal(file)
err = ioutil.WriteFile(filename, c, 0600)
if filename == "-" {
_, err = fmt.Print(string(c))
} else {
err = ioutil.WriteFile(filename, c, 0600)
}
if err != nil {
return err
}
Expand Down
112 changes: 112 additions & 0 deletions file/writer_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package file

import (
"bytes"
"fmt"
"io"
"log"
"os"
"sync"
"testing"

"github.com/stretchr/testify/assert"

"github.com/hbagdi/deck/state"
"github.com/hbagdi/go-kong/kong"
)

func captureOutput(f func()) string {
reader, writer, err := os.Pipe()
if err != nil {
panic(err)
}
stdout := os.Stdout
stderr := os.Stderr
defer func() {
os.Stdout = stdout
os.Stderr = stderr
log.SetOutput(os.Stderr)
}()
os.Stdout = writer
os.Stderr = writer

log.SetOutput(writer)
out := make(chan string)
wg := new(sync.WaitGroup)
wg.Add(1)
go func() {
var buf bytes.Buffer
wg.Done()
io.Copy(&buf, reader)
out <- buf.String()
}()
wg.Wait()
f()
writer.Close()
return <-out
}
func TestWriteKongStateToStdoutEmptyState(t *testing.T) {
var ks, _ = state.NewKongState()
var filename = "-"
assert := assert.New(t)
assert.Equal("-", filename)
assert.NotEmpty(t, ks)
output := captureOutput(func() {
KongStateToFile(ks, filename)
})
assert.Equal("{}\n", output)

}
func TestWriteKongStateToStdoutStateWithOneService(t *testing.T) {
var ks, _ = state.NewKongState()
var filename = "-"
assert := assert.New(t)
var service state.Service
service.ID = kong.String("first")
service.Host = kong.String("example.com")
service.Name = kong.String("my-service")
ks.Services.Add(service)
output := captureOutput(func() {
KongStateToFile(ks, filename)
})
fmt.Print(service.Host)
expected := fmt.Sprintf("services:\n- host: %s\n name: %s\n", *service.Host, *service.Name)
assert.Equal(expected, output)

}
func TestWriteKongStateToStdoutStateWithOneServiceOneRoute(t *testing.T) {
var ks, _ = state.NewKongState()
var filename = "-"
assert := assert.New(t)
var service state.Service
service.ID = kong.String("first")
service.Host = kong.String("example.com")
service.Name = kong.String("my-service")
ks.Services.Add(service)

var route state.Route
route.Name = kong.String("my-route")
route.ID = kong.String("first")
route.Hosts = kong.StringSlice("example.com", "demo.example.com")
route.Service = &kong.Service{
ID: kong.String(*service.ID),
Name: kong.String(*service.Name),
}

ks.Routes.Add(route)

output := captureOutput(func() {
KongStateToFile(ks, filename)
})
fmt.Print(service.Host)
expected := fmt.Sprintf(`services:
- host: %s
name: %s
routes:
- hosts:
- %s
- %s
name: %s
`, *service.Host, *service.Name, *route.Hosts[0], *route.Hosts[1], *route.Name)
assert.Equal(expected, output)
}
3 changes: 2 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ require (
github.com/yudai/gojsondiff v1.0.0
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 // indirect
github.com/yudai/pp v2.0.1+incompatible // indirect
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 // indirect
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 // indirect
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 // indirect
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
gopkg.in/yaml.v2 v2.2.2
)
8 changes: 6 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -86,15 +86,19 @@ github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82 h1:BHyfKlQyqbsFN5p3Ifn
github.com/yudai/golcs v0.0.0-20170316035057-ecda9a501e82/go.mod h1:lgjkn3NuSvDfVJdfcVVdX+jpBxNmX4rDAzaS45IcYoM=
github.com/yudai/pp v2.0.1+incompatible h1:Q4//iY4pNF6yPLZIigmvcl7k/bPgrcTPIFIcmawg5bI=
github.com/yudai/pp v2.0.1+incompatible/go.mod h1:PuxR/8QJ7cyCkFp/aUDS+JY727OFEZkTdatxwunjIkc=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd h1:nTDtHvHSdCn1m6ITfMRqtOd/9+7a3s8RBNOZ3eYZzJA=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992 h1:BH3eQWeGbwRU2+wxxuuPOdFBmaiBH81O8BugSjHeTFg=
golang.org/x/sys v0.0.0-20180906133057-8cf3aee42992/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35 h1:YAFjXN64LMvktoUZH9zgY4lGc/msGN7HQfoSuKCgaDU=
golang.org/x/sys v0.0.0-20181128092732-4ed8d59d0b35/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67 h1:1Fzlr8kkDLQwqMP8GxrhptBLqZG/EDpiATneiZHY998=
golang.org/x/sys v0.0.0-20190405154228-4b34438f7a67/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
Expand Down