Просмотр исходного кода

add namespace annotations FE changes

Mohammed Nafees 3 лет назад
Родитель
Сommit
ab3442ff4a

+ 11 - 11
api/server/handlers/environment/update_environment_settings.go

@@ -89,20 +89,20 @@ func (c *UpdateEnvironmentSettingsHandler) ServeHTTP(w http.ResponseWriter, r *h
 		changed = true
 	}
 
-	changed = reflect.DeepEqual(env.ToEnvironmentType().NamespaceAnnotations, request.NamespaceAnnotations)
+	if len(request.NamespaceAnnotations) > 0 {
+		var annotations []string
 
-	if changed {
-		if len(request.NamespaceAnnotations) > 0 {
-			var annotations []string
+		for k, v := range request.NamespaceAnnotations {
+			annotations = append(annotations, fmt.Sprintf("%s=%s", k, v))
+		}
 
-			for k, v := range request.NamespaceAnnotations {
-				annotations = append(annotations, fmt.Sprintf("%s=%s", k, v))
-			}
+		env.NamespaceAnnotations = []byte(strings.Join(annotations, ","))
 
-			env.NamespaceAnnotations = []byte(strings.Join(annotations, ","))
-		} else {
-			env.NamespaceAnnotations = []byte{}
-		}
+		changed = true
+	} else {
+		env.NamespaceAnnotations = []byte{}
+
+		changed = true
 	}
 
 	if changed {

+ 53 - 9
dashboard/src/main/home/cluster-dashboard/preview-environments/ConnectNewRepo.tsx

@@ -15,6 +15,9 @@ import PullRequestIcon from "assets/pull_request_icon.svg";
 import CheckboxRow from "components/form-components/CheckboxRow";
 import BranchFilterSelector from "./components/BranchFilterSelector";
 import Helper from "components/form-components/Helper";
+import NamespaceAnnotations, {
+  KeyValueType,
+} from "./components/NamespaceAnnotations";
 
 const ConnectNewRepo: React.FC = () => {
   const { currentProject, currentCluster, setCurrentError } = useContext(
@@ -49,6 +52,11 @@ const ConnectNewRepo: React.FC = () => {
   // Use custom namespaces
   const [useCustomNamespaces, setUseCustomNamespaces] = useState(false);
 
+  // Namespace annotations
+  const [namespaceAnnotations, setNamespaceAnnotations] = useState<
+    KeyValueType[]
+  >([]);
+
   useEffect(() => {
     api
       .listEnvironments<Environment[]>(
@@ -112,7 +120,32 @@ const ConnectNewRepo: React.FC = () => {
 
   const addRepo = () => {
     let [owner, repoName] = repo.split("/");
+    let annotations: Record<string, string> = {};
+
     setStatus("loading");
+
+    namespaceAnnotations
+      .filter((elem: KeyValueType, index: number, self: KeyValueType[]) => {
+        // remove any collisions that are duplicates
+        let numCollisions = self.reduce((n, _elem: KeyValueType) => {
+          return n + (_elem.key === elem.key ? 1 : 0);
+        }, 0);
+
+        if (numCollisions == 1) {
+          return true;
+        } else {
+          return (
+            index ===
+            self.findIndex((_elem: KeyValueType) => _elem.key === elem.key)
+          );
+        }
+      })
+      .forEach((elem: KeyValueType) => {
+        if (elem.key !== "" && elem.value !== "") {
+          annotations[elem.key] = elem.value;
+        }
+      });
+
     api
       .createEnvironment(
         "<token>",
@@ -122,7 +155,7 @@ const ConnectNewRepo: React.FC = () => {
           disable_new_comments: isNewCommentsDisabled,
           git_repo_branches: selectedBranches,
           custom_namespaces: useCustomNamespaces,
-          namespace_annotations: {},
+          namespace_annotations: annotations,
         },
         {
           project_id: currentProject.id,
@@ -240,9 +273,9 @@ const ConnectNewRepo: React.FC = () => {
 
       <Heading>Custom namespaces</Heading>
       <Helper>
-        By default, Porter chooses a templated namespace for every new GitHub PR.
-        When this is enabled, you can choose a custom namespace of your choice in
-        the GitHub action workflow file that we create for you.
+        By default, Porter chooses a templated namespace for every new GitHub
+        PR. When this is enabled, you can choose a custom namespace of your
+        choice in the GitHub action workflow file that we create for you.
       </Helper>
       <CheckboxWrapper>
         <CheckboxRow
@@ -253,13 +286,24 @@ const ConnectNewRepo: React.FC = () => {
             disableMargin: true,
           }}
         />
-        <DocsHelper
-          disableMargin
-          tooltipText="When checked, it is up to you to choose a namespace for every new deployment."
-          placement="top-end"
-        />
       </CheckboxWrapper>
 
+      <Heading>Namespace annotations</Heading>
+      <Helper>
+        Custom annotations to be injected into the Kubernetes namespace created
+        for each deployment.
+      </Helper>
+      <NamespaceAnnotations
+        values={namespaceAnnotations}
+        setValues={(x: KeyValueType[]) => {
+          let annotations: KeyValueType[] = [];
+          x.forEach((entry) => {
+            annotations.push({ key: entry.key, value: entry.value });
+          });
+          setNamespaceAnnotations(annotations);
+        }}
+      />
+
       <ActionContainer>
         <SaveButton
           text="Add repository"

+ 164 - 0
dashboard/src/main/home/cluster-dashboard/preview-environments/components/NamespaceAnnotations.tsx

@@ -0,0 +1,164 @@
+import React, { useEffect } from "react";
+import styled from "styled-components";
+
+export type KeyValueType = {
+  key: string;
+  value: string;
+};
+
+type PropsType = {
+  values: KeyValueType[];
+  setValues: (x: KeyValueType[]) => void;
+};
+
+const NamespaceAnnotations = ({ values, setValues }: PropsType) => {
+  useEffect(() => {
+    if (!values) {
+      setValues([]);
+    }
+  }, [values]);
+
+  if (!values) {
+    return null;
+  }
+
+  return (
+    <>
+      <StyledInputArray>
+        {!!values?.length &&
+          values.map((entry: KeyValueType, i: number) => {
+            return (
+              <InputWrapper key={i}>
+                <Input
+                  placeholder="ex: key"
+                  width="270px"
+                  value={entry.key}
+                  onChange={(e: any) => {
+                    let _values = values;
+                    _values[i].key = e.target.value;
+                    setValues(_values);
+                  }}
+                />
+                <Spacer />
+                <Input
+                  placeholder="ex: value"
+                  width="270px"
+                  value={entry.value}
+                  onChange={(e: any) => {
+                    let _values = values;
+                    _values[i].value = e.target.value;
+                    setValues(_values);
+                  }}
+                />
+                <DeleteButton
+                  onClick={() => {
+                    let _values = values;
+                    _values = _values.filter((val) => val.key !== entry.key);
+                    setValues(_values);
+                  }}
+                >
+                  <i className="material-icons">cancel</i>
+                </DeleteButton>
+              </InputWrapper>
+            );
+          })}
+        <InputWrapper>
+          <AddRowButton
+            onClick={() => {
+              let _values = values;
+              _values.push({
+                key: "",
+                value: "",
+              });
+              setValues(_values);
+            }}
+          >
+            <i className="material-icons">add</i> Add Row
+          </AddRowButton>
+          <Spacer />
+        </InputWrapper>
+      </StyledInputArray>
+    </>
+  );
+};
+
+export default NamespaceAnnotations;
+
+const Spacer = styled.div`
+  width: 10px;
+  height: 20px;
+`;
+
+const AddRowButton = styled.div`
+  display: flex;
+  align-items: center;
+  width: 270px;
+  font-size: 13px;
+  color: #aaaabb;
+  height: 32px;
+  border-radius: 3px;
+  cursor: pointer;
+  background: #ffffff11;
+  :hover {
+    background: #ffffff22;
+  }
+
+  > i {
+    color: #ffffff44;
+    font-size: 16px;
+    margin-left: 8px;
+    margin-right: 10px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+  }
+`;
+
+const DeleteButton = styled.div`
+  width: 15px;
+  height: 15px;
+  display: flex;
+  align-items: center;
+  margin-left: 8px;
+  margin-top: -3px;
+  justify-content: center;
+
+  > i {
+    font-size: 17px;
+    color: #ffffff44;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    cursor: pointer;
+    :hover {
+      color: #ffffff88;
+    }
+  }
+`;
+
+const InputWrapper = styled.div`
+  display: flex;
+  align-items: center;
+  margin-top: 5px;
+`;
+
+const Input = styled.input`
+  outline: none;
+  border: none;
+  margin-bottom: 5px;
+  font-size: 13px;
+  background: #ffffff11;
+  border: 1px solid #ffffff55;
+  border-radius: 3px;
+  width: ${(props: { disabled?: boolean; width: string }) =>
+    props.width ? props.width : "270px"};
+  color: ${(props: { disabled?: boolean; width: string }) =>
+    props.disabled ? "#ffffff44" : "white"};
+  padding: 5px 10px;
+  height: 35px;
+`;
+
+const StyledInputArray = styled.div`
+  margin-bottom: 15px;
+  margin-top: 22px;
+`;

+ 62 - 2
dashboard/src/main/home/cluster-dashboard/preview-environments/environments/EnvironmentSettings.tsx

@@ -18,6 +18,9 @@ import Banner from "components/Banner";
 import InputRow from "components/form-components/InputRow";
 import Modal from "main/home/modals/Modal";
 import { useRouting } from "shared/routing";
+import NamespaceAnnotations, {
+  KeyValueType,
+} from "../components/NamespaceAnnotations";
 
 const EnvironmentSettings = () => {
   const router = useRouting();
@@ -33,6 +36,9 @@ const EnvironmentSettings = () => {
     deploymentMode,
     setDeploymentMode,
   ] = useState<EnvironmentDeploymentMode>("manual");
+  const [namespaceAnnotations, setNamespaceAnnotations] = useState<
+    KeyValueType[]
+  >([]);
   const {
     environment_id: environmentId,
     repo_name: repoName,
@@ -60,6 +66,19 @@ const EnvironmentSettings = () => {
       setEnvironment(environment);
       setNewCommentsDisabled(environment.new_comments_disabled);
       setDeploymentMode(environment.mode);
+
+      if (environment.namespace_annotations) {
+        const annotations: KeyValueType[] = [];
+
+        Object.keys(environment.namespace_annotations).forEach((k) => {
+          annotations.push({
+            key: k,
+            value: environment.namespace_annotations[k],
+          });
+        });
+
+        setNamespaceAnnotations(annotations);
+      }
     };
 
     try {
@@ -70,8 +89,32 @@ const EnvironmentSettings = () => {
   }, []);
 
   const handleSave = async () => {
+    let annotations: Record<string, string> = {};
+
     setSaveStatus("loading");
 
+    namespaceAnnotations
+      .filter((elem: KeyValueType, index: number, self: KeyValueType[]) => {
+        // remove any collisions that are duplicates
+        let numCollisions = self.reduce((n, _elem: KeyValueType) => {
+          return n + (_elem.key === elem.key ? 1 : 0);
+        }, 0);
+
+        if (numCollisions == 1) {
+          return true;
+        } else {
+          return (
+            index ===
+            self.findIndex((_elem: KeyValueType) => _elem.key === elem.key)
+          );
+        }
+      })
+      .forEach((elem: KeyValueType) => {
+        if (elem.key !== "" && elem.value !== "") {
+          annotations[elem.key] = elem.value;
+        }
+      });
+
     try {
       await api.updateEnvironment(
         "<token>",
@@ -79,7 +122,7 @@ const EnvironmentSettings = () => {
           mode: deploymentMode,
           disable_new_comments: newCommentsDisabled,
           git_repo_branches: [],
-          namespace_annotations: {},
+          namespace_annotations: annotations,
         },
         {
           project_id: currentProject.id,
@@ -172,7 +215,7 @@ const EnvironmentSettings = () => {
         </Helper>
         <CheckboxRow
           label="Update the most recent PR comment"
-          checked={!newCommentsDisabled}
+          checked={newCommentsDisabled}
           toggle={() => setNewCommentsDisabled(!newCommentsDisabled)}
         />
         <Br />
@@ -190,6 +233,23 @@ const EnvironmentSettings = () => {
             )
           }
         />
+        <Br />
+        <Heading>Namespace annotations</Heading>
+        <Helper>
+          Custom annotations to be injected into the Kubernetes namespace
+          created for each deployment. Note that this will not affect existing
+          deployments in this preview environment.
+        </Helper>
+        <NamespaceAnnotations
+          values={namespaceAnnotations}
+          setValues={(x: KeyValueType[]) => {
+            let annotations: KeyValueType[] = [];
+            x.forEach((entry) => {
+              annotations.push({ key: entry.key, value: entry.value });
+            });
+            setNamespaceAnnotations(annotations);
+          }}
+        />
         <SavePreviewEnvironmentSettings
           text={"Save"}
           status={saveStatus}

+ 1 - 0
dashboard/src/main/home/cluster-dashboard/preview-environments/types.ts

@@ -44,6 +44,7 @@ export type Environment = {
   last_deployment_status: DeploymentStatusUnion;
   deployment_count: number;
   mode: EnvironmentDeploymentMode;
+  namespace_annotations: Record<string, string>;
 };
 
 export type PullRequest = {