Skip to content

Commit

Permalink
Merge pull request #28 from AlertFlow/release/1.0.0-beta9
Browse files Browse the repository at this point in the history
Release Version 1.0.0 beta9
  • Loading branch information
JustNZ authored Jan 5, 2025
2 parents 9c8526d + 86c144d commit 04443d4
Show file tree
Hide file tree
Showing 34 changed files with 512 additions and 302 deletions.
33 changes: 17 additions & 16 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -1,28 +1,26 @@
name: Build and Release

on:
pull_request:
types: [closed]
branches:
- develop
push:
tags:
- 'v*.*.*'

jobs:
build:
if: github.event.pull_request.merged == true && github.event.pull_request.base.ref == 'develop' && startsWith(github.event.pull_request.head.ref, 'release')
runs-on: ubuntu-latest

steps:
- name: Checkout code
uses: actions/checkout@v2

- name: Build Frontend Docker Image
run: docker build . --file services/frontend/Dockerfile --tag justnz/alertflow:frontend-latest --tag justnz/alertflow:frontend-${{ github.sha }}
run: docker build . --file services/frontend/Dockerfile --tag justnz/alertflow:frontend-latest --tag justnz/alertflow:frontend-${{ github.sha }} --tag justnz/alertflow-runner:${{ github.ref_name }}

- name: Build Backend Docker Image
run: docker build . --file services/backend/Dockerfile --tag justnz/alertflow:backend-latest --tag justnz/alertflow:backend-${{ github.sha }}
run: docker build . --file services/backend/Dockerfile --tag justnz/alertflow:backend-latest --tag justnz/alertflow:backend-${{ github.sha }} --tag justnz/alertflow-runner:${{ github.ref_name }}

- name: Build AlertFlow Docker Image
run: docker build . --file services/backend/Dockerfile --tag justnz/alertflow:latest --tag justnz/alertflow:${{ github.sha }}
run: docker build . --file services/backend/Dockerfile --tag justnz/alertflow:latest --tag justnz/alertflow:${{ github.sha }} --tag justnz/alertflow-runner:${{ github.ref_name }}

- name: Login to Docker Hub
uses: docker/login-action@v3
Expand All @@ -34,18 +32,21 @@ jobs:
run: |
docker push justnz/alertflow:frontend-latest
docker push justnz/alertflow:frontend-${{ github.sha }}
docker push justnz/alertflow:frontend-${{ github.ref_name }}
docker push justnz/alertflow:backend-latest
docker push justnz/alertflow:backend-${{ github.sha }}
docker push justnz/alertflow:backend-${{ github.ref_name }}
docker push justnz/alertflow:latest
docker push justnz/alertflow:${{ github.sha }}
docker push justnz/alertflow:${{ github.ref_name }}
- name: Create release
- name: Create Release
id: create_release
uses: actions/create-release@v1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
uses: ncipollo/release-action@v1
with:
tag_name: ${{ github.sha }}
release_name: Release ${{ github.sha }}
draft: false
prerelease: false
name: Release ${{ github.ref_name }}
tag: ${{ github.ref_name }}
artifacts: alertflow-runner
skipIfReleaseExists: true
generateReleaseNotes: true
token: ${{ secrets.ACCESS_TOKEN }}
22 changes: 22 additions & 0 deletions services/backend/functions/auth/jwt.go
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,28 @@ func GetUserIDFromToken(signedToken string) (id uuid.UUID, err error) {
return
}

func GetRunnerDataFromToken(signedToken string) (runnerID string, projectID string, runnerType string, err error) {
token, err := jwt.ParseWithClaims(
signedToken,
&models.JWTProjectClaim{},
func(token *jwt.Token) (interface{}, error) {
return []byte(jwtKey), nil
},
)
if err != nil {
return
}
claims, ok := token.Claims.(*models.JWTProjectClaim)
if !ok {
err = errors.New("couldn't parse claims")
return
}
runnerID = claims.RunnerID
projectID = claims.ProjectID
runnerType = claims.Type
return
}

