sdess09 преди 2 години
родител
ревизия
c6dfcc4fd4

+ 0 - 1
api/server/handlers/porter_app/validate.go

@@ -208,7 +208,6 @@ func (c *ValidatePorterAppHandler) ServeHTTP(w http.ResponseWriter, r *http.Requ
 	}
 
 	b64 := base64.StdEncoding.EncodeToString(encoded)
-
 	response := &ValidatePorterAppResponse{
 		ValidatedBase64AppProto: b64,
 	}

+ 1 - 0
api/types/project.go

@@ -35,6 +35,7 @@ type Project struct {
 	StacksEnabled          bool    `json:"stacks_enabled"`
 	CapiProvisionerEnabled bool    `json:"capi_provisioner_enabled"`
 	DBEnabled              bool    `json:"db_enabled"`
+	EFSEnabled             bool    `json:"efs_enabled"`
 	SimplifiedViewEnabled  bool    `json:"simplified_view_enabled"`
 	AzureEnabled           bool    `json:"azure_enabled"`
 	HelmValuesEnabled      bool    `json:"helm_values_enabled"`

+ 7 - 7
dashboard/package-lock.json

@@ -90,7 +90,7 @@
         "@babel/preset-react": "^7.14.5",
         "@babel/preset-typescript": "^7.15.0",
         "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
-        "@porter-dev/api-contracts": "^0.2.27",
+        "@porter-dev/api-contracts": "^0.2.28",
         "@testing-library/jest-dom": "^4.2.4",
         "@testing-library/react": "^9.3.2",
         "@testing-library/user-event": "^7.1.2",
@@ -2459,9 +2459,9 @@
       }
     },
     "node_modules/@porter-dev/api-contracts": {
-      "version": "0.2.27",
-      "resolved": "https://registry.npmjs.org/@porter-dev/api-contracts/-/api-contracts-0.2.27.tgz",
-      "integrity": "sha512-Zh8R4p+gPGYXeELP5rlUbkDLf0w7wgUPWeUHniKK7WyCQNIb4zrSImVl3PC1S12eUbRfT+Nry6Jj0VcktMN0Tw==",
+      "version": "0.2.28",
+      "resolved": "https://registry.npmjs.org/@porter-dev/api-contracts/-/api-contracts-0.2.28.tgz",
+      "integrity": "sha512-+UCD2ukvdjMkYEGCORSgXG3yZ6m//XzC9zEWuBf/p+iaztW5dM3vYaIDVpqDEzvndihLRekYrqVeD6cE/AoONQ==",
       "dev": true,
       "dependencies": {
         "@bufbuild/protobuf": "^1.1.0"
@@ -16988,9 +16988,9 @@
       "integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
     },
     "@porter-dev/api-contracts": {
-      "version": "0.2.27",
-      "resolved": "https://registry.npmjs.org/@porter-dev/api-contracts/-/api-contracts-0.2.27.tgz",
-      "integrity": "sha512-Zh8R4p+gPGYXeELP5rlUbkDLf0w7wgUPWeUHniKK7WyCQNIb4zrSImVl3PC1S12eUbRfT+Nry6Jj0VcktMN0Tw==",
+      "version": "0.2.28",
+      "resolved": "https://registry.npmjs.org/@porter-dev/api-contracts/-/api-contracts-0.2.28.tgz",
+      "integrity": "sha512-+UCD2ukvdjMkYEGCORSgXG3yZ6m//XzC9zEWuBf/p+iaztW5dM3vYaIDVpqDEzvndihLRekYrqVeD6cE/AoONQ==",
       "dev": true,
       "requires": {
         "@bufbuild/protobuf": "^1.1.0"

+ 2 - 2
dashboard/package.json

@@ -95,7 +95,7 @@
     "@babel/preset-react": "^7.14.5",
     "@babel/preset-typescript": "^7.15.0",
     "@pmmmwh/react-refresh-webpack-plugin": "^0.4.3",
-    "@porter-dev/api-contracts": "^0.2.27",
+    "@porter-dev/api-contracts": "^0.2.28",
     "@testing-library/jest-dom": "^4.2.4",
     "@testing-library/react": "^9.3.2",
     "@testing-library/user-event": "^7.1.2",
@@ -151,4 +151,4 @@
     "webpack-cli": "^3.3.12",
     "webpack-dev-server": "^3.11.0"
   }
-}
+}

+ 23 - 1
dashboard/src/lib/porter-apps/index.ts

@@ -15,6 +15,7 @@ import {
   HelmOverrides,
   PorterApp,
   Service,
+  EFS,
 } from "@porter-dev/api-contracts";
 import { match } from "ts-pattern";
 import { KeyValueType } from "main/home/cluster-dashboard/env-groups/EnvGroupArray";
