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

Side by Side Diff: deploytool/cmd/luci_deploy/appengine.go

Issue 2580213002: luci_depkoy: Add instance class, params. (Closed)
Patch Set: Created 4 years 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/api/deploy/util.go ('k') | deploytool/cmd/luci_deploy/deploy_appengine.go » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The LUCI Authors. All rights reserved. 1 // Copyright 2016 The LUCI Authors. All rights reserved.
2 // Use of this source code is governed under the Apache License, Version 2.0 2 // Use of this source code is governed under the Apache License, Version 2.0
3 // that can be found in the LICENSE file. 3 // that can be found in the LICENSE file.
4 4
5 package main 5 package main
6 6
7 import ( 7 import (
8 "io/ioutil" 8 "io/ioutil"
9 "path/filepath" 9 "path/filepath"
10 "strings" 10 "strings"
11 11
12 "github.com/luci/luci-go/common/errors" 12 "github.com/luci/luci-go/common/errors"
13 "github.com/luci/luci-go/deploytool/api/deploy" 13 "github.com/luci/luci-go/deploytool/api/deploy"
14 "gopkg.in/yaml.v2" 14 "gopkg.in/yaml.v2"
15 ) 15 )
16 16
17 // gaeAppYAML is a YAML struct for an AppEngine "app.yaml". 17 // gaeAppYAML is a YAML struct for an AppEngine "app.yaml".
18 type gaeAppYAML struct { 18 type gaeAppYAML struct {
19 » Module string `yaml:"module,omitempty"` 19 » Module string `yaml:"module,omitempty"`
20 » Runtime string `yaml:"runtime,omitempty"` 20 » Runtime string `yaml:"runtime,omitempty"`
21 » ThreadSafe *bool `yaml:"threadsafe,omitempty"` 21 » ThreadSafe *bool `yaml:"threadsafe,omitempty"`
22 » APIVersion interface{} `yaml:"api_version,omitempty"` 22 » APIVersion interface{} `yaml:"api_version,omitempty"`
23 » VM bool `yaml:"vm,omitempty"` 23 » VM bool `yaml:"vm,omitempty"`
24 » InstanceClass string `yaml:"instance_class,omitempty"`
25
26 » AutomaticScaling *gaeAppAutomaticScalingParams `yaml:"automatic_scaling, omitempty"`
27 » BasicScaling *gaeAppBasicScalingParams `yaml:"basic_scaling,omit empty"`
28 » ManualScaling *gaeAppManualScalingParams `yaml:"manual_scaling,omi tempty"`
24 29
25 BetaSettings *gaeAppYAMLBetaSettings `yaml:"beta_settings,omitempty"` 30 BetaSettings *gaeAppYAMLBetaSettings `yaml:"beta_settings,omitempty"`
26 31
27 Handlers []*gaeAppYAMLHandler `yaml:"handlers,omitempty"` 32 Handlers []*gaeAppYAMLHandler `yaml:"handlers,omitempty"`
28 } 33 }
29 34
35 // gaeAppAutomaticScalingParams is the combined set of scaling parameters for
36 // automatic scaling.
37 type gaeAppAutomaticScalingParams struct {
38 MinIdleInstances *int `yaml:"min_idle_instances,omitempty"`
39 MaxIdleInstances *int `yaml:"max_idle_instances,omitempty"`
40 MinPendingLatency string `yaml:"min_pending_latency,omitempty"`
41 MaxPendingLatency string `yaml:"max_pending_latency,omitempty"`
42 MaxConcurrentRequests *int `yaml:"max_concurrent_requests,omitempty"`
43 }
44
45 // gaeAppBasicScalingParams is the combined set of scaling parameters for
46 // basic scaling.
47 type gaeAppBasicScalingParams struct {
48 IdleTimeout string `yaml:"idle_timeout,omitempty"`
49 MaxInstances int `yaml:"max_instances"`
50 }
51
52 // gaeAppManualScalingParams is the combined set of scaling parameters for
53 // manual scaling.
54 type gaeAppManualScalingParams struct {
55 Instances int `yaml:"instances"`
56 }
57
58 // gaeAppYAMLBuiltIns is the set of builtin options that can be enabled. Any
59 // enabled option will have the value, "on".
60 type gaeAppYAMLBuiltIns struct {
61 AppStats string `yaml:"appstats,omitempty"`
62 RemoteAPI string `yaml:"remote_api,omitempty"`
63 Deferred string `yaml:"deferred,omitempty"`
64 }
65
66 type gaeLibrary struct {
67 Name string `yaml:"name"`
68 Version string `yaml:"version,omitempty"`
69 }
70
30 // gaeAppYAMLBetaSettings is a YAML struct for an AppEngine "app.yaml" 71 // gaeAppYAMLBetaSettings is a YAML struct for an AppEngine "app.yaml"
31 // beta settings. 72 // beta settings.
32 type gaeAppYAMLBetaSettings struct { 73 type gaeAppYAMLBetaSettings struct {
33 ServiceAccountScopes string `yaml:"service_account_scopes,omitempty"` 74 ServiceAccountScopes string `yaml:"service_account_scopes,omitempty"`
34 } 75 }
35 76
36 // gaeAppYAMLHAndler is a YAML struct for an AppEngine "app.yaml" 77 // gaeAppYAMLHAndler is a YAML struct for an AppEngine "app.yaml"
37 // handler entry. 78 // handler entry.
38 type gaeAppYAMLHandler struct { 79 type gaeAppYAMLHandler struct {
39 URL string `yaml:"url"` 80 URL string `yaml:"url"`
40 Script string `yaml:"script,omitempty"` 81 Script string `yaml:"script,omitempty"`
41 82
42 Secure string `yaml:"secure,omitempty"` 83 Secure string `yaml:"secure,omitempty"`
43 Login string `yaml:"login,omitempty"` 84 Login string `yaml:"login,omitempty"`
44 85
45 StaticDir string `yaml:"static_dir,omitempty"` 86 StaticDir string `yaml:"static_dir,omitempty"`
46 StaticFiles string `yaml:"static_files,omitempty"` 87 StaticFiles string `yaml:"static_files,omitempty"`
47 Upload string `yaml:"upload,omitempty"` 88 Upload string `yaml:"upload,omitempty"`
89 Expiration string `yaml:"expiration,omitempty"`
90
91 HTTPHeaders map[string]string `yaml:"http_headers,omitempty"`
48 } 92 }
49 93
50 // gaeCronYAML is a YAML struct for an AppEngine "cron.yaml". 94 // gaeCronYAML is a YAML struct for an AppEngine "cron.yaml".
51 type gaeCronYAML struct { 95 type gaeCronYAML struct {
52 Cron []*gaeCronYAMLEntry `yaml:"cron,omitempty"` 96 Cron []*gaeCronYAMLEntry `yaml:"cron,omitempty"`
53 } 97 }
54 98
55 // gaeCronYAMLEntry is a YAML struct for an AppEngine "cron.yaml" entry. 99 // gaeCronYAMLEntry is a YAML struct for an AppEngine "cron.yaml" entry.
56 type gaeCronYAMLEntry struct { 100 type gaeCronYAMLEntry struct {
57 Description string `yaml:"description,omitempty"` 101 Description string `yaml:"description,omitempty"`
58 URL string `yaml:"url"` 102 URL string `yaml:"url"`
59 Schedule string `yaml:"schedule"` 103 Schedule string `yaml:"schedule"`
60 Target string `yaml:"target,omitempty"` 104 Target string `yaml:"target,omitempty"`
61 } 105 }
62 106
63 // gaeIndexYAML is a YAML struct for an AppEngine "index.yaml". 107 // gaeIndexYAML is a YAML struct for an AppEngine "index.yaml".
64 type gaeIndexYAML struct { 108 type gaeIndexYAML struct {
65 Indexes []*gaeIndexYAMLEntry `yaml:"indexes,omitempty"` 109 Indexes []*gaeIndexYAMLEntry `yaml:"indexes,omitempty"`
66 } 110 }
67 111
68 // gaeIndexYAMLEntry is a YAML struct for an AppEngine "index.yaml" entry. 112 // gaeIndexYAMLEntry is a YAML struct for an AppEngine "index.yaml" entry.
69 type gaeIndexYAMLEntry struct { 113 type gaeIndexYAMLEntry struct {
70 Kind string `yaml:"kind"` 114 Kind string `yaml:"kind"`
71 » Ancestor string `yaml:"ancestor,omitempty"` 115 » Ancestor interface{} `yaml:"ancestor,omitempty"`
72 Properties []*gaeIndexYAMLEntryProperty `yaml:"properties"` 116 Properties []*gaeIndexYAMLEntryProperty `yaml:"properties"`
73 } 117 }
74 118
75 // gaeIndexYAMLEntryProperty is a YAML struct for an AppEngine "index.yaml" 119 // gaeIndexYAMLEntryProperty is a YAML struct for an AppEngine "index.yaml"
76 // entry's property. 120 // entry's property.
77 type gaeIndexYAMLEntryProperty struct { 121 type gaeIndexYAMLEntryProperty struct {
78 Name string `yaml:"name"` 122 Name string `yaml:"name"`
79 Direction string `yaml:"direction,omitempty"` 123 Direction string `yaml:"direction,omitempty"`
80 } 124 }
81 125
(...skipping 11 matching lines...) Expand all
93 // gaeQueueYAML is a YAML struct for an AppEngine "queue.yaml". 137 // gaeQueueYAML is a YAML struct for an AppEngine "queue.yaml".
94 type gaeQueueYAML struct { 138 type gaeQueueYAML struct {
95 Queue []*gaeQueueYAMLEntry `yaml:"queue,omitempty"` 139 Queue []*gaeQueueYAMLEntry `yaml:"queue,omitempty"`
96 } 140 }
97 141
98 // gaeQueueYAMLEntry is a YAML struct for an AppEngine "queue.yaml" entry. 142 // gaeQueueYAMLEntry is a YAML struct for an AppEngine "queue.yaml" entry.
99 type gaeQueueYAMLEntry struct { 143 type gaeQueueYAMLEntry struct {
100 Name string `yaml:"name"` 144 Name string `yaml:"name"`
101 Mode string `yaml:"mode"` 145 Mode string `yaml:"mode"`
102 146
103 » Rate string `yaml:"rate,omit empty"` 147 » Rate string `yaml:"rate,omitempty"`
104 » BucketSize int `yaml:"bucket_si ze,omitempty"` 148 » BucketSize int `yaml:"bucket_size,omitempty"`
105 » MaxConcurrentRequests int `yaml:"max_concu rrent_requests,omitempty"` 149 » MaxConcurrentRequests int `yaml:"max_concurrent_requests,omitempty"`
106 » RetryParameters *gaeQueueYAMLEntryRetryParameters `yaml:"retry_par ameters,omitempty"` 150 » Target string `yaml:"target,omitempty"`
107 » Target string `yaml:"target,om itempty"` 151
152 » RetryParameters *gaeQueueYAMLEntryRetryParameters `yaml:"retry_parameter s,omitempty"`
108 } 153 }
109 154
110 // gaeQueueYAMLEntryRetryParameters is a YAML struct for an AppEngine 155 // gaeQueueYAMLEntryRetryParameters is a YAML struct for an AppEngine
111 // "queue.yaml" entry push queue retry parameters. 156 // "queue.yaml" entry push queue retry parameters.
112 type gaeQueueYAMLEntryRetryParameters struct { 157 type gaeQueueYAMLEntryRetryParameters struct {
113 TaskAgeLimit string `yaml:"task_age_limit,omitempty"` 158 TaskAgeLimit string `yaml:"task_age_limit,omitempty"`
114 MinBackoffSeconds int `yaml:"min_backoff_seconds,omitempty"` 159 MinBackoffSeconds int `yaml:"min_backoff_seconds,omitempty"`
115 MaxBackoffSeconds int `yaml:"max_backoff_seconds,omitempty"` 160 MaxBackoffSeconds int `yaml:"max_backoff_seconds,omitempty"`
116 MaxDoublings int `yaml:"max_doublings,omitempty"` 161 MaxDoublings int `yaml:"max_doublings,omitempty"`
117 } 162 }
118 163
119 func gaeBuildAppYAML(aem *deploy.AppEngineModule, staticMap map[*deploy.BuildPat h]string) (*gaeAppYAML, error) { 164 func gaeBuildAppYAML(aem *deploy.AppEngineModule, staticMap map[*deploy.BuildPat h]string) (*gaeAppYAML, error) {
120 appYAML := gaeAppYAML{ 165 appYAML := gaeAppYAML{
121 Module: aem.ModuleName, 166 Module: aem.ModuleName,
122 } 167 }
123 168
124 var ( 169 var (
125 defaultScript = "" 170 defaultScript = ""
126 isStub = false 171 isStub = false
127 ) 172 )
128 173
129 switch aem.GetRuntime().(type) { 174 switch aem.GetRuntime().(type) {
130 case *deploy.AppEngineModule_GoModule_: 175 case *deploy.AppEngineModule_GoModule_:
131 appYAML.Runtime = "go" 176 appYAML.Runtime = "go"
132 177
133 » » if aem.ManagedVm != nil { 178 » » if aem.GetManagedVm() != nil {
134 appYAML.APIVersion = 1 179 appYAML.APIVersion = 1
135 appYAML.VM = true 180 appYAML.VM = true
136 } else { 181 } else {
137 appYAML.APIVersion = "go1" 182 appYAML.APIVersion = "go1"
138 } 183 }
139 defaultScript = "_go_app" 184 defaultScript = "_go_app"
140 185
141 case *deploy.AppEngineModule_StaticModule_: 186 case *deploy.AppEngineModule_StaticModule_:
142 // A static module presents itself as an empty Python AppEngine module. This 187 // A static module presents itself as an empty Python AppEngine module. This
143 // doesn't actually need any Python code to support it. 188 // doesn't actually need any Python code to support it.
144 appYAML.Runtime = "python27" 189 appYAML.Runtime = "python27"
145 appYAML.APIVersion = "1" 190 appYAML.APIVersion = "1"
146 threadSafe := true 191 threadSafe := true
147 appYAML.ThreadSafe = &threadSafe 192 appYAML.ThreadSafe = &threadSafe
148 isStub = true 193 isStub = true
149 194
150 default: 195 default:
151 return nil, errors.Reason("unsupported runtime %(runtime)q").D(" runtime", aem.Runtime).Err() 196 return nil, errors.Reason("unsupported runtime %(runtime)q").D(" runtime", aem.Runtime).Err()
152 } 197 }
153 198
154 » if mvm := aem.ManagedVm; mvm != nil { 199 » // Classic / Managed VM Properties
155 » » if len(mvm.Scopes) > 0 { 200 » switch p := aem.GetParams().(type) {
201 » case *deploy.AppEngineModule_Classic:
202 » » var icPrefix string
203 » » switch sc := p.Classic.GetScaling().(type) {
204 » » case nil:
205 » » » // (Automatic, default).
206 » » » icPrefix = "F"
207
208 » » case *deploy.AppEngineModule_ClassicParams_AutomaticScaling_:
209 » » » as := sc.AutomaticScaling
210 » » » appYAML.AutomaticScaling = &gaeAppAutomaticScalingParams {
211 » » » » MinIdleInstances: intPtrOrNilIfZero(as.MinI dleInstances),
212 » » » » MaxIdleInstances: intPtrOrNilIfZero(as.MaxI dleInstances),
213 » » » » MinPendingLatency: as.MinPendingLatency.AppY AMLString(),
214 » » » » MaxPendingLatency: as.MaxPendingLatency.AppY AMLString(),
215 » » » » MaxConcurrentRequests: intPtrOrNilIfZero(as.MaxC oncurrentRequests),
216 » » » }
217 » » » icPrefix = "F"
218
219 » » case *deploy.AppEngineModule_ClassicParams_BasicScaling_:
220 » » » bs := sc.BasicScaling
221 » » » appYAML.BasicScaling = &gaeAppBasicScalingParams{
222 » » » » IdleTimeout: bs.IdleTimeout.AppYAMLString(),
223 » » » » MaxInstances: int(bs.MaxInstances),
224 » » » }
225 » » » icPrefix = "B"
226
227 » » case *deploy.AppEngineModule_ClassicParams_ManualScaling_:
228 » » » ms := sc.ManualScaling
229 » » » appYAML.ManualScaling = &gaeAppManualScalingParams{
230 » » » » Instances: int(ms.Instances),
231 » » » }
232 » » » icPrefix = "B"
233
234 » » default:
235 » » » return nil, errors.Reason("unknown scaling type %(type)T ").D("type", sc).Err()
236 » » }
237 » » appYAML.InstanceClass = p.Classic.InstanceClass.AppYAMLString(ic Prefix)
238
239 » case *deploy.AppEngineModule_ManagedVm:
240 » » if scopes := p.ManagedVm.Scopes; len(scopes) > 0 {
156 appYAML.BetaSettings = &gaeAppYAMLBetaSettings{ 241 appYAML.BetaSettings = &gaeAppYAMLBetaSettings{
157 » » » » ServiceAccountScopes: strings.Join(mvm.Scopes, " ,"), 242 » » » » ServiceAccountScopes: strings.Join(scopes, ","),
158 } 243 }
159 } 244 }
160 } 245 }
161 246
162 if handlerSet := aem.Handlers; handlerSet != nil { 247 if handlerSet := aem.Handlers; handlerSet != nil {
163 appYAML.Handlers = make([]*gaeAppYAMLHandler, len(handlerSet.Han dler)) 248 appYAML.Handlers = make([]*gaeAppYAMLHandler, len(handlerSet.Han dler))
164 for i, handler := range handlerSet.Handler { 249 for i, handler := range handlerSet.Handler {
165 entry := gaeAppYAMLHandler{ 250 entry := gaeAppYAMLHandler{
166 URL: handler.Url, 251 URL: handler.Url,
167 Secure: handler.Secure.AppYAMLString(), 252 Secure: handler.Secure.AppYAMLString(),
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after
221 return &cronYAML 306 return &cronYAML
222 } 307 }
223 308
224 func gaeBuildIndexYAML(cp *layoutDeploymentCloudProject) *gaeIndexYAML { 309 func gaeBuildIndexYAML(cp *layoutDeploymentCloudProject) *gaeIndexYAML {
225 var indexYAML gaeIndexYAML 310 var indexYAML gaeIndexYAML
226 311
227 for _, index := range cp.resources.Index { 312 for _, index := range cp.resources.Index {
228 entry := gaeIndexYAMLEntry{ 313 entry := gaeIndexYAMLEntry{
229 Kind: index.Kind, 314 Kind: index.Kind,
230 Properties: make([]*gaeIndexYAMLEntryProperty, len(index .Property)), 315 Properties: make([]*gaeIndexYAMLEntryProperty, len(index .Property)),
231 » » } 316 » » » Ancestor: index.Ancestor,
232 » » if index.Ancestor {
233 » » » entry.Ancestor = "yes"
234 } 317 }
235 for i, prop := range index.Property { 318 for i, prop := range index.Property {
236 entry.Properties[i] = &gaeIndexYAMLEntryProperty{ 319 entry.Properties[i] = &gaeIndexYAMLEntryProperty{
237 Name: prop.Name, 320 Name: prop.Name,
238 Direction: prop.Direction.AppYAMLString(), 321 Direction: prop.Direction.AppYAMLString(),
239 } 322 }
240 } 323 }
241 indexYAML.Indexes = append(indexYAML.Indexes, &entry) 324 indexYAML.Indexes = append(indexYAML.Indexes, &entry)
242 } 325 }
243 return &indexYAML 326 return &indexYAML
(...skipping 26 matching lines...) Expand all
270 Name: queue.Name, 353 Name: queue.Name,
271 } 354 }
272 switch t := queue.GetType().(type) { 355 switch t := queue.GetType().(type) {
273 case *deploy.AppEngineResources_TaskQueue_Push_: 356 case *deploy.AppEngineResources_TaskQueue_Push_:
274 push := t.Push 357 push := t.Push
275 358
276 entry.Target = m.ModuleName 359 entry.Target = m.ModuleName
277 entry.Mode = "push" 360 entry.Mode = "push"
278 entry.Rate = push.Rate 361 entry.Rate = push.Rate
279 entry.BucketSize = int(push.BucketSize) 362 entry.BucketSize = int(push.BucketSize)
363 entry.MaxConcurrentRequests = int(push.MaxConcur rentRequests)
280 entry.RetryParameters = &gaeQueueYAMLEntryRetryP arameters{ 364 entry.RetryParameters = &gaeQueueYAMLEntryRetryP arameters{
281 TaskAgeLimit: push.RetryTaskAgeLimi t, 365 TaskAgeLimit: push.RetryTaskAgeLimi t,
282 MinBackoffSeconds: int(push.RetryMinBack offSeconds), 366 MinBackoffSeconds: int(push.RetryMinBack offSeconds),
283 MaxBackoffSeconds: int(push.RetryMaxBack offSeconds), 367 MaxBackoffSeconds: int(push.RetryMaxBack offSeconds),
284 MaxDoublings: int(push.RetryMaxDoub lings), 368 MaxDoublings: int(push.RetryMaxDoub lings),
285 } 369 }
286 370
287 default: 371 default:
288 return nil, errors.Reason("unknown task queue ty pe %(type)T").D("type", t).Err() 372 return nil, errors.Reason("unknown task queue ty pe %(type)T").D("type", t).Err()
289 } 373 }
(...skipping 17 matching lines...) Expand all
307 return nil, errors.Annotate(err).Reason("could not load 'index.y aml' from [%(path)s]"). 391 return nil, errors.Annotate(err).Reason("could not load 'index.y aml' from [%(path)s]").
308 D("path", path).Err() 392 D("path", path).Err()
309 } 393 }
310 394
311 var res deploy.AppEngineResources 395 var res deploy.AppEngineResources
312 if len(indexYAML.Indexes) > 0 { 396 if len(indexYAML.Indexes) > 0 {
313 res.Index = make([]*deploy.AppEngineResources_Index, len(indexYA ML.Indexes)) 397 res.Index = make([]*deploy.AppEngineResources_Index, len(indexYA ML.Indexes))
314 for i, idx := range indexYAML.Indexes { 398 for i, idx := range indexYAML.Indexes {
315 entry := deploy.AppEngineResources_Index{ 399 entry := deploy.AppEngineResources_Index{
316 Kind: idx.Kind, 400 Kind: idx.Kind,
317 » » » » Ancestor: (idx.Ancestor == "yes"), 401 » » » » Ancestor: yesOrBoolToBool(idx.Ancestor),
318 Property: make([]*deploy.AppEngineResources_Inde x_Property, len(idx.Properties)), 402 Property: make([]*deploy.AppEngineResources_Inde x_Property, len(idx.Properties)),
319 } 403 }
320 for pidx, idxProp := range idx.Properties { 404 for pidx, idxProp := range idx.Properties {
321 dir, err := deploy.IndexDirectionFromAppYAMLStri ng(idxProp.Direction) 405 dir, err := deploy.IndexDirectionFromAppYAMLStri ng(idxProp.Direction)
322 if err != nil { 406 if err != nil {
323 return nil, errors.Annotate(err).Reason( "could not identify direction for entry"). 407 return nil, errors.Annotate(err).Reason( "could not identify direction for entry").
324 D("index", i).D("propertyIndex", pidx).Err() 408 D("index", i).D("propertyIndex", pidx).Err()
325 } 409 }
326 410
327 entry.Property[pidx] = &deploy.AppEngineResource s_Index_Property{ 411 entry.Property[pidx] = &deploy.AppEngineResource s_Index_Property{
328 Name: idxProp.Name, 412 Name: idxProp.Name,
329 Direction: dir, 413 Direction: dir,
330 } 414 }
331 } 415 }
332 416
333 res.Index[i] = &entry 417 res.Index[i] = &entry
334 } 418 }
335 } 419 }
336 return &res, nil 420 return &res, nil
337 } 421 }
422
423 func intPtrOrNilIfZero(v uint32) *int {
424 if v == 0 {
425 return nil
426 }
427
428 value := int(v)
429 return &value
430 }
431
432 func yesOrBoolToBool(v interface{}) bool {
433 switch t := v.(type) {
434 case string:
435 return (t == "yes")
436 case bool:
437 return t
438 default:
439 return false
440 }
441 }
OLDNEW
« no previous file with comments | « deploytool/api/deploy/util.go ('k') | deploytool/cmd/luci_deploy/deploy_appengine.go » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698