list.go 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. package datastore
  2. import (
  3. "net/http"
  4. "connectrpc.com/connect"
  5. porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
  6. "github.com/porter-dev/porter/api/server/authz"
  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/server/shared/requestutils"
  12. "github.com/porter-dev/porter/api/types"
  13. "github.com/porter-dev/porter/internal/models"
  14. "github.com/porter-dev/porter/internal/telemetry"
  15. )
  16. // ListDatastoresRequest is a struct that represents the various filter options used for
  17. // retrieving the datastores
  18. type ListDatastoresRequest struct {
  19. // Name is the name of the datastore to filter by
  20. Name string `schema:"name"`
  21. // Type is the type of the datastore to filter by
  22. Type string `schema:"type"`
  23. // IncludeEnvGroup controls whether to include the datastore env group or not
  24. IncludeEnvGroup bool `schema:"include_env_group"`
  25. // IncludeMetadata controls whether to include datastore metadata or not
  26. IncludeMetadata bool `schema:"include_metadata"`
  27. }
  28. // ListDatastoresResponse describes the list datastores response body
  29. type ListDatastoresResponse struct {
  30. // Datastores is a list of datastore entries for the http response
  31. Datastores []DatastoresResponseEntry `json:"datastores"`
  32. }
  33. // DatastoresResponseEntry describes an outbound datastores response entry
  34. type DatastoresResponseEntry struct {
  35. // Name is the name of the datastore
  36. Name string `json:"name"`
  37. // Type is the type of the datastore
  38. Type string `json:"type"`
  39. // Env is the env group for the datastore
  40. Env *porterv1.EnvGroup `json:"env,omitempty"`
  41. // Metadata is a list of metadata objects for the datastore
  42. Metadata []*porterv1.DatastoreMetadata `json:"metadata,omitempty"`
  43. // Status is the status of the datastore
  44. Status string `json:"status,omitempty"`
  45. }
  46. // ListDatastoresHandler is a struct for handling datastore status requests
  47. type ListDatastoresHandler struct {
  48. handlers.PorterHandlerReadWriter
  49. authz.KubernetesAgentGetter
  50. }
  51. // NewListDatastoresHandler constructs a datastore ListDatastoresHandler
  52. func NewListDatastoresHandler(
  53. config *config.Config,
  54. decoderValidator shared.RequestDecoderValidator,
  55. writer shared.ResultWriter,
  56. ) *ListDatastoresHandler {
  57. return &ListDatastoresHandler{
  58. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  59. KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
  60. }
  61. }
  62. // ServeHTTP returns a list of datastores associated with the specified project/cloud-provider
  63. func (h *ListDatastoresHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  64. ctx, span := telemetry.NewSpan(r.Context(), "serve-datastore-list")
  65. defer span.End()
  66. project, _ := ctx.Value(types.ProjectScope).(*models.Project)
  67. request := &ListDatastoresRequest{}
  68. if ok := h.DecodeAndValidate(w, r, request); !ok {
  69. return
  70. }
  71. cloudProviderType, err := requestutils.GetURLParamString(r, types.URLParamCloudProviderType)
  72. if err != nil {
  73. err := telemetry.Error(ctx, span, err, "error parsing cloud provider type")
  74. h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  75. return
  76. }
  77. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "cloud-provider-type", Value: cloudProviderType})
  78. cloudProviderID, err := requestutils.GetURLParamString(r, types.URLParamCloudProviderID)
  79. if err != nil {
  80. err := telemetry.Error(ctx, span, err, "error parsing cloud provider id")
  81. h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  82. return
  83. }
  84. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "cloud-provider-id", Value: cloudProviderID})
  85. datastoreType := porterv1.EnumDatastore_ENUM_DATASTORE_UNSPECIFIED
  86. switch request.Type {
  87. case "elasticache-redis":
  88. datastoreType = porterv1.EnumDatastore_ENUM_DATASTORE_ELASTICACHE_REDIS
  89. case "rds-postgresql":
  90. datastoreType = porterv1.EnumDatastore_ENUM_DATASTORE_RDS_POSTGRESQL
  91. case "rds-postgresql-aurora":
  92. datastoreType = porterv1.EnumDatastore_ENUM_DATASTORE_RDS_AURORA_POSTGRESQL
  93. case "":
  94. datastoreType = porterv1.EnumDatastore_ENUM_DATASTORE_UNSPECIFIED
  95. default:
  96. err := telemetry.Error(ctx, span, err, "invalid datastore type specified")
  97. h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  98. return
  99. }
  100. telemetry.WithAttributes(span,
  101. telemetry.AttributeKV{Key: "datastore-name", Value: request.Name},
  102. telemetry.AttributeKV{Key: "datastore-type", Value: request.Type},
  103. telemetry.AttributeKV{Key: "include-env-group", Value: request.IncludeEnvGroup},
  104. telemetry.AttributeKV{Key: "include-metadata", Value: request.IncludeMetadata},
  105. )
  106. var cloudProvider porterv1.EnumCloudProvider
  107. switch cloudProviderType {
  108. case "aws":
  109. cloudProvider = porterv1.EnumCloudProvider_ENUM_CLOUD_PROVIDER_AWS
  110. default:
  111. err := telemetry.Error(ctx, span, nil, "unsupported cloud provider")
  112. h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  113. return
  114. }
  115. message := porterv1.ListDatastoresRequest{
  116. ProjectId: int64(project.ID),
  117. CloudProvider: cloudProvider,
  118. CloudProviderAccountId: cloudProviderID,
  119. Name: request.Name,
  120. IncludeEnvGroup: request.IncludeEnvGroup,
  121. IncludeMetadata: request.IncludeMetadata,
  122. }
  123. if datastoreType != porterv1.EnumDatastore_ENUM_DATASTORE_UNSPECIFIED {
  124. message.Type = &datastoreType
  125. }
  126. req := connect.NewRequest(&message)
  127. resp, ccpErr := h.Config().ClusterControlPlaneClient.ListDatastores(ctx, req)
  128. if ccpErr != nil {
  129. err := telemetry.Error(ctx, span, ccpErr, "error listing datastores from ccp")
  130. h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  131. return
  132. }
  133. if resp.Msg == nil {
  134. err := telemetry.Error(ctx, span, err, "missing response message from ccp")
  135. h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  136. return
  137. }
  138. response := ListDatastoresResponse{
  139. Datastores: []DatastoresResponseEntry{},
  140. }
  141. for _, datastore := range resp.Msg.Datastores {
  142. response.Datastores = append(response.Datastores, DatastoresResponseEntry{
  143. Name: datastore.Name,
  144. Type: datastore.Type.Enum().String(),
  145. Metadata: datastore.Metadata,
  146. Env: datastore.Env,
  147. })
  148. }
  149. h.WriteResult(w, r, response)
  150. }