Selaa lähdekoodia

Forking the get datastore flow (#4409)

Feroze Mohideen 2 vuotta sitten
vanhempi
sitoutus
7a811a1c8d

+ 0 - 9
api/server/handlers/datastore/create_proxy.go

@@ -17,15 +17,6 @@ import (
 	"github.com/porter-dev/porter/internal/telemetry"
 )
 
-// Credential has all information about connecting to a datastore
-type Credential struct {
-	Host         string `json:"host"`
-	Port         int    `json:"port"`
-	Username     string `json:"username"`
-	Password     string `json:"password"`
-	DatabaseName string `json:"database_name"`
-}
-
 // CreateDatastoreProxyResponse is the response body for the create datastore proxy endpoint
 type CreateDatastoreProxyResponse struct {
 	// PodName is the name of the pod that was created

+ 117 - 0
api/server/handlers/datastore/credential.go

@@ -0,0 +1,117 @@
+package datastore
+
+import (
+	"net/http"
+
+	"connectrpc.com/connect"
+	"github.com/google/uuid"
+	porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
+	"github.com/porter-dev/porter/api/server/authz"
+	"github.com/porter-dev/porter/api/server/handlers"
+	"github.com/porter-dev/porter/api/server/shared"
+	"github.com/porter-dev/porter/api/server/shared/apierrors"
+	"github.com/porter-dev/porter/api/server/shared/config"
+	"github.com/porter-dev/porter/api/server/shared/requestutils"
+	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/models"
+	"github.com/porter-dev/porter/internal/telemetry"
+)
+
+// Credential has all information about connecting to a datastore
+type Credential struct {
+	Host         string `json:"host"`
+	Port         int    `json:"port"`
+	Username     string `json:"username"`
+	Password     string `json:"password"`
+	DatabaseName string `json:"database_name"`
+}
+
+// GetDatastoreCredentialsResponse describes the datastore credentials response body
+type GetDatastoreCredentialsResponse struct {
+	// Credential is the credential that has been retrieved for this datastore
+	Credential Credential `json:"credential"`
+}
+
+// GetDatastoreCredentialsHandler is a struct for retrieving credentials for datastore
+type GetDatastoreCredentialsHandler struct {
+	handlers.PorterHandlerReadWriter
+	authz.KubernetesAgentGetter
+}
+
+// NewGetDatastoreCredentialsHandler returns a DatastoreCredentialsHandler
+func NewGetDatastoreCredentialsHandler(
+	config *config.Config,
+	decoderValidator shared.RequestDecoderValidator,
+	writer shared.ResultWriter,
+) *GetDatastoreCredentialsHandler {
+	return &GetDatastoreCredentialsHandler{
+		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
+		KubernetesAgentGetter:   authz.NewOutOfClusterAgentGetter(config),
+	}
+}
+
+// ServeHTTP retrieves the credentials for a datastore
+func (c *GetDatastoreCredentialsHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
+	ctx, span := telemetry.NewSpan(r.Context(), "serve-get-datastore-credentials")
+	defer span.End()
+
+	project, _ := ctx.Value(types.ProjectScope).(*models.Project)
+	if project.ID == 0 {
+		err := telemetry.Error(ctx, span, nil, "project not found")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
+		return
+	}
+	projectId := int64(project.ID)
+
+	var resp GetDatastoreCredentialsResponse
+
+	datastoreName, reqErr := requestutils.GetURLParamString(r, types.URLParamDatastoreName)
+	if reqErr != nil {
+		err := telemetry.Error(ctx, span, nil, "error parsing datastore name")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
+		return
+	}
+	telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "datastore-name", Value: datastoreName})
+
+	datastoreRecord, err := c.Repo().Datastore().GetByProjectIDAndName(ctx, project.ID, datastoreName)
+	if err != nil {
+		err = telemetry.Error(ctx, span, err, "datastore record not found")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+		return
+	}
+
+	if datastoreRecord == nil || datastoreRecord.ID == uuid.Nil {
+		err = telemetry.Error(ctx, span, nil, "datastore record does not exist")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
+		return
+	}
+
+	message := porterv1.CreateDatastoreProxyRequest{
+		ProjectId:   projectId,
+		DatastoreId: datastoreRecord.ID.String(),
+	}
+	req := connect.NewRequest(&message)
+	ccpResp, err := c.Config().ClusterControlPlaneClient.CreateDatastoreProxy(ctx, req)
+	if err != nil {
+		err = telemetry.Error(ctx, span, err, "error creating datastore proxy")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+		return
+	}
+	if ccpResp == nil || ccpResp.Msg == nil {
+		err = telemetry.Error(ctx, span, nil, "error creating datastore proxy")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+		return
+	}
+
+	resp = GetDatastoreCredentialsResponse{
+		Credential: Credential{
+			Host:         ccpResp.Msg.Credential.Host,
+			Port:         int(ccpResp.Msg.Credential.Port),
+			Username:     ccpResp.Msg.Credential.Username,
+			Password:     ccpResp.Msg.Credential.Password,
+			DatabaseName: ccpResp.Msg.Credential.DatabaseName,
+		},
+	}
+
+	c.WriteResult(w, r, resp)
+}

