diff --git a/.gitignore b/.gitignore index 5f924b621b8924d90e43e0f57d12b3b3003efac2..cec487f155b85442477e62a03271809a062bbe7c 100644 --- a/.gitignore +++ b/.gitignore @@ -26,4 +26,7 @@ _testmain.go vendor/*/ .env + +coverage.* + vaultenv diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5bc29b5edbec90844618cc49c31f14b8e6ad4a20..0a720300837da7564ab7e0c0bbb0e1e3cfb442e2 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,3 +9,36 @@ include: file: 'SonarQube.gitlab-ci.yml' - project: 'gitlab/templates' file: 'Go-CLI.gitlab-ci.yml' + +go-test: + image: golang:$GOLANG_VERSION + stage: test + rules: + - if: $CI_COMMIT_BRANCH + cache: + key: + files: + - go.mod + - go.sum + paths: + - .cache + policy: pull + services: + - name: hashicorp/vault:latest + alias: vault + command: ["server","-dev-kv-v1","-dev-root-token-id","00000000-0000-0000-0000-000000000000"] + script: + - | + export VAULT_ADDR=http://vault:8200 + export VAULT_TOKEN=00000000-0000-0000-0000-000000000000 + export GOPATH=${CI_PROJECT_DIR}/.cache + export PATH=${PATH}:${GOPATH}/bin + go test -covermode=count -coverprofile=coverage.txt $(go list ./... | grep -v /vendor/) + (gocover-cobertura < coverage.txt > coverage.xml) || true + artifacts: + paths: + - coverage.txt + reports: + coverage_report: + coverage_format: cobertura + path: coverage.xml diff --git a/.vscode/tasks.json b/.vscode/tasks.json index a2883d2b5e481f7cb27d0c2fa8005c96122eaa47..ad8b6c72237e1bfa2e6b20478105c49e8fd3c6ed 100644 --- a/.vscode/tasks.json +++ b/.vscode/tasks.json @@ -40,5 +40,14 @@ "reveal": "silent" } }, + { + "label": "Start vault dev server", + "type": "shell", + "command": "docker run --rm -it -p 8200:8200 --name vault-dev hashicorp/vault:latest server -dev-kv-v1 -dev-root-token-id 00000000-0000-0000-0000-000000000000", + "group": { + "kind": "test", + "isDefault": false + } + }, ] } diff --git a/cmd/root.go b/cmd/root.go index 6035e42138a767ec60707e0d09bface251c51010..1961d998af882d2a8326a7d32a1611d0cf8520a8 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -13,6 +13,7 @@ var RootCmd = &cobra.Command{ Short: "Root Short", Long: `Root Long`, DisableAutoGenTag: true, + SilenceUsage: true, } func init() { @@ -20,8 +21,8 @@ func init() { RootCmd.PersistentFlags().StringP("addr", "a", "http://127.0.0.1:8200", "Address to the vault server") RootCmd.PersistentFlags().StringP("token", "t", "", "Vault access token") viper.SetEnvPrefix("VAULT") - viper.BindPFlag("ADDR", RootCmd.PersistentFlags().Lookup("addr")) - viper.BindPFlag("TOKEN", RootCmd.PersistentFlags().Lookup("token")) + _ = viper.BindPFlag("ADDR", RootCmd.PersistentFlags().Lookup("addr")) + _ = viper.BindPFlag("TOKEN", RootCmd.PersistentFlags().Lookup("token")) } func initEnv() { diff --git a/cmd/root_test.go b/cmd/root_test.go new file mode 100644 index 0000000000000000000000000000000000000000..d1b92217b6badfd763f9c87ebf5898df947d9dd4 --- /dev/null +++ b/cmd/root_test.go @@ -0,0 +1,57 @@ +package cmd + +import ( + "bytes" + "io" + "os" + "strings" + "sync" + "testing" + + _ "gitlab.hedenstroem.com/go/vaultenv/testing" +) + +func execute(t *testing.T, args string, input io.Reader) (string, error) { + + osStdout := os.Stdout // keep backup of the real stdout + defer func() { os.Stdout = osStdout }() // restore the real stdout + r, w, _ := os.Pipe() + os.Stdout = w + + output := new(bytes.Buffer) + + var wg sync.WaitGroup + wg.Add(1) + go func() { + _, err := io.Copy(output, r) + if err != nil { + t.Error(err) + } + wg.Done() + }() + + RootCmd.SetIn(input) + RootCmd.SetOut(output) // usage messages + RootCmd.SetErr(output) // error messages + RootCmd.SetArgs(strings.Split(args, " ")) + err := RootCmd.Execute() + + w.Close() + wg.Wait() + + return output.String(), err +} + +func TestWriteCmd(t *testing.T) { + _, err := execute(t, "write secret/test hello world", nil) + if err != nil { + t.Fatal(err) + } + out, err := execute(t, "read secret/test", nil) + if err != nil { + t.Fatal(err) + } + if strings.Contains(out, "\"hello\": \"world\"") == false { + t.Errorf("expected \"hello\": \"world\", got '%s'", out) + } +} diff --git a/main.go b/main.go index ff9a66f5612aaada43c2e6d205808da1fb6a4ab0..f5b508dc95e12a4e388ac9c726f4039993745a42 100644 --- a/main.go +++ b/main.go @@ -20,8 +20,11 @@ package main -import "gitlab.hedenstroem.com/go/vaultenv/cmd" +import ( + "github.com/spf13/cobra" + "gitlab.hedenstroem.com/go/vaultenv/cmd" +) func main() { - cmd.RootCmd.Execute() + cobra.CheckErr(cmd.RootCmd.Execute()) } diff --git a/sonar-project.properties b/sonar-project.properties index ee63546e3b6e2ef8d5d6e39b2cacadb5a86e3887..ac9921a3acd7b783764edf4d37b8974559d265e9 100644 --- a/sonar-project.properties +++ b/sonar-project.properties @@ -1 +1,9 @@ sonar.projectKey=go_vaultenv_AXyPJ7AsH35cfvcLwDFS + +sonar.sources=. +sonar.exclusions=**/*_test.go + +sonar.tests=. +sonar.test.inclusions=**/*_test.go + +sonar.go.coverage.reportPaths=coverage.txt diff --git a/testing/testing.go b/testing/testing.go new file mode 100644 index 0000000000000000000000000000000000000000..413cb4805906a1ee32f744bb5afbe42b3c857ecc --- /dev/null +++ b/testing/testing.go @@ -0,0 +1,16 @@ +package testing + +import ( + "os" + "path" + "runtime" +) + +func init() { + _, filename, _, _ := runtime.Caller(0) + dir := path.Join(path.Dir(filename), "..") + err := os.Chdir(dir) + if err != nil { + panic(err) + } +} diff --git a/vault/http.go b/vault/http.go index d3d76db7be1db3a782ef4ab95e5e3d596a386058..4628ab4fe22a4f368289a1ce9250aaadfecfc1da 100644 --- a/vault/http.go +++ b/vault/http.go @@ -40,7 +40,10 @@ func GetSecret(path string) (data map[string]interface{}, err error) { case http.StatusOK: var parsed map[string]interface{} defer res.Body.Close() - json.NewDecoder(res.Body).Decode(&parsed) + err = json.NewDecoder(res.Body).Decode(&parsed) + if err != nil { + return + } data = parsed["data"].(map[string]interface{}) case http.StatusNoContent: data = make(map[string]interface{}) @@ -52,7 +55,10 @@ func GetSecret(path string) (data map[string]interface{}, err error) { default: var parsed map[string]interface{} defer res.Body.Close() - json.NewDecoder(res.Body).Decode(&parsed) + err = json.NewDecoder(res.Body).Decode(&parsed) + if err != nil { + return + } err = &Error{ Status: res.StatusCode, Message: fmt.Sprint(parsed["errors"]),