deploy_handler.go 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194
  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. // Set image URL
  44. (*defaultValues)["image"].(map[interface{}]interface{})["repository"] = params.ImageURL
  45. // Loop through form params to override
  46. for k := range params.FormValues {
  47. switch v := interface{}(k).(type) {
  48. case string:
  49. splits := strings.Split(v, ".")
  50. // Validate that the field to override exists
  51. currentLoc := *defaultValues
  52. for s := range splits {
  53. key := splits[s]
  54. val := currentLoc[key]
  55. if val == nil {
  56. fmt.Printf("No such field: %v\n", key)
  57. } else if s == len(splits)-1 {
  58. newValue := params.FormValues[v]
  59. fmt.Printf("Overriding default %v with %v\n", val, newValue)
  60. currentLoc[key] = newValue
  61. } else {
  62. fmt.Println("Traversing...")
  63. currentLoc = val.(map[interface{}]interface{})
  64. }
  65. }
  66. default:
  67. fmt.Println("Non-string type")
  68. }
  69. }
  70. d, err := yaml.Marshal(defaultValues)
  71. if err != nil {
  72. return
  73. }
  74. // Output values.yaml string
  75. fmt.Println(string(d))
  76. }
  77. func getDefaultValues(templateName string, baseURL string) (*map[interface{}]interface{}, error) {
  78. resp, err := http.Get(baseURL + "index.yaml")
  79. if err != nil {
  80. fmt.Println(err)
  81. return nil, err
  82. }
  83. defer resp.Body.Close()
  84. body, _ := ioutil.ReadAll(resp.Body)
  85. form := models.IndexYAML{}
  86. if err := yaml.Unmarshal([]byte(body), &form); err != nil {
  87. fmt.Println(err)
  88. return nil, err
  89. }
  90. // Loop over charts in index.yaml
  91. for k := range form.Entries {
  92. indexChart := form.Entries[k][0]
  93. tarURL := indexChart.Urls[0]
  94. splits := strings.Split(tarURL, "-")
  95. strAcc := splits[0]
  96. for i := 1; i < len(splits)-1; i++ {
  97. strAcc += "-" + splits[i]
  98. }
  99. // Unpack the target chart and retrieve values.yaml
  100. if strAcc == templateName {
  101. tgtURL := baseURL + tarURL
  102. values, err := processValues(tgtURL)
  103. if err != nil {
  104. fmt.Println(err)
  105. return nil, err
  106. }
  107. return values, nil
  108. }
  109. }
  110. return nil, errors.New("no values.yaml found")
  111. }
  112. func processValues(tgtURL string) (*map[interface{}]interface{}, error) {
  113. resp, err := http.Get(tgtURL)
  114. if err != nil {
  115. fmt.Println(err)
  116. return nil, err
  117. }
  118. defer resp.Body.Close()
  119. body, _ := ioutil.ReadAll(resp.Body)
  120. buf := bytes.NewBuffer(body)
  121. gzf, err := gzip.NewReader(buf)
  122. if err != nil {
  123. fmt.Println(err)
  124. return nil, err
  125. }
  126. // Process tarball to generate FormYAML and retrieve markdown
  127. tarReader := tar.NewReader(gzf)
  128. for {
  129. header, err := tarReader.Next()
  130. if err == io.EOF {
  131. break
  132. } else if err != nil {
  133. fmt.Println(err)
  134. return nil, err
  135. }
  136. name := header.Name
  137. switch header.Typeflag {
  138. case tar.TypeDir:
  139. continue
  140. case tar.TypeReg:
  141. // Handle values.yaml located in archive
  142. if strings.Contains(name, "values.yaml") {
  143. bufForm := new(bytes.Buffer)
  144. _, err := io.Copy(bufForm, tarReader)
  145. if err != nil {
  146. fmt.Println(err)
  147. return nil, err
  148. }
  149. // Unmarshal yaml byte buffer
  150. form := make(map[interface{}]interface{})
  151. if err := yaml.Unmarshal(bufForm.Bytes(), &form); err != nil {
  152. fmt.Println(err)
  153. return nil, err
  154. }
  155. return &form, nil
  156. }
  157. default:
  158. fmt.Printf("%s : %c %s %s\n",
  159. "Unknown type",
  160. header.Typeflag,
  161. "in file",
  162. name,
  163. )
  164. }
  165. }
  166. return nil, errors.New("no values.yaml found")
  167. }