get.go 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  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. } else if err != gorm.ErrRecordNotFound {
  46. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  47. return
  48. } else {
  49. res.PorterRelease = &types.PorterRelease{}
  50. }
  51. // detect if Porter application chart and attempt to get the latest version
  52. // from chart repo
  53. cache := c.Config().URLCache
  54. chartRepoURL, foundFirst := cache.GetURL(helmRelease.Chart.Metadata.Name)
  55. if !foundFirst {
  56. cache.Update()
  57. chartRepoURL, _ = cache.GetURL(helmRelease.Chart.Metadata.Name)
  58. }
  59. if chartRepoURL != "" {
  60. repoIndex, err := loader.LoadRepoIndexPublic(chartRepoURL)
  61. if err == nil {
  62. porterChart := loader.FindPorterChartInIndexList(repoIndex, res.Chart.Metadata.Name)
  63. res.LatestVersion = res.Chart.Metadata.Version
  64. // set latest version to the greater of porterChart.Versions and res.Chart.Metadata.Version
  65. porterChartVersion, porterChartErr := semver.NewVersion(porterChart.Versions[0])
  66. currChartVersion, currChartErr := semver.NewVersion(res.Chart.Metadata.Version)
  67. if currChartErr == nil && porterChartErr == nil && porterChartVersion.GreaterThan(currChartVersion) {
  68. res.LatestVersion = porterChart.Versions[0]
  69. }
  70. }
  71. }
  72. // look for the form using the dynamic client
  73. dynClient, err := c.GetDynamicClient(r, cluster)
  74. if err != nil {
  75. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  76. return
  77. }
  78. parserDef := &parser.ClientConfigDefault{
  79. DynamicClient: dynClient,
  80. HelmChart: helmRelease.Chart,
  81. HelmRelease: helmRelease,
  82. }
  83. form, err := parser.GetFormFromRelease(parserDef, helmRelease)
  84. if err != nil {
  85. c.HandleAPIErrorNoWrite(w, r, apierrors.NewErrInternal(err))
  86. } else {
  87. res.Form = form
  88. }
  89. // if form not populated, detect common charts
  90. if res.Form == nil {
  91. // for now just case by name
  92. if res.Release.Chart.Name() == "cert-manager" {
  93. formYAML, err := parser.FormYAMLFromBytes(parserDef, []byte(certManagerForm), "")
  94. if err == nil {
  95. res.Form = formYAML
  96. }
  97. }
  98. }
  99. c.WriteResult(w, r, res)
  100. }
  101. const certManagerForm string = `tags:
  102. - hello
  103. tabs:
  104. - name: main
  105. context:
  106. type: cluster
  107. config:
  108. group: cert-manager.io
  109. version: v1
  110. resource: certificates
  111. label: Certificates
  112. sections:
  113. - name: section_one
  114. contents:
  115. - type: heading
  116. label: Certificates
  117. - type: resource-list
  118. settings:
  119. options:
  120. resource-button:
  121. name: "Renew Certificate"
  122. description: "This will delete the existing certificate resource, triggering a new certificate request."
  123. actions:
  124. - delete:
  125. scope: namespace
  126. relative_uri: /crd
  127. context:
  128. type: cluster
  129. config:
  130. group: cert-manager.io
  131. version: v1
  132. resource: certificates
  133. value: |
  134. .items[] | {
  135. metadata: .metadata,
  136. name: "\(.spec.dnsNames | join(","))",
  137. label: "\(.metadata.namespace)/\(.metadata.name)",
  138. status: (
  139. ([.status.conditions[].type] | index("Ready")) as $index | (
  140. if $index then (
  141. if .status.conditions[$index].status == "True" then "Ready" else "Not Ready" end
  142. ) else (
  143. "Not Ready"
  144. ) end
  145. )
  146. ),
  147. timestamp: .status.conditions[0].lastTransitionTime,
  148. message: [.status.conditions[].message] | unique | join(","),
  149. data: {}
  150. }`