Skip to content

Commit

Permalink
Merge pull request #6 from anthdm/deploy-rollbacks
Browse files Browse the repository at this point in the history
Deploy rollbacks
  • Loading branch information
anthdm authored Dec 30, 2023
2 parents c6f52e1 + fb2d2d4 commit 57f9cdf
Show file tree
Hide file tree
Showing 13 changed files with 242 additions and 143 deletions.
28 changes: 14 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ coming soon...

Get server status

- Method: `POST`
- Method: `GET`
- Response Content-Type: `application/json`

Request Body: `empty`
Expand All @@ -25,9 +25,9 @@ Example Response:

---

### /application/\<app-id\>
### /endpoint/\<id\>

Get Application by ID
Get Endpoint by ID

- Method: `GET`
- Response Content-Type: `application/json`
Expand All @@ -40,12 +40,12 @@ Example Response:
{
"id": "09248ef6-c401-4601-8928-5964d61f2c61",
"name": "My first ffaas app",
"endpoint": "http://0.0.0.0:4000/09248ef6-c401-4601-8928-5964d61f2c61",
"url": "http://0.0.0.0:4000/09248ef6-c401-4601-8928-5964d61f2c61",
"active_deploy_id": "aeacab67-91d6-45c1-ae29-f27922b0fcf0",
"deploy_history": [
{
"id": "aeacab67-91d6-45c1-ae29-f27922b0fcf0",
"app_id": "09248ef6-c401-4601-8928-5964d61f2c61",
"endpoint_id": "09248ef6-c401-4601-8928-5964d61f2c61",
"hash": "c4dd6753109e47b317a4fc792d231b64",
"created_at": "2023-12-29T12:19:20.594726Z"
}
Expand All @@ -56,9 +56,9 @@ Example Response:

---

### /application
### /endpoint

Create a new application
Create a new endpoint

- Method: `POST`
- Request Content-Type: `application/json`
Expand All @@ -68,7 +68,7 @@ Example Request Body:

```json
{
"name": "my-app"
"name": "my-endpoint"
}
```

Expand All @@ -77,8 +77,8 @@ Example Response Body:
```json
{
"id": "2488b7be-e3d3-4e4c-8f79-13d9d568483d",
"name": "my-app",
"endpoint": "http://0.0.0.0:4000/2488b7be-e3d3-4e4c-8f79-13d9d568483d",
"name": "my-endpoint",
"url": "http://0.0.0.0:4000/2488b7be-e3d3-4e4c-8f79-13d9d568483d",
"active_deploy_id": "00000000-0000-0000-0000-000000000000",
"deploy_history": [],
"created_at": "2023-12-29T12:08:20.542039Z"
Expand All @@ -87,9 +87,9 @@ Example Response Body:

---

### /application/\<app-id\>/deploy
### /endpoint/\<id\>/deploy

Deploy Wasm Blob to Application
Deploy Wasm Blob to Endpoint

- Method: `POST`
- Request Content-Type: `application/octet-stream`
Expand All @@ -102,7 +102,7 @@ Example Response:
```json
{
"id": "e2a1ceea-d19e-4231-adc9-995ac61bdaf0",
"app_id": "2488b7be-e3d3-4e4c-8f79-13d9d568483d",
"endpoint_id": "2488b7be-e3d3-4e4c-8f79-13d9d568483d",
"hash": "75b196bcd44611d9f74d62ed16a54e03",
"created_at": "2023-12-29T12:12:39.91252Z"
}
Expand All @@ -112,7 +112,7 @@ Example Response:

## Wasm Server Endpoints

### /\<app-id\>
### /\<endpoint-id\>

Call the Wasm function

Expand Down
24 changes: 12 additions & 12 deletions cmd/ffaas/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ func main() {
}

if seed {
seedApplication(memstore, modCache)
seedEndpoint(memstore, modCache)
}

fmt.Println(banner())
Expand All @@ -54,29 +54,29 @@ func main() {
log.Fatal(wasmServer.Listen(config.Get().WASMServerAddr))
}

func seedApplication(store storage.Store, cache storage.ModCacher) {
func seedEndpoint(store storage.Store, cache storage.ModCacher) {
b, err := os.ReadFile("examples/go/app.wasm")
if err != nil {
log.Fatal(err)
}
app := &types.Application{
endpoint := &types.Endpoint{
ID: uuid.MustParse("09248ef6-c401-4601-8928-5964d61f2c61"),
Name: "My first ffaas app",
Environment: map[string]string{"FOO": "fooenv"},
Name: "Catfact parser",
Environment: map[string]string{"FOO": "bar"},
CreatedAT: time.Now(),
}

deploy := types.NewDeploy(app, b)
app.ActiveDeployID = deploy.ID
app.Endpoint = config.GetWasmUrl() + "/" + app.ID.String()
app.DeployHistory = append(app.DeployHistory, *deploy)
store.CreateApp(app)
deploy := types.NewDeploy(endpoint, b)
endpoint.ActiveDeployID = deploy.ID
endpoint.URL = config.GetWasmUrl() + "/" + endpoint.ID.String()
endpoint.DeployHistory = append(endpoint.DeployHistory, deploy)
store.CreateEndpoint(endpoint)
store.CreateDeploy(deploy)
fmt.Printf("app: %s\n", app.Endpoint)
fmt.Printf("endpoint: %s\n", endpoint.URL)

compCache := wazero.NewCompilationCache()
runtime.Compile(context.Background(), compCache, deploy.Blob)
cache.Put(app.ID, compCache)
cache.Put(endpoint.ID, compCache)
}

func banner() string {
Expand Down
Binary file modified examples/go/app.wasm
Binary file not shown.
5 changes: 4 additions & 1 deletion examples/go/main.go
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
package main

import (
"fmt"
"net/http"
"os"

ffaas "github.com/anthdm/ffaas/sdk"
)

func myHandler(w http.ResponseWriter, r *http.Request) {
w.Write([]byte("hello from my handler"))
fmt.Println("the env:", os.Getenv("FOO"))
w.Write([]byte("from tinder swiper"))
}

func main() {
Expand Down
99 changes: 74 additions & 25 deletions pkg/api/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -37,9 +37,10 @@ func (s *Server) Listen(addr string) error {
func (s *Server) initRouter() {
s.router = chi.NewRouter()
s.router.Get("/status", handleStatus)
s.router.Get("/application/{appID}", makeAPIHandler(s.handleGetApp))
s.router.Post("/application", makeAPIHandler(s.handleCreateApp))
s.router.Post("/application/{appID}/deploy", makeAPIHandler(s.handleCreateDeploy))
s.router.Get("/endpoint/{id}", makeAPIHandler(s.handleGetEndpoint))
s.router.Post("/endpoint", makeAPIHandler(s.handleCreateEndpoint))
s.router.Post("/endpoint/{id}/deploy", makeAPIHandler(s.handleCreateDeploy))
s.router.Post("/endpoint/{id}/rollback", makeAPIHandler(s.handleCreateRollback))
}

func handleStatus(w http.ResponseWriter, r *http.Request) {
Expand All @@ -51,44 +52,51 @@ func handleStatus(w http.ResponseWriter, r *http.Request) {
json.NewEncoder(w).Encode(status)
}

// CreateAppParams holds all the necessary fields to create a new ffaas application.
type CreateAppParams struct {
Name string `json:"name"`
// CreateEndpointParams holds all the necessary fields to create a new ffaas application.
type CreateEndpointParams struct {
Name string `json:"name"`
Environment map[string]string `json:"environment"`
}

func (p CreateAppParams) validate() error {
if len(p.Name) < 3 || len(p.Name) > 20 {
return fmt.Errorf("name of the application should be longer than 3 and less than 20 characters")
func (p CreateEndpointParams) validate() error {
minlen, maxlen := 3, 50
if len(p.Name) < minlen {
return fmt.Errorf("endpoint name should be at least %d characters long", minlen)
}
if len(p.Name) > maxlen {
return fmt.Errorf("endpoint name can be maximum %d characters long", maxlen)
}
return nil
}

func (s *Server) handleCreateApp(w http.ResponseWriter, r *http.Request) error {
var params CreateAppParams
func (s *Server) handleCreateEndpoint(w http.ResponseWriter, r *http.Request) error {
var params CreateEndpointParams
if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
return writeJSON(w, http.StatusBadRequest, ErrorResponse(ErrDecodeRequestBody))
}
defer r.Body.Close()

if err := params.validate(); err != nil {
return writeJSON(w, http.StatusBadRequest, ErrorResponse(err))
}
app := types.NewApplication(params.Name, nil)
app.Endpoint = config.GetWasmUrl() + "/" + app.ID.String()
if err := s.store.CreateApp(app); err != nil {

endpoint := types.NewEndpoint(params.Name, params.Environment)
endpoint.URL = config.GetWasmUrl() + "/" + endpoint.ID.String()
if err := s.store.CreateEndpoint(endpoint); err != nil {
return writeJSON(w, http.StatusBadRequest, ErrorResponse(err))
}
return writeJSON(w, http.StatusOK, app)
return writeJSON(w, http.StatusOK, endpoint)
}

// CreateDeployParams holds all the necessary fields to deploy a new function.
type CreateDeployParams struct{}

func (s *Server) handleCreateDeploy(w http.ResponseWriter, r *http.Request) error {
appID, err := uuid.Parse(chi.URLParam(r, "appID"))
endpointID, err := uuid.Parse(chi.URLParam(r, "id"))
if err != nil {
return writeJSON(w, http.StatusBadRequest, ErrorResponse(err))
}
app, err := s.store.GetAppByID(appID)
endpoint, err := s.store.GetEndpoint(endpointID)
if err != nil {
return writeJSON(w, http.StatusNotFound, ErrorResponse(err))
}
Expand All @@ -97,28 +105,69 @@ func (s *Server) handleCreateDeploy(w http.ResponseWriter, r *http.Request) erro
if err != nil {
return writeJSON(w, http.StatusNotFound, ErrorResponse(err))
}
deploy := types.NewDeploy(app, b)
deploy := types.NewDeploy(endpoint, b)
if err := s.store.CreateDeploy(deploy); err != nil {
return writeJSON(w, http.StatusUnprocessableEntity, ErrorResponse(err))
}
// Each new deploy will be the app's active deploy
err = s.store.UpdateApp(appID, storage.UpdateAppParams{
ActiveDeploy: deploy.ID,
// Each new deploy will be the endpoint's active deploy
err = s.store.UpdateEndpoint(endpointID, storage.UpdateEndpointParams{
ActiveDeployID: deploy.ID,
Deploys: []*types.Deploy{deploy},
})
if err != nil {
return writeJSON(w, http.StatusUnprocessableEntity, ErrorResponse(err))
}
return writeJSON(w, http.StatusOK, deploy)
}

func (s *Server) handleGetApp(w http.ResponseWriter, r *http.Request) error {
appID, err := uuid.Parse(chi.URLParam(r, "appID"))
func (s *Server) handleGetEndpoint(w http.ResponseWriter, r *http.Request) error {
id, err := uuid.Parse(chi.URLParam(r, "id"))
if err != nil {
return writeJSON(w, http.StatusBadRequest, ErrorResponse(err))
}
endpoint, err := s.store.GetEndpoint(id)
if err != nil {
return writeJSON(w, http.StatusNotFound, ErrorResponse(err))
}
return writeJSON(w, http.StatusOK, endpoint)
}

// CreateRollbackParams holds all the necessary fields to rollback your application
// to a specific deploy id (version).
type CreateRollbackParams struct {
DeployID uuid.UUID `json:"deploy_id"`
}

func (s *Server) handleCreateRollback(w http.ResponseWriter, r *http.Request) error {
endpointID, err := uuid.Parse(chi.URLParam(r, "id"))
if err != nil {
return writeJSON(w, http.StatusBadRequest, ErrorResponse(err))
}
endpoint, err := s.store.GetEndpoint(endpointID)
if err != nil {
return writeJSON(w, http.StatusBadRequest, ErrorResponse(err))
}
app, err := s.store.GetAppByID(appID)

currentDeployID := endpoint.ActiveDeployID

var params CreateRollbackParams
if err := json.NewDecoder(r.Body).Decode(&params); err != nil {
return writeJSON(w, http.StatusBadRequest, ErrorResponse(err))
}

deploy, err := s.store.GetDeploy(params.DeployID)
if err != nil {
return writeJSON(w, http.StatusNotFound, ErrorResponse(err))
}
return writeJSON(w, http.StatusOK, app)

updateParams := storage.UpdateEndpointParams{
ActiveDeployID: deploy.ID,
}
if err := s.store.UpdateEndpoint(endpointID, updateParams); err != nil {
return writeJSON(w, http.StatusBadRequest, ErrorResponse(err))
}

s.cache.Delete(currentDeployID)

return writeJSON(w, http.StatusOK, map[string]any{"deploy": deploy.ID})
}
Loading

0 comments on commit 57f9cdf

Please sign in to comment.