Explorar el Código

app update command and no build flag on apply (#4459)

ianedwards hace 2 años
padre
commit
8a4b560ffb

+ 11 - 6
api/server/handlers/porter_app/update_app.go

@@ -213,12 +213,7 @@ func (c *UpdateAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 			return
 		}
 
-		appProto, err = v2.PatchApp(ctx, appFromYaml.AppProto, request.PatchOperations)
-		if err != nil {
-			err := telemetry.Error(ctx, span, err, "error patching app proto")
-			c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
-			return
-		}
+		appProto = appFromYaml.AppProto
 
 		// only public variables can be defined in porter.yaml
 		envVariables = mergeEnvVariables(request.Variables, appFromYaml.EnvVariables)
@@ -232,6 +227,16 @@ func (c *UpdateAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 		addons = appFromYaml.Addons
 	}
 
+	if appProto != nil {
+		patchedProto, err := v2.PatchApp(ctx, appProto, request.PatchOperations)
+		if err != nil {
+			err := telemetry.Error(ctx, span, err, "error patching app proto")
+			c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+			return
+		}
+		appProto = patchedProto
+	}
+
 	if appProto.Name == "" {
 		if request.Name == "" {
 			err := telemetry.Error(ctx, span, nil, "app name is empty")

+ 71 - 0
cli/cmd/commands/app.go

@@ -141,6 +141,34 @@ You can specify a tag using the --tag flag:
 	)
 	appCmd.AddCommand(appPushCommand)
 
+	appUpdateCommand := &cobra.Command{
+		Use:   "update [application]",
+		Args:  cobra.MinimumNArgs(1),
+		Short: "Updates an application with the provided configuration.",
+		Long: fmt.Sprintf(`
+	%s
+
+Updates the specified app with the provided configuration. This command differs from "porter apply"
+in that it only updates the app, but does not attempt to build a new image.`,
+			color.New(color.FgBlue, color.Bold).Sprintf("Help for \"porter app update\":"),
+		),
+		RunE: func(cmd *cobra.Command, args []string) error {
+			return checkLoginAndRunWithConfig(cmd, cliConf, args, appUpdate)
+		},
+	}
+	appUpdateCommand.PersistentFlags().StringVarP(&porterYAML, "file", "f", "", "path to porter.yaml")
+	appUpdateCommand.PersistentFlags().BoolVarP(
+		&appWait,
+		"wait",
+		"w",
+		false,
+		"set this to wait until an update has rolled out successfully, otherwise time out",
+	)
+	flags.UseAppConfigFlags(appUpdateCommand)
+	flags.UseAppImageFlags(appUpdateCommand)
+
+	appCmd.AddCommand(appUpdateCommand)
+
 	// appRunCmd represents the "porter app run" subcommand
 	appRunCmd := &cobra.Command{
 		Use:   "run [application] -- COMMAND [args...]",
@@ -357,6 +385,49 @@ func appPush(ctx context.Context, _ *types.GetAuthenticatedUserResponse, client
 	return nil
 }
 
+func appUpdate(ctx context.Context, _ *types.GetAuthenticatedUserResponse, client api.Client, cliConfig config.CLIConfig, _ config.FeatureFlags, cmd *cobra.Command, args []string) error {
+	appName := args[0]
+	if appName == "" {
+		return fmt.Errorf("app name must be specified")
+	}
+
+	extraAppConfig, err := flags.AppConfigValuesFromCmd(cmd)
+	if err != nil {
+		return fmt.Errorf("could not retrieve app config values from command")
+	}
+
+	imageValues, err := flags.AppImageValuesFromCmd(cmd)
+	if err != nil {
+		return fmt.Errorf("could not retrieve image values from command")
+	}
+
+	patchOperations := appV2.PatchOperationsFromFlagValues(appV2.PatchOperationsFromFlagValuesInput{
+		EnvGroups:       extraAppConfig.AttachEnvGroups,
+		ImageRepository: imageValues.Repository,
+		ImageTag:        imageValues.Tag,
+	})
+
+	inp := v2.ApplyInput{
+		CLIConfig:                   cliConfig,
+		Client:                      client,
+		PorterYamlPath:              porterYAML,
+		AppName:                     appName,
+		ImageTagOverride:            imageValues.Tag,
+		PreviewApply:                previewApply,
+		WaitForSuccessfulDeployment: appWait,
+		Exact:                       exact,
+		PatchOperations:             patchOperations,
+		SkipBuild:                   true, // skip build for update
+	}
+
+	err = v2.Apply(ctx, inp)
+	if err != nil {
+		return err
+	}
+
+	return nil
+}
+
 func appManifests(ctx context.Context, _ *types.GetAuthenticatedUserResponse, client api.Client, cliConfig config.CLIConfig, _ config.FeatureFlags, _ *cobra.Command, args []string) error {
 	appName := args[0]
 	if appName == "" {

+ 14 - 3
cli/cmd/commands/apply.go

@@ -44,7 +44,6 @@ import (
 var (
 	porterYAML   string
 	previewApply bool
-	envGroups    []string
 	// pullImageBeforeBuild is a flag that determines whether to pull the docker image from a repo before building
 	pullImageBeforeBuild bool
 	predeploy            bool
@@ -124,10 +123,11 @@ applying a configuration:
 		false,
 		"set this to wait and be notified when an apply is successful, otherwise time out",
 	)
-	applyCmd.PersistentFlags().StringSliceVar(&envGroups, "attach-env-groups", nil, "attach environment groups to the app on apply")
+	applyCmd.PersistentFlags().Bool(flags.App_NoBuild, false, "apply configuration without building a new image")
 
 	flags.UseAppBuildFlags(applyCmd)
 	flags.UseAppImageFlags(applyCmd)
+	flags.UseAppConfigFlags(applyCmd)
 
 	applyCmd.MarkFlagRequired("file")
 
@@ -162,13 +162,23 @@ func apply(ctx context.Context, _ *types.GetAuthenticatedUserResponse, client ap
 		return fmt.Errorf("could not retrieve build values from command")
 	}
 
+	extraAppConfig, err := flags.AppConfigValuesFromCmd(cmd)
+	if err != nil {
+		return fmt.Errorf("could not retrieve app config values from command")
+	}
+
+	noBuild, err := cmd.Flags().GetBool(flags.App_NoBuild)
+	if err != nil {
+		return fmt.Errorf("could not retrieve no-build flag from command")
+	}
+
 	if project.ValidateApplyV2 {
 		if previewApply && !project.PreviewEnvsEnabled {
 			return fmt.Errorf("preview environments are not enabled for this project. Please contact support@porter.run")
 		}
 
 		patchOperations := appV2.PatchOperationsFromFlagValues(appV2.PatchOperationsFromFlagValuesInput{
-			EnvGroups:       envGroups,
+			EnvGroups:       extraAppConfig.AttachEnvGroups,
 			BuildMethod:     buildValues.BuildMethod,
 			Dockerfile:      buildValues.Dockerfile,
 			Builder:         buildValues.Builder,
@@ -190,6 +200,7 @@ func apply(ctx context.Context, _ *types.GetAuthenticatedUserResponse, client ap
 			WithPredeploy:               predeploy,
 			Exact:                       exact,
 			PatchOperations:             patchOperations,
+			SkipBuild:                   noBuild,
 		}
 		err = v2.Apply(ctx, inp)
 		if err != nil {

+ 2 - 0
cli/cmd/commands/flags/app_build.go

@@ -17,6 +17,8 @@ const (
 	App_Buildpacks = "attach-buildpacks"
 	// App_BuildContext is the key for the build context flag
 	App_BuildContext = "build-context"
+	// App_NoBuild is the key for the no build flag
+	App_NoBuild = "no-build"
 )
 
 // UseAppBuildFlags adds build flags to the given command

+ 41 - 0
cli/cmd/commands/flags/app_config.go

@@ -0,0 +1,41 @@
+package flags
+
+import (
+	"fmt"
+
+	"github.com/spf13/cobra"
+)
+
+const (
+	// App_ConfigAttachEnvGroups is the key for the attach env groups flag
+	App_ConfigAttachEnvGroups = "attach-env-groups"
+)
+
+type appConfigValues struct {
+	AttachEnvGroups []string
+}
+
+// UseAppConfigFlags adds config flags to the given command
+func UseAppConfigFlags(cmd *cobra.Command) {
+	cmd.PersistentFlags().StringSlice(
+		App_ConfigAttachEnvGroups,
+		nil,
+		"attach environment groups to the app",
+	)
+}
+
+// AppConfigValuesFromCmd retrieves config values from command flags
+func AppConfigValuesFromCmd(cmd *cobra.Command) (appConfigValues, error) {
+	var values appConfigValues
+
+	envGroups, err := cmd.Flags().GetStringSlice(App_ConfigAttachEnvGroups)
+	if err != nil {
+		return values, fmt.Errorf("error getting attach env groups: %w", err)
+	}
+
+	values = appConfigValues{
+		AttachEnvGroups: envGroups,
+	}
+
+	return values, nil
+}

+ 10 - 2
cli/cmd/v2/apply.go

@@ -50,6 +50,8 @@ type ApplyInput struct {
 	Exact bool
 	// PatchOperations is a list of patch operations to apply to the app
 	PatchOperations []v2.PatchOperation
+	// SkipBuild is true when Apply should skip the build step
+	SkipBuild bool
 }
 
 // Apply implements the functionality of the `porter apply` command for validate apply v2 projects
@@ -119,7 +121,13 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 		color.New(color.FgGreen).Printf("Using Porter YAML at path: %s\n", inp.PorterYamlPath) // nolint:errcheck,gosec
 	}
 
-	commitSHA := commitSHAFromEnv()
+	var commitSHA string
+	if !inp.SkipBuild {
+		// providing a commit SHA indicates the app is eligible for a build
+		// only set it if we want the build to run
+		commitSHA = commitSHAFromEnv()
+	}
+
 	gitSource, err := gitSourceFromEnv()
 	if err != nil {
 		return fmt.Errorf("error getting git source from env: %w", err)
@@ -160,7 +168,7 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 		return fmt.Errorf("error getting build from revision: %w", err)
 	}
 
-	if buildSettings != nil && buildSettings.Build.Method != "" {
+	if !inp.SkipBuild && buildSettings != nil && buildSettings.Build.Method != "" {
 		eventID, _ := createBuildEvent(ctx, client, appName, cliConf.Project, cliConf.Cluster, deploymentTargetID, commitSHA)
 
 		var buildFinished bool