Преглед изворни кода

Merge pull request #336 from porter-dev/master

add-on's
abelanger5 пре 5 година
родитељ
комит
e6d4cb871e
31 измењених фајлова са 394 додато и 208 уклоњено
  1. 3 0
      cli/cmd/auth.go
  2. 1 0
      dashboard/src/components/ResourceTab.tsx
  3. 1 1
      dashboard/src/components/TabRegion.tsx
  4. 2 2
      dashboard/src/components/TabSelector.tsx
  5. 2 4
      dashboard/src/components/image-selector/ImageList.tsx
  6. 1 1
      dashboard/src/components/image-selector/ImageSelector.tsx
  7. 1 1
      dashboard/src/components/repo-selector/ActionConfEditor.tsx
  8. 8 8
      dashboard/src/components/repo-selector/ActionDetails.tsx
  9. 0 2
      dashboard/src/components/values-form/InputArray.tsx
  10. 185 0
      dashboard/src/components/values-form/KeyValueArray.tsx
  11. 30 0
      dashboard/src/components/values-form/ValuesForm.tsx
  12. 5 0
      dashboard/src/components/values-form/ValuesWrapper.tsx
  13. 2 16
      dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx
  14. 8 0
      dashboard/src/main/home/cluster-dashboard/expanded-chart/SettingsSection.tsx
  15. 2 2
      dashboard/src/main/home/launch/Launch.tsx
  16. 1 1
      dashboard/src/main/home/launch/expanded-template/ExpandedTemplate.tsx
  17. 138 108
      dashboard/src/main/home/launch/expanded-template/LaunchTemplate.tsx
  18. 1 1
      dashboard/src/main/home/launch/expanded-template/TemplateInfo.tsx
  19. 1 0
      dashboard/src/shared/types.tsx
  20. 1 1
      internal/config/config.go
  21. 0 4
      internal/forms/release.go
  22. 0 2
      internal/kubernetes/agent.go
  23. 0 4
      internal/kubernetes/provisioner/global_stream.go
  24. 0 2
      internal/kubernetes/provisioner/resource_stream.go
  25. 0 4
      internal/registry/registry.go
  26. 0 3
      server/api/cluster_handler.go
  27. 0 2
      server/api/git_repo_handler.go
  28. 0 5
      server/api/integration_handler_test.go
  29. 0 28
      server/api/registry_handler.go
  30. 0 3
      server/api/release_handler_test.go
  31. 1 3
      server/router/middleware/auth.go

+ 3 - 0
cli/cmd/auth.go

@@ -200,6 +200,9 @@ func loginManual() error {
 		return err
 	}
 
+	// set the token to empty since this is manual (cookie-based) login
+	setToken("")
+
 	color.New(color.FgGreen).Println("Successfully logged in!")
 
 	// get a list of projects, and set the current project

+ 1 - 0
dashboard/src/components/ResourceTab.tsx

@@ -259,6 +259,7 @@ const StatusColor = styled.div`
 
 const ResourceName = styled.div`
   color: #ffffff;
+  max-width: 40%;
   margin-left: ${(props: { showKindLabels: boolean }) =>
     props.showKindLabels ? "10px" : ""};
   text-transform: none;

+ 1 - 1
dashboard/src/components/TabRegion.tsx

@@ -77,7 +77,7 @@ const Div = styled.div`
 `;
 
 const TabContents = styled.div`
-  height: calc(100% - 60px);
+  height: calc(100% - 80px);
 `;
 
 const Gap = styled.div`

+ 2 - 2
dashboard/src/components/TabSelector.tsx

@@ -95,10 +95,10 @@ const Tab = styled.div`
 
 const StyledTabSelector = styled.div`
   display: flex;
-  width: calc(100% - 4px);
+  width: calc(100% - 2px);
   align-items: center;
   border-bottom: 1px solid #aaaabb55;
   padding-bottom: 1px;
-  margin-left: 2px;
+  margin-left: 1px;
   position: relative;
 `;

+ 2 - 4
dashboard/src/components/image-selector/ImageList.tsx

@@ -93,7 +93,7 @@ export default class ImageSelector extends Component<PropsType, StateType> {
                         }) == registries.length
                           ? true
                           : false;
-                      
+
                       this.setState({
                         images,
                         loading: false,
@@ -102,11 +102,9 @@ export default class ImageSelector extends Component<PropsType, StateType> {
                     } else {
                       this.setState({
                         images,
-                      })
+                      });
                     }
 
