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 |