func RefreshToken(signedToken string) (newToken string, ExpiresAt int64, err error) {
token, err := jwt.ParseWithClaims(
signedToken,
Expand Down
24 changes: 24 additions & 0 deletions services/backend/functions/auth/projectAutoRunnerToken.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package auth

import (
"alertflow-backend/models"
"time"

"github.com/golang-jwt/jwt/v5"
"github.com/google/uuid"
)

func GenerateProjectAutoRunnerJWT(projectID string, id uuid.UUID) (tokenString string, err error) {
expirationTime := time.Now().Add(50 * 365 * 24 * time.Hour) // 10 years
claims := &models.JWTProjectClaim{
ProjectID: projectID,
ID: id,
Type: "project_auto_runner",
RegisteredClaims: jwt.RegisteredClaims{
ExpiresAt: jwt.NewNumericDate(expirationTime),
},
}
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
tokenString, err = token.SignedString(jwtKey)
return
}
3 changes: 2 additions & 1 deletion services/backend/functions/auth/runnerToken.go
Original file line number Diff line number Diff line change
Expand Up @@ -8,9 +8,10 @@ import (
"github.com/google/uuid"
)

func GenerateRunnerJWT(projectID string, id uuid.UUID) (tokenString string, err error) {
func GenerateRunnerJWT(runnerID string, projectID string, id uuid.UUID) (tokenString string, err error) {
expirationTime := time.Now().Add(50 * 365 * 24 * time.Hour) // 10 years
claims := &models.JWTProjectClaim{
RunnerID: runnerID,
ProjectID: projectID,
ID: id,
Type: "runner",
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package background_checks

import (
"alertflow-backend/models"
"context"
"fmt"

log "github.com/sirupsen/logrus"
"github.com/uptrace/bun"
)

func checkDisconnectedAutoRunners(db *bun.DB) {
context := context.Background()

log.Info("Bot: Checking for disconnected runners")

// get all executions that are not finished
var runners []models.Runners
err := db.NewSelect().Model(&runners).Where("last_heartbeat < NOW() - INTERVAL '30 minutes'").Scan(context)
if err != nil {
log.Error("Bot: Error getting running runners. ", err)
}

for _, runner := range runners {
log.Info(fmt.Sprintf("Bot: Runner %s seems to be not connected anymore. Removing it", runner.ID))
_, err := db.NewDelete().Model(&runner).Where("id = ?", runner.ID).Exec(context)
if err != nil {
log.Error(fmt.Sprintf("Bot: Error removing runner %s. ", runner.ID), err)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import (
func checkHangingExecutions(db *bun.DB) {
context := context.Background()

log.Info("Bot: Checking for hanging executions")

// get all executions that are not finished
var executions []models.Executions
err := db.NewSelect().Model(&executions).Where("running = ? AND executed_at < NOW() - INTERVAL '15 minutes'", true).Scan(context)
Expand Down
3 changes: 1 addition & 2 deletions services/backend/functions/background_checks/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@ package background_checks
import (
"time"

log "github.com/sirupsen/logrus"
"github.com/uptrace/bun"
)

Expand All @@ -15,8 +14,8 @@ func Init(db *bun.DB) {
for {
select {
case <-ticker.C:
log.Info("Bot: Checking for hanging executions")
checkHangingExecutions(db)
checkDisconnectedAutoRunners(db)
case <-quit:
ticker.Stop()
return
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ func CheckUserProjectAccess(projectID string, context *gin.Context, db *bun.DB)
return false, err
}

if tokenType == "runner" {
if tokenType == "runner" || tokenType == "project_auto_runner" {
// check if projectID in runner token equals to the requested projectID
tokenProjectID, err := auth.GetProjectIDFromToken(tokenString)
if err != nil {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package functions_runner

import (
"alertflow-backend/functions/auth"
"alertflow-backend/models"
"context"
"time"

"github.com/google/uuid"
"github.com/uptrace/bun"
)

func GenerateProjectAutoJoinToken(projectID string, db *bun.DB) (token string, err error) {
var key models.Tokens

key.ID = uuid.New()
key.CreatedAt = time.Now()
key.ProjectID = projectID
key.Type = "project_auto_runner"
key.Description = "Token for Project Auto Runner Join"

key.Key, err = auth.GenerateProjectAutoRunnerJWT(projectID, key.ID)
if err != nil {
return "", err
}

_, err = db.NewInsert().Model(&key).Exec(context.Background())
if err != nil {
return "", err
}

return key.Key, nil
}
33 changes: 33 additions & 0 deletions services/backend/functions/runner/generate_runner_token.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
package functions_runner

import (
"alertflow-backend/functions/auth"
"alertflow-backend/models"
"context"
"time"

"github.com/google/uuid"
"github.com/uptrace/bun"
)

func GenerateRunnerToken(projectID string, runnerID string, db *bun.DB) (token string, err error) {
var key models.Tokens

key.ID = uuid.New()
key.CreatedAt = time.Now()
key.ProjectID = projectID
key.Type = "runner"
key.Description = "Token for runner " + runnerID

key.Key, err = auth.GenerateRunnerJWT(runnerID, projectID, key.ID)
if err != nil {
return "", err
}

_, err = db.NewInsert().Model(&key).Exec(context.Background())
if err != nil {
return "", err
}

return key.Key, nil
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package executions

import (
"alertflow-backend/functions/auth"
"alertflow-backend/functions/httperror"
"alertflow-backend/models"
"net/http"
Expand All @@ -10,11 +11,17 @@ import (
)

func GetPendingExecutions(context *gin.Context, db *bun.DB) {
var runnerID = context.Param("executionID")
_, projectID, _, err := auth.GetRunnerDataFromToken(context.GetHeader("Authorization"))
if err != nil {
httperror.Unauthorized(context, "Error receiving runner data from token", err)
return
}

runnerID := context.Param("runnerID")

// get runner data is assigned to
var runner models.Runners
err := db.NewSelect().Model(&runner).Where("id = ?", runnerID).Scan(context)
err = db.NewSelect().Model(&runner).Where("id = ?", runnerID).Scan(context)
if err != nil {
httperror.InternalServerError(context, "Error collecting runner data on db", err)
return
Expand All @@ -23,7 +30,7 @@ func GetPendingExecutions(context *gin.Context, db *bun.DB) {
executions := make([]models.Executions, 0)

if !runner.AlertFlowRunner {
err = db.NewSelect().Model(&executions).Where("flow_id::text IN (SELECT id::text FROM flows WHERE project_id = ?)", runner.ProjectID).Where("pending = true AND runner_id = ''").Scan(context)
err = db.NewSelect().Model(&executions).Where("flow_id::text IN (SELECT id::text FROM flows WHERE project_id = ?)", projectID).Where("pending = true AND runner_id = ''").Scan(context)
if err != nil {
httperror.InternalServerError(context, "Error collecting executions from db", err)
return
Expand Down
5 changes: 3 additions & 2 deletions services/backend/handlers/projects/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package projects
import (
"alertflow-backend/functions/auth"
"alertflow-backend/functions/httperror"
functions_runner "alertflow-backend/functions/runner"
"alertflow-backend/models"
"crypto/rand"
"encoding/hex"
Expand Down Expand Up @@ -48,12 +49,12 @@ func CreateProject(context *gin.Context, db *bun.DB) {

project.ID = uuid.New()

project.RunnerJoinSecret, err = GenerateProjectRunnerJoinSecretKey()
project.RunnerAutoJoinToken, err = functions_runner.GenerateProjectAutoJoinToken(project.ID.String(), db)
if err != nil {
return
}

_, err = db.NewInsert().Model(&project).Column("id", "name", "description", "alertflow_runners", "icon", "color", "runner_join_secret").Exec(context)
_, err = db.NewInsert().Model(&project).Column("id", "name", "description", "alertflow_runners", "icon", "color", "runner_auto_join_token").Exec(context)
if err != nil {
log.Error(err)
httperror.InternalServerError(context, "Error creating project on db", err)
Expand Down
12 changes: 3 additions & 9 deletions services/backend/handlers/runners/busy.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,14 @@ import (
func Busy(context *gin.Context, db *bun.DB) {
runnerID := context.Param("runnerID")

var runner models.Runners
if err := context.ShouldBindJSON(&runner); err != nil {
httperror.StatusBadRequest(context, "Error parsing incoming data", err)
return
}

// check if runner is disabled
var runnerDB models.Runners
err := db.NewSelect().Model(&runnerDB).Where("id = ?", runnerID).Scan(context)
var runner models.Runners
err := db.NewSelect().Model(&runner).Where("id = ?", runnerID).Scan(context)
if err != nil {
httperror.InternalServerError(context, "Error collecting runner data from db", err)
return
}
if runnerDB.Disabled {
if runner.Disabled {
httperror.StatusBadRequest(context, "Runner is disabled", errors.New("runner is disabled"))
return
}
Expand Down
10 changes: 9 additions & 1 deletion services/backend/handlers/runners/create.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import (
"alertflow-backend/functions/auth"
"alertflow-backend/functions/gatekeeper"
"alertflow-backend/functions/httperror"
functions_runner "alertflow-backend/functions/runner"
"alertflow-backend/models"
"errors"
"net/http"
Expand Down Expand Up @@ -57,5 +58,12 @@ func CreateRunner(context *gin.Context, db *bun.DB) {
return
}

context.JSON(http.StatusCreated, gin.H{"result": "success", "runner": runner})
// generate token for runner
token, err := functions_runner.GenerateRunnerToken(runner.ProjectID, runner.ID.String(), db)
if err != nil {
httperror.InternalServerError(context, "Error generating runner token", err)
return
}

context.JSON(http.StatusCreated, gin.H{"result": "success", "runner": runner, "token": token})
}
Loading

0 comments on commit 04443d4

Please sign in to comment.