+ 100 - 20
api/server/handlers/datastore/get.go

@@ -1,10 +1,14 @@
 package datastore
 
 import (
+	"context"
+	"encoding/base64"
 	"net/http"
 
+	"connectrpc.com/connect"
 	"github.com/aws/aws-sdk-go/aws/arn"
 	"github.com/google/uuid"
+	"github.com/porter-dev/api-contracts/generated/go/helpers"
 	porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
 	"github.com/porter-dev/porter/api/server/authz"
 	"github.com/porter-dev/porter/api/server/handlers"
@@ -14,6 +18,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/server/shared/requestutils"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/datastore"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/internal/telemetry"
 )
@@ -21,7 +26,7 @@ import (
 // GetDatastoreResponse describes the list datastores response body
 type GetDatastoreResponse struct {
 	// Datastore is the datastore that has been retrieved
-	Datastore Datastore `json:"datastore"`
+	Datastore datastore.Datastore `json:"datastore"`
 }
 
 // GetDatastoreHandler is a struct for retrieving a datastore
@@ -76,33 +81,102 @@ func (c *GetDatastoreHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		return
 	}
 
-	datastore := Datastore{
-		Name:                              datastoreRecord.Name,
-		Type:                              datastoreRecord.Type,
-		Engine:                            datastoreRecord.Engine,
-		CreatedAtUTC:                      datastoreRecord.CreatedAt,
-		Status:                            string(datastoreRecord.Status),
-		CloudProvider:                     datastoreRecord.CloudProvider,
-		CloudProviderCredentialIdentifier: datastoreRecord.CloudProviderCredentialIdentifier,
+	// TODO: delete this branch once all datastores are on the management cluster
+	if !datastoreRecord.OnManagementCluster {
+		awsArn, err := arn.Parse(datastoreRecord.CloudProviderCredentialIdentifier)
+		if err != nil {
+			err = telemetry.Error(ctx, span, err, "error parsing aws account id")
+			c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+			return
+		}
+
+		datastore, err := c.LEGACY_handleGetDatastore(ctx, project.ID, awsArn.AccountID, datastoreName)
+		if err != nil {
+			err = telemetry.Error(ctx, span, err, "error retrieving datastore")
+			c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+			return
+		}
+		resp.Datastore = datastore
+		c.WriteResult(w, r, resp)
+		return
 	}
 
-	if datastoreRecord.CloudProvider != SupportedDatastoreCloudProvider_AWS {
-		err = telemetry.Error(ctx, span, nil, "unsupported datastore cloud provider")
-		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
+	req := connect.NewRequest(&porterv1.ReadCloudContractRequest{
+		ProjectId: int64(project.ID),
+	})
+	ccpResp, err := c.Config().ClusterControlPlaneClient.ReadCloudContract(ctx, req)
+	if err != nil {
+		err = telemetry.Error(ctx, span, err, "error getting cloud contract")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
+		return
+	}
+	if ccpResp.Msg == nil {
+		err = telemetry.Error(ctx, span, nil, "cloud contract not found")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusNotFound))
+		return
+	}
+
+	cloudContract := ccpResp.Msg.CloudContract
+	if cloudContract == nil {
+		err = telemetry.Error(ctx, span, nil, "cloud contract is empty")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusNotFound))
 		return
 	}
 
