jusrhee 5 лет назад
Родитель
Сommit
d11f80da49

+ 10 - 0
dashboard/package-lock.json

@@ -401,6 +401,11 @@
         "jest-diff": "^24.3.0"
       }
     },
+    "@types/js-base64": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/@types/js-base64/-/js-base64-3.0.0.tgz",
+      "integrity": "sha512-BnEyOcDE4H6bkg8m84xhdbkYoAoCg8sYERmAvE4Ff50U8jTfbmOinRdJpauBn1P9XsCCQgCLuSiyz3PM4WHYOA=="
+    },
     "@types/js-yaml": {
       "version": "3.12.5",
       "resolved": "https://registry.npmjs.org/@types/js-yaml/-/js-yaml-3.12.5.tgz",
@@ -4183,6 +4188,11 @@
       "resolved": "https://registry.npmjs.org/join-component/-/join-component-1.1.0.tgz",
       "integrity": "sha1-uEF7dQZho5K+4sJTfGiyqdSXfNU="
     },
+    "js-base64": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.6.0.tgz",
+      "integrity": "sha512-wVdUBYQeY2gY73RIlPrysvpYx+2vheGo8Y1SNQv/BzHToWpAZzJU7Z6uheKMAe+GLSBig5/Ps2nxg/8tRB73xg=="
+    },
     "js-tokens": {
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz",

+ 2 - 0
dashboard/package.json

@@ -4,12 +4,14 @@
   "private": true,
   "dependencies": {
     "@fullstory/browser": "^1.4.5",
+    "@types/js-base64": "^3.0.0",
     "@types/js-yaml": "^3.12.5",
     "@types/markdown-to-jsx": "^6.11.3",
     "@types/qs": "^6.9.5",
     "ace-builds": "^1.4.12",
     "axios": "^0.20.0",
     "dotenv": "^8.2.0",
+    "js-base64": "^3.6.0",
     "js-yaml": "^3.14.0",
     "markdown-to-jsx": "^7.0.1",
     "posthog-node": "^1.0.6",

+ 22 - 20
dashboard/src/components/values-form/ValuesForm.tsx

@@ -11,8 +11,9 @@ import InputRow from './InputRow';
 import SelectRow from './SelectRow';
 
 type PropsType = {
-  sections?: Section[],
   onSubmit: (formValues: any) => void,
+  sections?: Section[],
+  disabled?: boolean,
 };
 
 type StateType = any;
@@ -22,13 +23,13 @@ export default class ValuesForm extends Component<PropsType, StateType> {
   updateFormState() {
     let formState: any = {};
     this.props.sections.forEach((section: Section, i: number) => {
-      section.Contents.forEach((item: FormElement, i: number) => {
+      section.contents.forEach((item: FormElement, i: number) => {
 
         // If no name is assigned use values.yaml variable as identifier
-        let key = item.Name || item.Variable;
+        let key = item.name || item.variable;
         
-        let def = item.Settings.Default;
-        switch (item.Type) {
+        let def = item.settings && item.settings.default;
+        switch (item.type) {
           case 'checkbox':
             formState[key] = def ? def : false;
             break;
@@ -39,7 +40,7 @@ export default class ValuesForm extends Component<PropsType, StateType> {
             formState[key] = def.toString() ? def : '';
             break;
           case 'select':
-            formState[key] = def ? def : item.Settings.Options[0].Value;
+            formState[key] = def ? def : item.settings.options[0].value;
           default:
         }
       });
@@ -59,22 +60,22 @@ export default class ValuesForm extends Component<PropsType, StateType> {
   }
 
   renderSection = (section: Section) => {
-    return section.Contents.map((item: FormElement, i: number) => {
+    return section.contents.map((item: FormElement, i: number) => {
 
       // If no name is assigned use values.yaml variable as identifier
-      let key = item.Name || item.Variable;
-      switch (item.Type) {
+      let key = item.name || item.variable;
+      switch (item.type) {
         case 'heading':
-          return <Heading key={i}>{item.Label}</Heading>
+          return <Heading key={i}>{item.label}</Heading>
         case 'subtitle':
-          return <Helper key={i}>{item.Label}</Helper>
+          return <Helper key={i}>{item.label}</Helper>
         case 'checkbox':
           return (
             <CheckboxRow
               key={i}
               checked={this.state[key]}
               toggle={() => this.setState({ [key]: !this.state[key] })}
-              label={item.Label}
+              label={item.label}
             />
           );
         case 'string-input':
@@ -84,8 +85,8 @@ export default class ValuesForm extends Component<PropsType, StateType> {
               type='text'
               value={this.state[key]}
               setValue={(x: string) => this.setState({ [key]: x })}
-              label={item.Label}
-              unit={item.Settings ? item.Settings.Unit : null}
+              label={item.label}
+              unit={item.settings ? item.settings.unit : null}
             />
           );
         case 'number-input':
@@ -95,8 +96,8 @@ export default class ValuesForm extends Component<PropsType, StateType> {
               type='number'
               value={this.state[key]}
               setValue={(x: number) => this.setState({ [key]: x })}
-              label={item.Label}
-              unit={item.Settings ? item.Settings.Unit : null}
+              label={item.label}
+              unit={item.settings ? item.settings.unit : null}
             />
           );
         case 'select':
@@ -105,9 +106,9 @@ export default class ValuesForm extends Component<PropsType, StateType> {
               key={i}
               value={this.state[key]}
               setActiveValue={(val) => this.setState({ [key]: val })}
-              options={item.Settings.Options}
+              options={item.settings.options}
               dropdownLabel=''
-              label={item.Label}
+              label={item.label}
             />
           );
         default:
@@ -120,8 +121,8 @@ export default class ValuesForm extends Component<PropsType, StateType> {
       return this.props.sections.map((section: Section, i: number) => {
 
         // Hide collapsible section if deciding field is false
-        if (section.ShowIf) {
-          if (!this.state[section.ShowIf]) {
+        if (section.show_if) {
+          if (!this.state[section.show_if]) {
             return null;
           }
         }
@@ -143,6 +144,7 @@ export default class ValuesForm extends Component<PropsType, StateType> {
           {this.renderFormContents()}
         </StyledValuesForm>
         <SaveButton
+          disabled={this.props.disabled}
           text='Deploy'
           onClick={() => this.props.onSubmit(this.state)}
           status={null}

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

@@ -1,5 +1,7 @@
 import React, { Component } from 'react';
 import styled from 'styled-components';
+import yaml from 'js-yaml';
+import { Base64 } from 'js-base64';
 import close from '../../../../assets/close.png';
 
 import { ResourceType, ChartType, StorageType, ChoiceType } from '../../../../shared/types';
@@ -84,25 +86,39 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
     });
   }
 
+  getFormData = (): any => {
+    let { files } = this.props.currentChart.chart;
+    for (const file of files) { 
+      if (file.name === 'form.yaml') {
+        let formData = yaml.load(Base64.decode(file.data));
+        return formData;
+      }
+    };
+    return null;
+  }
+
   refreshTabs = () => {
-    
-    // Generate settings tabs from the provided form
+    let formData = this.getFormData();
     let tabOptions = [] as ChoiceType[];
     let tabContents = [] as any;
-    dummyFormTabs.map((tab: any, i: number) => {
-      tabOptions.push({ value: '@' + tab.Name, label: tab.Label });
-      tabContents.push({
-        value: '@' + tab.Name,
-        component: (
-          <ValuesFormWrapper>
-            <ValuesForm 
-              sections={tab.Sections} 
-              onSubmit={(x: any) => console.log(x)}
-            />
-          </ValuesFormWrapper>
-        ),
+
+    // Generate form tabs if form.yaml exists
+    if (formData && formData.tabs) {
+      formData.tabs.map((tab: any, i: number) => {
+        tabOptions.push({ value: '@' + tab.name, label: tab.label });
+        tabContents.push({
+          value: '@' + tab.name,
+          component: (
+            <ValuesFormWrapper>
+              <ValuesForm 
+                sections={tab.sections} 
+                onSubmit={(x: any) => console.log(x)}
+              />
+            </ValuesFormWrapper>
+          ),
+        });
       });
-    });
+    }
 
     // Append universal tabs
     tabOptions.push(
@@ -180,7 +196,6 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
   }
 
   setRevisionPreview = (oldChart: ChartType) => {
-    console.log('set it..')
     let { currentCluster, currentProject } = this.context;
     this.setState({ revisionPreview: oldChart, checkTabExists: true });
 

+ 4 - 4
dashboard/src/main/home/templates/Templates.tsx

@@ -75,15 +75,15 @@ export default class Templates extends Component<PropsType, StateType> {
     }
 
     return this.state.porterCharts.map((template: PorterChart, i: number) => {
-      let { Name, Icon, Description } = template.Form;
+      let { name, icon, description } = template.form;
       return (
         <TemplateBlock key={i} onClick={() => this.setState({ currentTemplate: template })}>
-          {Icon ? this.renderIcon(Icon) : this.renderIcon(template.Icon)}
+          {icon ? this.renderIcon(icon) : this.renderIcon(template.icon)}
           <TemplateTitle>
-            {Name ? Name : template.Name}
+            {name ? name : template.name}
           </TemplateTitle>
           <TemplateDescription>
-            {Description ? Description : template.Description}
+            {description ? description : template.description}
           </TemplateDescription>
         </TemplateBlock>
       )

+ 10 - 9
dashboard/src/main/home/templates/expanded-template/LaunchTemplate.tsx

@@ -40,9 +40,9 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
     let { currentCluster, currentProject } = this.context;
     console.log(formValues);
     api.deployTemplate('<token>', {
-      templateName: this.props.currentTemplate.Name,
+      templateName: this.props.currentTemplate.name,
       clusterID: currentCluster.id,
-      imageURL: this.state.selectedImageUrl,
+      imageURL: "index.docker.io/bitnami/redis",
       formValues,
     }, {
       id: currentProject.id,
@@ -60,14 +60,15 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
     // Generate settings tabs from the provided form
     let tabOptions = [] as ChoiceType[];
     let tabContents = [] as any;
-    this.props.currentTemplate.Form.Tabs.map((tab: any, i: number) => {
-      tabOptions.push({ value: tab.Name, label: tab.Label });
+    this.props.currentTemplate.form.tabs.map((tab: any, i: number) => {
+      tabOptions.push({ value: tab.name, label: tab.label });
       tabContents.push({
-        value: tab.Name, component: (
+        value: tab.name, component: (
           <ValuesFormWrapper>
             <ValuesForm 
-              sections={tab.Sections} 
+              sections={tab.sections} 
               onSubmit={this.onSubmit}
+              disabled={this.state.selectedImageUrl === ''}
             />
           </ValuesFormWrapper>
         ),
@@ -100,9 +101,9 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
   }
 
   render() {
-    let { Name, Icon, Description } = this.props.currentTemplate.Form;
+    let { name, icon, description } = this.props.currentTemplate.form;
     let { currentTemplate } = this.props;
-    let name = Name ? Name : currentTemplate.Name;
+    name = name ? name : currentTemplate.name;
 
     return (
       <StyledLaunchTemplate>
@@ -116,7 +117,7 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
         </TitleSection>
         <ClusterSection>
           <Template>
-            {Icon ? this.renderIcon(Icon) : this.renderIcon(currentTemplate.Icon)}
+            {icon ? this.renderIcon(icon) : this.renderIcon(currentTemplate.icon)}
             {name}
           </Template>
           <i className="material-icons">arrow_right_alt</i>

+ 10 - 10
dashboard/src/main/home/templates/expanded-template/TemplateInfo.tsx

@@ -28,7 +28,7 @@ export default class TemplateInfo extends Component<PropsType, StateType> {
   }
 
   renderTagList = () => {
-    return this.props.currentTemplate.Form.Tags.map((tag: string, i: number) => {
+    return this.props.currentTemplate.form.tags.map((tag: string, i: number) => {
       return (
         <Tag key={i}>{tag}</Tag>
       )
@@ -37,19 +37,19 @@ export default class TemplateInfo extends Component<PropsType, StateType> {
 
   renderMarkdown = () => {
     let { currentTemplate } = this.props;
-    if (currentTemplate.Markdown) {
+    if (currentTemplate.markdown) {
       return (
-        <Markdown>{currentTemplate.Markdown}</Markdown>
+        <Markdown>{currentTemplate.markdown}</Markdown>
       );
-    } else if (currentTemplate.Form.Description) {
-      return currentTemplate.Form.Description;
+    } else if (currentTemplate.form.description) {
+      return currentTemplate.form.description;
     }
 
-    return currentTemplate.Description;
+    return currentTemplate.description;
   }
 
   renderTagSection = () => {
-    if (this.props.currentTemplate.Form.Tags) {
+    if (this.props.currentTemplate.form.tags) {
       return (
         <TagSection>
           <i className="material-icons">local_offer</i>
@@ -61,9 +61,9 @@ export default class TemplateInfo extends Component<PropsType, StateType> {
 
   render() {
     let { currentCluster } = this.context;
-    let { Name, Icon } = this.props.currentTemplate.Form;
+    let { name, icon } = this.props.currentTemplate.form;
     let { currentTemplate } = this.props;
-    let name = Name ? Name : currentTemplate.Name;
+    name = name ? name : currentTemplate.name;
     return (
       <StyledExpandedTemplate>
         <TitleSection>
@@ -71,7 +71,7 @@ export default class TemplateInfo extends Component<PropsType, StateType> {
             <i className="material-icons" onClick={() => this.props.setCurrentTemplate(null)}>
               keyboard_backspace
             </i>
-            {Icon ? this.renderIcon(Icon) : this.renderIcon(currentTemplate.Icon)}
+            {icon ? this.renderIcon(icon) : this.renderIcon(currentTemplate.icon)}
             <Title>{name}</Title>
           </Flex>
           <Button

+ 6 - 0
dashboard/src/shared/common.tsx

@@ -9,4 +9,10 @@ export const getRegistryIcon = (kind: string) => {
     default:
       return null
   }
+}
+
+export const getIgnoreCase = (object: any, key: string) => {
+  return object[Object.keys(object)
+    .find(k => k.toLowerCase() === key.toLowerCase())
+  ];
 }

+ 28 - 24
dashboard/src/shared/types.tsx

@@ -23,6 +23,10 @@ export interface ChartType {
       icon: string,
       apiVersion: string
     },
+    files?: {
+      data: string,
+      name: string,
+    }[],
   },
   config: string,
   version: number,
@@ -64,42 +68,42 @@ export enum StorageType {
 
 // PorterChart represents a bundled Porter template
 export interface PorterChart {
-	Name: string,
-	Description: string,
-	Icon: string,
-  Form: FormYAML,
-  Markdown?: string,
+	name: string,
+	description: string,
+	icon: string,
+  form: FormYAML,
+  markdown?: string,
 }
 
 // FormYAML represents a chart's values.yaml form abstraction
 export interface FormYAML {
-	Name?: string,  
-	Icon?: string,   
-	Description?: string,   
-  Tags?: string[],
-  Tabs?: {
-    Name: string,
-    Label: string,
-    Sections?: Section[]
+	name?: string,  
+	icon?: string,   
+	description?: string,   
+  tags?: string[],
+  tabs?: {
+    name: string,
+    label: string,
+    sections?: Section[]
   }[]
 }
 
 export interface Section {
-  Name?: string,
-  ShowIf?: string,
-  Contents: FormElement[]
+  name?: string,
+  show_if?: string,
+  contents: FormElement[]
 }
 
 // FormElement represents a form element
 export interface FormElement {
-  Type: string,
-  Label: string,
-  Name?: string,
-  Variable?: string,
-  Settings?: {
-    Default?: number | string | boolean,
-    Options?: any[],
-    Unit?: string
+  type: string,
+  label: string,
+  name?: string,
+  variable?: string,
+  settings?: {
+    default?: number | string | boolean,
+    options?: any[],
+    unit?: string
   }
 }
 

+ 24 - 23
internal/models/templates.go

@@ -23,34 +23,35 @@ type ChartYAML []struct {
 
 // PorterChart represents a bundled Porter template
 type PorterChart struct {
-	Name        string
-	Description string
-	Icon        string
-	Form        FormYAML
-	Markdown    string
+	Name        string   `json:"name"`
+	Description string   `json:"description"`
+	Icon        string   `json:"icon"`
+	Form        FormYAML `json:"form"`
+	Markdown    string   `json:"markdown"`
 }
 
 // FormYAML represents a chart's values.yaml form abstraction
 type FormYAML struct {
-	Name        string   `yaml:"name"`
-	Icon        string   `yaml:"icon"`
-	Description string   `yaml:"description"`
-	Tags        []string `yaml:"tags"`
+	Name        string   `yaml:"name" json:"name"`
+	Icon        string   `yaml:"icon" json:"icon"`
+	Description string   `yaml:"description" json:"description"`
+	Tags        []string `yaml:"tags" json:"tags"`
 	Tabs        []struct {
-		Name     string `yaml:"name"`
-		Label    string `yaml:"label"`
+		Name     string `yaml:"name" json:"name"`
+		Label    string `yaml:"label" json:"label"`
 		Sections []struct {
-			Name     string `yaml:"name"`
-			ShowIf   string `yaml:"show_if"`
+			Name     string `yaml:"name" json:"name"`
+			ShowIf   string `yaml:"show_if" json:"show_if"`
 			Contents []struct {
-				Type     string `yaml:"type"`
-				Label    string `yaml:"label"`
-				Name     string `yaml:"name,omitempty"`
-				Variable string `yaml:"variable,omitempty"`
+				Type     string `yaml:"type" json:"type"`
+				Label    string `yaml:"label" json:"label"`
+				Name     string `yaml:"name,omitempty" json:"name,omitempty"`
+				Variable string `yaml:"variable,omitempty" json:"variable,omitempty"`
 				Settings struct {
-					Default interface{}
-				} `yaml:"settings,omitempty"`
-			} `yaml:"contents"`
-		} `yaml:"sections"`
-	} `yaml:"tabs"`
-}
+					Default interface{} `yaml:"default,omitempty" json:"default,omitempty"`
+					Unit    interface{} `yaml:"unit,omitempty" json:"unit,omitempty"`
+				} `yaml:"settings,omitempty" json:"settings,omitempty"`
+			} `yaml:"contents" json:"contents,omitempty"`
+		} `yaml:"sections" json:"sections,omitempty"`
+	} `yaml:"tabs" json:"tabs,omitempty"`
+}

+ 3 - 0
server/api/deploy_handler.go

@@ -49,6 +49,9 @@ func (app *App) HandleDeployTemplate(w http.ResponseWriter, r *http.Request) {
 		return
 	}
 
+	// Set image URL
+	(*defaultValues)["image"].(map[interface{}]interface{})["repository"] = params.ImageURL
+
 	// Loop through form params to override
 	for k := range params.FormValues {
 		switch v := interface{}(k).(type) {