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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: db287b6e4753b077e1b2340e263137e739ac94a8
4
- data.tar.gz: 69b980247f1809ec81f478bf402a14eb6e0e1358
3
+ metadata.gz: 8d89537c56c0c2a5fc49a16238d2b063e55806d7
4
+ data.tar.gz: 3f4231dd7aa7ea3717d146f49c751068992a957f
5
5
  SHA512:
6
- metadata.gz: ac64bd6b4fc973a71c4bc97666ec4b52878cddff5d432ea2af47ef37b6f83a9fdf7a85483b0a78013b8ba9e6f7002c02d0d850496c89061b789585fa0f86dbf9
7
- data.tar.gz: 2422fd823be52e7e6294b9e76564c147ee73a3c043d713c5d0b4943f38c3758f8be8da0957df327a8a9cd7a6f1f015b93d6d0eee841065e30f4c431f07069f6a
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 = @settings.full_cluster_name_for_shorthand(cluster)
71
- @project = @settings.project_for_cluster(@cluster)
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
@@ -194,7 +194,7 @@ module Seira
194
194
  end
195
195
 
196
196
  def pgbouncer_tier
197
- name.gsub("handshake-", "")
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: #{ENVIRONMENTS.join(', ')}"
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
- # TODO: Is this possible to automate?
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
@@ -1,3 +1,3 @@
1
1
  module Seira
2
- VERSION = "0.2.2".freeze
2
+ VERSION = "0.3.0".freeze
3
3
  end
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.2.2
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-02 00:00:00.000000000 Z
11
+ date: 2018-02-27 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: highline