| OLD | NEW |
| (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 "io/ioutil" |
| 9 "path/filepath" |
| 10 "strings" |
| 11 |
| 12 "github.com/luci/luci-go/common/errors" |
| 13 "github.com/luci/luci-go/deploytool/api/deploy" |
| 14 "gopkg.in/yaml.v2" |
| 15 ) |
| 16 |
| 17 // gaeAppYAML is a YAML struct for an AppEngine "app.yaml". |
| 18 type gaeAppYAML struct { |
| 19 Module string `yaml:"module,omitempty"` |
| 20 Runtime string `yaml:"runtime,omitempty"` |
| 21 ThreadSafe *bool `yaml:"threadsafe,omitempty"` |
| 22 APIVersion interface{} `yaml:"api_version,omitempty"` |
| 23 VM bool `yaml:"vm,omitempty"` |
| 24 |
| 25 BetaSettings *gaeAppYAMLBetaSettings `yaml:"beta_settings,omitempty"` |
| 26 |
| 27 Handlers []*gaeAppYAMLHandler `yaml:"handlers,omitempty"` |
| 28 } |
| 29 |
| 30 // gaeAppYAMLBetaSettings is a YAML struct for an AppEngine "app.yaml" |
| 31 // beta settings. |
| 32 type gaeAppYAMLBetaSettings struct { |
| 33 ServiceAccountScopes string `yaml:"service_account_scopes,omitempty"` |
| 34 } |
| 35 |
| 36 // gaeAppYAMLHAndler is a YAML struct for an AppEngine "app.yaml" |
| 37 // handler entry. |
| 38 type gaeAppYAMLHandler struct { |
| 39 URL string `yaml:"url"` |
| 40 Script string `yaml:"script,omitempty"` |
| 41 |
| 42 Secure string `yaml:"secure,omitempty"` |
| 43 Login string `yaml:"login,omitempty"` |
| 44 |
| 45 StaticDir string `yaml:"static_dir,omitempty"` |
| 46 StaticFiles string `yaml:"static_files,omitempty"` |
| 47 Upload string `yaml:"upload,omitempty"` |
| 48 } |
| 49 |
| 50 // gaeCronYAML is a YAML struct for an AppEngine "cron.yaml". |
| 51 type gaeCronYAML struct { |
| 52 Cron []*gaeCronYAMLEntry `yaml:"cron,omitempty"` |
| 53 } |
| 54 |
| 55 // gaeCronYAMLEntry is a YAML struct for an AppEngine "cron.yaml" entry. |
| 56 type gaeCronYAMLEntry struct { |
| 57 Description string `yaml:"description,omitempty"` |
| 58 URL string `yaml:"url"` |
| 59 Schedule string `yaml:"schedule"` |
| 60 Target string `yaml:"target,omitempty"` |
| 61 } |
| 62 |
| 63 // gaeIndexYAML is a YAML struct for an AppEngine "index.yaml". |
| 64 type gaeIndexYAML struct { |
| 65 Indexes []*gaeIndexYAMLEntry `yaml:"indexes,omitempty"` |
| 66 } |
| 67 |
| 68 // gaeIndexYAMLEntry is a YAML struct for an AppEngine "index.yaml" entry. |
| 69 type gaeIndexYAMLEntry struct { |
| 70 Kind string `yaml:"kind"` |
| 71 Ancestor string `yaml:"ancestor,omitempty"` |
| 72 Properties []*gaeIndexYAMLEntryProperty `yaml:"properties"` |
| 73 } |
| 74 |
| 75 // gaeIndexYAMLEntryProperty is a YAML struct for an AppEngine "index.yaml" |
| 76 // entry's property. |
| 77 type gaeIndexYAMLEntryProperty struct { |
| 78 Name string `yaml:"name"` |
| 79 Direction string `yaml:"direction,omitempty"` |
| 80 } |
| 81 |
| 82 // gaeDispatchYAML is a YAML struct for an AppEngine "dispatch.yaml". |
| 83 type gaeDispatchYAML struct { |
| 84 Dispatch []*gaeDispatchYAMLEntry `yaml:"dispatch,omitempty"` |
| 85 } |
| 86 |
| 87 // gaeDispatchYAMLEntry is a YAML struct for an AppEngine "dispatch.yaml" entry. |
| 88 type gaeDispatchYAMLEntry struct { |
| 89 Module string `yaml:"module"` |
| 90 URL string `yaml:"url"` |
| 91 } |
| 92 |
| 93 // gaeQueueYAML is a YAML struct for an AppEngine "queue.yaml". |
| 94 type gaeQueueYAML struct { |
| 95 Queue []*gaeQueueYAMLEntry `yaml:"queue,omitempty"` |
| 96 } |
| 97 |
| 98 // gaeQueueYAMLEntry is a YAML struct for an AppEngine "queue.yaml" entry. |
| 99 type gaeQueueYAMLEntry struct { |
| 100 Name string `yaml:"name"` |
| 101 Mode string `yaml:"mode"` |
| 102 |
| 103 Rate string `yaml:"rate,omit
empty"` |
| 104 BucketSize int `yaml:"bucket_si
ze,omitempty"` |
| 105 MaxConcurrentRequests int `yaml:"max_concu
rrent_requests,omitempty"` |
| 106 RetryParameters *gaeQueueYAMLEntryRetryParameters `yaml:"retry_par
ameters,omitempty"` |
| 107 Target string `yaml:"target,om
itempty"` |
| 108 } |
| 109 |
| 110 // gaeQueueYAMLEntryRetryParameters is a YAML struct for an AppEngine |
| 111 // "queue.yaml" entry push queue retry parameters. |
| 112 type gaeQueueYAMLEntryRetryParameters struct { |
| 113 TaskAgeLimit string `yaml:"task_age_limit,omitempty"` |
| 114 MinBackoffSeconds int `yaml:"min_backoff_seconds,omitempty"` |
| 115 MaxBackoffSeconds int `yaml:"max_backoff_seconds,omitempty"` |
| 116 MaxDoublings int `yaml:"max_doublings,omitempty"` |
| 117 } |
| 118 |
| 119 func gaeBuildAppYAML(aem *deploy.AppEngineModule, staticMap map[*deploy.BuildPat
h]string) (*gaeAppYAML, error) { |
| 120 appYAML := gaeAppYAML{ |
| 121 Module: aem.ModuleName, |
| 122 } |
| 123 |
| 124 var ( |
| 125 defaultScript = "" |
| 126 isStub = false |
| 127 ) |
| 128 |
| 129 switch aem.GetRuntime().(type) { |
| 130 case *deploy.AppEngineModule_GoModule_: |
| 131 appYAML.Runtime = "go" |
| 132 |
| 133 if aem.ManagedVm != nil { |
| 134 appYAML.APIVersion = 1 |
| 135 appYAML.VM = true |
| 136 } else { |
| 137 appYAML.APIVersion = "go1" |
| 138 } |
| 139 defaultScript = "_go_app" |
| 140 |
| 141 case *deploy.AppEngineModule_StaticModule_: |
| 142 // A static module presents itself as an empty Python AppEngine
module. This |
| 143 // doesn't actually need any Python code to support it. |
| 144 appYAML.Runtime = "python27" |
| 145 appYAML.APIVersion = "1" |
| 146 threadSafe := true |
| 147 appYAML.ThreadSafe = &threadSafe |
| 148 isStub = true |
| 149 |
| 150 default: |
| 151 return nil, errors.Reason("unsupported runtime %(runtime)q").D("
runtime", aem.Runtime).Err() |
| 152 } |
| 153 |
| 154 if mvm := aem.ManagedVm; mvm != nil { |
| 155 if len(mvm.Scopes) > 0 { |
| 156 appYAML.BetaSettings = &gaeAppYAMLBetaSettings{ |
| 157 ServiceAccountScopes: strings.Join(mvm.Scopes, "
,"), |
| 158 } |
| 159 } |
| 160 } |
| 161 |
| 162 if handlerSet := aem.Handlers; handlerSet != nil { |
| 163 appYAML.Handlers = make([]*gaeAppYAMLHandler, len(handlerSet.Han
dler)) |
| 164 for i, handler := range handlerSet.Handler { |
| 165 entry := gaeAppYAMLHandler{ |
| 166 URL: handler.Url, |
| 167 Secure: handler.Secure.AppYAMLString(), |
| 168 Login: handler.Login.AppYAMLString(), |
| 169 } |
| 170 |
| 171 // Fill in static dir/file paths. |
| 172 switch t := handler.GetContent().(type) { |
| 173 case nil: |
| 174 entry.Script = defaultScript |
| 175 |
| 176 case *deploy.AppEngineModule_Handler_Script: |
| 177 entry.Script = t.Script |
| 178 if entry.Script == "" { |
| 179 entry.Script = defaultScript |
| 180 } |
| 181 |
| 182 case *deploy.AppEngineModule_Handler_StaticBuildDir: |
| 183 entry.StaticDir = staticMap[t.StaticBuildDir] |
| 184 |
| 185 case *deploy.AppEngineModule_Handler_StaticFiles_: |
| 186 sf := t.StaticFiles |
| 187 |
| 188 relDir := staticMap[sf.GetBuild()] |
| 189 entry.Upload = filepath.Join(relDir, sf.Upload) |
| 190 entry.StaticFiles = filepath.Join(relDir, sf.Url
Map) |
| 191 |
| 192 default: |
| 193 return nil, errors.Reason("don't know how to han
dle content %(content)T"). |
| 194 D("content", t).D("url", handler.Url).Er
r() |
| 195 } |
| 196 |
| 197 if entry.Script != "" && isStub { |
| 198 return nil, errors.Reason("stub module cannot ha
ve entry script").Err() |
| 199 } |
| 200 |
| 201 appYAML.Handlers[i] = &entry |
| 202 } |
| 203 } |
| 204 |
| 205 return &appYAML, nil |
| 206 } |
| 207 |
| 208 func gaeBuildCronYAML(cp *layoutDeploymentCloudProject) *gaeCronYAML { |
| 209 var cronYAML gaeCronYAML |
| 210 |
| 211 for _, m := range cp.appEngineModules { |
| 212 for _, cron := range m.resources.Cron { |
| 213 cronYAML.Cron = append(cronYAML.Cron, &gaeCronYAMLEntry{ |
| 214 Description: cron.Description, |
| 215 URL: cron.Url, |
| 216 Schedule: cron.Schedule, |
| 217 Target: m.ModuleName, |
| 218 }) |
| 219 } |
| 220 } |
| 221 return &cronYAML |
| 222 } |
| 223 |
| 224 func gaeBuildIndexYAML(cp *layoutDeploymentCloudProject) *gaeIndexYAML { |
| 225 var indexYAML gaeIndexYAML |
| 226 |
| 227 for _, index := range cp.resources.Index { |
| 228 entry := gaeIndexYAMLEntry{ |
| 229 Kind: index.Kind, |
| 230 Properties: make([]*gaeIndexYAMLEntryProperty, len(index
.Property)), |
| 231 } |
| 232 if index.Ancestor { |
| 233 entry.Ancestor = "yes" |
| 234 } |
| 235 for i, prop := range index.Property { |
| 236 entry.Properties[i] = &gaeIndexYAMLEntryProperty{ |
| 237 Name: prop.Name, |
| 238 Direction: prop.Direction.AppYAMLString(), |
| 239 } |
| 240 } |
| 241 indexYAML.Indexes = append(indexYAML.Indexes, &entry) |
| 242 } |
| 243 return &indexYAML |
| 244 } |
| 245 |
| 246 func gaeBuildDispatchYAML(cp *layoutDeploymentCloudProject) (*gaeDispatchYAML, e
rror) { |
| 247 var dispatchYAML gaeDispatchYAML |
| 248 |
| 249 for _, m := range cp.appEngineModules { |
| 250 for _, url := range m.resources.Dispatch { |
| 251 dispatchYAML.Dispatch = append(dispatchYAML.Dispatch, &g
aeDispatchYAMLEntry{ |
| 252 Module: m.ModuleName, |
| 253 URL: url, |
| 254 }) |
| 255 } |
| 256 } |
| 257 if count := len(dispatchYAML.Dispatch); count > 10 { |
| 258 return nil, errors.Reason("dispatch file has more than 10 routin
g rules (%(count)d)"). |
| 259 D("count", count).Err() |
| 260 } |
| 261 return &dispatchYAML, nil |
| 262 } |
| 263 |
| 264 func gaeBuildQueueYAML(cp *layoutDeploymentCloudProject) (*gaeQueueYAML, error)
{ |
| 265 var queueYAML gaeQueueYAML |
| 266 |
| 267 for _, m := range cp.appEngineModules { |
| 268 for _, queue := range m.resources.TaskQueue { |
| 269 entry := gaeQueueYAMLEntry{ |
| 270 Name: queue.Name, |
| 271 } |
| 272 switch t := queue.GetType().(type) { |
| 273 case *deploy.AppEngineResources_TaskQueue_Push_: |
| 274 push := t.Push |
| 275 |
| 276 entry.Target = m.ModuleName |
| 277 entry.Mode = "push" |
| 278 entry.Rate = push.Rate |
| 279 entry.BucketSize = int(push.BucketSize) |
| 280 entry.RetryParameters = &gaeQueueYAMLEntryRetryP
arameters{ |
| 281 TaskAgeLimit: push.RetryTaskAgeLimi
t, |
| 282 MinBackoffSeconds: int(push.RetryMinBack
offSeconds), |
| 283 MaxBackoffSeconds: int(push.RetryMaxBack
offSeconds), |
| 284 MaxDoublings: int(push.RetryMaxDoub
lings), |
| 285 } |
| 286 |
| 287 default: |
| 288 return nil, errors.Reason("unknown task queue ty
pe %(type)T").D("type", t).Err() |
| 289 } |
| 290 |
| 291 queueYAML.Queue = append(queueYAML.Queue, &entry) |
| 292 } |
| 293 } |
| 294 return &queueYAML, nil |
| 295 } |
| 296 |
| 297 // loadIndexYAMLResource loads an AppEngine "index.yaml" file from the specified |
| 298 // filesystem path and translates it into AppEngineResources Index entries. |
| 299 func loadIndexYAMLResource(path string) (*deploy.AppEngineResources, error) { |
| 300 data, err := ioutil.ReadFile(path) |
| 301 if err != nil { |
| 302 return nil, errors.Annotate(err).Reason("failed to load [%(path)
s]").D("path", path).Err() |
| 303 } |
| 304 |
| 305 var indexYAML gaeIndexYAML |
| 306 if err := yaml.Unmarshal(data, &indexYAML); err != nil { |
| 307 return nil, errors.Annotate(err).Reason("could not load 'index.y
aml' from [%(path)s]"). |
| 308 D("path", path).Err() |
| 309 } |
| 310 |
| 311 var res deploy.AppEngineResources |
| 312 if len(indexYAML.Indexes) > 0 { |
| 313 res.Index = make([]*deploy.AppEngineResources_Index, len(indexYA
ML.Indexes)) |
| 314 for i, idx := range indexYAML.Indexes { |
| 315 entry := deploy.AppEngineResources_Index{ |
| 316 Kind: idx.Kind, |
| 317 Ancestor: (idx.Ancestor == "yes"), |
| 318 Property: make([]*deploy.AppEngineResources_Inde
x_Property, len(idx.Properties)), |
| 319 } |
| 320 for pidx, idxProp := range idx.Properties { |
| 321 dir, err := deploy.IndexDirectionFromAppYAMLStri
ng(idxProp.Direction) |
| 322 if err != nil { |
| 323 return nil, errors.Annotate(err).Reason(
"could not identify direction for entry"). |
| 324 D("index", i).D("propertyIndex",
pidx).Err() |
| 325 } |
| 326 |
| 327 entry.Property[pidx] = &deploy.AppEngineResource
s_Index_Property{ |
| 328 Name: idxProp.Name, |
| 329 Direction: dir, |
| 330 } |
| 331 } |
| 332 |
| 333 res.Index[i] = &entry |
| 334 } |
| 335 } |
| 336 return &res, nil |
| 337 } |
| OLD | NEW |