2
0
ianedwards 2 жил өмнө
parent
commit
c4e71ea208

+ 22 - 10
api/client/porter_app.go

@@ -215,28 +215,40 @@ func (c *Client) ValidatePorterApp(
 	return resp, err
 }
 
+// ApplyPorterAppInput is the input struct to ApplyPorterApp
+type ApplyPorterAppInput struct {
+	ProjectID        uint
+	ClusterID        uint
+	Base64AppProto   string
+	DeploymentTarget string
+	AppRevisionID    string
+	ForceBuild       bool
+	Variables        map[string]string
+	Secrets          map[string]string
+	HardEnvUpdate    bool
+}
+
 // ApplyPorterApp takes in a base64 encoded app definition and applies it to the cluster
 func (c *Client) ApplyPorterApp(
 	ctx context.Context,
-	projectID, clusterID uint,
-	base64AppProto string,
-	deploymentTarget string,
-	appRevisionID string,
-	forceBuild bool,
+	inp ApplyPorterAppInput,
 ) (*porter_app.ApplyPorterAppResponse, error) {
 	resp := &porter_app.ApplyPorterAppResponse{}
 
 	req := &porter_app.ApplyPorterAppRequest{
-		Base64AppProto:     base64AppProto,
-		DeploymentTargetId: deploymentTarget,
-		AppRevisionID:      appRevisionID,
-		ForceBuild:         forceBuild,
+		Base64AppProto:     inp.Base64AppProto,
+		DeploymentTargetId: inp.DeploymentTarget,
+		AppRevisionID:      inp.AppRevisionID,
+		ForceBuild:         inp.ForceBuild,
+		Variables:          inp.Variables,
+		Secrets:            inp.Secrets,
+		HardEnvUpdate:      inp.HardEnvUpdate,
 	}
 
 	err := c.postRequest(
 		fmt.Sprintf(
 			"/projects/%d/clusters/%d/apps/apply",
-			projectID, clusterID,
+			inp.ProjectID, inp.ClusterID,
 		),
 		req,
 		resp,

+ 14 - 4
api/server/handlers/porter_app/apply.go

@@ -47,10 +47,15 @@ func NewApplyPorterAppHandler(
 
 // ApplyPorterAppRequest is the request object for the /apps/apply endpoint
 type ApplyPorterAppRequest struct {
-	Base64AppProto     string `json:"b64_app_proto"`
-	DeploymentTargetId string `json:"deployment_target_id"`
-	AppRevisionID      string `json:"app_revision_id"`
-	ForceBuild         bool   `json:"force_build"`
+	Base64AppProto     string            `json:"b64_app_proto"`
+	DeploymentTargetId string            `json:"deployment_target_id"`
+	AppRevisionID      string            `json:"app_revision_id"`
+	ForceBuild         bool              `json:"force_build"`
+	Variables          map[string]string `json:"variables"`
+	Secrets            map[string]string `json:"secrets"`
+	// HardEnvUpdate is used to remove any variables that are not specified in the request.  If false, the request will only update the variables specified in the request,
+	// and leave all other variables untouched.
+	HardEnvUpdate bool `json:"hard_env_update"`
 }
 
 // ApplyPorterAppResponse is the response object for the /apps/apply endpoint
@@ -181,6 +186,11 @@ func (c *ApplyPorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
 		App:                 appProto,
 		PorterAppRevisionId: appRevisionID,
 		ForceBuild:          request.ForceBuild,
+		AppEnv: &porterv1.EnvGroupVariables{
+			Normal: request.Variables,
+			Secret: request.Secrets,
+		},
+		IsHardEnvUpdate: request.HardEnvUpdate,
 	})
 	ccpResp, err := c.Config().ClusterControlPlaneClient.ApplyPorterApp(ctx, applyReq)
 	if err != nil {

+ 31 - 13
cli/cmd/v2/apply.go

@@ -78,6 +78,10 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 	// overrides incorporated into the app contract baed on the deployment target
 	var overrides *porter_app.EncodedAppWithEnv
 
+	// env variables and secrets to be passed to the apply endpoint
+	var envVariables map[string]string
+	var envSecrets map[string]string
+
 	appName := inp.AppName
 	if porterYamlExists {
 		porterYaml, err := os.ReadFile(filepath.Clean(inp.PorterYamlPath))
@@ -120,16 +124,6 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 			return fmt.Errorf("unable to create porter app from yaml: %w", err)
 		}
 
-		envGroupResp, err := client.CreateOrUpdateAppEnvironment(ctx, cliConf.Project, cliConf.Cluster, appName, deploymentTargetID, parseResp.EnvVariables, parseResp.EnvSecrets, parseResp.B64AppProto)
-		if err != nil {
-			return fmt.Errorf("error calling create or update app environment group endpoint: %w", err)
-		}
-
-		b64AppProto, err = updateEnvGroupsInProto(ctx, b64AppProto, envGroupResp.EnvGroups)
-		if err != nil {
-			return fmt.Errorf("error updating app env group in proto: %w", err)
-		}
-
 		color.New(color.FgGreen).Printf("Successfully parsed Porter YAML: applying app \"%s\"\n", appName) // nolint:errcheck,gosec
 	}
 
@@ -182,7 +176,17 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 	}
 	base64AppProto := validateResp.ValidatedBase64AppProto
 
-	applyResp, err := client.ApplyPorterApp(ctx, cliConf.Project, cliConf.Cluster, base64AppProto, deploymentTargetID, "", forceBuild)
+	applyInput := api.ApplyPorterAppInput{
+		ProjectID:        cliConf.Project,
+		ClusterID:        cliConf.Cluster,
+		Base64AppProto:   base64AppProto,
+		DeploymentTarget: deploymentTargetID,
+		ForceBuild:       forceBuild,
+		Variables:        envVariables,
+		Secrets:          envSecrets,
+	}
+
+	applyResp, err := client.ApplyPorterApp(ctx, applyInput)
 	if err != nil {
 		return fmt.Errorf("error calling apply endpoint: %w", err)
 	}
@@ -280,7 +284,14 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 		buildMetadata["end_time"] = time.Now().UTC()
 		_ = updateExistingEvent(ctx, client, appName, cliConf.Project, cliConf.Cluster, deploymentTargetID, types.PorterAppEventType_Build, eventID, types.PorterAppEventStatus_Success, buildMetadata)
 
-		applyResp, err = client.ApplyPorterApp(ctx, cliConf.Project, cliConf.Cluster, "", "", applyResp.AppRevisionId, !forceBuild)
+		applyInput = api.ApplyPorterAppInput{
+			ProjectID:     cliConf.Project,
+			ClusterID:     cliConf.Cluster,
+			AppRevisionID: applyResp.AppRevisionId,
+			ForceBuild:    !forceBuild,
+		}
+
+		applyResp, err = client.ApplyPorterApp(ctx, applyInput)
 		if err != nil {
 			return fmt.Errorf("apply error post-build: %w", err)
 		}
@@ -327,7 +338,14 @@ func Apply(ctx context.Context, inp ApplyInput) error {
 		metadata["end_time"] = time.Now().UTC()
 		_ = updateExistingEvent(ctx, client, appName, cliConf.Project, cliConf.Cluster, deploymentTargetID, types.PorterAppEventType_PreDeploy, eventID, eventStatus, metadata)
 
-		applyResp, err = client.ApplyPorterApp(ctx, cliConf.Project, cliConf.Cluster, "", "", applyResp.AppRevisionId, !forceBuild)
+		applyInput = api.ApplyPorterAppInput{
+			ProjectID:     cliConf.Project,
+			ClusterID:     cliConf.Cluster,
+			AppRevisionID: applyResp.AppRevisionId,
+			ForceBuild:    !forceBuild,
+		}
+
+		applyResp, err = client.ApplyPorterApp(ctx, applyInput)
 		if err != nil {
 			return fmt.Errorf("apply error post-predeploy: %w", err)
 		}

+ 13 - 41
dashboard/src/main/home/app-dashboard/app-view/AppDataContainer.tsx

@@ -218,50 +218,17 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
         return;
       }
 
-      // updates the default env group associated with this app to store app specific env vars
-      const res = await api.updateEnvironmentGroupV2(
-        "<token>",
-        {
-          deployment_target_id: deploymentTarget.id,
-          variables,
-          secrets,
-          b64_app_proto: btoa(validatedAppProto.toJsonString()),
-          remove_missing: true,
-        },
-        {
-          id: projectId,
-          cluster_id: clusterId,
-          app_name: porterAppRecord.name,
-        }
-      );
-
-      const updatedEnvGroups = z
-        .object({
-          env_groups: z
-            .object({
-              name: z.string(),
-              latest_version: z.coerce.bigint(),
-            })
-            .array(),
-        })
-        .parse(res.data);
-
-      const protoWithUpdatedEnv = new PorterApp({
-        ...validatedAppProto,
-        envGroups: updatedEnvGroups.env_groups.map((eg) => ({
-          name: eg.name,
-          version: eg.latest_version,
-        })),
-      });
-
       // force_build will create a new 0 revision that will not be deployed
       // but will be used to hydrate values when the workflow is run
       await api.applyApp(
         "<token>",
         {
-          b64_app_proto: btoa(protoWithUpdatedEnv.toJsonString()),
+          b64_app_proto: btoa(validatedAppProto.toJsonString()),
           deployment_target_id: deploymentTarget.id,
           force_build: needsRebuild,
+          variables,
+          secrets,
+          hard_env_update: true,
         },
         {
           project_id: projectId,
@@ -307,8 +274,10 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
       // redirect to the default tab after save
       history.push(`/apps/${porterAppRecord.name}/${DEFAULT_TAB}`);
     } catch (err) {
-      showIntercomWithMessage({ message: "I am running into an issue updating my application." });
-      
+      showIntercomWithMessage({
+        message: "I am running into an issue updating my application.",
+      });
+
       let message =
         "App update failed: please try again or contact support@porter.run if the error persists.";
       let stack = "Unable to get error stack";
@@ -404,12 +373,15 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
             errorMessage = `${errorMessage} - ${serviceErrorMessage}`;
           }
           errorMessage = `${errorMessage}. To undo all changes, refresh the page.`;
-        } else if (appErrors.includes("message")) {  // this is the high level error message coming from the apply
+        } else if (appErrors.includes("message")) {
+          // this is the high level error message coming from the apply
           errorMessage = errors.app?.message ?? errorMessage;
         }
       }
 
