parse.go 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176
  1. package stacks
  2. import (
  3. "fmt"
  4. "github.com/porter-dev/porter/api/server/shared/config"
  5. "github.com/porter-dev/porter/api/types"
  6. stack "github.com/porter-dev/porter/cli/cmd/stack"
  7. "github.com/porter-dev/porter/internal/helm/loader"
  8. "github.com/porter-dev/porter/internal/templater/utils"
  9. "github.com/stefanmcshane/helm/pkg/chart"
  10. "gopkg.in/yaml.v2"
  11. )
  12. func parse(porterYaml []byte, imageInfo *types.ImageInfo, config *config.Config, projectID uint) (*chart.Chart, map[string]interface{}, error) {
  13. parsed := &stack.PorterStackYAML{}
  14. err := yaml.Unmarshal(porterYaml, parsed)
  15. if err != nil {
  16. return nil, nil, fmt.Errorf("%s: %w", "error parsing porter.yaml", err)
  17. }
  18. values, err := buildStackValues(parsed, imageInfo)
  19. if err != nil {
  20. return nil, nil, fmt.Errorf("%s: %w", "error building values from porter.yaml", err)
  21. }
  22. convertedValues := convertMap(values)
  23. chart, err := buildStackChart(parsed, config, projectID)
  24. if err != nil {
  25. return nil, nil, fmt.Errorf("%s: %w", "error building chart from porter.yaml", err)
  26. }
  27. return chart, convertedValues.(map[string]interface{}), nil
  28. }
  29. func buildStackValues(parsed *stack.PorterStackYAML, imageInfo *types.ImageInfo) (map[string]interface{}, error) {
  30. values := make(map[string]interface{})
  31. for name, app := range parsed.Apps {
  32. defaultValues := getDefaultValues(app, parsed.Env, imageInfo)
  33. helm_values := utils.CoalesceValues(defaultValues, app.Config)
  34. values[name] = helm_values
  35. }
  36. return values, nil
  37. }
  38. func getDefaultValues(app *stack.App, env map[string]string, imageInfo *types.ImageInfo) map[string]interface{} {
  39. var defaultValues map[string]interface{}
  40. if *app.Type == "web" {
  41. defaultValues = map[string]interface{}{
  42. "ingress": map[string]interface{}{
  43. "enabled": false,
  44. },
  45. "container": map[string]interface{}{
  46. "command": *app.Run,
  47. "env": map[string]interface{}{
  48. "normal": stack.CopyEnv(env),
  49. },
  50. },
  51. }
  52. } else {
  53. defaultValues = map[string]interface{}{
  54. "container": map[string]interface{}{
  55. "command": *app.Run,
  56. "env": map[string]interface{}{
  57. "normal": stack.CopyEnv(env),
  58. },
  59. },
  60. }
  61. }
  62. if imageInfo != nil {
  63. defaultValues["image"] = map[string]interface{}{
  64. "repository": imageInfo.Repository,
  65. "tag": imageInfo.Tag,
  66. }
  67. }
  68. return defaultValues
  69. }
  70. func buildStackChart(parsed *stack.PorterStackYAML, config *config.Config, projectID uint) (*chart.Chart, error) {
  71. deps := make([]*chart.Dependency, 0)
  72. for alias, app := range parsed.Apps {
  73. selectedRepo := "https://charts.getporter.dev"
  74. selectedVersion, err := getLatestTemplateVersion(*app.Type, config, projectID)
  75. if err != nil {
  76. return nil, err
  77. }
  78. deps = append(deps, &chart.Dependency{
  79. Name: *app.Type,
  80. Alias: alias,
  81. Version: selectedVersion,
  82. Repository: selectedRepo,
  83. })
  84. }
  85. chart, err := createChartFromDependencies(deps)
  86. if err != nil {
  87. return nil, err
  88. }
  89. return chart, nil
  90. }
  91. func createChartFromDependencies(deps []*chart.Dependency) (*chart.Chart, error) {
  92. metadata := &chart.Metadata{
  93. Name: "umbrella",
  94. Description: "Web application that is exposed to external traffic.",
  95. Version: "0.96.0",
  96. APIVersion: "v2",
  97. Home: "https://getporter.dev/",
  98. Icon: "https://user-images.githubusercontent.com/65516095/111255214-07d3da80-85ed-11eb-99e2-fddcbdb99bdb.png",
  99. Keywords: []string{
  100. "porter",
  101. "application",
  102. "service",
  103. "umbrella",
  104. },
  105. Type: "application",
  106. Dependencies: deps,
  107. }
  108. // create a new chart object with the metadata
  109. c := &chart.Chart{
  110. Metadata: metadata,
  111. }
  112. return c, nil
  113. }
  114. func getLatestTemplateVersion(templateName string, config *config.Config, projectID uint) (string, error) {
  115. repoIndex, err := loader.LoadRepoIndexPublic(config.ServerConf.DefaultApplicationHelmRepoURL)
  116. if err != nil {
  117. return "", fmt.Errorf("%s: %w", "unable to load porter chart repo", err)
  118. }
  119. templates := loader.RepoIndexToPorterChartList(repoIndex, config.ServerConf.DefaultApplicationHelmRepoURL)
  120. if err != nil {
  121. return "", fmt.Errorf("%s: %w", "unable to load porter chart list", err)
  122. }
  123. var version string
  124. // find the matching template name
  125. for _, template := range templates {
  126. if templateName == template.Name {
  127. version = template.Versions[0]
  128. break
  129. }
  130. }
  131. if version == "" {
  132. return "", fmt.Errorf("matching template version not found")
  133. }
  134. return version, nil
  135. }
  136. func convertMap(m interface{}) interface{} {
  137. switch m := m.(type) {
  138. case map[string]interface{}:
  139. for k, v := range m {
  140. m[k] = convertMap(v)
  141. }
  142. case map[interface{}]interface{}:
  143. result := map[string]interface{}{}
  144. for k, v := range m {
  145. result[k.(string)] = convertMap(v)
  146. }
  147. return result
  148. case []interface{}:
  149. for i, v := range m {
  150. m[i] = convertMap(v)
  151. }
  152. }
  153. return m
  154. }