ianedwards 2 лет назад
Родитель
Сommit
fa119a38c4

+ 31 - 17
api/server/handlers/environment_groups/list.go

@@ -49,6 +49,7 @@ func (c *ListEnvironmentGroupsHandler) ServeHTTP(w http.ResponseWriter, r *http.
 	ctx, span := telemetry.NewSpan(r.Context(), "serve-list-env-groups")
 	defer span.End()
 
+	project, _ := ctx.Value(types.ProjectScope).(*models.Project)
 	cluster, _ := ctx.Value(types.ClusterScope).(*models.Cluster)
 
 	agent, err := c.GetAgent(r, cluster, "")
@@ -84,26 +85,39 @@ func (c *ListEnvironmentGroupsHandler) ServeHTTP(w http.ResponseWriter, r *http.
 			return
 		}
 
-		applications, err := environmentgroups.LinkedApplications(ctx, agent, latestVersion.Name)
-		if err != nil {
-			err = telemetry.Error(ctx, span, err, "unable to get linked applications")
-			c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
-			return
-		}
+		var linkedApplications []string
+		if !project.GetFeatureFlag(models.ValidateApplyV2, c.Config().LaunchDarklyClient) {
+			applications, err := environmentgroups.LinkedApplications(ctx, agent, latestVersion.Name, true)
+			if err != nil {
+				err = telemetry.Error(ctx, span, err, "unable to get linked applications")
+				c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+				return
+			}
 
-		applicationSetForEnvGroup := make(map[string]struct{})
-		for _, app := range applications {
-			if app.Namespace == "" {
-				continue
+			applicationSetForEnvGroup := make(map[string]struct{})
+			for _, app := range applications {
+				if app.Namespace == "" {
+					continue
+				}
+				if _, ok := applicationSetForEnvGroup[app.Namespace]; !ok {
+					applicationSetForEnvGroup[app.Namespace] = struct{}{}
+				}
 			}
-			if _, ok := applicationSetForEnvGroup[app.Namespace]; !ok {
-				applicationSetForEnvGroup[app.Namespace] = struct{}{}
+			for appNamespace := range applicationSetForEnvGroup {
+				porterAppName := strings.TrimPrefix(appNamespace, "porter-stack-")
+				linkedApplications = append(linkedApplications, porterAppName)
+			}
+		} else {
+			applications, err := environmentgroups.LinkedApplications(ctx, agent, latestVersion.Name, false)
+			if err != nil {
+				err = telemetry.Error(ctx, span, err, "unable to get linked applications")
+				c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+				return
+			}
+
+			for _, app := range applications {
+				linkedApplications = append(linkedApplications, app.Name)
 			}
-		}
-		var linkedApplications []string
-		for appNamespace := range applicationSetForEnvGroup {
-			porterAppName := strings.TrimPrefix(appNamespace, "porter-stack-")
-			linkedApplications = append(linkedApplications, porterAppName)
 		}
 
 		secrets := make(map[string]string)

+ 3 - 3
dashboard/src/main/home/app-dashboard/app-view/LatestRevisionContext.tsx

@@ -27,8 +27,8 @@ export const LatestRevisionContext = createContext<{
   clusterId: number;
   projectId: number;
   deploymentTargetId: string;
-  previewRevision: number | null;
-  setPreviewRevision: Dispatch<SetStateAction<number | null>>;
+  previewRevision: AppRevision | null;
+  setPreviewRevision: Dispatch<SetStateAction<AppRevision | null>>;
 } | null>(null);
 
 export const useLatestRevision = () => {
@@ -48,7 +48,7 @@ export const LatestRevisionProvider = ({
   appName?: string;
   children: JSX.Element;
 }) => {
-  const [previewRevision, setPreviewRevision] = useState<number | null>(null);
+  const [previewRevision, setPreviewRevision] = useState<AppRevision | null>(null);
   const { currentCluster, currentProject } = useContext(Context);
   const deploymentTarget = useDefaultDeploymentTarget();
 

+ 2 - 1
dashboard/src/main/home/app-dashboard/app-view/tabs/Environment.tsx

@@ -19,6 +19,7 @@ const Environment: React.FC = () => {
     latestProto,
     clusterId,
     projectId,
+    previewRevision,
   } = useLatestRevision();
   const {
     formState: { isSubmitting, errors },
@@ -68,7 +69,7 @@ const Environment: React.FC = () => {
       <EnvVariables />
       <EnvGroups
         appName={latestProto.name}
-        revisionId={latestRevision.id}
+        revisionId={previewRevision ? previewRevision.id : latestRevision.id} // get versions of env groups attached to preview revision if set
         baseEnvGroups={baseEnvGroups}
         existingEnvGroupNames={envGroupNames}
       />

+ 1 - 1
dashboard/src/main/home/app-dashboard/validate-apply/app-settings/EnvGroups.tsx

@@ -153,7 +153,7 @@ const EnvGroups: React.FC<Props> = ({
           Max 4 Env Groups allowed
         </TooltipText>
       </TooltipWrapper>
-      {envGroups.length > 0 && (
+      {populatedEnvWithFallback.length > 0 && (
         <>
           <Spacer y={0.5} />
           <Text size={16}>Synced environment groups</Text>

+ 5 - 5
dashboard/src/main/home/app-dashboard/validate-apply/revisions-list/RevisionTableContents.tsx

@@ -102,7 +102,7 @@ const RevisionTableContents: React.FC<RevisionTableContentsProps> = ({
     const { numDeployed, latestRevision } = args;
 
     if (previewRevision) {
-      return previewRevision;
+      return previewRevision.revision_number;
     }
 
     if (latestRevision && latestRevision.revision_number !== 0) {
@@ -181,7 +181,8 @@ const RevisionTableContents: React.FC<RevisionTableContentsProps> = ({
                     key={revision.revision_number}
                     selected={
                       previewRevision
-                        ? revision.revision_number === previewRevision
+                        ? revision.revision_number ===
+                          previewRevision.revision_number
                         : isLatestDeployedRevision
                     }
                     onClick={() => {
@@ -198,10 +199,9 @@ const RevisionTableContents: React.FC<RevisionTableContentsProps> = ({
                           envGroupNames: [],
                         },
                       });
+
                       setPreviewRevision(
-                        isLatestDeployedRevision
-                          ? null
-                          : revision.revision_number
+                        isLatestDeployedRevision ? null : revision
                       );
                     }}
                   >

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

@@ -27,7 +27,7 @@ func DeleteEnvironmentGroup(ctx context.Context, a *kubernetes.Agent, name strin
 	}
 
 	for _, environmentGroup := range environmentGroups {
-		applications, err := LinkedApplications(ctx, a, environmentGroup.Name)
+		applications, err := LinkedApplications(ctx, a, environmentGroup.Name, true)
 		if err != nil {
 			return telemetry.Error(ctx, span, err, "unable to list linked applications")
 		}

+ 77 - 23
internal/kubernetes/environment_groups/list.go

@@ -9,6 +9,8 @@ import (
 
 	"github.com/porter-dev/porter/internal/kubernetes"
 	"github.com/porter-dev/porter/internal/telemetry"
+	appsv1 "k8s.io/api/apps/v1"
+	batchv1 "k8s.io/api/batch/v1"
 	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
 )
 
@@ -22,6 +24,9 @@ const (
 	// Namespace_EnvironmentGroups is the base namespace for storing all environment groups.
 	// The configmaps and secrets here should be considered the source's of truth for a given version
 	Namespace_EnvironmentGroups = "porter-env-group"
+
+	// LabelKey_AppName is the label key for the app name
+	LabelKey_AppName = "porter.run/app-name"
 )
 
 // EnvironmentGroup represents a ConfigMap in the porter-env-group namespace
@@ -228,26 +233,48 @@ type LinkedPorterApplication struct {
 	Namespace string
 }
 
-// LinkedApplications lists all applications that are linked to a given environment group. Since there can be multiple linked environment groups we must check by the presence of a label on the deployment and job
-func LinkedApplications(ctx context.Context, a *kubernetes.Agent, environmentGroupName string) ([]LinkedPorterApplication, error) {
-	ctx, span := telemetry.NewSpan(ctx, "list-linked-applications")
-	defer span.End()
+func listLinkedAppsByUniqueAppLabel(environmentGroupName string, deployments []appsv1.Deployment, cronJobs []batchv1.CronJob) []LinkedPorterApplication {
+	appsByName := make(map[string]LinkedPorterApplication)
 
-	if environmentGroupName == "" {
-		return nil, telemetry.Error(ctx, span, nil, "environment group cannot be empty")
+	for _, d := range deployments {
+		applicationsLinkedEnvironmentGroups := strings.Split(d.Labels[LabelKey_LinkedEnvironmentGroup], ".")
+		appName := d.Labels[LabelKey_AppName]
+
+		for _, linkedEnvironmentGroup := range applicationsLinkedEnvironmentGroups {
+			if linkedEnvironmentGroup == environmentGroupName && appName != "" {
+				appsByName[appName] = LinkedPorterApplication{
+					Name:      appName,
+					Namespace: d.Namespace,
+				}
+			}
+		}
 	}
-	telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "environment-group-name", Value: environmentGroupName})
 
-	deployListResp, err := a.Clientset.AppsV1().Deployments(metav1.NamespaceAll).List(ctx,
-		metav1.ListOptions{
-			LabelSelector: LabelKey_LinkedEnvironmentGroup,
-		})
-	if err != nil {
-		return nil, telemetry.Error(ctx, span, err, "unable to list linked deployment applications")
+	for _, d := range cronJobs {
+		applicationsLinkedEnvironmentGroups := strings.Split(d.Labels[LabelKey_LinkedEnvironmentGroup], ".")
+		appName := d.Labels[LabelKey_AppName]
+
+		for _, linkedEnvironmentGroup := range applicationsLinkedEnvironmentGroups {
+			if linkedEnvironmentGroup == environmentGroupName && appName != "" {
+				appsByName[appName] = LinkedPorterApplication{
+					Name:      appName,
+					Namespace: d.Namespace,
+				}
+			}
+		}
 	}
 
 	var apps []LinkedPorterApplication
-	for _, d := range deployListResp.Items {
+	for _, app := range appsByName {
+		apps = append(apps, app)
+	}
+
+	return apps
+}
+
+func listLinkedAppsByUniqueNamespace(environmentGroupName string, deployments []appsv1.Deployment, cronJobs []batchv1.CronJob) []LinkedPorterApplication {
+	var apps []LinkedPorterApplication
+	for _, d := range deployments {
 		applicationsLinkedEnvironmentGroups := strings.Split(d.Labels[LabelKey_LinkedEnvironmentGroup], ".")
 
 		for _, linkedEnvironmentGroup := range applicationsLinkedEnvironmentGroups {
@@ -260,15 +287,7 @@ func LinkedApplications(ctx context.Context, a *kubernetes.Agent, environmentGro
 		}
 	}
 
-	cronListResp, err := a.Clientset.BatchV1().CronJobs(metav1.NamespaceAll).List(ctx,
-		metav1.ListOptions{
-			LabelSelector: LabelKey_LinkedEnvironmentGroup,
-		})
-	if err != nil {
-		return nil, telemetry.Error(ctx, span, err, "unable to list linked cronjob applications")
-	}
-
-	for _, d := range cronListResp.Items {
+	for _, d := range cronJobs {
 		applicationsLinkedEnvironmentGroups := strings.Split(d.Labels[LabelKey_LinkedEnvironmentGroup], ".")
 		for _, linkedEnvironmentGroup := range applicationsLinkedEnvironmentGroups {
 			if linkedEnvironmentGroup == environmentGroupName {
@@ -280,5 +299,40 @@ func LinkedApplications(ctx context.Context, a *kubernetes.Agent, environmentGro
 		}
 	}
 
+	return apps
+}
+
+// LinkedApplications lists all applications that are linked to a given environment group. Since there can be multiple linked environment groups we must check by the presence of a label on the deployment and job
+func LinkedApplications(ctx context.Context, a *kubernetes.Agent, environmentGroupName string, byUniqueNamespace bool) ([]LinkedPorterApplication, error) {
+	ctx, span := telemetry.NewSpan(ctx, "list-linked-applications")
+	defer span.End()
+
+	if environmentGroupName == "" {
+		return nil, telemetry.Error(ctx, span, nil, "environment group cannot be empty")
+	}
+	telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "environment-group-name", Value: environmentGroupName})
+
+	deployListResp, err := a.Clientset.AppsV1().Deployments(metav1.NamespaceAll).List(ctx,
+		metav1.ListOptions{
+			LabelSelector: LabelKey_LinkedEnvironmentGroup,
+		})
+	if err != nil {
+		return nil, telemetry.Error(ctx, span, err, "unable to list linked deployment applications")
+	}
+	cronListResp, err := a.Clientset.BatchV1().CronJobs(metav1.NamespaceAll).List(ctx,
+		metav1.ListOptions{
+			LabelSelector: LabelKey_LinkedEnvironmentGroup,
+		})
+	if err != nil {
+		return nil, telemetry.Error(ctx, span, err, "unable to list linked cronjob applications")
+	}
+
+	var apps []LinkedPorterApplication
+	if byUniqueNamespace {
+		apps = listLinkedAppsByUniqueNamespace(environmentGroupName, deployListResp.Items, cronListResp.Items)
+		return apps, nil
+	}
+
+	apps = listLinkedAppsByUniqueAppLabel(environmentGroupName, deployListResp.Items, cronListResp.Items)
 	return apps, nil
 }