Commit 0d0bf537 authored by Christopher Harm's avatar Christopher Harm

Switching to cobra for CLI support

parent c5d371b7
// 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/environment"
"github.com/fatih/color"
"github.com/spf13/cobra"
)
var skipCompile bool
// buildCmd represents the build command
var buildCmd = &cobra.Command{
Use: "build",
Short: "build the application",
Run: func(cmd *cobra.Command, args []string) {
build()
},
}
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")
}
func build() {
err := environment.Run(true, "minikube", "status")
if err != nil {
color.Red("Minikube is not started... run `devtool start` first.")
return
}
if skipCompile {
color.Yellow("Skipping Build Step.")
} else {
color.Blue("Building Application")
}
}
// 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"
homedir "github.com/mitchellh/go-homedir"
"github.com/spf13/cobra"
"github.com/spf13/viper"
)
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())
}
}
// 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)
}
}
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.Marshal(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
}
package main
import (
"fmt"
"os"
"os/exec"
"regexp"
"sort"
"strings"
"git.psu.edu/swe-golang/logenv"
"github.com/codegangsta/cli"
"github.com/fatih/color"
log "github.com/sirupsen/logrus"
)
var printWarn = color.New(color.FgYellow).PrintfFunc()
var printError = color.New(color.FgRed).PrintfFunc()
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"`
}
)
import "git.psu.edu/k8s/devtool/cmd"
func main() {
app := cli.NewApp()
app.Name = "Penn State devtool"
app.Usage = "to build projects"
app.Version = "1.0"
app.Author = "EIT - SWE"
app.Flags = []cli.Flag{
cli.BoolFlag{
Name: "verbose",
Usage: "Show more output",
},
}
app.Commands = []cli.Command{
{
Name: "start",
Usage: "start the environment",
Action: CmdStart,
Flags: []cli.Flag{
cli.StringFlag{
Name: "memory",
Usage: "memory for the minikube environment",
},
cli.StringFlag{
Name: "cpu",
Usage: "cpus for the minikube environment",
},
},
},
{
Name: "environment",
Usage: "displays the current environment variables",
Action: CmdEnvironment,
Flags: []cli.Flag{
cli.StringFlag{
Name: "memory",
Usage: "memory for the minikube environment",
},
cli.StringFlag{
Name: "cpu",
Usage: "cpus for the minikube environment",
},
},
},
{
Name: "build",
Usage: "builds the current project",
Action: CmdBuild,
Flags: []cli.Flag{
cli.StringFlag{
Name: "memory",
Usage: "memory for the minikube environment",
},
cli.StringFlag{
Name: "cpu",
Usage: "cpus for the minikube environment",
},
},
},
}
color.Blue("Penn State - devtool...")
sort.Sort(cli.FlagsByName(app.Flags))
sort.Sort(cli.CommandsByName(app.Commands))
app.Run(os.Args)
}
func CmdStart(context *cli.Context) {
color.Blue("Checking Prerequisites.")
kubeMem := context.String("memory")
kubeCpu := context.String("cpu")
prereqsInstalled := checkLocalSetup()
if !prereqsInstalled {
printError("Please install the required applications and try again.\n")
return
}
runRequired(true, "helm", "repo", "add", "qa", "https://charts.qa.k8s.psu.edu")
runRequired(true, "helm", "repo", "add", "cm", "https://cm.qa.k8s.psu.edu")
color.Blue("Setting up Minikube environment.")
if kubeMem == "" {
kubeMem = os.Getenv("MINIKUBE_MEMORY_MB")
if kubeMem == "" {
kubeMem = "8192"
}
}
if kubeCpu == "" {
kubeCpu = os.Getenv("MINIKUBE_CPUS")
if kubeCpu == "" {
kubeCpu = "6"
}
}
err := run(true, "minikube", "status")
if err != nil {
runRequired(true, "minikube", "start", "--memory", kubeMem, "--cpus", kubeCpu)
}
evalDockerEnv()
checkInstallTiller()
}
func init() {
err := logenv.ConfigureFromEnvironment()
if err != nil {
log.Fatal("Unable to configure the logger from the environment", err)
}
}
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 {
printWarn("%s is not installed\n", app)
} else {
fmt.Printf("%s: %s\n", app, path)
}
return err
}
func evalDockerEnv() {
lines := runAndGetOutput("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 checkInstallTiller() {
clientVersionOutput := runAndGetOutput("helm", "version", "--client", "--short")
serverVersionOutput := runAndGetOutput("helm", "version", "--server", "--short")
clientVersion := strings.TrimPrefix(clientVersionOutput[0], "Client: ")
serverVersion := strings.TrimPrefix(serverVersionOutput[0], "Server: ")
if clientVersion != serverVersion {
printWarn("Helm version mismatch between client and server")
printWarn("Client Version: %s\n", clientVersion)
printWarn("Server Version: %s\n", serverVersion)
}
}
func CmdEnvironment(context *cli.Context) {
CmdStart(context)
color.Blue("Environment Variables:")
for _, e := range os.Environ() {
fmt.Println(e)
}
}
func CmdBuild(context *cli.Context) {
CmdStart(context)
color.Blue("Building Application")
}
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 {
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
cmd.Execute()
}
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()