Skip to content
This repository has been archived by the owner on Jul 12, 2023. It is now read-only.

Create e2e-runner service. #417

Merged
merged 5 commits into from
Aug 30, 2020
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,12 @@ cmd/add-users/add-users
cmd/adminapi/adminapi
cmd/apiserver/apiserver
cmd/cleanup/cleanup
cmd/e2e-runner/e2e-runner
cmd/get-certificate/get-certificate
cmd/get-code/get-code
cmd/get-token/get-token
cmd/migrate/migrate
cmd/server/server
tools/e2e-test/e2e-test
tools/gen-secret/gen-secret
tools/seed/seed

Expand Down
66 changes: 12 additions & 54 deletions tools/e2e-test/main.go → cmd/e2e-runner/e2e.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,46 +12,28 @@
// See the License for the specific language governing permissions and
// limitations under the License.

// Command line test that exercises the verification and key server,
// simulating a mobile device uploading TEKs.
//
// E2E test code that exercises the verification and key server, simulating a
// mobile device uploading TEKs.
//
package main

import (
"context"
"encoding/base64"
"flag"
"fmt"
"net/http"
"os"
"strconv"
"time"

"github.com/google/exposure-notifications-verification-server/pkg/clients"
"github.com/google/exposure-notifications-verification-server/pkg/config"
"github.com/google/exposure-notifications-verification-server/pkg/jsonclient"

verifyapi "github.com/google/exposure-notifications-server/pkg/api/v1"
"github.com/google/exposure-notifications-server/pkg/logging"
"github.com/google/exposure-notifications-server/pkg/util"
"github.com/google/exposure-notifications-server/pkg/verification"

"github.com/sethvargo/go-envconfig"
"github.com/sethvargo/go-signalcontext"
)

type Config struct {
VerificationAdminAPIServer string `env:"VERIFICATION_ADMIN_API, default=http://localhost:8081"`
VerificationAdminAPIKey string `env:"VERIFICATION_ADMIN_API_KEY,required"`
VerificationAPIServer string `env:"VERIFICATION_SERVER_API, default=http://localhost:8082"`
VerificationAPIServerKey string `env:"VERIFICATION_SERVER_API_KEY,required"`
KeyServer string `env:"KEY_SERVER, default=http://localhost:8080"`
HealthAuthorityCode string `env:"HEALTH_AUTHORITY_CODE,required"`

// Publish config
Region string `env:"REGION,default=US"`
}

