seira 0.2.2 → 0.3.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/lib/helpers.rb +5 -0
- data/lib/seira.rb +11 -2
- data/lib/seira/db.rb +93 -1
- data/lib/seira/db/create.rb +3 -4
- data/lib/seira/pods.rb +2 -7
- data/lib/seira/setup.rb +4 -16
- data/lib/seira/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8d89537c56c0c2a5fc49a16238d2b063e55806d7
|
4
|
+
data.tar.gz: 3f4231dd7aa7ea3717d146f49c751068992a957f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 2068e219e27356551e61d964806ab43ec229dc5b6f6b2aee665495c5d27a3006664e08dc0f4640c0058d0d21a134e7c08f24d64e45a95fa95f9b70d3a8cb3bb3
|
7
|
+
data.tar.gz: 95678bb5225c5065bf3a9cfcf67a81950e835aa9d95f2c2cd8cc7ba537d4624466cb28534d476b679d9a4e00665e4991c053a7076eb75dbb4e55a9d8447f8e7c
|
data/lib/helpers.rb
CHANGED
@@ -8,6 +8,11 @@ module Seira
|
|
8
8
|
context[:cluster]
|
9
9
|
end
|
10
10
|
end
|
11
|
+
|
12
|
+
def fetch_pods(filters:, app:)
|
13
|
+
filter_string = { app: app }.merge(filters).map { |k, v| "#{k}=#{v}" }.join(',')
|
14
|
+
JSON.parse(`kubectl get pods --namespace=#{app} -o json --selector=#{filter_string}`)['items']
|
15
|
+
end
|
11
16
|
end
|
12
17
|
end
|
13
18
|
end
|
data/lib/seira.rb
CHANGED
@@ -4,6 +4,7 @@ require 'colorize'
|
|
4
4
|
require 'tmpdir'
|
5
5
|
|
6
6
|
require "seira/version"
|
7
|
+
require 'helpers'
|
7
8
|
require 'seira/app'
|
8
9
|
require 'seira/cluster'
|
9
10
|
require 'seira/memcached'
|
@@ -67,8 +68,16 @@ module Seira
|
|
67
68
|
@args = reversed_args.reverse
|
68
69
|
end
|
69
70
|
|
70
|
-
@cluster =
|
71
|
-
|
71
|
+
@cluster =
|
72
|
+
if category == 'setup' && cluster == 'all'
|
73
|
+
cluster
|
74
|
+
else
|
75
|
+
@settings.full_cluster_name_for_shorthand(cluster)
|
76
|
+
end
|
77
|
+
|
78
|
+
unless category == 'setup' && cluster == 'all'
|
79
|
+
@project = @settings.project_for_cluster(@cluster)
|
80
|
+
end
|
72
81
|
end
|
73
82
|
|
74
83
|
def run
|
data/lib/seira/db.rb
CHANGED
@@ -4,7 +4,7 @@ require_relative 'db/create'
|
|
4
4
|
|
5
5
|
module Seira
|
6
6
|
class Db
|
7
|
-
VALID_ACTIONS = %w[help create delete list restart connect].freeze
|
7
|
+
VALID_ACTIONS = %w[help create delete list restart connect ps kill analyze].freeze
|
8
8
|
SUMMARY = "Manage your Cloud SQL Postgres databases.".freeze
|
9
9
|
|
10
10
|
attr_reader :app, :action, :args, :context
|
@@ -30,6 +30,12 @@ module Seira
|
|
30
30
|
run_restart
|
31
31
|
when 'connect'
|
32
32
|
run_connect
|
33
|
+
when 'ps'
|
34
|
+
run_ps
|
35
|
+
when 'kill'
|
36
|
+
run_kill
|
37
|
+
when 'analyze'
|
38
|
+
run_analyze
|
33
39
|
else
|
34
40
|
fail "Unknown command encountered"
|
35
41
|
end
|
@@ -57,6 +63,9 @@ module Seira
|
|
57
63
|
puts "list: List all postgres instances."
|
58
64
|
puts "restart: Fully restart the database."
|
59
65
|
puts "connect: Open a psql command prompt. You will be shown the password needed before the prompt opens."
|
66
|
+
puts "ps: List running queries"
|
67
|
+
puts "kill: Kill a query"
|
68
|
+
puts "analyze: Display database performance information"
|
60
69
|
end
|
61
70
|
|
62
71
|
def run_create
|
@@ -96,6 +105,89 @@ module Seira
|
|
96
105
|
system("gcloud sql connect #{name}")
|
97
106
|
end
|
98
107
|
|
108
|
+
def run_ps
|
109
|
+
verbose = false
|
110
|
+
args.each do |arg|
|
111
|
+
if %w[--verbose -v].include? arg
|
112
|
+
verbose = true
|
113
|
+
else
|
114
|
+
puts "Warning: unrecognized argument #{arg}"
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
execute_db_command(<<~SQL
|
119
|
+
SELECT
|
120
|
+
pid,
|
121
|
+
state,
|
122
|
+
application_name AS source,
|
123
|
+
age(now(),query_start) AS running_for,
|
124
|
+
query_start,
|
125
|
+
wait_event IS NOT NULL AS waiting,
|
126
|
+
query
|
127
|
+
FROM pg_stat_activity
|
128
|
+
WHERE
|
129
|
+
query <> '<insufficient privilege>'
|
130
|
+
#{verbose ? '' : "AND state <> 'idle'"}
|
131
|
+
AND pid <> pg_backend_pid()
|
132
|
+
ORDER BY query_start DESC
|
133
|
+
SQL
|
134
|
+
)
|
135
|
+
end
|
136
|
+
|
137
|
+
def run_kill
|
138
|
+
force = false
|
139
|
+
pid = nil
|
140
|
+
|
141
|
+
args.each do |arg|
|
142
|
+
if %w[--force -f].include? arg
|
143
|
+
force = true
|
144
|
+
elsif /^\d+$/.match? arg
|
145
|
+
if pid.nil?
|
146
|
+
pid = arg
|
147
|
+
else
|
148
|
+
puts 'Must specify only one PID'
|
149
|
+
exit 1
|
150
|
+
end
|
151
|
+
else
|
152
|
+
puts "Warning: unrecognized argument #{arg}"
|
153
|
+
end
|
154
|
+
end
|
155
|
+
|
156
|
+
execute_db_command("SELECT #{force ? 'pg_terminate_backend' : 'pg_cancel_backend'}(#{pid})")
|
157
|
+
end
|
158
|
+
|
159
|
+
def run_analyze
|
160
|
+
puts 'Cache Hit Rates'.bold
|
161
|
+
execute_db_command(
|
162
|
+
<<~SQL
|
163
|
+
SELECT sum(heap_blks_read) as heap_read, sum(heap_blks_hit) as heap_hit, (sum(heap_blks_hit) - sum(heap_blks_read)) / sum(heap_blks_hit) as ratio
|
164
|
+
FROM pg_statio_user_tables;
|
165
|
+
SQL
|
166
|
+
)
|
167
|
+
|
168
|
+
puts 'Index Usage Rates'.bold
|
169
|
+
execute_db_command(
|
170
|
+
<<~SQL
|
171
|
+
SELECT relname, 100 * idx_scan / (seq_scan + idx_scan) percent_of_times_index_used, n_live_tup rows_in_table
|
172
|
+
FROM pg_stat_user_tables
|
173
|
+
WHERE (seq_scan + idx_scan) > 0
|
174
|
+
ORDER BY n_live_tup DESC;
|
175
|
+
SQL
|
176
|
+
)
|
177
|
+
end
|
178
|
+
|
179
|
+
def execute_db_command(sql_command)
|
180
|
+
# TODO(josh): move pgbouncer naming logic here and in Create to a common location
|
181
|
+
tier = primary_instance.gsub("#{app}-", '')
|
182
|
+
matching_pods = Helpers.fetch_pods(app: app, filters: { tier: tier })
|
183
|
+
if matching_pods.empty?
|
184
|
+
puts 'Could not find pgbouncer pod to connect to'
|
185
|
+
exit 1
|
186
|
+
end
|
187
|
+
pod_name = matching_pods.first['metadata']['name']
|
188
|
+
exit 1 unless system("kubectl exec #{pod_name} --namespace #{app} -- psql -c \"#{sql_command}\"")
|
189
|
+
end
|
190
|
+
|
99
191
|
def existing_instances
|
100
192
|
`gcloud sql instances list --uri`.split("\n").map { |uri| uri.split('/').last }.select { |name| name.start_with? "#{app}-" }.map { |name| name.gsub(/^#{app}-/, '') }
|
101
193
|
end
|
data/lib/seira/db/create.rb
CHANGED
@@ -194,7 +194,7 @@ module Seira
|
|
194
194
|
end
|
195
195
|
|
196
196
|
def pgbouncer_tier
|
197
|
-
name.gsub("
|
197
|
+
name.gsub("#{app}-", "")
|
198
198
|
end
|
199
199
|
|
200
200
|
def default_database_name
|
@@ -203,6 +203,8 @@ module Seira
|
|
203
203
|
|
204
204
|
def write_pgbouncer_yaml
|
205
205
|
# TODO: Clean this up by moving into a proper templated yaml file
|
206
|
+
# TODO: Fix warning from cloudsql-proxy. We use context[:default_zone] for the DB Instance name
|
207
|
+
# which causes this error: got region "us-central1-c", want "us-central1".
|
206
208
|
pgbouncer_yaml = <<-FOO
|
207
209
|
---
|
208
210
|
apiVersion: v1
|
@@ -289,9 +291,6 @@ spec:
|
|
289
291
|
ports:
|
290
292
|
- containerPort: 5432
|
291
293
|
protocol: TCP
|
292
|
-
envFrom:
|
293
|
-
- configMapRef:
|
294
|
-
name: cloudsql-configs
|
295
294
|
volumeMounts:
|
296
295
|
- name: cloudsql-credentials
|
297
296
|
mountPath: /secrets/cloudsql
|
data/lib/seira/pods.rb
CHANGED
@@ -62,7 +62,7 @@ module Seira
|
|
62
62
|
|
63
63
|
def run_connect
|
64
64
|
# If a pod name is specified, connect to that pod; otherwise pick a random web pod
|
65
|
-
target_pod_name = pod_name || fetch_pods(app: app, tier: 'web').sample&.dig('metadata', 'name')
|
65
|
+
target_pod_name = pod_name || Helpers.fetch_pods(app: app, filters: { tier: 'web' }).sample&.dig('metadata', 'name')
|
66
66
|
|
67
67
|
if target_pod_name
|
68
68
|
connect_to_pod(target_pod_name)
|
@@ -104,7 +104,7 @@ module Seira
|
|
104
104
|
command = args.join(' ')
|
105
105
|
|
106
106
|
# Find a 'template' pod from the proper tier
|
107
|
-
template_pod = fetch_pods(app: app, tier: tier).first
|
107
|
+
template_pod = Helpers.fetch_pods(app: app, filters: { tier: tier }).first
|
108
108
|
if template_pod.nil?
|
109
109
|
puts "Unable to find #{tier} tier pod to copy config from"
|
110
110
|
exit(1)
|
@@ -164,11 +164,6 @@ module Seira
|
|
164
164
|
end
|
165
165
|
end
|
166
166
|
|
167
|
-
def fetch_pods(filters)
|
168
|
-
filter_string = filters.map { |k, v| "#{k}=#{v}" }.join(',')
|
169
|
-
JSON.parse(`kubectl get pods --namespace=#{app} -o json --selector=#{filter_string}`)['items']
|
170
|
-
end
|
171
|
-
|
172
167
|
def connect_to_pod(name, command = 'bash')
|
173
168
|
puts "Connecting to #{name}..."
|
174
169
|
system("kubectl exec -ti #{name} --namespace=#{app} -- #{command}")
|
data/lib/seira/setup.rb
CHANGED
@@ -18,7 +18,7 @@ module Seira
|
|
18
18
|
ensure_software_installed
|
19
19
|
|
20
20
|
if arg == 'all'
|
21
|
-
puts "We will now set up gcloud and kubectl for each project. We use a distinct GCP Project for each environment
|
21
|
+
puts "We will now set up gcloud and kubectl for each project. We use a distinct GCP Project for each environment, which are specified in .seira.yml."
|
22
22
|
settings.valid_cluster_names.each do |cluster|
|
23
23
|
setup_cluster(cluster)
|
24
24
|
end
|
@@ -26,7 +26,7 @@ module Seira
|
|
26
26
|
puts "We will now set up gcloud and kubectl for #{arg}"
|
27
27
|
setup_cluster(arg)
|
28
28
|
else
|
29
|
-
puts "Please specify a valid cluster name or 'all'."
|
29
|
+
puts "Please specify a valid cluster name or 'all'. Got #{arg}"
|
30
30
|
exit(1)
|
31
31
|
end
|
32
32
|
|
@@ -51,20 +51,8 @@ module Seira
|
|
51
51
|
end
|
52
52
|
|
53
53
|
system("gcloud config configurations activate #{cluster_name}")
|
54
|
-
|
55
|
-
|
56
|
-
# system("gcloud iam service-accounts create #{iam_user} --display-name=#{iam_user}")
|
57
|
-
# puts "Created service account:"
|
58
|
-
# system("gcloud iam service-accounts describe #{iam_user}@#{cluster_metadata['project']}.iam.gserviceaccount.com")
|
59
|
-
puts "First,"
|
60
|
-
puts "First, set up a service account in the #{cluster_metadata['project']} project and download the credentials for it. You may do so by accessing the below link. Save the file in a safe location."
|
61
|
-
puts "https://console.cloud.google.com/iam-admin/serviceaccounts/project?project=#{cluster_metadata['project']}&organizationId=#{settings.organization_id}"
|
62
|
-
puts "Then, set up an IAM user that it will inherit the permissions for."
|
63
|
-
|
64
|
-
puts "Please enter the path of your JSON key:"
|
65
|
-
filename = STDIN.gets
|
66
|
-
puts "Activating service account..."
|
67
|
-
system("gcloud auth activate-service-account --key-file #{filename}")
|
54
|
+
puts "Authenticating in order to set the auth for project #{cluster_name}. You will be directed to a google login page."
|
55
|
+
system("gcloud auth login")
|
68
56
|
system("gcloud config set project #{cluster_metadata['project']}")
|
69
57
|
system("gcloud config set compute/zone #{settings.default_zone}")
|
70
58
|
puts "Your new gcloud setup for #{cluster_name}:"
|
data/lib/seira/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: seira
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Scott Ringwelski
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2018-02-
|
11
|
+
date: 2018-02-27 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: highline
|