From 609c4c981311e2d79e148142570b56861801d56b Mon Sep 17 00:00:00 2001 From: Asim Aslam Date: Thu, 16 Dec 2021 19:23:03 +0000 Subject: [PATCH] functions branch and region support (#330) * branch and region support * . * . * . * . * fix functions * . * break loop * update example * update examples --- function/examples.json | 140 +++- function/handler/function.go | 16 + function/handler/google.go | 554 +++++++++++----- function/handler/reserve.go | 128 ++++ function/proto/function.pb.go | 996 ++++++++++++++++++++-------- function/proto/function.pb.micro.go | 51 ++ function/proto/function.proto | 147 ++-- function/publicapi.json | 11 +- 8 files changed, 1503 insertions(+), 540 deletions(-) create mode 100644 function/handler/function.go create mode 100644 function/handler/reserve.go diff --git a/function/examples.json b/function/examples.json index 768d91d..943b78e 100644 --- a/function/examples.json +++ b/function/examples.json @@ -2,23 +2,41 @@ "deploy": [ { "title": "Deploy a function", + "run_check": false, "request": { - "repo": "github.com/m3o/nodejs-function-example", - "name": "helloworld", - "entrypoint": "helloworld", - "runtime": "nodejs14" + "name": "helloworld", + "repo": "https://github.com/m3o/m3o", + "branch": "main", + "entrypoint": "Helloworld", + "subfolder": "examples/go-function", + "runtime": "go116", + "region": "europe-west1" }, - "response": {} + "response": { + "function": { + "id": "helloworld", + "name": "helloworld", + "repo": "https://github.com/m3o/m3o", + "branch": "main", + "entrypoint": "Helloworld", + "subfolder": "examples/go-function", + "runtime": "go116", + "region": "europe-west1", + "env_vars": {}, + "status": "Deploying", + "url": "https://helloworld.m3o.sh", + "created": "2021-12-16T17:27:09.230134479Z", + "updated": "" + } + } } ], "update": [ { "title": "Update a function", + "run_check": false, "request": { - "repo": "github.com/m3o/nodejs-function-example", - "name": "helloworld", - "entrypoint": "helloworld", - "runtime": "nodejs14" + "name": "helloworld" }, "response": {} } @@ -26,13 +44,16 @@ "call": [ { "title": "Call a function", + "run_check": false, "request": { "name": "helloworld", - "request": {} + "request": { + "name": "Alice" + } }, "response": { "response": { - "message": "Hello World!" + "message": "Hello Alice!" } } } @@ -40,24 +61,31 @@ "list": [ { "title": "List functions", + "run_check": false, "request": {}, "response": { - "functions": [ - { - "name": "helloworld", - "entrypoint": "helloworld", - "repo": "github.com/m3o/nodejs-function-example", - "subfolder": "", - "runtime": "nodejs14", - "status": "DEPLOY_IN_PROGRESS" - } - ] + "functions": [{ + "id": "helloworld", + "name": "helloworld", + "repo": "https://github.com/m3o/m3o", + "branch": "main", + "entrypoint": "Helloworld", + "subfolder": "examples/go-function", + "runtime": "go116", + "region": "europe-west1", + "env_vars": {}, + "status": "Deploying", + "url": "https://helloworld.m3o.sh", + "created": "2021-12-16T17:27:09.230134479Z", + "updated": "" + }] } } ], "delete": [ { "title": "Delete a function", + "run_check": false, "request": { "name": "helloworld" }, @@ -67,20 +95,72 @@ "describe": [ { "title": "Describe function status", + "run_check": false, "request": { "name": "helloworld" }, "response": { "function": { - "name": "helloworld", - "entrypoint": "helloworld", - "repo": "github.com/m3o/nodejs-function-example", - "subfolder": "", - "runtime": "nodejs14", - "status": "ACTIVE" - }, - "updated_at": "2021-10-08T10:17:34.914Z", - "timeout": "60s" + "id": "helloworld", + "name": "helloworld", + "repo": "https://github.com/m3o/m3o", + "branch": "main", + "entrypoint": "Helloworld", + "subfolder": "examples/go-function", + "runtime": "go116", + "region": "europe-west1", + "env_vars": {}, + "status": "Deploying", + "url": "https://helloworld.m3o.sh", + "created": "2021-12-16T17:27:09.230134479Z", + "updated": "" + } + } + } + ], + "regions": [ + { + "title": "List regions", + "run_check": false, + "request": {}, + "response": { + "regions": [ + "asia-east1", + "europe-west1", + "us-central1", + "us-east1", + "us-west1" + ] + } + } + ], + "reserve": [ + { + "title": "Reserve a function", + "run_check": false, + "request": { + "name": "helloworld" + }, + "response": { + "reservation": { + "name": "helloworld", + "owner": "micro/40e5d9aa-1185-4add-b248-ce4d72ff7947", + "token": "c580be106204d103df461bb3a3075aefedda5f85", + "created": "2021-12-16T19:19:29.615737412Z", + "expires": "2022-12-16T19:19:29.615737502Z" + } + } + } + ], + "proxy": [ + { + "title": "Proxy URL", + "run_check": false, + "request": { + "id": "helloworld" + }, + "response": { + "url": "https://europe-west1-m3o-apis.cloudfunctions.net/helloworld" } } ] diff --git a/function/handler/function.go b/function/handler/function.go new file mode 100644 index 0000000..e2ea65c --- /dev/null +++ b/function/handler/function.go @@ -0,0 +1,16 @@ +package handler + +import ( + "regexp" +) + +var ( + IDFormat = regexp.MustCompilePOSIX("[a-z0-9-]+") + NameFormat = regexp.MustCompilePOSIX("[a-z0-9]+") + + FunctionKey = "function/func/" + OwnerKey = "function/owner/" + ReservationKey = "function/reservation/" +) + +type Function struct{} diff --git a/function/handler/google.go b/function/handler/google.go index 1249db2..dc07fcc 100644 --- a/function/handler/google.go +++ b/function/handler/google.go @@ -6,10 +6,12 @@ import ( "encoding/json" "fmt" "io/ioutil" + "math/rand" "net/http" "os/exec" "path/filepath" "strings" + "time" "github.com/micro/micro/v3/service/config" "github.com/micro/micro/v3/service/errors" @@ -18,7 +20,7 @@ import ( "github.com/micro/micro/v3/service/store" function "github.com/micro/services/function/proto" "github.com/micro/services/pkg/tenant" - "gopkg.in/yaml.v2" + "github.com/teris-io/shortid" ) type GoogleFunction struct { @@ -29,6 +31,10 @@ type GoogleFunction struct { limit int // function identity identity string + // custom domain + domain string + + *Function } var ( @@ -51,8 +57,27 @@ var ( "ruby26", "php74", } + + // hardcoded list of supported regions + GoogleRegions = []string{"europe-west1", "us-central1", "us-east1", "us-west1", "asia-east1"} ) +var ( + alphanum = "abcdefghijklmnopqrstuvwxyz0123456789" +) + +func random(i int) string { + bytes := make([]byte, i) + for { + rand.Read(bytes) + for i, b := range bytes { + bytes[i] = alphanum[b%byte(len(alphanum))] + } + return string(bytes) + } + return fmt.Sprintf("%d", time.Now().Unix()) +} + func NewFunction() *GoogleFunction { v, err := config.Get("function.service_account_json") if err != nil { @@ -98,6 +123,11 @@ func NewFunction() *GoogleFunction { log.Fatalf("function.service_identity: %v", err) } identity := v.String("") + v, err = config.Get("function.domain") + if err != nil { + log.Fatalf("function.domain: %v", err) + } + domain := v.String("") m := map[string]interface{}{} err = json.Unmarshal(keyfile, &m) @@ -124,12 +154,16 @@ func NewFunction() *GoogleFunction { if err != nil { log.Fatalf(string(outp)) } + log.Info(string(outp)) + return &GoogleFunction{ project: project, address: address, limit: limit, identity: identity, + domain: domain, + Function: new(Function), } } @@ -159,17 +193,36 @@ func (e *GoogleFunction) Deploy(ctx context.Context, req *function.DeployRequest return errors.BadRequest("function.deploy", "invalid runtime") } - gitter := git.NewGitter(map[string]string{}) + var supportedRegion bool - var err error + if len(req.Region) == 0 { + // set to default region + req.Region = GoogleRegions[0] + supportedRegion = true + } - for _, branch := range []string{"master", "main"} { - err = gitter.Checkout(req.Repo, branch) - if err == nil { + // check if its in the supported regions + for _, reg := range GoogleRegions { + if req.Region == reg { + supportedRegion = true break } } + // unsupported region requested + if !supportedRegion { + return errors.BadRequest("function.deploy", "Unsupported region") + } + + if len(req.Branch) == 0 { + req.Branch = "master" + } + + gitter := git.NewGitter(map[string]string{}) + + var err error + + err = gitter.Checkout(req.Repo, req.Branch) if err != nil { return errors.InternalServerError("function.deploy", err.Error()) } @@ -179,18 +232,12 @@ func (e *GoogleFunction) Deploy(ctx context.Context, req *function.DeployRequest tenantId = "micro" } - multitenantPrefix := strings.Replace(tenantId, "/", "-", -1) if req.Entrypoint == "" { req.Entrypoint = req.Name } - project := req.Project - if project == "" { - project = "default" - } - - key := fmt.Sprintf("function/%s/%s/%s", tenantId, project, req.Name) - + // read the function by owner + key := fmt.Sprintf(OwnerKey+"%s/%s", tenantId, req.Name) records, err := store.Read(key) if err != nil && err != store.ErrNotFound { return err @@ -203,7 +250,7 @@ func (e *GoogleFunction) Deploy(ctx context.Context, req *function.DeployRequest // check for function limit if e.limit > 0 { // read all the records for the user - records, err := store.Read("function/"+tenantId+"/", store.ReadPrefix()) + records, err := store.Read(OwnerKey+tenantId+"/", store.ReadPrefix()) if err != nil { return err } @@ -213,6 +260,26 @@ func (e *GoogleFunction) Deploy(ctx context.Context, req *function.DeployRequest } } + // set the id + id := req.Name + + // check the owner isn't already running it + recs, err := store.Read(FunctionKey+req.Name, store.ReadLimit(1)) + + // if there's an existing function then generate a unique id + if err == nil && len(recs) > 0 { + // generate an id for the service + sid, err := shortid.Generate() + if err != nil || len(sid) == 0 { + sid = random(8) + } + + sid = strings.ToLower(sid) + sid = strings.Replace(sid, "-", "", -1) + sid = strings.Replace(sid, "_", "", -1) + id = req.Name + "-" + sid + } + // process the env vars to the required format var envVars []string @@ -220,40 +287,122 @@ func (e *GoogleFunction) Deploy(ctx context.Context, req *function.DeployRequest envVars = append(envVars, k+"="+v) } - go func() { + fn := &function.Func{ + Id: id, + Name: req.Name, + Repo: req.Repo, + Subfolder: req.Subfolder, + Entrypoint: req.Entrypoint, + Runtime: req.Runtime, + EnvVars: req.EnvVars, + Region: req.Region, + Branch: req.Branch, + Created: time.Now().Format(time.RFC3339Nano), + Status: "Deploying", + Url: fmt.Sprintf("https://%s-%s.cloudfunctions.net/%s", req.Region, e.project, id), + } + + // write the owner key + rec := store.NewRecord(key, fn) + if err := store.Write(rec); err != nil { + return err + } + + // write the global key + rec = store.NewRecord(FunctionKey+fn.Id, fn) + if err := store.Write(rec); err != nil { + return err + } + + // set the custom domain + if len(e.domain) > 0 { + fn.Url = fmt.Sprintf("https://%s.%s", fn.Id, e.domain) + } + + // set the response + rsp.Function = fn + + go func(fn *function.Func) { // https://jsoverson.medium.com/how-to-deploy-node-js-functions-to-google-cloud-8bba05e9c10a - cmd := exec.Command("gcloud", "functions", "deploy", - multitenantPrefix+"-"+req.Name, "--region", "europe-west1", "--service-account", e.identity, - "--allow-unauthenticated", "--entry-point", req.Entrypoint, - "--trigger-http", "--project", e.project, "--runtime", req.Runtime) + cmd := exec.Command("gcloud", "functions", "deploy", fn.Id, "--quiet", + "--region", fn.Region, "--service-account", e.identity, + "--allow-unauthenticated", "--entry-point", fn.Entrypoint, + "--trigger-http", "--project", e.project, "--runtime", fn.Runtime) // if env vars exist then set them if len(envVars) > 0 { cmd.Args = append(cmd.Args, "--set-env-vars", strings.Join(envVars, ",")) } - cmd.Dir = filepath.Join(gitter.RepoDir(), req.Subfolder) + cmd.Dir = filepath.Join(gitter.RepoDir(), fn.Subfolder) outp, err := cmd.CombinedOutput() if err != nil { log.Error(fmt.Errorf(string(outp))) + fn.Status = "DeploymentError" + store.Write(store.NewRecord(key, fn)) + return } - }() - id := fmt.Sprintf("%v-%v-%v", tenantId, project, req.Name) - rec := store.NewRecord(key, map[string]interface{}{ - "id": id, - "project": project, - "name": req.Name, - "tenantId": tenantId, - "repo": req.Repo, - "subfolder": req.Subfolder, - "entrypoint": req.Entrypoint, - "runtime": req.Runtime, - "env_vars": envVars, - }) + var status string - // write the record - return store.Write(rec) +LOOP: + // wait for the deployment and status update + for i := 0; i < 120; i++ { + cmd = exec.Command("gcloud", "functions", "describe", "--format", "json", + "--region", fn.Region, "--project", e.project, fn.Id) + + outp, err = cmd.CombinedOutput() + if err != nil { + log.Error(fmt.Errorf(string(outp))) + return + } + + log.Info(string(outp)) + + var m map[string]interface{} + if err := json.Unmarshal(outp, &m); err != nil { + log.Error(err) + return + } + + // write back the url + trigger := m["httpsTrigger"].(map[string]interface{}) + + if v := trigger["url"].(string); len(v) > 0 { + fn.Url = v + } else { + fn.Url = fmt.Sprintf("https://%s-%s.cloudfunctions.net/%s", fn.Region, e.project, fn.Id) + } + + v := m["status"].(string) + + switch v { + case "ACTIVE": + status = "Deployed" + break LOOP + case "DEPLOY_IN_PROGRESS": + status = "Deploying" + case "OFFLINE": + status = "DeploymentError" + break LOOP + } + + // we need to try get the url again + time.Sleep(time.Second) + } + + fn.Updated = time.Now().Format(time.RFC3339Nano) + fn.Status = status + + // write the owners key + store.Write(store.NewRecord(key, fn)) + + // write the global key + rec = store.NewRecord(FunctionKey+fn.Id, fn) + store.Write(rec) + }(fn) + + return nil } func (e *GoogleFunction) Update(ctx context.Context, req *function.UpdateRequest, rsp *function.UpdateResponse) error { @@ -263,29 +412,12 @@ func (e *GoogleFunction) Update(ctx context.Context, req *function.UpdateRequest return errors.BadRequest("function.update", "Missing name") } - if len(req.Repo) == 0 { - return errors.BadRequest("function.update", "Missing repo") - } - if req.Runtime == "" { - return fmt.Errorf("missing runtime field, please specify nodejs14, go116 etc") - } - tenantId, ok := tenant.FromContext(ctx) if !ok { tenantId = "micro" } - multitenantPrefix := strings.Replace(tenantId, "/", "-", -1) - if req.Entrypoint == "" { - req.Entrypoint = req.Name - } - - project := req.Project - if project == "" { - project = "default" - } - - key := fmt.Sprintf("function/%s/%s/%s", tenantId, project, req.Name) + key := fmt.Sprintf(OwnerKey+"%s/%s", tenantId, req.Name) records, err := store.Read(key) if err != nil { @@ -296,60 +428,87 @@ func (e *GoogleFunction) Update(ctx context.Context, req *function.UpdateRequest return errors.BadRequest("function.deploy", "function does not exist") } - gitter := git.NewGitter(map[string]string{}) - - for _, branch := range []string{"master", "main"} { - err = gitter.Checkout(req.Repo, branch) - if err == nil { - break - } + fn := new(function.Func) + if err := records[0].Decode(fn); err != nil { + return err } - if err != nil { + if len(fn.Region) == 0 { + fn.Region = GoogleRegions[0] + } + + if len(fn.Branch) == 0 { + fn.Branch = "master" + } + + gitter := git.NewGitter(map[string]string{}) + if err := gitter.Checkout(fn.Repo, fn.Branch); err != nil { return errors.InternalServerError("function.update", err.Error()) } // process the env vars to the required format var envVars []string - for k, v := range req.EnvVars { + for k, v := range fn.EnvVars { envVars = append(envVars, k+"="+v) } + var status string + go func() { - // https://jsoverson.medium.com/how-to-deploy-node-js-functions-to-google-cloud-8bba05e9c10a - cmd := exec.Command("gcloud", "functions", "deploy", - multitenantPrefix+"-"+req.Name, "--region", "europe-west1", "--service-account", e.identity, - "--allow-unauthenticated", "--entry-point", req.Entrypoint, - "--trigger-http", "--project", e.project, "--runtime", req.Runtime) - // if env vars exist then set them - if len(envVars) > 0 { - cmd.Args = append(cmd.Args, "--set-env-vars", strings.Join(envVars, ",")) +LOOP: + // wait for the deployment and status update + for i := 0; i < 120; i++ { + cmd := exec.Command("gcloud", "functions", "describe", "--quiet", "--format", "json", + "--region", fn.Region, "--project", e.project, fn.Id) + + outp, err := cmd.CombinedOutput() + if err != nil { + log.Error(fmt.Errorf(string(outp))) + return + } + + log.Info(string(outp)) + + var m map[string]interface{} + if err := json.Unmarshal(outp, &m); err != nil { + log.Error(err) + return + } + + // write back the url + trigger := m["httpsTrigger"].(map[string]interface{}) + if v := trigger["url"].(string); len(v) > 0 { + fn.Url = v + } else { + fn.Url = fmt.Sprintf("https://%s-%s.cloudfunctions.net/%s", fn.Region, e.project, fn.Id) + } + + v := m["status"].(string) + + switch v { + case "ACTIVE": + status = "Deployed" + break LOOP + case "DEPLOY_IN_PROGRESS": + status = "Deploying" + case "OFFLINE": + status = "DeploymentError" + break LOOP + } + + // we need to try get the url again + time.Sleep(time.Second) } - cmd.Dir = filepath.Join(gitter.RepoDir(), req.Subfolder) - outp, err := cmd.CombinedOutput() - if err != nil { - log.Error(fmt.Errorf(string(outp))) - } + fn.Status = status + fn.Updated = time.Now().Format(time.RFC3339Nano) + store.Write(store.NewRecord(key, fn)) }() - id := fmt.Sprintf("%v-%v-%v", tenantId, project, req.Name) - rec := store.NewRecord(key, map[string]interface{}{ - "id": id, - "project": project, - "name": req.Name, - "tenantId": tenantId, - "repo": req.Repo, - "subfolder": req.Subfolder, - "entrypoint": req.Entrypoint, - "runtime": req.Runtime, - "env_vars": envVars, - }) - - // write the record - return store.Write(rec) + // TODO: allow updating of branch and related? + return nil } func (e *GoogleFunction) Call(ctx context.Context, req *function.CallRequest, rsp *function.CallResponse) error { @@ -363,10 +522,28 @@ func (e *GoogleFunction) Call(ctx context.Context, req *function.CallRequest, rs if !ok { tenantId = "micro" } - multitenantPrefix := strings.Replace(tenantId, "/", "-", -1) - url := e.address + multitenantPrefix + "-" + req.Name - fmt.Println("URL:>", url) + // get the function id based on the tenant + recs, err := store.Read(OwnerKey + tenantId + "/" + req.Name) + if err != nil { + return err + } + if len(recs) == 0 { + return errors.NotFound("function.call", "not found") + } + + fn := new(function.Func) + recs[0].Decode(fn) + + if len(fn.Id) == 0 { + return errors.NotFound("function.call", "not found") + } + + url := fn.Url + + if len(url) == 0 { + url = fmt.Sprintf("https://%s-%s.cloudfunctions.net/%s", fn.Region, e.project, fn.Id) + } js, _ := json.Marshal(req.Request) if req.Request == nil || len(req.Request.Fields) == 0 { @@ -412,23 +589,40 @@ func (e *GoogleFunction) Delete(ctx context.Context, req *function.DeleteRequest if !ok { tenantId = "micro" } - multitenantPrefix := strings.Replace(tenantId, "/", "-", -1) - project := req.Project - if project == "" { - project = "default" + key := fmt.Sprintf(OwnerKey+"%v/%v", tenantId, req.Name) + + records, err := store.Read(key) + if err != nil && err == store.ErrNotFound { + return nil } - cmd := exec.Command("gcloud", "functions", "delete", "--project", e.project, "--region", "europe-west1", multitenantPrefix+"-"+req.Name) - outp, err := cmd.CombinedOutput() - if err != nil && !strings.Contains(string(outp), "does not exist") { - log.Error(fmt.Errorf(string(outp))) + if len(records) == 0 { + return nil + } + + fn := new(function.Func) + if err := records[0].Decode(fn); err != nil { return err } - key := fmt.Sprintf("function/%v/%v/%v", tenantId, project, req.Name) + // async delete + go func() { + cmd := exec.Command("gcloud", "functions", "delete", "--quiet", "--project", e.project, "--region", fn.Region, fn.Id) + outp, err := cmd.CombinedOutput() + if err != nil && !strings.Contains(string(outp), "does not exist") { + log.Error(fmt.Errorf(string(outp))) + return + } - return store.Delete(key) + // delete the owner key + store.Delete(key) + + // delete the global key + store.Delete(FunctionKey + fn.Id) + }() + + return nil } func (e *GoogleFunction) List(ctx context.Context, req *function.ListRequest, rsp *function.ListResponse) error { @@ -439,45 +633,31 @@ func (e *GoogleFunction) List(ctx context.Context, req *function.ListRequest, rs tenantId = "micro" } - key := "function/" + tenantId + "/" - - project := req.Project - if len(project) > 0 { - key = key + "/" + project + "/" - } + key := OwnerKey + tenantId + "/" records, err := store.Read(key, store.ReadPrefix()) if err != nil { return err } - multitenantPrefix := strings.Replace(tenantId, "/", "-", -1) - cmd := exec.Command("gcloud", "functions", "list", "--project", e.project, "--filter", "name~"+multitenantPrefix+"*") - outp, err := cmd.CombinedOutput() - if err != nil { - log.Error(fmt.Errorf(string(outp))) - } - - lines := strings.Split(string(outp), "\n") - statuses := map[string]string{} - - for _, line := range lines { - fields := strings.Fields(line) - if len(fields) < 2 { + for _, record := range records { + fn := new(function.Func) + if err := record.Decode(fn); err != nil { continue } - statuses[fields[0]] = fields[1] - } - - rsp.Functions = []*function.Func{} - - for _, record := range records { - f := new(function.Func) - if err := record.Decode(f); err != nil { - return err + if len(fn.Region) == 0 { + fn.Region = GoogleRegions[0] } - f.Status = statuses[multitenantPrefix+"-"+f.Name] - rsp.Functions = append(rsp.Functions, f) + + if len(fn.Branch) == 0 { + fn.Branch = "master" + } + // set the custom domain + if len(e.domain) > 0 { + fn.Url = fmt.Sprintf("https://%s.%s", fn.Id, e.domain) + } + + rsp.Functions = append(rsp.Functions, fn) } return nil @@ -493,20 +673,41 @@ func (e *GoogleFunction) Describe(ctx context.Context, req *function.DescribeReq tenantId = "micro" } - project := req.Project - if project == "" { - project = "default" - } - - multitenantPrefix := strings.Replace(tenantId, "/", "-", -1) - key := fmt.Sprintf("function/%v/%v/%v", tenantId, project, req.Name) + key := fmt.Sprintf(OwnerKey+"%v/%v", tenantId, req.Name) records, err := store.Read(key) if err != nil { return err } - cmd := exec.Command("gcloud", "functions", "describe", "--region", "europe-west1", "--project", e.project, multitenantPrefix+"-"+req.Name) + if len(records) == 0 { + return errors.NotFound("function.describe", "function does not exist") + } + + fn := new(function.Func) + + if err := records[0].Decode(fn); err != nil { + return err + } + + if len(fn.Region) == 0 { + fn.Region = GoogleRegions[0] + } + + if len(fn.Branch) == 0 { + fn.Branch = "master" + } + + // set the custom domain + if len(e.domain) > 0 { + fn.Url = fmt.Sprintf("https://%s.%s", fn.Id, e.domain) + } + + // set the response function + rsp.Function = fn + + // get the current status + cmd := exec.Command("gcloud", "functions", "describe", "--format", "json", "--region", fn.Region, "--project", e.project, fn.Id) outp, err := cmd.CombinedOutput() if err != nil { log.Error(fmt.Errorf(string(outp))) @@ -515,28 +716,65 @@ func (e *GoogleFunction) Describe(ctx context.Context, req *function.DescribeReq log.Info(string(outp)) m := map[string]interface{}{} - err = yaml.Unmarshal(outp, m) + + if err := json.Unmarshal(outp, &m); err != nil { + return err + } + + // set describe info + status := m["status"].(string) + status = strings.Replace(status, "_", " ", -1) + status = strings.Title(strings.ToLower(status)) + fn.Status = status + + // set the url + if len(fn.Url) == 0 && status == "Active" { + v := m["httpsTrigger"].(map[string]interface{}) + fn.Url = v["url"].(string) + } + + // write it back + go store.Write(store.NewRecord(key, fn)) + + rsp.Function.Status = status + + return nil +} + +func (g *GoogleFunction) Proxy(ctx context.Context, req *function.ProxyRequest, rsp *function.ProxyResponse) error { + if len(req.Id) == 0 { + return errors.BadRequest("function.proxy", "missing id") + } + + if !IDFormat.MatchString(req.Id) { + return errors.BadRequest("function.proxy", "invalid id") + } + + recs, err := store.Read(FunctionKey+req.Id, store.ReadLimit(1)) if err != nil { return err } - if len(records) > 0 { - f := &function.Func{} - if err := records[0].Decode(f); err != nil { - return err - } - rsp.Function = f - } else { - rsp.Function = &function.Func{ - Name: req.Name, - Project: req.Project, - } + if len(recs) == 0 { + return errors.BadRequest("function.proxy", "function does not exist") } - // set describe info - rsp.Function.Status = m["status"].(string) - rsp.Timeout = m["timeout"].(string) - rsp.UpdatedAt = m["updateTime"].(string) + fn := new(function.Func) + recs[0].Decode(fn) + + url := fn.Url + + // backup plan is to construct https://region-project.cloudfunctions.net/function-name + if len(url) == 0 { + url = fmt.Sprintf("https://%s-%s.cloudfunctions.net/%s", fn.Region, g.project, fn.Id) + } + + rsp.Url = url return nil } + +func (e *GoogleFunction) Regions(ctx context.Context, req *function.RegionsRequest, rsp *function.RegionsResponse) error { + rsp.Regions = GoogleRegions + return nil +} diff --git a/function/handler/reserve.go b/function/handler/reserve.go new file mode 100644 index 0000000..a8cb5b8 --- /dev/null +++ b/function/handler/reserve.go @@ -0,0 +1,128 @@ +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 +} diff --git a/function/proto/function.pb.go b/function/proto/function.pb.go index 1253946..a924b8e 100644 --- a/function/proto/function.pb.go +++ b/function/proto/function.pb.go @@ -137,25 +137,22 @@ type DeployRequest struct { Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` // github url to repo Repo string `protobuf:"bytes,2,opt,name=repo,proto3" json:"repo,omitempty"` + // branch to deploy. defaults to master + Branch string `protobuf:"bytes,3,opt,name=branch,proto3" json:"branch,omitempty"` // optional subfolder path - Subfolder string `protobuf:"bytes,3,opt,name=subfolder,proto3" json:"subfolder,omitempty"` + Subfolder string `protobuf:"bytes,4,opt,name=subfolder,proto3" json:"subfolder,omitempty"` // entry point, ie. handler name in the source code // if not provided, defaults to the name parameter - Entrypoint string `protobuf:"bytes,4,opt,name=entrypoint,proto3" json:"entrypoint,omitempty"` - // project is used for namespacing your functions - // optional. defaults to "default". - Project string `protobuf:"bytes,5,opt,name=project,proto3" json:"project,omitempty"` - // runtime/language of the function - // eg: php74, - // nodejs6, nodejs8, nodejs10, nodejs12, nodejs14, nodejs16 - // dotnet3 - // java11 - // ruby26, ruby27 - // go111, go113, go116 + Entrypoint string `protobuf:"bytes,5,opt,name=entrypoint,proto3" json:"entrypoint,omitempty"` + // runtime/lanaguage of the function e.g php74, + // nodejs6, nodejs8, nodejs10, nodejs12, nodejs14, nodejs16, + // dotnet3, java11, ruby26, ruby27, go111, go113, go116, // python37, python38, python39 Runtime string `protobuf:"bytes,6,opt,name=runtime,proto3" json:"runtime,omitempty"` + // region to deploy in. defaults to europe-west1 + Region string `protobuf:"bytes,7,opt,name=region,proto3" json:"region,omitempty"` // environment variables to pass in at runtime - EnvVars map[string]string `protobuf:"bytes,7,rep,name=env_vars,json=envVars,proto3" json:"env_vars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + EnvVars map[string]string `protobuf:"bytes,8,rep,name=env_vars,json=envVars,proto3" json:"env_vars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *DeployRequest) Reset() { @@ -204,6 +201,13 @@ func (x *DeployRequest) GetRepo() string { return "" } +func (x *DeployRequest) GetBranch() string { + if x != nil { + return x.Branch + } + return "" +} + func (x *DeployRequest) GetSubfolder() string { if x != nil { return x.Subfolder @@ -218,16 +222,16 @@ func (x *DeployRequest) GetEntrypoint() string { return "" } -func (x *DeployRequest) GetProject() string { +func (x *DeployRequest) GetRuntime() string { if x != nil { - return x.Project + return x.Runtime } return "" } -func (x *DeployRequest) GetRuntime() string { +func (x *DeployRequest) GetRegion() string { if x != nil { - return x.Runtime + return x.Region } return "" } @@ -243,6 +247,8 @@ type DeployResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields + + Function *Func `protobuf:"bytes,1,opt,name=function,proto3" json:"function,omitempty"` } func (x *DeployResponse) Reset() { @@ -277,14 +283,18 @@ func (*DeployResponse) Descriptor() ([]byte, []int) { return file_proto_function_proto_rawDescGZIP(), []int{3} } +func (x *DeployResponse) GetFunction() *Func { + if x != nil { + return x.Function + } + return nil +} + // List all the deployed functions type ListRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - - // optional project name - Project string `protobuf:"bytes,1,opt,name=project,proto3" json:"project,omitempty"` } func (x *ListRequest) Reset() { @@ -319,44 +329,41 @@ func (*ListRequest) Descriptor() ([]byte, []int) { return file_proto_function_proto_rawDescGZIP(), []int{4} } -func (x *ListRequest) GetProject() string { - if x != nil { - return x.Project - } - return "" -} - type Func struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - // project of function, optional - // defaults to literal "default" - // used to namespace functions - Project string `protobuf:"bytes,1,opt,name=project,proto3" json:"project,omitempty"` + // id of the function + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` // function name // limitation: must be unique across projects Name string `protobuf:"bytes,2,opt,name=name,proto3" json:"name,omitempty"` - // name of handler in source code - Entrypoint string `protobuf:"bytes,3,opt,name=entrypoint,proto3" json:"entrypoint,omitempty"` // git repo address - Repo string `protobuf:"bytes,4,opt,name=repo,proto3" json:"repo,omitempty"` + Repo string `protobuf:"bytes,3,opt,name=repo,proto3" json:"repo,omitempty"` + // branch to deploy. defaults to master + Branch string `protobuf:"bytes,4,opt,name=branch,proto3" json:"branch,omitempty"` + // name of handler in source code + Entrypoint string `protobuf:"bytes,5,opt,name=entrypoint,proto3" json:"entrypoint,omitempty"` // subfolder path to entrypoint - Subfolder string `protobuf:"bytes,5,opt,name=subfolder,proto3" json:"subfolder,omitempty"` - // runtime/language of the function - // eg: php74, - // nodejs6, nodejs8, nodejs10, nodejs12, nodejs14, nodejs16 - // dotnet3 - // java11 - // ruby26, ruby27 - // go111, go113, go116 + Subfolder string `protobuf:"bytes,6,opt,name=subfolder,proto3" json:"subfolder,omitempty"` + // runtime/language of the function e.g php74, + // nodejs6, nodejs8, nodejs10, nodejs12, nodejs14, nodejs16, + // dotnet3, java11, ruby26, ruby27, go111, go113, go116, // python37, python38, python39 - Runtime string `protobuf:"bytes,6,opt,name=runtime,proto3" json:"runtime,omitempty"` - // eg. ACTIVE, DEPLOY_IN_PROGRESS, OFFLINE etc - Status string `protobuf:"bytes,7,opt,name=status,proto3" json:"status,omitempty"` + Runtime string `protobuf:"bytes,7,opt,name=runtime,proto3" json:"runtime,omitempty"` + // region to deploy in. defaults to europe-west1 + Region string `protobuf:"bytes,8,opt,name=region,proto3" json:"region,omitempty"` // associated env vars - EnvVars map[string]string `protobuf:"bytes,8,rep,name=env_vars,json=envVars,proto3" json:"env_vars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + EnvVars map[string]string `protobuf:"bytes,9,rep,name=env_vars,json=envVars,proto3" json:"env_vars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` + // eg. ACTIVE, DEPLOY_IN_PROGRESS, OFFLINE etc + Status string `protobuf:"bytes,10,opt,name=status,proto3" json:"status,omitempty"` + // unique url of the function + Url string `protobuf:"bytes,11,opt,name=url,proto3" json:"url,omitempty"` + // time of creation + Created string `protobuf:"bytes,12,opt,name=created,proto3" json:"created,omitempty"` + // time it was updated + Updated string `protobuf:"bytes,13,opt,name=updated,proto3" json:"updated,omitempty"` } func (x *Func) Reset() { @@ -391,9 +398,9 @@ func (*Func) Descriptor() ([]byte, []int) { return file_proto_function_proto_rawDescGZIP(), []int{5} } -func (x *Func) GetProject() string { +func (x *Func) GetId() string { if x != nil { - return x.Project + return x.Id } return "" } @@ -405,16 +412,23 @@ func (x *Func) GetName() string { return "" } -func (x *Func) GetEntrypoint() string { +func (x *Func) GetRepo() string { if x != nil { - return x.Entrypoint + return x.Repo } return "" } -func (x *Func) GetRepo() string { +func (x *Func) GetBranch() string { if x != nil { - return x.Repo + return x.Branch + } + return "" +} + +func (x *Func) GetEntrypoint() string { + if x != nil { + return x.Entrypoint } return "" } @@ -433,9 +447,9 @@ func (x *Func) GetRuntime() string { return "" } -func (x *Func) GetStatus() string { +func (x *Func) GetRegion() string { if x != nil { - return x.Status + return x.Region } return "" } @@ -447,6 +461,34 @@ func (x *Func) GetEnvVars() map[string]string { return nil } +func (x *Func) GetStatus() string { + if x != nil { + return x.Status + } + return "" +} + +func (x *Func) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +func (x *Func) GetCreated() string { + if x != nil { + return x.Created + } + return "" +} + +func (x *Func) GetUpdated() string { + if x != nil { + return x.Updated + } + return "" +} + type ListResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -503,8 +545,6 @@ type DeleteRequest struct { // The name of the function Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Optional project name - Project string `protobuf:"bytes,2,opt,name=project,proto3" json:"project,omitempty"` } func (x *DeleteRequest) Reset() { @@ -546,13 +586,6 @@ func (x *DeleteRequest) GetName() string { return "" } -func (x *DeleteRequest) GetProject() string { - if x != nil { - return x.Project - } - return "" -} - type DeleteResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -599,8 +632,6 @@ type DescribeRequest struct { // The name of the function Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // Optional project name - Project string `protobuf:"bytes,2,opt,name=project,proto3" json:"project,omitempty"` } func (x *DescribeRequest) Reset() { @@ -642,13 +673,6 @@ func (x *DescribeRequest) GetName() string { return "" } -func (x *DescribeRequest) GetProject() string { - if x != nil { - return x.Project - } - return "" -} - type DescribeResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -656,10 +680,6 @@ type DescribeResponse struct { // The function requested Function *Func `protobuf:"bytes,1,opt,name=function,proto3" json:"function,omitempty"` - // The time at which the function was updated - UpdatedAt string `protobuf:"bytes,2,opt,name=updated_at,json=updatedAt,proto3" json:"updated_at,omitempty"` - // The timeout for requests to the function - Timeout string `protobuf:"bytes,3,opt,name=timeout,proto3" json:"timeout,omitempty"` } func (x *DescribeResponse) Reset() { @@ -701,20 +721,7 @@ func (x *DescribeResponse) GetFunction() *Func { return nil } -func (x *DescribeResponse) GetUpdatedAt() string { - if x != nil { - return x.UpdatedAt - } - return "" -} - -func (x *DescribeResponse) GetTimeout() string { - if x != nil { - return x.Timeout - } - return "" -} - +// Update a function. Downloads the source, builds and redeploys type UpdateRequest struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -722,27 +729,6 @@ type UpdateRequest struct { // function name Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` - // github url to repo - Repo string `protobuf:"bytes,2,opt,name=repo,proto3" json:"repo,omitempty"` - // optional subfolder path - Subfolder string `protobuf:"bytes,3,opt,name=subfolder,proto3" json:"subfolder,omitempty"` - // entry point, ie. handler name in the source code - // if not provided, defaults to the name parameter - Entrypoint string `protobuf:"bytes,4,opt,name=entrypoint,proto3" json:"entrypoint,omitempty"` - // project is used for namespacing your functions - // optional. defaults to "default". - Project string `protobuf:"bytes,5,opt,name=project,proto3" json:"project,omitempty"` - // runtime/language of the function - // eg: php74, - // nodejs6, nodejs8, nodejs10, nodejs12, nodejs14, nodejs16 - // dotnet3 - // java11 - // ruby26, ruby27 - // go111, go113, go116 - // python37, python38, python39 - Runtime string `protobuf:"bytes,6,opt,name=runtime,proto3" json:"runtime,omitempty"` - // environment variables to pass in at runtime - EnvVars map[string]string `protobuf:"bytes,7,rep,name=env_vars,json=envVars,proto3" json:"env_vars,omitempty" protobuf_key:"bytes,1,opt,name=key,proto3" protobuf_val:"bytes,2,opt,name=value,proto3"` } func (x *UpdateRequest) Reset() { @@ -784,48 +770,6 @@ func (x *UpdateRequest) GetName() string { return "" } -func (x *UpdateRequest) GetRepo() string { - if x != nil { - return x.Repo - } - return "" -} - -func (x *UpdateRequest) GetSubfolder() string { - if x != nil { - return x.Subfolder - } - return "" -} - -func (x *UpdateRequest) GetEntrypoint() string { - if x != nil { - return x.Entrypoint - } - return "" -} - -func (x *UpdateRequest) GetProject() string { - if x != nil { - return x.Project - } - return "" -} - -func (x *UpdateRequest) GetRuntime() string { - if x != nil { - return x.Runtime - } - return "" -} - -func (x *UpdateRequest) GetEnvVars() map[string]string { - if x != nil { - return x.EnvVars - } - return nil -} - type UpdateResponse struct { state protoimpl.MessageState sizeCache protoimpl.SizeCache @@ -864,6 +808,371 @@ func (*UpdateResponse) Descriptor() ([]byte, []int) { return file_proto_function_proto_rawDescGZIP(), []int{12} } +// Return the backend url for proxying +type ProxyRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // id of the function + Id string `protobuf:"bytes,1,opt,name=id,proto3" json:"id,omitempty"` +} + +func (x *ProxyRequest) Reset() { + *x = ProxyRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_function_proto_msgTypes[13] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProxyRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProxyRequest) ProtoMessage() {} + +func (x *ProxyRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_function_proto_msgTypes[13] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProxyRequest.ProtoReflect.Descriptor instead. +func (*ProxyRequest) Descriptor() ([]byte, []int) { + return file_proto_function_proto_rawDescGZIP(), []int{13} +} + +func (x *ProxyRequest) GetId() string { + if x != nil { + return x.Id + } + return "" +} + +type ProxyResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // backend url + Url string `protobuf:"bytes,1,opt,name=url,proto3" json:"url,omitempty"` +} + +func (x *ProxyResponse) Reset() { + *x = ProxyResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_function_proto_msgTypes[14] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ProxyResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ProxyResponse) ProtoMessage() {} + +func (x *ProxyResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_function_proto_msgTypes[14] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ProxyResponse.ProtoReflect.Descriptor instead. +func (*ProxyResponse) Descriptor() ([]byte, []int) { + return file_proto_function_proto_rawDescGZIP(), []int{14} +} + +func (x *ProxyResponse) GetUrl() string { + if x != nil { + return x.Url + } + return "" +} + +// Return a list of supported regions +type RegionsRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields +} + +func (x *RegionsRequest) Reset() { + *x = RegionsRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_function_proto_msgTypes[15] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegionsRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegionsRequest) ProtoMessage() {} + +func (x *RegionsRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_function_proto_msgTypes[15] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegionsRequest.ProtoReflect.Descriptor instead. +func (*RegionsRequest) Descriptor() ([]byte, []int) { + return file_proto_function_proto_rawDescGZIP(), []int{15} +} + +type RegionsResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + Regions []string `protobuf:"bytes,1,rep,name=regions,proto3" json:"regions,omitempty"` +} + +func (x *RegionsResponse) Reset() { + *x = RegionsResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_function_proto_msgTypes[16] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *RegionsResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*RegionsResponse) ProtoMessage() {} + +func (x *RegionsResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_function_proto_msgTypes[16] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use RegionsResponse.ProtoReflect.Descriptor instead. +func (*RegionsResponse) Descriptor() ([]byte, []int) { + return file_proto_function_proto_rawDescGZIP(), []int{16} +} + +func (x *RegionsResponse) GetRegions() []string { + if x != nil { + return x.Regions + } + return nil +} + +// Reservation represents a reserved function +type Reservation struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // name of the app + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` + // owner id + Owner string `protobuf:"bytes,2,opt,name=owner,proto3" json:"owner,omitempty"` + // associated token + Token string `protobuf:"bytes,3,opt,name=token,proto3" json:"token,omitempty"` + // time of reservation + Created string `protobuf:"bytes,4,opt,name=created,proto3" json:"created,omitempty"` + // time reservation expires + Expires string `protobuf:"bytes,5,opt,name=expires,proto3" json:"expires,omitempty"` +} + +func (x *Reservation) Reset() { + *x = Reservation{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_function_proto_msgTypes[17] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *Reservation) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*Reservation) ProtoMessage() {} + +func (x *Reservation) ProtoReflect() protoreflect.Message { + mi := &file_proto_function_proto_msgTypes[17] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use Reservation.ProtoReflect.Descriptor instead. +func (*Reservation) Descriptor() ([]byte, []int) { + return file_proto_function_proto_rawDescGZIP(), []int{17} +} + +func (x *Reservation) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +func (x *Reservation) GetOwner() string { + if x != nil { + return x.Owner + } + return "" +} + +func (x *Reservation) GetToken() string { + if x != nil { + return x.Token + } + return "" +} + +func (x *Reservation) GetCreated() string { + if x != nil { + return x.Created + } + return "" +} + +func (x *Reservation) GetExpires() string { + if x != nil { + return x.Expires + } + return "" +} + +// Reserve function names and resources beyond free quota +type ReserveRequest struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // name of your app e.g helloworld + Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"` +} + +func (x *ReserveRequest) Reset() { + *x = ReserveRequest{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_function_proto_msgTypes[18] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReserveRequest) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReserveRequest) ProtoMessage() {} + +func (x *ReserveRequest) ProtoReflect() protoreflect.Message { + mi := &file_proto_function_proto_msgTypes[18] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReserveRequest.ProtoReflect.Descriptor instead. +func (*ReserveRequest) Descriptor() ([]byte, []int) { + return file_proto_function_proto_rawDescGZIP(), []int{18} +} + +func (x *ReserveRequest) GetName() string { + if x != nil { + return x.Name + } + return "" +} + +type ReserveResponse struct { + state protoimpl.MessageState + sizeCache protoimpl.SizeCache + unknownFields protoimpl.UnknownFields + + // The app reservation + Reservation *Reservation `protobuf:"bytes,1,opt,name=reservation,proto3" json:"reservation,omitempty"` +} + +func (x *ReserveResponse) Reset() { + *x = ReserveResponse{} + if protoimpl.UnsafeEnabled { + mi := &file_proto_function_proto_msgTypes[19] + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + ms.StoreMessageInfo(mi) + } +} + +func (x *ReserveResponse) String() string { + return protoimpl.X.MessageStringOf(x) +} + +func (*ReserveResponse) ProtoMessage() {} + +func (x *ReserveResponse) ProtoReflect() protoreflect.Message { + mi := &file_proto_function_proto_msgTypes[19] + if protoimpl.UnsafeEnabled && x != nil { + ms := protoimpl.X.MessageStateOf(protoimpl.Pointer(x)) + if ms.LoadMessageInfo() == nil { + ms.StoreMessageInfo(mi) + } + return ms + } + return mi.MessageOf(x) +} + +// Deprecated: Use ReserveResponse.ProtoReflect.Descriptor instead. +func (*ReserveResponse) Descriptor() ([]byte, []int) { + return file_proto_function_proto_rawDescGZIP(), []int{19} +} + +func (x *ReserveResponse) GetReservation() *Reservation { + if x != nil { + return x.Reservation + } + return nil +} + var File_proto_function_proto protoreflect.FileDescriptor var file_proto_function_proto_rawDesc = []byte{ @@ -880,114 +1189,136 @@ var file_proto_function_proto_rawDesc = []byte{ 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x33, 0x0a, 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x17, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x53, 0x74, 0x72, 0x75, 0x63, 0x74, 0x52, - 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xa6, 0x02, 0x0a, 0x0d, 0x44, 0x65, + 0x08, 0x72, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0xbc, 0x02, 0x0a, 0x0d, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, - 0x65, 0x70, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, - 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, 0x66, 0x6f, 0x6c, 0x64, 0x65, - 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, - 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, - 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x72, - 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, - 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x3f, 0x0a, 0x08, 0x65, 0x6e, 0x76, 0x5f, 0x76, 0x61, 0x72, - 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, - 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x65, 0x70, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x18, 0x03, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1c, 0x0a, 0x09, 0x73, + 0x75, 0x62, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, + 0x73, 0x75, 0x62, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x74, + 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, + 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6e, + 0x74, 0x69, 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x74, + 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x18, 0x07, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x3f, 0x0a, 0x08, 0x65, + 0x6e, 0x76, 0x5f, 0x76, 0x61, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, + 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x76, 0x56, 0x61, 0x72, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, + 0x45, 0x6e, 0x76, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, + 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, + 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, + 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3c, 0x0a, 0x0e, 0x44, 0x65, 0x70, 0x6c, + 0x6f, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x46, 0x75, 0x6e, 0x63, 0x52, 0x08, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x0d, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, + 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x98, 0x03, 0x0a, 0x04, 0x46, 0x75, 0x6e, 0x63, 0x12, 0x0e, + 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x02, 0x69, 0x64, 0x12, 0x12, + 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, + 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x12, 0x16, 0x0a, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x62, 0x72, 0x61, 0x6e, 0x63, 0x68, 0x12, 0x1e, + 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x05, 0x20, 0x01, + 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x1c, + 0x0a, 0x09, 0x73, 0x75, 0x62, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x06, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, + 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, + 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, 0x12, 0x16, 0x0a, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, + 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x72, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x12, 0x36, + 0x0a, 0x08, 0x65, 0x6e, 0x76, 0x5f, 0x76, 0x61, 0x72, 0x73, 0x18, 0x09, 0x20, 0x03, 0x28, 0x0b, + 0x32, 0x1b, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x46, 0x75, 0x6e, 0x63, 0x2e, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, - 0x6e, 0x76, 0x56, 0x61, 0x72, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x72, - 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, - 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, - 0x38, 0x01, 0x22, 0x10, 0x0a, 0x0e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x27, 0x0a, 0x0b, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, - 0x65, 0x73, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x01, - 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0xac, 0x02, - 0x0a, 0x04, 0x46, 0x75, 0x6e, 0x63, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, - 0x74, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, - 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, - 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x1e, 0x0a, 0x0a, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, - 0x6e, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x70, - 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x18, 0x04, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x12, 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x66, - 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, - 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, 0x65, - 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x36, 0x0a, 0x08, 0x65, 0x6e, 0x76, 0x5f, - 0x76, 0x61, 0x72, 0x73, 0x18, 0x08, 0x20, 0x03, 0x28, 0x0b, 0x32, 0x1b, 0x2e, 0x66, 0x75, 0x6e, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x46, 0x75, 0x6e, 0x63, 0x2e, 0x45, 0x6e, 0x76, 0x56, 0x61, - 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x76, 0x56, 0x61, 0x72, 0x73, - 0x1a, 0x3a, 0x0a, 0x0c, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, - 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x6b, - 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x3c, 0x0a, 0x0c, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2c, 0x0a, 0x09, - 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x0b, 0x32, - 0x0e, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x46, 0x75, 0x6e, 0x63, 0x52, - 0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x3d, 0x0a, 0x0d, 0x44, 0x65, - 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, - 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, - 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x10, 0x0a, 0x0e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x3f, 0x0a, 0x0f, 0x44, - 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, - 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, - 0x6d, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x22, 0x77, 0x0a, 0x10, + 0x6e, 0x76, 0x56, 0x61, 0x72, 0x73, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, + 0x18, 0x0a, 0x20, 0x01, 0x28, 0x09, 0x52, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73, 0x12, 0x10, + 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, + 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0c, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, 0x18, 0x0a, 0x07, 0x75, 0x70, + 0x64, 0x61, 0x74, 0x65, 0x64, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x75, 0x70, 0x64, + 0x61, 0x74, 0x65, 0x64, 0x1a, 0x3a, 0x0a, 0x0c, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x72, 0x73, 0x45, + 0x6e, 0x74, 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, + 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, + 0x22, 0x3c, 0x0a, 0x0c, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, + 0x12, 0x2c, 0x0a, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, + 0x03, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x46, + 0x75, 0x6e, 0x63, 0x52, 0x09, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x23, + 0x0a, 0x0d, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, + 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x22, 0x10, 0x0a, 0x0e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, + 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x25, 0x0a, 0x0f, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, + 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, + 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, 0x3e, 0x0a, 0x10, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x2a, 0x0a, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x0e, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x46, 0x75, - 0x6e, 0x63, 0x52, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x1d, 0x0a, 0x0a, - 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x5f, 0x61, 0x74, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, - 0x52, 0x09, 0x75, 0x70, 0x64, 0x61, 0x74, 0x65, 0x64, 0x41, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x74, - 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x74, 0x69, - 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x22, 0xa6, 0x02, 0x0a, 0x0d, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, - 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, - 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x12, 0x0a, 0x04, 0x72, - 0x65, 0x70, 0x6f, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x72, 0x65, 0x70, 0x6f, 0x12, - 0x1c, 0x0a, 0x09, 0x73, 0x75, 0x62, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x18, 0x03, 0x20, 0x01, - 0x28, 0x09, 0x52, 0x09, 0x73, 0x75, 0x62, 0x66, 0x6f, 0x6c, 0x64, 0x65, 0x72, 0x12, 0x1e, 0x0a, - 0x0a, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x18, 0x04, 0x20, 0x01, 0x28, - 0x09, 0x52, 0x0a, 0x65, 0x6e, 0x74, 0x72, 0x79, 0x70, 0x6f, 0x69, 0x6e, 0x74, 0x12, 0x18, 0x0a, - 0x07, 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, - 0x70, 0x72, 0x6f, 0x6a, 0x65, 0x63, 0x74, 0x12, 0x18, 0x0a, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, - 0x6d, 0x65, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x72, 0x75, 0x6e, 0x74, 0x69, 0x6d, - 0x65, 0x12, 0x3f, 0x0a, 0x08, 0x65, 0x6e, 0x76, 0x5f, 0x76, 0x61, 0x72, 0x73, 0x18, 0x07, 0x20, - 0x03, 0x28, 0x0b, 0x32, 0x24, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, - 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x45, 0x6e, 0x76, - 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, 0x72, 0x79, 0x52, 0x07, 0x65, 0x6e, 0x76, 0x56, 0x61, - 0x72, 0x73, 0x1a, 0x3a, 0x0a, 0x0c, 0x45, 0x6e, 0x76, 0x56, 0x61, 0x72, 0x73, 0x45, 0x6e, 0x74, - 0x72, 0x79, 0x12, 0x10, 0x0a, 0x03, 0x6b, 0x65, 0x79, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, - 0x03, 0x6b, 0x65, 0x79, 0x12, 0x14, 0x0a, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x18, 0x02, 0x20, - 0x01, 0x28, 0x09, 0x52, 0x05, 0x76, 0x61, 0x6c, 0x75, 0x65, 0x3a, 0x02, 0x38, 0x01, 0x22, 0x10, - 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x32, 0xfe, 0x02, 0x0a, 0x08, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, - 0x04, 0x43, 0x61, 0x6c, 0x6c, 0x12, 0x15, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x66, - 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, - 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x06, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, - 0x12, 0x17, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x70, 0x6c, - 0x6f, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x66, 0x75, 0x6e, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, - 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x37, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x15, 0x2e, - 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3d, - 0x0a, 0x06, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, - 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, - 0x74, 0x1a, 0x18, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6c, - 0x65, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x43, 0x0a, - 0x08, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x12, 0x19, 0x2e, 0x66, 0x75, 0x6e, 0x63, - 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, - 0x75, 0x65, 0x73, 0x74, 0x1a, 0x1a, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, - 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, - 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x66, - 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, - 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, - 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, - 0x00, 0x42, 0x12, 0x5a, 0x10, 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x66, 0x75, 0x6e, - 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x6e, 0x63, 0x52, 0x08, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x22, 0x23, 0x0a, 0x0d, + 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, + 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, + 0x65, 0x22, 0x10, 0x0a, 0x0e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, + 0x6e, 0x73, 0x65, 0x22, 0x1e, 0x0a, 0x0c, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, + 0x65, 0x73, 0x74, 0x12, 0x0e, 0x0a, 0x02, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, + 0x02, 0x69, 0x64, 0x22, 0x21, 0x0a, 0x0d, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x73, 0x70, + 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x10, 0x0a, 0x03, 0x75, 0x72, 0x6c, 0x18, 0x01, 0x20, 0x01, 0x28, + 0x09, 0x52, 0x03, 0x75, 0x72, 0x6c, 0x22, 0x10, 0x0a, 0x0e, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, + 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x22, 0x2b, 0x0a, 0x0f, 0x52, 0x65, 0x67, 0x69, + 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x12, 0x18, 0x0a, 0x07, 0x72, + 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x73, 0x18, 0x01, 0x20, 0x03, 0x28, 0x09, 0x52, 0x07, 0x72, 0x65, + 0x67, 0x69, 0x6f, 0x6e, 0x73, 0x22, 0x81, 0x01, 0x0a, 0x0b, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, + 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x12, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, + 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x12, 0x14, 0x0a, 0x05, 0x6f, 0x77, 0x6e, + 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, 0x6f, 0x77, 0x6e, 0x65, 0x72, 0x12, + 0x14, 0x0a, 0x05, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x52, 0x05, + 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x18, 0x0a, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, + 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x63, 0x72, 0x65, 0x61, 0x74, 0x65, 0x64, 0x12, + 0x18, 0x0a, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, + 0x52, 0x07, 0x65, 0x78, 0x70, 0x69, 0x72, 0x65, 0x73, 0x22, 0x24, 0x0a, 0x0e, 0x52, 0x65, 0x73, + 0x65, 0x72, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x12, 0x0a, 0x04, 0x6e, + 0x61, 0x6d, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x22, + 0x4a, 0x0a, 0x0f, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, + 0x73, 0x65, 0x12, 0x37, 0x0a, 0x0b, 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, + 0x6e, 0x18, 0x01, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x15, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x0b, + 0x72, 0x65, 0x73, 0x65, 0x72, 0x76, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x32, 0xbe, 0x04, 0x0a, 0x08, + 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x12, 0x37, 0x0a, 0x04, 0x43, 0x61, 0x6c, 0x6c, + 0x12, 0x15, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x6c, 0x6c, + 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x16, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x43, 0x61, 0x6c, 0x6c, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, + 0x00, 0x12, 0x3d, 0x0a, 0x06, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x12, 0x17, 0x2e, 0x66, 0x75, + 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x71, + 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x44, 0x65, 0x70, 0x6c, 0x6f, 0x79, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, + 0x12, 0x37, 0x0a, 0x04, 0x4c, 0x69, 0x73, 0x74, 0x12, 0x15, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, + 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x16, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x4c, 0x69, 0x73, 0x74, 0x52, + 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3d, 0x0a, 0x06, 0x44, 0x65, 0x6c, + 0x65, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, + 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x18, 0x2e, 0x66, + 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x6c, 0x65, 0x74, 0x65, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x43, 0x0a, 0x08, 0x44, 0x65, 0x73, 0x63, + 0x72, 0x69, 0x62, 0x65, 0x12, 0x19, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, + 0x44, 0x65, 0x73, 0x63, 0x72, 0x69, 0x62, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x1a, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x44, 0x65, 0x73, 0x63, 0x72, + 0x69, 0x62, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3d, 0x0a, + 0x06, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x12, 0x17, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, + 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x64, 0x61, 0x74, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, + 0x1a, 0x18, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x55, 0x70, 0x64, 0x61, + 0x74, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x3a, 0x0a, 0x05, + 0x50, 0x72, 0x6f, 0x78, 0x79, 0x12, 0x16, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x17, 0x2e, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x50, 0x72, 0x6f, 0x78, 0x79, 0x52, 0x65, + 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x07, 0x52, 0x65, 0x67, 0x69, + 0x6f, 0x6e, 0x73, 0x12, 0x18, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, + 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x73, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, 0x19, 0x2e, + 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x67, 0x69, 0x6f, 0x6e, 0x73, + 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x12, 0x40, 0x0a, 0x07, 0x52, 0x65, + 0x73, 0x65, 0x72, 0x76, 0x65, 0x12, 0x18, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x2e, 0x52, 0x65, 0x73, 0x65, 0x72, 0x76, 0x65, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x1a, + 0x19, 0x2e, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x2e, 0x52, 0x65, 0x73, 0x65, 0x72, + 0x76, 0x65, 0x52, 0x65, 0x73, 0x70, 0x6f, 0x6e, 0x73, 0x65, 0x22, 0x00, 0x42, 0x12, 0x5a, 0x10, + 0x2e, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x3b, 0x66, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, + 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, } var ( @@ -1002,7 +1333,7 @@ func file_proto_function_proto_rawDescGZIP() []byte { return file_proto_function_proto_rawDescData } -var file_proto_function_proto_msgTypes = make([]protoimpl.MessageInfo, 16) +var file_proto_function_proto_msgTypes = make([]protoimpl.MessageInfo, 22) var file_proto_function_proto_goTypes = []interface{}{ (*CallRequest)(nil), // 0: function.CallRequest (*CallResponse)(nil), // 1: function.CallResponse @@ -1017,36 +1348,49 @@ var file_proto_function_proto_goTypes = []interface{}{ (*DescribeResponse)(nil), // 10: function.DescribeResponse (*UpdateRequest)(nil), // 11: function.UpdateRequest (*UpdateResponse)(nil), // 12: function.UpdateResponse - nil, // 13: function.DeployRequest.EnvVarsEntry - nil, // 14: function.Func.EnvVarsEntry - nil, // 15: function.UpdateRequest.EnvVarsEntry - (*structpb.Struct)(nil), // 16: google.protobuf.Struct + (*ProxyRequest)(nil), // 13: function.ProxyRequest + (*ProxyResponse)(nil), // 14: function.ProxyResponse + (*RegionsRequest)(nil), // 15: function.RegionsRequest + (*RegionsResponse)(nil), // 16: function.RegionsResponse + (*Reservation)(nil), // 17: function.Reservation + (*ReserveRequest)(nil), // 18: function.ReserveRequest + (*ReserveResponse)(nil), // 19: function.ReserveResponse + nil, // 20: function.DeployRequest.EnvVarsEntry + nil, // 21: function.Func.EnvVarsEntry + (*structpb.Struct)(nil), // 22: google.protobuf.Struct } var file_proto_function_proto_depIdxs = []int32{ - 16, // 0: function.CallRequest.request:type_name -> google.protobuf.Struct - 16, // 1: function.CallResponse.response:type_name -> google.protobuf.Struct - 13, // 2: function.DeployRequest.env_vars:type_name -> function.DeployRequest.EnvVarsEntry - 14, // 3: function.Func.env_vars:type_name -> function.Func.EnvVarsEntry - 5, // 4: function.ListResponse.functions:type_name -> function.Func - 5, // 5: function.DescribeResponse.function:type_name -> function.Func - 15, // 6: function.UpdateRequest.env_vars:type_name -> function.UpdateRequest.EnvVarsEntry - 0, // 7: function.Function.Call:input_type -> function.CallRequest - 2, // 8: function.Function.Deploy:input_type -> function.DeployRequest - 4, // 9: function.Function.List:input_type -> function.ListRequest - 7, // 10: function.Function.Delete:input_type -> function.DeleteRequest - 9, // 11: function.Function.Describe:input_type -> function.DescribeRequest - 11, // 12: function.Function.Update:input_type -> function.UpdateRequest - 1, // 13: function.Function.Call:output_type -> function.CallResponse - 3, // 14: function.Function.Deploy:output_type -> function.DeployResponse - 6, // 15: function.Function.List:output_type -> function.ListResponse - 8, // 16: function.Function.Delete:output_type -> function.DeleteResponse - 10, // 17: function.Function.Describe:output_type -> function.DescribeResponse - 12, // 18: function.Function.Update:output_type -> function.UpdateResponse - 13, // [13:19] is the sub-list for method output_type - 7, // [7:13] is the sub-list for method input_type - 7, // [7:7] is the sub-list for extension type_name - 7, // [7:7] is the sub-list for extension extendee - 0, // [0:7] is the sub-list for field type_name + 22, // 0: function.CallRequest.request:type_name -> google.protobuf.Struct + 22, // 1: function.CallResponse.response:type_name -> google.protobuf.Struct + 20, // 2: function.DeployRequest.env_vars:type_name -> function.DeployRequest.EnvVarsEntry + 5, // 3: function.DeployResponse.function:type_name -> function.Func + 21, // 4: function.Func.env_vars:type_name -> function.Func.EnvVarsEntry + 5, // 5: function.ListResponse.functions:type_name -> function.Func + 5, // 6: function.DescribeResponse.function:type_name -> function.Func + 17, // 7: function.ReserveResponse.reservation:type_name -> function.Reservation + 0, // 8: function.Function.Call:input_type -> function.CallRequest + 2, // 9: function.Function.Deploy:input_type -> function.DeployRequest + 4, // 10: function.Function.List:input_type -> function.ListRequest + 7, // 11: function.Function.Delete:input_type -> function.DeleteRequest + 9, // 12: function.Function.Describe:input_type -> function.DescribeRequest + 11, // 13: function.Function.Update:input_type -> function.UpdateRequest + 13, // 14: function.Function.Proxy:input_type -> function.ProxyRequest + 15, // 15: function.Function.Regions:input_type -> function.RegionsRequest + 18, // 16: function.Function.Reserve:input_type -> function.ReserveRequest + 1, // 17: function.Function.Call:output_type -> function.CallResponse + 3, // 18: function.Function.Deploy:output_type -> function.DeployResponse + 6, // 19: function.Function.List:output_type -> function.ListResponse + 8, // 20: function.Function.Delete:output_type -> function.DeleteResponse + 10, // 21: function.Function.Describe:output_type -> function.DescribeResponse + 12, // 22: function.Function.Update:output_type -> function.UpdateResponse + 14, // 23: function.Function.Proxy:output_type -> function.ProxyResponse + 16, // 24: function.Function.Regions:output_type -> function.RegionsResponse + 19, // 25: function.Function.Reserve:output_type -> function.ReserveResponse + 17, // [17:26] is the sub-list for method output_type + 8, // [8:17] is the sub-list for method input_type + 8, // [8:8] is the sub-list for extension type_name + 8, // [8:8] is the sub-list for extension extendee + 0, // [0:8] is the sub-list for field type_name } func init() { file_proto_function_proto_init() } @@ -1211,6 +1555,90 @@ func file_proto_function_proto_init() { return nil } } + file_proto_function_proto_msgTypes[13].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProxyRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_function_proto_msgTypes[14].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ProxyResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_function_proto_msgTypes[15].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegionsRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_function_proto_msgTypes[16].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*RegionsResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_function_proto_msgTypes[17].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*Reservation); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_function_proto_msgTypes[18].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReserveRequest); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } + file_proto_function_proto_msgTypes[19].Exporter = func(v interface{}, i int) interface{} { + switch v := v.(*ReserveResponse); i { + case 0: + return &v.state + case 1: + return &v.sizeCache + case 2: + return &v.unknownFields + default: + return nil + } + } } type x struct{} out := protoimpl.TypeBuilder{ @@ -1218,7 +1646,7 @@ func file_proto_function_proto_init() { GoPackagePath: reflect.TypeOf(x{}).PkgPath(), RawDescriptor: file_proto_function_proto_rawDesc, NumEnums: 0, - NumMessages: 16, + NumMessages: 22, NumExtensions: 0, NumServices: 1, }, diff --git a/function/proto/function.pb.micro.go b/function/proto/function.pb.micro.go index 5271bf2..c7d08ee 100644 --- a/function/proto/function.pb.micro.go +++ b/function/proto/function.pb.micro.go @@ -49,6 +49,9 @@ type FunctionService interface { Delete(ctx context.Context, in *DeleteRequest, opts ...client.CallOption) (*DeleteResponse, error) Describe(ctx context.Context, in *DescribeRequest, opts ...client.CallOption) (*DescribeResponse, error) Update(ctx context.Context, in *UpdateRequest, opts ...client.CallOption) (*UpdateResponse, error) + Proxy(ctx context.Context, in *ProxyRequest, opts ...client.CallOption) (*ProxyResponse, error) + Regions(ctx context.Context, in *RegionsRequest, opts ...client.CallOption) (*RegionsResponse, error) + Reserve(ctx context.Context, in *ReserveRequest, opts ...client.CallOption) (*ReserveResponse, error) } type functionService struct { @@ -123,6 +126,36 @@ func (c *functionService) Update(ctx context.Context, in *UpdateRequest, opts .. return out, nil } +func (c *functionService) Proxy(ctx context.Context, in *ProxyRequest, opts ...client.CallOption) (*ProxyResponse, error) { + req := c.c.NewRequest(c.name, "Function.Proxy", in) + out := new(ProxyResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *functionService) Regions(ctx context.Context, in *RegionsRequest, opts ...client.CallOption) (*RegionsResponse, error) { + req := c.c.NewRequest(c.name, "Function.Regions", in) + out := new(RegionsResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + +func (c *functionService) Reserve(ctx context.Context, in *ReserveRequest, opts ...client.CallOption) (*ReserveResponse, error) { + req := c.c.NewRequest(c.name, "Function.Reserve", in) + out := new(ReserveResponse) + err := c.c.Call(ctx, req, out, opts...) + if err != nil { + return nil, err + } + return out, nil +} + // Server API for Function service type FunctionHandler interface { @@ -132,6 +165,9 @@ type FunctionHandler interface { Delete(context.Context, *DeleteRequest, *DeleteResponse) error Describe(context.Context, *DescribeRequest, *DescribeResponse) error Update(context.Context, *UpdateRequest, *UpdateResponse) error + Proxy(context.Context, *ProxyRequest, *ProxyResponse) error + Regions(context.Context, *RegionsRequest, *RegionsResponse) error + Reserve(context.Context, *ReserveRequest, *ReserveResponse) error } func RegisterFunctionHandler(s server.Server, hdlr FunctionHandler, opts ...server.HandlerOption) error { @@ -142,6 +178,9 @@ func RegisterFunctionHandler(s server.Server, hdlr FunctionHandler, opts ...serv Delete(ctx context.Context, in *DeleteRequest, out *DeleteResponse) error Describe(ctx context.Context, in *DescribeRequest, out *DescribeResponse) error Update(ctx context.Context, in *UpdateRequest, out *UpdateResponse) error + Proxy(ctx context.Context, in *ProxyRequest, out *ProxyResponse) error + Regions(ctx context.Context, in *RegionsRequest, out *RegionsResponse) error + Reserve(ctx context.Context, in *ReserveRequest, out *ReserveResponse) error } type Function struct { function @@ -177,3 +216,15 @@ func (h *functionHandler) Describe(ctx context.Context, in *DescribeRequest, out func (h *functionHandler) Update(ctx context.Context, in *UpdateRequest, out *UpdateResponse) error { return h.FunctionHandler.Update(ctx, in, out) } + +func (h *functionHandler) Proxy(ctx context.Context, in *ProxyRequest, out *ProxyResponse) error { + return h.FunctionHandler.Proxy(ctx, in, out) +} + +func (h *functionHandler) Regions(ctx context.Context, in *RegionsRequest, out *RegionsResponse) error { + return h.FunctionHandler.Regions(ctx, in, out) +} + +func (h *functionHandler) Reserve(ctx context.Context, in *ReserveRequest, out *ReserveResponse) error { + return h.FunctionHandler.Reserve(ctx, in, out) +} diff --git a/function/proto/function.proto b/function/proto/function.proto index 2364371..83a0c0e 100644 --- a/function/proto/function.proto +++ b/function/proto/function.proto @@ -12,6 +12,9 @@ service Function { rpc Delete(DeleteRequest) returns (DeleteResponse) {} rpc Describe(DescribeRequest) returns (DescribeResponse) {} rpc Update(UpdateRequest) returns (UpdateResponse) {} + rpc Proxy(ProxyRequest) returns (ProxyResponse) {} + rpc Regions(RegionsRequest) returns (RegionsResponse) {} + rpc Reserve(ReserveRequest) returns (ReserveResponse) {} } // Call a function by name @@ -33,63 +36,63 @@ message DeployRequest { string name = 1; // github url to repo string repo = 2; + // branch to deploy. defaults to master + string branch = 3; // optional subfolder path - string subfolder = 3; + string subfolder = 4; // entry point, ie. handler name in the source code // if not provided, defaults to the name parameter - string entrypoint = 4; - // project is used for namespacing your functions - // optional. defaults to "default". - string project = 5; - // runtime/language of the function - // eg: php74, - // nodejs6, nodejs8, nodejs10, nodejs12, nodejs14, nodejs16 - // dotnet3 - // java11 - // ruby26, ruby27 - // go111, go113, go116 + string entrypoint = 5; + // runtime/lanaguage of the function e.g php74, + // nodejs6, nodejs8, nodejs10, nodejs12, nodejs14, nodejs16, + // dotnet3, java11, ruby26, ruby27, go111, go113, go116, // python37, python38, python39 string runtime = 6; + // region to deploy in. defaults to europe-west1 + string region = 7; // environment variables to pass in at runtime - map env_vars = 7; + map env_vars = 8; } message DeployResponse { + Func function = 1; } // List all the deployed functions message ListRequest { - // optional project name - string project = 1; } message Func { - // project of function, optional - // defaults to literal "default" - // used to namespace functions - string project = 1; + // id of the function + string id = 1; // function name // limitation: must be unique across projects string name = 2; - // name of handler in source code - string entrypoint = 3; // git repo address - string repo = 4; + string repo = 3; + // branch to deploy. defaults to master + string branch = 4; + // name of handler in source code + string entrypoint = 5; // subfolder path to entrypoint - string subfolder = 5; - // runtime/language of the function - // eg: php74, - // nodejs6, nodejs8, nodejs10, nodejs12, nodejs14, nodejs16 - // dotnet3 - // java11 - // ruby26, ruby27 - // go111, go113, go116 + string subfolder = 6; + // runtime/language of the function e.g php74, + // nodejs6, nodejs8, nodejs10, nodejs12, nodejs14, nodejs16, + // dotnet3, java11, ruby26, ruby27, go111, go113, go116, // python37, python38, python39 - string runtime = 6; - // eg. ACTIVE, DEPLOY_IN_PROGRESS, OFFLINE etc - string status = 7; + string runtime = 7; + // region to deploy in. defaults to europe-west1 + string region = 8; // associated env vars - map env_vars = 8; + map env_vars = 9; + // eg. ACTIVE, DEPLOY_IN_PROGRESS, OFFLINE etc + string status = 10; + // unique url of the function + string url = 11; + // time of creation + string created = 12; + // time it was updated + string updated = 13; } message ListResponse { @@ -101,8 +104,6 @@ message ListResponse { message DeleteRequest { // The name of the function string name = 1; - // Optional project name - string project = 2; } message DeleteResponse { @@ -113,45 +114,63 @@ message DeleteResponse { message DescribeRequest { // The name of the function string name = 1; - // Optional project name - string project = 2; } message DescribeResponse { // The function requested Func function = 1; - // The time at which the function was updated - string updated_at = 2; - // The timeout for requests to the function - string timeout = 3; } -// Update a function +// Update a function. Downloads the source, builds and redeploys message UpdateRequest { // function name string name = 1; - // github url to repo - string repo = 2; - // optional subfolder path - string subfolder = 3; - // entry point, ie. handler name in the source code - // if not provided, defaults to the name parameter - string entrypoint = 4; - // project is used for namespacing your functions - // optional. defaults to "default". - string project = 5; - // runtime/language of the function - // eg: php74, - // nodejs6, nodejs8, nodejs10, nodejs12, nodejs14, nodejs16 - // dotnet3 - // java11 - // ruby26, ruby27 - // go111, go113, go116 - // python37, python38, python39 - string runtime = 6; - // environment variables to pass in at runtime - map env_vars = 7; } message UpdateResponse { } + +// Return the backend url for proxying +message ProxyRequest { + // id of the function + string id = 1; +} + +message ProxyResponse { + // backend url + string url = 1; +} + +// Return a list of supported regions +message RegionsRequest { +} + +message RegionsResponse { + repeated string regions = 1; +} + +// Reservation represents a reserved function +message Reservation { + // name of the app + string name = 1; + // owner id + string owner = 2; + // associated token + string token = 3; + // time of reservation + string created = 4; + // time reservation expires + string expires = 5; +} + +// Reserve function names and resources beyond free quota +message ReserveRequest { + // name of your app e.g helloworld + string name = 1; +} + +message ReserveResponse { + // The app reservation + Reservation reservation = 1; +} + diff --git a/function/publicapi.json b/function/publicapi.json index 0825f5e..d085204 100644 --- a/function/publicapi.json +++ b/function/publicapi.json @@ -1,7 +1,10 @@ { - "name": "function", - "icon": "🔥", - "category": "hosting", - "display_name": "Functions" + "name": "function", + "icon": "🔥", + "category": "hosting", + "display_name": "Functions", + "pricing": { + "Function.Reserve": 10000000 } +}