-	awsArn, err := arn.Parse(datastoreRecord.CloudProviderCredentialIdentifier)
+	datastores := cloudContract.Datastores
+	if datastores == nil {
+		err = telemetry.Error(ctx, span, nil, "datastores is empty")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusNotFound))
+		return
+	}
+
+	var matchingDatastore *porterv1.ManagedDatastore
+	for _, ds := range datastores {
+		if ds.Id == datastoreRecord.ID.String() {
+			matchingDatastore = ds
+			break
+		}
+	}
+	if matchingDatastore == nil {
+		err = telemetry.Error(ctx, span, nil, "datastore not found")
+		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusNotFound))
+		return
+	}
+	encoded, err := helpers.MarshalContractObject(ctx, matchingDatastore)
 	if err != nil {
-		err = telemetry.Error(ctx, span, err, "error parsing aws account id")
+		err = telemetry.Error(ctx, span, err, "error marshaling datastore")
 		c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
 		return
 	}
 
+	b64 := base64.StdEncoding.EncodeToString(encoded)
+
+	datastore := datastore.Datastore{
+		Name:                              datastoreRecord.Name,
+		Type:                              datastoreRecord.Type,
+		Engine:                            datastoreRecord.Engine,
+		CreatedAtUTC:                      datastoreRecord.CreatedAt,
+		Status:                            string(datastoreRecord.Status),
+		CloudProvider:                     SupportedDatastoreCloudProvider_AWS,
+		CloudProviderCredentialIdentifier: datastoreRecord.CloudProviderCredentialIdentifier,
+		B64Proto:                          b64,
+	}
+
+	resp.Datastore = datastore
+	c.WriteResult(w, r, resp)
+}
+
+// LEGACY_handleGetDatastore retrieves the datastore in the given project for datastores that are on the customer clusters rather than the management cluster
+func (c *GetDatastoreHandler) LEGACY_handleGetDatastore(ctx context.Context, projectId uint, accountId string, datastoreName string) (datastore.Datastore, error) {
+	ctx, span := telemetry.NewSpan(ctx, "legacy-handle-get-datastore")
+	defer span.End()
+
+	var datastore datastore.Datastore
+
 	datastores, err := Datastores(ctx, DatastoresInput{
-		ProjectID: project.ID,
+		ProjectID: projectId,
 		CloudProvider: cloud_provider.CloudProvider{
-			AccountID: awsArn.AccountID,
+			AccountID: accountId,
 			Type:      porterv1.EnumCloudProvider_ENUM_CLOUD_PROVIDER_AWS,
 		},
 		Name:                datastoreName,
@@ -111,11 +185,17 @@ func (c *GetDatastoreHandler) ServeHTTP(w http.ResponseWriter, r *http.Request)
 		CCPClient:           c.Config().ClusterControlPlaneClient,
 		DatastoreRepository: c.Repo().Datastore(),
 	})
-	if err == nil && len(datastores) == 1 {
-		datastore = datastores[0]
+	if err != nil {
+		return datastore, err
 	}
 
-	resp.Datastore = datastore
+	if len(datastores) != 1 {
+		telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "datastore-count", Value: len(datastores)})
+		if len(datastores) == 0 {
+			return datastore, telemetry.Error(ctx, span, nil, "datastore not found")
+		}
+		return datastore, telemetry.Error(ctx, span, nil, "unexpected number of datastores found matching filters")
+	}
 
-	c.WriteResult(w, r, resp)
+	return datastores[0], nil
 }

+ 26 - 56
api/server/handlers/datastore/list.go

