2
0

preflight.go 2.9 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. package api_contract
  2. import (
  3. "net/http"
  4. "connectrpc.com/connect"
  5. "github.com/porter-dev/api-contracts/generated/go/helpers"
  6. porterv1 "github.com/porter-dev/api-contracts/generated/go/porter/v1"
  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/internal/telemetry"
  12. )
  13. // PreflightCheckHandler runs preflight checks on a cluster contract
  14. type PreflightCheckHandler struct {
  15. handlers.PorterHandlerReadWriter
  16. }
  17. // NewPreflightCheckHandler returns a new PreflightCheckHandler
  18. func NewPreflightCheckHandler(
  19. config *config.Config,
  20. decoderValidator shared.RequestDecoderValidator,
  21. writer shared.ResultWriter,
  22. ) *PreflightCheckHandler {
  23. return &PreflightCheckHandler{
  24. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  25. }
  26. }
  27. // PorterError is the error response for the preflight check endpoint
  28. type PorterError struct {
  29. Code string `json:"code"`
  30. Message string `json:"message"`
  31. Metadata map[string]string `json:"metadata,omitempty"`
  32. }
  33. // PreflightCheckError is the error response for the preflight check endpoint
  34. type PreflightCheckError struct {
  35. Name string `json:"name"`
  36. Error PorterError `json:"error"`
  37. }
  38. // PreflightCheckResponse is the response to the preflight check endpoint
  39. type PreflightCheckResponse struct {
  40. Errors []PreflightCheckError `json:"errors"`
  41. }
  42. func (p *PreflightCheckHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  43. ctx, span := telemetry.NewSpan(r.Context(), "serve-preflight-checks")
  44. defer span.End()
  45. var apiContract porterv1.Contract
  46. err := helpers.UnmarshalContractObjectFromReader(r.Body, &apiContract)
  47. if err != nil {
  48. e := telemetry.Error(ctx, span, err, "error parsing api contract")
  49. p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(e, http.StatusBadRequest))
  50. return
  51. }
  52. var resp PreflightCheckResponse
  53. req := porterv1.CloudContractPreflightCheckRequest{
  54. Contract: &apiContract,
  55. }
  56. checkResp, err := p.Config().ClusterControlPlaneClient.CloudContractPreflightCheck(ctx, connect.NewRequest(&req))
  57. if err != nil {
  58. err = telemetry.Error(ctx, span, err, "error calling preflight checks")
  59. p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  60. return
  61. }
  62. if checkResp.Msg == nil {
  63. err = telemetry.Error(ctx, span, nil, "no message received from preflight checks")
  64. p.HandleAPIError(w, r, apierrors.NewErrPassThroughToClient(err, http.StatusInternalServerError))
  65. return
  66. }
  67. errors := []PreflightCheckError{}
  68. for _, check := range checkResp.Msg.FailingPreflightChecks {
  69. errors = append(errors, PreflightCheckError{
  70. Name: check.Type,
  71. Error: PorterError{
  72. Message: check.Message,
  73. Metadata: check.Metadata,
  74. },
  75. })
  76. }
  77. resp.Errors = errors
  78. p.WriteResult(w, r, resp)
  79. }