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

Side by Side Diff: deploytool/cmd/deploy_appengine.go

Issue 2182213002: deploytool: Add README.md, migrate docs to it. (Closed) Base URL: https://github.com/luci/luci-go@master
Patch Set: Rename to "luci_deploy" Created 4 years, 4 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 unified diff | Download patch
« no previous file with comments | « deploytool/cmd/deploy.go ('k') | deploytool/cmd/deploy_container_engine.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
1 // Copyright 2016 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file.
4
5 package main
6
7 import (
8 "fmt"
9 "path/filepath"
10 "sort"
11 "strings"
12
13 "github.com/luci/luci-go/common/errors"
14 log "github.com/luci/luci-go/common/logging"
15 "github.com/luci/luci-go/deploytool/api/deploy"
16 "github.com/luci/luci-go/deploytool/managedfs"
17 )
18
19 // gaeDeployment is a consolidated AppEngine deployment configuration. It
20 // includes staged configurations for specifically-deployed components, as well
21 // as global AppEngine state (e.g., index, queue, etc.).
22 type gaeDeployment struct {
23 // project is the cloud project that this deployment is targeting.
24 project *layoutDeploymentCloudProject
25
26 // modules is the set of staged AppEngine modules that are being deploye d. The
27 // map is keyed on the module names.
28 modules map[string]*stagedGAEModule
29 // moduleNames is the sorted set of modules. It is generated during stag ing.
30 moduleNames []string
31
32 // versionModuleMap is a map of AppEngine module versions to the module names.
33 //
34 // For deployments whose modules all originate from the same Source, thi s
35 // will have one entry. However, for multi-source deployments, this may have
36 // more. Each entry translates to a "set_default_version" call on commit .
37 versionModuleMap map[string][]string
38 // yamlDir is the directory containing the AppEngine-wide YAMLs.
39 yamlDir string
40 }
41
42 func makeGAEDeployment(project *layoutDeploymentCloudProject) *gaeDeployment {
43 return &gaeDeployment{
44 project: project,
45 modules: make(map[string]*stagedGAEModule),
46 }
47 }
48
49 func (d *gaeDeployment) addModule(module *layoutDeploymentGAEModule) {
50 d.modules[module.ModuleName] = &stagedGAEModule{
51 layoutDeploymentGAEModule: module,
52 gaeDep: d,
53 }
54 d.moduleNames = append(d.moduleNames, module.ModuleName)
55 }
56
57 func (d *gaeDeployment) clearModules() {
58 d.modules, d.moduleNames = nil, nil
59 }
60
61 func (d *gaeDeployment) stage(w *work, root *managedfs.Dir, params *deployParams ) error {
62 sort.Strings(d.moduleNames)
63
64 // Generate a directory for our deployment's modules.
65 moduleBaseDir, err := root.EnsureDirectory("modules")
66 if err != nil {
67 return errors.Annotate(err).Reason("failed to create modules dir ectory").Err()
68 }
69
70 // Stage each module in parallel. Also, generate AppEngine-wide YAMLs.
71 err = w.RunMulti(func(workC chan<- func() error) {
72 // Generate our AppEngine-wide YAML files.
73 workC <- func() error {
74 return d.generateYAMLs(w, root)
75 }
76
77 // Stage each AppEngine module.
78 for _, name := range d.moduleNames {
79 module := d.modules[name]
80 workC <- func() error {
81 moduleDir, err := moduleBaseDir.EnsureDirectory( string(module.comp.comp.title))
82 if err != nil {
83 return errors.Annotate(err).Reason("fail ed to create module directory for %(module)q").
84 D("module", module.comp.comp.tit le).Err()
85 }
86 if err := module.stage(w, moduleDir); err != nil {
87 return errors.Annotate(err).Reason("fail ed to stage module %(name)q").
88 D("name", module.comp.comp.title ).Err()
89 }
90 return nil
91 }
92 }
93 })
94 if err != nil {
95 return errors.Annotate(err).Reason("failed to stage modules").Er r()
96 }
97
98 // Build our verison/module map for commit.
99 for _, name := range d.moduleNames {
100 module := d.modules[name]
101 if module.comp.dep.sg.Tainted && !params.commitTainted {
102 log.Fields{
103 "component": module.comp.String(),
104 }.Warningf(w, "Not committing tainted component.")
105 continue
106 }
107
108 if d.versionModuleMap == nil {
109 d.versionModuleMap = make(map[string][]string)
110 }
111
112 version := module.version.String()
113 moduleName := module.ModuleName
114 if moduleName == "" {
115 moduleName = "default"
116 }
117 d.versionModuleMap[version] = append(d.versionModuleMap[version] , moduleName)
118 }
119 return nil
120 }
121
122 func (d *gaeDeployment) localBuild(w *work) error {
123 // During the build phase, we simply assert that builds work as a sanity
124 // check. This prevents us from having to engage remote deployment servi ces
125 // only to find that some fundamental build problem has occurred.
126 return w.RunMulti(func(workC chan<- func() error) {
127 for _, name := range d.moduleNames {
128 module := d.modules[name]
129 workC <- func() error {
130 // Run the module's build function.
131 if module.localBuildFn == nil {
132 return nil
133 }
134 return module.localBuildFn(w)
135 }
136 }
137 })
138 }
139
140 func (d *gaeDeployment) push(w *work) error {
141 // Run push operations on modules in parallel.
142 return w.RunMulti(func(workC chan<- func() error) {
143 for _, name := range d.moduleNames {
144 module := d.modules[name]
145 if module.pushFn != nil {
146 workC <- func() error {
147 return module.pushFn(w)
148 }
149 }
150 }
151 })
152 }
153
154 func (d *gaeDeployment) commit(w *work) error {
155 appcfg, err := w.tools.appcfg()
156 if err != nil {
157 return errors.Annotate(err).Err()
158 }
159
160 // Set default modules for each module version.
161 versions := make([]string, 0, len(d.versionModuleMap))
162 for v := range d.versionModuleMap {
163 versions = append(versions, v)
164 }
165 sort.Strings(versions)
166
167 err = w.RunMulti(func(workC chan<- func() error) {
168 for _, v := range versions {
169 modules := d.versionModuleMap[v]
170 workC <- func() error {
171 appcfgArgs := []string{
172 "--application", d.project.Name,
173 "--version", v,
174 "--module", strings.Join(modules, ","),
175 }
176 if err := appcfg.exec("set_default_version", app cfgArgs...).check(w); err != nil {
177 return errors.Annotate(err).Reason("fail ed to set default version").
178 D("version", v).Err()
179 }
180 return nil
181 }
182 }
183 })
184 if err != nil {
185 return errors.Annotate(err).Reason("failed to set default versio ns").Err()
186 }
187
188 for _, updateCmd := range []string{"update_dispatch", "update_indexes", "update_queues", "update_cron"} {
189 if err := appcfg.exec(updateCmd, "--application", d.project.Name , d.yamlDir).check(w); err != nil {
190 return errors.Annotate(err).Reason("failed to update %(c md)q").D("cmd", updateCmd).Err()
191 }
192 }
193 return nil
194 }
195
196 func (d *gaeDeployment) generateYAMLs(w *work, root *managedfs.Dir) error {
197 // Get ALL AppEngine modules for this cloud project.
198 var (
199 err error
200 yamls = make(map[string]interface{}, 3)
201 )
202
203 yamls["index.yaml"] = gaeBuildIndexYAML(d.project)
204 yamls["cron.yaml"] = gaeBuildCronYAML(d.project)
205 if yamls["dispatch.yaml"], err = gaeBuildDispatchYAML(d.project); err != nil {
206 return errors.Annotate(err).Reason("failed to generate dispatch. yaml").Err()
207 }
208 if yamls["queue.yaml"], err = gaeBuildQueueYAML(d.project); err != nil {
209 return errors.Annotate(err).Reason("failed to generate index.yam l").Err()
210 }
211
212 for k, v := range yamls {
213 f := root.File(k)
214 if err := f.GenerateYAML(w, v); err != nil {
215 return errors.Annotate(err).Reason("failed to generate % (yaml)q").D("yaml", k).Err()
216 }
217 }
218
219 d.yamlDir = root.String()
220 return nil
221 }
222
223 // stagedGAEModule is a single staged AppEngine module.
224 type stagedGAEModule struct {
225 *layoutDeploymentGAEModule
226
227 // gaeDep is the GAE deployment that this module belongs to.
228 gaeDep *gaeDeployment
229
230 // version is the calculated version for this module. It is populated du ring
231 // staging.
232 version *cloudProjectVersion
233
234 // For Go AppEngine modules, the generated GOPATH.
235 goPath []string
236
237 localBuildFn func(*work) error
238 pushFn func(*work) error
239 }
240
241 // stage creates a staging space for an AppEngine module.
242 //
243 // root is the component module's root directory.
244 //
245 // The specific layout of the staging directory is based on the language and
246 // type of module.
247 //
248 // Go / Classic:
249 // -------------
250 // <root>/component/* (Contains shallow symlinks to
251 // <checkout>/path/to/component/*)
252 // <root>/component/{index,queue,cron}.yaml (Generated)
253 // <root>/component/source (Link to Component's source)
254 //
255 // Go / Managed VM (Same as Go/Classic, plus):
256 // -------------------------------------------
257 // <root>/component/Dockerfile (Generated Docker file)
258 func (m *stagedGAEModule) stage(w *work, root *managedfs.Dir) error {
259 // Calculate our version.
260 var err error
261 m.version, err = makeCloudProjectVersion(m.comp.dep.cloudProject, m.comp .source())
262 if err != nil {
263 return errors.Annotate(err).Reason("failed to calculate cloud pr oject version").Err()
264 }
265
266 // The directory where the base YAML and other depoyment-relative data w ill be
267 // written.
268 base := root
269
270 // appYAMLPath is used in the immediate "switch" statement as a
271 // parameter to some inline functions. It will be populated later in thi s
272 // staging function, well before those inline functions are evaluated.
273 //
274 // It is a pointer so that if, somehow, this does not end up being the c ase,
275 // we will panic instead of silently using an empty string.
276 var appYAMLPath *string
277
278 // Build each Component. We will delete any existing contents and leave it
279 // unmanaged to allow our build system to put whatever files it wants in
280 // there.
281 buildDir, err := root.EnsureDirectory("build")
282 if err != nil {
283 return errors.Annotate(err).Reason("failed to create build direc tory").Err()
284 }
285 if err := buildDir.CleanUp(); err != nil {
286 return errors.Annotate(err).Reason("failed to cleanup build dire ctory").Err()
287 }
288 buildDir.Ignore()
289
290 // Build our Component into this directory.
291 if err := buildComponent(w, m.comp, buildDir); err != nil {
292 return errors.Annotate(err).Reason("failed to build component"). Err()
293 }
294
295 switch t := m.GetRuntime().(type) {
296 case *deploy.AppEngineModule_GoModule_:
297 gom := t.GoModule
298
299 // Construct a GOPATH for this module.
300 goPath, err := root.EnsureDirectory("gopath")
301 if err != nil {
302 return errors.Annotate(err).Reason("failed to create GOP ATH base").Err()
303 }
304 if err := stageGoPath(w, m.comp, goPath); err != nil {
305 return errors.Annotate(err).Reason("failed to stage GOPA TH").Err()
306 }
307
308 // Generate a stub Go package, which we will populate with an en try point.
309 goSrcDir, err := root.EnsureDirectory("src")
310 if err != nil {
311 return errors.Annotate(err).Reason("failed to create stu b source directory").Err()
312 }
313
314 mainPkg := fmt.Sprintf("%s/main", m.comp.comp.title)
315 mainPkgParts := strings.Split(mainPkg, "/")
316 mainPkgDir, err := goSrcDir.EnsureDirectory(mainPkgParts[0], mai nPkgParts[1:]...)
317 if err != nil {
318 return errors.Annotate(err).Reason("failed to create dir ectory for main package %(pkg)q").
319 D("pkg", mainPkg).Err()
320 }
321 m.goPath = []string{root.String(), goPath.String()}
322
323 // Choose how to push based on whether or not this is a Managed VM.
324 if m.ManagedVm != nil {
325 // If this is a Managed VM, symlink files from the main package.
326 //
327 // NOTE: This has the effect of prohibiting the entry po int package for
328 // GAE Managed VMs from importing "internal" directories , since this stub
329 // space is outside of the main package space.
330 pkgPath := findGoPackage(t.GoModule.EntryPackage, m.goPa th)
331 if pkgPath == "" {
332 return errors.Reason("unable to find path for %( package)q").D("package", t.GoModule.EntryPackage).Err()
333 }
334
335 if err := mainPkgDir.ShallowSymlinkFrom(pkgPath, true); err != nil {
336 return errors.Annotate(err).Reason("failed to cr eate shallow symlink of main module").Err()
337 }
338
339 m.localBuildFn = func(w *work) error {
340 return m.localBuildGo(w, t.GoModule.EntryPackage )
341 }
342 m.pushFn = func(w *work) error {
343 return m.pushGoMVM(w, *appYAMLPath)
344 }
345 } else {
346 // Generate a classic GAE stub. Since GAE works through "init()", all this
347 // stub has to do is import the actual entry point packa ge.
348 if err := m.writeGoClassicGAEStub(w, mainPkgDir, gom.Ent ryPackage); err != nil {
349 return errors.Annotate(err).Reason("failed to ge nerate GAE classic entry stub").Err()
350 }
351
352 m.localBuildFn = func(w *work) error {
353 return m.localBuildGo(w, mainPkg)
354 }
355 m.pushFn = func(w *work) error {
356 return m.pushClassic(w, *appYAMLPath)
357 }
358 }
359
360 // Write artifacts into the Go stub package path.
361 base = mainPkgDir
362
363 case *deploy.AppEngineModule_StaticModule_:
364 m.pushFn = func(w *work) error {
365 return m.pushClassic(w, *appYAMLPath)
366 }
367 }
368
369 // Build our static files map.
370 //
371 // For each static files directory, symlink a generated directory immedi ately
372 // under our source.
373 staticMap := make(map[string]string)
374 staticBuildPathMap := make(map[*deploy.BuildPath]string)
375 if handlerSet := m.Handlers; handlerSet != nil {
376 for _, h := range handlerSet.Handler {
377 var bp *deploy.BuildPath
378 switch t := h.GetContent().(type) {
379 case *deploy.AppEngineModule_Handler_StaticBuildDir:
380 bp = t.StaticBuildDir
381 case *deploy.AppEngineModule_Handler_StaticFiles_:
382 bp = t.StaticFiles.GetBuild()
383 }
384 if bp == nil {
385 continue
386 }
387
388 // Have we already mapped this BuildPath?
389 if _, ok := staticBuildPathMap[bp]; ok {
390 continue
391 }
392
393 // Get the actual path for this BuildPath entry.
394 dirPath, err := m.comp.buildPath(bp)
395 if err != nil {
396 return errors.Annotate(err).Reason("cannot resol ve static directory").Err()
397 }
398
399 // Do we already have a static map entry for this filesy stem source path?
400 staticName, ok := staticMap[dirPath]
401 if !ok {
402 sd := base.File(fmt.Sprintf("_static_%d", len(st aticMap)))
403 if err := sd.SymlinkFrom(dirPath, true); err != nil {
404 return errors.Annotate(err).Reason("fail ed to symlink static content for [%(path)s]").
405 D("path", dirPath).Err()
406 }
407 staticName = sd.Name()
408 staticMap[dirPath] = staticName
409 }
410 staticBuildPathMap[bp] = staticName
411 }
412 }
413
414 // "app.yaml" / "module.yaml"
415 appYAML, err := gaeBuildAppYAML(m.AppEngineModule, staticBuildPathMap)
416 if err != nil {
417 return errors.Annotate(err).Reason("failed to generate module YA ML").Err()
418 }
419
420 appYAMLName := "module.yaml"
421 if m.ModuleName == "" {
422 // This is the default module, so name it "app.yaml".
423 appYAMLName = "app.yaml"
424 }
425 f := base.File(appYAMLName)
426 if err := f.GenerateYAML(w, appYAML); err != nil {
427 return errors.Annotate(err).Reason("failed to generate %(filenam e)q file").
428 D("filename", appYAMLName).Err()
429 }
430 s := f.String()
431 appYAMLPath = &s
432
433 // Cleanup our staging filesystem.
434 if err := root.CleanUp(); err != nil {
435 return errors.Annotate(err).Reason("failed to cleanup component directory").Err()
436 }
437 return nil
438 }
439
440 func (m *stagedGAEModule) writeGoClassicGAEStub(w *work, mainDir *managedfs.Dir, pkg string) error {
441 main := mainDir.File("stub.go")
442 return main.GenerateGo(w, fmt.Sprintf(``+
443 `
444 // Package entry is a stub entry package for classic AppEngine application.
445 //
446 // The Go GAE module requires some .go files to be present, even if it is a pure
447 // static module, and the gae.py tool does not support different runtimes in the
448 // same deployment.
449 package entry
450
451 // Import our real package. This will cause its "init()" method to be invoked,
452 // registering its handlers with the AppEngine runtime.
453 import _ %q
454 `, pkg))
455 }
456
457 // pushClassic pushes a classic AppEngine module using "appcfg.py".
458 func (m *stagedGAEModule) pushClassic(w *work, appYAMLPath string) error {
459 // Deploy classic.
460 appcfg, err := w.tools.appcfg()
461 if err != nil {
462 return errors.Annotate(err).Err()
463 }
464
465 appPath, appYAML := filepath.Split(appYAMLPath)
466 x := appcfg.exec(
467 "--application", m.gaeDep.project.Name,
468 "--version", m.version.String(),
469 "--verbose",
470 "update", appYAML,
471 ).cwd(appPath)
472 x = addGoEnv(m.goPath, x)
473 if err := x.check(w); err != nil {
474 return errors.Annotate(err).Reason("failed to deploy classic GAE module").Err()
475 }
476 return nil
477 }
478
479 // localBuildGo performs a local build against a Go binary.
480 func (m *stagedGAEModule) localBuildGo(w *work, mainPkg string) error {
481 gt, err := w.goTool(m.goPath)
482 if err != nil {
483 return errors.Annotate(err).Reason("failed to get Go tool").Err( )
484 }
485 if err := gt.build(w, "", mainPkg); err != nil {
486 return errors.Annotate(err).Reason("failed to build %(pkg)q").D( "pkg", mainPkg).Err()
487 }
488 return nil
489 }
490
491 // pushGoMVM pushes a Go Managed VM version to the AppEngine instance.
492 func (m *stagedGAEModule) pushGoMVM(w *work, appYAMLPath string) error {
493 appDir, appYAML := filepath.Split(appYAMLPath)
494
495 // Deploy Managed VM.
496 aedeploy, err := w.tools.aedeploy(m.goPath)
497 if err != nil {
498 return errors.Annotate(err).Err()
499 }
500
501 gcloud, err := w.tools.gcloud(m.gaeDep.project.Name)
502 if err != nil {
503 return errors.Annotate(err).Err()
504 }
505
506 // Deploy via: aedeploy gcloud preview app deploy
507 //
508 // We will promote it later on commit.
509 gcloudArgs := []string{
510 "preview", "app", "deploy", appYAML,
511 "--version", m.version.String(),
512 "--no-promote",
513 }
514
515 // Set verbosity based on current logging level.
516 logLevel := log.GetLevel(w)
517 switch logLevel {
518 case log.Info:
519 gcloudArgs = append(gcloudArgs, []string{"--verbosity", "info"}. ..)
520
521 case log.Debug:
522 gcloudArgs = append(gcloudArgs, []string{"--verbosity", "debug"} ...)
523 }
524
525 x := aedeploy.bootstrap(gcloud.exec(gcloudArgs[0], gcloudArgs[1:]...)).o utputAt(logLevel).cwd(appDir)
526 if err := x.check(w); err != nil {
527 return errors.Annotate(err).Reason("failed to deploy managed VM" ).Err()
528 }
529 return nil
530 }
531
532 // gaeAppYAML is a YAML struct for an AppEngine "app.yaml".
533 type gaeAppYAML struct {
534 Module string `yaml:"module,omitempty"`
535 Runtime string `yaml:"runtime,omitempty"`
536 ThreadSafe *bool `yaml:"threadsafe,omitempty"`
537 APIVersion interface{} `yaml:"api_version,omitempty"`
538 VM bool `yaml:"vm,omitempty"`
539
540 BetaSettings *gaeAppYAMLBetaSettings `yaml:"beta_settings,omitempty"`
541
542 Handlers []*gaeAppYAMLHandler `yaml:"handlers,omitempty"`
543 }
544
545 // gaeAppYAMLBetaSettings is a YAML struct for an AppEngine "app.yaml"
546 // beta settings.
547 type gaeAppYAMLBetaSettings struct {
548 ServiceAccountScopes string `yaml:"service_account_scopes,omitempty"`
549 }
550
551 // gaeAppYAMLHAndler is a YAML struct for an AppEngine "app.yaml"
552 // handler entry.
553 type gaeAppYAMLHandler struct {
554 URL string `yaml:"url"`
555 Script string `yaml:"script,omitempty"`
556
557 Secure string `yaml:"secure,omitempty"`
558 Login string `yaml:"login,omitempty"`
559
560 StaticDir string `yaml:"static_dir,omitempty"`
561 StaticFiles string `yaml:"static_files,omitempty"`
562 Upload string `yaml:"upload,omitempty"`
563 }
564
565 // gaeCronYAML is a YAML struct for an AppEngine "cron.yaml".
566 type gaeCronYAML struct {
567 Cron []*gaeCronYAMLEntry `yaml:"cron,omitempty"`
568 }
569
570 // gaeCronYAMLEntry is a YAML struct for an AppEngine "cron.yaml" entry.
571 type gaeCronYAMLEntry struct {
572 Description string `yaml:"description,omitempty"`
573 URL string `yaml:"url"`
574 Schedule string `yaml:"schedule"`
575 Target string `yaml:"target,omitempty"`
576 }
577
578 // gaeIndexYAML is a YAML struct for an AppEngine "index.yaml".
579 type gaeIndexYAML struct {
580 Indexes []*gaeIndexYAMLEntry `yaml:"indexes,omitempty"`
581 }
582
583 // gaeIndexYAMLEntry is a YAML struct for an AppEngine "index.yaml" entry.
584 type gaeIndexYAMLEntry struct {
585 Kind string `yaml:"kind"`
586 Ancestor string `yaml:"ancestor,omitempty"`
587 Properties []*gaeIndexYAMLEntryProperty `yaml:"properties"`
588 }
589
590 // gaeIndexYAMLEntryProperty is a YAML struct for an AppEngine "index.yaml"
591 // entry's property.
592 type gaeIndexYAMLEntryProperty struct {
593 Name string `yaml:"name"`
594 Direction string `yaml:"direction,omitempty"`
595 }
596
597 // gaeDispatchYAML is a YAML struct for an AppEngine "dispatch.yaml".
598 type gaeDispatchYAML struct {
599 Dispatch []*gaeDispatchYAMLEntry `yaml:"dispatch,omitempty"`
600 }
601
602 // gaeDispatchYAMLEntry is a YAML struct for an AppEngine "dispatch.yaml" entry.
603 type gaeDispatchYAMLEntry struct {
604 Module string `yaml:"module"`
605 URL string `yaml:"url"`
606 }
607
608 // gaeQueueYAML is a YAML struct for an AppEngine "queue.yaml".
609 type gaeQueueYAML struct {
610 Queue []*gaeQueueYAMLEntry `yaml:"queue,omitempty"`
611 }
612
613 // gaeQueueYAMLEntry is a YAML struct for an AppEngine "queue.yaml" entry.
614 type gaeQueueYAMLEntry struct {
615 Name string `yaml:"name"`
616 Mode string `yaml:"mode"`
617
618 Rate string `yaml:"rate,omit empty"`
619 BucketSize int `yaml:"bucket_si ze,omitempty"`
620 MaxConcurrentRequests int `yaml:"max_concu rrent_requests,omitempty"`
621 RetryParameters *gaeQueueYAMLEntryRetryParameters `yaml:"retry_par ameters,omitempty"`
622 Target string `yaml:"target,om itempty"`
623 }
624
625 // gaeQueueYAMLEntryRetryParameters is a YAML struct for an AppEngine
626 // "queue.yaml" entry push queue retry parameters.
627 type gaeQueueYAMLEntryRetryParameters struct {
628 TaskAgeLimit string `yaml:"task_age_limit,omitempty"`
629 MinBackoffSeconds int `yaml:"min_backoff_seconds,omitempty"`
630 MaxBackoffSeconds int `yaml:"max_backoff_seconds,omitempty"`
631 MaxDoublings int `yaml:"max_doublings,omitempty"`
632 }
633
634 func gaeBuildAppYAML(aem *deploy.AppEngineModule, staticMap map[*deploy.BuildPat h]string) (*gaeAppYAML, error) {
635 appYAML := gaeAppYAML{
636 Module: aem.ModuleName,
637 }
638
639 var (
640 defaultScript = ""
641 isStub = false
642 )
643
644 switch aem.GetRuntime().(type) {
645 case *deploy.AppEngineModule_GoModule_:
646 appYAML.Runtime = "go"
647
648 if aem.ManagedVm != nil {
649 appYAML.APIVersion = 1
650 appYAML.VM = true
651 } else {
652 appYAML.APIVersion = "go1"
653 }
654 defaultScript = "_go_app"
655
656 case *deploy.AppEngineModule_StaticModule_:
657 // A static module presents itself as an empty Python AppEngine module. This
658 // doesn't actually need any Python code to support it.
659 appYAML.Runtime = "python27"
660 appYAML.APIVersion = "1"
661 threadSafe := true
662 appYAML.ThreadSafe = &threadSafe
663 isStub = true
664
665 default:
666 return nil, errors.Reason("unsupported runtime %(runtime)q").D(" runtime", aem.Runtime).Err()
667 }
668
669 if mvm := aem.ManagedVm; mvm != nil {
670 if len(mvm.Scopes) > 0 {
671 appYAML.BetaSettings = &gaeAppYAMLBetaSettings{
672 ServiceAccountScopes: strings.Join(mvm.Scopes, " ,"),
673 }
674 }
675 }
676
677 if handlerSet := aem.Handlers; handlerSet != nil {
678 appYAML.Handlers = make([]*gaeAppYAMLHandler, len(handlerSet.Han dler))
679 for i, handler := range handlerSet.Handler {
680 entry := gaeAppYAMLHandler{
681 URL: handler.Url,
682 Secure: handler.Secure.AppYAMLString(),
683 Login: handler.Login.AppYAMLString(),
684 }
685
686 // Fill in static dir/file paths.
687 switch t := handler.GetContent().(type) {
688 case nil:
689 entry.Script = defaultScript
690
691 case *deploy.AppEngineModule_Handler_Script:
692 entry.Script = t.Script
693 if entry.Script == "" {
694 entry.Script = defaultScript
695 }
696
697 case *deploy.AppEngineModule_Handler_StaticBuildDir:
698 entry.StaticDir = staticMap[t.StaticBuildDir]
699
700 case *deploy.AppEngineModule_Handler_StaticFiles_:
701 sf := t.StaticFiles
702
703 relDir := staticMap[sf.GetBuild()]
704 entry.Upload = filepath.Join(relDir, sf.Upload)
705 entry.StaticFiles = filepath.Join(relDir, sf.Url Map)
706
707 default:
708 return nil, errors.Reason("don't know how to han dle content %(content)T").
709 D("content", t).D("url", handler.Url).Er r()
710 }
711
712 if entry.Script != "" && isStub {
713 return nil, errors.Reason("stub module cannot ha ve entry script").Err()
714 }
715
716 appYAML.Handlers[i] = &entry
717 }
718 }
719
720 return &appYAML, nil
721 }
722
723 func gaeBuildCronYAML(cp *layoutDeploymentCloudProject) *gaeCronYAML {
724 var cronYAML gaeCronYAML
725
726 for _, m := range cp.appEngineModules {
727 for _, cron := range m.resources.Cron {
728 cronYAML.Cron = append(cronYAML.Cron, &gaeCronYAMLEntry{
729 Description: cron.Description,
730 URL: cron.Url,
731 Schedule: cron.Schedule,
732 Target: m.ModuleName,
733 })
734 }
735 }
736 return &cronYAML
737 }
738
739 func gaeBuildIndexYAML(cp *layoutDeploymentCloudProject) *gaeIndexYAML {
740 var indexYAML gaeIndexYAML
741
742 for _, index := range cp.resources.Index {
743 entry := gaeIndexYAMLEntry{
744 Kind: index.Kind,
745 Properties: make([]*gaeIndexYAMLEntryProperty, len(index .Property)),
746 }
747 if index.Ancestor {
748 entry.Ancestor = "yes"
749 }
750 for i, prop := range index.Property {
751 entry.Properties[i] = &gaeIndexYAMLEntryProperty{
752 Name: prop.Name,
753 Direction: prop.Direction.AppYAMLString(),
754 }
755 }
756 indexYAML.Indexes = append(indexYAML.Indexes, &entry)
757 }
758 return &indexYAML
759 }
760
761 func gaeBuildDispatchYAML(cp *layoutDeploymentCloudProject) (*gaeDispatchYAML, e rror) {
762 var dispatchYAML gaeDispatchYAML
763
764 for _, m := range cp.appEngineModules {
765 for _, url := range m.resources.Dispatch {
766 dispatchYAML.Dispatch = append(dispatchYAML.Dispatch, &g aeDispatchYAMLEntry{
767 Module: m.ModuleName,
768 URL: url,
769 })
770 }
771 }
772 if count := len(dispatchYAML.Dispatch); count > 10 {
773 return nil, errors.Reason("dispatch file has more than 10 routin g rules (%(count)d)").
774 D("count", count).Err()
775 }
776 return &dispatchYAML, nil
777 }
778
779 func gaeBuildQueueYAML(cp *layoutDeploymentCloudProject) (*gaeQueueYAML, error) {
780 var queueYAML gaeQueueYAML
781
782 for _, m := range cp.appEngineModules {
783 for _, queue := range m.resources.TaskQueue {
784 entry := gaeQueueYAMLEntry{
785 Name: queue.Name,
786 }
787 switch t := queue.GetType().(type) {
788 case *deploy.AppEngineResources_TaskQueue_Push_:
789 push := t.Push
790
791 entry.Target = m.ModuleName
792 entry.Mode = "push"
793 entry.Rate = push.Rate
794 entry.BucketSize = int(push.BucketSize)
795 entry.RetryParameters = &gaeQueueYAMLEntryRetryP arameters{
796 TaskAgeLimit: push.RetryTaskAgeLimi t,
797 MinBackoffSeconds: int(push.RetryMinBack offSeconds),
798 MaxBackoffSeconds: int(push.RetryMaxBack offSeconds),
799 MaxDoublings: int(push.RetryMaxDoub lings),
800 }
801
802 default:
803 return nil, errors.Reason("unknown task queue ty pe %(type)T").D("type", t).Err()
804 }
805
806 queueYAML.Queue = append(queueYAML.Queue, &entry)
807 }
808 }
809 return &queueYAML, nil
810 }
OLDNEW
« no previous file with comments | « deploytool/cmd/deploy.go ('k') | deploytool/cmd/deploy_container_engine.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698