get.go 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. package release
  2. import (
  3. "net/http"
  4. semver "github.com/Masterminds/semver/v3"
  5. "github.com/porter-dev/porter/api/server/authz"
  6. "github.com/porter-dev/porter/api/server/handlers"
  7. "github.com/porter-dev/porter/api/server/shared"
  8. "github.com/porter-dev/porter/api/server/shared/apierrors"
  9. "github.com/porter-dev/porter/api/server/shared/config"
  10. "github.com/porter-dev/porter/api/types"
  11. "github.com/porter-dev/porter/internal/helm/loader"
  12. "github.com/porter-dev/porter/internal/models"
  13. "github.com/porter-dev/porter/internal/templater/parser"
  14. "gorm.io/gorm"
  15. "helm.sh/helm/v3/pkg/release"
  16. )
  17. type ReleaseGetHandler struct {
  18. handlers.PorterHandlerWriter
  19. authz.KubernetesAgentGetter
  20. }
  21. func NewReleaseGetHandler(
  22. config *config.Config,
  23. writer shared.ResultWriter,
  24. ) *ReleaseGetHandler {
  25. return &ReleaseGetHandler{
  26. PorterHandlerWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
  27. KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
  28. }
  29. }
  30. func (c *ReleaseGetHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  31. helmRelease, _ := r.Context().Value(types.ReleaseScope).(*release.Release)
  32. res := &types.Release{
  33. Release: helmRelease,
  34. }
  35. // look up the release in the database; if not found, do not populate Porter fields
  36. cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
  37. release, err := c.Repo().Release().ReadRelease(cluster.ID, helmRelease.Name, helmRelease.Namespace)
  38. if err == nil {
  39. res.PorterRelease = release.ToReleaseType()
  40. res.ID = release.ID
  41. res.WebhookToken = release.WebhookToken
  42. if release.GitActionConfig != nil {
  43. res.GitActionConfig = release.GitActionConfig.ToGitActionConfigType()
  44. }
  45. if release.BuildConfig != 0 {
  46. bc, err := c.Repo().BuildConfig().GetBuildConfig(release.BuildConfig)
  47. if err != nil {
  48. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  49. return
  50. }
  51. res.BuildConfig = bc.ToBuildConfigType()
  52. }
  53. } else if err != gorm.ErrRecordNotFound {
  54. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  55. return
  56. } else {
  57. res.PorterRelease = &types.PorterRelease{}
  58. }
  59. // detect if Porter application chart and attempt to get the latest version
  60. // from chart repo
  61. cache := c.Config().URLCache
  62. chartRepoURL, foundFirst := cache.GetURL(helmRelease.Chart.Metadata.Name)
  63. if !foundFirst {
  64. cache.Update()
  65. chartRepoURL, _ = cache.GetURL(helmRelease.Chart.Metadata.Name)
  66. }
  67. if chartRepoURL != "" {
  68. repoIndex, err := loader.LoadRepoIndexPublic(chartRepoURL)
  69. if err == nil {
  70. porterChart := loader.FindPorterChartInIndexList(repoIndex, res.Chart.Metadata.Name)
  71. res.LatestVersion = res.Chart.Metadata.Version
  72. // set latest version to the greater of porterChart.Versions and res.Chart.Metadata.Version
  73. porterChartVersion, porterChartErr := semver.NewVersion(porterChart.Versions[0])
  74. currChartVersion, currChartErr := semver.NewVersion(res.Chart.Metadata.Version)
  75. if currChartErr == nil && porterChartErr == nil && porterChartVersion.GreaterThan(currChartVersion) {
  76. res.LatestVersion = porterChart.Versions[0]
  77. }
  78. }
  79. }
  80. // look for the form using the dynamic client
  81. dynClient, err := c.GetDynamicClient(r, cluster)
  82. if err != nil {
  83. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  84. return
  85. }
  86. parserDef := &parser.ClientConfigDefault{
  87. DynamicClient: dynClient,
  88. HelmChart: helmRelease.Chart,
  89. HelmRelease: helmRelease,
  90. }
  91. form, err := parser.GetFormFromRelease(parserDef, helmRelease)
  92. if err != nil {
  93. c.HandleAPIErrorNoWrite(w, r, apierrors.NewErrInternal(err))
  94. } else {
  95. res.Form = form
  96. }
  97. // if form not populated, detect common charts
  98. if res.Form == nil {
  99. // for now just case by name
  100. if res.Release.Chart.Name() == "cert-manager" {
  101. formYAML, err := parser.FormYAMLFromBytes(parserDef, []byte(certManagerForm), "", "")
  102. if err == nil {
  103. res.Form = formYAML
  104. }
  105. }
  106. }
  107. c.WriteResult(w, r, res)
  108. }
  109. const certManagerForm string = `tags:
  110. - hello
  111. tabs:
  112. - name: main
  113. context:
  114. type: cluster
  115. config:
  116. group: cert-manager.io
  117. version: v1
  118. resource: certificates
  119. label: Certificates
  120. sections:
  121. - name: section_one
  122. contents:
  123. - type: heading
  124. label: Certificates
  125. - type: resource-list
  126. settings:
  127. options:
  128. resource-button:
  129. name: "Renew Certificate"
  130. description: "This will delete the existing certificate resource, triggering a new certificate request."
  131. actions:
  132. - delete:
  133. scope: namespace
  134. relative_uri: /crd
  135. context:
  136. type: cluster
  137. config:
  138. group: cert-manager.io
  139. version: v1
  140. resource: certificates
  141. value: |
  142. .items[] | {
  143. metadata: .metadata,
  144. name: "\(.spec.dnsNames | join(","))",
  145. label: "\(.metadata.namespace)/\(.metadata.name)",
  146. status: (
  147. ([.status.conditions[].type] | index("Ready")) as $index | (
  148. if $index then (
  149. if .status.conditions[$index].status == "True" then "Ready" else "Not Ready" end
  150. ) else (
  151. "Not Ready"
  152. ) end
  153. )
  154. ),
  155. timestamp: .status.conditions[0].lastTransitionTime,
  156. message: [.status.conditions[].message] | unique | join(","),
  157. data: {}
  158. }`