Skip to content
This repository has been archived by the owner on Nov 17, 2021. It is now read-only.

Commit

Permalink
Initial version. Implements 'show'
Browse files Browse the repository at this point in the history
Basic skeleton.  Can evaluate jsonnet and show the output as yaml or
json .. and that's about it.
  • Loading branch information
anguslees committed May 12, 2017
1 parent 2f06a35 commit 21ac8db
Show file tree
Hide file tree
Showing 92 changed files with 21,796 additions and 0 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
/kubecfg

# Compiled Object files, Static and Dynamic libs (Shared Objects)
*.o
*.a
Expand Down
17 changes: 17 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
VERSION = dev-$(shell date +%FT%T%z)

GO = go
GO_FLAGS = -ldflags="-X main.version=$(VERSION)"

all: kubecfg

kubecfg:
$(GO) build $(GO_FLAGS) .

test:
$(GO) test ./cmd/...

clean:
$(RM) ./kubecfg

.PHONY: all test clean kubecfg
69 changes: 69 additions & 0 deletions cmd/root.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package cmd

import (
"encoding/json"
goflag "flag"
"path/filepath"

"github.com/golang/glog"
"github.com/spf13/cobra"
jsonnet "github.com/strickyak/jsonnet_cgo"
)

func init() {
RootCmd.PersistentFlags().String("context", "", "The name of the kubeconfig context to use")
RootCmd.PersistentFlags().StringP("jpath", "J", "", "Additional jsonnet library search path")
RootCmd.PersistentFlags().AddGoFlagSet(goflag.CommandLine)
RootCmd.PersistentFlags().Set("logtostderr", "true")
}

// RootCmd is the root of cobra subcommand tree
var RootCmd = &cobra.Command{
Use: "kubecfg",
Short: "Synchronise Kubernetes resources with config files",
SilenceErrors: true,
SilenceUsage: true,
PersistentPreRun: func(cmd *cobra.Command, args []string) {
goflag.CommandLine.Parse([]string{})
glog.CopyStandardLogTo("INFO")
},
}

// JsonnetVM constructs a new jsonnet.VM, according to command line
// flags
func JsonnetVM(cmd *cobra.Command) (*jsonnet.VM, error) {
vm := jsonnet.Make()
flags := cmd.Flags()

jpath, err := flags.GetString("jpath")
if err != nil {
return nil, err
}
for _, p := range filepath.SplitList(jpath) {
glog.V(2).Infoln("Adding jsonnet path", p)
vm.JpathAdd(p)
}

return vm, nil
}

func evalFile(vm *jsonnet.VM, file string) (interface{}, error) {
var err error
jsonstr := ""
if file != "" {
jsonstr, err = vm.EvaluateFile(file)
if err != nil {
return nil, err
}
}

glog.V(4).Infof("jsonnet result is: %s\n", jsonstr)

var jsobj interface{}
err = json.Unmarshal([]byte(jsonstr), &jsobj)
if err != nil {
return nil, err
}

return jsobj, nil
}
63 changes: 63 additions & 0 deletions cmd/show.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package cmd

import (
"encoding/json"
"fmt"

"github.com/spf13/cobra"
"gopkg.in/yaml.v2"
)

func init() {
RootCmd.AddCommand(showCmd)
showCmd.PersistentFlags().StringP("file", "f", "", "Input jsonnet file")
showCmd.MarkFlagFilename("file", "jsonnet", "libsonnet")
showCmd.PersistentFlags().StringP("format", "o", "yaml", "Output format. Supported values are: json, yaml")
}

var showCmd = &cobra.Command{
Use: "show",
Short: "Show expanded resource definitions",
RunE: func(cmd *cobra.Command, args []string) error {
flags := cmd.Flags()
out := cmd.OutOrStdout()

vm, err := JsonnetVM(cmd)
if err != nil {
return err
}
defer vm.Destroy()

file, err := flags.GetString("file")
if err != nil {
return err
}
jsobj, err := evalFile(vm, file)
if err != nil {
return err
}

format, err := flags.GetString("format")
if err != nil {
return err
}
switch format {
case "yaml":
buf, err := yaml.Marshal(jsobj)
if err != nil {
return err
}
out.Write(buf)
case "json":
enc := json.NewEncoder(out)
enc.SetIndent("", " ")
if err := enc.Encode(&jsobj); err != nil {
return err
}
default:
return fmt.Errorf("Unknown --format: %s", format)
}

return nil
},
}
71 changes: 71 additions & 0 deletions cmd/show_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package cmd

