delete.go 2.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798
  1. package project
  2. import (
  3. "fmt"
  4. "net/http"
  5. "github.com/bufbuild/connect-go"
  6. porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
  7. "github.com/porter-dev/porter/api/server/handlers"
  8. "github.com/porter-dev/porter/api/server/shared"
  9. "github.com/porter-dev/porter/api/server/shared/apierrors"
  10. "github.com/porter-dev/porter/api/server/shared/config"
  11. "github.com/porter-dev/porter/api/types"
  12. "github.com/porter-dev/porter/internal/models"
  13. "github.com/porter-dev/porter/internal/notifier"
  14. )
  15. type ProjectDeleteHandler struct {
  16. handlers.PorterHandlerWriter
  17. }
  18. func NewProjectDeleteHandler(
  19. config *config.Config,
  20. writer shared.ResultWriter,
  21. ) *ProjectDeleteHandler {
  22. return &ProjectDeleteHandler{
  23. PorterHandlerWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
  24. }
  25. }
  26. func (p *ProjectDeleteHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  27. ctx := r.Context()
  28. user, _ := ctx.Value(types.UserScope).(*models.User)
  29. proj, _ := ctx.Value(types.ProjectScope).(*models.Project)
  30. if proj.CapiProvisionerEnabled {
  31. clusters, err := p.Config().Repo.Cluster().ListClustersByProjectID(proj.ID)
  32. if err != nil {
  33. p.HandleAPIError(w, r, apierrors.NewErrInternal(fmt.Errorf("error finding clusters for project: %w", err)))
  34. return
  35. }
  36. for _, cluster := range clusters {
  37. if cluster.ProvisionedBy == "CAPI" {
  38. if !cluster.DeletedAt.Time.IsZero() {
  39. continue
  40. }
  41. contractRevision, err := p.Config().Repo.APIContractRevisioner().List(ctx, proj.ID, cluster.ID)
  42. if err != nil {
  43. p.HandleAPIError(w, r, apierrors.NewErrInternal(fmt.Errorf("error finding contract revisions for cluster: %w", err)))
  44. return
  45. }
  46. if len(contractRevision) == 0 {
  47. continue
  48. }
  49. req := connect.NewRequest(&porterv1.DeleteClusterRequest{
  50. ContractRevision: &porterv1.ContractRevision{
  51. ClusterId: int32(cluster.ID),
  52. ProjectId: int32(cluster.ProjectID),
  53. RevisionId: contractRevision[0].ID.String(),
  54. },
  55. })
  56. _, err = p.Config().ClusterControlPlaneClient.DeleteCluster(ctx, req)
  57. if err != nil {
  58. p.HandleAPIError(w, r, apierrors.NewErrInternal(fmt.Errorf("error deleting cluster: %w", err)))
  59. return
  60. }
  61. }
  62. }
  63. }
  64. err := p.Config().UserNotifier.SendProjectDeleteEmail(
  65. &notifier.SendProjectDeleteEmailOpts{
  66. Email: user.Email,
  67. Project: proj.Name,
  68. },
  69. )
  70. if err != nil {
  71. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  72. return
  73. }
  74. deletedProject, err := p.Repo().Project().DeleteProject(proj)
  75. if err != nil {
  76. p.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  77. return
  78. }
  79. p.WriteResult(w, r, deletedProject.ToProjectType())
  80. // delete the billing team
  81. if err := p.Config().BillingManager.DeleteTeam(user, proj); err != nil {
  82. // we do not write error response, since setting up billing error can be
  83. // resolved later and may not be fatal
  84. p.HandleAPIErrorNoWrite(w, r, apierrors.NewErrInternal(err))
  85. }
  86. }