@@ -73,6 +74,10 @@ export const clientAppValidator = z.object({
         message: 'Lowercase letters, numbers, and "-" only.',
       }),
   }),
+  efsStorage: z.object({
+    enabled: z.boolean(),
+    readOnly: z.boolean().optional(),
+  }),
   envGroups: z
     .object({ name: z.string(), version: z.bigint() })
     .array()
@@ -280,7 +285,12 @@ export function clientAppToProto(data: PorterAppFormData): PorterApp {
               app.helmOverrides != null
                 ? new HelmOverrides({ b64Values: btoa(app.helmOverrides) })
                 : undefined,
+
           }),
+          efsStorage:
+            new EFS({
+              enabled: app.efsStorage.enabled,
+            })
         })
     )
     .with(
@@ -301,6 +311,11 @@ export function clientAppToProto(data: PorterAppFormData): PorterApp {
             app.helmOverrides != null
               ? new HelmOverrides({ b64Values: btoa(app.helmOverrides) })
               : undefined,
+          efsStorage:
+            new EFS({
+              enabled: app.efsStorage.enabled,
+            })
+
         })
     )
     .exhaustive();
@@ -439,6 +454,10 @@ export function clientAppFromProto({
         builder: "",
       },
       helmOverrides: helmOverrides,
+      efsStorage: new EFS({
+        enabled: proto.efsStorage?.enabled ?? false,
+      })
+
     };
   }
 
@@ -477,6 +496,10 @@ export function clientAppFromProto({
       builder: "",
     },
     helmOverrides: helmOverrides,
+    efsStorage:
+      { enabled: proto.efsStorage?.enabled ?? false }
+
+    ,
   };
 }
 
@@ -569,6 +592,5 @@ export function applyPreviewOverrides({
     }));
 
   app.env = [...env, ...additionalEnv];
-
   return app;
 }

+ 40 - 3
dashboard/src/main/home/app-dashboard/app-view/tabs/Settings.tsx

@@ -13,16 +13,23 @@ import { useAppAnalytics } from "lib/hooks/useAppAnalytics";
 import { useQueryClient } from "@tanstack/react-query";
 import { Context } from "shared/Context";
 import PreviewEnvironmentSettings from "./preview-environments/PreviewEnvironmentSettings";