import (
"bytes"
"encoding/json"
"path/filepath"
"reflect"
"testing"

"gopkg.in/yaml.v2"
)

func cmdOutput(t *testing.T, args []string) string {
var buf bytes.Buffer
RootCmd.SetOutput(&buf)
defer RootCmd.SetOutput(nil)

t.Log("Running args", args)
RootCmd.SetArgs(args)
if err := RootCmd.Execute(); err != nil {
t.Fatal("command failed:", err)
}

return buf.String()
}

func TestShow(t *testing.T) {
formats := map[string]func(string) (interface{}, error){
"json": func(text string) (ret interface{}, err error) {
err = json.Unmarshal([]byte(text), &ret)
return
},
"yaml": func(text string) (ret interface{}, err error) {
err = yaml.Unmarshal([]byte(text), &ret)
return
},
}

// Use the fact that JSON is also valid YAML ..
expected := `
{
"nil": null,
"bool": true,
"number": 42,
"string": "bar",
"array": ["one", 2, [3]],
"object": {"foo": "bar"}
}
`

for format, parser := range formats {
expected, err := parser(expected)
if err != nil {
t.Errorf("error parsing *expected* value: %s", err)
}

output := cmdOutput(t, []string{"show",
"-J", filepath.FromSlash("../testdata/lib"),
"-f", filepath.FromSlash("../testdata/test.jsonnet"),
"-o", format,
})

t.Log("output is", output)
actual, err := parser(output)
if err != nil {
t.Errorf("error parsing output of format %s: %s", format, err)
} else if !reflect.DeepEqual(expected, actual) {
t.Errorf("format %s expected != actual: %s != %s", format, expected, actual)
}
}
}
25 changes: 25 additions & 0 deletions cmd/version.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package cmd

import (
"fmt"

"github.com/spf13/cobra"
jsonnet "github.com/strickyak/jsonnet_cgo"
)

func init() {
RootCmd.AddCommand(versionCmd)
}

// Version is overridden by main
var Version = "(dev build)"

var versionCmd = &cobra.Command{
Use: "version",
Short: "Print version information",
Run: func(cmd *cobra.Command, args []string) {
out := cmd.OutOrStdout()
fmt.Fprintln(out, "kubecfg version:", Version)
fmt.Fprintln(out, "jsonnet version:", jsonnet.Version())
},
}
15 changes: 15 additions & 0 deletions cmd/version_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package cmd

import (
"regexp"
"testing"
)

func TestVersion(t *testing.T) {
output := cmdOutput(t, []string{"version"})

// Also a good smoke-test that libjsonnet linked successfully
if !regexp.MustCompile(`jsonnet version: v[\d.]+`).MatchString(output) {
t.Error("Failed to find jsonnet version in:", output)
}
}
21 changes: 21 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package main

import (
"fmt"
"os"

"github.com/anguslees/kubecfg/cmd"
)

// Version is overridden using `-X main.version` during release builds
var version = "(dev build)"

func main() {
cmd.Version = version

if err := cmd.RootCmd.Execute(); err != nil {
fmt.Println("got error")
fmt.Println("Error:", err)
os.Exit(1)
}
}
10 changes: 10 additions & 0 deletions testdata/lib/test.libsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
nil: null,
bool: true,
number: 42,
string: "foo",
array: ["one", 2, [3]],
object: {
foo: "bar",
},
}
5 changes: 5 additions & 0 deletions testdata/test.jsonnet
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
local test = import "test.libsonnet";

test {
string: "bar",
}
Loading

0 comments on commit 21ac8db

Please sign in to comment.