Skip to content

Commit

Permalink
Merge pull request #291 from COS301-SE-2024/profile/backend/profiling
Browse files Browse the repository at this point in the history
Profile/backend/profiling
  • Loading branch information
waveyboym authored Aug 10, 2024
2 parents 98d4e47 + e6fa2a6 commit 5a513fd
Show file tree
Hide file tree
Showing 32 changed files with 868 additions and 170 deletions.
12 changes: 11 additions & 1 deletion .github/workflows/lint-test-build-golang.yml
Original file line number Diff line number Diff line change
Expand Up @@ -113,14 +113,24 @@ jobs:
- name: 🧪 Run tests
run: |
gotestsum --format testname -- -v -coverpkg=github.com/COS301-SE-2024/occupi/occupi-backend/pkg/authenticator,github.com/COS301-SE-2024/occupi/occupi-backend/pkg/cache,github.com/COS301-SE-2024/occupi/occupi-backend/pkg/database,github.com/COS301-SE-2024/occupi/occupi-backend/pkg/middleware,github.com/COS301-SE-2024/occupi/occupi-backend/pkg/utils ./tests/... -coverprofile=coverage.out
gotestsum --format testname --junitfile tmp/test-results/gotestsum-report.xml -- -v -coverpkg=github.com/COS301-SE-2024/occupi/occupi-backend/pkg/authenticator,github.com/COS301-SE-2024/occupi/occupi-backend/pkg/cache,github.com/COS301-SE-2024/occupi/occupi-backend/pkg/database,github.com/COS301-SE-2024/occupi/occupi-backend/pkg/middleware,github.com/COS301-SE-2024/occupi/occupi-backend/pkg/utils ./tests/... -coverprofile=coverage.out
- name: 📋 Upload coverage reports to Codecov
uses: codecov/codecov-action@v4.0.1
with:
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
verbose: true

- name: 📋 Upload test results to BuildPulse for flaky test detection 🚩
uses: buildpulse/buildpulse-action@main
with:
account: ${{ secrets.BUILDPULSE_ACCOUNT_ID }}
repository: ${{ secrets.BUILDPULSE_REPOSITORY_ID }}
path: |
tmp/test-results/gotestsum-report.xml
key: ${{ secrets.BUILDPULSE_ACCESS_KEY_ID }}
secret: ${{ secrets.BUILDPULSE_SECRET_ACCESS_KEY }}

build:
name: 🏗️ Build
Expand Down
90 changes: 90 additions & 0 deletions .github/workflows/load-test-system.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
name: Load Test System 🏋️💪

# trigger action to run once a month on Sunday at 12:00 AM
on:
schedule:
- cron: "0 0 1-7 * 0"

workflow_dispatch:

defaults:
run:
working-directory: testing/load

env:
USERNAME: ${{ secrets.K6_USERNAME }}
PASSWORD: ${{ secrets.K6_PASSWORD }}
PASSPHRASE: ${{ secrets.GPG_PASSPHRASE }}

jobs:
load_test_backend:
name: 🏋️💪 Load Test Backend 🔌
runs-on: ubuntu-latest

steps:
- name: ⬇️ Checkout repository
uses: actions/checkout@v4

- name: 🏗 Install k6
run: |
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
- name: 👩‍💻 Login to k6
run: k6 login cloud --token ${{ secrets.K6_CLOUD_TOKEN }}

- name: 🏋️💪 Run load test
run: k6 run --out=cloud backend.js

- name: ✅ Load test completed
run: echo "Load test completed successfully!"

load_test_web:
name: 🏋️💪 Load Test Web 🌐
runs-on: ubuntu-latest

steps:
- name: ⬇️ Checkout repository
uses: actions/checkout@v4

- name: 🏗 Install k6
run: |
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
- name: 👩‍💻 Login to k6
run: k6 login cloud --token ${{ secrets.K6_CLOUD_TOKEN }}

- name: 🏋️💪 Run load test
run: k6 run --out=cloud web.js

- name: ✅ Load test completed
run: echo "Load test completed successfully!"

load_test_landing_page:
name: 🏋️💪 Load Test Landing Page 🏠
runs-on: ubuntu-latest

steps:
- name: ⬇️ Checkout repository
uses: actions/checkout@v4

- name: 🏗 Install k6
run: |
sudo apt-key adv --keyserver hkp://keyserver.ubuntu.com:80 --recv-keys C5AD17C747E3415A3642D57D77C6C491D6AC1D69
echo "deb https://dl.k6.io/deb stable main" | sudo tee /etc/apt/sources.list.d/k6.list
sudo apt-get update
sudo apt-get install k6
- name: 👩‍💻 Login to k6
run: k6 login cloud --token ${{ secrets.K6_CLOUD_TOKEN }}

- name: 🏋️💪 Run load test
run: k6 run --out=cloud landing-page.js

