Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright 2015 The Chromium Authors. All rights reserved. | |
| 2 // Use of this source code is governed by a BSD-style license that can be | |
| 3 // found in the LICENSE file. | |
| 4 | |
| 5 // EPService will run a local appengine service with all of the endpoint | |
| 6 // services under this directory. The tool assumes that: | |
| 7 // * goapp exists in PATH (and understands all the import paths in the | |
| 8 // defined services) | |
| 9 package main | |
| 10 | |
| 11 import ( | |
| 12 "bytes" | |
| 13 "encoding/json" | |
| 14 "flag" | |
| 15 "fmt" | |
| 16 "io/ioutil" | |
| 17 "os" | |
| 18 "os/exec" | |
| 19 "os/signal" | |
| 20 "path/filepath" | |
| 21 "regexp" | |
| 22 "strings" | |
| 23 "sync" | |
| 24 "text/template" | |
| 25 "time" | |
| 26 | |
| 27 "infra/libs/jsutil" | |
| 28 ) | |
| 29 | |
| 30 var ( | |
| 31 clearDatastore = flag.Bool("clear_datastore", false, "if set, clear the datastore.") | |
| 32 leak = flag.Bool("leak", false, "if set, leak the tempora ry directory.") | |
| 33 verbose = flag.Bool("verbose", false, "if set, print skipped packages.") | |
| 34 servicePackagesBase = flag.String("base", "infra", | |
| 35 "base Go package to walk to find service definitions.") | |
| 36 ) | |
| 37 | |
| 38 var serviceScript = `{{define "go"}} | |
| 39 // DO NOT EDIT | |
| 40 // Auto-generated by infra/gae/epservice | |
| 41 // {{.Timestamp}} | |
| 42 | |
| 43 package main | |
| 44 | |
| 45 import ( | |
| 46 "fmt" | |
| 47 | |
| 48 {{range $idx, $pkg := .Pkgs}} | |
| 49 pkg{{$idx}} "{{$pkg.Imp}}"{{end}} | |
| 50 | |
| 51 "github.com/GoogleCloudPlatform/go-endpoints/endpoints" | |
| 52 ) | |
| 53 | |
| 54 func init() { | |
| 55 var err error | |
| 56 server := endpoints.NewServer("") | |
| 57 | |
| 58 {{range $idx, $pkg := .Pkgs }} | |
| 59 err = pkg{{$idx}}.RegisterEndpointsService(server) | |
| 60 if err != nil { | |
| 61 panic(fmt.Errorf("Error while registering service {{$pkg}}: %s", err)) | |
| 62 } | |
| 63 {{end}} | |
| 64 | |
| 65 server.HandleHTTP(nil) | |
| 66 } | |
| 67 {{end}} | |
| 68 ` | |
| 69 | |
| 70 const appYaml = `{{define "yaml"}} | |
| 71 # DO NOT EDIT | |
| 72 # Auto-generated by infra/gae/epservice | |
| 73 # {{.Timestamp}} | |
| 74 | |
| 75 application: epclient-tmp-app | |
| 76 version: nope | |
| 77 runtime: go | |
| 78 api_version: go1 | |
| 79 | |
| 80 handlers: | |
| 81 - url: /.* | |
| 82 script: _go_app | |
| 83 {{end}} | |
| 84 ` | |
| 85 | |
| 86 var templ = template.New("service") | |
| 87 | |
| 88 func init() { | |
| 89 template.Must(templ.Parse(serviceScript)) | |
| 90 template.Must(templ.Parse(appYaml)) | |
| 91 } | |
| 92 | |
| 93 type templInput struct { | |
| 94 Pkgs []Pkg | |
| 95 Timestamp time.Time | |
| 96 } | |
| 97 | |
| 98 // Pkg holds the import and real filesystem paths of an endpoint service | |
| 99 // package. It's exported merely for reflection purposes, since it's used | |
| 100 // by text/template. | |
| 101 type Pkg struct { | |
| 102 Imp string | |
| 103 Pth string | |
| 104 } | |
| 105 | |
| 106 func boom(err error) { | |
| 107 if err != nil { | |
| 108 panic(err) | |
| 109 } | |
| 110 } | |
| 111 | |
| 112 var reRegisterEndpointsService = regexp.MustCompile( | |
| 113 `\nfunc RegisterEndpointsService\(\w* \*\w*\.Server\) error {\n`) | |
|
dnj
2015/06/09 01:48:14
This seems like a dangerous approach. There are ma
iannucci
2015/06/09 02:22:07
That would be true if we didn't enforce gofmt, but
| |
| 114 | |
| 115 func getPkgs() []Pkg { | |
| 116 cmd := exec.Command("goapp", "list", "-json", filepath.Join(*servicePack agesBase+"...")) | |
| 117 d, err := cmd.Output() | |
| 118 boom(err) | |
| 119 | |
| 120 d = []byte("[" + strings.Replace(string(d), "}\n{", "},{", -1) + "]") | |
| 121 | |
| 122 js := interface{}(nil) | |
| 123 boom(json.Unmarshal(d, &js)) | |
| 124 | |
| 125 ret := []Pkg{} | |
| 126 | |
| 127 type pkgOk struct { | |
| 128 p Pkg | |
| 129 ok bool | |
| 130 } | |
| 131 pkgs := make(chan pkgOk) | |
| 132 wg := sync.WaitGroup{} | |
| 133 | |
| 134 for _, m := range js.([]interface{}) { | |
| 135 pkg := Pkg{ | |
| 136 jsutil.Get(m, "ImportPath").(string), | |
| 137 jsutil.Get(m, "Dir").(string), | |
| 138 } | |
| 139 wg.Add(1) | |
| 140 go func() { | |
| 141 defer wg.Done() | |
| 142 paths, err := ioutil.ReadDir(pkg.Pth) | |
| 143 boom(err) | |
| 144 | |
| 145 for _, f := range paths { | |
| 146 if !f.IsDir() && strings.HasSuffix(f.Name(), ".g o") { | |
| 147 data, err := ioutil.ReadFile(filepath.Jo in(pkg.Pth, f.Name())) | |
| 148 boom(err) | |
| 149 if reRegisterEndpointsService.Match(data ) { | |
| 150 pkgs <- pkgOk{pkg, true} | |
| 151 return | |
| 152 } | |
| 153 } | |
| 154 } | |
| 155 pkgs <- pkgOk{pkg, false} | |
| 156 }() | |
| 157 } | |
| 158 go func() { | |
| 159 wg.Wait() | |
| 160 close(pkgs) | |
| 161 }() | |
| 162 | |
| 163 for p := range pkgs { | |
| 164 if !p.ok { | |
| 165 if *verbose { | |
| 166 fmt.Println("skipping package", p.p.Imp, "(it do esn't impliment RegisterEndpointsService?)") | |
| 167 } | |
| 168 } else { | |
| 169 fmt.Println("including package", p.p.Imp) | |
| 170 ret = append(ret, p.p) | |
| 171 } | |
| 172 } | |
| 173 | |
| 174 return ret | |
| 175 } | |
| 176 | |
| 177 func writeFiles(dir string, pkgs []Pkg) { | |
| 178 input := &templInput{pkgs, time.Now()} | |
| 179 for _, ext := range []string{"go", "yaml"} { | |
| 180 buf := &bytes.Buffer{} | |
| 181 boom(templ.ExecuteTemplate(buf, ext, input)) | |
| 182 boom(ioutil.WriteFile(filepath.Join(dir, "app."+ext), buf.Bytes( ), 0666)) | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 func startServer(dir string) (func(), func()) { | |
| 187 args := []string{"serve"} | |
| 188 if *clearDatastore { | |
| 189 args = append(args, "-clear_datastore") | |
| 190 } | |
| 191 args = append(args, dir) | |
| 192 server := exec.Command("goapp", args...) | |
| 193 server.Stdout = os.Stdout | |
| 194 server.Stderr = os.Stderr | |
| 195 boom(server.Start()) | |
| 196 | |
| 197 wait := func() { server.Wait() } | |
| 198 stop := func() { | |
| 199 server.Process.Signal(os.Interrupt) | |
| 200 wait() | |
| 201 } | |
| 202 return stop, wait | |
| 203 } | |
| 204 | |
| 205 func main() { | |
| 206 _, err := exec.LookPath("goapp") | |
| 207 if err != nil { | |
| 208 panic("goapp must be on your path") | |
| 209 } | |
| 210 | |
| 211 flag.Parse() | |
| 212 pkgs := getPkgs() | |
| 213 | |
| 214 dir, err := ioutil.TempDir("", "epservice_gen") | |
| 215 boom(err) | |
| 216 prefix := "LEAKING" | |
| 217 if !*leak { | |
| 218 prefix = "generating" | |
| 219 defer os.RemoveAll(dir) | |
| 220 } | |
| 221 fmt.Println(prefix, "files in:", dir) | |
| 222 | |
| 223 writeFiles(dir, pkgs) | |
| 224 | |
| 225 stop, wait := startServer(dir) | |
| 226 intChan := make(chan os.Signal, 1) | |
| 227 signal.Notify(intChan, os.Interrupt, os.Kill) | |
| 228 go func() { | |
| 229 <-intChan | |
| 230 stop() | |
|
dnj
2015/06/09 01:48:13
Since you are not exiting on signal, "stop()" can
iannucci
2015/06/09 02:22:07
Done.
| |
| 231 }() | |
| 232 defer stop() | |
| 233 wait() | |
| 234 } | |
| OLD | NEW |