Skip to content

Commit

Permalink
feat: use #10 to spawn function instances
Browse files Browse the repository at this point in the history
  • Loading branch information
WoodenMaiden committed May 13, 2024
1 parent a584da1 commit aea4c44
Show file tree
Hide file tree
Showing 5 changed files with 58 additions and 17 deletions.
2 changes: 1 addition & 1 deletion main.go
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ func main() {
}()

db := database.Init(cfg.FuntionStateStorageDSN)
r := routes.GetRoutes(db, cfg.JWTSecret, cfg.BuilderEndpoint, getMinioClient(cfg))
r := routes.GetRoutes(db, cfg.JWTSecret, cfg.BuilderEndpoint, getMinioClient(cfg), *s)

err := r.Run()

Expand Down
59 changes: 49 additions & 10 deletions routes/function/controller.go
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,23 @@ import (
"fmt"
"io"
"log"
"time"
"net/http"
"time"

"github.com/do4-2022/grobuzin/database"
"github.com/do4-2022/grobuzin/objectStorage"
"github.com/do4-2022/grobuzin/scheduler"
"github.com/gin-gonic/gin"
"github.com/google/uuid"
"gorm.io/gorm"
"gorm.io/gorm/clause"
)

type Controller struct {
CodeStorageService *objectStorage.CodeStorageService
DB *gorm.DB
BuilderEndpoint string
Scheduler *scheduler.Scheduler
}

func (cont *Controller) GetAllFunction(c *gin.Context) {
Expand Down Expand Up @@ -177,40 +180,76 @@ func (c *Controller) RunFunction(ctx *gin.Context) {
return
}

var fn database.FunctionState
err = c.DB.Where(&database.FunctionState{FunctionID: fnID}).First(&fn).Error
var fn database.Function
var fnState database.FunctionState

// does the function exist?
err = c.DB.Where(&database.Function{ID: fnID}).First(&fn).Error

if err != nil && errors.Is(err, gorm.ErrRecordNotFound) {
ctx.AbortWithStatusJSON(404, gin.H{"error": "Function not found"})
return
}

if fn.Status != "Ready" {
// does the function have an instance?
err = c.DB.Where(&database.FunctionState{FunctionID: fnID}).First(&fnState).Error

// if the function does not have an instance, we create ask the scheduler to create one
if errors.Is(err, gorm.ErrRecordNotFound) {
res, err := c.Scheduler.SpawnVM(fnID)

if err != nil {
ctx.AbortWithStatusJSON(500, gin.H{"error": err.Error()})
return
}

fnState = database.FunctionState{
FunctionID: fnID,
Status: "Creating",
Address: res.Address,
Port: res.Port,
}

err = c.DB.Clauses(clause.Returning{}).Create(&fnState).Error

if err != nil {
ctx.AbortWithStatusJSON(500, gin.H{"error": err.Error()})
return
}

}

if fnState.Status != "Ready" {
log.Println("Waiting for function", fn.ID, "to be ready")
time.Sleep(100 * time.Millisecond)


// we will try 5 times to find a ready function instance
for attempts := 0; attempts < 5; attempts++ {
// we are looking for a ready instance
err = c.DB.Where(&database.FunctionState{FunctionID: fnID, Status: "Ready"}).First(&fn).Error

if !errors.Is(err, gorm.ErrRecordNotFound) {
// if the error is something else than a record not found, we return an error, else we retry since it is not ready yet
if !errors.Is(err, gorm.ErrRecordNotFound) {
log.Println(err)
ctx.AbortWithStatusJSON(500, gin.H{"error": err.Error()})
return
} else {
log.Println("Function", fn.FunctionID, "is not ready yet... Retrying")
} else {
log.Println("Function", fnID, "is not ready yet... Retrying")

time.Sleep(100 * time.Millisecond)
}

if fn.Status == "Ready" {
log.Println("Function", fn.FunctionID, "is ready")
if fnState.Status == "Ready" {
log.Println("Function", fnID, "is ready")

break
};

}

if fn.Status != "Ready" {
// if even after 5 attempts the function is not ready, we return an error
if fnState.Status != "Ready" {
ctx.AbortWithStatusJSON(500, gin.H{"error": "Function is not ready"})
return
}
Expand Down
5 changes: 3 additions & 2 deletions routes/function/routes.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,20 @@ package function

import (
"github.com/do4-2022/grobuzin/objectStorage"
"github.com/do4-2022/grobuzin/scheduler"
"github.com/gin-gonic/gin"
"github.com/minio/minio-go/v7"
"gorm.io/gorm"
)

func ConfigureRoutes(router *gin.Engine, db *gorm.DB, minioClient *minio.Client, builderEndpoint string) {
func ConfigureRoutes(router *gin.Engine, db *gorm.DB, minioClient *minio.Client, builderEndpoint string, scheduler scheduler.Scheduler) {

group := router.Group("/function")

codeStorageService := objectStorage.CodeStorageService{MinioClient: minioClient}
codeStorageService.Init()

controller := Controller{&codeStorageService, db, builderEndpoint}
controller := Controller{&codeStorageService, db, builderEndpoint, &scheduler}

group.POST("/", controller.PostFunction)
group.GET("/", controller.GetAllFunction)
Expand Down
7 changes: 4 additions & 3 deletions routes/router.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,21 +3,22 @@ package routes
import (
"log"

"github.com/do4-2022/grobuzin/routes/user"
"github.com/do4-2022/grobuzin/routes/function"
"github.com/do4-2022/grobuzin/routes/user"
"github.com/do4-2022/grobuzin/scheduler"
"github.com/gin-gonic/gin"
"github.com/minio/minio-go/v7"
"gorm.io/gorm"
)

func GetRoutes(db *gorm.DB, JWTSecret string, BuilderEndpoint string, minioClient *minio.Client) *gin.Engine {
func GetRoutes(db *gorm.DB, JWTSecret string, BuilderEndpoint string, minioClient *minio.Client, scheduler scheduler.Scheduler) *gin.Engine {
router := gin.Default()

requireAuthMiddleware := user.RequireAuth(JWTSecret)

log.Println("Setting up routes", requireAuthMiddleware)

function.ConfigureRoutes(router, db, minioClient, BuilderEndpoint)
function.ConfigureRoutes(router, db, minioClient, BuilderEndpoint, scheduler)
user.ConfigureRoutes(router, db, JWTSecret)

return router
Expand Down
2 changes: 1 addition & 1 deletion scheduler/scheduler.go
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ func (s *Scheduler) LookForReadyInstance(functionId uuid.UUID, cursor uint64) (i
return "", 0, errors.New("could not find an available function") // we did not find anything thus, id is empty
}

func (s *Scheduler) SpawnVM(functionId uuid.UUID) (LambdoRunResponse, err error) {
func (s *Scheduler) SpawnVM(functionId uuid.UUID) (LambdoRunResponse LambdoSpawnResponse, err error) {
res, err := s.Lambdo.SpawnVM(functionId)

if (err != nil) {
Expand Down

0 comments on commit aea4c44

Please sign in to comment.