Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(13)

Unified Diff: go/src/infra/gae/epclient/epclient.go

Issue 1153473008: A client/server helper wrapper for endpoints in Go. (Closed) Base URL: https://chromium.googlesource.com/infra/infra.git@master
Patch Set: Use the real client generator! Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: go/src/infra/gae/epclient/epclient.go
diff --git a/go/src/infra/gae/epclient/epclient.go b/go/src/infra/gae/epclient/epclient.go
new file mode 100644
index 0000000000000000000000000000000000000000..6c55da03931fe97d593d80edc92c67861424b79e
--- /dev/null
+++ b/go/src/infra/gae/epclient/epclient.go
@@ -0,0 +1,206 @@
+// Copyright 2015 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+// The following comment allows `go generate` to correctly generate clients
+// for all endpoints services located in the "infra/gae/epservice" package.
+//go:generate goapp run epclient.go
+
+// EPClient will auto-generate endpoints clients from Google Cloud Endpoint
+// service definitions. The tool assumes that:
+// * all go endpoint service definitions occur in a package which has
+// a public method `RegisterEndpointsService(*endpoints.Server) error`, and
+// that calling this method will register the endpoints service with the
+// provided server or return an error.
+// * goapp exists in PATH (and understands all the import paths in the
+// defined services)
+// * google-api-go-generator exists in PATH.
dnj 2015/06/08 16:43:18 Could you "enforce" this by adding it as a Go depe
iannucci 2015/06/09 00:01:54 It's a 'main' package, so I can't add it as an imp
+package main
+
+import (
+ "bytes"
+ "encoding/json"
+ "flag"
+ "fmt"
+ "io/ioutil"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "strings"
+ "text/template"
+ "time"
+
+ "infra/libs/jsutil"
+)
+
+var (
+ servicePackagesBase = flag.String("pkgs", "infra/gae/epservice",
+ "The base Go package to walk to find service definitions.")
+ outDir = flag.String("outdir", ".",
+ "The directory to generate the client libraries in.")
+ runOnly = flag.Bool("run", false,
+ "If set to true, will run the server and wait, without generating any clients.")
+ clearDatastore = flag.Bool("clear_datastore", false,
+ "If set to true, will clear the datastore when in 'run' mode.")
+)
+
+var serviceScript = `
+package main
dnj 2015/06/08 16:43:18 Add "generated" comment, info, timestamp.
iannucci 2015/06/09 00:01:54 Done.
+
+import (
+ "fmt"
+
+{{range $idx, $pkg := .Pkgs}}
+ pkg{{$idx}} "{{$pkg.Imp}}"{{end}}
+
+ "github.com/GoogleCloudPlatform/go-endpoints/endpoints"
+)
+
+func init() {
+ var err error
+ server := endpoints.NewServer("")
+
+ {{range $idx, $pkg := .Pkgs }}
+ err = pkg{{$idx}}.RegisterEndpointsService(server)
+ if err != nil {
+ panic(fmt.Errorf("Error while registering service {{$pkg}}: %s", err))
+ }
+ {{end}}
+
+ server.HandleHTTP(nil)
+}
+`
+
+const appYaml = `
+application: epclient-tmp-app
dnj 2015/06/08 16:43:18 Add "generated" comment, info, timestamp.
iannucci 2015/06/09 00:01:54 Done.
+version: nope
+runtime: go
+api_version: go1
+
+handlers:
+- url: /.*
+ script: _go_app
+`
+
+var temp = template.Must(template.New("service").Parse(serviceScript))
+
+// Pkg holds the import and real filesystem paths of an endpoint service
+// package. It's exported merely for reflection purposes, since it's used
+// by text/template.
+type Pkg struct {
+ Imp string
+ Pth string
+}
+
+func boom(err error) {
+ if err != nil {
+ panic(err)
+ }
+}
+
+func getPkgs() []Pkg {
+ cmd := exec.Command("goapp", "list", "-json", filepath.Join(*servicePackagesBase, "..."))
+ d, err := cmd.Output()
+ boom(err)
+
+ d = []byte("[" + strings.Replace(string(d), "}\n{", "},{", -1) + "]")
+
+ js := interface{}(nil)
+ boom(json.Unmarshal(d, &js))
+
+ ret := []Pkg{}
+
+ for _, m := range js.([]interface{}) {
+ ret = append(ret, Pkg{
+ jsutil.Get(m, "ImportPath").(string),
+ jsutil.Get(m, "Dir").(string),
+ })
+ }
+
+ return ret
+}
+
+func writeFiles(dir string, pkgs []Pkg) {
+ buf := &bytes.Buffer{}
+ if err := temp.Execute(buf, struct{ Pkgs []Pkg }{pkgs}); err != nil {
+ panic(err)
+ }
+
+ if err := ioutil.WriteFile(filepath.Join(dir, "app.go"), buf.Bytes(), 0666); err != nil {
+ panic(err)
+ }
+
+ if err := ioutil.WriteFile(filepath.Join(dir, "app.yaml"), []byte(appYaml), 0666); err != nil {
+ panic(err)
+ }
+}
+
+func parseFlags() {
+ err := error(nil)
dnj 2015/06/08 16:43:17 I generally prefer "var err error" *shrug* Up to y
iannucci 2015/06/09 00:01:53 me too... I was getting bikeshedding elsewhere on
+ flag.Parse()
+ if *outDir == "" {
+ if *outDir, err = os.Getwd(); err != nil {
+ panic(err)
+ }
+ }
+ if *outDir, err = filepath.Abs(*outDir); err != nil {
+ panic(err)
+ }
+}
+
+func startServer(dir string) (func(), func()) {
+ args := []string{"serve"}
+ if *clearDatastore {
+ args = append(args, "-clear_datastore")
+ }
+ args = append(args, dir)
+ server := exec.Command("goapp", args...)
+ server.Stdout = os.Stdout
+ server.Stderr = os.Stderr
+ if err := server.Start(); err != nil {
+ panic(err)
+ }
+ time.Sleep(time.Second) // yeah yeah, sue me.
dnj 2015/06/08 16:43:18 Since you know the discovery service URL, you coul
iannucci 2015/06/09 00:01:54 Done.
+ fmt.Println("Discovery service up")
+
+ wait := func() { server.Wait() }
+ stop := func() {
+ server.Process.Signal(os.Interrupt)
+ wait()
+ }
+ return stop, wait
+}
+
+func generate() {
+ gencmd := exec.Command("google-api-go-generator", "-discoveryurl", "http://localhost:8080/_ah/api/discovery/v1/apis", "-gendir", *outDir, "-cache=false")
+ gencmd.Stdout = os.Stdout
+ gencmd.Stderr = os.Stderr
+ if err := gencmd.Run(); err != nil {
+ panic(err)
+ }
+}
+
+func main() {
+ parseFlags()
+
dnj 2015/06/08 16:43:18 Test that "google-api-go-generator" and "goapp" ar
iannucci 2015/06/09 00:01:53 Done.
+ pkgs := getPkgs()
+
+ dir, err := ioutil.TempDir("", "epclient_gen")
+ if err != nil {
+ panic(err)
+ }
+ defer os.RemoveAll(dir)
+
+ writeFiles(dir, pkgs)
+
+ stop, wait := startServer(dir)
+ defer stop()
+
+ if *runOnly {
+ wait()
+ } else {
+ generate()
+ }
+
+ // inject DoWithRetries methods.
+}

Powered by Google App Engine
This is Rietveld 408576698