diff --git a/.gitignore b/.gitignore index 836e00a..51b6df4 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,12 @@ vendor/ *.iml .idea/ + +# Bazel +bazel-bin +bazel-genfiles +bazel-kexpand +bazel-out +bazel-testlogs + + diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index 167a774..0000000 --- a/.gitmodules +++ /dev/null @@ -1,15 +0,0 @@ -[submodule "_vendor/github.com/ghodss/yaml"] - path = _vendor/github.com/ghodss/yaml - url = https://github.com/ghodss/yaml -[submodule "_vendor/github.com/cloudfoundry-incubator/candiedyaml"] - path = _vendor/github.com/cloudfoundry-incubator/candiedyaml - url = https://github.com/cloudfoundry-incubator/candiedyaml -[submodule "_vendor/github.com/golang/glog"] - path = _vendor/github.com/golang/glog - url = https://github.com/golang/glog -[submodule "_vendor/github.com/spf13/cobra"] - path = _vendor/github.com/spf13/cobra - url = https://github.com/spf13/cobra -[submodule "_vendor/github.com/spf13/pflag"] - path = _vendor/github.com/spf13/pflag - url = https://github.com/spf13/pflag diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..551589e --- /dev/null +++ b/.travis.yml @@ -0,0 +1,12 @@ +language: go +os: linux +dist: trusty + +sudo: required + +before_script: + - wget "http://storage.googleapis.com/bazel-apt/pool/jdk1.8/b/bazel/bazel_0.4.0_amd64.deb" + - sudo dpkg --force-all -i bazel_0.4.0_amd64.deb + +script: + - make ci \ No newline at end of file diff --git a/BUILD b/BUILD new file mode 100644 index 0000000..d99ae38 --- /dev/null +++ b/BUILD @@ -0,0 +1,8 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load("@io_bazel_rules_go//go:def.bzl", "go_prefix") + +go_prefix("github.com/kopeio/kexpand") + diff --git a/Makefile b/Makefile index 52b1f51..d4957fe 100644 --- a/Makefile +++ b/Makefile @@ -1,3 +1,6 @@ +# TODO: Move entirely to bazel? +.PHONY: images + all: gocode DOCKER_REGISTRY=kopeio @@ -6,24 +9,36 @@ ifndef VERSION endif gocode: - GO15VENDOREXPERIMENT=1 go install -ldflags "-X main.BuildVersion=${VERSION}" github.com/kopeio/kexpand + GO15VENDOREXPERIMENT=1 go install -ldflags "-X main.BuildVersion=${VERSION}" github.com/kopeio/kexpand/cmd/... gofmt: - gofmt -w -s main.go gofmt -w -s cmd +syncdeps: + rsync -avz _vendor/ vendor/ -builder-image: - docker build -f images/kexpand-builder/Dockerfile -t kexpand-builder . - -build-in-docker: builder-image - docker run -it -v `pwd`:/src kexpand-builder /onbuild.sh -image: build-in-docker - docker build -t ${DOCKER_REGISTRY}/kexpand -f images/kexpand/Dockerfile . +# -------------------------------------------------- +# Docker images -push: image +push: images docker push ${DOCKER_REGISTRY}/kexpand -syncdeps: - rsync -avz _vendor/ vendor/ +images: + bazel run //images:kexpand ${DOCKER_REGISTRY}/kexpand + + +# -------------------------------------------------- +# Continuous integration targets + +ci: images test govet + echo "Done" + +govet: + go vet \ + github.com/kopeio/kexpand/cmd/... + +test: + bazel test //... + #go test github.com/kopeio/kexpand/cmd/... + diff --git a/WORKSPACE b/WORKSPACE new file mode 100644 index 0000000..934a0e5 --- /dev/null +++ b/WORKSPACE @@ -0,0 +1,61 @@ +git_repository( + name = "io_bazel_rules_go", + remote = "https://github.com/bazelbuild/rules_go.git", + tag = "0.3.0", +) + +load("@io_bazel_rules_go//go:def.bzl", "go_repositories", "new_go_repository") + +go_repositories() + +# for building docker base images +debs = ( + ( + "busybox_deb", + "51651980a993b02c8dc663a5539a4d83704e56c2fed93dd8d1b2580e61319af5", + "http://ftp.us.debian.org/debian/pool/main/b/busybox/busybox-static_1.22.0-19_amd64.deb", + ), + ( + "libc_deb", + "ee4d9dea08728e2c2bbf43d819c3c7e61798245fab4b983ae910865980f791ad", + "http://ftp.us.debian.org/debian/pool/main/g/glibc/libc6_2.19-18+deb8u6_amd64.deb", + ) +) + +[http_file( + name = name, + sha256 = sha256, + url = url, +) for name, sha256, url in debs] + + +new_go_repository( + name = "com_github_golang_glog", + importpath = "github.com/golang/glog", + commit = "23def4e6c14b4da8ac2ed8007337bc5eb5007998", +) + +new_go_repository( + name = "com_github_spf13_cobra", + importpath = "github.com/spf13/cobra", + commit = "dbb7c2d02e284aa24423e3e6e105969dffe6e4d6", +) + +new_go_repository( + name = "com_github_spf13_pflag", + importpath = "github.com/spf13/pflag", + commit = "1560c1005499d61b80f865c04d39ca7505bf7f0b", +) + +new_go_repository( + name = "com_github_ghodss_yaml", + importpath = "github.com/ghodss/yaml", + commit = "bea76d6a4713e18b7f5321a2b020738552def3ea", +) + + +new_go_repository( + name = "in_gopkg_yaml_v2", + importpath = "gopkg.in/yaml.v2", + commit = "a5b47d31c556af34a302ce5d659e6fea44d90de0", +) diff --git a/_vendor/github.com/cloudfoundry-incubator/candiedyaml b/_vendor/github.com/cloudfoundry-incubator/candiedyaml deleted file mode 160000 index 99c3df8..0000000 --- a/_vendor/github.com/cloudfoundry-incubator/candiedyaml +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 99c3df83b51532e3615f851d8c2dbb638f5313bf diff --git a/_vendor/github.com/ghodss/yaml b/_vendor/github.com/ghodss/yaml deleted file mode 160000 index aa0c862..0000000 --- a/_vendor/github.com/ghodss/yaml +++ /dev/null @@ -1 +0,0 @@ -Subproject commit aa0c862057666179de291b67d9f093d12b5a8473 diff --git a/_vendor/github.com/golang/glog b/_vendor/github.com/golang/glog deleted file mode 160000 index 23def4e..0000000 --- a/_vendor/github.com/golang/glog +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 23def4e6c14b4da8ac2ed8007337bc5eb5007998 diff --git a/_vendor/github.com/spf13/cobra b/_vendor/github.com/spf13/cobra deleted file mode 160000 index dbb7c2d..0000000 --- a/_vendor/github.com/spf13/cobra +++ /dev/null @@ -1 +0,0 @@ -Subproject commit dbb7c2d02e284aa24423e3e6e105969dffe6e4d6 diff --git a/_vendor/github.com/spf13/pflag b/_vendor/github.com/spf13/pflag deleted file mode 160000 index 1560c10..0000000 --- a/_vendor/github.com/spf13/pflag +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 1560c1005499d61b80f865c04d39ca7505bf7f0b diff --git a/cmd/kexpand/BUILD b/cmd/kexpand/BUILD new file mode 100644 index 0000000..098cba8 --- /dev/null +++ b/cmd/kexpand/BUILD @@ -0,0 +1,21 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_binary", + "go_test", +) + +go_binary( + name = "kexpand", + srcs = ["expand.go", "main.go", "root.go", "version.go"], + deps = [ + "@com_github_golang_glog//:go_default_library", + "@com_github_ghodss_yaml//:go_default_library", + "@com_github_spf13_pflag//:go_default_library", + "@com_github_spf13_cobra//:go_default_library", + "//pkg/expand:go_default_library", + ], +) diff --git a/cmd/expand.go b/cmd/kexpand/expand.go similarity index 66% rename from cmd/expand.go rename to cmd/kexpand/expand.go index 6953660..e60297c 100644 --- a/cmd/expand.go +++ b/cmd/kexpand/expand.go @@ -1,16 +1,14 @@ -package cmd +package main import ( "fmt" - "io/ioutil" - "os" - "regexp" - "strings" - - "encoding/base64" "github.com/ghodss/yaml" "github.com/golang/glog" "github.com/spf13/cobra" + "io/ioutil" + "os" + "strings" + "github.com/kopeio/kexpand/pkg/expand" ) type ExpandCmd struct { @@ -70,56 +68,9 @@ func (c *ExpandCmd) Run(args []string) error { return fmt.Errorf("expected exactly one argument, a path to a file to expand") } - expanded := src - - { - // All - expr := `\$(\({1,2})([[:alnum:]_\.\-]+)(\|base64)?\){1,2}|(\{{2})([[:alnum:]_\.\-]+)(\|base64)?\}{2}` - re := regexp.MustCompile(expr) - expandFunction := func(match []byte) []byte { - re := regexp.MustCompile(expr) - - matchStr := string(match[:]) - result := re.FindStringSubmatch(matchStr) - - if result[0] != matchStr { - glog.Fatalf("Unexpected match: %q", matchStr) - } - - if result[2] == "" && result[5] == "" { - glog.Fatalf("No variable defined within: %q", matchStr) - } - - key := result[2] + result[5] - replacement := values[key] - - if replacement == nil { - err = fmt.Errorf("Key not found: %q", key) - return match - } - - if (result[3] + result[6]) == "|base64" { - replacement = base64.StdEncoding.EncodeToString([]byte(replacement.(string))) - } - - var s string - delim := result[1] + result[4] - switch len(delim) { - case 1: - s = fmt.Sprintf("\"%v\"", replacement) - case 2: - s = fmt.Sprintf("%v", replacement) - default: - glog.Fatalf("Unexpected delimiter %q count: %q", delim, len(delim)) - } - - return []byte(s) - } - - expanded = re.ReplaceAllFunc(expanded, expandFunction) - if err != nil { - return err - } + expanded, err := expand.DoExpand(src, values) + if err != nil { + return err } _, err = os.Stdout.Write(expanded) diff --git a/cmd/kexpand/main.go b/cmd/kexpand/main.go new file mode 100644 index 0000000..736ef31 --- /dev/null +++ b/cmd/kexpand/main.go @@ -0,0 +1,5 @@ +package main + +func main() { + Execute() +} diff --git a/cmd/root.go b/cmd/kexpand/root.go similarity index 97% rename from cmd/root.go rename to cmd/kexpand/root.go index b3bbe1a..1f64ec4 100644 --- a/cmd/root.go +++ b/cmd/kexpand/root.go @@ -1,11 +1,10 @@ -package cmd +package main import ( goflag "flag" "fmt" - "os" - "github.com/spf13/cobra" + "os" ) type RootCmd struct { diff --git a/cmd/version.go b/cmd/kexpand/version.go similarity index 98% rename from cmd/version.go rename to cmd/kexpand/version.go index e762d0a..a226ffa 100644 --- a/cmd/version.go +++ b/cmd/kexpand/version.go @@ -1,4 +1,4 @@ -package cmd +package main import ( "fmt" diff --git a/doc.go b/doc.go new file mode 100644 index 0000000..1ea745c --- /dev/null +++ b/doc.go @@ -0,0 +1,2 @@ +// Package kexpand is the parent package for the kexpand tool +package kexpand diff --git a/images/BUILD b/images/BUILD new file mode 100644 index 0000000..0edecd1 --- /dev/null +++ b/images/BUILD @@ -0,0 +1,35 @@ +package(default_visibility = ["//visibility:public"]) + +load("@bazel_tools//tools/build_defs/docker:docker.bzl", "docker_build") + +docker_build( + name = "busybox", + debs = [ + "@busybox_deb//file", + ], + symlinks = { + "/bin/sh": "/bin/busybox", + "/usr/bin/busybox": "/bin/busybox", + "/usr/sbin/busybox": "/bin/busybox", + "/sbin/busybox": "/bin/busybox", + }, +) + +docker_build( + name = "busybox-libc", + base = ":busybox", + debs = [ + "@libc_deb//file", + ], +) + +docker_build( + name = "kexpand", + base = ":busybox-libc", + cmd = ["/kexpand"], + files = [ + "//cmd/kexpand", + ], + directory = "/usr/bin/", + repository = "kopeio", +) diff --git a/main.go b/main.go deleted file mode 100644 index b458ceb..0000000 --- a/main.go +++ /dev/null @@ -1,9 +0,0 @@ -package main - -import ( - "github.com/kopeio/kexpand/cmd" -) - -func main() { - cmd.Execute() -} diff --git a/pkg/expand/BUILD b/pkg/expand/BUILD new file mode 100644 index 0000000..c8a807c --- /dev/null +++ b/pkg/expand/BUILD @@ -0,0 +1,25 @@ +package(default_visibility = ["//visibility:public"]) + +licenses(["notice"]) + +load( + "@io_bazel_rules_go//go:def.bzl", + "go_library", + "go_test" +) + +go_library( + name = "go_default_library", + srcs = [ + "expand.go", + ], + deps = [ + "@com_github_golang_glog//:go_default_library", + ], +) + +go_test( + name = "go_default_tests", + srcs = ["expand_test.go"], + library = ":go_default_library", +) diff --git a/pkg/expand/expand.go b/pkg/expand/expand.go new file mode 100644 index 0000000..2b11728 --- /dev/null +++ b/pkg/expand/expand.go @@ -0,0 +1,62 @@ +package expand + +import ( + "encoding/base64" + "fmt" + "github.com/golang/glog" + "regexp" +) + +func DoExpand(src []byte, values map[string]interface{}) ([]byte, error) { + var err error + + // All + expr := `\$(\({1,2})([[:alnum:]_\.\-]+)(\|base64)?\){1,2}|(\{{2})([[:alnum:]_\.\-]+)(\|base64)?\}{2}` + re := regexp.MustCompile(expr) + expandFunction := func(match []byte) []byte { + re := regexp.MustCompile(expr) + + matchStr := string(match[:]) + result := re.FindStringSubmatch(matchStr) + + if result[0] != matchStr { + glog.Fatalf("Unexpected match: %q", matchStr) + } + + if result[2] == "" && result[5] == "" { + glog.Fatalf("No variable defined within: %q", matchStr) + } + + key := result[2] + result[5] + replacement := values[key] + + if replacement == nil { + err = fmt.Errorf("Key not found: %q", key) + return match + } + + if (result[3] + result[6]) == "|base64" { + replacement = base64.StdEncoding.EncodeToString([]byte(replacement.(string))) + } + + var s string + delim := result[1] + result[4] + switch len(delim) { + case 1: + s = fmt.Sprintf("\"%v\"", replacement) + case 2: + s = fmt.Sprintf("%v", replacement) + default: + glog.Fatalf("Unexpected delimiter %q count: %q", delim, len(delim)) + } + + return []byte(s) + } + + expanded := re.ReplaceAllFunc(src, expandFunction) + if err != nil { + return nil, err + } + + return expanded, nil +} \ No newline at end of file diff --git a/pkg/expand/expand_test.go b/pkg/expand/expand_test.go new file mode 100644 index 0000000..a6d482b --- /dev/null +++ b/pkg/expand/expand_test.go @@ -0,0 +1,57 @@ +package expand + +import "testing" + +func Test_Find(t *testing.T) { + type test struct { + input string + expected string + values map[string]interface{} + } + grid := []test{ + { + input: "hello", + expected: "hello", + }, + { + input: "hello $((who))", + expected: "hello world", + values: map[string]interface{}{ + "who": "world", + }, + }, + { + input: "hello {{who}}", + expected: "hello world", + values: map[string]interface{}{ + "who": "world", + }, + }, + { + input: "hello $(who)", + expected: "hello \"world\"", + values: map[string]interface{}{ + "who": "world", + }, + }, + { + input: "hello $((who|base64))", + expected: "hello d29ybGQ=", + values: map[string]interface{}{ + "who": "world", + }, + }, + } + + for i, spec := range grid { + actual, err := DoExpand([]byte(spec.input), spec.values) + if err != nil { + t.Errorf("doExpand unexpected error in test %d: %v", i, err) + } + + if string(actual) != spec.expected { + t.Errorf("unexpected expansion; expected=%q; actual=%q", spec.expected, string(actual)) + } + } + +}