appengine 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
|