google-cloud-gemserver 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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