Explorar el Código

Set the correct vars for apply cmd

Mohammed Nafees hace 4 años
padre
commit
46a7a59c6b
Se han modificado 4 ficheros con 229 adiciones y 93 borrados
  1. 23 0
      api/client/k8s.go
  2. 151 49
      cli/cmd/apply.go
  3. 19 15
      cli/cmd/create.go
  4. 36 29
      cli/cmd/deploy.go

+ 23 - 0
api/client/k8s.go

@@ -28,6 +28,29 @@ func (c *Client) GetK8sNamespaces(
 	return resp, err
 }
 
+// CreateNewK8sNamespace creates a new namespace in a k8s cluster
+func (c *Client) CreateNewK8sNamespace(
+	ctx context.Context,
+	projectID uint,
+	clusterID uint,
+	name string,
+) (*types.CreateNamespaceResponse, error) {
+	resp := &types.CreateNamespaceResponse{}
+
+	err := c.postRequest(
+		fmt.Sprintf(
+			"/projects/%d/clusters/%d/namespaces/create",
+			projectID, clusterID,
+		),
+		&types.CreateNamespaceRequest{
+			Name: name,
+		},
+		resp,
+	)
+
+	return resp, err
+}
+
 func (c *Client) GetKubeconfig(
 	ctx context.Context,
 	projectID uint,

+ 151 - 49
cli/cmd/apply.go

@@ -1,10 +1,13 @@
 package cmd
 
 import (
+	"context"
+	"fmt"
 	"io/ioutil"
 	"os"
-	"path/filepath"
+	"strconv"
 
+	"github.com/cli/cli/git"
 	"github.com/mitchellh/mapstructure"
 	api "github.com/porter-dev/porter/api/client"
 	"github.com/porter-dev/porter/api/types"
@@ -80,8 +83,9 @@ type Target struct {
 
 type Config struct {
 	Build struct {
-		Method  string
-		Context string
+		Method     string
+		Context    string
+		Dockerfile string
 	}
 	Values map[string]interface{}
 }
@@ -101,43 +105,23 @@ func NewPorterDriver(resource *models.Resource, opts *drivers.SharedDriverOpts)
 		logger:      opts.Logger,
 	}
 
-	if resource.Source != nil {
-		source, err := getSource(resource.Source)
-		if err != nil {
-			return nil, err
-		}
-		driver.source = source
-	} else {
-		// default source
-		driver.source = &Source{
-			Name:    "web",
-			Repo:    "https://charts.getporter.dev",
-			Version: "v0.13.0",
-		}
+	source, err := getSource(resource.Source)
+	if err != nil {
+		return nil, err
 	}
+	driver.source = source
 
-	if resource.Target != nil {
-		target, err := getTarget(resource.Target)
-		if err != nil {
-			return nil, err
-		}
-		driver.target = target
-	} else {
-		// default target
-		driver.target = &Target{
-			Project:   config.Project,
-			Cluster:   config.Cluster,
-			Namespace: "default",
-		}
+	target, err := getTarget(resource.Target)
+	if err != nil {
+		return nil, err
 	}
+	driver.target = target
 
-	if resource.Config != nil {
-		config, err := getConfig(resource.Config)
-		if err != nil {
-			return nil, err
-		}
-		driver.config = config
+	config, err := getConfig(resource.Config)
+	if err != nil {
+		return nil, err
 	}
+	driver.config = config
 
 	return driver, nil
 }
@@ -147,23 +131,82 @@ func (d *Driver) ShouldApply(resource *models.Resource) bool {
 }
 
 func (d *Driver) Apply(resource *models.Resource) (*models.Resource, error) {
+	client := GetAPIClient(config)
+
 	// TODO: use source.repo, source.version, config.values
-	name = resource.Name
-	source = "local"
+	config.SetProject(d.target.Project)
+	config.SetCluster(d.target.Cluster)
+
 	namespace = d.target.Namespace
-	if d.config != nil {
-		method = d.config.Build.Method
-		absPath, err := filepath.Abs(d.config.Build.Context)
+	existingNamespaces, err := client.GetK8sNamespaces(context.Background(), config.Project, config.Cluster)
+	if err != nil {
+		return nil, err
+	}
+	namespaceFound := false
+	for _, ns := range existingNamespaces.Items {
+		if namespace == ns.Name {
+			namespaceFound = true
+			break
+		}
+	}
+	if !namespaceFound {
+		_, err := client.CreateNewK8sNamespace(
+			context.Background(), config.Project, config.Cluster, namespace)
 		if err != nil {
 			return nil, err
 		}
-		localPath = absPath
 	}
-	config.SetProject(d.target.Project)
-	config.SetCluster(d.target.Cluster)
-	err := createFull(nil, GetAPIClient(config), []string{d.source.Name})
+
+	method = d.config.Build.Method
+	if method == "" {
+		return nil, fmt.Errorf("method should either be \"docker\" or \"pack\"")
+	} else if method == "docker" {
+		dockerfile = d.config.Build.Dockerfile
+	}
+
+	localPath = d.config.Build.Context
+	source = "local"
+	valuesObj = d.config.Values
+
+	_, err = client.GetRelease(context.Background(), config.Project, config.Cluster, d.target.Namespace, resource.Name)
 	if err != nil {
-		return nil, err
+		// app exists
+		if resource.Name == "" {
+			return nil, fmt.Errorf("empty app name")
+		}
+		app = resource.Name
+		tag = os.Getenv("PORTER_TAG")
+		if tag == "" {
+			commit, err := git.LastCommit()
+			if err != nil {
+				return nil, err
+			}
+			tag = commit.Sha
+		}
+		if tag == "" {
+			return nil, fmt.Errorf("could not find commit SHA to tag the image")
+		}
+
+		err = updateFull(nil, client, []string{})
+		if err != nil {
+			return nil, err
+		}
+	} else {
+		// create new app
+		name = resource.Name
+
+		regList, err := client.ListRegistries(context.Background(), config.Project)
+		if err != nil {
+			return nil, err
+		}
+		if len(*regList) > 0 {
+			registryURL = (*regList)[0].URL
+		}
+
+		err = createFull(nil, client, []string{d.source.Name})
+		if err != nil {
+			return nil, err
+		}
 	}
 
 	return resource, nil
@@ -176,9 +219,35 @@ func (d *Driver) Output() (map[string]interface{}, error) {
 func getSource(genericSource map[string]interface{}) (*Source, error) {
 	source := &Source{}
 
-	err := mapstructure.Decode(genericSource, source)
-	if err != nil {
-		return nil, err
+	// first read from env vars
+	source.Name = os.Getenv("PORTER_SOURCE_NAME")
+	source.Repo = os.Getenv("PORTER_SOURCE_REPO")
+	source.Version = os.Getenv("PORTER_SOURCE_VERSION")
+
+	// next, check for values in the YAML file
+	if source.Name == "" {
+		if name, ok := genericSource["name"]; ok {
+			source.Name = name.(string)
+		}
+	}
+	if source.Repo == "" {
+		if repo, ok := genericSource["repo"]; ok {
+			source.Repo = repo.(string)
+		}
+	}
+	if source.Version == "" {
+		if version, ok := genericSource["version"]; ok {
+			source.Version = version.(string)
+		}
+	}
+
+	// lastly, just put in the defaults
+	if source.Name == "" || source.Repo == "" || source.Version == "" {
+		// default to these values when any one of the source values are empty
+		// this makes sense because it might save us from version mismatches and other mishaps
+		source.Name = "web"
+		source.Repo = "https://charts.getporter.dev"
+		source.Version = "v0.13.0"
 	}
 
 	return source, nil
@@ -187,10 +256,43 @@ func getSource(genericSource map[string]interface{}) (*Source, error) {
 func getTarget(genericTarget map[string]interface{}) (*Target, error) {
 	target := &Target{}
 
-	err := mapstructure.Decode(genericTarget, target)
+	// first read from env vars
+	project, err := strconv.Atoi(os.Getenv("PORTER_PROJECT"))
+	if err != nil {
+		return nil, err
+	}
+	target.Project = uint(project)
+	cluster, err := strconv.Atoi(os.Getenv("PORTER_CLUSTER"))
 	if err != nil {
 		return nil, err
 	}
+	target.Cluster = uint(cluster)
+	target.Namespace = os.Getenv("PORTER_NAMESPACE")
+
+	// next, check for values in the YAML file
+	if target.Project == 0 {
+		if project, ok := genericTarget["project"]; ok {
+			target.Project = project.(uint)
+		}
+	}
+	if target.Cluster == 0 {
+		if cluster, ok := genericTarget["cluster"]; ok {
+			target.Cluster = cluster.(uint)
+		}
+	}
+	if target.Namespace == "" {
+		if namespace, ok := genericTarget["namespace"]; ok {
+			target.Namespace = namespace.(string)
+		}
+	}
+
+	// lastly, just put in the defaults
+	if target.Project == 0 || target.Cluster == 0 || target.Namespace == "" {
+		// default to these values when any one of the target values are empty
+		target.Project = config.Project
+		target.Cluster = config.Cluster
+		target.Namespace = "default"
+	}
 
 	return target, nil
 }

+ 19 - 15
cli/cmd/create.go

@@ -22,30 +22,30 @@ var createCmd = &cobra.Command{
 	Args:  cobra.ExactArgs(1),
 	Short: "Creates a new application with name given by the --app flag.",
 	Long: fmt.Sprintf(`
-%s 
+%s
 
-Creates a new application with name given by the --app flag and a "kind", which can be one of 
+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. 
+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: 
+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 
+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 
+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
@@ -53,7 +53,7 @@ Github repository to link to. Otherwise, Porter will use the remote given by ori
 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 
+  %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"),
@@ -76,6 +76,7 @@ var values string
 var source string
 var image string
 var registryURL string
+var valuesObj map[string]interface{}
 
 func init() {
 	rootCmd.AddCommand(createCmd)
@@ -156,11 +157,14 @@ func createFull(_ *types.GetAuthenticatedUserResponse, client *api.Client, args
 		return fmt.Errorf("%s is not a supported type: specify web, job, or worker", args[0])
 	}
 
-	// read the values if necessary
-	valuesObj, err := readValuesFile()
+	var err error
 
-	if err != nil {
-		return err
+	// read the values if necessary
+	if valuesObj == nil {
+		valuesObj, err = readValuesFile()
+		if err != nil {
+			return err
+		}
 	}
 
 	color.New(color.FgGreen).Printf("Creating %s release: %s\n", args[0], name)

+ 36 - 29
cli/cmd/deploy.go

@@ -17,34 +17,34 @@ var updateCmd = &cobra.Command{
 	Use:   "update",
 	Short: "Builds and updates a specified application given by the --app flag.",
 	Long: fmt.Sprintf(`
-%s 
+%s
 
 Builds and updates a specified application given by the --app flag. For example:
 
   %s
 
-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 
+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
 local directory ~/path-to-dir with the tag "testing":
 
   %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 "--source github". Porter will use 
-the latest commit from the remote repo and branch to update an application, and will use the latest 
+Git repository should be used to build the new image by specifying "--source github". 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;
 
   %s
 
-If your application is set up to use a Dockerfile by default, you can use a buildpack via the flag 
-"--method pack". Conversely, if your application is set up to use a buildpack by default, you can 
-use a Dockerfile by passing the flag "--method docker". You can specify the relative path to a Dockerfile 
-in your remote Git repository. For example, if a Dockerfile is found at ./docker/prod.Dockerfile, you can 
+If your application is set up to use a Dockerfile by default, you can use a buildpack via the flag
+"--method pack". Conversely, if your application is set up to use a buildpack by default, you can
+use a Dockerfile by passing the flag "--method docker". You can specify the relative path to a Dockerfile
+in your remote Git repository. For example, if a Dockerfile is found at ./docker/prod.Dockerfile, you can
 specify it as follows:
 
   %s
@@ -69,14 +69,14 @@ var updateGetEnvCmd = &cobra.Command{
 	Use:   "get-env",
 	Short: "Gets environment variables for a deployment for a specified application given by the --app flag.",
 	Long: fmt.Sprintf(`
-%s 
+%s
 
-Gets environment variables for a deployment for a specified application given by the --app 
+Gets environment variables for a deployment for a specified application given by the --app
 flag. By default, env variables are printed via stdout for use in downstream commands:
 
   %s
 
-Output can also be written to a file via the --file flag, which should specify the 
+Output can also be written to a file via the --file flag, which should specify the
 destination path for a .env file. For example:
 
   %s
@@ -98,26 +98,26 @@ var updateBuildCmd = &cobra.Command{
 	Use:   "build",
 	Short: "Builds a new version of the application specified by the --app flag.",
 	Long: fmt.Sprintf(`
-%s 
+%s
 
-Builds a new version of the application specified by the --app flag. Depending on the 
-configured settings, this command may work automatically or will require a specified 
---method flag. 
+Builds a new version of the application specified by the --app flag. Depending on the
+configured settings, this command may work automatically or will require a specified
+--method flag.
 
-If you have configured the Dockerfile path and/or a build context for this application, 
-this command will by default use those settings, so you just need to specify the --app 
+If you have configured the Dockerfile path and/or a build context for this application,
+this command will by default use those settings, so you just need to specify the --app
 flag:
 
   %s
 
 If you have not linked the build-time requirements for this application, the command will
-use a local build. By default, the cloud-native buildpacks builder will automatically be run 
-from the current directory. If you would like to change the build method, you can do so by 
+use a local build. By default, the cloud-native buildpacks builder will automatically be run
+from the current directory. If you would like to change the build method, you can do so by
 using the --method flag, for example:
 
   %s
 
-When using "--method docker", you can specify the path to the Dockerfile using the 
+When using "--method docker", you can specify the path to the Dockerfile using the
 --dockerfile flag. This will also override the Dockerfile path that you may have linked
 for the application:
 
@@ -141,17 +141,17 @@ var updatePushCmd = &cobra.Command{
 	Use:   "push",
 	Short: "Pushes a new image for an application specified by the --app flag.",
 	Long: fmt.Sprintf(`
-%s 
+%s
 
 Pushes a new image for an application specified by the --app flag. This command uses
-the image repository saved in the application config by default. For example, if an 
-application "nginx" was created from the image repo "gcr.io/snowflake-123456/nginx", 
+the image repository saved in the application config by default. For example, if an
+application "nginx" was created from the image repo "gcr.io/snowflake-123456/nginx",
 the following command would push the image "gcr.io/snowflake-123456/nginx:new-tag":
 
   %s
 
 This command will not use your pre-saved authentication set up via "docker login," so if you
-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".
 `,
 		color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter update push\":"),
@@ -170,10 +170,10 @@ var updateConfigCmd = &cobra.Command{
 	Use:   "config",
 	Short: "Updates the configuration for an application specified by the --app flag.",
 	Long: fmt.Sprintf(`
-%s 
+%s
 
 Updates the configuration for an application specified by the --app flag, using the configuration
-given by the --values flag. This will trigger a new deployment for the application with 
+given by the --values flag. This will trigger a new deployment for the application with
 new configuration set. Note that this will merge your existing configuration with configuration
 specified in the --values file. For example:
 
@@ -523,8 +523,15 @@ func updateUpgradeWithAgent(updateAgent *deploy.DeployAgent) error {
 		})
 	}
 
+	var err error
+
 	// read the values if necessary
-	valuesObj, err := readValuesFile()
+	if valuesObj == nil {
+		valuesObj, err = readValuesFile()
+		if err != nil {
+			return err
+		}
+	}
 
 	if err != nil {
 		if stream {