@@ -3,7 +3,6 @@ package datastore
 import (
 	"context"
 	"net/http"
-	"time"
 
 	"connectrpc.com/connect"
 	porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
@@ -16,6 +15,7 @@ import (
 	"github.com/porter-dev/porter/api/server/shared/apierrors"
 	"github.com/porter-dev/porter/api/server/shared/config"
 	"github.com/porter-dev/porter/api/types"
+	"github.com/porter-dev/porter/internal/datastore"
 	"github.com/porter-dev/porter/internal/models"
 	"github.com/porter-dev/porter/internal/repository"
 	"github.com/porter-dev/porter/internal/telemetry"
@@ -40,37 +40,7 @@ type ListDatastoresRequest struct {
 // ListDatastoresResponse describes the list datastores response body
 type ListDatastoresResponse struct {
 	// Datastores is a list of datastore entries for the http response
-	Datastores []Datastore `json:"datastores"`
-}
-
-// Datastore describes an outbound datastores response entry
-type Datastore struct {
-	// Name is the name of the datastore
-	Name string `json:"name"`
-
-	// Type is the type of the datastore
-	Type string `json:"type"`
-
-	// Engine is the engine of the datastore
-	Engine string `json:"engine,omitempty"`
-
-	// Env is the env group for the datastore
-	Env environment_groups.EnvironmentGroupListItem `json:"env,omitempty"`
-
-	// Metadata is a list of metadata objects for the datastore
-	Metadata []*porterv1.DatastoreMetadata `json:"metadata,omitempty"`
-
-	// Status is the status of the datastore
-	Status string `json:"status"`
-
-	// CreatedAtUTC is the time the datastore was created in UTC
-	CreatedAtUTC time.Time `json:"created_at"`
-
-	// CloudProvider is the cloud provider associated with the datastore
-	CloudProvider string `json:"cloud_provider"`
-
-	// CloudProviderCredentialIdentifier is the cloud provider credential identifier associated with the datastore
-	CloudProviderCredentialIdentifier string `json:"cloud_provider_credential_identifier"`
+	Datastores []datastore.Datastore `json:"datastores"`
 }
 
 // ListDatastoresHandler is a struct for listing all datastores for a given project
@@ -99,7 +69,7 @@ func (h *ListDatastoresHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
 	project, _ := ctx.Value(types.ProjectScope).(*models.Project)
 
 	resp := ListDatastoresResponse{}
-	datastoreList := []Datastore{}
+	datastoreList := []datastore.Datastore{}
 
 	datastores, err := h.Repo().Datastore().ListByProjectID(ctx, project.ID)
 	if err != nil {
@@ -108,15 +78,15 @@ func (h *ListDatastoresHandler) ServeHTTP(w http.ResponseWriter, r *http.Request
 		return
 	}
 
-	for _, datastore := range datastores {
-		datastoreList = append(datastoreList, Datastore{
-			Name:                              datastore.Name,
-			Type:                              datastore.Type,
-			Engine:                            datastore.Engine,
-			CreatedAtUTC:                      datastore.CreatedAt,
-			Status:                            string(datastore.Status),
-			CloudProvider:                     datastore.CloudProvider,
-			CloudProviderCredentialIdentifier: datastore.CloudProviderCredentialIdentifier,
+	for _, ds := range datastores {
+		datastoreList = append(datastoreList, datastore.Datastore{
+			Name:                              ds.Name,
+			Type:                              ds.Type,
+			Engine:                            ds.Engine,
+			CreatedAtUTC:                      ds.CreatedAt,
+			Status:                            string(ds.Status),
+			CloudProvider:                     ds.CloudProvider,
+			CloudProviderCredentialIdentifier: ds.CloudProviderCredentialIdentifier,
 		})
 	}
 
@@ -139,7 +109,7 @@ type DatastoresInput struct {
 }
 
 // Datastores returns a list of datastores associated with the specified project/cloud-provider
-func Datastores(ctx context.Context, inp DatastoresInput) ([]Datastore, error) {
+func Datastores(ctx context.Context, inp DatastoresInput) ([]datastore.Datastore, error) {
 	ctx, span := telemetry.NewSpan(ctx, "datastores-for-cloud-provider")
 	defer span.End()
 
@@ -153,7 +123,7 @@ func Datastores(ctx context.Context, inp DatastoresInput) ([]Datastore, error) {
 		telemetry.AttributeKV{Key: "project-id", Value: inp.ProjectID},
 	)
 
-	datastores := []Datastore{}
+	datastores := []datastore.Datastore{}
 
 	if inp.ProjectID == 0 {
 		return datastores, telemetry.Error(ctx, span, nil, "project id must be specified")
@@ -185,30 +155,30 @@ func Datastores(ctx context.Context, inp DatastoresInput) ([]Datastore, error) {
 		return datastores, telemetry.Error(ctx, span, nil, "missing response message from ccp")
 	}
 
-	for _, datastore := range resp.Msg.Datastores {
-		datastoreRecord, err := inp.DatastoreRepository.GetByProjectIDAndName(ctx, inp.ProjectID, datastore.Name)
+	for _, ds := range resp.Msg.Datastores {
+		datastoreRecord, err := inp.DatastoreRepository.GetByProjectIDAndName(ctx, inp.ProjectID, ds.Name)
 		if err != nil {
-			telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "err-datastore-name", Value: datastore.Name})
+			telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "err-datastore-name", Value: ds.Name})
 			return datastores, telemetry.Error(ctx, span, err, "datastore record not found")
 		}
 
-		encodedDatastore := Datastore{
-			Name:                              datastore.Name,
+		encodedDatastore := datastore.Datastore{
+			Name:                              ds.Name,
 			Type:                              datastoreRecord.Type,
 			Engine:                            datastoreRecord.Engine,
 			CreatedAtUTC:                      datastoreRecord.CreatedAt,
 			Status:                            string(datastoreRecord.Status),
-			Metadata:                          datastore.Metadata,
+			Metadata:                          ds.Metadata,
 			CloudProvider:                     datastoreRecord.CloudProvider,
 			CloudProviderCredentialIdentifier: datastoreRecord.CloudProviderCredentialIdentifier,
 		}
-		if inp.IncludeEnvGroup && datastore.Env != nil {
+		if inp.IncludeEnvGroup && ds.Env != nil {
 			encodedDatastore.Env = environment_groups.EnvironmentGroupListItem{
-				Name:               datastore.Env.Name,
-				LatestVersion:      int(datastore.Env.Version),
-				Variables:          datastore.Env.Variables,
-				SecretVariables:    datastore.Env.SecretVariables,
-				LinkedApplications: datastore.Env.LinkedApplications,
+				Name:               ds.Env.Name,
+				LatestVersion:      int(ds.Env.Version),
+				Variables:          ds.Env.Variables,
+				SecretVariables:    ds.Env.SecretVariables,
+				LinkedApplications: ds.Env.LinkedApplications,
 			}
 		}
 		datastores = append(datastores, encodedDatastore)

+ 0 - 104
api/server/handlers/datastore/status.go

@@ -1,104 +0,0 @@
-package datastore
-
-import (
-	"net/http"
-
-	"connectrpc.com/connect"
-	porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
-	"github.com/porter-dev/porter/api/server/authz"
-	"github.com/porter-dev/porter/api/server/handlers"
-	"github.com/porter-dev/porter/api/server/shared"
-	"github.com/porter-dev/porter/api/server/shared/apierrors"
-	"github.com/porter-dev/porter/api/server/shared/config"
-	"github.com/porter-dev/porter/api/types"
-	"github.com/porter-dev/porter/internal/models"
-	"github.com/porter-dev/porter/internal/telemetry"
-)
-
-// StatusRequest describes an inbound datastore status request
-type StatusRequest struct {
-	Type string `json:"type"`
-	Name string `json:"name"`
-}
-
-// StatusResponse describes an outbound datastore status response
-type StatusResponse struct {
-	Status string `json:"status"`
-}
-
-// StatusHandler is a struct for handling datastore status requests
-type StatusHandler struct {
-	handlers.PorterHandlerReadWriter
-	authz.KubernetesAgentGetter
-}
-
-// NewStatusHandler constructs a datastore StatusHandler
-func NewStatusHandler(
-	config *config.Config,
-	decoderValidator shared.RequestDecoderValidator,
-	writer shared.ResultWriter,
-) *StatusHandler {
-	return &StatusHandler{
-		PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
-		KubernetesAgentGetter:   authz.NewOutOfClusterAgentGetter(config),
-	}
-}
-
-func (h *StatusHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
-	ctx, span := telemetry.NewSpan(r.Context(), "serve-datastore-status")
-	defer span.End()
-	// read the project from context
-	project, _ := ctx.Value(types.ProjectScope).(*models.Project)
-	cluster, _ := ctx.Value(types.ClusterScope).(*models.Cluster)
-
-	request := &StatusRequest{}
-	if ok := h.DecodeAndValidate(w, r, request); !ok {
-		err := telemetry.Error(ctx, span, nil, "error decoding request")
-		h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
-		return
-	}
-
-	telemetry.WithAttributes(span,
-		telemetry.AttributeKV{Key: "datastore-name", Value: request.Name},
-		telemetry.AttributeKV{Key: "datastore-type", Value: request.Type},
-	)
-
-	var datastoreType porterv1.EnumDatastore
-	switch request.Type {
-	case "rds-postgresql":
-		datastoreType = porterv1.EnumDatastore_ENUM_DATASTORE_RDS_POSTGRESQL
-	case "rds-postgresql-aurora":
-		datastoreType = porterv1.EnumDatastore_ENUM_DATASTORE_RDS_AURORA_POSTGRESQL
-	case "elasticache-redis":
-		datastoreType = porterv1.EnumDatastore_ENUM_DATASTORE_ELASTICACHE_REDIS
-	default:
-		err := telemetry.Error(ctx, span, nil, "invalid datastore specified")
-		h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
-		return
-	}
-
-	req := connect.NewRequest(&porterv1.DatastoreStatusRequest{
-		ProjectId: int64(project.ID),
-		ClusterId: int64(cluster.ID),
-		Type:      datastoreType,
-		Name:      request.Name,
-	})
-
-	resp, err := h.Config().ClusterControlPlaneClient.DatastoreStatus(ctx, req)
-	if err != nil {
-		err := telemetry.Error(ctx, span, err, "error fetching datastore status from ccp")
-		h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
-		return
-	}
-
-	if resp.Msg == nil {
-		err := telemetry.Error(ctx, span, err, "missing response message from ccp")
-		h.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
-		return
-	}
-
-	telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "datastore-status", Value: resp.Msg.Status})
-	h.WriteResult(w, r, StatusResponse{
-		Status: resp.Msg.Status,
-	})
-}

+ 0 - 29
api/server/router/cluster.go

@@ -319,35 +319,6 @@ func getClusterRoutes(
 		Router:   r,
 	})
 
-	// GET /api/projects/{project_id}/clusters/{cluster_id}/datastore/status -> datastore.NewStatusHandler
-	datastoreStatusEndpoint := factory.NewAPIEndpoint(
-		&types.APIRequestMetadata{
-			Verb:   types.APIVerbList,
-			Method: types.HTTPVerbGet,
-			Path: &types.Path{
-				Parent:       basePath,
-				RelativePath: relPath + "/datastore/status",
-			},
-			Scopes: []types.PermissionScope{
-				types.UserScope,
-				types.ProjectScope,
-				types.ClusterScope,
-			},
-		},
-	)
-
-	datastoreStatusHandler := datastore.NewStatusHandler(
-		config,
-		factory.GetDecoderValidator(),
-		factory.GetResultWriter(),
-	)
-
-	routes = append(routes, &router.Route{
-		Endpoint: datastoreStatusEndpoint,
-		Handler:  datastoreStatusHandler,
-		Router:   r,
-	})
-
 	// GET /api/projects/{project_id}/clusters/{cluster_id}/compliance/checks -> cluster.NewListComplianceChecksHandler
 	listComplianceChecksEndpoint := factory.NewAPIEndpoint(
 		&types.APIRequestMetadata{

+ 1 - 1
cli/cmd/commands/datastore.go

@@ -157,7 +157,7 @@ func datastoreConnect(ctx context.Context, _ *types.GetAuthenticatedUserResponse
 
 	req := config.RestClient.Post().
 		Resource("pods").
-		Namespace(namespace).
+		Namespace(resp.Namespace).
 		Name(proxyPod.Name).
 		SubResource("portforward")
 

+ 0 - 23
dashboard/src/main/home/cluster-dashboard/expanded-chart/ExpandedChart.tsx

@@ -115,26 +115,6 @@ const ExpandedChart: React.FC<Props> = (props) => {
     setOverrideCurrentTab("logs");
   };
 
-  const updateDatabaseStatuses = async (): Promise<void> => {
-    try {
-
-      const statusRes = await api.getDatabaseStatus("<token>", {
-        name: currentChart.name,
-        type: currentChart.chart.metadata.name
-      }, {
-        project_id: currentProject?.id ?? 0,
-        cluster_id: currentCluster?.id ?? 0,
-      });
-      if (statusRes.data.status === "available") {
-        setDatabaseStatus(true);
-      }
-      else {
-        setDatabaseStatus(false);
-      }
-    } catch (err) {
-      setDatabaseStatus(false);
-    }
-  };
   // Retrieve full chart data (includes form and values)
   const getChartData = async (chart: ChartType) => {
     setIsLoadingChartData(true);
@@ -850,9 +830,6 @@ const ExpandedChart: React.FC<Props> = (props) => {
           });
       });
     });
-    if (templateWhitelist.includes(currentChart.chart.metadata.name)) {
-      void updateDatabaseStatuses()
-    }
     return () => {
       closeAllWebsockets();
     };

+ 2 - 3
dashboard/src/main/home/database-dashboard/DatabaseTabs.tsx

@@ -7,7 +7,6 @@ import TabSelector from "components/TabSelector";
 
 import { useDatastoreContext } from "./DatabaseContextProvider";
 import DatastoreProvisioningIndicator from "./DatastoreProvisioningIndicator";
-import ConfigurationTab from "./tabs/ConfigurationTab";
 import ConnectedAppsTab from "./tabs/ConnectedAppsTab";
 import ConnectTab from "./tabs/ConnectTab";
 import MetricsTab from "./tabs/MetricsTab";
@@ -46,7 +45,7 @@ const DatabaseTabs: React.FC<DbTabProps> = ({ tabParam }) => {
     return [
       { label: "Connect", value: "connect" },
       { label: "Connected Apps", value: "connected-apps" },
-      { label: "Configuration", value: "configuration" },
+      // { label: "Configuration", value: "configuration" },
       { label: "Settings", value: "settings" },
     ];
   }, []);
@@ -70,7 +69,7 @@ const DatabaseTabs: React.FC<DbTabProps> = ({ tabParam }) => {
         .with("connect", () => <ConnectTab />)
         .with("settings", () => <SettingsTab />)
         .with("metrics", () => <MetricsTab />)
-        .with("configuration", () => <ConfigurationTab />)
+        // .with("configuration", () => <ConfigurationTab />)
         .with("connected-apps", () => <ConnectedAppsTab />)
         .otherwise(() => null)}
       <Spacer y={2} />

+ 0 - 14
dashboard/src/shared/api.tsx

@@ -1979,19 +1979,6 @@ const updateDatabaseStatus = baseApi<
 >("POST", (pathParams) => {
   return `/api/projects/${pathParams.project_id}/infras/${pathParams.infra_id}/database`;
 });
-// GET /api/projects/{project_id}/clusters/{cluster_id}/datastore/status
-const getDatabaseStatus = baseApi<
-  {
-    name: string;
-    type: string;
-  },
-  {
-    project_id: number;
-    cluster_id: number;
-  }
->("GET", (pathParams) => {
-  return `/api/projects/${pathParams.project_id}/clusters/${pathParams.cluster_id}/datastore/status`;
-});
 
 const getRepoIntegrations = baseApi("GET", "/api/integrations/repo");
 
@@ -3786,7 +3773,6 @@ export default {
 
   // STATUS
   getGithubStatus,
-  getDatabaseStatus,
   getCloudProviderPermissionsStatus,
 
   getCloudSqlSecret,

+ 1 - 1
go.mod

@@ -83,7 +83,7 @@ require (
 	github.com/matryer/is v1.4.0
 	github.com/nats-io/nats.go v1.24.0
 	github.com/open-policy-agent/opa v0.44.0
-	github.com/porter-dev/api-contracts v0.2.123
+	github.com/porter-dev/api-contracts v0.2.124
 	github.com/riandyrn/otelchi v0.5.1
 	github.com/santhosh-tekuri/jsonschema/v5 v5.0.1
 	github.com/stefanmcshane/helm v0.0.0-20221213002717-88a4a2c6e77d

+ 2 - 0
go.sum

@@ -1525,6 +1525,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
 github.com/polyfloyd/go-errorlint v0.0.0-20210722154253-910bb7978349/go.mod h1:wi9BfjxjF/bwiZ701TzmfKu6UKC357IOAtNr0Td0Lvw=
 github.com/porter-dev/api-contracts v0.2.123 h1:bDtyC2ueirKmu9NN1YEClv2qVrMjvu913HGibG7ISRQ=
 github.com/porter-dev/api-contracts v0.2.123/go.mod h1:fX6JmP5QuzxDLvqP3evFOTXjI4dHxsG0+VKNTjImZU8=
+github.com/porter-dev/api-contracts v0.2.124 h1:0ChXriR88KanBMMJfDWIabEvPqt9eLsmOScDbuJucBQ=
+github.com/porter-dev/api-contracts v0.2.124/go.mod h1:fX6JmP5QuzxDLvqP3evFOTXjI4dHxsG0+VKNTjImZU8=
 github.com/porter-dev/switchboard v0.0.3 h1:dBuYkiVLa5Ce7059d6qTe9a1C2XEORFEanhbtV92R+M=
 github.com/porter-dev/switchboard v0.0.3/go.mod h1:xSPzqSFMQ6OSbp42fhCi4AbGbQbsm6nRvOkrblFeXU4=
 github.com/posener/complete v1.1.1/go.mod h1:em0nMJCgc9GFtwrmVmEMR/ZL6WyhyjMBndrE9hABlRI=

+ 32 - 0
internal/datastore/datastore.go

@@ -0,0 +1,32 @@
+package datastore
+
+import (
+	"time"
+
+	porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
+	"github.com/porter-dev/porter/api/server/handlers/environment_groups"
+)
+
+// Datastore describes an outbound datastores response entry
+type Datastore struct {
+	// Name is the name of the datastore
+	Name string `json:"name"`
+	// Type is the type of the datastore
+	Type string `json:"type"`
+	// Engine is the engine of the datastore
+	Engine string `json:"engine,omitempty"`
+	// Env is the env group for the datastore
+	Env environment_groups.EnvironmentGroupListItem `json:"env,omitempty"`
+	// Metadata is a list of metadata objects for the datastore - TODO: remove this field, it is unnecessary
+	Metadata []*porterv1.DatastoreMetadata `json:"metadata,omitempty"`
+	// Status is the status of the datastore
+	Status string `json:"status"`
+	// CreatedAtUTC is the time the datastore was created in UTC
+	CreatedAtUTC time.Time `json:"created_at"`
+	// CloudProvider is the cloud provider associated with the datastore
+	CloudProvider string `json:"cloud_provider"`
+	// CloudProviderCredentialIdentifier is the cloud provider credential identifier associated with the datastore
+	CloudProviderCredentialIdentifier string `json:"cloud_provider_credential_identifier"`
+	// B64Proto is the base64 encoded datastore proto. Note that this is only populated for datastores created with the new cloud contract flow
+	B64Proto string `json:"b64_proto"`
+}