Browse Source

update CLI help for porter create

Alexander Belanger 4 years ago
parent
commit
b5b2ad4700
3 changed files with 210 additions and 105 deletions
  1. 67 10
      cli/cmd/create.go
  2. 86 95
      cli/cmd/deploy.go
  3. 57 0
      cli/cmd/deploy/create.go

+ 67 - 10
cli/cmd/create.go

@@ -19,7 +19,48 @@ import (
 var createCmd = &cobra.Command{
 var createCmd = &cobra.Command{
 	Use:   "create [kind]",
 	Use:   "create [kind]",
 	Args:  cobra.ExactArgs(1),
 	Args:  cobra.ExactArgs(1),
-	Short: "TODO.",
+	Short: "Creates a new application with name given by the --app flag.",
+	Long: fmt.Sprintf(`
+%s 
+
+Creates a new application with name given by the --app flag and a "kind", which can be one of 
+web, worker, or job. For example:
+
+  %s
+
+To modify the default configuration of the application, you can pass a values.yaml file in via the 
+--values flag. 
+
+  %s
+
+To read more about the configuration options, go here: 
+
+https://docs.getporter.dev/docs/deploying-from-the-cli#common-configuration-options
+
+This command will automatically build from a local path, and will create a new Docker image in your 
+default Docker registry. The path can be configured via the --path flag. For example:
+  
+  %s
+
+To connect the application to Github, so that the application rebuilds and redeploys on each push 
+to a Github branch, you can specify "--source github". If your local branch is set to track changes 
+from an upstream remote branch, Porter will try to use the connected remote and remote branch as the 
+Github repository to link to. Otherwise, Porter will use the remote given by origin. For example:
+
+  %s
+
+To deploy an application from a Docker registry, use "--source registry" and pass the image in via the
+--image flag. The image flag must be of the form repository:tag. For example:
+
+  %s 
+`,
+		color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter create\":"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter create web --app example-app"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter create web --app example-app --values values.yaml"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter create web --app example-app --path ./path/to/app"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter create web --app example-app --source github"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter create web --app example-app --source registry --image gcr.io/snowflake-12345/example-app:latest"),
+	),
 	Run: func(cmd *cobra.Command, args []string) {
 	Run: func(cmd *cobra.Command, args []string) {
 		err := checkLoginAndRun(args, createFull)
 		err := checkLoginAndRun(args, createFull)
 
 
@@ -32,24 +73,25 @@ var createCmd = &cobra.Command{
 var name string
 var name string
 var values string
 var values string
 var source string
 var source string
+var image string
 
 
 func init() {
 func init() {
 	rootCmd.AddCommand(createCmd)
 	rootCmd.AddCommand(createCmd)
 
 
 	createCmd.PersistentFlags().StringVar(
 	createCmd.PersistentFlags().StringVar(
 		&name,
 		&name,
-		"name",
+		"app",
 		"",
 		"",
-		"Name of the new application/job/worker.",
+		"name of the new application/job/worker.",
 	)
 	)
 
 
-	createCmd.MarkPersistentFlagRequired("name")
+	createCmd.MarkPersistentFlagRequired("app")
 
 
 	createCmd.PersistentFlags().BoolVar(
 	createCmd.PersistentFlags().BoolVar(
 		&local,
 		&local,
 		"local",
 		"local",
 		true,
 		true,
-		"Whether local context should be used for build",
+		"whether local context should be used for build",
 	)
 	)
 
 
 	createCmd.PersistentFlags().StringVarP(
 	createCmd.PersistentFlags().StringVarP(
@@ -57,14 +99,14 @@ func init() {
 		"path",
 		"path",
 		"p",
 		"p",
 		".",
 		".",
-		"If local build, the path to the build directory",
+		"if local build, the path to the build directory",
 	)
 	)
 
 
 	createCmd.PersistentFlags().StringVar(
 	createCmd.PersistentFlags().StringVar(
 		&namespace,
 		&namespace,
 		"namespace",
 		"namespace",
 		"default",
 		"default",
-		"Namespace of the application",
+		"namespace of the application",
 	)
 	)
 
 
 	createCmd.PersistentFlags().StringVarP(
 	createCmd.PersistentFlags().StringVarP(
@@ -72,7 +114,7 @@ func init() {
 		"values",
 		"values",
 		"v",
 		"v",
 		"",
 		"",
-		"Filepath to a values.yaml file",
+		"filepath to a values.yaml file",
 	)
 	)
 
 
 	createCmd.PersistentFlags().StringVar(
 	createCmd.PersistentFlags().StringVar(
@@ -93,7 +135,14 @@ func init() {
 		&source,
 		&source,
 		"source",
 		"source",
 		"local",
 		"local",
-		"the type of source (\"local\" or \"github\")",
+		"the type of source (\"local\", \"github\", or \"registry\")",
+	)
+
+	createCmd.PersistentFlags().StringVar(
+		&image,
+		"image",
+		"",
+		"if the source is \"registry\", the image to use, in image-path:tag format",
 	)
 	)
 }
 }
 
 
@@ -152,8 +201,16 @@ func createFull(resp *api.AuthCheckResponse, client *api.Client, args []string)
 		}
 		}
 
 
 		color.New(color.FgGreen).Printf("Your web application is ready at: %s\n", subdomain)
 		color.New(color.FgGreen).Printf("Your web application is ready at: %s\n", subdomain)
-	} else {
+	} else if source == "github" {
 		return createFromGithub(createAgent, valuesObj)
 		return createFromGithub(createAgent, valuesObj)
+	} else {
+		subdomain, err := createAgent.CreateFromRegistry(image, valuesObj)
+
+		if err != nil {
+			return err
+		}
+
+		color.New(color.FgGreen).Printf("Your web application is ready at: %s\n", subdomain)
 	}
 	}
 
 
 	return nil
 	return nil

+ 86 - 95
cli/cmd/deploy.go

@@ -10,28 +10,31 @@ import (
 	"github.com/spf13/cobra"
 	"github.com/spf13/cobra"
 )
 )
 
 
-// deployCmd represents the "porter deploy" base command when called
+// updateCmd represents the "porter update" base command when called
 // without any subcommands
 // without any subcommands
-var deployCmd = &cobra.Command{
-	Use:   "deploy",
-	Short: "Builds and deploys a specified application given by the --app flag.",
+var updateCmd = &cobra.Command{
+	Use:   "update",
+	Short: "Builds and updates a specified application given by the --app flag.",
 	Long: fmt.Sprintf(`
 	Long: fmt.Sprintf(`
 %s 
 %s 
 
 
-Builds and deploys a specified application given by the --app flag. For example:
+Builds and updates a specified application given by the --app flag. For example:
 
 
   %s
   %s
 
 
-If the application has a remote Git repository source configured, this command uses the latest commit 
-from the remote repo and branch to deploy an application. It will use the latest commit as the image 
-tag. 
-
-To build from a local directory, you must specify the --local flag. The path can be configured via the 
+This command will automatically build from a local path. The path can be configured via the 
 --path flag. You can also overwrite the tag using the --tag flag. For example, to build from the 
 --path flag. You can also overwrite the tag using the --tag flag. For example, to build from the 
 local directory ~/path-to-dir with the tag "testing":
 local directory ~/path-to-dir with the tag "testing":
 
 
   %s
   %s
 
 
+If the application has a remote Git repository source configured, you can specify that the remote
+Git repository should be used to build the new image by specifying "--local false". Porter will use 
+the latest commit from the remote repo and branch to update an application, and will use the latest 
+commit as the image tag.
+
+  %s
+
 To add new configuration or update existing configuration, you can pass a values.yaml file in via the 
 To add new configuration or update existing configuration, you can pass a values.yaml file in via the 
 --values flag. For example;
 --values flag. For example;
 
 
@@ -44,28 +47,16 @@ in your remote Git repository. For example, if a Dockerfile is found at ./docker
 specify it as follows:
 specify it as follows:
 
 
   %s
   %s
-
-If an application does not have a remote Git repository source, this command will attempt to use a 
-cloud-native buildpack builder and build from the current directory. If this is the desired behavior,
-you do not need to configure additional flags:
-
-  %s
-
-If you would like to build from a Dockerfile instead, use the flag --dockerfile and "--method docker"
-as documented above. For example:
-
-  %s
 `,
 `,
-		color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter deploy\":"),
-		color.New(color.FgGreen, color.Bold).Sprintf("porter deploy --app example-app"),
-		color.New(color.FgGreen, color.Bold).Sprintf("porter deploy --app remote-git-app --local --path ~/path-to-dir --tag testing"),
-		color.New(color.FgGreen, color.Bold).Sprintf("porter deploy --app remote-git-app --values my-values.yaml"),
-		color.New(color.FgGreen, color.Bold).Sprintf("porter deploy --app remote-git-app --method docker --dockerfile ./docker/prod.Dockerfile"),
-		color.New(color.FgGreen, color.Bold).Sprintf("porter deploy --app local-app"),
-		color.New(color.FgGreen, color.Bold).Sprintf("porter deploy --app local-app --method docker --dockerfile ~/porter-test/prod.Dockerfile"),
+		color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter update\":"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter update --app example-app"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter update --app example-app --path ~/path-to-dir --tag testing"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter update --app remote-git-app --local false"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter update --app example-app --values my-values.yaml"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter update --app example-app --method docker --dockerfile ./docker/prod.Dockerfile"),
 	),
 	),
 	Run: func(cmd *cobra.Command, args []string) {
 	Run: func(cmd *cobra.Command, args []string) {
-		err := checkLoginAndRun(args, deployFull)
+		err := checkLoginAndRun(args, updateFull)
 
 
 		if err != nil {
 		if err != nil {
 			os.Exit(1)
 			os.Exit(1)
@@ -73,7 +64,7 @@ as documented above. For example:
 	},
 	},
 }
 }
 
 
-var deployGetEnvCmd = &cobra.Command{
+var updateGetEnvCmd = &cobra.Command{
 	Use:   "get-env",
 	Use:   "get-env",
 	Short: "Gets environment variables for a deployment for a specified application given by the --app flag.",
 	Short: "Gets environment variables for a deployment for a specified application given by the --app flag.",
 	Long: fmt.Sprintf(`
 	Long: fmt.Sprintf(`
@@ -89,12 +80,12 @@ destination path for a .env file. For example:
 
 
   %s
   %s
 `,
 `,
-		color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter deploy get-env\":"),
-		color.New(color.FgGreen, color.Bold).Sprintf("porter deploy get-env --app example-app | xargs"),
-		color.New(color.FgGreen, color.Bold).Sprintf("porter deploy get-env --app example-app --file .env"),
+		color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter update get-env\":"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter update get-env --app example-app | xargs"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter update get-env --app example-app --file .env"),
 	),
 	),
 	Run: func(cmd *cobra.Command, args []string) {
 	Run: func(cmd *cobra.Command, args []string) {
-		err := checkLoginAndRun(args, deployGetEnv)
+		err := checkLoginAndRun(args, updateGetEnv)
 
 
 		if err != nil {
 		if err != nil {
 			os.Exit(1)
 			os.Exit(1)
@@ -102,7 +93,7 @@ destination path for a .env file. For example:
 	},
 	},
 }
 }
 
 
-var deployBuildCmd = &cobra.Command{
+var updateBuildCmd = &cobra.Command{
 	Use:   "build",
 	Use:   "build",
 	Short: "Builds a new version of the application specified by the --app flag.",
 	Short: "Builds a new version of the application specified by the --app flag.",
 	Long: fmt.Sprintf(`
 	Long: fmt.Sprintf(`
@@ -131,13 +122,13 @@ for the application:
 
 
   %s
   %s
 `,
 `,
-		color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter deploy build\":"),
-		color.New(color.FgGreen, color.Bold).Sprintf("porter deploy build --app example-app"),
-		color.New(color.FgGreen, color.Bold).Sprintf("porter deploy build --app example-app --method docker"),
-		color.New(color.FgGreen, color.Bold).Sprintf("porter deploy build --app example-app --method docker --dockerfile ./prod.Dockerfile"),
+		color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter update build\":"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter update build --app example-app"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter update build --app example-app --method docker"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter update build --app example-app --method docker --dockerfile ./prod.Dockerfile"),
 	),
 	),
 	Run: func(cmd *cobra.Command, args []string) {
 	Run: func(cmd *cobra.Command, args []string) {
-		err := checkLoginAndRun(args, deployBuild)
+		err := checkLoginAndRun(args, updateBuild)
 
 
 		if err != nil {
 		if err != nil {
 			os.Exit(1)
 			os.Exit(1)
@@ -145,7 +136,7 @@ for the application:
 	},
 	},
 }
 }
 
 
-var deployPushCmd = &cobra.Command{
+var updatePushCmd = &cobra.Command{
 	Use:   "push",
 	Use:   "push",
 	Short: "Pushes a new image for an application specified by the --app flag.",
 	Short: "Pushes a new image for an application specified by the --app flag.",
 	Long: fmt.Sprintf(`
 	Long: fmt.Sprintf(`
@@ -162,11 +153,11 @@ This command will not use your pre-saved authentication set up via "docker login
 are using an image registry that was created outside of Porter, make sure that you have 
 are using an image registry that was created outside of Porter, make sure that you have 
 linked it via "porter connect".
 linked it via "porter connect".
 `,
 `,
-		color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter deploy push\":"),
-		color.New(color.FgGreen, color.Bold).Sprintf("porter deploy push --app nginx --tag new-tag"),
+		color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter update push\":"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter update push --app nginx --tag new-tag"),
 	),
 	),
 	Run: func(cmd *cobra.Command, args []string) {
 	Run: func(cmd *cobra.Command, args []string) {
-		err := checkLoginAndRun(args, deployPush)
+		err := checkLoginAndRun(args, updatePush)
 
 
 		if err != nil {
 		if err != nil {
 			os.Exit(1)
 			os.Exit(1)
@@ -174,8 +165,8 @@ linked it via "porter connect".
 	},
 	},
 }
 }
 
 
-var deployCallWebhookCmd = &cobra.Command{
-	Use:   "update-config",
+var updateConfigCmd = &cobra.Command{
+	Use:   "config",
 	Short: "Updates the configuration for an application specified by the --app flag.",
 	Short: "Updates the configuration for an application specified by the --app flag.",
 	Long: fmt.Sprintf(`
 	Long: fmt.Sprintf(`
 %s 
 %s 
@@ -192,12 +183,12 @@ the image that the application uses if no --values file is specified:
 
 
   %s
   %s
 `,
 `,
-		color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter deploy update-config\":"),
-		color.New(color.FgGreen, color.Bold).Sprintf("porter deploy call-webhook --app example-app --values my-values.yaml"),
-		color.New(color.FgGreen, color.Bold).Sprintf("porter deploy call-webhook --app example-app --tag custom-tag"),
+		color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter update update-config\":"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter update call-webhook --app example-app --values my-values.yaml"),
+		color.New(color.FgGreen, color.Bold).Sprintf("porter update call-webhook --app example-app --tag custom-tag"),
 	),
 	),
 	Run: func(cmd *cobra.Command, args []string) {
 	Run: func(cmd *cobra.Command, args []string) {
-		err := checkLoginAndRun(args, deployUpgrade)
+		err := checkLoginAndRun(args, updateUpgrade)
 
 
 		if err != nil {
 		if err != nil {
 			os.Exit(1)
 			os.Exit(1)
@@ -214,32 +205,32 @@ var dockerfile string
 var method string
 var method string
 
 
 func init() {
 func init() {
-	rootCmd.AddCommand(deployCmd)
+	rootCmd.AddCommand(updateCmd)
 
 
-	deployCmd.PersistentFlags().StringVar(
+	updateCmd.PersistentFlags().StringVar(
 		&app,
 		&app,
 		"app",
 		"app",
 		"",
 		"",
 		"Application in the Porter dashboard",
 		"Application in the Porter dashboard",
 	)
 	)
 
 
-	deployCmd.MarkPersistentFlagRequired("app")
+	updateCmd.MarkPersistentFlagRequired("app")
 
 
-	deployCmd.PersistentFlags().StringVar(
+	updateCmd.PersistentFlags().StringVar(
 		&namespace,
 		&namespace,
 		"namespace",
 		"namespace",
 		"default",
 		"default",
 		"Namespace of the application",
 		"Namespace of the application",
 	)
 	)
 
 
-	deployCmd.PersistentFlags().BoolVar(
+	updateCmd.PersistentFlags().BoolVar(
 		&local,
 		&local,
 		"local",
 		"local",
 		true,
 		true,
 		"Whether local context should be used for build",
 		"Whether local context should be used for build",
 	)
 	)
 
 
-	deployCmd.PersistentFlags().StringVarP(
+	updateCmd.PersistentFlags().StringVarP(
 		&localPath,
 		&localPath,
 		"path",
 		"path",
 		"p",
 		"p",
@@ -247,7 +238,7 @@ func init() {
 		"If local build, the path to the build directory",
 		"If local build, the path to the build directory",
 	)
 	)
 
 
-	deployCmd.PersistentFlags().StringVarP(
+	updateCmd.PersistentFlags().StringVarP(
 		&tag,
 		&tag,
 		"tag",
 		"tag",
 		"t",
 		"t",
@@ -255,7 +246,7 @@ func init() {
 		"the specified tag to use, if not \"latest\"",
 		"the specified tag to use, if not \"latest\"",
 	)
 	)
 
 
-	deployCmd.PersistentFlags().StringVarP(
+	updateCmd.PersistentFlags().StringVarP(
 		&values,
 		&values,
 		"values",
 		"values",
 		"v",
 		"v",
@@ -263,56 +254,56 @@ func init() {
 		"Filepath to a values.yaml file",
 		"Filepath to a values.yaml file",
 	)
 	)
 
 
-	deployCmd.PersistentFlags().StringVar(
+	updateCmd.PersistentFlags().StringVar(
 		&dockerfile,
 		&dockerfile,
 		"dockerfile",
 		"dockerfile",
 		"",
 		"",
 		"the path to the dockerfile",
 		"the path to the dockerfile",
 	)
 	)
 
 
-	deployCmd.PersistentFlags().StringVar(
+	updateCmd.PersistentFlags().StringVar(
 		&method,
 		&method,
 		"method",
 		"method",
 		"",
 		"",
 		"the build method to use (\"docker\" or \"pack\")",
 		"the build method to use (\"docker\" or \"pack\")",
 	)
 	)
 
 
-	deployCmd.AddCommand(deployGetEnvCmd)
+	updateCmd.AddCommand(updateGetEnvCmd)
 
 
-	deployGetEnvCmd.PersistentFlags().StringVar(
+	updateGetEnvCmd.PersistentFlags().StringVar(
 		&getEnvFileDest,
 		&getEnvFileDest,
 		"file",
 		"file",
 		"",
 		"",
 		"file destination for .env files",
 		"file destination for .env files",
 	)
 	)
 
 
-	deployCmd.AddCommand(deployBuildCmd)
-	deployCmd.AddCommand(deployPushCmd)
-	deployCmd.AddCommand(deployCallWebhookCmd)
+	updateCmd.AddCommand(updateBuildCmd)
+	updateCmd.AddCommand(updatePushCmd)
+	updateCmd.AddCommand(updateConfigCmd)
 }
 }
 
 
-func deployFull(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
+func updateFull(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
 	color.New(color.FgGreen).Println("Deploying app:", app)
 	color.New(color.FgGreen).Println("Deploying app:", app)
 
 
-	deployAgent, err := deployGetAgent(client)
+	updateAgent, err := updateGetAgent(client)
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	err = deployBuildWithAgent(deployAgent)
+	err = updateBuildWithAgent(updateAgent)
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	err = deployPushWithAgent(deployAgent)
+	err = updatePushWithAgent(updateAgent)
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	err = deployUpgradeWithAgent(deployAgent)
+	err = updateUpgradeWithAgent(updateAgent)
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err
@@ -321,69 +312,69 @@ func deployFull(resp *api.AuthCheckResponse, client *api.Client, args []string)
 	return nil
 	return nil
 }
 }
 
 
-func deployGetEnv(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
-	deployAgent, err := deployGetAgent(client)
+func updateGetEnv(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
+	updateAgent, err := updateGetAgent(client)
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	buildEnv, err := deployAgent.GetBuildEnv()
+	buildEnv, err := updateAgent.GetBuildEnv()
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	// set the environment variables in the process
 	// set the environment variables in the process
-	err = deployAgent.SetBuildEnv(buildEnv)
+	err = updateAgent.SetBuildEnv(buildEnv)
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	// write the environment variables to either a file or stdout (stdout by default)
 	// write the environment variables to either a file or stdout (stdout by default)
-	return deployAgent.WriteBuildEnv(getEnvFileDest)
+	return updateAgent.WriteBuildEnv(getEnvFileDest)
 }
 }
 
 
-func deployBuild(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
-	deployAgent, err := deployGetAgent(client)
+func updateBuild(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
+	updateAgent, err := updateGetAgent(client)
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	return deployBuildWithAgent(deployAgent)
+	return updateBuildWithAgent(updateAgent)
 }
 }
 
 
-func deployPush(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
-	deployAgent, err := deployGetAgent(client)
+func updatePush(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
+	updateAgent, err := updateGetAgent(client)
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	return deployPushWithAgent(deployAgent)
+	return updatePushWithAgent(updateAgent)
 }
 }
 
 
-func deployUpgrade(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
-	deployAgent, err := deployGetAgent(client)
+func updateUpgrade(resp *api.AuthCheckResponse, client *api.Client, args []string) error {
+	updateAgent, err := updateGetAgent(client)
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	return deployUpgradeWithAgent(deployAgent)
+	return updateUpgradeWithAgent(updateAgent)
 }
 }
 
 
 // HELPER METHODS
 // HELPER METHODS
-func deployGetAgent(client *api.Client) (*deploy.DeployAgent, error) {
+func updateGetAgent(client *api.Client) (*deploy.DeployAgent, error) {
 	var buildMethod deploy.DeployBuildType
 	var buildMethod deploy.DeployBuildType
 
 
 	if method != "" {
 	if method != "" {
 		buildMethod = deploy.DeployBuildType(method)
 		buildMethod = deploy.DeployBuildType(method)
 	}
 	}
 
 
-	// initialize the deploy agent
+	// initialize the update agent
 	return deploy.NewDeployAgent(client, app, &deploy.DeployOpts{
 	return deploy.NewDeployAgent(client, app, &deploy.DeployOpts{
 		SharedOpts: &deploy.SharedOpts{
 		SharedOpts: &deploy.SharedOpts{
 			ProjectID:       config.Project,
 			ProjectID:       config.Project,
@@ -398,34 +389,34 @@ func deployGetAgent(client *api.Client) (*deploy.DeployAgent, error) {
 	})
 	})
 }
 }
 
 
-func deployBuildWithAgent(deployAgent *deploy.DeployAgent) error {
+func updateBuildWithAgent(updateAgent *deploy.DeployAgent) error {
 	// build the deployment
 	// build the deployment
 	color.New(color.FgGreen).Println("Building docker image for", app)
 	color.New(color.FgGreen).Println("Building docker image for", app)
 
 
-	buildEnv, err := deployAgent.GetBuildEnv()
+	buildEnv, err := updateAgent.GetBuildEnv()
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
 	// set the environment variables in the process
 	// set the environment variables in the process
-	err = deployAgent.SetBuildEnv(buildEnv)
+	err = updateAgent.SetBuildEnv(buildEnv)
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	return deployAgent.Build()
+	return updateAgent.Build()
 }
 }
 
 
-func deployPushWithAgent(deployAgent *deploy.DeployAgent) error {
+func updatePushWithAgent(updateAgent *deploy.DeployAgent) error {
 	// push the deployment
 	// push the deployment
 	color.New(color.FgGreen).Println("Pushing new image for", app)
 	color.New(color.FgGreen).Println("Pushing new image for", app)
 
 
-	return deployAgent.Push()
+	return updateAgent.Push()
 }
 }
 
 
-func deployUpgradeWithAgent(deployAgent *deploy.DeployAgent) error {
+func updateUpgradeWithAgent(updateAgent *deploy.DeployAgent) error {
 	// push the deployment
 	// push the deployment
 	color.New(color.FgGreen).Println("Calling webhook for", app)
 	color.New(color.FgGreen).Println("Calling webhook for", app)
 
 
@@ -436,13 +427,13 @@ func deployUpgradeWithAgent(deployAgent *deploy.DeployAgent) error {
 		return err
 		return err
 	}
 	}
 
 
-	err = deployAgent.UpdateImageAndValues(valuesObj)
+	err = updateAgent.UpdateImageAndValues(valuesObj)
 
 
 	if err != nil {
 	if err != nil {
 		return err
 		return err
 	}
 	}
 
 
-	color.New(color.FgGreen).Println("Successfully re-deployed", app)
+	color.New(color.FgGreen).Println("Successfully re-updateed", app)
 
 
 	return nil
 	return nil
 }
 }

+ 57 - 0
cli/cmd/deploy/create.go

@@ -5,6 +5,7 @@ import (
 	"fmt"
 	"fmt"
 	"os"
 	"os"
 	"path/filepath"
 	"path/filepath"
+	"strings"
 
 
 	"github.com/porter-dev/porter/cli/cmd/api"
 	"github.com/porter-dev/porter/cli/cmd/api"
 	"github.com/porter-dev/porter/cli/cmd/docker"
 	"github.com/porter-dev/porter/cli/cmd/docker"
@@ -143,6 +144,62 @@ func (c *CreateAgent) CreateFromGithub(
 	return subdomain, nil
 	return subdomain, nil
 }
 }
 
 
+func (c *CreateAgent) CreateFromRegistry(
+	image string,
+	overrideValues map[string]interface{},
+) (string, error) {
+	if image == "" {
+		return "", fmt.Errorf("image cannot be empty")
+	}
+
+	// split image into image-path:tag format
+	imageSpl := strings.Split(image, ":")
+
+	if len(imageSpl) != 2 {
+		return "", fmt.Errorf("invalid image format: must be image-path:tag format")
+	}
+
+	opts := c.CreateOpts
+
+	latestVersion, mergedValues, err := c.getMergedValues(overrideValues)
+
+	if err != nil {
+		return "", err
+	}
+
+	mergedValues["image"] = map[string]interface{}{
+		"repository": imageSpl[0],
+		"tag":        imageSpl[1],
+	}
+
+	subdomain, err := c.CreateSubdomainIfRequired(mergedValues)
+
+	if err != nil {
+		return "", err
+	}
+
+	err = c.Client.DeployTemplate(
+		context.Background(),
+		opts.ProjectID,
+		opts.ClusterID,
+		opts.Kind,
+		latestVersion,
+		&api.DeployTemplateRequest{
+			TemplateName: opts.Kind,
+			ImageURL:     imageSpl[0],
+			FormValues:   mergedValues,
+			Namespace:    opts.Namespace,
+			Name:         opts.ReleaseName,
+		},
+	)
+
+	if err != nil {
+		return "", err
+	}
+
+	return subdomain, nil
+}
+
 func (c *CreateAgent) CreateFromDocker(
 func (c *CreateAgent) CreateFromDocker(
 	overrideValues map[string]interface{},
 	overrideValues map[string]interface{},
 ) (string, error) {
 ) (string, error) {