-                   
-                    
                     resolveToNextController();
                   });
               }

+ 1 - 1
dashboard/src/components/image-selector/ImageSelector.tsx

@@ -295,7 +295,7 @@ const ImageItem = styled.div`
   font-size: 13px;
   border-bottom: 1px solid
     ${(props: { lastItem: boolean; isSelected: boolean }) =>
-    props.lastItem ? "#00000000" : "#606166"};
+      props.lastItem ? "#00000000" : "#606166"};
   color: #ffffff;
   user-select: none;
   align-items: center;

+ 1 - 1
dashboard/src/components/repo-selector/ActionConfEditor.tsx

@@ -16,7 +16,7 @@ type PropsType = {
   setActionConfig: (x: ActionConfigType) => void;
   setBranch: (x: string) => void;
   setPath: (x: boolean) => void;
-  reset: () => void;
+  reset: any;
 };
 
 type StateType = {

+ 8 - 8
dashboard/src/components/repo-selector/ActionDetails.tsx

@@ -64,14 +64,14 @@ export default class ActionDetails extends Component<PropsType, StateType> {
           setValue={(x: string) => console.log(x)}
         />
         <Label>Target Image URL</Label>
-          <ImageSelector
-            selectedTag="latest"
-            selectedImageUrl={this.props.actionConfig.image_repo_uri}
-            setSelectedImageUrl={this.setURL}
-            setSelectedTag={() => null}
-            forceExpanded={true}
-            noTagSelection={true}
-          />
+        <ImageSelector
+          selectedTag="latest"
+          selectedImageUrl={this.props.actionConfig.image_repo_uri}
+          setSelectedImageUrl={this.setURL}
+          setSelectedTag={() => null}
+          forceExpanded={true}
+          noTagSelection={true}
+        />
       </Holder>
     );
   };

+ 0 - 2
dashboard/src/components/values-form/InputArray.tsx

@@ -1,8 +1,6 @@
 import React, { Component } from "react";
 import styled from "styled-components";
 
-import InputRow from "./InputRow";
-
 type PropsType = {
   label?: string;
   values: string[];

+ 185 - 0
dashboard/src/components/values-form/KeyValueArray.tsx

@@ -0,0 +1,185 @@
+import React, { Component } from "react";
+import styled from "styled-components";
+
+type PropsType = {
+  label?: string;
+  values: any;
+  setValues: (x: any) => void;
+  width?: string;
+};
+
+type StateType = {
+  values: any[];
+};
+
+export default class KeyValueArray extends Component<PropsType, StateType> {
+  state = {
+    values: [] as any[],
+  };
+
+  componentDidMount() {
+    let arr = [] as any[];
+    Object.keys(this.props.values).forEach((key: string, i: number) => {
+      arr.push({ key, value: this.props.values[key] });
+    });
+    this.setState({ values: arr });
+  }
+
+  valuesToObject = () => {
+    let obj = {} as any;
+    this.state.values.forEach((entry: any, i: number) => {
+      obj[entry.key] = entry.value;
+    });
+    return obj;
+  };
+
+  renderInputList = () => {
+    return (
+      <>
+        {this.state.values.map((entry: any, i: number) => {
+          return (
+            <InputWrapper key={i}>
+              <Input
+                placeholder="ex: key"
+                width="270px"
+                value={entry.key}
+                onChange={(e: any) => {
+                  this.state.values[i].key = e.target.value;
+                  this.setState({ values: this.state.values });
+
+                  let obj = this.valuesToObject();
+                  this.props.setValues(obj);
+                }}
+              />
+              <Spacer />
+              <Input
+                placeholder="ex: value"
+                width="270px"
+                value={entry.value}
+                onChange={(e: any) => {
+                  this.state.values[i].value = e.target.value;
+                  this.setState({ values: this.state.values });
+
+                  let obj = this.valuesToObject();
+                  this.props.setValues(obj);
+                }}
+              />
+              <DeleteButton
+                onClick={() => {
+                  this.state.values.splice(i, 1);
+                  this.setState({ values: this.state.values });
+
+                  let obj = this.valuesToObject();
+                  this.props.setValues(obj);
+                }}
+              >
+                <i className="material-icons">cancel</i>
+              </DeleteButton>
+            </InputWrapper>
+          );
+        })}
+      </>
+    );
+  };
+
+  render() {
+    return (
+      <StyledInputArray>
+        <Label>{this.props.label}</Label>
+        {this.state.values.length === 0 ? <></> : this.renderInputList()}
+        <AddRowButton
+          onClick={() => {
+            this.state.values.push({ key: "", value: "" });
+            this.setState({ values: this.state.values });
+          }}
+        >
+          <i className="material-icons">add</i> Add Row
+        </AddRowButton>
+      </StyledInputArray>
+    );
+  }
+}
+
+const Spacer = styled.div`
+  width: 10px;
+  height: 20px;
+`;
+
+const AddRowButton = styled.div`
+  display: flex;
+  align-items: center;
+  margin-top: 5px;
+  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;
+`;
+
+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 Label = styled.div`
+  color: #ffffff;
+  margin-bottom: 10px;
+`;
+
+const StyledInputArray = styled.div`
+  margin-bottom: 15px;
+  margin-top: 22px;
+`;

+ 30 - 0
dashboard/src/components/values-form/ValuesForm.tsx

@@ -13,6 +13,7 @@ import Heading from "./Heading";
 import ExpandableResource from "../ExpandableResource";
 import VeleroForm from "../forms/VeleroForm";
 import InputArray from "./InputArray";
+import KeyValueArray from "./KeyValueArray";
 
 type PropsType = {
   sections?: Section[];
@@ -22,6 +23,7 @@ type PropsType = {
 
 type StateType = any;
 
+// Requires an internal representation unlike other values components because metaState value underdetermines input order
 export default class ValuesForm extends Component<PropsType, StateType> {
   getInputValue = (item: FormElement) => {
     let key = item.name || item.variable;
@@ -70,6 +72,16 @@ export default class ValuesForm extends Component<PropsType, StateType> {
               label={item.label}
             />
           );
+        case "key-value-array":
+          return (
+            <KeyValueArray
+              values={this.props.metaState[key]}
+              setValues={(x: any) => {
+                this.props.setMetaState({ [key]: x });
+              }}
+              label={item.label}
+            />
+          );
         case "array-input":
           return (
             <InputArray
@@ -98,6 +110,24 @@ export default class ValuesForm extends Component<PropsType, StateType> {
               unit={item.settings ? item.settings.unit : null}
             />
           );
+        case "string-input-password":
+          return (
+            <InputRow
+              key={i}
+              isRequired={item.required}
+              type="password"
+              value={this.getInputValue(item)}
+              setValue={(x: string) => {
+                console.log("string input", x);
+                if (item.settings && item.settings.unit && x !== "") {
+                  x = x + item.settings.unit;
+                }
+                this.props.setMetaState({ [key]: x });
+              }}
+              label={item.label}
+              unit={item.settings ? item.settings.unit : null}
+            />
+          );
         case "number-input":
           return (
             <InputRow

+ 5 - 0
dashboard/src/components/values-form/ValuesWrapper.tsx

@@ -55,9 +55,14 @@ export default class ValuesWrapper extends Component<PropsType, StateType> {
               case "string-input":
                 metaState[key] = def ? def : "";
                 break;
+              case "string-input-password":
+                metaState[key] = def ? def : item.settings.default;
               case "array-input":
                 metaState[key] = def ? def : [];
                 break;
+              case "key-value-array":
+                metaState[key] = def ? def : {};
+                break;
               case "number-input":
                 metaState[key] = def.toString() ? def : "";
                 break;

+ 2 - 16
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx

@@ -225,21 +225,7 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
     let values = {};
 
     for (let key in rawValues) {
-      if (key === "ingress.annotations") {
-        let annotations = {} as Record<string, any>;
-
-        if (Array.isArray(rawValues[key])) {
-          rawValues[key].forEach((v: string) => {
-            let splits = v.split(":");
-            annotations[splits[0].trim()] = splits[1].trim();
-          });
-        }
-
-        annotations["porter"] = "true";
-        _.set(values, key, annotations);
-      } else {
-        _.set(values, key, rawValues[key]);
-      }
+      _.set(values, key, rawValues[key]);
     }
 
     // Weave in preexisting values and convert to yaml
@@ -383,7 +369,7 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
     if (this.state.devOpsMode) {
       tabOptions.push(
         { label: "Manifests", value: "list" },
-        { label: "Raw Values", value: "values" }
+        { label: "Helm Values", value: "values" }
       );
     }
 

+ 8 - 0
dashboard/src/main/home/cluster-dashboard/expanded-chart/SettingsSection.tsx

@@ -145,6 +145,10 @@ export default class SettingsSection extends Component<PropsType, StateType> {
     </Helper>
   */
   renderSourceSection = () => {
+    if (!this.props.currentChart.form.hasSource) {
+      return;
+    }
+
     if (this.state.action.git_repo.length > 0) {
       return (
         <>
@@ -231,6 +235,10 @@ export default class SettingsSection extends Component<PropsType, StateType> {
   };
 
   renderWebhookSection = () => {
+    if (!this.props.currentChart.form.hasSource) {
+      return;
+    }
+
     if (true || this.state.webhookToken) {
       let webhookText = `curl -X POST 'https://dashboard.getporter.dev/api/webhooks/deploy/${this.state.webhookToken}?commit=???&repository=???'`;
       return (

+ 2 - 2
dashboard/src/main/home/launch/Launch.tsx

@@ -13,8 +13,8 @@ import hardcodedNames from "./hardcodedNameDict";
 import { Link } from "react-router-dom";
 
 const tabOptions = [
-  { label: "Launch service", value: "docker" },
-  { label: "Community Templates", value: "community" },
+  { label: "New Application", value: "docker" },
+  { label: "Community Add-ons", value: "community" },
 ];
 
 type PropsType = {};

+ 1 - 1
dashboard/src/main/home/launch/expanded-template/ExpandedTemplate.tsx

@@ -129,7 +129,7 @@ const LoadingWrapper = styled.div`
 `;
 
 const StyledExpandedTemplate = styled.div`
