Chromium Code Reviews| 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. |
| +} |