const (
timeout = 2 * time.Second
oneDay = 24 * time.Hour
Expand All @@ -63,36 +45,12 @@ func timeToInterval(t time.Time) int32 {
return int32(t.UTC().Truncate(oneDay).Unix() / int64(intervalLength.Seconds()))
}

func main() {
ctx, done := signalcontext.OnInterrupt()

debug, _ := strconv.ParseBool(os.Getenv("LOG_DEBUG"))
logger := logging.NewLogger(debug)
ctx = logging.WithLogger(ctx, logger)

err := realMain(ctx)
done()

if err != nil {
logger.Fatal(err)
}
logger.Info("successful shutdown")
}

func realMain(ctx context.Context) error {
func e2e(ctx context.Context, config config.E2ERunnerConfig) error {
logger := logging.FromContext(ctx)
var config Config
if err := envconfig.ProcessWith(ctx, &config, envconfig.OsLookuper()); err != nil {
return fmt.Errorf("unable to process environment: %w", err)
}

doRevision := flag.Bool("revise", false, "--revise means to do a likely diagnosis and then revise to confirmed. one new key is added in between.")
verbose := flag.Bool("v", false, "ALL THE MESSAGES!")
flag.Parse()

testType := "confirmed"
iterations := 1
if *doRevision {
if config.DoRevise {
testType = "likely"
iterations++
}
Expand Down Expand Up @@ -122,7 +80,7 @@ func realMain(ctx context.Context) error {
} else if code.Error != "" {
return fmt.Errorf("issue API Error: %+v", code)
}
if *verbose {
if config.Verbose {
yegle marked this conversation as resolved.
Show resolved Hide resolved
logger.Infof("Code Request: %+v", codeRequest)
logger.Infof("Code Response: %+v", code)
}
Expand All @@ -135,7 +93,7 @@ func realMain(ctx context.Context) error {
} else if token.Error != "" {
return fmt.Errorf("verification API Error %+v", token)
}
if *verbose {
if config.Verbose {
logger.Infof("Token Request: %+v", tokenRequest)
logger.Infof("Token Response: %+v", token)
}
Expand All @@ -147,7 +105,7 @@ func realMain(ctx context.Context) error {
} else if codeStatus.Error != "" {
return fmt.Errorf("check code status Error: %+v", codeStatus)
}
if *verbose {
if config.Verbose {
logger.Infof("Code Status Request: %+v", statusReq)
logger.Infof("Code Status Response: %+v", codeStatus)
}
Expand All @@ -169,7 +127,7 @@ func realMain(ctx context.Context) error {
} else if certificate.Error != "" {
return fmt.Errorf("certificate API Error: %+v", certificate)
}
if *verbose {
if config.Verbose {
logger.Infof("Certificate Request: %+v", certRequest)
logger.Infof("Certificate Response: %+v", certificate)
}
Expand All @@ -189,7 +147,7 @@ func realMain(ctx context.Context) error {
client := &http.Client{
Timeout: timeout,
}
if *verbose {
if config.Verbose {
logger.Infof("Publish request: %+v", publish)
}
if err := jsonclient.MakeRequest(ctx, client, config.KeyServer, http.Header{}, &publish, &response); err != nil {
Expand All @@ -198,11 +156,11 @@ func realMain(ctx context.Context) error {
return fmt.Errorf("publish API error: %+v", response)
}
logger.Infof("Inserted %v exposures", response.InsertedExposures)
if *verbose {
if config.Verbose {
logger.Infof("Publish response: %+v", response)
}

if *doRevision {
if config.DoRevise {
testType = "confirmed"
revisionToken = response.RevisionToken

Expand Down
173 changes: 173 additions & 0 deletions cmd/e2e-runner/main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// This server is a simple webserver that triggers the e2e-test binary.
package main

import (
"context"
"fmt"
"math/rand"
"net/http"
"os"
"strconv"
"time"

"github.com/google/exposure-notifications-server/pkg/logging"
"github.com/google/exposure-notifications-server/pkg/server"
"github.com/google/exposure-notifications-verification-server/pkg/config"
"github.com/google/exposure-notifications-verification-server/pkg/database"

"github.com/gorilla/handlers"
"github.com/gorilla/mux"
"github.com/sethvargo/go-signalcontext"
)

const (
realmName = "e2e-test-realm"
realmRegionCode = "e2e-test"
adminKeyName = "e2e-admin-key."
deviceKeyName = "e2e-device-key."
)

func main() {
ctx, done := signalcontext.OnInterrupt()

debug, _ := strconv.ParseBool(os.Getenv("LOG_DEBUG"))
logger := logging.NewLogger(debug)
ctx = logging.WithLogger(ctx, logger)

err := realMain(ctx)
done()

if err != nil {
logger.Fatal(err)
}
logger.Info("successful shutdown")
}

func randomString() string {
rand.Seed(time.Now().Unix())
return fmt.Sprintf("%x", rand.Int63())
}

func realMain(ctx context.Context) error {
logger := logging.FromContext(ctx)

// load configs
e2eConfig, err := config.NewE2ERunnerConfig(ctx)
if err != nil {
return fmt.Errorf("failed to process e2e-runner config: %w", err)
}

yegle marked this conversation as resolved.
Show resolved Hide resolved
db, err := e2eConfig.Database.Load(ctx)
if err != nil {
return fmt.Errorf("failed to load database config: %w", err)
}
if err := db.Open(ctx); err != nil {
return fmt.Errorf("failed to connect to database: %w", err)
}
defer db.Close()

// Create or reuse the existing realm
realm, err := db.FindRealmByName(realmName)
if err != nil {
if !database.IsNotFound(err) {
return fmt.Errorf("error when finding the realm: %w", err)
}
realm = database.NewRealmWithDefaults(realmName)
realm.RegionCode = realmRegionCode
if err := db.SaveRealm(realm); err != nil {
return fmt.Errorf("failed to create realm: %w: %v", err, realm.ErrorMessages())
yegle marked this conversation as resolved.
Show resolved Hide resolved
}
}

// Create new API keys
suffix := randomString()

adminKey, err := realm.CreateAuthorizedApp(db, &database.AuthorizedApp{
Name: adminKeyName + suffix,
APIKeyType: database.APIUserTypeAdmin,
})
if err != nil {
return fmt.Errorf("error trying to create a new Admin API Key: %w", err)
}

defer func() {
app, err := db.FindAuthorizedAppByAPIKey(adminKey)
if err != nil {
logger.Errorf("admin API key cleanup failed: %w", err)
}
if err := app.Disable(db); err != nil {
logger.Errorf("admin API key disable failed: %w", err)
}
logger.Info("successfully cleaned up e2e test admin key")
}()

deviceKey, err := realm.CreateAuthorizedApp(db, &database.AuthorizedApp{
Name: deviceKeyName + suffix,
APIKeyType: database.APIUserTypeDevice,
})
if err != nil {
return fmt.Errorf("error trying to create a new Device API Key: %w", err)
}

defer func() {
app, err := db.FindAuthorizedAppByAPIKey(deviceKey)
if err != nil {
logger.Errorf("device API key cleanup failed: %w", err)
}
if err := app.Disable(db); err != nil {
logger.Errorf("device API key disable failed: %w", err)
}
logger.Info("successfully cleaned up e2e test device key")
}()

e2eConfig.VerificationAdminAPIKey = adminKey
e2eConfig.VerificationAPIServerKey = deviceKey

// Create the router
r := mux.NewRouter()
r.HandleFunc("/default", defaultHandler(ctx, *e2eConfig))
r.HandleFunc("/revise", reviseHandler(ctx, *e2eConfig))

srv, err := server.New(e2eConfig.Port)
if err != nil {
return fmt.Errorf("failed to create server: %w", err)
}
logger.Infow("server listening", "port", e2eConfig.Port)
return srv.ServeHTTPHandler(ctx, handlers.CombinedLoggingHandler(os.Stdout, r))
}

func defaultHandler(ctx context.Context, c config.E2ERunnerConfig) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
c.DoRevise = false
if err := e2e(ctx, c); err != nil {
http.Error(w, "failed (check server logs for more details): "+err.Error(), http.StatusInternalServerError)
} else {
fmt.Fprint(w, "ok")
}
}
}

func reviseHandler(ctx context.Context, c config.E2ERunnerConfig) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
c.DoRevise = true
if err := e2e(ctx, c); err != nil {
http.Error(w, "failed (check server logs for more details): "+err.Error(), http.StatusInternalServerError)
} else {
fmt.Fprint(w, "ok")
}
}
}
59 changes: 59 additions & 0 deletions pkg/config/e2e_runner_server_config.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
// Copyright 2020 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package config

import (
"context"

"github.com/google/exposure-notifications-verification-server/pkg/database"
"github.com/sethvargo/go-envconfig"
yegle marked this conversation as resolved.
Show resolved Hide resolved
)

// E2ERunnerConfig represents the environment based configuration for the e2e-runner server.
type E2ERunnerConfig struct {
Database database.Config

// DevMode produces additional debugging information. Do not enable in
// production environments.
DevMode bool `env:"DEV_MODE"`

Port string `env:"PORT,default=8080"`

// e2e-runner config
VerificationAdminAPIServer string `env:"VERIFICATION_ADMIN_API, default=http://localhost:8081"`
VerificationAdminAPIKey string
VerificationAPIServer string `env:"VERIFICATION_SERVER_API, default=http://localhost:8082"`
VerificationAPIServerKey string
KeyServer string `env:"KEY_SERVER, default=http://localhost:8080"`
HealthAuthorityCode string `env:"HEALTH_AUTHORITY_CODE,required"`

// e2e-test config
DoRevise bool
Verbose bool
}

// NewE2ERunnerConfig returns the environment config for the e2e-runner server.
// Only needs to be called once per instance, but may be called multiple times.
func NewE2ERunnerConfig(ctx context.Context) (*E2ERunnerConfig, error) {
var config E2ERunnerConfig
if err := ProcessWith(ctx, &config, envconfig.OsLookuper()); err != nil {
return nil, err
}
return &config, nil
}

func (c *E2ERunnerConfig) Validate() error {
return nil
}
Loading