Kaynağa Gözat

use shared config flags for cli commands

Alexander Belanger 5 yıl önce
ebeveyn
işleme
5d9873087c
15 değiştirilmiş dosya ile 542 ekleme ve 369 silme
  1. 12 26
      cli/cmd/auth.go
  2. 5 12
      cli/cmd/cluster.go
  3. 405 181
      cli/cmd/config.go
  4. 16 30
      cli/cmd/connect.go
  5. 57 0
      cli/cmd/deploy.go
  6. 10 10
      cli/cmd/docker.go
  7. 2 2
      cli/cmd/errors.go
  8. 5 10
      cli/cmd/helm_repo.go
  9. 2 9
      cli/cmd/open.go
  10. 2 11
      cli/cmd/project.go
  11. 8 13
      cli/cmd/registry.go
  12. 5 41
      cli/cmd/root.go
  13. 4 11
      cli/cmd/run.go
  14. 8 13
      cli/cmd/server.go
  15. 1 0
      go.mod

+ 12 - 26
cli/cmd/auth.go

@@ -66,20 +66,6 @@ func init() {
 	authCmd.AddCommand(registerCmd)
 	authCmd.AddCommand(logoutCmd)
 
-	authCmd.PersistentFlags().StringVar(
-		&host,
-		"host",
-		getHost(),
-		"host url of Porter instance",
-	)
-
-	loginCmd.PersistentFlags().StringVar(
-		&token,
-		"token",
-		"",
-		"bearer token for authentication",
-	)
-
 	loginCmd.PersistentFlags().BoolVar(
 		&manual,
 		"manual",
@@ -89,7 +75,7 @@ func init() {
 }
 
 func login() error {
-	client := api.NewClientWithToken(getHost()+"/api", getToken())
+	client := api.NewClientWithToken(config.Host+"/api", config.Token)
 
 	user, _ := client.AuthCheck(context.Background())
 
@@ -107,20 +93,20 @@ func login() error {
 	var err error
 
 	if token == "" {
-		token, err = loginBrowser.Login(getHost())
+		token, err = loginBrowser.Login(config.Host)
 
 		if err != nil {
 			return err
 		}
 
 		// set the token in config
-		err = setToken(token)
+		err = config.SetToken(token)
 
 		if err != nil {
 			return err
 		}
 
-		client := api.NewClientWithToken(getHost()+"/api", token)
+		client := api.NewClientWithToken(config.Host+"/api", token)
 
 		user, err := client.AuthCheck(context.Background())
 
@@ -139,17 +125,17 @@ func login() error {
 		}
 
 		if len(projects) > 0 {
-			setProject(projects[0].ID)
+			config.SetProject(projects[0].ID)
 		}
 	} else {
 		// set the token in config
-		err = setToken(token)
+		err = config.SetToken(token)
 
 		if err != nil {
 			return err
 		}
 
-		client := api.NewClientWithToken(getHost()+"/api", token)
+		client := api.NewClientWithToken(config.Host+"/api", token)
 
 		user, err := client.AuthCheck(context.Background())
 
@@ -166,14 +152,14 @@ func login() error {
 			return err
 		}
 
-		setProject(projID)
+		config.SetProject(projID)
 	}
 
 	return nil
 }
 
 func loginManual() error {
-	client := api.NewClient(getHost()+"/api", "cookie.json")
+	client := api.NewClient(config.Host+"/api", "cookie.json")
 
 	var username, pw string
 
@@ -201,7 +187,7 @@ func loginManual() error {
 	}
 
 	// set the token to empty since this is manual (cookie-based) login
-	setToken("")
+	config.SetToken("")
 
 	color.New(color.FgGreen).Println("Successfully logged in!")
 
@@ -213,7 +199,7 @@ func loginManual() error {
 	}
 
 	if len(projects) > 0 {
-		setProject(projects[0].ID)
+		config.SetProject(projects[0].ID)
 	}
 
 	return nil
@@ -257,7 +243,7 @@ func logout(user *api.AuthCheckResponse, client *api.Client, args []string) erro
 		return err
 	}
 
-	setToken("")
+	config.SetToken("")
 
 	color.Green("Successfully logged out")
 

+ 5 - 12
cli/cmd/cluster.go

@@ -68,13 +68,6 @@ var clusterNamespaceListCmd = &cobra.Command{
 func init() {
 	rootCmd.AddCommand(clusterCmd)
 
-	clusterCmd.PersistentFlags().UintVar(
-		&clusterID,
-		"cluster-id",
-		getClusterID(),
-		"id of the cluster",
-	)
-
 	clusterCmd.AddCommand(clusterNamespaceCmd)
 	clusterCmd.AddCommand(clusterListCmd)
 	clusterCmd.AddCommand(clusterDeleteCmd)
@@ -83,7 +76,7 @@ func init() {
 }
 
 func listClusters(user *api.AuthCheckResponse, client *api.Client, args []string) error {
-	clusters, err := client.ListProjectClusters(context.Background(), getProjectID())
+	clusters, err := client.ListProjectClusters(context.Background(), config.Project)
 
 	if err != nil {
 		return err
@@ -94,7 +87,7 @@ func listClusters(user *api.AuthCheckResponse, client *api.Client, args []string
 
 	fmt.Fprintf(w, "%s\t%s\t%s\n", "ID", "NAME", "SERVER")
 
-	currClusterID := getClusterID()
+	currClusterID := config.Cluster
 
 	for _, cluster := range clusters {
 		if currClusterID == cluster.ID {
@@ -129,7 +122,7 @@ func deleteCluster(user *api.AuthCheckResponse, client *api.Client, args []strin
 			return err
 		}
 
-		err = client.DeleteProjectCluster(context.Background(), getProjectID(), uint(id))
+		err = client.DeleteProjectCluster(context.Background(), config.Project, uint(id))
 
 		if err != nil {
 			return err
@@ -142,10 +135,10 @@ func deleteCluster(user *api.AuthCheckResponse, client *api.Client, args []strin
 }
 
 func listNamespaces(user *api.AuthCheckResponse, client *api.Client, args []string) error {
-	pID := getProjectID()
+	pID := config.Project
 
 	// get the service account based on the cluster id
-	cID := getClusterID()
+	cID := config.Cluster
 
 	// get the list of namespaces
 	namespaces, err := client.GetK8sNamespaces(

+ 405 - 181
cli/cmd/config.go

@@ -1,254 +1,478 @@
 package cmd
 
 import (
-	"fmt"
 	"io/ioutil"
 	"os"
 	"path/filepath"
-	"strconv"
 
 	"github.com/fatih/color"
-	"github.com/spf13/cobra"
 	"github.com/spf13/viper"
-)
 
-// a set of shared flags
-var (
-	driver     string
-	host       string
-	projectID  uint
-	registryID uint
-	clusterID  uint
-	helmRepoID uint
+	flag "github.com/spf13/pflag"
 )
 
-var configCmd = &cobra.Command{
-	Use:   "config",
-	Short: "Commands that control local configuration settings",
-	Run: func(cmd *cobra.Command, args []string) {
-		if err := printConfig(); err != nil {
-			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
-			os.Exit(1)
-		}
-	},
-}
+// shared sets of flags used by multiple commands
+var driverFlagSet = flag.NewFlagSet("driver", flag.ExitOnError)
+var defaultFlagSet = flag.NewFlagSet("shared", flag.ExitOnError) // used by all commands
+var registryFlagSet = flag.NewFlagSet("registry", flag.ExitOnError)
+var helmRepoFlagSet = flag.NewFlagSet("helmrepo", flag.ExitOnError)
 
-var setProjectCmd = &cobra.Command{
-	Use:   "set-project [id]",
-	Args:  cobra.ExactArgs(1),
-	Short: "Saves the project id in the default configuration",
-	Run: func(cmd *cobra.Command, args []string) {
-		projID, err := strconv.ParseUint(args[0], 10, 64)
+// config is a shared object used by all commands
+var config = &CLIConfig{}
 
-		if err != nil {
-			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
-			os.Exit(1)
-		}
+// CLIConfig is the set of shared configuration options for the CLI commands.
+// This config is used by viper: calling Set() function for any parameter will
+// update the corresponding field in the viper config file.
+type CLIConfig struct {
+	// Driver can be either "docker" or "local", and represents which driver is
+	// used to run an instance of the server.
+	Driver string `yaml:"driver"`
 
-		err = setProject(uint(projID))
+	Host    string `yaml:"host"`
+	Project uint   `yaml:"project"`
+	Cluster uint   `yaml:"cluster"`
 
-		if err != nil {
-			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
-			os.Exit(1)
-		}
-	},
-}
+	Token string `yaml:"token"`
 
-var setClusterCmd = &cobra.Command{
-	Use:   "set-cluster [id]",
-	Args:  cobra.ExactArgs(1),
-	Short: "Saves the cluster id in the default configuration",
-	Run: func(cmd *cobra.Command, args []string) {
-		clusterID, err := strconv.ParseUint(args[0], 10, 64)
-
-		if err != nil {
-			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
-			os.Exit(1)
-		}
-
-		err = setCluster(uint(clusterID))
-
-		if err != nil {
-			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
-			os.Exit(1)
-		}
-	},
+	Registry uint `yaml:"registry"`
+	HelmRepo uint `yaml:"helm_repo"`
 }
 
-var setRegistryCmd = &cobra.Command{
-	Use:   "set-registry [id]",
-	Args:  cobra.ExactArgs(1),
-	Short: "Saves the registry id in the default configuration",
-	Run: func(cmd *cobra.Command, args []string) {
-		registryID, err := strconv.ParseUint(args[0], 10, 64)
+// InitAndLoadConfig populates the config object with the following precedence rules:
+// 1. flag
+// 2. env
+// 3. config
+// 4. default
+//
+// It populates the shared config object above
+func InitAndLoadConfig() {
+	initFlagSet()
+
+	// check that the .porter folder exists; create if not
+	porterDir := filepath.Join(home, ".porter")
+
+	if _, err := os.Stat(porterDir); os.IsNotExist(err) {
+		os.Mkdir(porterDir, 0700)
+	} else if err != nil {
+		color.New(color.FgRed).Printf("%v\n", err)
+		os.Exit(1)
+	}
 
-		if err != nil {
-			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
-			os.Exit(1)
-		}
+	viper.SetConfigName("porter")
+	viper.SetConfigType("yaml")
+	viper.AddConfigPath(porterDir)
 
-		err = setRegistry(uint(registryID))
+	// Bind the flagset initialized above
+	viper.BindPFlags(driverFlagSet)
+	viper.BindPFlags(defaultFlagSet)
+	viper.BindPFlags(registryFlagSet)
+	viper.BindPFlags(helmRepoFlagSet)
 
-		if err != nil {
-			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
-			os.Exit(1)
-		}
-	},
-}
-
-var setHelmRepoCmd = &cobra.Command{
-	Use:   "set-helmrepo [id]",
-	Args:  cobra.ExactArgs(1),
-	Short: "Saves the helm repo id in the default configuration",
-	Run: func(cmd *cobra.Command, args []string) {
-		hrID, err := strconv.ParseUint(args[0], 10, 64)
-
-		if err != nil {
-			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
-			os.Exit(1)
-		}
+	// Bind the environment variables with prefix "PORTER_"
+	viper.SetEnvPrefix("PORTER")
+	viper.BindEnv("host")
+	viper.BindEnv("project")
+	viper.BindEnv("cluster")
+	viper.BindEnv("token")
 
-		err = setHelmRepo(uint(hrID))
+	err := viper.ReadInConfig()
 
-		if err != nil {
-			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
+	if err != nil {
+		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
+			// create blank config file
+			err := ioutil.WriteFile(filepath.Join(home, ".porter", "porter.yaml"), []byte{}, 0644)
+
+			if err != nil {
+				color.New(color.FgRed).Printf("%v\n", err)
+				os.Exit(1)
+			}
+		} else {
+			// Config file was found but another error was produced
+			color.New(color.FgRed).Printf("%v\n", err)
 			os.Exit(1)
 		}
-	},
-}
-
-var setHostCmd = &cobra.Command{
-	Use:   "set-host [host]",
-	Args:  cobra.ExactArgs(1),
-	Short: "Saves the host in the default configuration",
-	Run: func(cmd *cobra.Command, args []string) {
-		err := setHost(args[0])
+	}
 
-		if err != nil {
-			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
-			os.Exit(1)
-		}
-	},
+	// unmarshal the config into the shared config struct
+	viper.Unmarshal(config)
 }
 
-func init() {
-	rootCmd.AddCommand(configCmd)
-
-	configCmd.AddCommand(setProjectCmd)
-	configCmd.AddCommand(setClusterCmd)
-	configCmd.AddCommand(setHostCmd)
-	configCmd.AddCommand(setRegistryCmd)
-	configCmd.AddCommand(setHelmRepoCmd)
+// initFlagSet initializes the shared flags used by multiple commands
+func initFlagSet() {
+	driverFlagSet.StringVar(
+		&config.Driver,
+		"driver",
+		"local",
+		"driver to use (local or docker)",
+	)
+
+	defaultFlagSet.StringVar(
+		&config.Host,
+		"host",
+		"https://dashboard.getporter.dev",
+		"host URL of Porter instance",
+	)
+
+	defaultFlagSet.UintVar(
+		&config.Project,
+		"project",
+		0,
+		"project ID of Porter project",
+	)
+
+	defaultFlagSet.UintVar(
+		&config.Cluster,
+		"cluster",
+		0,
+		"cluster ID of Porter cluster",
+	)
+
+	defaultFlagSet.StringVar(
+		&config.Token,
+		"token",
+		"",
+		"token for Porter authentication",
+	)
+
+	registryFlagSet.UintVar(
+		&config.Registry,
+		"registry",
+		0,
+		"registry ID of connected Porter registry",
+	)
+
+	helmRepoFlagSet.UintVar(
+		&config.HelmRepo,
+		"helmrepo",
+		0,
+		"helm repo ID of connected Porter Helm repository",
+	)
 }
 
-func setDriver(driver string) error {
+func (c *CLIConfig) SetDriver(driver string) error {
 	viper.Set("driver", driver)
-	err := viper.WriteConfig()
 	color.New(color.FgGreen).Printf("Set the current driver as %s\n", driver)
-	return err
-}
+	err := viper.WriteConfig()
 
-func getDriver() string {
-	if driver != "" {
-		return driver
+	if err != nil {
+		return err
 	}
 
-	if opts.driver != "" {
-		return opts.driver
-	}
+	config.Driver = driver
 
-	return viper.GetString("driver")
+	return nil
 }
 
-func printConfig() error {
-	config, err := ioutil.ReadFile(filepath.Join(home, ".porter", "porter.yaml"))
+func (c *CLIConfig) SetHost(host string) error {
+	viper.Set("host", host)
+	color.New(color.FgGreen).Printf("Set the current host as %s\n", host)
+	err := viper.WriteConfig()
 
 	if err != nil {
 		return err
 	}
 
-	fmt.Printf(string(config))
+	config.Host = host
 
 	return nil
 }
 
-func setProject(id uint) error {
-	viper.Set("project", id)
-	color.New(color.FgGreen).Printf("Set the current project id as %d\n", id)
-	return viper.WriteConfig()
-}
+func (c *CLIConfig) SetProject(projectID uint) error {
+	viper.Set("project", projectID)
+	color.New(color.FgGreen).Printf("Set the current project as %d\n", projectID)
+	err := viper.WriteConfig()
 
-func setCluster(id uint) error {
-	viper.Set("cluster", id)
-	color.New(color.FgGreen).Printf("Set the current cluster id as %d\n", id)
-	return viper.WriteConfig()
-}
+	if err != nil {
+		return err
+	}
 
-func setRegistry(id uint) error {
-	viper.Set("registry", id)
-	color.New(color.FgGreen).Printf("Set the current registry id as %d\n", id)
-	return viper.WriteConfig()
-}
+	config.Project = projectID
 
-func setHelmRepo(id uint) error {
-	viper.Set("helm_repo", id)
-	color.New(color.FgGreen).Printf("Set the current helm repo id as %d\n", id)
-	return viper.WriteConfig()
+	return nil
 }
 
-func setHost(host string) error {
-	viper.Set("host", host)
+func (c *CLIConfig) SetCluster(clusterID uint) error {
+	viper.Set("cluster", clusterID)
+	color.New(color.FgGreen).Printf("Set the current cluster as %d\n", clusterID)
 	err := viper.WriteConfig()
-	color.New(color.FgGreen).Printf("Set the current host as %s\n", host)
-	return err
+
+	if err != nil {
+		return err
+	}
+
+	config.Cluster = clusterID
+
+	return nil
 }
 
-func setToken(token string) error {
+func (c *CLIConfig) SetToken(token string) error {
 	viper.Set("token", token)
 	err := viper.WriteConfig()
-	return err
-}
 
-func getHost() string {
-	if host != "" {
-		return host
+	if err != nil {
+		return err
 	}
 
-	return viper.GetString("host")
-}
+	config.Token = token
 
-func getToken() string {
-	return viper.GetString("token")
+	return nil
 }
 
-func getClusterID() uint {
-	if clusterID != 0 {
-		return clusterID
-	}
-
-	return viper.GetUint("cluster")
-}
+func (c *CLIConfig) SetRegistry(registryID uint) error {
+	viper.Set("registry", registryID)
+	color.New(color.FgGreen).Printf("Set the current registry as %d\n", registryID)
+	err := viper.WriteConfig()
 
-func getRegistryID() uint {
-	if registryID != 0 {
-		return registryID
+	if err != nil {
+		return err
 	}
 
-	return viper.GetUint("registry")
-}
-
-func getHelmRepoID() uint {
-	if helmRepoID != 0 {
-		return helmRepoID
-	}
+	config.Registry = registryID
 
-	return viper.GetUint("helm_repo")
+	return nil
 }
 
-func getProjectID() uint {
-	if projectID != 0 {
-		return projectID
+func (c *CLIConfig) SetHelmRepo(helmRepoID uint) error {
+	viper.Set("helm_repo", helmRepoID)
+	color.New(color.FgGreen).Printf("Set the current Helm repo as %d\n", helmRepoID)
+	err := viper.WriteConfig()
+
+	if err != nil {
+		return err
 	}
 
-	return viper.GetUint("project")
+	config.HelmRepo = helmRepoID
+
+	return nil
 }
+
+// var configCmd = &cobra.Command{
+// 	Use:   "config",
+// 	Short: "Commands that control local configuration settings",
+// 	Run: func(cmd *cobra.Command, args []string) {
+// 		if err := printConfig(); err != nil {
+// 			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
+// 			os.Exit(1)
+// 		}
+// 	},
+// }
+
+// var config.SetProjectCmd = &cobra.Command{
+// 	Use:   "set-project [id]",
+// 	Args:  cobra.ExactArgs(1),
+// 	Short: "Saves the project id in the default configuration",
+// 	Run: func(cmd *cobra.Command, args []string) {
+// 		projID, err := strconv.ParseUint(args[0], 10, 64)
+
+// 		if err != nil {
+// 			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
+// 			os.Exit(1)
+// 		}
+
+// 		err = config.SetProject(uint(projID))
+
+// 		if err != nil {
+// 			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
+// 			os.Exit(1)
+// 		}
+// 	},
+// }
+
+// var config.SetClusterCmd = &cobra.Command{
+// 	Use:   "set-cluster [id]",
+// 	Args:  cobra.ExactArgs(1),
+// 	Short: "Saves the cluster id in the default configuration",
+// 	Run: func(cmd *cobra.Command, args []string) {
+// 		clusterID, err := strconv.ParseUint(args[0], 10, 64)
+
+// 		if err != nil {
+// 			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
+// 			os.Exit(1)
+// 		}
+
+// 		err = config.SetCluster(uint(clusterID))
+
+// 		if err != nil {
+// 			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
+// 			os.Exit(1)
+// 		}
+// 	},
+// }
+
+// var setRegistryCmd = &cobra.Command{
+// 	Use:   "set-registry [id]",
+// 	Args:  cobra.ExactArgs(1),
+// 	Short: "Saves the registry id in the default configuration",
+// 	Run: func(cmd *cobra.Command, args []string) {
+// 		registryID, err := strconv.ParseUint(args[0], 10, 64)
+
+// 		if err != nil {
+// 			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
+// 			os.Exit(1)
+// 		}
+
+// 		err = setRegistry(uint(registryID))
+
+// 		if err != nil {
+// 			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
+// 			os.Exit(1)
+// 		}
+// 	},
+// }
+
+// var setHelmRepoCmd = &cobra.Command{
+// 	Use:   "set-helmrepo [id]",
+// 	Args:  cobra.ExactArgs(1),
+// 	Short: "Saves the helm repo id in the default configuration",
+// 	Run: func(cmd *cobra.Command, args []string) {
+// 		hrID, err := strconv.ParseUint(args[0], 10, 64)
+
+// 		if err != nil {
+// 			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
+// 			os.Exit(1)
+// 		}
+
+// 		err = setHelmRepo(uint(hrID))
+
+// 		if err != nil {
+// 			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
+// 			os.Exit(1)
+// 		}
+// 	},
+// }
+
+// var config.SetHostCmd = &cobra.Command{
+// 	Use:   "set-host [host]",
+// 	Args:  cobra.ExactArgs(1),
+// 	Short: "Saves the host in the default configuration",
+// 	Run: func(cmd *cobra.Command, args []string) {
+// 		err := config.SetHost(args[0])
+
+// 		if err != nil {
+// 			color.New(color.FgRed).Printf("An error occurred: %v\n", err)
+// 			os.Exit(1)
+// 		}
+// 	},
+// }
+
+// func init() {
+// 	rootCmd.AddCommand(configCmd)
+
+// 	configCmd.AddCommand(config.SetProjectCmd)
+// 	configCmd.AddCommand(config.SetClusterCmd)
+// 	configCmd.AddCommand(config.SetHostCmd)
+// 	configCmd.AddCommand(setRegistryCmd)
+// 	configCmd.AddCommand(setHelmRepoCmd)
+// }
+
+// func setDriver(driver string) error {
+// 	viper.Set("driver", driver)
+// 	err := viper.WriteConfig()
+// 	color.New(color.FgGreen).Printf("Set the current driver as %s\n", driver)
+// 	return err
+// }
+
+// func getDriver() string {
+// 	if driver != "" {
+// 		return driver
+// 	}
+
+// 	if opts.driver != "" {
+// 		return opts.driver
+// 	}
+
+// 	return viper.GetString("driver")
+// }
+
+// func printConfig() error {
+// 	config, err := ioutil.ReadFile(filepath.Join(home, ".porter", "porter.yaml"))
+
+// 	if err != nil {
+// 		return err
+// 	}
+
+// 	fmt.Printf(string(config))
+
+// 	return nil
+// }
+
+// func config.SetProject(id uint) error {
+// 	viper.Set("project", id)
+// 	color.New(color.FgGreen).Printf("Set the current project id as %d\n", id)
+// 	return viper.WriteConfig()
+// }
+
+// func config.SetCluster(id uint) error {
+// 	viper.Set("cluster", id)
+// 	color.New(color.FgGreen).Printf("Set the current cluster id as %d\n", id)
+// 	return viper.WriteConfig()
+// }
+
+// func setRegistry(id uint) error {
+// 	viper.Set("registry", id)
+// 	color.New(color.FgGreen).Printf("Set the current registry id as %d\n", id)
+// 	return viper.WriteConfig()
+// }
+
+// func setHelmRepo(id uint) error {
+// 	viper.Set("helm_repo", id)
+// 	color.New(color.FgGreen).Printf("Set the current helm repo id as %d\n", id)
+// 	return viper.WriteConfig()
+// }
+
+// func config.SetHost(host string) error {
+// 	viper.Set("host", host)
+// 	err := viper.WriteConfig()
+// 	color.New(color.FgGreen).Printf("Set the current host as %s\n", host)
+// 	return err
+// }
+
+// func config.SetToken(token string) error {
+// 	viper.Set("token", token)
+// 	err := viper.WriteConfig()
+// 	return err
+// }
+
+// func config.Host string {
+// 	if host != "" {
+// 		return host
+// 	}
+
+// 	return viper.GetString("host")
+// }
+
+// func config.Token string {
+// 	return viper.GetString("token")
+// }
+
+// func config.Cluster() uint {
+// 	if clusterID != 0 {
+// 		return clusterID
+// 	}
+
+// 	return viper.GetUint("cluster")
+// }
+
+// func config.Registry uint {
+// 	if registryID != 0 {
+// 		return registryID
+// 	}
+
+// 	return viper.GetUint("registry")
+// }
+
+// func config.HelmRepo() uint {
+// 	if helmRepoID != 0 {
+// 		return helmRepoID
+// 	}
+
+// 	return viper.GetUint("helm_repo")
+// }
+
+// func config.Project uint {
+// 	if projectID != 0 {
+// 		return projectID
+// 	}
+
+// 	return viper.GetUint("project")
+// }

+ 16 - 30
cli/cmd/connect.go

@@ -121,20 +121,6 @@ func init() {
 
 	connectCmd.AddCommand(connectKubeconfigCmd)
 
-	connectCmd.PersistentFlags().StringVar(
-		&host,
-		"host",
-		getHost(),
-		"host url of Porter instance",
-	)
-
-	projectID = *connectCmd.PersistentFlags().UintP(
-		"project-id",
-		"p",
-		getProjectID(),
-		"project id to use",
-	)
-
 	connectKubeconfigCmd.PersistentFlags().StringVarP(
 		&kubeconfigPath,
 		"kubeconfig",
@@ -161,7 +147,7 @@ func init() {
 func runConnectKubeconfig(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
 	isLocal := false
 
-	if getDriver() == "local" {
+	if config.Driver == "local" {
 		isLocal = true
 	}
 
@@ -169,7 +155,7 @@ func runConnectKubeconfig(_ *api.AuthCheckResponse, client *api.Client, _ []stri
 		client,
 		kubeconfigPath,
 		*contexts,
-		getProjectID(),
+		config.Project,
 		isLocal,
 	)
 
@@ -177,90 +163,90 @@ func runConnectKubeconfig(_ *api.AuthCheckResponse, client *api.Client, _ []stri
 		return err
 	}
 
-	return setCluster(id)
+	return config.SetCluster(id)
 }
 
 func runConnectECR(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
 	regID, err := connect.ECR(
 		client,
-		getProjectID(),
+		config.Project,
 	)
 
 	if err != nil {
 		return err
 	}
 
-	return setRegistry(regID)
+	return config.SetRegistry(regID)
 }
 
 func runConnectGCR(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
 	regID, err := connect.GCR(
 		client,
-		getProjectID(),
+		config.Project,
 	)
 
 	if err != nil {
 		return err
 	}
 
-	return setRegistry(regID)
+	return config.SetRegistry(regID)
 }
 
 func runConnectDOCR(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
 	regID, err := connect.DOCR(
 		client,
-		getProjectID(),
+		config.Project,
 	)
 
 	if err != nil {
 		return err
 	}
 
-	return setRegistry(regID)
+	return config.SetRegistry(regID)
 }
 
 func runConnectDockerhub(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
 	regID, err := connect.Dockerhub(
 		client,
-		getProjectID(),
+		config.Project,
 	)
 
 	if err != nil {
 		return err
 	}
 
-	return setRegistry(regID)
+	return config.SetRegistry(regID)
 }
 
 func runConnectRegistry(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
 	regID, err := connect.Registry(
 		client,
-		getProjectID(),
+		config.Project,
 	)
 
 	if err != nil {
 		return err
 	}
 
-	return setRegistry(regID)
+	return config.SetRegistry(regID)
 }
 
 func runConnectHelmRepoBasic(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
 	hrID, err := connect.Helm(
 		client,
-		getProjectID(),
+		config.Project,
 	)
 
 	if err != nil {
 		return err
 	}
 
-	return setHelmRepo(hrID)
+	return config.SetHelmRepo(hrID)
 }
 
 func runConnectActions(_ *api.AuthCheckResponse, client *api.Client, _ []string) error {
 	return connect.Actions(
 		client,
-		getProjectID(),
+		config.Project,
 	)
 }

+ 57 - 0
cli/cmd/deploy.go

@@ -0,0 +1,57 @@
+package cmd
+
+import (
+	"os"
+
+	"github.com/fatih/color"
+	"github.com/porter-dev/porter/cli/cmd/api"
+	"github.com/spf13/cobra"
+)
+
+var app = ""
+
+// deployCmd represents the "porter deploy" base command when called
+// without any subcommands
+var deployCmd = &cobra.Command{
+	Use:   "app deploy",
+	Short: "Builds and deploys a specified application given by the --app flag.",
+	Run: func(cmd *cobra.Command, args []string) {
+		err := checkLoginAndRun(args, deploy)
+
+		if err != nil {
+			os.Exit(1)
+		}
+	},
+}
+
+func init() {
+	rootCmd.AddCommand(deployCmd)
+
+	deployCmd.PersistentFlags().StringVar(
+		&app,
+		"app",
+		"",
+		"Application in the Porter dashboard",
+	)
+}
+
+func deploy(_ *api.AuthCheckResponse, client *api.Client, args []string) error {
+	color.New(color.FgGreen).Println("Deploying app:", app)
+
+	return nil
+}
+
+// deployInit first reads the release given by the --app or the --job flag. It then
+// configures docker with the registries linked to the project.
+func deployInit(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
+	return dockerConfig(resp, client, args)
+}
+
+// deploySetEnv reads the build environment variables from a release and sets them using
+// os.SetEnv
+// func deploySetBuildEnv() error {
+
+// }
+
+// deployBuild uses the configuration stored in the release to
+// func deployBuild()

+ 10 - 10
cli/cmd/docker.go

@@ -45,7 +45,7 @@ func init() {
 }
 
 func dockerConfig(user *api.AuthCheckResponse, client *api.Client, args []string) error {
-	pID := getProjectID()
+	pID := config.Project
 
 	// get all registries that should be added
 	regToAdd := make([]string, 0)
@@ -124,7 +124,7 @@ func dockerConfig(user *api.AuthCheckResponse, client *api.Client, args []string
 		}
 	}
 
-	config := &configfile.ConfigFile{
+	configFile := &configfile.ConfigFile{
 		Filename: dockerConfigFile,
 	}
 
@@ -134,8 +134,8 @@ func dockerConfig(user *api.AuthCheckResponse, client *api.Client, args []string
 		return err
 	}
 
-	if config.CredentialHelpers == nil {
-		config.CredentialHelpers = make(map[string]string)
+	if configFile.CredentialHelpers == nil {
+		configFile.CredentialHelpers = make(map[string]string)
 	}
 
 	for _, regURL := range regToAdd {
@@ -144,7 +144,7 @@ func dockerConfig(user *api.AuthCheckResponse, client *api.Client, args []string
 		if strings.Contains(regURL, "index.docker.io") {
 			isAuthenticated := false
 
-			for key, _ := range config.AuthConfigs {
+			for key, _ := range configFile.AuthConfigs {
 				if key == "https://index.docker.io/v1/" {
 					isAuthenticated = true
 				}
@@ -152,7 +152,7 @@ func dockerConfig(user *api.AuthCheckResponse, client *api.Client, args []string
 
 			if !isAuthenticated {
 				// get a dockerhub token from the Porter API
-				tokenResp, err := client.GetDockerhubAuthorizationToken(context.Background(), getProjectID())
+				tokenResp, err := client.GetDockerhubAuthorizationToken(context.Background(), config.Project)
 
 				if err != nil {
 					return err
@@ -170,21 +170,21 @@ func dockerConfig(user *api.AuthCheckResponse, client *api.Client, args []string
 					return fmt.Errorf("Invalid token: expected two parts, got %d", len(parts))
 				}
 
-				config.AuthConfigs["https://index.docker.io/v1/"] = types.AuthConfig{
+				configFile.AuthConfigs["https://index.docker.io/v1/"] = types.AuthConfig{
 					Auth:     tokenResp.Token,
 					Username: parts[0],
 					Password: parts[1],
 				}
 
 				// since we're using token-based auth, unset the credstore
-				config.CredentialsStore = ""
+				configFile.CredentialsStore = ""
 			}
 		} else {
-			config.CredentialHelpers[regURL] = "porter"
+			configFile.CredentialHelpers[regURL] = "porter"
 		}
 	}
 
-	return config.Save()
+	return configFile.Save()
 }
 
 func downloadCredMatchingRelease() error {

+ 2 - 2
cli/cmd/errors.go

@@ -20,7 +20,7 @@ func checkLoginAndRun(args []string, runner func(user *api.AuthCheckResponse, cl
 			red.Print("You are not logged in. Log in using \"porter auth login\"\n")
 			return nil
 		} else if strings.Contains(err.Error(), "connection refused") {
-			red.Printf("Unable to connect to the Porter server at %s\n", getHost())
+			red.Printf("Unable to connect to the Porter server at %s\n", config.Host)
 			red.Print("To set a different host, run \"porter config set-host [HOST]\"\n")
 			red.Print("To start a local server, run \"porter server start\"\n")
 			return nil
@@ -39,7 +39,7 @@ func checkLoginAndRun(args []string, runner func(user *api.AuthCheckResponse, cl
 			red.Print("You do not have the necessary permissions to view this resource")
 			return nil
 		} else if strings.Contains(err.Error(), "connection refused") {
-			red.Printf("Unable to connect to the Porter server at %s\n", getHost())
+			red.Printf("Unable to connect to the Porter server at %s\n", config.Host)
 			red.Print("To set a different host, run \"porter config set-host [HOST]\"")
 			red.Print("To start a local server, run \"porter server start\"")
 			return nil

+ 5 - 10
cli/cmd/helm_repo.go

@@ -53,12 +53,7 @@ var helmRepoChartListCmd = &cobra.Command{
 func init() {
 	rootCmd.AddCommand(helmRepoCmd)
 
-	helmRepoCmd.PersistentFlags().UintVar(
-		&helmRepoID,
-		"helmrepo-id",
-		0,
-		"id of the helm repo",
-	)
+	helmRepoCmd.PersistentFlags().AddFlagSet(helmRepoFlagSet)
 
 	helmRepoCmd.AddCommand(helmRepoListCmd)
 	helmRepoCmd.AddCommand(helmRepoChartCmd)
@@ -67,7 +62,7 @@ func init() {
 }
 
 func listHelmRepos(user *api.AuthCheckResponse, client *api.Client, args []string) error {
-	pID := getProjectID()
+	pID := config.Project
 
 	hrs, err := client.ListHelmRepos(
 		context.Background(),
@@ -83,7 +78,7 @@ func listHelmRepos(user *api.AuthCheckResponse, client *api.Client, args []strin
 
 	fmt.Fprintf(w, "%s\t%s\t%s\t%s\n", "ID", "NAME", "URL", "SERVICE")
 
-	currHelmID := getHelmRepoID()
+	currHelmID := config.HelmRepo
 
 	for _, hr := range hrs {
 		if currHelmID == hr.ID {
@@ -99,8 +94,8 @@ func listHelmRepos(user *api.AuthCheckResponse, client *api.Client, args []strin
 }
 
 func listHelmRepoCharts(user *api.AuthCheckResponse, client *api.Client, args []string) error {
-	pID := getProjectID()
-	hrID := getHelmRepoID()
+	pID := config.Project
+	hrID := config.HelmRepo
 
 	charts, err := client.ListCharts(
 		context.Background(),

+ 2 - 9
cli/cmd/open.go

@@ -18,20 +18,13 @@ var openCmd = &cobra.Command{
 		user, err := client.AuthCheck(context.Background())
 
 		if err == nil {
-			utils.OpenBrowser(fmt.Sprintf("%s/login?email=%s", getHost(), user.Email))
+			utils.OpenBrowser(fmt.Sprintf("%s/login?email=%s", config.Host, user.Email))
 		} else {
-			utils.OpenBrowser(fmt.Sprintf("%s/register", getHost()))
+			utils.OpenBrowser(fmt.Sprintf("%s/register", config.Host))
 		}
 	},
 }
 
 func init() {
 	rootCmd.AddCommand(openCmd)
-
-	rootCmd.PersistentFlags().StringVar(
-		&host,
-		"host",
-		getHost(),
-		"host url of Porter instance",
-	)
 }

+ 2 - 11
cli/cmd/project.go

@@ -63,17 +63,8 @@ var listProjectCmd = &cobra.Command{
 func init() {
 	rootCmd.AddCommand(projectCmd)
 
-	projectCmd.PersistentFlags().StringVar(
-		&host,
-		"host",
-		getHost(),
-		"host url of Porter instance",
-	)
-
 	projectCmd.AddCommand(createProjectCmd)
-
 	projectCmd.AddCommand(deleteProjectCmd)
-
 	projectCmd.AddCommand(listProjectCmd)
 }
 
@@ -88,7 +79,7 @@ func createProject(_ *api.AuthCheckResponse, client *api.Client, args []string)
 
 	color.New(color.FgGreen).Printf("Created project with name %s and id %d\n", args[0], resp.ID)
 
-	return setProject(resp.ID)
+	return config.SetProject(resp.ID)
 }
 
 func listProjects(user *api.AuthCheckResponse, client *api.Client, args []string) error {
@@ -103,7 +94,7 @@ func listProjects(user *api.AuthCheckResponse, client *api.Client, args []string
 
 	fmt.Fprintf(w, "%s\t%s\n", "ID", "NAME")
 
-	currProjectID := getProjectID()
+	currProjectID := config.Project
 
 	for _, project := range projects {
 		if currProjectID == project.ID {

+ 8 - 13
cli/cmd/registry.go

@@ -87,12 +87,7 @@ var registryImageListCmd = &cobra.Command{
 func init() {
 	rootCmd.AddCommand(registryCmd)
 
-	registryCmd.PersistentFlags().UintVar(
-		&registryID,
-		"registry-id",
-		0,
-		"id of the registry",
-	)
+	registryCmd.PersistentFlags().AddFlagSet(registryFlagSet)
 
 	registryCmd.AddCommand(registryReposCmd)
 	registryCmd.AddCommand(registryListCmd)
@@ -105,7 +100,7 @@ func init() {
 }
 
 func listRegistries(user *api.AuthCheckResponse, client *api.Client, args []string) error {
-	pID := getProjectID()
+	pID := config.Project
 
 	// get the list of namespaces
 	registries, err := client.ListRegistries(
@@ -122,7 +117,7 @@ func listRegistries(user *api.AuthCheckResponse, client *api.Client, args []stri
 
 	fmt.Fprintf(w, "%s\t%s\t%s\n", "ID", "URL", "SERVICE")
 
-	currRegistryID := getRegistryID()
+	currRegistryID := config.Registry
 
 	for _, registry := range registries {
 		if currRegistryID == registry.ID {
@@ -157,7 +152,7 @@ func deleteRegistry(user *api.AuthCheckResponse, client *api.Client, args []stri
 			return err
 		}
 
-		err = client.DeleteProjectRegistry(context.Background(), getProjectID(), uint(id))
+		err = client.DeleteProjectRegistry(context.Background(), config.Project, uint(id))
 
 		if err != nil {
 			return err
@@ -170,8 +165,8 @@ func deleteRegistry(user *api.AuthCheckResponse, client *api.Client, args []stri
 }
 
 func listRepos(user *api.AuthCheckResponse, client *api.Client, args []string) error {
-	pID := getProjectID()
-	rID := getRegistryID()
+	pID := config.Project
+	rID := config.Registry
 
 	// get the list of namespaces
 	repos, err := client.ListRegistryRepositories(
@@ -199,8 +194,8 @@ func listRepos(user *api.AuthCheckResponse, client *api.Client, args []string) e
 }
 
 func listImages(user *api.AuthCheckResponse, client *api.Client, args []string) error {
-	pID := getProjectID()
-	rID := getRegistryID()
+	pID := config.Project
+	rID := config.Registry
 	repoName := args[0]
 
 	// get the list of namespaces

+ 5 - 41
cli/cmd/root.go

@@ -1,9 +1,7 @@
 package cmd
 
 import (
-	"io/ioutil"
 	"os"
-	"path/filepath"
 
 	"github.com/fatih/color"
 	"github.com/porter-dev/porter/cli/cmd/api"
@@ -26,6 +24,8 @@ var home = homedir.HomeDir()
 func Execute() {
 	Setup()
 
+	rootCmd.PersistentFlags().AddFlagSet(defaultFlagSet)
+
 	if err := rootCmd.Execute(); err != nil {
 		color.New(color.FgRed).Println(err)
 		os.Exit(1)
@@ -33,49 +33,13 @@ func Execute() {
 }
 
 func Setup() {
-	// check that the .porter folder exists; create if not
-	porterDir := filepath.Join(home, ".porter")
-
-	if _, err := os.Stat(porterDir); os.IsNotExist(err) {
-		os.Mkdir(porterDir, 0700)
-	} else if err != nil {
-		color.New(color.FgRed).Printf("%v\n", err)
-		os.Exit(1)
-	}
-
-	viper.SetConfigName("porter")
-	viper.SetConfigType("yaml")
-	viper.AddConfigPath(porterDir)
-
-	err := viper.ReadInConfig()
-
-	if err != nil {
-		if _, ok := err.(viper.ConfigFileNotFoundError); ok {
-			// create blank config file
-			err := ioutil.WriteFile(filepath.Join(home, ".porter", "porter.yaml"), []byte{}, 0644)
-
-			if err != nil {
-				color.New(color.FgRed).Printf("%v\n", err)
-				os.Exit(1)
-			}
-		} else {
-			// Config file was found but another error was produced
-			color.New(color.FgRed).Printf("%v\n", err)
-			os.Exit(1)
-		}
-	}
-
-	// create defaults if configs are not set
-	if viper.GetString("host") == "" {
-		viper.Set("host", "https://dashboard.getporter.dev")
-		viper.WriteConfig()
-	}
+	InitAndLoadConfig()
 }
 
 func GetAPIClient() *api.Client {
 	if token := viper.GetString("token"); token != "" {
-		return api.NewClientWithToken(getHost()+"/api", token)
+		return api.NewClientWithToken(config.Host+"/api", token)
 	}
 
-	return api.NewClient(getHost()+"/api", "cookie.json")
+	return api.NewClient(config.Host+"/api", "cookie.json")
 }

+ 4 - 11
cli/cmd/run.go

@@ -38,13 +38,6 @@ var runCmd = &cobra.Command{
 func init() {
 	rootCmd.AddCommand(runCmd)
 
-	runCmd.PersistentFlags().StringVar(
-		&host,
-		"host",
-		getHost(),
-		"host url of Porter instance",
-	)
-
 	runCmd.PersistentFlags().StringVar(
 		&namespace,
 		"namespace",
@@ -87,8 +80,8 @@ func run(_ *api.AuthCheckResponse, client *api.Client, args []string) error {
 }
 
 func getRESTConfig(client *api.Client) (*rest.Config, error) {
-	pID := getProjectID()
-	cID := getClusterID()
+	pID := config.Project
+	cID := config.Cluster
 
 	kubeResp, err := client.GetKubeconfig(context.TODO(), pID, cID)
 
@@ -121,8 +114,8 @@ func getRESTConfig(client *api.Client) (*rest.Config, error) {
 }
 
 func getPods(client *api.Client, namespace, releaseName string) ([]string, error) {
-	pID := getProjectID()
-	cID := getClusterID()
+	pID := config.Project
+	cID := config.Cluster
 
 	resp, err := client.GetK8sAllPods(context.TODO(), pID, cID, namespace, releaseName)
 

+ 8 - 13
cli/cmd/server.go

@@ -34,8 +34,8 @@ var startCmd = &cobra.Command{
 	Use:   "start",
 	Short: "Starts a Porter server instance on the host",
 	Run: func(cmd *cobra.Command, args []string) {
-		if getDriver() == "docker" {
-			setDriver("docker")
+		if config.Driver == "docker" {
+			config.SetDriver("docker")
 
 			err := startDocker(
 				opts.imageTag,
@@ -57,7 +57,7 @@ var startCmd = &cobra.Command{
 				os.Exit(1)
 			}
 		} else {
-			setDriver("local")
+			config.SetDriver("local")
 			err := startLocal(
 				opts.db,
 				*opts.port,
@@ -76,7 +76,7 @@ var stopCmd = &cobra.Command{
 	Use:   "stop",
 	Short: "Stops a Porter instance running on the Docker engine",
 	Run: func(cmd *cobra.Command, args []string) {
-		if getDriver() == "docker" {
+		if config.Driver == "docker" {
 			if err := stopDocker(); err != nil {
 				color.New(color.FgRed).Println("Shutdown unsuccessful:", err.Error())
 				os.Exit(1)
@@ -91,6 +91,8 @@ func init() {
 	serverCmd.AddCommand(startCmd)
 	serverCmd.AddCommand(stopCmd)
 
+	serverCmd.PersistentFlags().AddFlagSet(driverFlagSet)
+
 	startCmd.PersistentFlags().StringVar(
 		&opts.db,
 		"db",
@@ -98,13 +100,6 @@ func init() {
 		"the db to use, one of sqlite or postgres",
 	)
 
-	startCmd.PersistentFlags().StringVar(
-		&opts.driver,
-		"driver",
-		"local",
-		"the driver to use, one of \"local\" or \"docker\"",
-	)
-
 	startCmd.PersistentFlags().StringVar(
 		&opts.imageTag,
 		"image-tag",
@@ -157,7 +152,7 @@ func startDocker(
 
 	green.Printf("Server ready: listening on localhost:%d\n", port)
 
-	return setHost(fmt.Sprintf("http://localhost:%d", port))
+	return config.SetHost(fmt.Sprintf("http://localhost:%d", port))
 }
 
 func startLocal(
@@ -168,7 +163,7 @@ func startLocal(
 		return fmt.Errorf("postgres not available for local driver, run \"porter server start --db postgres --driver docker\"")
 	}
 
-	setHost(fmt.Sprintf("http://localhost:%d", port))
+	config.SetHost(fmt.Sprintf("http://localhost:%d", port))
 
 	porterDir := filepath.Join(home, ".porter")
 	cmdPath := filepath.Join(home, ".porter", "portersvr")

+ 1 - 0
go.mod

@@ -63,6 +63,7 @@ require (
 	github.com/sendgrid/sendgrid-go v3.8.0+incompatible
 	github.com/sirupsen/logrus v1.7.0
 	github.com/spf13/cobra v1.0.0
+	github.com/spf13/pflag v1.0.5
 	github.com/spf13/viper v1.4.0
 	github.com/stretchr/testify v1.6.1
 	github.com/xtgo/uuid v0.0.0-20140804021211-a0b114877d4c // indirect