Jelajahi Sumber

POR-1781 associated attached env versions with revision (#3609)

ianedwards 2 tahun lalu
induk
melakukan
40daca35d8

+ 60 - 45
api/server/handlers/porter_app/update_app_environment_group.go

@@ -69,8 +69,7 @@ type UpdateAppEnvironmentRequest struct {
 
 // UpdateAppEnvironmentResponse represents the fields on the response object from the /apps/{porter_app_name}/environment-group endpoint
 type UpdateAppEnvironmentResponse struct {
-	EnvGroupName    string `json:"env_group_name"`
-	EnvGroupVersion int    `json:"env_group_version"`
+	EnvGroups []environment_groups.EnvironmentGroup `json:"env_groups"`
 }
 
 // ServeHTTP updates or creates the environment group for an app
@@ -221,21 +220,24 @@ func (c *UpdateAppEnvironmentHandler) ServeHTTP(w http.ResponseWriter, r *http.R
 				envGroups:          appProto.EnvGroups,
 				appName:            appName,
 				appEnvName:         appEnvGroupName,
-				sameAppEnv:         true,
 				namespace:          namespace,
 				deploymentTargetID: request.DeploymentTargetID,
 				k8sAgent:           agent,
 			}
-			err = syncLatestEnvGroupVersions(ctx, syncInp)
+			latestEnvGroups, err := syncLatestEnvGroupVersions(ctx, syncInp)
 			if err != nil {
 				err := telemetry.Error(ctx, span, err, "error syncing latest env group versions")
 				c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
 				return
 			}
 
+			latestEnvGroups = append(latestEnvGroups, environment_groups.EnvironmentGroup{
+				Name:    latestEnvironmentGroup.Name,
+				Version: latestEnvironmentGroup.Version,
+			})
+
 			res := &UpdateAppEnvironmentResponse{
-				EnvGroupName:    latestEnvironmentGroup.Name,
-				EnvGroupVersion: latestEnvironmentGroup.Version,
+				EnvGroups: latestEnvGroups,
 			}
 
 			c.WriteResult(w, r, res)
@@ -288,47 +290,50 @@ func (c *UpdateAppEnvironmentHandler) ServeHTTP(w http.ResponseWriter, r *http.R
 		TargetNamespace:          namespace,
 	}
 
-	syncedEnvironment, err := environment_groups.SyncLatestVersionToNamespace(ctx, agent, inp, additionalEnvGroupLabels)
+	syncedAppEnvironment, err := environment_groups.SyncLatestVersionToNamespace(ctx, agent, inp, additionalEnvGroupLabels)
 	if err != nil {
 		err := telemetry.Error(ctx, span, err, "unable to create or update synced environment group")
 		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
 		return
 	}
-	telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "env-group-versioned-name", Value: syncedEnvironment.EnvironmentGroupVersionedName})
-
-	split := strings.Split(syncedEnvironment.EnvironmentGroupVersionedName, ".")
-	if len(split) != 2 {
-		err := telemetry.Error(ctx, span, err, "unexpected environment group versioned name")
-		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
-		return
-	}
-
-	version, err := strconv.Atoi(split[1])
-	if err != nil {
-		err := telemetry.Error(ctx, span, err, "error converting environment group version to int")
-		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
-		return
-	}
+	telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "env-group-versioned-name", Value: syncedAppEnvironment.EnvironmentGroupVersionedName})
 
 	syncInp := syncLatestEnvGroupVersionsInput{
 		envGroups:          appProto.EnvGroups,
 		appName:            appName,
 		appEnvName:         appEnvGroupName,
-		sameAppEnv:         false,
 		namespace:          namespace,
 		deploymentTargetID: request.DeploymentTargetID,
 		k8sAgent:           agent,
 	}
-	err = syncLatestEnvGroupVersions(ctx, syncInp)
+	latestEnvGroups, err := syncLatestEnvGroupVersions(ctx, syncInp)
 	if err != nil {
 		err := telemetry.Error(ctx, span, err, "error syncing latest env group versions")
 		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
 		return
 	}
 
