mirror of
https://github.com/kevin-DL/services.git
synced 2026-01-11 19:04:35 +00:00
* branch and region support * . * . * . * . * fix functions * . * break loop * update example * update examples
129 lines
3.2 KiB
Go
129 lines
3.2 KiB
Go
package handler
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha1"
|
|
"fmt"
|
|
"io"
|
|
"sync"
|
|
"time"
|
|
|
|
"github.com/micro/micro/v3/service/errors"
|
|
"github.com/micro/micro/v3/service/store"
|
|
pb "github.com/micro/services/function/proto"
|
|
"github.com/micro/services/pkg/tenant"
|
|
)
|
|
|
|
var (
|
|
mtx sync.Mutex
|
|
)
|
|
|
|
type Reservation struct {
|
|
// The function name
|
|
Name string `json:"name"`
|
|
// The owner e.g tenant id
|
|
Owner string `json:"owner"`
|
|
// Uniq associated token
|
|
Token string `json:"token"`
|
|
// Time of creation
|
|
Created time.Time `json:"created"`
|
|
// The expiry time
|
|
Expires time.Time `json:"expires"`
|
|
}
|
|
|
|
func genToken(name, owner string) string {
|
|
h := sha1.New()
|
|
io.WriteString(h, name+owner)
|
|
return fmt.Sprintf("%x", h.Sum(nil))
|
|
}
|
|
|
|
// Call is a single request handler called via client.Call or the generated client code
|
|
func (f *Function) Reserve(ctx context.Context, req *pb.ReserveRequest, rsp *pb.ReserveResponse) error {
|
|
id, ok := tenant.FromContext(ctx)
|
|
if !ok {
|
|
id = "micro"
|
|
}
|
|
|
|
if len(req.Name) == 0 {
|
|
return errors.BadRequest("function.reserve", "missing function name")
|
|
}
|
|
|
|
if len(req.Name) < 3 || len(req.Name) > 32 {
|
|
return errors.BadRequest("function.reserve", "name must be longer than 3-32 chars in length")
|
|
}
|
|
|
|
if !NameFormat.MatchString(req.Name) {
|
|
return errors.BadRequest("function.reserve", "invalidate name format")
|
|
}
|
|
|
|
// to prevent race conditions in reservation lets global lock
|
|
mtx.Lock()
|
|
defer mtx.Unlock()
|
|
|
|
// check the store for reservation
|
|
recs, err := store.Read(ReservationKey + req.Name)
|
|
if err != nil && err != store.ErrNotFound {
|
|
return errors.InternalServerError("function.reserve", "failed to reserve name")
|
|
}
|
|
|
|
var rsrv *Reservation
|
|
|
|
// check if the record exists
|
|
if len(recs) > 0 {
|
|
// existing reservation exists
|
|
rec := recs[0]
|
|
|
|
if err := rec.Decode(&rsrv); err != nil {
|
|
return errors.BadRequest("function.reserve", "name already reserved")
|
|
}
|
|
|
|
// check the owner matches or if the reservation expired
|
|
if rsrv.Owner != id && rsrv.Expires.After(time.Now()) {
|
|
return errors.BadRequest("function.reserve", "name already reserved")
|
|
}
|
|
|
|
// update the owner
|
|
rsrv.Owner = id
|
|
|
|
// update the reservation expiry
|
|
rsrv.Expires = time.Now().AddDate(1, 0, 0)
|
|
} else {
|
|
// check if its already running
|
|
key := FunctionKey + req.Name
|
|
recs, err := store.Read(key, store.ReadLimit(1))
|
|
if err != nil && err != store.ErrNotFound {
|
|
return errors.InternalServerError("function.reserve", "failed to reserve name")
|
|
}
|
|
|
|
// existing function is running by that name
|
|
if len(recs) > 0 {
|
|
return errors.BadRequest("function.reserve", "function already exists")
|
|
}
|
|
|
|
// not reserved
|
|
rsrv = &Reservation{
|
|
Name: req.Name,
|
|
Owner: id,
|
|
Created: time.Now(),
|
|
Expires: time.Now().AddDate(1, 0, 0),
|
|
Token: genToken(req.Name, id),
|
|
}
|
|
}
|
|
|
|
rec := store.NewRecord(ReservationKey+req.Name, rsrv)
|
|
|
|
if err := store.Write(rec); err != nil {
|
|
return errors.InternalServerError("function.reserve", "error while reserving name")
|
|
}
|
|
|
|
rsp.Reservation = &pb.Reservation{
|
|
Name: rsrv.Name,
|
|
Owner: rsrv.Owner,
|
|
Created: rsrv.Created.Format(time.RFC3339Nano),
|
|
Expires: rsrv.Expires.Format(time.RFC3339Nano),
|
|
Token: rsrv.Token,
|
|
}
|
|
|
|
return nil
|
|
}
|