+import { Controller, useFormContext } from "react-hook-form";
+import { PorterAppFormData } from "lib/porter-apps";
+import Checkbox from "components/porter/Checkbox";
 
 const Settings: React.FC = () => {
-  const { currentProject } = useContext(Context);
+  const { currentProject, currentCluster } = useContext(Context);
   const queryClient = useQueryClient();
   const history = useHistory();
   const [isDeleteModalOpen, setIsDeleteModalOpen] = useState(false);
   const { porterApp, clusterId, projectId, latestProto } = useLatestRevision();
   const { updateAppStep } = useAppAnalytics();
   const [isDeleting, setIsDeleting] = useState(false);
-
+  const {
+    control,
+    setValue,
+    watch
+  } = useFormContext<PorterAppFormData>();
   const [githubWorkflowFilename, setGithubWorkflowFilename] = useState(
     `porter_stack_${porterApp.name}.yml`
   );
@@ -142,8 +149,38 @@ const Settings: React.FC = () => {
       {currentProject?.preview_envs_enabled && !!latestProto.build ? (
         <PreviewEnvironmentSettings />
       ) : null}
+
+      {(currentCluster?.cloud_provider == "AWS" && currentProject?.efs_enabled) && <>
+        <Text size={16}>Enable shared storage across services for "{porterApp.name}"</Text>
+        <Spacer y={0.5} />
+        <Spacer y={.5} />
+        <Controller
+          name={`app.efsStorage`}
+          control={control}
+          render={({ field: { value, onChange } }) => (
+            <Checkbox
+              checked={value.enabled}
+              toggleChecked={() => {
+                onChange({
+                  ...value,
+                  enabled: !value.enabled,
+                },
+                );
+              }}
+              disabled={value.readOnly}
+              disabledTooltip={
+                "You may only edit this field in your porter.yaml."
+              }
+            >
+              <Text color="helper">
+                Enable EFS Storage
+              </Text>
+            </Checkbox>
+          )} />
+        <Spacer y={1} />
+      </>}
       <Text size={16}>Delete "{porterApp.name}"</Text>
-      <Spacer y={0.5} />
+      <Spacer y={.5} />
       <Text color="helper">
         Delete this application and all of its resources.
       </Text>

+ 6 - 4
dashboard/src/main/home/app-dashboard/create-app/CreateApp.tsx

@@ -157,6 +157,9 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
           buildpacks: [],
         },
         env: [],
+        efsStorage: {
+          enabled: false,
+        }
       },
       source: {
         git_repo_name: "",
@@ -289,7 +292,7 @@ const CreateApp: React.FC<CreateAppProps> = ({ history }) => {
             deployment_target_id: deploymentTarget.deployment_target_id,
             variables,
             secrets,
-            hard_env_update: true
+            hard_env_update: true,
           },
           {
             project_id: currentProject.id,
@@ -645,9 +648,8 @@ 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>

+ 2 - 0
dashboard/src/shared/types.tsx

@@ -287,6 +287,8 @@ export interface ProjectType {
   multi_cluster: boolean;
   full_add_ons: boolean;
   enable_reprovision: boolean;
+  quota_increase: boolean;
+  efs_enabled: boolean;
   validate_apply_v2: boolean;
   roles: {
     id: number;

+ 7 - 0
internal/models/project.go

@@ -28,6 +28,9 @@ const (
 	// DBEnabled enables the "Databases" tab
 	DBEnabled FeatureFlagLabel = "db_enabled"
 
+	// EFSEnabled enables the "EFS" checkbox in App Settings
+	EFSEnabled FeatureFlagLabel = "efs_enabled"
+
 	// EnableReprovision enables the provisioning button after initial creation of the cluster
 	EnableReprovision FeatureFlagLabel = "enable_reprovision"
 
@@ -68,6 +71,7 @@ var ProjectFeatureFlags = map[FeatureFlagLabel]bool{
 	AzureEnabled:           false,
 	CapiProvisionerEnabled: true,
 	DBEnabled:              false,
+	EFSEnabled:             false,
 	EnableReprovision:      false,
 	FullAddOns:             false,
 	HelmValuesEnabled:      false,
@@ -209,6 +213,8 @@ func (p *Project) GetFeatureFlag(flagName FeatureFlagLabel, launchDarklyClient *
 			return p.StacksEnabled
 		case "validate_apply_v2":
 			return p.ValidateApplyV2
+		case "efs_enabled":
+			return false
 		}
 	}
 
@@ -251,6 +257,7 @@ func (p *Project) ToProjectType(launchDarklyClient *features.Client) types.Proje
 		ValidateApplyV2:        p.GetFeatureFlag(ValidateApplyV2, launchDarklyClient),
 		FullAddOns:             p.GetFeatureFlag(FullAddOns, launchDarklyClient),
 		QuotaIncrease:          p.GetFeatureFlag(QuotaIncrease, launchDarklyClient),
+		EFSEnabled:             p.GetFeatureFlag(EFSEnabled, launchDarklyClient),
 	}
 }
 

+ 19 - 2
internal/porter_app/v2/yaml.go

@@ -88,8 +88,9 @@ type PorterApp struct {
 	Build    *Build            `yaml:"build,omitempty"`
 	Env      map[string]string `yaml:"env,omitempty"`
 
-	Predeploy *Service   `yaml:"predeploy,omitempty"`
-	EnvGroups []EnvGroup `yaml:"envGroups,omitempty"`
+	Predeploy  *Service    `yaml:"predeploy,omitempty"`
+	EnvGroups  []EnvGroup  `yaml:"envGroups,omitempty"`
+	EfsStorage *EfsStorage `yaml:"efsStorage,omitempty"`
 }
 
 // PorterYAML represents all the possible fields in a Porter YAML file
@@ -98,6 +99,11 @@ type PorterYAML struct {
 	Previews  *PorterApp `yaml:"previews,omitempty"`
 }
 
+// EfsStorage represents the EFS storage settings for a Porter app
+type EfsStorage struct {
+	Enabled bool `yaml:"enabled"`
+}
+
 // Build represents the build settings for a Porter app
 type Build struct {
 	Context    string   `yaml:"context" validate:"dir"`
@@ -227,6 +233,11 @@ func ProtoFromApp(ctx context.Context, porterApp PorterApp) (*porterv1.PorterApp
 	}
 	appProto.EnvGroups = envGroups
 
+	if porterApp.EfsStorage != nil {
+		appProto.EfsStorage = &porterv1.EFS{
+			Enabled: porterApp.EfsStorage.Enabled,
+		}
+	}
 	return appProto, porterApp.Env, nil
 }
 
@@ -403,6 +414,12 @@ func AppFromProto(appProto *porterv1.PorterApp) (PorterApp, error) {
 		})
 	}
 
+	if appProto.EfsStorage != nil {
+		porterApp.EfsStorage = &EfsStorage{
+			Enabled: appProto.EfsStorage.Enabled,
+		}
+	}
+
 	return porterApp, nil
 }