+	split := strings.Split(syncedAppEnvironment.EnvironmentGroupVersionedName, ".")
+	if len(split) != 2 {
+		err := telemetry.Error(ctx, span, err, "unexpected environment group versioned name")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+		return
+	}
+
+	version, err := strconv.Atoi(split[1])
+	if err != nil {
+		err := telemetry.Error(ctx, span, err, "error converting environment group version to int")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+		return
+	}
+
+	latestEnvGroups = append(latestEnvGroups, environment_groups.EnvironmentGroup{
+		Name:    split[0],
+		Version: version,
+	})
+
 	res := &UpdateAppEnvironmentResponse{
-		EnvGroupName:    split[0],
-		EnvGroupVersion: version,
+		EnvGroups: latestEnvGroups,
 	}
 
 	c.WriteResult(w, r, res)
@@ -341,8 +346,6 @@ type syncLatestEnvGroupVersionsInput struct {
 	appName string
 	// appEnvName is the name of the app env. This is the env group created when the app is created for storing app-specific variables
 	appEnvName string
-	// sameAppEnv is true if the app env group variables are unchanged. If true, we do not need to sync the latest version of the app env group
-	sameAppEnv bool
 	// namespace is the namespace to sync the latest versions to
 	namespace string
 	// deploymentTargetID is the id of the deployment target
@@ -352,30 +355,35 @@ type syncLatestEnvGroupVersionsInput struct {
 }
 
 // syncLatestEnvGroupVersions syncs the latest versions of the env groups to the namespace where an app is deployed
-func syncLatestEnvGroupVersions(ctx context.Context, inp syncLatestEnvGroupVersionsInput) error {
+func syncLatestEnvGroupVersions(ctx context.Context, inp syncLatestEnvGroupVersionsInput) ([]environment_groups.EnvironmentGroup, error) {
 	ctx, span := telemetry.NewSpan(ctx, "sync-latest-env-group-versions")
 	defer span.End()
 
+	var envGroups []environment_groups.EnvironmentGroup
+
 	if inp.deploymentTargetID == "" {
-		return telemetry.Error(ctx, span, nil, "deployment target id is empty")
+		return envGroups, telemetry.Error(ctx, span, nil, "deployment target id is empty")
 	}
 	if inp.appName == "" {
-		return telemetry.Error(ctx, span, nil, "app name is empty")
+		return envGroups, telemetry.Error(ctx, span, nil, "app name is empty")
 	}
 	if inp.appEnvName == "" {
-		return telemetry.Error(ctx, span, nil, "app env name is empty")
+		return envGroups, telemetry.Error(ctx, span, nil, "app env name is empty")
 	}
 	if inp.namespace == "" {
-		return telemetry.Error(ctx, span, nil, "namespace is empty")
+		return envGroups, telemetry.Error(ctx, span, nil, "namespace is empty")
 	}
 	if inp.k8sAgent == nil {
-		return telemetry.Error(ctx, span, nil, "k8s agent is nil")
+		return envGroups, telemetry.Error(ctx, span, nil, "k8s agent is nil")
 	}
 
 	for _, envGroup := range inp.envGroups {
 		if envGroup == nil {
 			continue
 		}
+		if envGroup.GetName() == inp.appEnvName {
+			continue
+		}
 
 		additionalEnvGroupLabels := map[string]string{
 			LabelKey_AppName:            inp.appName,
@@ -383,23 +391,30 @@ func syncLatestEnvGroupVersions(ctx context.Context, inp syncLatestEnvGroupVersi
 			LabelKey_PorterManaged:      "true",
 		}
 
-		if envGroup.GetName() == inp.appEnvName {
-			if inp.sameAppEnv {
-				continue
-			}
-
-			additionalEnvGroupLabels[environment_groups.LabelKey_DefaultAppEnvironment] = "true"
-		}
-
-		_, err := environment_groups.SyncLatestVersionToNamespace(ctx, inp.k8sAgent, environment_groups.SyncLatestVersionToNamespaceInput{
+		syncedEnvironment, err := environment_groups.SyncLatestVersionToNamespace(ctx, inp.k8sAgent, environment_groups.SyncLatestVersionToNamespaceInput{
 			TargetNamespace:          inp.namespace,
 			BaseEnvironmentGroupName: envGroup.GetName(),
 		}, additionalEnvGroupLabels)
 		if err != nil {
 			telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "env-group-name", Value: envGroup.GetName()})
-			return telemetry.Error(ctx, span, err, "error syncing latest version to namespace")
+			return envGroups, telemetry.Error(ctx, span, err, "error syncing latest version to namespace")
 		}
+
+		split := strings.Split(syncedEnvironment.EnvironmentGroupVersionedName, ".")
+		if len(split) != 2 {
+			return envGroups, telemetry.Error(ctx, span, err, "unexpected environment group versioned name")
+		}
+
+		version, err := strconv.Atoi(split[1])
+		if err != nil {
+			return envGroups, telemetry.Error(ctx, span, err, "error converting environment group version to int")
+		}
+
+		envGroups = append(envGroups, environment_groups.EnvironmentGroup{
+			Name:    split[0],
+			Version: version,
+		})
 	}
 
-	return nil
+	return envGroups, nil
 }

+ 9 - 14
cli/cmd/v2/apply.go

@@ -12,6 +12,7 @@ import (
 
 	"github.com/porter-dev/porter/api/server/handlers/porter_app"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/kubernetes/environment_groups"
 	"github.com/porter-dev/porter/internal/models"
 
 	"github.com/cli/cli/git"
@@ -78,7 +79,7 @@ func Apply(ctx context.Context, cliConf config.CLIConfig, client api.Client, por
 			return fmt.Errorf("error calling create or update app environment group endpoint: %w", err)
 		}
 
-		b64AppProto, err = updateAppEnvGroupInProto(ctx, b64AppProto, envGroupResp.EnvGroupName, envGroupResp.EnvGroupVersion)
+		b64AppProto, err = updateEnvGroupsInProto(ctx, b64AppProto, envGroupResp.EnvGroups)
 		if err != nil {
 			return fmt.Errorf("error updating app env group in proto: %w", err)
 		}
@@ -355,7 +356,7 @@ func imageTagFromBase64AppProto(base64AppProto string) (string, error) {
 	return app.Image.Tag, nil
 }
 
-func updateAppEnvGroupInProto(ctx context.Context, base64AppProto string, envGroupName string, envGroupVersion int) (string, error) {
+func updateEnvGroupsInProto(ctx context.Context, base64AppProto string, envGroups []environment_groups.EnvironmentGroup) (string, error) {
 	var editedB64AppProto string
 
 	decoded, err := base64.StdEncoding.DecodeString(base64AppProto)
@@ -369,20 +370,14 @@ func updateAppEnvGroupInProto(ctx context.Context, base64AppProto string, envGro
 		return editedB64AppProto, fmt.Errorf("unable to unmarshal app for revision: %w", err)
 	}
 
-	envGroupExists := false
-	for _, envGroup := range app.EnvGroups {
-		if envGroup.Name == envGroupName {
-			envGroup.Version = int64(envGroupVersion)
-			envGroupExists = true
-			break
-		}
-	}
-	if !envGroupExists {
-		app.EnvGroups = append(app.EnvGroups, &porterv1.EnvGroup{
-			Name:    envGroupName,
-			Version: int64(envGroupVersion),
+	egs := make([]*porterv1.EnvGroup, 0)
+	for _, envGroup := range envGroups {
+		egs = append(egs, &porterv1.EnvGroup{
+			Name:    envGroup.Name,
+			Version: int64(envGroup.Version),
 		})
 	}
+	app.EnvGroups = egs
 
 	marshalled, err := helpers.MarshalContractObject(ctx, app)
 	if err != nil {

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

@@ -181,25 +181,23 @@ const AppDataContainer: React.FC<AppDataContainerProps> = ({ tabParam }) => {
         }
       );
 
-      const updatedEnvGroup = z
+      const updatedEnvGroups = z
         .object({
-          env_group_name: z.string(),
-          env_group_version: z.coerce.bigint(),
+          env_groups: z
+            .object({
+              name: z.string(),
+              latest_version: z.coerce.bigint(),
+            })
+            .array(),
         })
         .parse(res.data);
 
       const protoWithUpdatedEnv = new PorterApp({
         ...validatedAppProto,
-        envGroups: validatedAppProto.envGroups.map((envGroup) => {
-          if (envGroup.name === updatedEnvGroup.env_group_name) {
-            return {
-              ...envGroup,
-              version: updatedEnvGroup.env_group_version,
-            };
-          }
-
-          return envGroup;
-        }),
+        envGroups: updatedEnvGroups.env_groups.map((eg) => ({
+          name: eg.name,
+          version: eg.latest_version,
+        })),
       });
 
       await api.applyApp(

+ 19 - 18
dashboard/src/main/home/app-dashboard/create-app/CreateApp.tsx

@@ -183,7 +183,10 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
     porterYamlFound,
     detectedName,
     loading: isLoadingPorterYaml,
-  } = usePorterYaml({ source: source?.type === "github" ? source : null, appName: name.value });
+  } = usePorterYaml({
+    source: source?.type === "github" ? source : null,
+    appName: name.value,
+  });
   const deploymentTarget = useDefaultDeploymentTarget();
   const { updateAppStep } = useAppAnalytics(name.value);
   const { validateApp } = useAppValidation({
@@ -257,7 +260,7 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
           }
         );
 
-        const envGroupResponse = await api.updateEnvironmentGroupV2(
+        const res = await api.updateEnvironmentGroupV2(
           "<token>",
           {
             deployment_target_id: deploymentTarget.deployment_target_id,
@@ -272,31 +275,29 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
           }
         );
 
-        const addedEnvGroup = await z
+        const updatedEnvGroups = z
           .object({
-            env_group_name: z.string(),
-            env_group_version: z.coerce.bigint(),
+            env_groups: z
+              .object({
+                name: z.string(),
+                latest_version: z.coerce.bigint(),
+              })
+              .array(),
           })
-          .parseAsync(envGroupResponse.data);
+          .parse(res.data);
 
-        const envGroups = [
-          ...app.envGroups.filter(
-            (group) => group.name !== addedEnvGroup.env_group_name
-          ),
-          {
-            name: addedEnvGroup.env_group_name,
-            version: addedEnvGroup.env_group_version,
-          },
-        ];
-        const appWithSeededEnv = new PorterApp({
+        const protoWithUpdatedEnv = new PorterApp({
           ...app,
-          envGroups,
+          envGroups: updatedEnvGroups.env_groups.map((eg) => ({
+            name: eg.name,
+            version: eg.latest_version,
+          })),
         });
 
         await api.applyApp(
           "<token>",
           {
-            b64_app_proto: btoa(appWithSeededEnv.toJsonString()),
+            b64_app_proto: btoa(protoWithUpdatedEnv.toJsonString()),
             deployment_target_id: deploymentTarget.deployment_target_id,
           },
           {

+ 1 - 1
internal/kubernetes/environment_groups/list.go

@@ -35,7 +35,7 @@ type EnvironmentGroup struct {
 	// SecretVariables are secret values for the EnvironmentGroup. This usually will be a Secret on the kubernetes cluster
 	SecretVariables map[string]string `json:"secret_variables,omitempty"`
 	// CreatedAt is only used for display purposes and is in UTC Unix time
-	CreatedAtUTC time.Time `json:"created_at"`
+	CreatedAtUTC time.Time `json:"created_at,omitempty"`
 }
 
 type environmentGroupOptions struct {