-      showIntercomWithMessage({ message: "I am running into an issue updating my application." });
+      showIntercomWithMessage({
+        message: "I am running into an issue updating my application.",
+      });
       updateAppStep({
         step: "porter-app-update-failure",
         errorMessage: `Form validation error (visible to user): ${errorMessage}. Stringified JSON errors (invisible to user): ${stringifiedJson}`,

+ 25 - 46
dashboard/src/main/home/app-dashboard/create-app/CreateApp.tsx

@@ -282,45 +282,14 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
           }
         );
 
-        const res = await api.updateEnvironmentGroupV2(
-          "<token>",
-          {
-            deployment_target_id: deploymentTarget.deployment_target_id,
-            variables: variables,
-            b64_app_proto: btoa(app.toJsonString()),
-            secrets: secrets,
-          },
-          {
-            id: currentProject.id,
-            cluster_id: currentCluster.id,
-            app_name: app.name,
-          }
-        );
-
-        const updatedEnvGroups = z
-          .object({
-            env_groups: z
-              .object({
-                name: z.string(),
-                latest_version: z.coerce.bigint(),
-              })
-              .array(),
-          })
-          .parse(res.data);
-
-        const protoWithUpdatedEnv = new PorterApp({
-          ...app,
-          envGroups: updatedEnvGroups.env_groups.map((eg) => ({
-            name: eg.name,
-            version: eg.latest_version,
-          })),
-        });
-
         await api.applyApp(
           "<token>",
           {
-            b64_app_proto: btoa(protoWithUpdatedEnv.toJsonString()),
+            b64_app_proto: btoa(app.toJsonString()),
             deployment_target_id: deploymentTarget.deployment_target_id,
+            variables,
+            secrets,
+            hard_env_update: true
           },
           {
             project_id: currentProject.id,
@@ -337,7 +306,9 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
 
         return true;
       } catch (err) {
-        showIntercomWithMessage({ message: "I am running into an issue launching an application." });
+        showIntercomWithMessage({
+          message: "I am running into an issue launching an application.",
+        });
 
         if (axios.isAxiosError(err) && err.response?.data?.error) {
           updateAppStep({
@@ -433,7 +404,9 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
         }
       }
 
-      showIntercomWithMessage({ message: "I am running into an issue launching an application." });
+      showIntercomWithMessage({
+        message: "I am running into an issue launching an application.",
+      });
 
       updateAppStep({
         step: "stack-launch-failure",
@@ -614,12 +587,12 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
                                       porterYamlPath={value}
                                       projectId={currentProject.id}
                                       repoId={source.git_repo_id}
-                                      repoOwner={source.git_repo_name.split(
-                                        "/"
-                                      )[0]}
-                                      repoName={source.git_repo_name.split(
-                                        "/"
-                                      )[1]}
+                                      repoOwner={
+                                        source.git_repo_name.split("/")[0]
+                                      }
+                                      repoName={
+                                        source.git_repo_name.split("/")[1]
+                                      }
                                       branch={source.git_branch}
                                     />
                                   )}
@@ -672,8 +645,9 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
                             }
                           >
                             {detectedServices.count > 0
-                              ? `Detected ${detectedServices.count} service${detectedServices.count > 1 ? "s" : ""
-                              } from porter.yaml.`
+                              ? `Detected ${detectedServices.count} service${
+                                  detectedServices.count > 1 ? "s" : ""
+                                } from porter.yaml.`
                               : `Could not detect any services from porter.yaml. Make sure it exists in the root of your repo.`}
                           </Text>
                         </AppearingDiv>
@@ -685,7 +659,9 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
                       fieldArrayName={"app.services"}
                       maxCPU={currentClusterResources.maxCPU}
                       maxRAM={currentClusterResources.maxRAM}
-                      clusterContainsGPUNodes={currentClusterResources.clusterContainsGPUNodes}
+                      clusterContainsGPUNodes={
+                        currentClusterResources.clusterContainsGPUNodes
+                      }
                     />
                   </>,
                   <>
@@ -721,6 +697,9 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
                         fieldArrayName={"app.predeploy"}
                         maxCPU={currentClusterResources.maxCPU}
                         maxRAM={currentClusterResources.maxRAM}
+                        clusterContainsGPUNodes={
+                          currentClusterResources.clusterContainsGPUNodes
+                        }
                       />
                     </>
                   ),

+ 3 - 0
dashboard/src/shared/api.tsx

@@ -1004,6 +1004,9 @@ const applyApp = baseApi<
     b64_app_proto?: string;
     app_revision_id?: string;
     force_build?: boolean;
+    variables?: Record<string, string>;
+    secrets?: Record<string, string>;
+    hard_env_update?: boolean;
   },
   {
     project_id: number;

+ 1 - 1
go.mod

@@ -83,7 +83,7 @@ require (
 	github.com/matryer/is v1.4.0
 	github.com/nats-io/nats.go v1.24.0
 	github.com/open-policy-agent/opa v0.44.0
-	github.com/porter-dev/api-contracts v0.2.24
+	github.com/porter-dev/api-contracts v0.2.25
 	github.com/riandyrn/otelchi v0.5.1
 	github.com/santhosh-tekuri/jsonschema/v5 v5.0.1
 	github.com/stefanmcshane/helm v0.0.0-20221213002717-88a4a2c6e77d

+ 2 - 2
go.sum

@@ -1520,8 +1520,8 @@ github.com/pmezard/go-difflib v0.0.0-20151028094244-d8ed2627bdf0/go.mod h1:iKH77
 github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
 github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw=
-github.com/porter-dev/api-contracts v0.2.24 h1:0Jas/m7l+FD+1VRGYg8lq6vxTZ77A79X4A1wZcTVrEI=
-github.com/porter-dev/api-contracts v0.2.24/go.mod h1:fX6JmP5QuzxDLvqP3evFOTXjI4dHxsG0+VKNTjImZU8=
+github.com/porter-dev/api-contracts v0.2.25 h1:VQRxdksCPsTV6cd/ZgQ8nKltiOJaROKkK/642qyJKZ4=
+github.com/porter-dev/api-contracts v0.2.25/go.mod h1:fX6JmP5QuzxDLvqP3evFOTXjI4dHxsG0+VKNTjImZU8=
 github.com/porter-dev/switchboard v0.0.3 h1:dBuYkiVLa5Ce7059d6qTe9a1C2XEORFEanhbtV92R+M=
 github.com/porter-dev/switchboard v0.0.3/go.mod h1:xSPzqSFMQ6OSbp42fhCi4AbGbQbsm6nRvOkrblFeXU4=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=