-  width: calc(90% - 150px);
+  width: 100%;
   min-width: 300px;
   padding-top: 30px;
 `;

+ 138 - 108
dashboard/src/main/home/launch/expanded-template/LaunchTemplate.tsx

@@ -103,7 +103,7 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
   };
 
   onSubmitAddon = (wildcard?: any) => {
-    let { currentCluster, currentProject } = this.context;
+    let { currentCluster, currentProject, setCurrentError } = this.context;
     let name =
       this.state.templateName || randomWords({ exactly: 3, join: "-" });
     this.setState({ saveValuesStatus: "loading" });
@@ -131,8 +131,6 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
         }
       )
       .then((_) => {
-        console.log("ST");
-        console.log(this.state.sourceType);
         if (this.state.sourceType === "repo") {
           this.createGHAction(name, this.state.selectedNamespace);
         }
@@ -148,6 +146,7 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
       })
       .catch((err) => {
         this.setState({ saveValuesStatus: "error" });
+        setCurrentError(err.response.data.errors[0]);
         posthog.capture("Failed to deploy template", {
           name: this.props.currentTemplate.name,
           namespace: this.state.selectedNamespace,
@@ -166,19 +165,7 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
     // Convert dotted keys to nested objects
     let values = {};
     for (let key in rawValues) {
-      if (key === "ingress.annotations") {
-        let annotations = {} as Record<string, any>;
-        if (Array.isArray(rawValues[key])) {
-          rawValues[key].forEach((v: string) => {
-            let splits = v.split(":");
-            annotations[splits[0].trim()] = splits[1].trim();
-          });
-        }
-        annotations["porter"] = "true";
-        _.set(values, key, annotations);  
-      } else {
-        _.set(values, key, rawValues[key]);
-      }
+      _.set(values, key, rawValues[key]);
     }
 
     let imageUrl = this.state.selectedImageUrl;
@@ -321,7 +308,6 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
     if (this.props.currentTemplate.name !== "docker") {
       this.setState({ saveValuesStatus: "" });
     }
-
     // Retrieve tab options
     let tabOptions = [] as ChoiceType[];
     this.props.form?.tabs.map((tab: any, i: number) => {
@@ -329,8 +315,11 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
         tabOptions.push({ value: tab.name, label: tab.label });
       }
     });
-    console.log(tabOptions);
-    this.setState({ tabOptions, currentTab: tabOptions[0]["value"] });
+
+    this.setState({
+      tabOptions,
+      currentTab: tabOptions[0] && tabOptions[0]["value"],
+    });
 
     // TODO: query with selected filter once implemented
     let { currentProject, currentCluster } = this.context;
@@ -439,65 +428,67 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
 
   // Display if current template uses source (image or repo)
   renderSourceSelectorContent = () => {
-    if (this.props.form?.hasSource) {
-      if (this.state.sourceType === "registry") {
-        return (
-          <>
-            <Subtitle>
-              Select the container image you would like to connect to this
-              template
-              {/* <Highlight onClick={() => this.setState({ sourceType: "repo" })}>
-                link a git repository
-              </Highlight> */}
-              .<Required>*</Required>
-            </Subtitle>
-            <DarkMatter />
-            <ImageSelector
-              selectedTag={this.state.selectedTag}
-              selectedImageUrl={this.state.selectedImageUrl}
-              setSelectedImageUrl={this.setSelectedImageUrl}
-              setSelectedTag={(x: string) => this.setState({ selectedTag: x })}
-              forceExpanded={true}
-            />
-            <br />
-          </>
-        );
-      } else {
-        return (
-          <>
-            <Subtitle>
-              Select a repo to connect to, then a Dockerfile to build from.
-              <Required>*</Required>
-            </Subtitle>
-            <ActionConfEditor
-              actionConfig={this.state.actionConfig}
-              branch={this.state.branch}
-              pathIsSet={this.state.pathIsSet}
-              setActionConfig={(actionConfig: ActionConfigType) =>
-                this.setState({ actionConfig }, () => {
-                  this.setSelectedImageUrl(
-                    this.state.actionConfig.image_repo_uri
-                  );
-                })
-              }
-              setBranch={(branch: string) => this.setState({ branch })}
-              setPath={(pathIsSet: boolean) => this.setState({ pathIsSet })}
-              reset={() => {
-                this.setState({
-                  actionConfig: { ...defaultActionConfig },
-                  branch: "",
-                  pathIsSet: false,
-                });
-              }}
-            />
-            <br />
-          </>
-        );
-      }
+    if (this.state.sourceType === "registry") {
+      return (
+        <>
+          <Subtitle>
+            Select the container image you would like to connect to this
+            template
+            {/* <Highlight onClick={() => this.setState({ sourceType: "repo" })}>
+              link a git repository
+            </Highlight> */}
+            .<Required>*</Required>
+          </Subtitle>
+          <DarkMatter />
+          <ImageSelector
+            selectedTag={this.state.selectedTag}
+            selectedImageUrl={this.state.selectedImageUrl}
+            setSelectedImageUrl={this.setSelectedImageUrl}
+            setSelectedTag={(x: string) => this.setState({ selectedTag: x })}
+            forceExpanded={true}
+          />
+          <br />
+        </>
+      );
+    } else {
+      return (
+        <>
+          <Subtitle>
+            Select a repo to connect to, then a Dockerfile to build from.
+            <Required>*</Required>
+          </Subtitle>
+          <ActionConfEditor
+            actionConfig={this.state.actionConfig}
+            branch={this.state.branch}
+            pathIsSet={this.state.pathIsSet}
+            setActionConfig={(actionConfig: ActionConfigType) =>
+              this.setState({ actionConfig }, () => {
+                this.setSelectedImageUrl(
+                  this.state.actionConfig.image_repo_uri
+                );
+              })
+            }
+            setBranch={(branch: string) => this.setState({ branch })}
+            setPath={(pathIsSet: boolean) => this.setState({ pathIsSet })}
+            reset={() => {
+              this.setState({
+                actionConfig: { ...defaultActionConfig },
+                branch: "",
+                pathIsSet: false,
+              });
+            }}
+          />
+          <br />
+        </>
+      );
     }
   };
 
   renderSourceSelector = () => {
+    if (!this.props.form?.hasSource) {
+      return;
+    }
+
     return (
       <>
         <TabRegion
@@ -522,21 +513,44 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
 
     return (
       <StyledLaunchTemplate>
-        <ClusterSection>
-          {this.props.hideBackButton ? null : (
-            <Flex>
-              <i className="material-icons" onClick={this.props.hideLaunch}>
-                keyboard_backspace
-              </i>
-            </Flex>
-          )}
-          <Template>
+        {name !== "docker" && (
+          <HeaderSection>
+            <i className="material-icons" onClick={this.props.hideLaunch}>
+              keyboard_backspace
+            </i>
             {icon
               ? this.renderIcon(icon)
               : this.renderIcon(currentTemplate.icon)}
-            {name}
-          </Template>
-          <i className="material-icons">arrow_right_alt</i>
+            <Title>{name}</Title>
+          </HeaderSection>
+        )}
+        <DarkMatter antiHeight="-13px" />
+        <Heading isAtTop={name !== "docker"}>Name</Heading>
+        <Subtitle>
+          Randomly generated if left blank.
+          <Warning
+            highlight={
+              !isAlphanumeric(this.state.templateName) &&
+              this.state.templateName !== ""
+            }
+          >
+            Lowercase letters, numbers, and "-" only.
+          </Warning>
+        </Subtitle>
+        <DarkMatter antiHeight="-29px" />
+        <InputRow
+          type="text"
+          value={this.state.templateName}
+          setValue={(x: string) => this.setState({ templateName: x })}
+          placeholder="ex: doctor-scientist"
+          width="100%"
+        />
+        <Heading>Destination</Heading>
+        <Subtitle>
+          Specify the cluster and namespace you would like to deploy your
+          application to.
+        </Subtitle>
+        <ClusterSection>
           <ClusterLabel>
             <i className="material-icons">device_hub</i>Cluster
           </ClusterLabel>
@@ -568,26 +582,6 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
             closeOverlay={true}
           />
         </ClusterSection>
-        <Subtitle>
-          Template name
-          <Warning
-            highlight={
-              !isAlphanumeric(this.state.templateName) &&
-              this.state.templateName !== ""
-            }
-          >
-            (lowercase letters, numbers, and "-" only)
-          </Warning>
-          . (Optional)
-        </Subtitle>
-        <DarkMatter antiHeight="-27px" />
-        <InputRow
-          type="text"
-          value={this.state.templateName}
-          setValue={(x: string) => this.setState({ templateName: x })}
-          placeholder="ex: doctor-scientist"
-          width="100%"
-        />
         {this.renderSourceSelector()}
         {this.renderSettingsRegion()}
       </StyledLaunchTemplate>
@@ -597,6 +591,41 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
 
 LaunchTemplate.contextType = Context;
 
+const Title = styled.div`
+  font-size: 24px;
+  font-weight: 600;
+  font-family: "Work Sans", sans-serif;
+  margin-left: 10px;
+  border-radius: 2px;
+  color: #ffffff;
+`;
+
+const HeaderSection = styled.div`
+  display: flex;
+  align-items: center;
+
+  > i {
+    cursor: pointer;
+    font-size 24px;
+    color: #969Fbbaa;
+    padding: 3px;
+    border-radius: 100px;
+    :hover {
+      background: #ffffff11;
+    }
+  }
+`;
+
+const Heading = styled.div<{ isAtTop?: boolean }>`
+  color: white;
+  font-weight: 500;
+  font-size: 16px;
+  margin-bottom: 5px;
+  margin-top: ${(props) => (props.isAtTop ? "30px" : "10px")};
+  display: flex;
+  align-items: center;
+`;
+
 const Warning = styled.span<{ highlight: boolean; makeFlush?: boolean }>`
   color: ${(props) => (props.highlight ? "#f5cb42" : "")};
   margin-left: ${(props) => (props.makeFlush ? "" : "5px")};
