From 2506813d1a2ded886c4f3ea4f7410f4e4c7d59a1 Mon Sep 17 00:00:00 2001 From: Seth Vargo Date: Mon, 22 Feb 2021 11:58:46 -0500 Subject: [PATCH] Make e2e-runner publish a real client (#1850) This gives us all the JSON parsing + automatic fallback and nice error messages when something fails. This commit changes the required configuration for `KEY_SERVER` from the full URL to the /publish endpoint to *just* the URL to the key server. Where previously you may have configured `KEY_SERVER=https://foo.bar/v1/publish`, please re-configure with `KEY_SERVER=https://foo.bar`. The system attempts to maintain backwards compatibility by parsing the URL, but this may be removed at a later date. --- cmd/e2e-runner/main.go | 4 +-- internal/clients/e2e.go | 46 +++++++++------------------------- internal/clients/key_server.go | 22 ++++++++++++++++ terraform/services.tf | 2 +- 4 files changed, 37 insertions(+), 37 deletions(-) diff --git a/cmd/e2e-runner/main.go b/cmd/e2e-runner/main.go index 68fba2f0e..7d8e0e412 100644 --- a/cmd/e2e-runner/main.go +++ b/cmd/e2e-runner/main.go @@ -192,7 +192,7 @@ func handleEndToEnd(cfg *config.E2ERunnerConfig, h *render.Renderer) http.Handle ctx := r.Context() if err := clients.RunEndToEnd(ctx, cfg); err != nil { - renderJSONError(w, r, h, err) + renderJSONError(w, r, h, fmt.Errorf("failed to run end-to-end: %w", err)) return } @@ -213,7 +213,7 @@ func handleENXRedirect(client *clients.ENXRedirectClient, h *render.Renderer) ht ctx := r.Context() if err := client.RunE2E(ctx); err != nil { - renderJSONError(w, r, h, err) + renderJSONError(w, r, h, fmt.Errorf("failed to run enx-redirect: %w", err)) return } diff --git a/internal/clients/e2e.go b/internal/clients/e2e.go index 57dc9b715..70b114e5c 100644 --- a/internal/clients/e2e.go +++ b/internal/clients/e2e.go @@ -15,13 +15,10 @@ package clients import ( - "bytes" "context" "crypto/rand" "encoding/base64" - "encoding/json" "fmt" - "net/http" "time" verifyapi "github.com/google/exposure-notifications-server/pkg/api/v1" @@ -87,13 +84,19 @@ func RunEndToEnd(ctx context.Context, cfg *config.E2ERunnerConfig) error { adminAPIClient, err := NewAdminAPIServerClient(cfg.VerificationAdminAPIServer, cfg.VerificationAdminAPIKey, WithTimeout(timeout)) if err != nil { - return err + return fmt.Errorf("failed to make adminapi server client: %w", err) } apiServerClient, err := NewAPIServerClient(cfg.VerificationAPIServer, cfg.VerificationAPIServerKey, WithTimeout(timeout)) if err != nil { - return err + return fmt.Errorf("failed to make apiserver client: %w", err) + } + + keyServerClient, err := NewKeyServerClient(cfg.KeyServer, + WithTimeout(timeout)) + if err != nil { + return fmt.Errorf("failed to make keyserver client: %w", err) } testType := "confirmed" @@ -256,6 +259,7 @@ func RunEndToEnd(ctx context.Context, cfg *config.E2ERunnerConfig) error { defer recordLatency(ctx, time.Now(), "upload_to_key_server") // Make the publish request. logger.Infof("Publish TEKs to the key server") + publishReq := &verifyapi.Publish{ Keys: teks, HealthAuthorityID: cfg.HealthAuthorityCode, @@ -263,41 +267,15 @@ func RunEndToEnd(ctx context.Context, cfg *config.E2ERunnerConfig) error { HMACKey: base64.StdEncoding.EncodeToString(hmacSecret), RevisionToken: revisionToken, } - - client := &http.Client{ - Timeout: timeout, - } - - var b bytes.Buffer - if err := json.NewEncoder(&b).Encode(publishReq); err != nil { - return nil, err - } - - httpReq, err := http.NewRequestWithContext(ctx, http.MethodPost, cfg.KeyServer, &b) - if err != nil { - return nil, err - } - httpReq.Header.Set("Content-Type", "application/json") - - httpResp, err := client.Do(httpReq) - if err != nil { - result = enobs.ResultNotOK - return nil, fmt.Errorf("error making request to publish teks: %w", err) - } - defer httpResp.Body.Close() - - var publishResp verifyapi.PublishResponse - if err := json.NewDecoder(httpResp.Body).Decode(&publishResp); err != nil { - return nil, err - } + publishResp, err := keyServerClient.Publish(ctx, publishReq) defer logger.Debugw("publish", "request", publishReq, "response", publishResp) if publishResp.ErrorMessage != "" { result = enobs.ResultNotOK - logger.Infow("failed to publish teks", "error", err, "keys", teks) + logger.Errorw("failed to publish teks", "error", err, "keys", teks) return nil, fmt.Errorf("publish API error: %+v", publishResp) } logger.Infof("Inserted %v exposures", publishResp.InsertedExposures) - return &publishResp, nil + return publishResp, nil }() if err != nil { return err diff --git a/internal/clients/key_server.go b/internal/clients/key_server.go index 755bfccc8..2a62eb268 100644 --- a/internal/clients/key_server.go +++ b/internal/clients/key_server.go @@ -18,6 +18,7 @@ import ( "context" "fmt" "net/http" + "strings" keyserver "github.com/google/exposure-notifications-server/pkg/api/v1" ) @@ -34,11 +35,32 @@ func NewKeyServerClient(base string, opts ...Option) (*KeyServerClient, error) { return nil, err } + // To maintain backwards-compatibility with older implementations that wanted + // KEY_SERVER as the full URL to the publish endpoint, strip off anything + // after /v1. + if idx := strings.Index(client.baseURL.Path, "/v1"); idx != -1 { + client.baseURL.Path = client.baseURL.Path[0:idx] + } + return &KeyServerClient{ client: client, }, nil } +// Publish uploads TEKs to the key server +func (c *KeyServerClient) Publish(ctx context.Context, in *keyserver.Publish) (*keyserver.PublishResponse, error) { + req, err := c.newRequest(ctx, http.MethodPost, "/v1/publish", in) + if err != nil { + return nil, err + } + + var out keyserver.PublishResponse + if err := c.doOK(req, &out); err != nil { + return &out, err + } + return &out, nil +} + // Stats calls the /v1/stats endpoint to get key-server statistics. func (c *KeyServerClient) Stats(ctx context.Context, in *keyserver.StatsRequest, authToken string) (*keyserver.StatsResponse, error) { req, err := c.newRequest(ctx, http.MethodPost, "/v1/stats", in) diff --git a/terraform/services.tf b/terraform/services.tf index 52bd0969c..a7d9bf78e 100644 --- a/terraform/services.tf +++ b/terraform/services.tf @@ -94,7 +94,7 @@ locals { e2e_runner_config = { HEALTH_AUTHORITY_CODE = "com.example" - KEY_SERVER = "https://example.com/v1/publish" + KEY_SERVER = "https://example.com" VERIFICATION_ADMIN_API = local.enable_lb ? "https://${var.adminapi_hosts[0]}" : google_cloud_run_service.adminapi.status.0.url VERIFICATION_SERVER_API = local.enable_lb ? "https://${var.apiserver_hosts[0]}" : google_cloud_run_service.apiserver.status.0.url E2E_SKIP_SMS = var.e2e_skip_sms