Browse Source

values.yaml generation

jusrhee 5 years ago
parent
commit
f06f8a9c3f

+ 10 - 4
dashboard/src/components/values-form/InputRow.tsx

@@ -5,7 +5,7 @@ type PropsType = {
   label?: string,
   type: string,
   value: string | number,
-  setValue: (x: string) => void,
+  setValue: (x: string | number) => void,
   unit?: string
   placeholder?: string
   width?: string
@@ -16,6 +16,14 @@ type StateType = {
 };
 
 export default class InputRow extends Component<PropsType, StateType> {
+  handleChange = (e: ChangeEvent<HTMLInputElement>) => {
+    if (this.props.type === 'number') {
+      this.props.setValue(parseInt(e.target.value));
+    } else {
+      this.props.setValue(e.target.value);
+    }
+  }
+
   render() {
     let { label, value, type, unit, placeholder, width } = this.props;
     return (
@@ -28,9 +36,7 @@ export default class InputRow extends Component<PropsType, StateType> {
             width={width}
             type={type}
             value={value || ''}
-            onChange={(e: ChangeEvent<HTMLInputElement>) =>
-              this.props.setValue(e.target.value)
-            }
+            onChange={this.handleChange}
           />
           <Unit>{unit}</Unit>
         </InputWrapper>

+ 5 - 19
dashboard/src/components/values-form/ValuesForm.tsx

@@ -11,7 +11,8 @@ import InputRow from './InputRow';
 import SelectRow from './SelectRow';
 
 type PropsType = {
-  sections?: Section[]
+  sections?: Section[],
+  onSubmit: (formValues: any) => void,
 };
 
 type StateType = any;
@@ -35,7 +36,7 @@ export default class ValuesForm extends Component<PropsType, StateType> {
             formState[key] = def ? def : '';
             break;
           case 'number-input':
-            formState[key] = def.toString() ? def.toString() : '';
+            formState[key] = def.toString() ? def : '';
             break;
           case 'select':
             formState[key] = def ? def : item.Settings.Options[0].Value;
@@ -57,21 +58,6 @@ export default class ValuesForm extends Component<PropsType, StateType> {
     }
   }
 
-  handleDeploy = () => {
-    console.log(this.state);
-    let { currentProject } = this.context;
-
-    api.deployTemplate('<token>', {}, {
-      id: currentProject.id,
-    }, (err: any, res: any) => {
-      if (err) {
-        // console.log(err)
-      } else {
-        // console.log(res.data)
-      }
-    });
-  }
-
   renderSection = (section: Section) => {
     return section.Contents.map((item: FormElement, i: number) => {
 
@@ -108,7 +94,7 @@ export default class ValuesForm extends Component<PropsType, StateType> {
               key={i}
               type='number'
               value={this.state[key]}
-              setValue={(x: string) => this.setState({ [key]: x })}
+              setValue={(x: number) => this.setState({ [key]: x })}
               label={item.Label}
               unit={item.Settings ? item.Settings.Unit : null}
             />
@@ -158,7 +144,7 @@ export default class ValuesForm extends Component<PropsType, StateType> {
         </StyledValuesForm>
         <SaveButton
           text='Deploy'
-          onClick={this.handleDeploy}
+          onClick={() => this.props.onSubmit(this.state)}
           status={null}
           makeFlush={true}
         />

+ 4 - 1
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx

@@ -95,7 +95,10 @@ export default class ExpandedChart extends Component<PropsType, StateType> {
         value: '@' + tab.Name,
         component: (
           <ValuesFormWrapper>
-            <ValuesForm sections={tab.Sections} />
+            <ValuesForm 
+              sections={tab.Sections} 
+              onSubmit={(x: any) => console.log(x)}
+            />
           </ValuesFormWrapper>
         ),
       });

+ 25 - 1
dashboard/src/main/home/templates/expanded-template/LaunchTemplate.tsx

@@ -34,6 +34,27 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
     tabContents: [] as any,
   };
 
+  onSubmit = (formValues: any) => {
+    console.log(formValues);
+
+    let { currentCluster, currentProject } = this.context;
+    console.log(formValues);
+    api.deployTemplate('<token>', {
+      templateName: this.props.currentTemplate.Name,
+      clusterID: currentCluster.id,
+      imageURL: this.state.selectedImageUrl,
+      formValues,
+    }, {
+      id: currentProject.id,
+    }, (err: any, res: any) => {
+      if (err) {
+        // console.log(err)
+      } else {
+        // console.log(res.data)
+      }
+    });
+  }
+
   componentDidMount() {
 
     // Generate settings tabs from the provided form
@@ -44,7 +65,10 @@ export default class LaunchTemplate extends Component<PropsType, StateType> {
       tabContents.push({
         value: tab.Name, component: (
           <ValuesFormWrapper>
-            <ValuesForm sections={tab.Sections} />
+            <ValuesForm 
+              sections={tab.Sections} 
+              onSubmit={this.onSubmit}
+            />
           </ValuesFormWrapper>
         ),
       });

+ 6 - 1
dashboard/src/shared/api.tsx

@@ -153,7 +153,12 @@ const deleteProject = baseApi<{}, { id: number }>('DELETE', pathParams => {
   return `/api/projects/${pathParams.id}`;
 });
 
-const deployTemplate = baseApi<{}, { id: number }>('POST', pathParams => {
+const deployTemplate = baseApi<{
+  templateName: string,
+  clusterID: number,
+  imageURL: string,
+  formValues: any
+}, { id: number }>('POST', pathParams => {
   return `/api/projects/${pathParams.id}/deploy`;
 });
 

+ 75 - 14
server/api/deploy_handler.go

@@ -4,6 +4,7 @@ import (
 	"archive/tar"
 	"bytes"
 	"compress/gzip"
+	"encoding/json"
 	"errors"
 	"fmt"
 	"io"
@@ -15,15 +16,80 @@ import (
 	"gopkg.in/yaml.v2"
 )
 
+// DeployTemplateForm describes the parameters of a deploy template request
+type DeployTemplateForm struct {
+	TemplateName string
+	ClusterID    int
+	ImageURL     string
+	FormValues   map[string]interface{}
+}
+
 // HandleDeployTemplate triggers a chart deployment from a template
 func (app *App) HandleDeployTemplate(w http.ResponseWriter, r *http.Request) {
-	tgt := "hello-porter"
+
+	// TODO: use create form
+	requestForm := make(map[string]interface{})
+
+	// decode from JSON to form value
+	if err := json.NewDecoder(r.Body).Decode(&requestForm); err != nil {
+		app.handleErrorFormDecoding(err, ErrProjectDecode, w)
+		return
+	}
+
+	// TODO: use create form
+	params := DeployTemplateForm{}
+	params.TemplateName = requestForm["templateName"].(string)
+	params.ClusterID = int(requestForm["clusterID"].(float64))
+	params.ImageURL = requestForm["imageURL"].(string)
+	params.FormValues = requestForm["formValues"].(map[string]interface{})
 
 	baseURL := "https://porter-dev.github.io/chart-repo/"
+	defaultValues, err := getDefaultValues(params.TemplateName, baseURL)
+	if err != nil {
+		return
+	}
+
+	// Loop through form params to override
+	for k := range params.FormValues {
+		switch v := interface{}(k).(type) {
+		case string:
+			splits := strings.Split(v, ".")
+
+			// Validate that the field to override exists
+			currentLoc := *defaultValues
+			for s := range splits {
+				key := splits[s]
+				val := currentLoc[key]
+				if val == nil {
+					fmt.Printf("No such field: %v\n", key)
+				} else if s == len(splits)-1 {
+					newValue := params.FormValues[v]
+					fmt.Printf("Overriding default %v with %v\n", val, newValue)
+					currentLoc[key] = newValue
+				} else {
+					fmt.Println("Traversing...")
+					currentLoc = val.(map[interface{}]interface{})
+				}
+			}
+		default:
+			fmt.Println("Non-string type")
+		}
+	}
+
+	d, err := yaml.Marshal(defaultValues)
+	if err != nil {
+		return
+	}
+
+	// Output values.yaml string
+	fmt.Println(string(d))
+}
+
+func getDefaultValues(templateName string, baseURL string) (*map[interface{}]interface{}, error) {
 	resp, err := http.Get(baseURL + "index.yaml")
 	if err != nil {
 		fmt.Println(err)
-		return
+		return nil, err
 	}
 
 	defer resp.Body.Close()
@@ -32,7 +98,7 @@ func (app *App) HandleDeployTemplate(w http.ResponseWriter, r *http.Request) {
 	form := models.IndexYAML{}
 	if err := yaml.Unmarshal([]byte(body), &form); err != nil {
 		fmt.Println(err)
-		return
+		return nil, err
 	}
 
 	// Loop over charts in index.yaml
@@ -47,25 +113,20 @@ func (app *App) HandleDeployTemplate(w http.ResponseWriter, r *http.Request) {
 		}
 
 		// Unpack the target chart and retrieve values.yaml
-		if strAcc == tgt {
+		if strAcc == templateName {
 			tgtURL := baseURL + tarURL
 			values, err := processValues(tgtURL)
 			if err != nil {
 				fmt.Println(err)
-				return
-			}
-
-			defaultValues := *values
-			defaultValues["replicaCount"] = 87
-			fmt.Println(defaultValues["replicaCount"])
-			for k := range *values {
-				fmt.Println(k)
+				return nil, err
 			}
+			return values, nil
 		}
 	}
+	return nil, errors.New("no values.yaml found")
 }
 
-func processValues(tgtURL string) (*map[string]interface{}, error) {
+func processValues(tgtURL string) (*map[interface{}]interface{}, error) {
 	resp, err := http.Get(tgtURL)
 	if err != nil {
 		fmt.Println(err)
@@ -110,7 +171,7 @@ func processValues(tgtURL string) (*map[string]interface{}, error) {
 				}
 
 				// Unmarshal yaml byte buffer
-				form := make(map[string]interface{})
+				form := make(map[interface{}]interface{})
 				if err := yaml.Unmarshal(bufForm.Bytes(), &form); err != nil {
 					fmt.Println(err)
 					return nil, err