create_addon.go 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. package release
  2. import (
  3. "fmt"
  4. "net/http"
  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/analytics"
  12. "github.com/porter-dev/porter/internal/helm"
  13. "github.com/porter-dev/porter/internal/helm/loader"
  14. "github.com/porter-dev/porter/internal/models"
  15. "github.com/porter-dev/porter/internal/oauth"
  16. "helm.sh/helm/v3/pkg/chart"
  17. )
  18. type CreateAddonHandler struct {
  19. handlers.PorterHandlerReadWriter
  20. authz.KubernetesAgentGetter
  21. }
  22. func NewCreateAddonHandler(
  23. config *config.Config,
  24. decoderValidator shared.RequestDecoderValidator,
  25. writer shared.ResultWriter,
  26. ) *CreateAddonHandler {
  27. return &CreateAddonHandler{
  28. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  29. KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
  30. }
  31. }
  32. func (c *CreateAddonHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  33. user, _ := r.Context().Value(types.UserScope).(*models.User)
  34. proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
  35. cluster, _ := r.Context().Value(types.ClusterScope).(*models.Cluster)
  36. namespace := r.Context().Value(types.NamespaceScope).(string)
  37. operationID := oauth.CreateRandomState()
  38. c.Config().AnalyticsClient.Track(analytics.ApplicationLaunchStartTrack(
  39. &analytics.ApplicationLaunchStartTrackOpts{
  40. ClusterScopedTrackOpts: analytics.GetClusterScopedTrackOpts(user.ID, cluster.ProjectID, cluster.ID),
  41. FlowID: operationID,
  42. },
  43. ))
  44. helmAgent, err := c.GetHelmAgent(r, cluster, "")
  45. if err != nil {
  46. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  47. return
  48. }
  49. request := &types.CreateAddonRequest{}
  50. if ok := c.DecodeAndValidate(w, r, request); !ok {
  51. return
  52. }
  53. if request.TemplateVersion == "latest" {
  54. request.TemplateVersion = ""
  55. }
  56. chart, err := LoadChart(c.Config(), &LoadAddonChartOpts{
  57. ProjectID: proj.ID,
  58. RepoURL: request.RepoURL,
  59. TemplateName: request.TemplateName,
  60. TemplateVersion: request.TemplateVersion,
  61. })
  62. if err != nil {
  63. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  64. return
  65. }
  66. registries, err := c.Repo().Registry().ListRegistriesByProjectID(cluster.ProjectID)
  67. if err != nil {
  68. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  69. return
  70. }
  71. conf := &helm.InstallChartConfig{
  72. Chart: chart,
  73. Name: request.Name,
  74. Namespace: namespace,
  75. Values: request.Values,
  76. Cluster: cluster,
  77. Repo: c.Repo(),
  78. Registries: registries,
  79. }
  80. helmRelease, err := helmAgent.InstallChart(conf, c.Config().DOConf)
  81. if err != nil {
  82. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(
  83. fmt.Errorf("error installing a new chart: %s", err.Error()),
  84. http.StatusBadRequest,
  85. ))
  86. return
  87. }
  88. c.Config().AnalyticsClient.Track(analytics.ApplicationLaunchSuccessTrack(
  89. &analytics.ApplicationLaunchSuccessTrackOpts{
  90. ApplicationScopedTrackOpts: analytics.GetApplicationScopedTrackOpts(
  91. user.ID,
  92. cluster.ProjectID,
  93. cluster.ID,
  94. helmRelease.Name,
  95. helmRelease.Namespace,
  96. chart.Metadata.Name,
  97. ),
  98. FlowID: operationID,
  99. },
  100. ))
  101. }
  102. type LoadAddonChartOpts struct {
  103. ProjectID uint
  104. RepoURL, TemplateName, TemplateVersion string
  105. }
  106. func LoadChart(config *config.Config, opts *LoadAddonChartOpts) (*chart.Chart, error) {
  107. // if the chart repo url is one of the specified application/addon charts, just load public
  108. if opts.RepoURL == config.ServerConf.DefaultAddonHelmRepoURL || opts.RepoURL == config.ServerConf.DefaultApplicationHelmRepoURL {
  109. return loader.LoadChartPublic(opts.RepoURL, opts.TemplateName, opts.TemplateVersion)
  110. } else {
  111. // load the helm repos in the project
  112. hrs, err := config.Repo.HelmRepo().ListHelmReposByProjectID(opts.ProjectID)
  113. if err != nil {
  114. return nil, err
  115. }
  116. for _, hr := range hrs {
  117. if hr.RepoURL == opts.RepoURL {
  118. if hr.BasicAuthIntegrationID != 0 {
  119. // read the basic integration id
  120. basic, err := config.Repo.BasicIntegration().ReadBasicIntegration(opts.ProjectID, hr.BasicAuthIntegrationID)
  121. if err != nil {
  122. return nil, err
  123. }
  124. return loader.LoadChart(&loader.BasicAuthClient{
  125. Username: string(basic.Username),
  126. Password: string(basic.Password),
  127. }, hr.RepoURL, opts.TemplateName, opts.TemplateVersion)
  128. } else {
  129. return loader.LoadChartPublic(hr.RepoURL, opts.TemplateName, opts.TemplateVersion)
  130. }
  131. }
  132. }
  133. }
  134. return nil, fmt.Errorf("chart repo not found")
  135. }