cloudsql.go 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. package porter_app
  2. import (
  3. "encoding/base64"
  4. "fmt"
  5. "net/http"
  6. k8serrors "k8s.io/apimachinery/pkg/api/errors"
  7. v1 "k8s.io/api/core/v1"
  8. metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
  9. "github.com/porter-dev/porter/api/server/authz"
  10. "github.com/porter-dev/porter/api/server/handlers"
  11. "github.com/porter-dev/porter/api/server/shared"
  12. "github.com/porter-dev/porter/api/server/shared/apierrors"
  13. "github.com/porter-dev/porter/api/server/shared/config"
  14. "github.com/porter-dev/porter/api/server/shared/requestutils"
  15. "github.com/porter-dev/porter/api/types"
  16. "github.com/porter-dev/porter/internal/telemetry"
  17. )
  18. // GetCloudSqlSecretHandler is a handler to get the cloudsql secret
  19. type GetCloudSqlSecretHandler struct {
  20. handlers.PorterHandlerReadWriter
  21. authz.KubernetesAgentGetter
  22. }
  23. // NewGetCloudSqlSecretHandler returns a GetCloudSqlSecretHandler
  24. func NewGetCloudSqlSecretHandler(
  25. config *config.Config,
  26. writer shared.ResultWriter,
  27. ) *GetCloudSqlSecretHandler {
  28. return &GetCloudSqlSecretHandler{
  29. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, nil, writer),
  30. KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
  31. }
  32. }
  33. // GetCloudSqlSecretResponse is the response payload for the GetCloudSqlSecretHandler
  34. type GetCloudSqlSecretResponse struct {
  35. SecretName string `json:"secret_name"`
  36. }
  37. // ServeHTTP retrieves the cloudsql secret
  38. func (c *GetCloudSqlSecretHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  39. ctx := r.Context()
  40. ctx, span := telemetry.NewSpan(ctx, "serve-get-cloudsql-secret")
  41. defer span.End()
  42. deploymentTarget, _ := ctx.Value(types.DeploymentTargetScope).(types.DeploymentTarget)
  43. appName, reqErr := requestutils.GetURLParamString(r, types.URLParamPorterAppName)
  44. if reqErr != nil {
  45. err := telemetry.Error(ctx, span, nil, "error parsing porter app name")
  46. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  47. return
  48. }
  49. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "application-name", Value: appName})
  50. cluster, err := c.Repo().Cluster().ReadCluster(deploymentTarget.ProjectID, deploymentTarget.ClusterID)
  51. if err != nil {
  52. err = telemetry.Error(ctx, span, err, "error reading cluster")
  53. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  54. return
  55. }
  56. agent, err := c.GetAgent(r, cluster, deploymentTarget.Namespace)
  57. if err != nil {
  58. err = telemetry.Error(ctx, span, err, "error getting agent")
  59. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  60. return
  61. }
  62. secret, err := agent.GetSecret(fmt.Sprintf("cloudsql-secret-%s", appName), deploymentTarget.Namespace)
  63. if err != nil && !k8serrors.IsNotFound(err) {
  64. err = telemetry.Error(ctx, span, err, "error getting secret")
  65. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  66. return
  67. }
  68. var secretName string
  69. if secret != nil {
  70. secretName = secret.Name
  71. }
  72. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "secret-name", Value: secretName})
  73. c.WriteResult(w, r, GetCloudSqlSecretResponse{SecretName: secretName})
  74. }
  75. // CreateCloudSqlSecretHandler is a handler to create the cloudsql secret
  76. type CreateCloudSqlSecretHandler struct {
  77. handlers.PorterHandlerReadWriter
  78. authz.KubernetesAgentGetter
  79. }
  80. // NewCreateCloudSqlSecretHandler returns a CreateCloudSqlSecretHandler
  81. func NewCreateCloudSqlSecretHandler(
  82. config *config.Config,
  83. decoderValidator shared.RequestDecoderValidator,
  84. writer shared.ResultWriter,
  85. ) *CreateCloudSqlSecretHandler {
  86. return &CreateCloudSqlSecretHandler{
  87. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  88. KubernetesAgentGetter: authz.NewOutOfClusterAgentGetter(config),
  89. }
  90. }
  91. // CreateCloudSqlSecretRequest is the request payload for the CreateCloudSqlSecretHandler
  92. type CreateCloudSqlSecretRequest struct {
  93. B64ServiceAccountJson string `json:"b64_service_account_json"`
  94. }
  95. // ServeHTTP creates the cloudsql secret
  96. func (c *CreateCloudSqlSecretHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  97. ctx := r.Context()
  98. ctx, span := telemetry.NewSpan(ctx, "serve-create-cloudsql-secret")
  99. defer span.End()
  100. deploymentTarget, _ := ctx.Value(types.DeploymentTargetScope).(types.DeploymentTarget)
  101. appName, reqErr := requestutils.GetURLParamString(r, types.URLParamPorterAppName)
  102. if reqErr != nil {
  103. err := telemetry.Error(ctx, span, nil, "error parsing porter app name")
  104. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  105. return
  106. }
  107. telemetry.WithAttributes(span, telemetry.AttributeKV{Key: "application-name", Value: appName})
  108. request := &CreateCloudSqlSecretRequest{}
  109. if ok := c.DecodeAndValidate(w, r, request); !ok {
  110. err := telemetry.Error(ctx, span, nil, "error decoding request")
  111. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  112. return
  113. }
  114. cluster, err := c.Repo().Cluster().ReadCluster(deploymentTarget.ProjectID, deploymentTarget.ClusterID)
  115. if err != nil {
  116. err = telemetry.Error(ctx, span, err, "error reading cluster")
  117. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  118. return
  119. }
  120. agent, err := c.GetAgent(r, cluster, deploymentTarget.Namespace)
  121. if err != nil {
  122. err = telemetry.Error(ctx, span, err, "error getting agent")
  123. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  124. return
  125. }
  126. decoded, err := base64.StdEncoding.DecodeString(request.B64ServiceAccountJson)
  127. if err != nil {
  128. err = telemetry.Error(ctx, span, err, "error decoding base64 service account json")
  129. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusBadRequest))
  130. return
  131. }
  132. secret := &v1.Secret{
  133. ObjectMeta: metav1.ObjectMeta{
  134. Name: fmt.Sprintf("cloudsql-secret-%s", appName),
  135. },
  136. Data: map[string][]byte{
  137. "service_account.json": decoded,
  138. },
  139. }
  140. _, err = agent.CreateSecret(secret, deploymentTarget.Namespace)
  141. if err != nil {
  142. err = telemetry.Error(ctx, span, err, "error creating secret")
  143. c.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  144. return
  145. }
  146. }