create.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. package infra
  2. import (
  3. "context"
  4. "fmt"
  5. "net/http"
  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/encryption"
  12. "github.com/porter-dev/porter/internal/models"
  13. "github.com/porter-dev/porter/provisioner/client"
  14. ptypes "github.com/porter-dev/porter/provisioner/types"
  15. )
  16. type InfraCreateHandler struct {
  17. handlers.PorterHandlerReadWriter
  18. }
  19. func NewInfraCreateHandler(
  20. config *config.Config,
  21. decoderValidator shared.RequestDecoderValidator,
  22. writer shared.ResultWriter,
  23. ) *InfraCreateHandler {
  24. return &InfraCreateHandler{
  25. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  26. }
  27. }
  28. func (c *InfraCreateHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  29. user, _ := r.Context().Value(types.UserScope).(*models.User)
  30. proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
  31. req := &types.CreateInfraRequest{}
  32. if ok := c.DecodeAndValidate(w, r, req); !ok {
  33. return
  34. }
  35. suffix, err := encryption.GenerateRandomBytes(6)
  36. if err != nil {
  37. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  38. return
  39. }
  40. sourceLink, sourceVersion := getSourceLinkAndVersion(types.InfraKind(req.Kind))
  41. // create the infra object
  42. infra := &models.Infra{
  43. Kind: types.InfraKind(req.Kind),
  44. APIVersion: "v2",
  45. ProjectID: proj.ID,
  46. Suffix: suffix,
  47. Status: types.StatusCreating,
  48. CreatedByUserID: user.ID,
  49. SourceLink: sourceLink,
  50. SourceVersion: sourceVersion,
  51. }
  52. // verify the credentials
  53. err = checkInfraCredentials(c.Config(), proj, infra, req.InfraCredentials)
  54. if err != nil {
  55. c.HandleAPIError(w, r, apierrors.NewErrForbidden(err))
  56. return
  57. }
  58. // handle write to the database
  59. infra, err = c.Repo().Infra().CreateInfra(infra)
  60. if err != nil {
  61. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  62. return
  63. }
  64. // call apply on the provisioner service
  65. pClient := client.NewClient("http://localhost:8082/api/v1")
  66. resp, err := pClient.Apply(context.Background(), proj.ID, infra.ID, &ptypes.ApplyBaseRequest{
  67. Kind: req.Kind,
  68. Values: req.Values,
  69. OperationKind: "create",
  70. })
  71. if err != nil {
  72. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  73. return
  74. }
  75. c.WriteResult(w, r, resp)
  76. }
  77. func checkInfraCredentials(config *config.Config, proj *models.Project, infra *models.Infra, req *types.InfraCredentials) error {
  78. if req == nil {
  79. return nil
  80. }
  81. if req.DOIntegrationID != 0 {
  82. _, err := config.Repo.OAuthIntegration().ReadOAuthIntegration(proj.ID, req.DOIntegrationID)
  83. if err != nil {
  84. return fmt.Errorf("do integration id %d not found in project %d", req.DOIntegrationID, proj.ID)
  85. }
  86. infra.DOIntegrationID = req.DOIntegrationID
  87. infra.AWSIntegrationID = 0
  88. infra.GCPIntegrationID = 0
  89. } else if req.AWSIntegrationID != 0 {
  90. _, err := config.Repo.AWSIntegration().ReadAWSIntegration(proj.ID, req.AWSIntegrationID)
  91. if err != nil {
  92. return fmt.Errorf("aws integration id %d not found in project %d", req.AWSIntegrationID, proj.ID)
  93. }
  94. infra.DOIntegrationID = 0
  95. infra.AWSIntegrationID = req.AWSIntegrationID
  96. infra.GCPIntegrationID = 0
  97. } else if req.GCPIntegrationID != 0 {
  98. _, err := config.Repo.GCPIntegration().ReadGCPIntegration(proj.ID, req.GCPIntegrationID)
  99. if err != nil {
  100. return fmt.Errorf("gcp integration id %d not found in project %d", req.GCPIntegrationID, proj.ID)
  101. }
  102. infra.DOIntegrationID = 0
  103. infra.AWSIntegrationID = 0
  104. infra.GCPIntegrationID = req.GCPIntegrationID
  105. }
  106. if infra.DOIntegrationID == 0 && infra.AWSIntegrationID == 0 && infra.GCPIntegrationID == 0 {
  107. return fmt.Errorf("at least one integration id must be set")
  108. }
  109. return nil
  110. }
  111. // getSourceLinkAndVersion returns the source link and version for the infrastructure. For now,
  112. // this is hardcoded
  113. func getSourceLinkAndVersion(kind types.InfraKind) (string, string) {
  114. switch kind {
  115. case types.InfraECR:
  116. return "porter/aws/ecr", "v0.1.0"
  117. case types.InfraEKS:
  118. return "porter/aws/eks", "v0.1.0"
  119. case types.InfraRDS:
  120. return "porter/aws/rds", "v0.1.0"
  121. case types.InfraGCR:
  122. return "porter/gcp/gcr", "v0.1.0"
  123. case types.InfraGKE:
  124. return "porter/gcp/gke", "v0.1.0"
  125. case types.InfraDOCR:
  126. return "porter/do/docr", "v0.1.0"
  127. case types.InfraDOKS:
  128. return "porter/do/doks", "v0.1.0"
  129. }
  130. return "porter/test", "v0.1.0"
  131. }