deploy_handler.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191
  1. package api
  2. import (
  3. "archive/tar"
  4. "bytes"
  5. "compress/gzip"
  6. "encoding/json"
  7. "errors"
  8. "fmt"
  9. "io"
  10. "io/ioutil"
  11. "net/http"
  12. "strings"
  13. "github.com/porter-dev/porter/internal/models"
  14. "gopkg.in/yaml.v2"
  15. )
  16. // DeployTemplateForm describes the parameters of a deploy template request
  17. type DeployTemplateForm struct {
  18. TemplateName string
  19. ClusterID int
  20. ImageURL string
  21. FormValues map[string]interface{}
  22. }
  23. // HandleDeployTemplate triggers a chart deployment from a template
  24. func (app *App) HandleDeployTemplate(w http.ResponseWriter, r *http.Request) {
  25. // TODO: use create form
  26. requestForm := make(map[string]interface{})
  27. // decode from JSON to form value
  28. if err := json.NewDecoder(r.Body).Decode(&requestForm); err != nil {
  29. app.handleErrorFormDecoding(err, ErrProjectDecode, w)
  30. return
  31. }
  32. // TODO: use create form
  33. params := DeployTemplateForm{}
  34. params.TemplateName = requestForm["templateName"].(string)
  35. params.ClusterID = int(requestForm["clusterID"].(float64))
  36. params.ImageURL = requestForm["imageURL"].(string)
  37. params.FormValues = requestForm["formValues"].(map[string]interface{})
  38. baseURL := "https://porter-dev.github.io/chart-repo/"
  39. defaultValues, err := getDefaultValues(params.TemplateName, baseURL)
  40. if err != nil {
  41. return
  42. }
  43. // Loop through form params to override
  44. for k := range params.FormValues {
  45. switch v := interface{}(k).(type) {
  46. case string:
  47. splits := strings.Split(v, ".")
  48. // Validate that the field to override exists
  49. currentLoc := *defaultValues
  50. for s := range splits {
  51. key := splits[s]
  52. val := currentLoc[key]
  53. if val == nil {
  54. fmt.Printf("No such field: %v\n", key)
  55. } else if s == len(splits)-1 {
  56. newValue := params.FormValues[v]
  57. fmt.Printf("Overriding default %v with %v\n", val, newValue)
  58. currentLoc[key] = newValue
  59. } else {
  60. fmt.Println("Traversing...")
  61. currentLoc = val.(map[interface{}]interface{})
  62. }
  63. }
  64. default:
  65. fmt.Println("Non-string type")
  66. }
  67. }
  68. d, err := yaml.Marshal(defaultValues)
  69. if err != nil {
  70. return
  71. }
  72. // Output values.yaml string
  73. fmt.Println(string(d))
  74. }
  75. func getDefaultValues(templateName string, baseURL string) (*map[interface{}]interface{}, error) {
  76. resp, err := http.Get(baseURL + "index.yaml")
  77. if err != nil {
  78. fmt.Println(err)
  79. return nil, err
  80. }
  81. defer resp.Body.Close()
  82. body, _ := ioutil.ReadAll(resp.Body)
  83. form := models.IndexYAML{}
  84. if err := yaml.Unmarshal([]byte(body), &form); err != nil {
  85. fmt.Println(err)
  86. return nil, err
  87. }
  88. // Loop over charts in index.yaml
  89. for k := range form.Entries {
  90. indexChart := form.Entries[k][0]
  91. tarURL := indexChart.Urls[0]
  92. splits := strings.Split(tarURL, "-")
  93. strAcc := splits[0]
  94. for i := 1; i < len(splits)-1; i++ {
  95. strAcc += "-" + splits[i]
  96. }
  97. // Unpack the target chart and retrieve values.yaml
  98. if strAcc == templateName {
  99. tgtURL := baseURL + tarURL
  100. values, err := processValues(tgtURL)
  101. if err != nil {
  102. fmt.Println(err)
  103. return nil, err
  104. }
  105. return values, nil
  106. }
  107. }
  108. return nil, errors.New("no values.yaml found")
  109. }
  110. func processValues(tgtURL string) (*map[interface{}]interface{}, error) {
  111. resp, err := http.Get(tgtURL)
  112. if err != nil {
  113. fmt.Println(err)
  114. return nil, err
  115. }
  116. defer resp.Body.Close()
  117. body, _ := ioutil.ReadAll(resp.Body)
  118. buf := bytes.NewBuffer(body)
  119. gzf, err := gzip.NewReader(buf)
  120. if err != nil {
  121. fmt.Println(err)
  122. return nil, err
  123. }
  124. // Process tarball to generate FormYAML and retrieve markdown
  125. tarReader := tar.NewReader(gzf)
  126. for {
  127. header, err := tarReader.Next()
  128. if err == io.EOF {
  129. break
  130. } else if err != nil {
  131. fmt.Println(err)
  132. return nil, err
  133. }
  134. name := header.Name
  135. switch header.Typeflag {
  136. case tar.TypeDir:
  137. continue
  138. case tar.TypeReg:
  139. // Handle values.yaml located in archive
  140. if strings.Contains(name, "values.yaml") {
  141. bufForm := new(bytes.Buffer)
  142. _, err := io.Copy(bufForm, tarReader)
  143. if err != nil {
  144. fmt.Println(err)
  145. return nil, err
  146. }
  147. // Unmarshal yaml byte buffer
  148. form := make(map[interface{}]interface{})
  149. if err := yaml.Unmarshal(bufForm.Bytes(), &form); err != nil {
  150. fmt.Println(err)
  151. return nil, err
  152. }
  153. return &form, nil
  154. }
  155. default:
  156. fmt.Printf("%s : %c %s %s\n",
  157. "Unknown type",
  158. header.Typeflag,
  159. "in file",
  160. name,
  161. )
  162. }
  163. }
  164. return nil, errors.New("no values.yaml found")
  165. }