get.go 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125
  1. package datastore
  2. import (
  3. "net/http"
  4. "github.com/aws/aws-sdk-go/aws/arn"
  5. "github.com/google/uuid"
  6. porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
  7. "github.com/porter-dev/porter/api/server/authz"
  8. "github.com/porter-dev/porter/api/server/handlers"
  9. "github.com/porter-dev/porter/api/server/handlers/cloud_provider"
  10. "github.com/porter-dev/porter/api/server/shared"
  11. "github.com/porter-dev/porter/api/server/shared/apierrors"
  12. "github.com/porter-dev/porter/api/server/shared/config"
  13. "github.com/porter-dev/porter/api/server/shared/requestutils"
  14. "github.com/porter-dev/porter/api/types"
  15. "github.com/porter-dev/porter/internal/models"
  16. "github.com/porter-dev/porter/internal/telemetry"
  17. )
  18. // GetDatastoreResponse describes the list datastores response body
  19. type GetDatastoreResponse struct {
  20. // Datastore is the datastore that has been retrieved
  21. Datastore Datastore `json:"datastore"`
  22. }
  23. // GetDatastoreHandler is a struct for retrieving a datastore
  24. type GetDatastoreHandler struct {
  25. handlers.PorterHandlerReadWriter
  26. authz.KubernetesAgentGetter
  27. }
  28. // NewGetDatastoreHandler returns a GetDatastoreHandler
  29. func NewGetDatastoreHandler(
  30. config *config.Config,
  31. decoderValidator shared.RequestDecoderValidator,
  32. writer shared.ResultWriter,
  33. ) *GetDatastoreHandler {
  34. return &GetDatastoreHandler{
  35. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  36. KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
  37. }
  38. }
  39. const (
  40. // SupportedDatastoreCloudProvider_AWS is the AWS cloud provider
  41. SupportedDatastoreCloudProvider_AWS string = "AWS"
  42. )
  43. // ServeHTTP retrieves the datastore in the given project
  44. func (c *GetDatastoreHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  45. ctx, span := telemetry.NewSpan(r.Context(), "serve-get-datastore")
  46. defer span.End()
  47. project, _ := ctx.Value(types.ProjectScope).(*models.Project)
  48. resp := GetDatastoreResponse{}
  49. datastoreName, reqErr := requestutils.GetURLParamString(r, types.URLParamDatastoreName)
  50. if reqErr != nil {
  51. err := telemetry.Error(ctx, span, nil, "error parsing datastore name")
  52. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  53. return
  54. }
  55. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "datastore-name", Value: datastoreName})
  56. datastoreRecord, err := c.Repo().Datastore().GetByProjectIDAndName(ctx, project.ID, datastoreName)
  57. if err != nil {
  58. err = telemetry.Error(ctx, span, err, "datastore record not found")
  59. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  60. return
  61. }
  62. if datastoreRecord == nil || datastoreRecord.ID == uuid.Nil {
  63. err = telemetry.Error(ctx, span, nil, "datastore record does not exist")
  64. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusNotFound))
  65. return
  66. }
  67. if datastoreRecord.CloudProvider != SupportedDatastoreCloudProvider_AWS {
  68. err = telemetry.Error(ctx, span, nil, "unsupported datastore cloud provider")
  69. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  70. return
  71. }
  72. awsArn, err := arn.Parse(datastoreRecord.CloudProviderCredentialIdentifier)
  73. if err != nil {
  74. err = telemetry.Error(ctx, span, err, "error parsing aws account id")
  75. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  76. return
  77. }
  78. datastores, err := Datastores(ctx, DatastoresInput{
  79. ProjectID: project.ID,
  80. CloudProvider: cloud_provider.CloudProvider{
  81. AccountID: awsArn.AccountID,
  82. Type: porterv1.EnumCloudProvider_ENUM_CLOUD_PROVIDER_AWS,
  83. },
  84. Name: datastoreName,
  85. IncludeEnvGroup: true,
  86. IncludeMetadata: true,
  87. CCPClient: c.Config().ClusterControlPlaneClient,
  88. DatastoreRepository: c.Repo().Datastore(),
  89. })
  90. if err != nil {
  91. err = telemetry.Error(ctx, span, err, "error getting datastore")
  92. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  93. return
  94. }
  95. if len(datastores) == 0 {
  96. err = telemetry.Error(ctx, span, nil, "datastore not found")
  97. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusNotFound))
  98. return
  99. }
  100. if len(datastores) > 1 {
  101. err = telemetry.Error(ctx, span, nil, "unexpected number of datastores found matching name")
  102. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "datastore-count", Value: len(datastores)})
  103. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  104. return
  105. }
  106. datastore := datastores[0]
  107. resp.Datastore = datastore
  108. c.WriteResult(w, r, resp)
  109. }