appengine 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/CHANGELOG.md +13 -0
- data/README.md +43 -4
- data/Rakefile +6 -0
- data/lib/appengine.rb +5 -4
- data/lib/appengine/env.rb +16 -8
- data/lib/appengine/exec.rb +421 -0
- data/lib/appengine/railtie.rb +55 -13
- data/lib/appengine/tasks.rb +308 -0
- data/lib/appengine/util/gcloud.rb +190 -0
- data/lib/appengine/version.rb +1 -1
- data/test/test_env.rb +0 -2
- metadata +41 -16
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 06eae82181654cc6c73ea33d6fda3c5cc5f2469a
|
4
|
+
data.tar.gz: 33c1eae652eb721b503eb848e7e6034f0843c279
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e8fe8e5431795a42492b08e7b86d61a5c9e3d21d2aa28625a1928da674fad328ba3e080ff4c7f2df87e54b0621c51ab797d44d4f0f10c9e327e7122a443a3ea3
|
7
|
+
data.tar.gz: 67586c5fae41dbbe9ca43dca1dfc9c59f8dc5f5b6e447902826b47c0b5313f339852e07c26f234f3dca19213c595e6bf663048c72ad7896d2e3921c4c8e14b39
|
data/CHANGELOG.md
CHANGED
@@ -2,6 +2,19 @@
|
|
2
2
|
|
3
3
|
This is the change history for the appengine gem.
|
4
4
|
|
5
|
+
## v0.4.0 (2017-07-17)
|
6
|
+
|
7
|
+
* Provided rake tasks to access App Engine remote execution.
|
8
|
+
* Reimplemented AppEngine::Env as an alias to the google-cloud-env gem's
|
9
|
+
functionality.
|
10
|
+
|
11
|
+
## v0.3.0 (2017-02-28)
|
12
|
+
|
13
|
+
* Replaced logger integration with a dependency on the stackdriver gem,
|
14
|
+
which supersedes it.
|
15
|
+
* Stubbed out the env class in preparation to change it to a dependency
|
16
|
+
on the upcoming google-cloud-env gem.
|
17
|
+
|
5
18
|
## v0.2.0 (2016-05-04)
|
6
19
|
|
7
20
|
* Tools for integration with the Google Cloud Console logger, including
|
data/README.md
CHANGED
@@ -4,18 +4,23 @@ Google App Engine Integration Tools
|
|
4
4
|
This repository contains the "appengine" gem, a collection of libraries and
|
5
5
|
plugins for integrating Ruby apps with Google App Engine. It is not required
|
6
6
|
for deploying a ruby application to Google App Engine, but it provides a
|
7
|
-
number of convenience hooks for
|
7
|
+
number of convenience hooks and tools for integrating into the App Engine
|
8
8
|
environment.
|
9
9
|
|
10
10
|
Currently, it includes:
|
11
11
|
|
12
12
|
* Automatic Stackdriver instrumentation for Rails apps. This means logs,
|
13
|
-
error reports, and latency traces are reported to the cloud console
|
14
|
-
|
15
|
-
|
13
|
+
error reports, and latency traces are reported to the cloud console,
|
14
|
+
and debugger integration is available.
|
15
|
+
* A client and rake tasks for executing application commands in the App
|
16
|
+
Engine environment against production resources, useful for tasks such as
|
17
|
+
running production database migrations.
|
18
|
+
* Convenient access to environment information such as project ID and VM
|
19
|
+
properties.
|
16
20
|
|
17
21
|
Planned for the near future:
|
18
22
|
|
23
|
+
* Tools for generating "app.yaml" configuration files for Ruby applications.
|
19
24
|
* Streamlined implementation of health checks and other lifecycle hooks.
|
20
25
|
|
21
26
|
For more information on using Google Cloud Platform to deploy Ruby apps,
|
@@ -36,6 +41,9 @@ may need to include the line:
|
|
36
41
|
in your `config/application.rb` file if you aren't already requiring all
|
37
42
|
bundled gems.
|
38
43
|
|
44
|
+
If you are running a different web framework, you may need to add some
|
45
|
+
initialization code to activate the features listed below.
|
46
|
+
|
39
47
|
## Logging and monitoring
|
40
48
|
|
41
49
|
This library automatically installs the "stackdriver" gem, which instruments
|
@@ -47,6 +55,37 @@ monitoring features of Google App Engine, see:
|
|
47
55
|
* [google-cloud-error_reporting instrumentation](http://googlecloudplatform.github.io/google-cloud-ruby/#/docs/google-cloud-error_reporting/latest/guides/instrumentation)
|
48
56
|
* [google-cloud-trace instrumentation](http://googlecloudplatform.github.io/google-cloud-ruby/#/docs/google-cloud-trace/latest/guides/instrumentation)
|
49
57
|
|
58
|
+
Rails applications automatically activate this instrumentation when the gem
|
59
|
+
is present. You may opt out of individual services by providing appropriate
|
60
|
+
Rails configuration. See {AppEngine::Railtie} for more information.
|
61
|
+
|
62
|
+
Non-Rails applications must provide initialization code to activate this
|
63
|
+
instrumentation, typically by installing a Rack middleware. See the individual
|
64
|
+
service documentation links above for more information.
|
65
|
+
|
66
|
+
## App Engine remote execution
|
67
|
+
|
68
|
+
This library provides rake tasks for App Engine remote execution, allowing
|
69
|
+
App Engine applications to perform on-demand tasks in the App Engine
|
70
|
+
environment. This may be used for safe running of ops and maintenance tasks,
|
71
|
+
such as database migrations, that access production cloud resources. For
|
72
|
+
example, you could run a production database migration in a Rails app using:
|
73
|
+
|
74
|
+
bundle exec rake appengine:exec -- bundle exec rake db:migrate
|
75
|
+
|
76
|
+
The migration would be run in VMs provided by Google Cloud. It uses a
|
77
|
+
privileged service account that will have access to the production cloud
|
78
|
+
resources, such as Cloud SQL instances, used by the application. This mechanism
|
79
|
+
is often much easier and safer than running the task on a local workstation and
|
80
|
+
granting that workstation direct access to those Cloud SQL instances.
|
81
|
+
|
82
|
+
See {AppEngine::Exec} for more information on App Engine remote execution.
|
83
|
+
|
84
|
+
See {AppEngine::Tasks} for more information on running the rake tasks. The
|
85
|
+
tasks are available automatically in Rails applications when the gem is
|
86
|
+
present. Non-Rails applications may install the tasks by adding the line
|
87
|
+
`require "appengine/tasks"` to the `Rakefile`.
|
88
|
+
|
50
89
|
## Development and support
|
51
90
|
|
52
91
|
The source code for this gem is available on Github at
|
data/Rakefile
CHANGED
data/lib/appengine.rb
CHANGED
@@ -13,7 +13,7 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
;
|
15
15
|
|
16
|
-
#
|
16
|
+
# # Google AppEngine integration
|
17
17
|
#
|
18
18
|
# The AppEngine module includes optional tools helping Ruby applications to
|
19
19
|
# integrate more closely with the Google App Engine environment.
|
@@ -22,8 +22,9 @@ module AppEngine
|
|
22
22
|
end
|
23
23
|
|
24
24
|
|
25
|
-
require
|
26
|
-
require
|
27
|
-
require
|
25
|
+
require "appengine/version"
|
26
|
+
require "appengine/env"
|
27
|
+
require "appengine/exec"
|
28
|
+
require "appengine/railtie" if defined? ::Rails
|
28
29
|
|
29
30
|
require "stackdriver"
|
data/lib/appengine/env.rb
CHANGED
@@ -13,18 +13,26 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
;
|
15
15
|
|
16
|
+
require "google/cloud/env"
|
17
|
+
|
16
18
|
|
17
19
|
module AppEngine
|
18
20
|
|
19
21
|
##
|
20
|
-
# A
|
22
|
+
# A convenience object that provides information on the Google Cloud
|
23
|
+
# hosting environment. For example, you can call
|
21
24
|
#
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
25
|
+
# if AppEngine::Env.app_engine?
|
26
|
+
# # Do something
|
27
|
+
# end
|
28
|
+
#
|
29
|
+
# Technically, `Env` is not actually a module, but is simply set to the
|
30
|
+
# `Google::Cloud.env` object.
|
31
|
+
#
|
32
|
+
# This is provided mostly for backward compatibility with previous usage, and
|
33
|
+
# is mildly deprecated. Generally, you should call `Google::Cloud.env`
|
34
|
+
# directly instead. See the documentation for the `google-cloud-env` gem.
|
35
|
+
#
|
36
|
+
Env = ::Google::Cloud.env
|
29
37
|
|
30
38
|
end
|
@@ -0,0 +1,421 @@
|
|
1
|
+
# Copyright 2017 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
;
|
15
|
+
|
16
|
+
require "yaml"
|
17
|
+
require "json"
|
18
|
+
|
19
|
+
require "appengine/util/gcloud"
|
20
|
+
|
21
|
+
|
22
|
+
module AppEngine
|
23
|
+
|
24
|
+
##
|
25
|
+
# # App Engine remote execution
|
26
|
+
#
|
27
|
+
# This class provides a client for App Engine remote execution, allowing
|
28
|
+
# App Engine applications to perform on-demand tasks in the App Engine
|
29
|
+
# environment. This may be used for safe running of ops and maintenance
|
30
|
+
# tasks, such as database migrations, that access production cloud resources.
|
31
|
+
#
|
32
|
+
# ## About App Engine execution
|
33
|
+
#
|
34
|
+
# App Engine execution spins up an image of a deployed App Engine app, and
|
35
|
+
# runs a command in that image. For example, if your app runs on Ruby on
|
36
|
+
# Rails, then your app provides a `bin/rails` tool, and you may invoke it
|
37
|
+
# using App Engine execution---for example to run a command such as
|
38
|
+
# `bundle exec bin/rails db:migrate` in the image.
|
39
|
+
#
|
40
|
+
# When App Engine execution runs your command, it provides access to key
|
41
|
+
# elements of the App Engine environment, including:
|
42
|
+
# * The same runtime that runs your application in App Engine itself.
|
43
|
+
# * Any Cloud SQL connections requested by your application.
|
44
|
+
# * Any environment variables set by your application.
|
45
|
+
#
|
46
|
+
# The command runs on virtual machines provided by Google Cloud Container
|
47
|
+
# Builder, and has access to the credentials of the Cloud Container Builder
|
48
|
+
# service account.
|
49
|
+
#
|
50
|
+
# ## Prerequisites
|
51
|
+
#
|
52
|
+
# To use App Engine remote execution, you will need:
|
53
|
+
#
|
54
|
+
# * An app deployed to Google App Engine, of course!
|
55
|
+
# * The gcloud SDK installed and configured. See https://cloud.google.com/sdk/
|
56
|
+
# * The `appengine` gem.
|
57
|
+
#
|
58
|
+
# You may also need to grant the Cloud Container Builder service account
|
59
|
+
# any permissions needed by your command. Often, Project Editor permissions
|
60
|
+
# will be sufficient for most tasks. You can find the service account
|
61
|
+
# configuration in the IAM tab in the Cloud Console under the name
|
62
|
+
# `[your-project-number]@cloudbuild.gserviceaccount.com`.
|
63
|
+
#
|
64
|
+
# You may use the `AppEngine::Exec` class to run commands directly. However,
|
65
|
+
# in most cases, it will be easier to run commands via the provided rake
|
66
|
+
# tasks. See {AppEngine::Tasks} for more info.
|
67
|
+
#
|
68
|
+
# ## Configuring
|
69
|
+
#
|
70
|
+
# This class uses three parameters to specify which application image to use
|
71
|
+
# to run your command: `service`, `config_path`, and `version`.
|
72
|
+
#
|
73
|
+
# In most cases, you can use the defaults. The Exec class will look in your
|
74
|
+
# current directory for a file called `./app.yaml` which describes your App
|
75
|
+
# Engine service. It gets the service name from this file (or uses the
|
76
|
+
# "default" name if none is specified), then looks up the most recently
|
77
|
+
# created deployment version for that service. That deployment version then
|
78
|
+
# provides the application image that runs your command.
|
79
|
+
#
|
80
|
+
# If your app has multiple services, you may specify which config file
|
81
|
+
# (other than `./app.yaml`) describes the desired service, by providing the
|
82
|
+
# `config_path` parameter. Alternately, you may specify a service name
|
83
|
+
# directly by providing the `service` parameter. If you provide both
|
84
|
+
# parameters, `service` takes precedence.
|
85
|
+
#
|
86
|
+
# Usually, App Engine execution uses the image for the most recently created
|
87
|
+
# version of the service. (Note: the most recently created version is used,
|
88
|
+
# regardless of whether that version is currently receiving traffic.) If you
|
89
|
+
# want to use the image for a different version, you may specify a version
|
90
|
+
# by providing the `version` parameter.
|
91
|
+
#
|
92
|
+
# You may also provide a timeout, which is the length of time that App
|
93
|
+
# Engine execution will allow your command to run before it is considered to
|
94
|
+
# have stalled and is terminated. The timeout should be a string of the form
|
95
|
+
# `2h15m10s`. The default is `10m`.
|
96
|
+
#
|
97
|
+
# ## Resource usage and billing
|
98
|
+
#
|
99
|
+
# App Engine remote execution uses virtual machine resources provided by
|
100
|
+
# Google Cloud Container Builder. Generally, a certain number of usage
|
101
|
+
# minutes per day is covered under a free tier, but additional compute usage
|
102
|
+
# beyond that time is billed to your Google Cloud account. For more details,
|
103
|
+
# see https://cloud.google.com/container-builder/pricing
|
104
|
+
#
|
105
|
+
# If your command makes API calls or utilizes other cloud resources, you may
|
106
|
+
# also be billed for that usage. However, remote execution does not use
|
107
|
+
# actual App Engine instances, and you will not be billed for additional App
|
108
|
+
# Engine instance usage.
|
109
|
+
#
|
110
|
+
class Exec
|
111
|
+
|
112
|
+
@default_timeout = "10m".freeze
|
113
|
+
@default_service = "default".freeze
|
114
|
+
@default_config_path = "./app.yaml".freeze
|
115
|
+
@default_wrapper_image = "gcr.io/google-appengine/exec-wrapper".freeze
|
116
|
+
|
117
|
+
|
118
|
+
##
|
119
|
+
# Base class for exec-related usage errors.
|
120
|
+
#
|
121
|
+
class UsageError < ::StandardError
|
122
|
+
end
|
123
|
+
|
124
|
+
|
125
|
+
##
|
126
|
+
# Exception raised when the App Engine config file could not be found.
|
127
|
+
#
|
128
|
+
class ConfigFileNotFound < UsageError
|
129
|
+
def initialize config_path
|
130
|
+
@config_path = config_path
|
131
|
+
super "Config file #{config_path} not found."
|
132
|
+
end
|
133
|
+
attr_reader :config_path
|
134
|
+
end
|
135
|
+
|
136
|
+
##
|
137
|
+
# Exception raised when the App Engine config file could not be parsed.
|
138
|
+
#
|
139
|
+
class BadConfigFileFormat < UsageError
|
140
|
+
def initialize config_path
|
141
|
+
@config_path = config_path
|
142
|
+
super "Config file #{config_path} malformed."
|
143
|
+
end
|
144
|
+
attr_reader :config_path
|
145
|
+
end
|
146
|
+
|
147
|
+
##
|
148
|
+
# Exception raised when the given version could not be found, or no
|
149
|
+
# versions at all could be found for the given service.
|
150
|
+
#
|
151
|
+
class NoSuchVersion < UsageError
|
152
|
+
def initialize service, version=nil
|
153
|
+
@service = service
|
154
|
+
@version = version
|
155
|
+
if version
|
156
|
+
super "No such version \"#{version}\" for service \"#{service}\""
|
157
|
+
else
|
158
|
+
super "No versions found for service \"#{service}\""
|
159
|
+
end
|
160
|
+
end
|
161
|
+
attr_reader :service
|
162
|
+
attr_reader :version
|
163
|
+
end
|
164
|
+
|
165
|
+
##
|
166
|
+
# Exception raised when an explicitly-specified service name conflicts with
|
167
|
+
# a config-specified service name.
|
168
|
+
#
|
169
|
+
class ServiceNameConflict < UsageError
|
170
|
+
def initialize service_name, config_name, config_path
|
171
|
+
@service_name = service_name
|
172
|
+
@config_name = config_name
|
173
|
+
@config_path = config_path
|
174
|
+
super "Service name conflicts with config file"
|
175
|
+
end
|
176
|
+
attr_reader :service_name
|
177
|
+
attr_reader :config_name
|
178
|
+
attr_reader :config_path
|
179
|
+
end
|
180
|
+
|
181
|
+
|
182
|
+
class << self
|
183
|
+
|
184
|
+
## @return [String] Default command timeout.
|
185
|
+
attr_accessor :default_timeout
|
186
|
+
|
187
|
+
## @return [String] Default service name if the config doesn't specify.
|
188
|
+
attr_accessor :default_service
|
189
|
+
|
190
|
+
## @return [String] Path to default config file.
|
191
|
+
attr_accessor :default_config_path
|
192
|
+
|
193
|
+
## @return [String] Docker image that implements the app engine wrapper.
|
194
|
+
attr_accessor :default_wrapper_image
|
195
|
+
|
196
|
+
##
|
197
|
+
# Create an execution for a rake task.
|
198
|
+
#
|
199
|
+
# @param name [String] Name of the task
|
200
|
+
# @param args [Array<String>] Args to pass to the task
|
201
|
+
# @param env_args [Array<String>] Environment variable settings, each
|
202
|
+
# of the form `NAME=value`.
|
203
|
+
# @param service [String,nil] Name of the service. If omitted, obtains
|
204
|
+
# the service name from the config file.
|
205
|
+
# @param config_path [String,nil] App Engine config file to get the
|
206
|
+
# service name from if the service name is not provided directly.
|
207
|
+
# Defaults to the value of `AppEngine::Exec.default_config_path`.
|
208
|
+
# @param version [String,nil] Version string. Defaults to the most
|
209
|
+
# recently created version of the given service (which may not be the
|
210
|
+
# one currently receiving traffic).
|
211
|
+
# @param timeout [String,nil] Timeout string. Defaults to the value of
|
212
|
+
# `AppEngine::Exec.default_timeout`.
|
213
|
+
#
|
214
|
+
def new_rake_task name, args: [], env_args: [],
|
215
|
+
service: nil, config_path: nil, version: nil,
|
216
|
+
timeout: nil
|
217
|
+
escaped_args = args.map{ |arg|
|
218
|
+
arg.gsub(/[,\[\]]/){ |m| "\\#{m}" }
|
219
|
+
}
|
220
|
+
if escaped_args.empty?
|
221
|
+
name_with_args = name
|
222
|
+
else
|
223
|
+
name_with_args = "#{name}[#{escaped_args.join ','}]"
|
224
|
+
end
|
225
|
+
new ["bundle", "exec", "rake", name_with_args] + env_args,
|
226
|
+
service: service, config_path: config_path, version: version,
|
227
|
+
timeout: timeout
|
228
|
+
end
|
229
|
+
|
230
|
+
end
|
231
|
+
|
232
|
+
|
233
|
+
##
|
234
|
+
# Create an execution for the given command.
|
235
|
+
#
|
236
|
+
# @param command [Array<String>] The command in array form.
|
237
|
+
# @param service [String,nil] Name of the service. If omitted, obtains
|
238
|
+
# the service name from the config file.
|
239
|
+
# @param config_path [String,nil] App Engine config file to get the
|
240
|
+
# service name from if the service name is not provided directly.
|
241
|
+
# Defaults to the value of `AppEngine::Exec.default_config_path`.
|
242
|
+
# @param version [String,nil] Version string. Defaults to the most
|
243
|
+
# recently created version of the given service (which may not be the
|
244
|
+
# one currently receiving traffic).
|
245
|
+
# @param timeout [String,nil] Timeout string. Defaults to the value of
|
246
|
+
# `AppEngine::Exec.default_timeout`.
|
247
|
+
#
|
248
|
+
def initialize command,
|
249
|
+
service: nil, config_path: nil, version: nil, timeout: nil
|
250
|
+
@command = command
|
251
|
+
@service = service
|
252
|
+
@config_path = config_path
|
253
|
+
@version = version
|
254
|
+
@timeout = timeout
|
255
|
+
@wrapper_image = nil
|
256
|
+
|
257
|
+
yield self if block_given?
|
258
|
+
end
|
259
|
+
|
260
|
+
|
261
|
+
## @return [String,nil] The service name, or nil to read from the config.
|
262
|
+
attr_accessor :service
|
263
|
+
|
264
|
+
## @return [String,nil] Path to the config file, or nil to use the default.
|
265
|
+
attr_accessor :config_path
|
266
|
+
|
267
|
+
## @return [String,nil] Service version, or nil to use the most recent.
|
268
|
+
attr_accessor :version
|
269
|
+
|
270
|
+
## @return [String,nil] Command timeout, or nil to use the default.
|
271
|
+
attr_accessor :timeout
|
272
|
+
|
273
|
+
## @return [String,Array<String>] Command to run.
|
274
|
+
attr_accessor :command
|
275
|
+
|
276
|
+
## @return [String] Custom wrapper image to use, or nil to use the default.
|
277
|
+
attr_accessor :wrapper_image
|
278
|
+
|
279
|
+
|
280
|
+
##
|
281
|
+
# Executes the command synchronously. Streams the logs back to standard out
|
282
|
+
# and does not return until the command has completed or timed out.
|
283
|
+
#
|
284
|
+
def start
|
285
|
+
resolve_parameters
|
286
|
+
|
287
|
+
version_info = version_info @service, @version
|
288
|
+
env_variables = version_info["envVariables"] || {}
|
289
|
+
beta_settings = version_info["betaSettings"] || {}
|
290
|
+
cloud_sql_instances = beta_settings["cloud_sql_instances"] || []
|
291
|
+
image = version_info["deployment"]["container"]["image"]
|
292
|
+
|
293
|
+
config = build_config command, image, env_variables, cloud_sql_instances
|
294
|
+
file = ::Tempfile.new ["cloudbuild_", ".json"]
|
295
|
+
begin
|
296
|
+
::JSON.dump config, file
|
297
|
+
file.flush
|
298
|
+
Util::Gcloud.execute [
|
299
|
+
"container", "builds", "submit",
|
300
|
+
"--no-source",
|
301
|
+
"--config=#{file.path}",
|
302
|
+
"--timeout=#{@timeout}"]
|
303
|
+
ensure
|
304
|
+
file.close!
|
305
|
+
end
|
306
|
+
end
|
307
|
+
|
308
|
+
|
309
|
+
private
|
310
|
+
|
311
|
+
##
|
312
|
+
# @private
|
313
|
+
# Resolves and canonicalizes all the parameters.
|
314
|
+
#
|
315
|
+
def resolve_parameters
|
316
|
+
unless @command.is_a? Array
|
317
|
+
@command = ::Shellwords.parse @command.to_s
|
318
|
+
end
|
319
|
+
|
320
|
+
config_service = config_path = nil
|
321
|
+
if @config_path || !@service
|
322
|
+
config_service = begin
|
323
|
+
config_path = @config_path || Exec.default_config_path
|
324
|
+
::YAML.load_file(config_path)["service"] || Exec.default_service
|
325
|
+
rescue ::Errno::ENOENT
|
326
|
+
raise ConfigFileNotFound.new config_path
|
327
|
+
rescue
|
328
|
+
raise BadConfigFileFormat.new config_path
|
329
|
+
end
|
330
|
+
end
|
331
|
+
if @service && config_service && @service != config_service
|
332
|
+
raise ServiceNameConflict.new @service, config_service, config_path
|
333
|
+
end
|
334
|
+
|
335
|
+
@service ||= config_service
|
336
|
+
@version ||= latest_version @service
|
337
|
+
@timeout ||= Exec.default_timeout
|
338
|
+
@wrapper_image ||= Exec.default_wrapper_image
|
339
|
+
end
|
340
|
+
|
341
|
+
##
|
342
|
+
# @private
|
343
|
+
# Builds a cloudbuild config as a data structure.
|
344
|
+
#
|
345
|
+
# @param command [Array<String>] The command in array form.
|
346
|
+
# @param image [String] The fully qualified image path.
|
347
|
+
# @param env_variables[Hash<String,String>] Environment variables.
|
348
|
+
# @param cloud_sql_instances[String,Array<String>] Names of cloud sql
|
349
|
+
# instances to connect to.
|
350
|
+
#
|
351
|
+
def build_config command, image, env_variables, cloud_sql_instances
|
352
|
+
args = ["-i", image]
|
353
|
+
env_variables.each do |k, v|
|
354
|
+
args << "-e" << "#{k}=#{v}"
|
355
|
+
end
|
356
|
+
unless cloud_sql_instances.empty?
|
357
|
+
cloud_sql_instances = Array(cloud_sql_instances)
|
358
|
+
cloud_sql_instances.each do |sql|
|
359
|
+
args << "-s" << sql
|
360
|
+
end
|
361
|
+
end
|
362
|
+
args << "--"
|
363
|
+
args += command
|
364
|
+
|
365
|
+
{
|
366
|
+
"steps" => [
|
367
|
+
"name" => @wrapper_image,
|
368
|
+
"args" => args
|
369
|
+
]
|
370
|
+
}
|
371
|
+
end
|
372
|
+
|
373
|
+
##
|
374
|
+
# @private
|
375
|
+
# Returns the name of the most recently created version of the given
|
376
|
+
# service.
|
377
|
+
#
|
378
|
+
# @param service [String] Name of the service.
|
379
|
+
# @return [String] Name of the most recent version.
|
380
|
+
#
|
381
|
+
def latest_version service
|
382
|
+
result = Util::Gcloud.execute [
|
383
|
+
"app", "versions", "list",
|
384
|
+
"--service=#{service}",
|
385
|
+
"--format=get(version.id)",
|
386
|
+
"--sort-by=~version.createTime",
|
387
|
+
"--limit=1"],
|
388
|
+
capture: true, assert: false
|
389
|
+
result = result.split.first
|
390
|
+
raise NoSuchVersion.new(service) unless result
|
391
|
+
result
|
392
|
+
end
|
393
|
+
|
394
|
+
##
|
395
|
+
# @private
|
396
|
+
# Returns full information on the given version of the given service.
|
397
|
+
#
|
398
|
+
# @param service [String] Name of the service. If omitted, the service
|
399
|
+
# "default" is used.
|
400
|
+
# @param version [String] Name of the version. If omitted, the most
|
401
|
+
# recently deployed is used.
|
402
|
+
# @return [Hash,nil] A collection of fields parsed from the JSON
|
403
|
+
# representation of the version, or nil if the requested version
|
404
|
+
# doesn't exist.
|
405
|
+
#
|
406
|
+
def version_info service, version
|
407
|
+
service ||= "default"
|
408
|
+
version ||= latest_version service
|
409
|
+
result = Util::Gcloud.execute [
|
410
|
+
"app", "versions", "describe", version,
|
411
|
+
"--service=#{service}",
|
412
|
+
"--format=json"],
|
413
|
+
capture: true, assert: false
|
414
|
+
result.strip!
|
415
|
+
raise NoSuchVersion.new(service, version) if result.empty?
|
416
|
+
::JSON.parse result
|
417
|
+
end
|
418
|
+
|
419
|
+
end
|
420
|
+
|
421
|
+
end
|
data/lib/appengine/railtie.rb
CHANGED
@@ -13,35 +13,77 @@
|
|
13
13
|
# limitations under the License.
|
14
14
|
;
|
15
15
|
|
16
|
-
require 'fileutils'
|
17
|
-
|
18
16
|
|
19
17
|
module AppEngine
|
20
18
|
|
21
|
-
|
22
|
-
#
|
19
|
+
##
|
20
|
+
# # AppEngine Rails integration
|
23
21
|
#
|
24
22
|
# A Railtie providing Rails integration with the Google App Engine runtime
|
25
|
-
# environment.
|
26
|
-
#
|
23
|
+
# environment.
|
24
|
+
#
|
25
|
+
# Specifically:
|
26
|
+
#
|
27
|
+
# * It installs the Stackdriver instrumentation, providing application
|
28
|
+
# diagnostics to the project's Stackdriver account.
|
29
|
+
# * It installs the rake tasks, providing the ability to execute commands
|
30
|
+
# on demand in the production App Engine environment.
|
27
31
|
#
|
28
32
|
# To use, just include the "appengine" gem in your gemfile, and make sure
|
29
33
|
# it is required in your config/application.rb (if you are not already
|
30
34
|
# using Bundler.require).
|
31
35
|
#
|
32
|
-
#
|
36
|
+
# ## Configuration
|
37
|
+
#
|
38
|
+
# You may selectively deactivate features of this Railtie using Rails
|
39
|
+
# configuration keys. For example, to disable rake tasks, include the
|
40
|
+
# following line in one of your Rails configuration files:
|
41
|
+
#
|
42
|
+
# config.appengine.define_tasks = false
|
43
|
+
#
|
44
|
+
# The following configuration keys are supported. Additional keys specific
|
45
|
+
# to the various Stackdriver services may be defined in the individual
|
46
|
+
# libraries.
|
47
|
+
#
|
48
|
+
# ### appengine.define_tasks
|
49
|
+
#
|
50
|
+
# Causes rake tasks to be added to the application. Default is true. Set it
|
51
|
+
# to false to disable App Engine rake tasks.
|
52
|
+
#
|
53
|
+
# ### google_cloud.use_logging
|
54
|
+
#
|
55
|
+
# Activates Stackdriver Logging, collecting Rails logs so they appear on
|
56
|
+
# the Google Cloud console. Default is true. Set it to false to disable
|
57
|
+
# logging instrumentation.
|
58
|
+
#
|
59
|
+
# ### google_cloud.use_error_reporting
|
60
|
+
#
|
61
|
+
# Activates Stackdriver Error Reporting, collecting exceptions so they appear
|
62
|
+
# on the Google Cloud console. Default is true. Set it to false to disable
|
63
|
+
# error instrumentation.
|
64
|
+
#
|
65
|
+
# ### google_cloud.use_trace
|
66
|
+
#
|
67
|
+
# Activates Stackdriver Trace instrumentation, collecting application latency
|
68
|
+
# trace data so it appears on the Google Cloud conosle. Default is true. Set
|
69
|
+
# it to false to disable trace instrumentation.
|
70
|
+
#
|
71
|
+
# ### google_cloud.use_debugger
|
72
|
+
#
|
73
|
+
# Enables the Stackdriver Debugger. Default is true. Set it to false to
|
74
|
+
# disable debugging.
|
33
75
|
#
|
34
|
-
# This is a placeholder for now.
|
35
|
-
|
36
76
|
class Railtie < ::Rails::Railtie
|
37
77
|
|
38
|
-
# :stopdoc:
|
39
|
-
|
40
78
|
config.appengine = ::ActiveSupport::OrderedOptions.new
|
79
|
+
config.appengine.define_tasks = true
|
41
80
|
|
42
|
-
|
81
|
+
rake_tasks do |app|
|
82
|
+
if app.config.appengine.define_tasks
|
83
|
+
require "appengine/tasks"
|
84
|
+
end
|
85
|
+
end
|
43
86
|
|
44
87
|
end
|
45
88
|
|
46
|
-
|
47
89
|
end
|
@@ -0,0 +1,308 @@
|
|
1
|
+
# Copyright 2017 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
;
|
15
|
+
|
16
|
+
# This file should be loaded or required from a Rakefile to define AppEngine
|
17
|
+
# related tasks.
|
18
|
+
|
19
|
+
require "shellwords"
|
20
|
+
|
21
|
+
require "appengine/util/gcloud"
|
22
|
+
require "appengine/exec"
|
23
|
+
|
24
|
+
|
25
|
+
module AppEngine
|
26
|
+
|
27
|
+
##
|
28
|
+
# # App Engine Rake Tasks.
|
29
|
+
#
|
30
|
+
# To make these tasks available, add the line `require "appengine/tasks"`
|
31
|
+
# to your Rakefile. If your app uses Ruby on Rails, then the appengine gem
|
32
|
+
# provides a railtie that adds its tasks automatically, so you don't have
|
33
|
+
# to do anything beyond adding the gem to your Gemfile.
|
34
|
+
#
|
35
|
+
# The following tasks are defined:
|
36
|
+
#
|
37
|
+
# ## Rake appengine:exec
|
38
|
+
#
|
39
|
+
# Executes a given command in the context of an App Engine application, using
|
40
|
+
# App Engine remote execution. See {AppEngine::Exec} for more information on
|
41
|
+
# this capability.
|
42
|
+
#
|
43
|
+
# The command to be run may either be provided as a rake argument, or as
|
44
|
+
# command line arguments, delimited by two dashes `--`. (The dashes are
|
45
|
+
# needed to separate your command from rake arguments and flags.)
|
46
|
+
# For example, to run a production database migration, you can run either of
|
47
|
+
# the following equivalent commands:
|
48
|
+
#
|
49
|
+
# bundle exec rake "appengine:exec[bundle exec bin/rails db:migrate]"
|
50
|
+
# bundle exec rake appengine:exec -- bundle exec bin/rails db:migrate
|
51
|
+
#
|
52
|
+
# To display usage instructions, provide two dashes but no command:
|
53
|
+
#
|
54
|
+
# bundle exec rake appengine:exec --
|
55
|
+
#
|
56
|
+
# ### Parameters
|
57
|
+
#
|
58
|
+
# You may customize the behavior of App Engine execution through a few
|
59
|
+
# enviroment variable parameters. These are set via the normal mechanism at
|
60
|
+
# the end of a rake command line. For example, to set GAE_CONFIG:
|
61
|
+
#
|
62
|
+
# bundle exec rake appengine:exec GAE_CONFIG=myservice.yaml -- bundle exec bin/rails db:migrate
|
63
|
+
#
|
64
|
+
# Be sure to set these parameters before the double dash. Any arguments
|
65
|
+
# following the double dash are interpreted as part of the command itself.
|
66
|
+
#
|
67
|
+
# The following environment variable parameters are supported:
|
68
|
+
#
|
69
|
+
# #### GAE_CONFIG
|
70
|
+
#
|
71
|
+
# Path to the App Engine config file, used when your app has multiple
|
72
|
+
# services, or the config file is otherwise not called `./app.yaml`. The
|
73
|
+
# config file is used to determine the name of the App Engine service.
|
74
|
+
#
|
75
|
+
# #### GAE_SERVICE
|
76
|
+
#
|
77
|
+
# Name of the service to be used. If both `GAE_CONFIG` and `GAE_SERVICE` are
|
78
|
+
# provided and imply different service names, an error will be raised.
|
79
|
+
#
|
80
|
+
# #### GAE_VERSION
|
81
|
+
#
|
82
|
+
# The version of the service, used to identify which application image to
|
83
|
+
# use to run your command. If not specified, uses the most recently created
|
84
|
+
# version, regardless of whether that version is actually serving traffic.
|
85
|
+
#
|
86
|
+
# #### GAE_TIMEOUT
|
87
|
+
#
|
88
|
+
# Amount of time to wait before appengine:exec terminates the command.
|
89
|
+
# Expressed as a string formatted like: "2h15m10s". Default is "10m".
|
90
|
+
#
|
91
|
+
module Tasks
|
92
|
+
|
93
|
+
## @private
|
94
|
+
CONFIG_ENV = "GAE_CONFIG"
|
95
|
+
## @private
|
96
|
+
SERVICE_ENV = "GAE_SERVICE"
|
97
|
+
## @private
|
98
|
+
VERSION_ENV = "GAE_VERSION"
|
99
|
+
## @private
|
100
|
+
TIMEOUT_ENV = "GAE_TIMEOUT"
|
101
|
+
|
102
|
+
@defined = false
|
103
|
+
|
104
|
+
class << self
|
105
|
+
|
106
|
+
##
|
107
|
+
# @private
|
108
|
+
# Define rake tasks.
|
109
|
+
#
|
110
|
+
def define
|
111
|
+
if @defined
|
112
|
+
puts "AppEngine rake tasks already defined."
|
113
|
+
return
|
114
|
+
end
|
115
|
+
@defined = true
|
116
|
+
|
117
|
+
setup_exec_task
|
118
|
+
end
|
119
|
+
|
120
|
+
private
|
121
|
+
|
122
|
+
def setup_exec_task
|
123
|
+
::Rake.application.last_description =
|
124
|
+
"Execute the given command in Google App Engine."
|
125
|
+
::Rake::Task.define_task "appengine:exec", [:cmd] do |t, args|
|
126
|
+
verify_gcloud_and_report_errors
|
127
|
+
if args[:cmd]
|
128
|
+
command = ::Shellwords.split args[:cmd]
|
129
|
+
else
|
130
|
+
i = (::ARGV.index{ |a| a.to_s == "--" } || -1) + 1
|
131
|
+
if i == 0
|
132
|
+
report_error <<~MESSAGE
|
133
|
+
No command provided for appengine:exec.
|
134
|
+
Did you remember to delimit it with two dashes? e.g.
|
135
|
+
bundle exec rake appengine:exec -- bundle exec ruby myscript.rb
|
136
|
+
For detailed usage instructions, provide two dashes but no command:
|
137
|
+
bundle exec rake appengine:exec --
|
138
|
+
MESSAGE
|
139
|
+
end
|
140
|
+
command = ::ARGV[i..-1]
|
141
|
+
if command.empty?
|
142
|
+
show_usage
|
143
|
+
exit
|
144
|
+
end
|
145
|
+
end
|
146
|
+
app_exec = Exec.new \
|
147
|
+
command,
|
148
|
+
service: ::ENV[SERVICE_ENV],
|
149
|
+
config_path: ::ENV[CONFIG_ENV],
|
150
|
+
version: ::ENV[VERSION_ENV],
|
151
|
+
timeout: ::ENV[TIMEOUT_ENV]
|
152
|
+
start_and_report_errors app_exec
|
153
|
+
exit
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
def show_usage
|
158
|
+
puts <<~USAGE
|
159
|
+
rake appengine:exec
|
160
|
+
|
161
|
+
This Rake task executes a given command in the context of an App Engine
|
162
|
+
application, using App Engine remote execution. For more information,
|
163
|
+
on this capability, see the AppEngine::Exec documentation at
|
164
|
+
http://www.rubydoc.info/gems/appengine/AppEngine/Exec
|
165
|
+
|
166
|
+
The command to be run may either be provided as a rake argument, or as
|
167
|
+
command line arguments delimited by two dashes `--`. (The dashes are
|
168
|
+
needed to separate your command from rake arguments and flags.)
|
169
|
+
For example, to run a production database migration, you can run either
|
170
|
+
of the following equivalent commands:
|
171
|
+
|
172
|
+
bundle exec rake "appengine:exec[bundle exec bin/rails db:migrate]"
|
173
|
+
bundle exec rake appengine:exec -- bundle exec bin/rails db:migrate
|
174
|
+
|
175
|
+
To display these usage instructions, provide two dashes but no command:
|
176
|
+
|
177
|
+
bundle exec rake appengine:exec --
|
178
|
+
|
179
|
+
You may customize the behavior of App Engine execution through a few
|
180
|
+
enviroment variable parameters. These are set via the normal mechanism at
|
181
|
+
the end of a rake command line. For example, to set GAE_CONFIG:
|
182
|
+
|
183
|
+
bundle exec rake appengine:exec GAE_CONFIG=myservice.yaml -- bundle exec bin/rails db:migrate
|
184
|
+
|
185
|
+
Be sure to set these parameters before the double dash. Any arguments
|
186
|
+
following the double dash are interpreted as part of the command itself.
|
187
|
+
|
188
|
+
The following environment variable parameters are supported:
|
189
|
+
|
190
|
+
GAE_CONFIG
|
191
|
+
|
192
|
+
Path to the App Engine config file, used when your app has multiple
|
193
|
+
services, or the config file is otherwise not called `./app.yaml`. The
|
194
|
+
config file is used to determine the name of the App Engine service.
|
195
|
+
|
196
|
+
GAE_SERVICE
|
197
|
+
|
198
|
+
Name of the service to be used. If both `GAE_CONFIG` and `GAE_SERVICE`
|
199
|
+
are provided and imply different service names, an error will be raised.
|
200
|
+
|
201
|
+
GAE_VERSION
|
202
|
+
|
203
|
+
The version of the service, used to identify which application image to
|
204
|
+
use to run your command. If not specified, uses the most recently created
|
205
|
+
version, regardless of whether that version is actually serving traffic.
|
206
|
+
|
207
|
+
GAE_TIMEOUT
|
208
|
+
|
209
|
+
Amount of time to wait before appengine:exec terminates the command.
|
210
|
+
Expressed as a string formatted like: "2h15m10s". Default is "10m".
|
211
|
+
|
212
|
+
This rake task is provided by the "appengine" gem. To make these tasks
|
213
|
+
available, add the following line to your Rakefile:
|
214
|
+
|
215
|
+
require "appengine/tasks"
|
216
|
+
|
217
|
+
If your app uses Ruby on Rails, the gem provides a railtie that adds its
|
218
|
+
tasks automatically, so you don't have to do anything beyond adding the
|
219
|
+
gem to your Gemfile.
|
220
|
+
|
221
|
+
For more information or to report issues, visit the Github page:
|
222
|
+
https://github.com/GoogleCloudPlatform/appengine-ruby
|
223
|
+
USAGE
|
224
|
+
end
|
225
|
+
|
226
|
+
def verify_gcloud_and_report_errors
|
227
|
+
Util::Gcloud.verify!
|
228
|
+
rescue Gcloud::BinaryNotFound
|
229
|
+
report_error <<~MESSAGE
|
230
|
+
Could not find the `gcloud` binary in your system path.
|
231
|
+
This tool requires the Google Cloud SDK. To download and install it,
|
232
|
+
visit https://cloud.google.com/sdk/downloads
|
233
|
+
MESSAGE
|
234
|
+
rescue Gcloud::GcloudNotAuthenticated
|
235
|
+
report_error <<~MESSAGE
|
236
|
+
The gcloud authorization has not been completed. If you have not yet
|
237
|
+
initialized the Google Cloud SDK, we recommend running the `gcloud init`
|
238
|
+
command as described at https://cloud.google.com/sdk/docs/initializing
|
239
|
+
Alternately, you may log in directly by running `gcloud auth login`.
|
240
|
+
MESSAGE
|
241
|
+
rescue Gcloud::ProjectNotSet
|
242
|
+
report_error <<~MESSAGE
|
243
|
+
The gcloud project configuration has not been set. If you have not yet
|
244
|
+
initialized the Google Cloud SDK, we recommend running the `gcloud init`
|
245
|
+
command as described at https://cloud.google.com/sdk/docs/initializing
|
246
|
+
Alternately, you may set the default project configuration directly by
|
247
|
+
running `gcloud config set project <project-name>`.
|
248
|
+
MESSAGE
|
249
|
+
end
|
250
|
+
|
251
|
+
def start_and_report_errors app_exec
|
252
|
+
app_exec.start
|
253
|
+
rescue Exec::ConfigFileNotFound => ex
|
254
|
+
report_error <<~MESSAGE
|
255
|
+
Could not determine which service should run this command because the App
|
256
|
+
Engine config file "#{ex.config_path}" was not found.
|
257
|
+
Specify the config file using the GAE_CONFIG argument. e.g.
|
258
|
+
bundle exec rake appengine:exec GAE_CONFIG=myapp.yaml -- myscript.sh
|
259
|
+
Alternately, you may specify a service name directly with GAE_SERVICE. e.g.
|
260
|
+
bundle exec rake appengine:exec GAE_SERVICE=myservice -- myscript.sh
|
261
|
+
MESSAGE
|
262
|
+
rescue Exec::BadConfigFileFormat => ex
|
263
|
+
report_error <<~MESSAGE
|
264
|
+
Could not determine which service should run this command because the App
|
265
|
+
Engine config file "#{ex.config_path}" was malformed.
|
266
|
+
It must be a valid YAML file.
|
267
|
+
Specify the config file using the GAE_CONFIG argument. e.g.
|
268
|
+
bundle exec rake appengine:exec GAE_CONFIG=myapp.yaml -- myscript.sh
|
269
|
+
Alternately, you may specify a service name directly with GAE_SERVICE. e.g.
|
270
|
+
bundle exec rake appengine:exec GAE_SERVICE=myservice -- myscript.sh
|
271
|
+
MESSAGE
|
272
|
+
rescue Exec::NoSuchVersion => ex
|
273
|
+
if ex.version
|
274
|
+
report_error <<~MESSAGE
|
275
|
+
Could not find version "#{ex.version}" of service "#{ex.service}".
|
276
|
+
Please double-check the version exists. To use the most recent version by
|
277
|
+
default, omit the GAE_VERSION argument.
|
278
|
+
MESSAGE
|
279
|
+
else
|
280
|
+
report_error <<~MESSAGE
|
281
|
+
Could not find any versions of service "#{ex.service}".
|
282
|
+
Please double-check that you have deployed this service. If you want to run
|
283
|
+
a command against a different service, you may provide a GAE_CONFIG argument
|
284
|
+
pointing to your App Engine config file, or a GAE_SERVICE argument to specify
|
285
|
+
a service directly.
|
286
|
+
MESSAGE
|
287
|
+
end
|
288
|
+
rescue Exec::ServiceNameConflict => ex
|
289
|
+
report_error <<~MESSAGE
|
290
|
+
The explicit service name "#{ex.service_name}" was requested
|
291
|
+
but conflicts with the service "#{ex.config_name}" from the
|
292
|
+
config file "#{ex.config_path}"
|
293
|
+
You should specify either GAE_SERVICE or GAE_CONFIG but not both.
|
294
|
+
MESSAGE
|
295
|
+
end
|
296
|
+
|
297
|
+
def report_error str
|
298
|
+
::STDERR.puts str
|
299
|
+
exit 1
|
300
|
+
end
|
301
|
+
|
302
|
+
end
|
303
|
+
|
304
|
+
end
|
305
|
+
end
|
306
|
+
|
307
|
+
|
308
|
+
::AppEngine::Tasks.define
|
@@ -0,0 +1,190 @@
|
|
1
|
+
# Copyright 2017 Google Inc. All rights reserved.
|
2
|
+
#
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
4
|
+
# you may not use this file except in compliance with the License.
|
5
|
+
# You may obtain a copy of the License at
|
6
|
+
#
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
8
|
+
#
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
12
|
+
# See the License for the specific language governing permissions and
|
13
|
+
# limitations under the License.
|
14
|
+
;
|
15
|
+
|
16
|
+
require "shellwords"
|
17
|
+
|
18
|
+
|
19
|
+
module AppEngine
|
20
|
+
module Util
|
21
|
+
|
22
|
+
##
|
23
|
+
# A collection of utility functions and classes for interacting with an
|
24
|
+
# installation of the gcloud SDK.
|
25
|
+
#
|
26
|
+
module Gcloud
|
27
|
+
|
28
|
+
##
|
29
|
+
# Base class for gcloud related errors.
|
30
|
+
#
|
31
|
+
class Error < ::StandardError
|
32
|
+
end
|
33
|
+
|
34
|
+
##
|
35
|
+
# Exception raised when the gcloud binary could not be found.
|
36
|
+
#
|
37
|
+
class BinaryNotFound < Gcloud::Error
|
38
|
+
def initialize
|
39
|
+
super "GCloud binary not found in path"
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
##
|
44
|
+
# Exception raised when the project gcloud config is not set.
|
45
|
+
#
|
46
|
+
class ProjectNotSet < Gcloud::Error
|
47
|
+
def initialize
|
48
|
+
super "GCloud project configuration not set"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
##
|
53
|
+
# Exception raised when gcloud auth has not been completed.
|
54
|
+
#
|
55
|
+
class GcloudNotAuthenticated < Gcloud::Error
|
56
|
+
def initialize
|
57
|
+
super "GCloud not authenticated"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
##
|
62
|
+
# Exception raised when gcloud fails and returns an error.
|
63
|
+
#
|
64
|
+
class GcloudFailed < Gcloud::Error
|
65
|
+
def initialize code
|
66
|
+
super "GCloud failed with result code #{code}"
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class << self
|
71
|
+
|
72
|
+
##
|
73
|
+
# @private
|
74
|
+
# Returns the path to the gcloud binary, or nil if the binary could
|
75
|
+
# not be found.
|
76
|
+
#
|
77
|
+
# @return [String,nil] Path to the gcloud binary.
|
78
|
+
#
|
79
|
+
def binary_path
|
80
|
+
unless defined? @binary_path
|
81
|
+
@binary_path = `which gcloud`.strip
|
82
|
+
@binary_path = nil if @binary_path.empty?
|
83
|
+
end
|
84
|
+
@binary_path
|
85
|
+
end
|
86
|
+
|
87
|
+
##
|
88
|
+
# @private
|
89
|
+
# Returns the path to the gcloud binary. Raises BinaryNotFound if the
|
90
|
+
# binary could not be found.
|
91
|
+
#
|
92
|
+
# @return [String] Path to the gcloud binary.
|
93
|
+
# @raise [BinaryNotFound] The gcloud binary is not present.
|
94
|
+
#
|
95
|
+
def binary_path!
|
96
|
+
value = binary_path
|
97
|
+
raise BinaryNotFound unless value
|
98
|
+
value
|
99
|
+
end
|
100
|
+
|
101
|
+
##
|
102
|
+
# @private
|
103
|
+
# Returns the ID of the current project, or nil if no project has
|
104
|
+
# been set.
|
105
|
+
#
|
106
|
+
# @return [String,nil] ID of the current project.
|
107
|
+
#
|
108
|
+
def current_project
|
109
|
+
unless defined? @current_project
|
110
|
+
@current_project = execute [
|
111
|
+
"config", "list", "core/project", "--format=value(core.project)"
|
112
|
+
], capture: true
|
113
|
+
@current_project = nil if @current_project.empty?
|
114
|
+
end
|
115
|
+
@current_project
|
116
|
+
end
|
117
|
+
|
118
|
+
##
|
119
|
+
# @private
|
120
|
+
# Returns the ID of the current project. Raises ProjectNotSet if no
|
121
|
+
# project has been set in the gcloud configuration.
|
122
|
+
#
|
123
|
+
# @return [String] ID of the current project.
|
124
|
+
# @raise [ProjectNotSet] The project config has not been set.
|
125
|
+
#
|
126
|
+
def current_project!
|
127
|
+
value = current_project
|
128
|
+
raise ProjectNotSet if value.empty?
|
129
|
+
value
|
130
|
+
end
|
131
|
+
|
132
|
+
##
|
133
|
+
# @private
|
134
|
+
# Verifies that all gcloud related dependencies are satisfied.
|
135
|
+
# Specifically, verifies that the gcloud binary is installed and
|
136
|
+
# authenticated, and a project has been set.
|
137
|
+
#
|
138
|
+
# @raise [BinaryNotFound] The gcloud binary is not present.
|
139
|
+
# @raise [ProjectNotSet] The project config has not been set.
|
140
|
+
# @raise [GcloudNotAuthenticated] Gcloud has not been authenticated.
|
141
|
+
#
|
142
|
+
def verify!
|
143
|
+
binary_path!
|
144
|
+
current_project!
|
145
|
+
auths = execute ["auth", "list", "--format=value(account)"],
|
146
|
+
capture: true
|
147
|
+
raise GcloudNotAuthenticated if auths.empty?
|
148
|
+
end
|
149
|
+
|
150
|
+
##
|
151
|
+
# @private
|
152
|
+
# Execute a given gcloud command in a subshell.
|
153
|
+
#
|
154
|
+
# @param args [Array<String>] The gcloud args.
|
155
|
+
# @param echo [boolean] Whether to echo the command to the terminal.
|
156
|
+
# Defaults to false.
|
157
|
+
# @param capture [boolean] If true, return the output. If false, return
|
158
|
+
# a boolean indicating success or failure. Defaults to false.
|
159
|
+
# @param assert [boolean] If true, raise GcloudFailed on failure.
|
160
|
+
# Defaults to true.
|
161
|
+
# @return [String,Integer] Either the output or the success status,
|
162
|
+
# depending on the value of the `capture` parameter.
|
163
|
+
#
|
164
|
+
def execute args, echo: false, capture: false, assert: true
|
165
|
+
joined_args = ::Shellwords.join args
|
166
|
+
cmd = "#{binary_path!} #{joined_args}"
|
167
|
+
puts cmd if echo
|
168
|
+
result = capture ? `#{cmd}` : system(cmd)
|
169
|
+
code = $?.exitstatus
|
170
|
+
raise GcloudFailed.new($?.exitstatus) if assert && code != 0
|
171
|
+
result
|
172
|
+
end
|
173
|
+
|
174
|
+
##
|
175
|
+
# @private
|
176
|
+
# Execute a given gcloud command in a subshell, and return the output
|
177
|
+
# as a string.
|
178
|
+
#
|
179
|
+
# @param args [Array<String>] The gcloud args.
|
180
|
+
# @return [String] The command output.
|
181
|
+
#
|
182
|
+
def capture args
|
183
|
+
execute args, capture: true
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
end
|
190
|
+
end
|
data/lib/appengine/version.rb
CHANGED
data/test/test_env.rb
CHANGED
@@ -22,7 +22,6 @@ module AppEngine
|
|
22
22
|
|
23
23
|
class TestEnv < ::Minitest::Test # :nodoc:
|
24
24
|
|
25
|
-
|
26
25
|
def test_app_engine
|
27
26
|
::ENV["GAE_INSTANCE"] = "instance-123"
|
28
27
|
assert Env.app_engine?
|
@@ -30,7 +29,6 @@ module AppEngine
|
|
30
29
|
refute Env.app_engine?
|
31
30
|
end
|
32
31
|
|
33
|
-
|
34
32
|
end
|
35
33
|
|
36
34
|
end
|
metadata
CHANGED
@@ -1,50 +1,57 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: appengine
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
|
-
- Adam Tanner
|
8
7
|
- Daniel Azuma
|
9
8
|
autorequire:
|
10
9
|
bindir: bin
|
11
10
|
cert_chain: []
|
12
|
-
date: 2017-
|
11
|
+
date: 2017-07-17 00:00:00.000000000 Z
|
13
12
|
dependencies:
|
14
13
|
- !ruby/object:Gem::Dependency
|
15
|
-
name:
|
14
|
+
name: google-cloud-env
|
16
15
|
requirement: !ruby/object:Gem::Requirement
|
17
16
|
requirements:
|
18
17
|
- - "~>"
|
19
18
|
- !ruby/object:Gem::Version
|
20
|
-
version: '0
|
21
|
-
- - ">="
|
22
|
-
- !ruby/object:Gem::Version
|
23
|
-
version: 0.4.1
|
19
|
+
version: '1.0'
|
24
20
|
type: :runtime
|
25
21
|
prerelease: false
|
26
22
|
version_requirements: !ruby/object:Gem::Requirement
|
27
23
|
requirements:
|
28
24
|
- - "~>"
|
29
25
|
- !ruby/object:Gem::Version
|
30
|
-
version: '0
|
31
|
-
|
26
|
+
version: '1.0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: stackdriver
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0.7'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
32
39
|
- !ruby/object:Gem::Version
|
33
|
-
version: 0.
|
40
|
+
version: '0.7'
|
34
41
|
- !ruby/object:Gem::Dependency
|
35
42
|
name: bundler
|
36
43
|
requirement: !ruby/object:Gem::Requirement
|
37
44
|
requirements:
|
38
45
|
- - "~>"
|
39
46
|
- !ruby/object:Gem::Version
|
40
|
-
version: '1.
|
47
|
+
version: '1.15'
|
41
48
|
type: :development
|
42
49
|
prerelease: false
|
43
50
|
version_requirements: !ruby/object:Gem::Requirement
|
44
51
|
requirements:
|
45
52
|
- - "~>"
|
46
53
|
- !ruby/object:Gem::Version
|
47
|
-
version: '1.
|
54
|
+
version: '1.15'
|
48
55
|
- !ruby/object:Gem::Dependency
|
49
56
|
name: minitest
|
50
57
|
requirement: !ruby/object:Gem::Requirement
|
@@ -87,12 +94,27 @@ dependencies:
|
|
87
94
|
- - "~>"
|
88
95
|
- !ruby/object:Gem::Version
|
89
96
|
version: '4.2'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: yard
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.9'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.9'
|
90
111
|
description: The appengine gem is a set of classes, plugins, and tools for integration
|
91
112
|
with Google App Engine. It provides access to the App Engine runtime environment,
|
92
113
|
including logging to the Google Cloud Console and interrogation of hosting properties.
|
93
|
-
|
114
|
+
It also provides rake tasks for managing your App Engine application, for example
|
115
|
+
to run production maintenance commands. This gem is not required to deploy your
|
116
|
+
Ruby application to App Engine.
|
94
117
|
email:
|
95
|
-
- adamtanner@google.com
|
96
118
|
- dazuma@gmail.com
|
97
119
|
executables: []
|
98
120
|
extensions: []
|
@@ -105,7 +127,10 @@ files:
|
|
105
127
|
- Rakefile
|
106
128
|
- lib/appengine.rb
|
107
129
|
- lib/appengine/env.rb
|
130
|
+
- lib/appengine/exec.rb
|
108
131
|
- lib/appengine/railtie.rb
|
132
|
+
- lib/appengine/tasks.rb
|
133
|
+
- lib/appengine/util/gcloud.rb
|
109
134
|
- lib/appengine/version.rb
|
110
135
|
- test/test_env.rb
|
111
136
|
homepage: https://github.com/GoogleCloudPlatform/appengine-ruby
|
@@ -128,7 +153,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
128
153
|
version: '0'
|
129
154
|
requirements: []
|
130
155
|
rubyforge_project:
|
131
|
-
rubygems_version: 2.6.
|
156
|
+
rubygems_version: 2.6.11
|
132
157
|
signing_key:
|
133
158
|
specification_version: 4
|
134
159
|
summary: Google App Engine integration tools
|