diff --git a/.devtool.config.example b/.devtool.config.example new file mode 100644 index 0000000000000000000000000000000000000000..908064eaaa7fcc1e479daeac668ef1ec54eab885 --- /dev/null +++ b/.devtool.config.example @@ -0,0 +1,20 @@ +{ + "name": "example", + "language": "java", + "deployables": [ + { + "name": "wildfly", + "dockerfile": "", + "chart": "cm/eio-swe-service", + "chartVersion": "v0.3.13", + "localConfig": "config/local.yml" + } + ], + "localEnvVars": [ + "OAUTH_CLIENT_ID" + ], + "localSecrets": [ + "OAUTH_CLIENT_SECRET", + "EXT_DB_PASSWORD" + ] +} \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..700353b37dd8e751d23fb3aaf994d5db4f2ec619 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.go-project.mk +devtool +.devtool.config diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..4b8d292f72568603d71db0a3e99a27d367bb6ec3 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,6 @@ +include: + - project: k8s/gitlab-ci-templates + ref: master + file: eio-swe-golang-flux/.gitlab-ci.yml +variables: + GO_LIB: "true" diff --git a/Makefile b/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..31fe6c1aeac96d39505842907daff21159140dc7 --- /dev/null +++ b/Makefile @@ -0,0 +1,9 @@ +# Define where your included makefile lives +GITURL = git.psu.edu/k8s/gitlab-ci-templates.git +GITPROJECT = eio-swe-golang-flux +GITFILE = .go-project.mk + +# Override your docker tag here, otherwise it'll use your PWD +# DOCKER_TAG = everything-is-awesome + +-include $(shell test -f .go-project.mk || git archive --remote=ssh://git@$(GITURL) HEAD:$(GITPROJECT) $(GITFILE) | tar -x ; echo .go-project.mk) diff --git a/cmd/build.go b/cmd/build.go new file mode 100644 index 0000000000000000000000000000000000000000..7aaac70e0eaecf45e4d01e5bea97732698f6f062 --- /dev/null +++ b/cmd/build.go @@ -0,0 +1,158 @@ +// Copyright © 2019 NAME HERE <EMAIL ADDRESS> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "errors" + "os" + + "git.psu.edu/k8s/devtool/config" + "git.psu.edu/k8s/devtool/environment" + "github.com/fatih/color" + "github.com/google/uuid" + "github.com/spf13/cobra" +) + +var skipCompile bool +var runTests bool +var openServices bool + +// buildCmd represents the build command +var buildCmd = &cobra.Command{ + Use: "build", + Short: "build the application", + Run: func(cmd *cobra.Command, args []string) { + environment.EvalDockerEnv() + + err := environment.Run(true, "minikube", "status") + if err != nil { + color.Red("Minikube is not started... run `devtool start` first.") + return + } + + conf, err := config.New(configFile) + if err != nil { + color.New(color.FgRed).PrintFunc()("Error reading configuration file: %s", configFile) + return + } + + err = build(conf) + if err != nil { + color.Red("Failed to build application.") + return + } + + err = buildDockerAndDeploy(conf) + if err != nil { + color.Red("Failed to build docker image.") + return + } + }, +} + +func init() { + rootCmd.AddCommand(buildCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // buildCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // buildCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + + buildCmd.Flags().BoolVar(&skipCompile, "skip-compile", false, "Skip the compile step") + buildCmd.Flags().BoolVar(&runTests, "tests", false, "Run unit tests during the build phase") + buildCmd.Flags().BoolVar(&openServices, "open", false, "Open browser to the services after deploying") +} + +func build(config config.Config) error { + if skipCompile { + color.Yellow("Skipping Build Step.") + return nil + } + + color.Blue("Building Application") + + switch lang := config.Language; lang { + case "java": + args := []string{"clean", "install", "-Ddependency-check.skip=true"} + if !runTests { + args = append(args, "-DskipTests") + } + environment.RunRequired(true, "mvn", args...) + case "golang": + fallthrough + case "go": + environment.RunRequired(true, "make", "build") + default: + color.New(color.FgRed).PrintFunc()("devtool doesn't support the '%s' language yet.", lang) + return errors.New("Language not supported") + } + return nil +} + +func buildDockerAndDeploy(config config.Config) error { + color.Blue("Building Docker Containers") + + for _, deployable := range config.Deployables { + dockerfile := deployable.Dockerfile + image := deployable.Name + tag, err := uuid.NewRandom() + if err != nil { + color.Red("Failed to generate UUID for docker tag") + return err + } + + dockerTag := image + ":" + tag.String() + + color.New(color.FgGreen).PrintFunc()("Building ", dockerTag) + err = environment.Run(true, "docker", "build", "-t", dockerTag, "-f", dockerfile, ".") + if err != nil { + return err + } + + color.Blue("Installing Chart") + + releaseName := deployable.Name + "-local" + args := []string{"upgrade", releaseName, "--install", deployable.Chart, "--version", deployable.ChartVersion} + if len(config.LocalEnvVars) > 0 { + for _, key := range config.LocalEnvVars { + args = append(args, "--set", "environmentVariables."+key+"="+os.Getenv(key)) + } + } + if len(config.LocalSecrets) > 0 { + for _, key := range config.LocalSecrets { + args = append(args, "--set", "secrets."+key+"="+os.Getenv(key)) + } + } + args = append(args, "--set", "image.repository="+image) + args = append(args, "--set", "image.tag="+tag.String()) + args = append(args, "-f", deployable.LocalConfig) + + err = environment.Run(true, "helm", args...) + if err != nil { + color.New(color.FgRed).PrintFunc()("helm error. try running 'helm del --purge %s'", releaseName) + return err + } + + if openServices { + environment.Run(true, "minikube", "service", releaseName) + } + } + return nil +} diff --git a/cmd/ci.go b/cmd/ci.go new file mode 100644 index 0000000000000000000000000000000000000000..576bfbe89491b55c627bfd1040bb7aa0edc5b8bb --- /dev/null +++ b/cmd/ci.go @@ -0,0 +1,55 @@ +// Copyright © 2019 NAME HERE <EMAIL ADDRESS> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "github.com/spf13/cobra" +) + +var dockerRegistry string +var dockerRegistryNamespace string +var imageTag string +var environmentSuffix string + +// configCmd represents the config command +var ciCmd = &cobra.Command{ + Use: "ci", + Short: "Build tools for the CI system", + // Run: func(cmd *cobra.Command, args []string) { + // fmt.Println("config called") + // }, +} + +func init() { + rootCmd.AddCommand(ciCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // configCmd.PersistentFlags().String("foo", "", "A help for foo") + ciCmd.PersistentFlags().StringVarP(&dockerRegistry, "registry", "r", "", "url of the docker registry") + ciCmd.PersistentFlags().StringVarP(&dockerRegistryNamespace, "registry-namespace", "n", "", "namespace in the docker registry") + ciCmd.PersistentFlags().StringVarP(&imageTag, "image", "i", "", "image tag for docker") + ciCmd.PersistentFlags().StringVarP(&environmentSuffix, "environment", "e", "", "environment suffix to append to helm release name") + + ciCmd.MarkPersistentFlagRequired("registry") + ciCmd.MarkPersistentFlagRequired("registry-namespace") + ciCmd.MarkPersistentFlagRequired("image") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // configCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/ci_build.go b/cmd/ci_build.go new file mode 100644 index 0000000000000000000000000000000000000000..b54d10d366cabf3a895eb0886e22f8282e7b47c8 --- /dev/null +++ b/cmd/ci_build.go @@ -0,0 +1,82 @@ +// Copyright © 2019 NAME HERE <EMAIL ADDRESS> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "git.psu.edu/k8s/devtool/config" + "git.psu.edu/k8s/devtool/environment" + "github.com/fatih/color" + "github.com/spf13/cobra" +) + +// buildCmd represents the build command +var ciBuildCmd = &cobra.Command{ + Use: "build", + Short: "build the application within the CI system", + RunE: func(cmd *cobra.Command, args []string) error { + conf, err := config.New(configFile) + if err != nil { + color.New(color.FgRed).PrintFunc()("Error reading configuration file: %s", configFile) + return err + } + + err = buildCiDocker(conf) + if err != nil { + color.Red("Failed to build docker image.") + return err + } + return nil + }, +} + +func init() { + ciCmd.AddCommand(ciBuildCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // buildCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // buildCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + +} + +func buildCiDocker(config config.Config) error { + color.Blue("Building Docker Containers") + + for _, deployable := range config.Deployables { + dockerfile := deployable.Dockerfile + image := deployable.Name + + dockerImage := dockerRegistry + "/" + dockerRegistryNamespace + "/" + image + dockerTag := dockerImage + ":" + imageTag + + color.New(color.FgGreen).PrintFunc()("Building: ", dockerTag) + err := environment.Run(true, "docker", "build", "-t", dockerTag, "-f", dockerfile, ".") + if err != nil { + return err + } + + color.New(color.FgGreen).PrintFunc()("Pushing Docker image: ", dockerTag) + err = environment.Run(true, "docker", "push", dockerImage) + if err != nil { + return err + } + } + return nil +} diff --git a/cmd/ci_deploy.go b/cmd/ci_deploy.go new file mode 100644 index 0000000000000000000000000000000000000000..35e068f10df8bb33e58717ffcc13f753c458b56a --- /dev/null +++ b/cmd/ci_deploy.go @@ -0,0 +1,88 @@ +// Copyright © 2019 NAME HERE <EMAIL ADDRESS> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "os" + + "git.psu.edu/k8s/devtool/config" + "git.psu.edu/k8s/devtool/environment" + "github.com/fatih/color" + "github.com/spf13/cobra" +) + +// buildCmd represents the build command +var ciDeployCmd = &cobra.Command{ + Use: "deploy", + Short: "deploy the application within the CI system", + RunE: func(cmd *cobra.Command, args []string) error { + conf, err := config.New(configFile) + if err != nil { + color.New(color.FgRed).PrintFunc()("Error reading configuration file: %s", configFile) + return err + } + + err = fluxDeployCi(conf) + if err != nil { + color.Red("Failed to build docker image.") + return err + } + return nil + }, +} + +func init() { + ciCmd.AddCommand(ciDeployCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // buildCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // buildCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} + +func fluxDeployCi(config config.Config) error { + color.Blue("Updating Flux Releases") + + for _, deployable := range config.Deployables { + image := deployable.Name + + dockerImage := dockerRegistry + "/" + dockerRegistryNamespace + "/" + image + dockerTag := dockerImage + ":" + imageTag + + var releaseName string + if environmentSuffix == "" { + releaseName = deployable.Name + } else { + releaseName = deployable.Name + "-" + environmentSuffix + } + color.Blue("Updating Flux Release:", releaseName) + + os.Setenv("KUBE_SERVICE_NAME", releaseName) + os.Setenv("DOCKER_IMAGE_TAG", dockerTag) + os.Setenv("CI_PROJECT_NAME", image) + + err := environment.Run(true, "fluxhelmrelease") + if err != nil { + color.Red("fluxhelmrelease error.") + return err + } + } + return nil +} diff --git a/cmd/config.go b/cmd/config.go new file mode 100644 index 0000000000000000000000000000000000000000..313b7daa2e2863e743441e078d3214f5df5b9451 --- /dev/null +++ b/cmd/config.go @@ -0,0 +1,42 @@ +// Copyright © 2019 NAME HERE <EMAIL ADDRESS> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "github.com/spf13/cobra" +) + +// configCmd represents the config command +var configCmd = &cobra.Command{ + Use: "config", + Short: "Manage the projects build configuration", + // Run: func(cmd *cobra.Command, args []string) { + // fmt.Println("config called") + // }, +} + +func init() { + rootCmd.AddCommand(configCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // configCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // configCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/config_add.go b/cmd/config_add.go new file mode 100644 index 0000000000000000000000000000000000000000..df71c6024264d0fb8da396730bb33b0259f73c66 --- /dev/null +++ b/cmd/config_add.go @@ -0,0 +1,43 @@ +// Copyright © 2019 NAME HERE <EMAIL ADDRESS> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "github.com/fatih/color" + "github.com/spf13/cobra" +) + +// addCmd represents the add command +var addCmd = &cobra.Command{ + Use: "add", + Short: "adds a deployable unit to the project configuration", + Run: func(cmd *cobra.Command, args []string) { + color.Red("Not implemented yet!") + }, +} + +func init() { + configCmd.AddCommand(addCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // addCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // addCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/config_delete.go b/cmd/config_delete.go new file mode 100644 index 0000000000000000000000000000000000000000..ffb429f4699d53c75e32737a36261f3dc07b078d --- /dev/null +++ b/cmd/config_delete.go @@ -0,0 +1,43 @@ +// Copyright © 2019 NAME HERE <EMAIL ADDRESS> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "github.com/fatih/color" + "github.com/spf13/cobra" +) + +// deleteCmd represents the delete command +var deleteCmd = &cobra.Command{ + Use: "delete", + Short: "removes a deployable unit to the project configuration", + Run: func(cmd *cobra.Command, args []string) { + color.Red("Not implemented yet!") + }, +} + +func init() { + configCmd.AddCommand(deleteCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // deleteCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // deleteCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/config_init.go b/cmd/config_init.go new file mode 100644 index 0000000000000000000000000000000000000000..0de207c2b72bc2b49c8f0545d6fba90969aabfe0 --- /dev/null +++ b/cmd/config_init.go @@ -0,0 +1,197 @@ +// Copyright © 2019 NAME HERE <EMAIL ADDRESS> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "fmt" + "log" + "os" + "path/filepath" + "strings" + + "git.psu.edu/k8s/devtool/config" + + "github.com/fatih/color" + "github.com/manifoldco/promptui" + "github.com/spf13/cobra" +) + +var force bool + +// initCmd represents the init command +var initCmd = &cobra.Command{ + Use: "init", + Short: "initialize the project configuration", + Run: func(cmd *cobra.Command, args []string) { + conf, err := config.New(configFile) + if err == nil && !force { + color.Red("Configuration already exists.") + prompt := promptui.Prompt{ + Label: "Are you sure you want to overwrite", + IsConfirm: true, + } + _, err := prompt.Run() + if err != nil { + return + } + } + conf.Deployables = []config.Deployable{} + + defaultName := conf.Name + if defaultName == "" { + wd, _ := os.Getwd() + fmt.Println(wd) + if idx := strings.LastIndex(wd, "/"); idx != -1 { + fmt.Println(idx) + idx++ + defaultName = wd[idx:] + fmt.Println(defaultName) + } + } + + prompt := promptui.Prompt{ + Label: "Project Name", + Default: defaultName, + } + + response, err := prompt.Run() + if err != nil { + return + } + conf.Name = response + + selectPrompt := promptui.Select{ + Label: "Language", + Items: []string{"java", "golang", "angular"}, + } + + _, response, err = selectPrompt.Run() + if err != nil { + return + } + conf.Language = response + + dockerfiles := findDockerfiles() + for _, file := range dockerfiles { + color.Yellow("Found Dockerfile: %s", file) + prompt := promptui.Prompt{ + Label: "Include in config", + Default: "Yes", + } + response, err = prompt.Run() + if err != nil { + return + } + + switch strings.ToLower(response) { + case "": + case "y": + case "yes": + default: + continue + } + + var deployable config.Deployable + deployable.Dockerfile = file + + defaultName := strings.TrimSuffix(file, "Dockerfile") + defaultName = strings.TrimSuffix(defaultName, "/") + if defaultName == "" { + defaultName = conf.Name + } + prompt = promptui.Prompt{ + Label: "Name", + Default: defaultName, + } + response, err = prompt.Run() + if err != nil { + return + } + deployable.Name = response + + selectPrompt := promptui.SelectWithAdd{ + Label: "Chart", + Items: []string{"cm/eio-swe-service", "cm/eio-swe-cronjob", "cm/angular-client", "cm/eio-swe-jms-processor"}, + AddLabel: "Other", + } + _, response, err = selectPrompt.Run() + if err != nil { + return + } + deployable.Chart = response + + prompt = promptui.Prompt{ + Label: "Chart Version", + } + response, err = prompt.Run() + if err != nil { + return + } + deployable.ChartVersion = response + + prompt = promptui.Prompt{ + Label: "Local Config File", + } + response, err = prompt.Run() + if err != nil { + return + } + deployable.LocalConfig = response + + conf.Deployables = append(conf.Deployables, deployable) + color.Green("Adding Deployment") + } + + color.Yellow("Writing configuration") + + conf.Write(configFile) + + }, +} + +func init() { + configCmd.AddCommand(initCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // initCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + initCmd.Flags().BoolVarP(&force, "force", "f", false, "force overwrite of current configuration") +} + +func findDockerfiles() []string { + var dockerfiles []string + err := filepath.Walk(".", + func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + return nil + } + if info.Name() == "Dockerfile" { + dockerfiles = append(dockerfiles, path) + } + return nil + }) + if err != nil { + log.Println(err) + } + return dockerfiles +} diff --git a/cmd/config_list.go b/cmd/config_list.go new file mode 100644 index 0000000000000000000000000000000000000000..8df3e96c090335909bd442c5a36d5addf9eece33 --- /dev/null +++ b/cmd/config_list.go @@ -0,0 +1,53 @@ +// Copyright © 2019 NAME HERE <EMAIL ADDRESS> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "fmt" + + "git.psu.edu/k8s/devtool/config" + "github.com/fatih/color" + "github.com/spf13/cobra" +) + +// listCmd represents the list command +var listCmd = &cobra.Command{ + Use: "list", + Short: "lists the project configuration", + Run: func(cmd *cobra.Command, args []string) { + conf, err := config.New(configFile) + if err != nil { + color.New(color.FgRed).PrintFunc()("Error reading configuration file: ", configFile, "\n") + return + } + fmt.Println("Project Configuration") + fmt.Printf("Name: %s\n", conf.Name) + fmt.Printf("Language: %s\n", conf.Language) + }, +} + +func init() { + configCmd.AddCommand(listCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // listCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // listCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} diff --git a/cmd/root.go b/cmd/root.go new file mode 100644 index 0000000000000000000000000000000000000000..8e9a17a0cff01ed71bc7c6f771f0467d74663db9 --- /dev/null +++ b/cmd/root.go @@ -0,0 +1,98 @@ +// Copyright © 2019 NAME HERE <EMAIL ADDRESS> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "fmt" + "os" + + "github.com/fatih/color" + homedir "github.com/mitchellh/go-homedir" + "github.com/spf13/cobra" + "github.com/spf13/viper" +) + +const ( + configFile string = ".devtool.config" +) + +var cfgFile string + +// rootCmd represents the base command when called without any subcommands +var rootCmd = &cobra.Command{ + Use: "devtool", + Short: "A developer's tool", + Long: `A tool used by software engineering to build applications.`, + // Uncomment the following line if your bare application + // has an action associated with it: + // Run: func(cmd *cobra.Command, args []string) { }, +} + +// Execute adds all child commands to the root command and sets flags appropriately. +// This is called by main.main(). It only needs to happen once to the rootCmd. +func Execute() { + if err := rootCmd.Execute(); err != nil { + fmt.Println(err) + os.Exit(1) + } +} + +func init() { + cobra.OnInitialize(initConfig) + + // Here you will define your flags and configuration settings. + // Cobra supports persistent flags, which, if defined here, + // will be global for your application. + rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file (default is $HOME/.devtool.yaml)") + + // Cobra also supports local flags, which will only run + // when this action is called directly. + // rootCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") +} + +// initConfig reads in config file and ENV variables if set. +func initConfig() { + if cfgFile != "" { + // Use config file from the flag. + viper.SetConfigFile(cfgFile) + } else { + // Find home directory. + home, err := homedir.Dir() + if err != nil { + fmt.Println(err) + os.Exit(1) + } + + // Search config in home directory with name ".devtool" (without extension). + viper.AddConfigPath(home) + viper.SetConfigName(".devtool") + } + + viper.AutomaticEnv() // read in environment variables that match + + // If a config file is found, read it in. + if err := viper.ReadInConfig(); err == nil { + fmt.Println("Using config file:", viper.ConfigFileUsed()) + } +} + +func PrintLogo() { + color.Blue("__________ _____________ ___ .___ __ .__ ") + color.Blue("\\______ \\/ _____/ | \\ __| _/_______ ___/ |_ ____ ____ | | ") + color.Blue(" | ___/\\_____ \\| | / / __ |/ __ \\ \\/ /\\ __\\/ _ \\ / _ \\| | ") + color.Blue(" | | / \\ | / / /_/ \\ ___/\\ / | | ( <_> | <_> ) |__") + color.Blue(" |____| /_______ /______/ \\____ |\\___ >\\_/ |__| \\____/ \\____/|____/") + color.Blue(" \\/ \\/ \\/ ") +} diff --git a/cmd/start.go b/cmd/start.go new file mode 100644 index 0000000000000000000000000000000000000000..246f367e29691c46db62cbe9303758f8e1b6221b --- /dev/null +++ b/cmd/start.go @@ -0,0 +1,147 @@ +// Copyright © 2019 NAME HERE <EMAIL ADDRESS> +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package cmd + +import ( + "fmt" + "os" + "os/exec" + "strings" + "time" + + "git.psu.edu/k8s/devtool/environment" + "github.com/fatih/color" + "github.com/spf13/cobra" +) + +var Memory string +var Cpu string + +// startCmd represents the start command +var startCmd = &cobra.Command{ + Use: "start", + Short: "start the minikube environment", + Run: func(command *cobra.Command, args []string) { + fmt.Println("start called") + + start(Memory, Cpu) + }, +} + +func init() { + rootCmd.AddCommand(startCmd) + + // Here you will define your flags and configuration settings. + + // Cobra supports Persistent Flags which will work for this command + // and all subcommands, e.g.: + // startCmd.PersistentFlags().String("foo", "", "A help for foo") + + // Cobra supports local flags which will only run when this command + // is called directly, e.g.: + // startCmd.Flags().BoolP("toggle", "t", false, "Help message for toggle") + startCmd.PersistentFlags().StringVarP(&Memory, "memory", "m", "8192", "memory for minikube (default is 8192)") + startCmd.PersistentFlags().StringVarP(&Cpu, "cpr", "c", "6", "cpr for minikube (default is 6)") + +} + +func start(memory, cpu string) { + color.Blue("Checking Prerequisites.") + + prereqsInstalled := checkLocalSetup() + if !prereqsInstalled { + color.Red("Please install the required applications and try again.\n") + return + } + + environment.RunRequired(true, "helm", "repo", "add", "qa", "https://charts.qa.k8s.psu.edu") + environment.RunRequired(true, "helm", "repo", "add", "cm", "https://cm.qa.k8s.psu.edu") + + color.Blue("Setting up Minikube environment.") + + fmt.Printf("Using memory: %s and cpr: %s\n", memory, cpu) + + err := environment.Run(true, "minikube", "status") + if err != nil { + environment.RunRequired(true, "minikube", "start", "--memory", memory, "--cpus", cpu) + } + + checkInstallTiller() + + environment.EvalDockerEnv() + + color.Blue("Environment Variables:") + + for _, e := range os.Environ() { + fmt.Println(e) + } + +} + +func checkLocalSetup() bool { + var prereqsInstalled = true + err := checkInstalled("kubectl") + if err != nil { + prereqsInstalled = false + } + + err = checkInstalled("helm") + if err != nil { + prereqsInstalled = false + } + + err = checkInstalled("minikube") + if err != nil { + prereqsInstalled = false + } + + return prereqsInstalled +} + +func checkInstalled(app string) error { + path, err := exec.LookPath(app) + if err != nil { + color.New(color.FgYellow).PrintfFunc()("%s is not installed\n", app) + } else { + fmt.Printf("%s: %s\n", app, path) + } + return err +} + +func checkInstallTiller() { + color.Blue("Checking if Tiller is installed.") + + clientVersionOutput := environment.RunAndGetOutputRequired("helm", "version", "--client", "--short") + serverVersionOutput, err := environment.RunAndGetOutput("helm", "version", "--server", "--short") + + if err != nil { + color.Yellow("Installing Tiller into the cluster...") + + environment.RunRequired(true, "helm", "init") + time.Sleep(10 * time.Second) + + serverVersionOutput, err = environment.RunAndGetOutput("helm", "version", "--server", "--short") + } + + clientVersion := strings.TrimPrefix(clientVersionOutput[0], "Client: ") + serverVersion := strings.TrimPrefix(serverVersionOutput[0], "Server: ") + + if clientVersion != serverVersion { + warn := color.New(color.FgYellow).PrintFunc() + warn("Helm version mismatch between client and server") + warn("Client Version: %s\n", clientVersion) + warn("Server Version: %s\n", serverVersion) + } +} diff --git a/config/config.go b/config/config.go new file mode 100644 index 0000000000000000000000000000000000000000..5e6423278cf7c2ebea75d008bdb8e9e5fcfb0750 --- /dev/null +++ b/config/config.go @@ -0,0 +1,42 @@ +package config + +import ( + "encoding/json" + "io/ioutil" +) + +type ( + Config struct { + Name string `json:"name"` + Language string `json:"language"` + Deployables []Deployable `json:"deployables"` + LocalEnvVars []string `json:"localEnvVars"` + LocalSecrets []string `json:"localSecrets"` + } + Deployable struct { + Name string `json:"name"` + Dockerfile string `json:"dockerfile"` + Chart string `json:"chart"` + ChartVersion string `json:"chartVersion"` + LocalConfig string `json:"localConfig"` + } +) + +func (config Config) Write(filename string) error { + data, err := json.MarshalIndent(config, "", " ") + if err != nil { + return err + } + err = ioutil.WriteFile(filename, data, 0644) + return err +} + +func New(filename string) (Config, error) { + data, err := ioutil.ReadFile(filename) + var config Config + if err != nil { + return config, err + } + err = json.Unmarshal(data, &config) + return config, err +} diff --git a/devtool.go b/devtool.go new file mode 100644 index 0000000000000000000000000000000000000000..ba3588e5d4a821abfbe1dacf63e0e1a80eaf1625 --- /dev/null +++ b/devtool.go @@ -0,0 +1,8 @@ +package main + +import "git.psu.edu/k8s/devtool/cmd" + +func main() { + cmd.PrintLogo() + cmd.Execute() +} diff --git a/environment/environment.go b/environment/environment.go new file mode 100644 index 0000000000000000000000000000000000000000..eaef4c39ba3b9378c3760156a67d5f8b372b2944 --- /dev/null +++ b/environment/environment.go @@ -0,0 +1,75 @@ +package environment + +import ( + "os" + "os/exec" + "regexp" + "strings" + + "github.com/fatih/color" +) + +type Environment struct { + defaultMinikubeCluster bool +} + +func EvalDockerEnv() { + color.Blue("Setting up Docker environment.") + + lines := RunAndGetOutputRequired("minikube", "docker-env") + r := regexp.MustCompile(`export (?P<Name>.*)="(?P<Value>.*)"`) + + for _, line := range lines { + if line != "" && !strings.HasPrefix(line, "#") { + parts := r.FindStringSubmatch(line) + os.Setenv(parts[1], parts[2]) + } + } +} + +func Run(viewOutput bool, name string, args ...string) error { + cmd := exec.Command(name, args...) + + cmd.Env = os.Environ() + + if viewOutput { + cmd.Stdout = os.Stdout + cmd.Stderr = os.Stderr + } + + err := cmd.Run() + + return err +} + +func RunRequired(viewOutput bool, name string, args ...string) { + err := Run(viewOutput, name, args...) + + if err != nil { + color.Red("Command Failed with '%s'\n", err) + panic("exiting.") + } +} + +func RunAndGetOutput(name string, args ...string) ([]string, error) { + cmd := exec.Command(name, args...) + + cmd.Env = os.Environ() + + out, err := cmd.CombinedOutput() + if err != nil { + color.Red("Command Failed with '%s'\n", err) + } + + lines := strings.Split(string(out), "\n") + return lines, err +} + +func RunAndGetOutputRequired(name string, args ...string) []string { + output, err := RunAndGetOutput(name, args...) + if err != nil { + color.Red("Command Failed with '%s'\n", err) + panic("exiting.") + } + return output +} diff --git a/go.mod b/go.mod new file mode 100644 index 0000000000000000000000000000000000000000..d6eeb87392b35d4163a58ff77c272b09d20de9ae --- /dev/null +++ b/go.mod @@ -0,0 +1,19 @@ +module git.psu.edu/k8s/devtool + +go 1.12 + +require ( + github.com/BurntSushi/toml v0.3.1 // indirect + github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf // indirect + github.com/fatih/color v1.7.0 + github.com/google/uuid v1.1.1 + github.com/inconshreveable/mousetrap v1.0.0 // indirect + github.com/manifoldco/promptui v0.3.2 + github.com/mattn/go-colorable v0.1.1 // indirect + github.com/mattn/go-isatty v0.0.7 // indirect + github.com/mitchellh/go-homedir v1.1.0 + github.com/nicksnyder/go-i18n v1.10.0 // indirect + github.com/spf13/cobra v0.0.3 + github.com/spf13/viper v1.3.2 + gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c // indirect +) diff --git a/go.sum b/go.sum new file mode 100644 index 0000000000000000000000000000000000000000..c2e62a75a68be6120ee564464d9811a391bc3bc7 --- /dev/null +++ b/go.sum @@ -0,0 +1,104 @@ +github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/alecthomas/gometalinter v2.0.11+incompatible h1:ENdXMllZNSVDTJUUVIzBW9CSEpntTrQa76iRsEFLX/M= +github.com/alecthomas/gometalinter v2.0.11+incompatible/go.mod h1:qfIpQGGz3d+NmgyPBqv+LSh50emm1pt72EtcX2vKYQk= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf h1:qet1QNfXsQxTZqLG4oE62mJzwPIB8+Tee4RNCL9ulrY= +github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= +github.com/armon/consul-api v0.0.0-20180202201655-eb2c6b5be1b6/go.mod h1:grANhF5doyWs3UAsr3K4I6qtAmlQcZDesFNEHPZAzj8= +github.com/chzyer/logex v1.1.10 h1:Swpa1K6QvQznwJRcfTfQJmTE72DqScAa40E+fbHEXEE= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e h1:fY5BOSpyZCqRo5OhCuC+XN+r/bBCmeuuJtjz+bCNIf8= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1 h1:q763qf9huN11kDQavWsoZXJNW3xEE4JJyHa5Q25/sd8= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/client9/misspell v0.3.4 h1:ta993UF76GwbvJcIo3Y68y/M3WxlpEHPWIGDkJYwzJI= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/coreos/etcd v3.3.10+incompatible/go.mod h1:uF7uidLiAD3TWHmW31ZFd/JWoc32PjwdhPthX9715RE= +github.com/coreos/go-etcd v2.0.0+incompatible/go.mod h1:Jez6KQU2B/sWsbdaef3ED8NzMklzPG4d5KIOhIy30Tk= +github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3EedlOD2RNk= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/fatih/color v1.7.0 h1:DkWD4oS2D8LGGgTQ6IvwJJXSL5Vp2ffcQg58nFV38Ys= +github.com/fatih/color v1.7.0/go.mod h1:Zm6kSWBoL9eyXnKyktHP6abPY2pDugNf5KwzbycvMj4= +github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I= +github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo= +github.com/golang/lint v0.0.0-20181026193005-c67002cb31c3 h1:I4BOK3PBMjhWfQM2zPJKK7lOBGsrsvOB7kBELP33hiE= +github.com/golang/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:tluoj9z5200jBnyusfRPU2LqT6J+DAorxEvtC7LHB+E= +github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf h1:7+FW5aGwISbqUtkfmIpZJGRgNFg2ioYPvFaUxdqpDsg= +github.com/google/shlex v0.0.0-20181106134648-c34317bd91bf/go.mod h1:RpwtwJQFrIEPstU94h88MWPXP2ektJZ8cZ0YntAmXiE= +github.com/google/uuid v1.1.1 h1:Gkbcsh/GbpXz7lPftLA3P6TYMwjCLYm83jiFQZF/3gY= +github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gordonklaus/ineffassign v0.0.0-20180909121442-1003c8bd00dc h1:cJlkeAx1QYgO5N80aF5xRGstVsRQwgLR7uA2FnP1ZjY= +github.com/gordonklaus/ineffassign v0.0.0-20180909121442-1003c8bd00dc/go.mod h1:cuNKsD1zp2v6XfE/orVX2QE1LC+i254ceGcVeDT3pTU= +github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= +github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= +github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a h1:FaWFmfWdAUKbSCtOU2QjDaorUexogfaMgbipgYATUMU= +github.com/juju/ansiterm v0.0.0-20180109212912-720a0952cc2a/go.mod h1:UJSiEoRfvx3hP73CvoARgeLjaIOjybY9vj8PUPPFGeU= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= +github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a h1:weJVJJRzAJBFRlAiJQROKQs8oC9vOxvm4rZmBBk0ONw= +github.com/lunixbochs/vtclean v0.0.0-20180621232353-2d01aacdc34a/go.mod h1:pHhQNgMf3btfWnGBVipUOjRYhoOsdGqdm/+2c2E2WMI= +github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY= +github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ= +github.com/manifoldco/promptui v0.3.2 h1:rir7oByTERac6jhpHUPErHuopoRDvO3jxS+FdadEns8= +github.com/manifoldco/promptui v0.3.2/go.mod h1:8JU+igZ+eeiiRku4T5BjtKh2ms8sziGpSYl1gN8Bazw= +github.com/mattn/go-colorable v0.0.9/go.mod h1:9vuHe8Xs5qXnSaW/c/ABM9alt+Vo+STaOChaDxuIBZU= +github.com/mattn/go-colorable v0.1.1 h1:G1f5SKeVxmagw/IyvzvtZE4Gybcc4Tr1tf7I8z0XgOg= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNxMWT7Zi4= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7 h1:UvyT9uN+3r7yLEYSlJsbQGdsaB/a0DlgWP3pql6iwOc= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y= +github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0= +github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE= +github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y= +github.com/nicksnyder/go-i18n v1.10.0 h1:5AzlPKvXBH4qBzmZ09Ua9Gipyruv6uApMcrNZdo96+Q= +github.com/nicksnyder/go-i18n v1.10.0/go.mod h1:HrK7VCrbOvQoUAQ7Vpy7i87N7JZZZ7R2xBGjv0j365Q= +github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= +github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/spf13/afero v1.1.2 h1:m8/z1t7/fwjysjQRYbP0RD+bUIF/8tJwPdEZsI83ACI= +github.com/spf13/afero v1.1.2/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ= +github.com/spf13/cast v1.3.0 h1:oget//CVOEoFewqQxwr0Ej5yjygnqGkvggSE/gB35Q8= +github.com/spf13/cast v1.3.0/go.mod h1:Qx5cxh0v+4UWYiBimWS+eyWzqEqokIECu5etghLkUJE= +github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8= +github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ= +github.com/spf13/jwalterweatherman v1.0.0 h1:XHEdyB+EcvlqZamSM4ZOMGlc93t6AcsBEu9Gc1vn7yk= +github.com/spf13/jwalterweatherman v1.0.0/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo= +github.com/spf13/pflag v1.0.3 h1:zPAT6CGy6wXeQ7NtTnaTerfKOsV6V6F8agHXFiazDkg= +github.com/spf13/pflag v1.0.3/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4= +github.com/spf13/viper v1.3.2 h1:VUFqw5KcqRf7i70GOzW7N+Q7+gxVBkSSqiXB12+JQ4M= +github.com/spf13/viper v1.3.2/go.mod h1:ZiWeW+zYFKm7srdB9IoDzzZXaJaI5eL9QjNiN/DMA2s= +github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/tsenart/deadcode v0.0.0-20160724212837-210d2dc333e9 h1:vY5WqiEon0ZSTGM3ayVVi+twaHKHDFUVloaQ/wug9/c= +github.com/tsenart/deadcode v0.0.0-20160724212837-210d2dc333e9/go.mod h1:q+QjxYvZ+fpjMXqs+XEriussHjSYqeXVnAdSV1tkMYk= +github.com/ugorji/go/codec v0.0.0-20181204163529-d75b2dcb6bc8/go.mod h1:VFNgLljTbGfSG7qAOspJ7OScBnGdDN/yBr0sguwnwf0= +github.com/xordataexchange/crypt v0.0.3-0.20170626215501-b2862e3d0a77/go.mod h1:aYKd//L2LvnjZzWKhF00oedf4jCCReLcmhLdhm1A27Q= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9 h1:mKdxBk7AujPs8kU4m80U72y/zjbZ3UcXC7dClwKbUI0= +golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3 h1:x/bBzNauLQAlE3fLku/xy92Y8QwKX5HZymrMz2IiKFc= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223 h1:DH4skfRX4EBpamg7iV4ZlCpblAHI6s6TDM39bFZumv8= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/tools v0.0.0-20181122213734-04b5d21e00f1 h1:bsEj/LXbv3BCtkp/rBj9Wi/0Nde4OMaraIZpndHAhdI= +golang.org/x/tools v0.0.0-20181122213734-04b5d21e00f1/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c h1:vTxShRUnK60yd8DZU+f95p1zSLj814+5CuEh7NjF2/Y= +gopkg.in/alecthomas/kingpin.v3-unstable v3.0.0-20180810215634-df19058c872c/go.mod h1:3HH7i1SgMqlzxCcBmUHW657sD4Kvv9sC3HpL3YukzwA= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=