template_handler.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200
  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. "gopkg.in/yaml.v2"
  14. )
  15. // IndexYAML represents a chart repo's index.yaml
  16. type IndexYAML struct {
  17. APIVersion string `yaml:"apiVersion"`
  18. Generated string `yaml:"generated"`
  19. Entries map[interface{}]ChartYAML `yaml:"entries"`
  20. }
  21. // ChartYAML represents the data for chart in index.yaml
  22. type ChartYAML []struct {
  23. APIVersion string `yaml:"apiVersion"`
  24. AppVersion string `yaml:"appVersion"`
  25. Created string `yaml:"created"`
  26. Description string `yaml:"description"`
  27. Digest string `yaml:"digest"`
  28. Icon string `yaml:"icon"`
  29. Name string `yaml:"name"`
  30. Type string `yaml:"type"`
  31. Urls []string `yaml:"urls"`
  32. Version string `yaml:"version"`
  33. }
  34. // PorterChart represents a bundled Porter template
  35. type PorterChart struct {
  36. Name string
  37. Description string
  38. Icon string
  39. Form FormYAML
  40. Markdown string
  41. }
  42. // FormYAML represents a chart's values.yaml form abstraction
  43. type FormYAML struct {
  44. Name string `yaml:"name"`
  45. Icon string `yaml:"icon"`
  46. Description string `yaml:"description"`
  47. Tags []string `yaml:"tags"`
  48. Tabs []struct {
  49. Name string `yaml:"name"`
  50. Label string `yaml:"label"`
  51. Sections []struct {
  52. Name string `yaml:"name"`
  53. ShowIf string `yaml:"show_if"`
  54. Contents []struct {
  55. Type string `yaml:"type"`
  56. Label string `yaml:"label"`
  57. Name string `yaml:"name,omitempty"`
  58. Variable string `yaml:"variable,omitempty"`
  59. Settings struct {
  60. Default interface{}
  61. } `yaml:"settings,omitempty"`
  62. } `yaml:"contents"`
  63. } `yaml:"sections"`
  64. } `yaml:"tabs"`
  65. }
  66. // HandleListTemplates retrieves a list of Porter templates
  67. // TODO: test and reduce fragility (handle untar/parse error for individual charts)
  68. // TODO: separate markdown retrieval into its own query if necessary
  69. func (app *App) HandleListTemplates(w http.ResponseWriter, r *http.Request) {
  70. baseURL := "https://porter-dev.github.io/chart-repo/"
  71. fmt.Println("and i oop!")
  72. resp, err := http.Get(baseURL + "index.yaml")
  73. if err != nil {
  74. fmt.Println(err)
  75. return
  76. }
  77. defer resp.Body.Close()
  78. body, _ := ioutil.ReadAll(resp.Body)
  79. form := IndexYAML{}
  80. if err := yaml.Unmarshal([]byte(body), &form); err != nil {
  81. fmt.Println(err)
  82. return
  83. }
  84. // Loop over charts in index.yaml
  85. porterCharts := []PorterChart{}
  86. for k := range form.Entries {
  87. indexChart := form.Entries[k][0]
  88. tarURL := indexChart.Urls[0]
  89. if !strings.Contains(tarURL, "http://") {
  90. tarURL = baseURL + tarURL
  91. }
  92. formData, markdown, err := processTarball(tarURL)
  93. if err != nil {
  94. fmt.Println(err)
  95. return
  96. }
  97. porterChart := PorterChart{}
  98. porterChart.Name = indexChart.Name
  99. porterChart.Description = indexChart.Description
  100. porterChart.Icon = indexChart.Icon
  101. porterChart.Form = *formData
  102. if markdown != "" {
  103. porterChart.Markdown = markdown
  104. }
  105. porterCharts = append(porterCharts, porterChart)
  106. }
  107. json.NewEncoder(w).Encode(porterCharts)
  108. }
  109. func processTarball(tarURL string) (*FormYAML, string, error) {
  110. resp, err := http.Get(tarURL)
  111. if err != nil {
  112. fmt.Println(err)
  113. return nil, "", err
  114. }
  115. defer resp.Body.Close()
  116. body, _ := ioutil.ReadAll(resp.Body)
  117. buf := bytes.NewBuffer(body)
  118. gzf, err := gzip.NewReader(buf)
  119. if err != nil {
  120. fmt.Println(err)
  121. return nil, "", err
  122. }
  123. // Process tarball to generate FormYAML and retrieve markdown
  124. tarReader := tar.NewReader(gzf)
  125. markdown := ""
  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 info.md if found
  140. if strings.Contains(name, "README.md") {
  141. bufMd := new(bytes.Buffer)
  142. _, err := io.Copy(bufMd, tarReader)
  143. if err != nil {
  144. fmt.Println(err)
  145. return nil, "", err
  146. }
  147. markdown = string(bufMd.Bytes())
  148. }
  149. // Handle form.yaml located in archive
  150. if strings.Contains(name, "form.yaml") {
  151. bufForm := new(bytes.Buffer)
  152. _, err := io.Copy(bufForm, tarReader)
  153. if err != nil {
  154. fmt.Println(err)
  155. return nil, "", err
  156. }
  157. // Unmarshal yaml byte buffer
  158. form := FormYAML{}
  159. if err := yaml.Unmarshal(bufForm.Bytes(), &form); err != nil {
  160. fmt.Println(err)
  161. return nil, "", err
  162. }
  163. return &form, markdown, nil
  164. }
  165. default:
  166. fmt.Printf("%s : %c %s %s\n",
  167. "Unknown type",
  168. header.Typeflag,
  169. "in file",
  170. name,
  171. )
  172. }
  173. }
  174. return nil, "", errors.New("no form.yaml found")
  175. }