google-cloud-gemserver 0.1.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.
@@ -0,0 +1,196 @@
1
+ # Copyright 2017 Google Inc.
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
+ # https://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
+ require "google/cloud/gemserver"
16
+ require "securerandom"
17
+ require "google/apis/sqladmin_v1beta4"
18
+ require "googleauth"
19
+ require "yaml"
20
+
21
+ module Google
22
+ module Cloud
23
+ module Gemserver
24
+ class CLI
25
+ ##
26
+ # # CloudSQL
27
+ #
28
+ # CloudSQL manages the creation of a Cloud SQL instance as well as
29
+ # the necessary database and user creation.
30
+ #
31
+ class CloudSQL
32
+ ##
33
+ # Permits SQL admin operations with the Cloud SQL API.
34
+ SCOPES = ["https://www.googleapis.com/auth/sqlservice.admin"]
35
+ .freeze
36
+
37
+ ##
38
+ # An alias for the SqladminV1beta4 module.
39
+ SQL = Google::Apis::SqladminV1beta4
40
+
41
+ ##
42
+ # The name of the database used to store gemserver data.
43
+ # @return [String]
44
+ attr_reader :db
45
+
46
+ ##
47
+ # The name of the default user created to access the database.
48
+ # @return [String]
49
+ attr_reader :user
50
+
51
+ ##
52
+ # The password of the default user created to access the database.
53
+ # @return [String]
54
+ attr_reader :pwd
55
+
56
+ ##
57
+ # The project ID of the project the gemserver will be deployed to.
58
+ # @return [String]
59
+ attr_reader :proj_id
60
+
61
+ ##
62
+ # The name of the Cloud SQL instance.
63
+ # @return [String]
64
+ attr_reader :inst
65
+
66
+ ##
67
+ # The Cloud SQL API used to manage Cloud SQL instances.
68
+ # @return [Google::Apis::SqladminV1beta4::SQLAdminService]
69
+ attr_reader :service
70
+
71
+ ##
72
+ # Creates a CloudSQL object and loads the necessary configuration
73
+ # settings.
74
+ #
75
+ # @param inst [String] Name of the instance to be used. Optional.
76
+ def initialize inst = nil
77
+ auth = Google::Auth.get_application_default SCOPES
78
+ Google::Apis::RequestOptions.default.authorization = auth
79
+ @config = Configuration.new
80
+ @service = SQL::SQLAdminService.new
81
+ @inst = inst || "instance-#{SecureRandom.uuid}".freeze
82
+ @custom = inst ? true : false
83
+ load_config
84
+ end
85
+
86
+ ##
87
+ # Prepares a Cloud SQL instance with a database and user. Also saves
88
+ # the database settings in the appropriate configuration file.
89
+ def run
90
+ create_instance do |instance_op|
91
+ run_sql_task instance_op if instance_op.class == SQL::Operation
92
+ update_root_user
93
+ create_db do |db_op|
94
+ run_sql_task db_op
95
+ create_user
96
+ end
97
+ end
98
+ update_config
99
+ end
100
+
101
+ private
102
+
103
+ ##
104
+ # @private Creates a Cloud SQL instance.
105
+ def create_instance &block
106
+ if @custom
107
+ puts "Using existing Cloud SQL instance: #{@inst}"
108
+ yield
109
+ return instance
110
+ end
111
+ puts "Creating Cloud SQL instance #{@inst} (this takes a few "\
112
+ "minutes to complete)"
113
+ settings = SQL::Settings.new(tier: "db-f1-micro")
114
+ payload = SQL::DatabaseInstance.new(
115
+ name: @inst,
116
+ project: @proj_id,
117
+ settings: settings
118
+ )
119
+ @service.insert_instance(@proj_id, payload, &block)
120
+ end
121
+
122
+ ##
123
+ # @private Creates a database for a Cloud SQL instance.
124
+ def create_db &block
125
+ puts "Creating database #{@db}"
126
+ db = SQL::Database.new name: @db
127
+ @service.insert_database(@proj_id, @inst, db, &block)
128
+ end
129
+
130
+ ##
131
+ # @private Creates a user for a Cloud SQL instance.
132
+ def create_user
133
+ puts "Creating user #{@user}"
134
+ user = SQL::User.new(name: @user, password: @pwd)
135
+ run_sql_task @service.insert_user(@proj_id, @inst, user)
136
+ end
137
+
138
+ ##
139
+ # @private Updates the password of the root user if a new Cloud SQL
140
+ # instance was created.
141
+ def update_root_user
142
+ return if @custom
143
+ cmd = "gcloud sql users set-password root % --password #{@pwd} "\
144
+ "-i #{@inst} --project #{@proj_id}"
145
+ `#{cmd}`
146
+ end
147
+
148
+ ##
149
+ # @private Fetches a Cloud SQL instance.
150
+ #
151
+ # @return [Google::Apis::SqladminV1beta4::DatabaseInstance
152
+ def instance
153
+ @service.get_instance @proj_id, @inst
154
+ end
155
+
156
+ ##
157
+ # Deletes the Cloud SQL instance for a gemserver.
158
+ def del_instance
159
+ puts "Deleting instance: #{@inst}"
160
+ @service.delete_instance @proj_id, @inst
161
+ end
162
+
163
+ ##
164
+ # Sets various Cloud SQL settings used to create a Cloud SQL
165
+ # instance.
166
+ def load_config
167
+ @db = @config[:db_connection_options][:database]
168
+ @user = @config[:db_connection_options][:username]
169
+ @pwd = @config[:db_connection_options][:password]
170
+ @proj_id = @config[:proj_id]
171
+ end
172
+
173
+ ##
174
+ # Saves the Cloud SQL configuration in the appropriate configuration
175
+ # file and app configuration file.
176
+ def update_config
177
+ puts "Updating configurations: app.yaml and config.yml "
178
+ conn_name = instance.connection_name
179
+ @config.update_config "/cloudsql/#{conn_name}",
180
+ :db_connection_options,
181
+ :socket
182
+ @config.update_app conn_name, "beta_settings", "cloud_sql_instances"
183
+ end
184
+
185
+ ##
186
+ # Runs a Cloud SQL task and polls for its completion.
187
+ def run_sql_task op
188
+ while @service.get_operation(@proj_id, op.name).status != "DONE"
189
+ sleep 2
190
+ end
191
+ end
192
+ end
193
+ end
194
+ end
195
+ end
196
+ end
@@ -0,0 +1,143 @@
1
+ # Copyright 2017 Google Inc.
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
+ # https://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
+ require "google/cloud/gemserver"
16
+ require "google/cloud/resource_manager"
17
+ require "securerandom"
18
+
19
+ module Google
20
+ module Cloud
21
+ module Gemserver
22
+ class CLI
23
+ ##
24
+ # # Project
25
+ #
26
+ # Holds a reference to a project on Google Cloud Platform.
27
+ #
28
+ class Project
29
+
30
+ ##
31
+ # The name of the project on Google Cloud platform; same as ID.
32
+ # @return [String]
33
+ attr_accessor :proj_name
34
+
35
+ ##
36
+ # The Configuration object storing the settings used by the Project
37
+ # object.
38
+ # @return [Configuration]
39
+ attr_accessor :config
40
+
41
+ ##
42
+ # Initializes the project name and Configuration object.
43
+ def initialize name = nil
44
+ @proj_name = name
45
+ @config = Configuration.new
46
+ end
47
+
48
+ ##
49
+ # Fetches a reference to the given project on Google Cloud Platform
50
+ # and prompts the user to configure it correctly.
51
+ def create
52
+ raise "Project name was not provided!" unless @proj_name
53
+ begin
54
+ @config.update_config @proj_name, :proj_id
55
+ create_gae_project
56
+ enable_api
57
+ enable_billing
58
+ project
59
+ rescue Google::Cloud::PermissionDeniedError, RuntimeError => e
60
+ puts "Permission denied. You might not be authorized with " \
61
+ "gcloud. Read github.com/GoogleCloudPlatform/google-cloud`." \
62
+ "-ruby/google-cloud-gemserver/docs/authentication.md for " \
63
+ "more information on how to get authenticated."
64
+ puts "If you still get this error the Cloud Resource Manager " \
65
+ "API might not be enabled."
66
+ abort "More details: #{e.message}"
67
+ end
68
+ end
69
+
70
+ private
71
+
72
+ ##
73
+ # @private Checks if the current Google Cloud Platform project
74
+ # contains a Google App Engine project. If not, one is created.
75
+ def create_gae_project
76
+ return if project_exists?
77
+ puts "Required Google App Engine project does not exist."
78
+ system "gcloud app create --project #{@proj_name}"
79
+ end
80
+
81
+ ##
82
+ # @private Checks if a Google App Engine project exists.
83
+ #
84
+ # @return [Boolean]
85
+ def project_exists?
86
+ system "gcloud app describe --project #{@proj_name} >/dev/null 2>&1"
87
+ end
88
+
89
+ ##
90
+ # @private Prompts the user to press enter.
91
+ #
92
+ # @return [String]
93
+ def prompt_user
94
+ puts "\nPress enter to continue..."
95
+ STDIN.gets
96
+ end
97
+
98
+ ##
99
+ # @private Fetches a given project on Google Cloud Platform.
100
+ #
101
+ # @return [Google::Cloud::ResourceManager::Project]
102
+ def project
103
+ resource_manager = Google::Cloud::ResourceManager.new
104
+ resource_manager.project @proj_name
105
+ end
106
+
107
+ ##
108
+ # @private Prompts the user to enable necessary APIs for the
109
+ # gemserver to work as intended.
110
+ #
111
+ # @return [String]
112
+ def enable_api
113
+ puts "\nEnable the Google Cloud SQL API if it is not already "\
114
+ "enabled by visiting:\n https://console.developers.google.com"\
115
+ "/apis/api/sqladmin.googleapis.com/overview?"\
116
+ "project=#{@proj_name} and clicking \"Enable\""
117
+ puts "\nEnable the Google Cloud Resource manager API if it is not"\
118
+ "already enabled by visiting:\nhttps://console.developers.google"\
119
+ ".com/apis/api/cloudresourcemanager.googleapis.com/overview?"\
120
+ "project=#{@proj_name} and clicking \"Enable\""
121
+ puts "\nEnable the Google App Engine Admin API if it is not "\
122
+ "already enabled by visiting:\nhttps://console.developers.google"\
123
+ ".com/apis/api/appengine.googleapis.com/overview?"\
124
+ "project=#{@proj_name} and clicking \"Enable\""
125
+ prompt_user
126
+ end
127
+
128
+ ##
129
+ # @private Prompts the user to enable billing such that the gemserver
130
+ # work as intended.
131
+ #
132
+ # @return [String]
133
+ def enable_billing
134
+ puts "\nEnable billing for the project you just created by "\
135
+ "visiting: \nconsole.cloud.google.com/billing?project="\
136
+ "#{@proj_name}\nand setting up a billing account."
137
+ prompt_user
138
+ end
139
+ end
140
+ end
141
+ end
142
+ end
143
+ end
@@ -0,0 +1,130 @@
1
+ # Copyright 2017 Google Inc.
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
+ # @https://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
+ require "google/cloud/gemserver"
16
+ require "net/http"
17
+ require "yaml"
18
+
19
+ module Google
20
+ module Cloud
21
+ module Gemserver
22
+ class CLI
23
+ ##
24
+ #
25
+ # # Request
26
+ #
27
+ # Responsible for sending requests to the gemserver for operations that
28
+ # involve the database. Gem operations are done with the 'gem' command
29
+ # and are not in the scope of Request.
30
+ class Request
31
+
32
+ ##
33
+ # The HTTP object used to connect to and send requests to the
34
+ # gemserver.
35
+ # @return [Net::HTTP]
36
+ attr_accessor :http
37
+
38
+ ##
39
+ # Initialize the Backend object by constructing an HTTP object for the
40
+ # gemserver.
41
+ #
42
+ # @param [String] url The URL of the gemserver. Optional.
43
+ #
44
+ # @param [String] proj_name The name of the Google Cloud Platform the
45
+ # gemserver was deployed to. Optional.
46
+ def initialize url = nil, proj_name = nil
47
+ gemserver_url = url.nil? == true ? remote(proj_name) : url
48
+ @http = Net::HTTP.new gemserver_url
49
+ end
50
+
51
+ ##
52
+ # Send a request to the gemserver to create a key with certain
53
+ # permissions.
54
+ #
55
+ # @param [String] permissions The permissions the generated key will
56
+ # have (read, write, or both). Optional.
57
+ #
58
+ # @return [Net::HTTPResponse]
59
+ def create_key permissions = nil
60
+ send_req Net::HTTP::Post, "/api/v1/key", {permissions: permissions}
61
+ end
62
+
63
+ ##
64
+ # Send a request to the gemserver to delete a key.
65
+ #
66
+ # @param [String] key The key to delete.
67
+ #
68
+ # @return [Net::HTTPResponse]
69
+ def delete_key key
70
+ send_req Net::HTTP::Put, "/api/v1/key", {key: key}
71
+ end
72
+
73
+ ##
74
+ # Send a request to the gemserver to fetch information about stored
75
+ # private gems and cached gem dependencies.
76
+ #
77
+ # @return [Net::HTTPResponse]
78
+ def stats
79
+ send_req Net::HTTP::Post, "/api/v1/stats"
80
+ end
81
+
82
+ ##
83
+ # Sends a request to the gemserver to ensure it is accessible.
84
+ #
85
+ # @return [Net::HTTPResponse]
86
+ def health
87
+ send_req Net::HTTP::Get, "/health"
88
+ end
89
+
90
+ private
91
+
92
+ ##
93
+ # @private The URL of the gemserver.
94
+ #
95
+ # @param [String] proj_name The Google Cloud Platform project the
96
+ # gemserver was deployed to.
97
+ #
98
+ # @return [String]
99
+ def remote proj_name
100
+ descrip = YAML.load(`gcloud app describe --project #{proj_name}`)
101
+ descrip["defaultHostname"]
102
+ end
103
+
104
+ ##
105
+ # @private Makes a request to the gemserver and returns the response.
106
+ #
107
+ # @param [Net::HTTP] type The type of HTTP request.
108
+ #
109
+ # @param [String] endpoint The endpoint the request is made to on the
110
+ # gemserver.
111
+ #
112
+ # @param [Object] params The data passed to the gemserver to be
113
+ # processed. Optional.
114
+ #
115
+ # @return [String]
116
+ def send_req type, endpoint, params = nil
117
+ auth = Google::Cloud::Gemserver::Authentication.new
118
+ t = auth.access_token["access_token"]
119
+ req = type.new endpoint
120
+ req["Authorization"] = Signet::OAuth2.generate_bearer_authorization_header t
121
+ if type != Net::HTTP::Get
122
+ req.set_form_data(params) if params
123
+ end
124
+ @http.request req
125
+ end
126
+ end
127
+ end
128
+ end
129
+ end
130
+ end