@@ -637,7 +666,7 @@ const DarkMatter = styled.div<{ antiHeight?: string }>`
 `;
 
 const Subtitle = styled.div`
-  padding: 11px 0px 20px;
+  padding: 11px 0px 16px;
   font-family: "Work Sans", sans-serif;
   font-size: 13px;
   color: #aaaabb;
@@ -694,8 +723,9 @@ const ClusterSection = styled.div`
   color: #ffffff;
   font-family: "Work Sans", sans-serif;
   font-size: 14px;
+  margin-top: 2px;
   font-weight: 500;
-  margin-bottom: 15px;
+  margin-bottom: 22px;
 
   > i {
     font-size: 25px;

+ 1 - 1
dashboard/src/main/home/launch/expanded-template/TemplateInfo.tsx

@@ -301,7 +301,7 @@ const TitleSection = styled.div`
   flex-direction: row;
   height: 40px;
   justify-content: space-between;
-  width: calc(100% + 42px);
+  width: 100%;
   align-items: center;
 `;
 

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

@@ -82,6 +82,7 @@ export interface FormYAML {
   name?: string;
   icon?: string;
   description?: string;
+  hasSource?: string;
   tags?: string[];
   tabs?: {
     name: string;

+ 1 - 1
internal/config/config.go

@@ -22,7 +22,7 @@ type ServerConf struct {
 	Port                 int           `env:"SERVER_PORT,default=8080"`
 	StaticFilePath       string        `env:"STATIC_FILE_PATH,default=/porter/static"`
 	CookieName           string        `env:"COOKIE_NAME,default=porter"`
-	CookieSecrets        []string      `env:"COOKIE_SECRETS,default=hashkey;blockkey"`
+	CookieSecrets        []string      `env:"COOKIE_SECRETS,default=random_hash_key_;random_block_key"`
 	TokenGeneratorSecret string        `env:"TOKEN_GENERATOR_SECRET,default=secret"`
 	TimeoutRead          time.Duration `env:"SERVER_TIMEOUT_READ,default=5s"`
 	TimeoutWrite         time.Duration `env:"SERVER_TIMEOUT_WRITE,default=10s"`

+ 0 - 4
internal/forms/release.go

@@ -1,7 +1,6 @@
 package forms
 
 import (
-	"fmt"
 	"net/url"
 	"strconv"
 
@@ -32,17 +31,14 @@ func (rf *ReleaseForm) PopulateHelmOptionsFromQueryParams(
 		if err != nil {
 			return err
 		}
-		fmt.Println("setting cluster")
 		rf.Cluster = cluster
 	}
 
 	if namespace, ok := vals["namespace"]; ok && len(namespace) == 1 {
-		fmt.Println("setting namespace")
 		rf.Namespace = namespace[0]
 	}
 
 	if storage, ok := vals["storage"]; ok && len(storage) == 1 {
-		fmt.Println("setting storage")
 		rf.Storage = storage[0]
 	}
 

+ 0 - 2
internal/kubernetes/agent.go

@@ -146,7 +146,6 @@ func (a *Agent) GetPodLogs(namespace string, name string, conn *websocket.Conn)
 			if _, _, err := conn.ReadMessage(); err != nil {
 				defer conn.Close()
 				errorchan <- nil
-				fmt.Println("Successfully closed log stream")
 				return
 			}
 		}
@@ -230,7 +229,6 @@ func (a *Agent) StreamControllerStatus(conn *websocket.Conn, kind string) error
 			if _, _, err := conn.ReadMessage(); err != nil {
 				defer conn.Close()
 				defer close(stopper)
-				defer fmt.Println("Successfully closed controller status stream")
 				errorchan <- nil
 				return
 			}

+ 0 - 4
internal/kubernetes/provisioner/global_stream.go

@@ -85,8 +85,6 @@ func GlobalStreamListener(
 	repo repository.Repository,
 	errorChan chan error,
 ) {
-	fmt.Println("starting global stream listener")
-
 	for {
 		xstreams, err := client.XReadGroup(
 			context.Background(),
@@ -98,8 +96,6 @@ func GlobalStreamListener(
 			},
 		).Result()
 
-		fmt.Println(xstreams, err)
-
 		if err != nil {
 			errorChan <- err
 			return

+ 0 - 2
internal/kubernetes/provisioner/resource_stream.go

@@ -2,7 +2,6 @@ package provisioner
 
 import (
 	"context"
-	"fmt"
 
 	redis "github.com/go-redis/redis/v8"
 	"github.com/gorilla/websocket"
@@ -39,7 +38,6 @@ func ResourceStream(client *redis.Client, streamName string, conn *websocket.Con
 			).Result()
 
 			if err != nil {
-				fmt.Println("ERROR XREAD", err)
 				return
 			}
 

+ 0 - 4
internal/registry/registry.go

@@ -317,8 +317,6 @@ func (r *Registry) listPrivateRegistryRepositories(
 			return nil, err
 		}
 
-		fmt.Println("AUTH IS", string(basic.Username), string(basic.Password))
-
 		req.SetBasicAuth(string(basic.Username), string(basic.Password))
 
 		resp, err = client.Do(req)
@@ -330,8 +328,6 @@ func (r *Registry) listPrivateRegistryRepositories(
 
 	gcrResp := gcrRepositoryResp{}
 
-	fmt.Println("STATUS IS", resp.Status)
-
 	if err := json.NewDecoder(resp.Body).Decode(&gcrResp); err != nil {
 		return nil, fmt.Errorf("Could not read private registry repositories: %v", err)
 	}

+ 0 - 3
server/api/cluster_handler.go

@@ -2,7 +2,6 @@ package api
 
 import (
 	"encoding/json"
-	"fmt"
 	"net/http"
 	"strconv"
 
@@ -58,8 +57,6 @@ func (app *App) HandleCreateProjectCluster(w http.ResponseWriter, r *http.Reques
 
 	clusterExt := cluster.Externalize()
 
-	fmt.Println("CLUSTER EXTERNAL PROJECT ID", clusterExt.ProjectID, cluster.ProjectID)
-
 	if err := json.NewEncoder(w).Encode(clusterExt); err != nil {
 		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
 		return

+ 0 - 2
server/api/git_repo_handler.go

@@ -134,7 +134,6 @@ func (app *App) HandleGetBranches(w http.ResponseWriter, r *http.Request) {
 	// List all branches for a specified repo
 	branches, _, err := client.Repositories.ListBranches(context.Background(), owner, name, nil)
 	if err != nil {
-		fmt.Println(err)
 		return
 	}
 
@@ -185,7 +184,6 @@ func (app *App) HandleGetBranchContents(w http.ResponseWriter, r *http.Request)
 
 	// Ret2: recursively traverse all dirs to create config bundle (case on type == dir)
 	// https://api.github.com/repos/porter-dev/porter/contents?ref=frontend-graph
-	// fmt.Println(res)
 	json.NewEncoder(w).Encode(res)
 }
 

+ 0 - 5
server/api/integration_handler_test.go

@@ -2,7 +2,6 @@ package api_test
 
 import (
 	"encoding/json"
-	"fmt"
 	"net/http"
 	"strings"
 	"testing"
@@ -269,8 +268,6 @@ func publicIntBodyValidator(c *publicIntTest, tester *tester, t *testing.T) {
 
 	bytes := tester.rr.Body.Bytes()
 
-	fmt.Println(string(bytes))
-
 	json.Unmarshal(bytes, &gotBody)
 	json.Unmarshal([]byte(c.expBody), &expBody)
 
@@ -312,8 +309,6 @@ func basicIntBodyValidator(c *publicIntTest, tester *tester, t *testing.T) {
 
 	bytes := tester.rr.Body.Bytes()
 
-	fmt.Println(string(bytes))
-
 	json.Unmarshal(bytes, &gotBody)
 	json.Unmarshal([]byte(c.expBody), &expBody)
 

+ 0 - 28
server/api/registry_handler.go

@@ -486,32 +486,4 @@ func (app *App) HandleListImages(w http.ResponseWriter, r *http.Request) {
 		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
 		return
 	}
-
-	// ref, err := name.ParseReference("gcr.io/google-containers/pause")
-	// if err != nil {
-	// 	fmt.Println(err)
-	// 	return
-	// }
-
-	// img, err := remote.Image(ref)
-	// if err != nil {
-	// 	fmt.Println(err)
-	// 	return
-	// }
-	// fmt.Println(img.Size())
-
-	// ctx := r.Context()
-	// reg, err := name.NewRegistry("index.docker.io")
-	// if err != nil {
-	// 	fmt.Println("fuk")
-	// 	fmt.Println(err)
-	// 	return
-	// }
-
-	// stuff, err := remote.Catalog(ctx, reg, remote.WithAuthFromKeychain(authn.DefaultKeychain))
-	// if err != nil {
-	// 	fmt.Println(err)
-	// 	return
-	// }
-	// fmt.Println(stuff[0])
 }

+ 0 - 3
server/api/release_handler_test.go

@@ -2,7 +2,6 @@ package api_test
 
 import (
 	"encoding/json"
-	"fmt"
 	"net/http"
 	"net/http/httptest"
 	"net/url"
@@ -362,8 +361,6 @@ var rollbackReleaseTests = []*releaseTest{
 
 				expBodyJSON := releaseStubToReleaseJSON(releaseStub{"wordpress", "default", 3, "1.0.1", release.StatusDeployed})
 
-				fmt.Println(rr2.Body.String())
-
 				json.Unmarshal(rr2.Body.Bytes(), gotBody)
 				json.Unmarshal([]byte(expBodyJSON), expBody)
 

+ 1 - 3
server/router/middleware/auth.go

@@ -670,8 +670,6 @@ func (auth *Auth) isLoggedIn(w http.ResponseWriter, r *http.Request) bool {
 
 	tok := auth.getTokenFromRequest(r)
 
-	fmt.Println("CHECKED TOKEN FROM REQUEST", tok)
-
 	if tok != nil {
 		return true
 	}
@@ -680,7 +678,7 @@ func (auth *Auth) isLoggedIn(w http.ResponseWriter, r *http.Request) bool {
 	if err != nil {
 		session.Values["authenticated"] = false
 		if err := session.Save(r, w); err != nil {
-			fmt.Println("error while saving session in isLoggedIn", err)
+			return false
 		}
 		return false
 	}