Cavnas: Implement basic service discovery logic

Change-Id: I71b25076dba94d6491ad4db748b259870991c526
diff --git a/apps/canvas/front/src/lib/config.ts b/apps/canvas/front/src/lib/config.ts
index 572fc78..513f1f0 100644
--- a/apps/canvas/front/src/lib/config.ts
+++ b/apps/canvas/front/src/lib/config.ts
@@ -309,6 +309,7 @@
 			EmptyValidator,
 			GitRepositoryValidator,
 			ServiceValidator,
+			ServiceAnalyzisValidator,
 			GatewayHTTPSValidator,
 			GatewayTCPValidator,
 		),
@@ -488,6 +489,56 @@
 	return noName.concat(noSource).concat(noRuntime).concat(noPorts).concat(noIngress).concat(multipleIngress);
 }
 
+function ServiceAnalyzisValidator(nodes: AppNode[]): Message[] {
+	const apps = nodes.filter((n) => n.type === "app");
+	return apps
+		.filter((n) => n.data.info != null)
+		.flatMap((n) => {
+			return n.data
+				.info!.configVars.map((cv): Message | undefined => {
+					if (cv.semanticType === "PORT") {
+						if (
+							!(n.data.envVars || []).some(
+								(p) => ("name" in p && p.name === cv.name) || ("alias" in p && p.alias === cv.name),
+							)
+						) {
+							return {
+								id: `${n.id}-missing-port-${cv.name}`,
+								type: "WARNING",
+								nodeId: n.id,
+								message: `Service requires port env variable ${cv.name}`,
+							};
+						}
+					}
+					if (cv.category === "EnvironmentVariable") {
+						if (
+							!(n.data.envVars || []).some(
+								(p) => ("name" in p && p.name === cv.name) || ("alias" in p && p.alias === cv.name),
+							)
+						) {
+							if (cv.semanticType === "FILESYSTEM_PATH") {
+								return {
+									id: `${n.id}-missing-env-${cv.name}`,
+									type: "FATAL",
+									nodeId: n.id,
+									message: `Service requires env variable ${cv.name}, representing filesystem path`,
+								};
+							} else if (cv.semanticType === "POSTGRES_URL") {
+								return {
+									id: `${n.id}-missing-env-${cv.name}`,
+									type: "FATAL",
+									nodeId: n.id,
+									message: `Service requires env variable ${cv.name}, representing postgres connection URL`,
+								};
+							}
+						}
+					}
+					return undefined;
+				})
+				.filter((m) => m !== undefined);
+		});
+}
+
 function GatewayHTTPSValidator(nodes: AppNode[]): Message[] {
 	const ing = nodes.filter((n) => n.type === "gateway-https");
 	const noNetwork: Message[] = ing