- name: ✅ Load test completed
run: echo "Load test completed successfully!"
2 changes: 0 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,3 @@
/benchmarks

documentation/SRS-Docs/SRS.aux
documentation/SRS-Docs/SRS.fdb_latexmk
documentation/SRS-Docs/SRS.fls
Expand Down
1 change: 1 addition & 0 deletions frontend/occupi-web/src/components/InputBox/InputBox.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ const InputBox = (props: InputBoxProps) => {
{err !== "" && <h5 className="text-text_col_red_salmon font-normal text-base">{err}</h5>}
</div>
<input
name={props.type}
type={props.type}
autoComplete={props.type === "email" ? "username" : "current-password" }
placeholder={props.placeholder}
Expand Down
162 changes: 19 additions & 143 deletions occupi-backend/cmd/occupi-backend/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -25,23 +25,8 @@ package main

import (
"flag"
"time"

"github.com/gin-contrib/cors"
"github.com/gin-contrib/sessions"
"github.com/gin-contrib/sessions/cookie"
"github.com/gin-gonic/gin"
nrgin "github.com/newrelic/go-agent/v3/integrations/nrgin"
"github.com/newrelic/go-agent/v3/newrelic"
"github.com/sirupsen/logrus"

"github.com/COS301-SE-2024/occupi/occupi-backend/configs"
"github.com/COS301-SE-2024/occupi/occupi-backend/pkg/constants"
"github.com/COS301-SE-2024/occupi/occupi-backend/pkg/middleware"
"github.com/COS301-SE-2024/occupi/occupi-backend/pkg/models"
"github.com/COS301-SE-2024/occupi/occupi-backend/pkg/receiver"
"github.com/COS301-SE-2024/occupi/occupi-backend/pkg/router"
"github.com/COS301-SE-2024/occupi/occupi-backend/pkg/utils"
"github.com/COS301-SE-2024/occupi/occupi-backend/pkg/application"
)

// occupi backend entry point
Expand All @@ -50,131 +35,22 @@ func main() {
env := flag.String("env", "dev.localhost", "Environment to use (dev.localhost, dev.localhost.docker, dev.deployed, prod)")
flag.Parse()

// init viper
configs.InitViper(env)

// setup logger to log all server interactions
utils.SetupLogger()

// create a new app session
appsession := createAppSession()

// start the consumer
go receiver.StartConsumeMessage(appsession)

// set gin run mode
gin.SetMode(configs.GetGinRunMode())

// Create a Gin router
ginRouter := gin.Default()

// Set CORS
addCORSPolicy(ginRouter)

// Set trusted proxies
setTrustedProxies(ginRouter)

// adding rate limiting middleware
middleware.AttachRateLimitMiddleware(ginRouter)

// adding newrelic middleware
attachNewRelicMiddleware(ginRouter)

// attach session middleware
attachSessionMiddleware(ginRouter)

// attach timezone middleware
attachTimeZoneMiddelware(ginRouter)

// attach real ip middleware
attachRealIPMiddleware(ginRouter)

// Register routes
router.OccupiRouter(ginRouter, appsession)

// Run the server
runServer(ginRouter)
}

func createAppSession() *models.AppSession {
// create a new app session
db := configs.ConnectToDatabase(constants.AdminDBAccessOption)
cache := configs.CreateCache()
appsession := models.New(db, cache)
return appsession
}

func addCORSPolicy(ginRouter *gin.Engine) {
// Set CORS
ginRouter.Use(cors.New(cors.Config{
AllowOrigins: configs.GetAllowOrigins(),
AllowMethods: configs.GetAllowMethods(),
AllowHeaders: configs.GetAllowHeaders(),
ExposeHeaders: configs.GetExposeHeaders(),
AllowCredentials: configs.GetAllowCredentials(),
MaxAge: time.Duration(configs.GetMaxAge()) * time.Second,
}))
}

func setTrustedProxies(ginRouter *gin.Engine) {
// Set trusted proxies
err := ginRouter.SetTrustedProxies(configs.GetTrustedProxies())
if err != nil {
logrus.Fatal("Failed to set trusted proxies: ", err)
}
}

func attachNewRelicMiddleware(ginRouter *gin.Engine) {
if configs.GetEnv() == "prod" || configs.GetEnv() == "devdeployed" {
// Create a newrelic application
app, err := newrelic.NewApplication(
newrelic.ConfigAppName("occupi-backend"),
newrelic.ConfigLicense(configs.GetConfigLicense()),
newrelic.ConfigAppLogForwardingEnabled(true),
)
if err != nil {
logrus.Fatal("Failed to create newrelic application: ", err)
}

// adding newrelic middleware
ginRouter.Use(nrgin.Middleware(app))
}
}

func attachSessionMiddleware(ginRouter *gin.Engine) {
// creating a new valid session for management of shared variables
store := cookie.NewStore([]byte(configs.GetSessionSecret()))
ginRouter.Use(sessions.Sessions("occupi-sessions-store", store))
}

func attachTimeZoneMiddelware(ginRouter *gin.Engine) {
// TimezoneMiddleware is a middleware that sets the timezone for the request.
ginRouter.Use(middleware.TimezoneMiddleware())
}

func attachRealIPMiddleware(ginRouter *gin.Engine) {
// RealIPMiddleware is a middleware that sets the real IP for the request.
ginRouter.Use(middleware.RealIPMiddleware())
}

func runServer(ginRouter *gin.Engine) {
certFile := configs.GetCertFileName()
keyFile := configs.GetKeyFileName()

// logrus all env variables
logrus.Infof("Server running on port: %s", configs.GetPort())
logrus.Infof("Server running in %s mode", configs.GetGinRunMode())
logrus.Infof("Server running with cert file: %s", certFile)
logrus.Infof("Server running with key file: %s", keyFile)

// Listening on the port with TLS if env is prod or dev.deployed
if configs.GetEnv() == "prod" || configs.GetEnv() == "devdeployed" {
if err := ginRouter.RunTLS(":"+configs.GetPort(), certFile, keyFile); err != nil {
logrus.Fatal("Failed to run server: ", err)
}
} else {
if err := ginRouter.Run(":" + configs.GetPort()); err != nil {
logrus.Fatal("Failed to run server: ", err)
}
}
// Create and configure the application
app := application.NewApplication().
SetEnvironment(*env).
InitializeConfig().
SetupLogger().
CreateAppSession().
StartConsumer().
SetupRouter().
AddCORSPolicy().
SetTrustedProxies().
AttachRateLimitMiddleware().
AttachSessionMiddleware().
AttachTimeZoneMiddleware().
AttachRealIPMiddleware().
RegisterRoutes().
SetEnvVariables()

app.RunServer()
}
Binary file modified occupi-backend/configs/centrifugo.config.json.gpg
Binary file not shown.
48 changes: 37 additions & 11 deletions occupi-backend/configs/config.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,6 @@ const (
OccupiDomains = "OCCUPI_DOMAINS"
Env = "ENV"
OtpExpiration = "OTP_EXPIRATION"
FrontendURL = "FRONTEND_URL"
ConfigLicense = "CONFIG_LICENSE"
CacheEviction = "CACHE_EVICTION"
OtpGenReqEviction = "OTP_GEN_REQ_EVICTION"
AllowOriginsVal = "ALLOW_ORIGINS"
Expand All @@ -50,6 +48,10 @@ const (
CentrifugoAKy = "CENTRIFUGO_API_KEY"
CentrifugoHost = "CENTRIFUGO_HOST"
CentrifugoPort = "CENTRIFUGO_PORT"
ConfigLicense = "CONFIG_LICENSE"
NewRelicAppName = "NEW_RELIC_APP_NAME"
SentryDSN = "SENTRY_DSN"
PassPhrase = "TEST_PASS_PHRASE"
)

// init viper
Expand Down Expand Up @@ -261,15 +263,6 @@ func GetOTPExpiration() int {
return expiration
}

// gets the config license as defined in the config.yaml file
func GetConfigLicense() string {
license := viper.GetString(ConfigLicense)
if license == "" {
license = "CONFIG_LICENSE"
}
return license
}

// gets the cache eviction time as defined in the config.yaml file in seconds
func GetCacheEviction() int {
time := viper.GetInt(CacheEviction)
Expand Down Expand Up @@ -433,3 +426,36 @@ func GetCentrifugoPort() string {
}
return port
}

// gets the config license as defined in the config.yaml file
func GetConfigLicense() string {
license := viper.GetString(ConfigLicense)
if license == "" {
license = "CONFIG_LICENSE"
}
return license
}

func GetNewRelicAppName() string {
appName := viper.GetString(NewRelicAppName)
if appName == "" {
appName = "NEW_RELIC_APP_NAME"
}
return appName
}

func GetSentryDSN() string {
dsn := viper.GetString(SentryDSN)
if dsn == "" {
dsn = "SENTRY_DSN"
}
return dsn
}

func GetTestPassPhrase() string {
passPhrase := viper.GetString(PassPhrase)
if passPhrase == "" {
passPhrase = "TEST_PASS_PHRASE"
}
return passPhrase
}
Binary file modified occupi-backend/configs/config.yaml.gpg
Binary file not shown.
Binary file modified occupi-backend/configs/dev.deployed.yaml.gpg
Binary file not shown.
Binary file modified occupi-backend/configs/dev.localhost.yaml.gpg
Binary file not shown.
Binary file modified occupi-backend/configs/prod.yaml.gpg
Binary file not shown.
Binary file modified occupi-backend/configs/test.yaml.gpg
Binary file not shown.
Loading

0 comments on commit 5a513fd

Please sign in to comment.