seira 0.3.3 → 0.3.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.default-rubocop.yml +216 -61
- data/.rubocop.yml +4 -1
- data/lib/helpers.rb +17 -1
- data/lib/seira.rb +8 -1
- data/lib/seira/app.rb +8 -6
- data/lib/seira/cluster.rb +12 -7
- data/lib/seira/commands.rb +22 -0
- data/lib/seira/commands/gcloud.rb +43 -0
- data/lib/seira/commands/kubectl.rb +34 -0
- data/lib/seira/db.rb +83 -10
- data/lib/seira/db/create.rb +10 -9
- data/lib/seira/jobs.rb +9 -6
- data/lib/seira/node_pools.rb +10 -8
- data/lib/seira/pods.rb +6 -4
- data/lib/seira/proxy.rb +3 -1
- data/lib/seira/secrets.rb +6 -4
- data/lib/seira/settings.rb +5 -1
- data/lib/seira/version.rb +1 -1
- data/seira.gemspec +1 -1
- metadata +7 -4
data/lib/seira/db/create.rb
CHANGED
@@ -1,6 +1,8 @@
|
|
1
1
|
module Seira
|
2
2
|
class Db
|
3
3
|
class Create
|
4
|
+
include Seira::Commands
|
5
|
+
|
4
6
|
attr_reader :app, :action, :args, :context
|
5
7
|
|
6
8
|
attr_reader :name, :version, :cpu, :memory, :storage, :replica_for, :make_highly_available
|
@@ -46,7 +48,7 @@ module Seira
|
|
46
48
|
|
47
49
|
def run_create_command
|
48
50
|
# The 'beta' is needed for HA and other beta features
|
49
|
-
create_command = "
|
51
|
+
create_command = "beta sql instances create #{name}"
|
50
52
|
|
51
53
|
args.each do |arg|
|
52
54
|
if arg.start_with? '--version='
|
@@ -95,10 +97,8 @@ module Seira
|
|
95
97
|
create_command += " --async"
|
96
98
|
end
|
97
99
|
|
98
|
-
puts "Running: #{create_command}"
|
99
|
-
|
100
100
|
# Create the sql instance with the specified/default parameters
|
101
|
-
if
|
101
|
+
if gcloud(create_command, context: context, format: :boolean)
|
102
102
|
async_additional =
|
103
103
|
unless replica_for.nil?
|
104
104
|
". Database is still being created and may take some time to be available."
|
@@ -115,7 +115,7 @@ module Seira
|
|
115
115
|
# Set the root user's password to something secure
|
116
116
|
@root_password = SecureRandom.urlsafe_base64(32)
|
117
117
|
|
118
|
-
if
|
118
|
+
if gcloud("sql users set-password postgres '' --instance=#{name} --password=#{root_password}", context: context, format: :boolean)
|
119
119
|
puts "Set root password to #{root_password}"
|
120
120
|
else
|
121
121
|
puts "Failed to set root password"
|
@@ -127,7 +127,7 @@ module Seira
|
|
127
127
|
# Create proxyuser with secure password
|
128
128
|
@proxyuser_password = SecureRandom.urlsafe_base64(32)
|
129
129
|
|
130
|
-
if
|
130
|
+
if gcloud("sql users create proxyuser '' --instance=#{name} --password=#{proxyuser_password}", context: context, format: :boolean)
|
131
131
|
puts "Created proxyuser with password #{proxyuser_password}"
|
132
132
|
else
|
133
133
|
puts "Failed to create proxyuser"
|
@@ -160,12 +160,13 @@ module Seira
|
|
160
160
|
if replica_for.nil?
|
161
161
|
create_pgbouncer_secret(db_user: 'proxyuser', db_password: proxyuser_password)
|
162
162
|
Secrets.new(app: app, action: 'set', args: ["#{env_name}_ROOT_PASSWORD=#{root_password}"], context: context).run
|
163
|
-
|
163
|
+
# Set DATABASE_URL if not already set
|
164
|
+
write_database_env(key: "DATABASE_URL", db_user: 'proxyuser', db_password: proxyuser_password) if Helpers.get_secret(app: app, key: "DATABASE_URL").nil?
|
164
165
|
write_database_env(key: "#{env_name}_DB_URL", db_user: 'proxyuser', db_password: proxyuser_password)
|
165
166
|
else
|
166
167
|
# When creating a replica, we cannot manage users on the replica. We must manage the users on the primary, which the replica
|
167
168
|
# inherits. For now we will use the same credentials that the primary uses.
|
168
|
-
primary_uri = URI.parse(
|
169
|
+
primary_uri = URI.parse(Helpers.get_secret(app: app, key: 'DATABASE_URL'))
|
169
170
|
primary_user = primary_uri.user
|
170
171
|
primary_password = primary_uri.password
|
171
172
|
create_pgbouncer_secret(db_user: primary_user, db_password: primary_password)
|
@@ -174,7 +175,7 @@ module Seira
|
|
174
175
|
end
|
175
176
|
|
176
177
|
def create_pgbouncer_secret(db_user:, db_password:)
|
177
|
-
|
178
|
+
kubectl("create secret generic #{pgbouncer_secret_name} --from-literal=DB_USER=#{db_user} --from-literal=DB_PASSWORD=#{db_password}", context: context)
|
178
179
|
end
|
179
180
|
|
180
181
|
def write_database_env(key:, db_user:, db_password:)
|
data/lib/seira/jobs.rb
CHANGED
@@ -2,6 +2,8 @@ require 'json'
|
|
2
2
|
|
3
3
|
module Seira
|
4
4
|
class Jobs
|
5
|
+
include Seira::Commands
|
6
|
+
|
5
7
|
VALID_ACTIONS = %w[help list delete run].freeze
|
6
8
|
SUMMARY = "Manage your application's jobs.".freeze
|
7
9
|
|
@@ -39,11 +41,11 @@ module Seira
|
|
39
41
|
end
|
40
42
|
|
41
43
|
def run_list
|
42
|
-
|
44
|
+
kubectl("get jobs -o wide", context: context)
|
43
45
|
end
|
44
46
|
|
45
47
|
def run_delete
|
46
|
-
|
48
|
+
kubectl("delete job #{job_name}", context: context)
|
47
49
|
end
|
48
50
|
|
49
51
|
def run_run
|
@@ -109,8 +111,9 @@ module Seira
|
|
109
111
|
end
|
110
112
|
File.open("#{destination}/#{file_name}", 'w') { |file| file.write(new_contents) }
|
111
113
|
|
112
|
-
|
113
|
-
|
114
|
+
kubectl("apply -f #{destination}", context: context)
|
115
|
+
log_link = Helpers.log_link(context: context, app: app, query: unique_name)
|
116
|
+
puts "View logs at: #{log_link}" unless log_link.nil?
|
114
117
|
end
|
115
118
|
|
116
119
|
unless async
|
@@ -118,7 +121,7 @@ module Seira
|
|
118
121
|
print 'Waiting for job to complete...'
|
119
122
|
job_spec = nil
|
120
123
|
loop do
|
121
|
-
job_spec = JSON.parse(
|
124
|
+
job_spec = JSON.parse(kubectl("get job #{unique_name} -o json", context: context, return_output: true, clean_output: true))
|
122
125
|
break if !job_spec['status']['succeeded'].nil? || !job_spec['status']['failed'].nil?
|
123
126
|
print '.'
|
124
127
|
sleep 3
|
@@ -137,7 +140,7 @@ module Seira
|
|
137
140
|
puts "Job finished with status #{status}. Leaving Job object in cluster, clean up manually when confirmed."
|
138
141
|
else
|
139
142
|
print "Job finished with status #{status}. Deleting Job from cluster for cleanup."
|
140
|
-
|
143
|
+
kubectl("delete job #{unique_name}", context: context)
|
141
144
|
end
|
142
145
|
end
|
143
146
|
end
|
data/lib/seira/node_pools.rb
CHANGED
@@ -5,6 +5,8 @@ require 'fileutils'
|
|
5
5
|
# Example usages:
|
6
6
|
module Seira
|
7
7
|
class NodePools
|
8
|
+
include Seira::Commands
|
9
|
+
|
8
10
|
VALID_ACTIONS = %w[help list list-nodes add cordon drain delete].freeze
|
9
11
|
SUMMARY = "For managing node pools for a cluster.".freeze
|
10
12
|
|
@@ -56,7 +58,7 @@ module Seira
|
|
56
58
|
# TODO: Info about what is running on it?
|
57
59
|
# TODO: What information do we get in the json format we could include here?
|
58
60
|
def run_list
|
59
|
-
|
61
|
+
gcloud("container node-pools list --cluster #{context[:cluster]}", context: context, format: :boolean)
|
60
62
|
end
|
61
63
|
|
62
64
|
def run_list_nodes
|
@@ -89,7 +91,7 @@ module Seira
|
|
89
91
|
end
|
90
92
|
|
91
93
|
command =
|
92
|
-
"
|
94
|
+
"container node-pools create #{new_pool_name} \
|
93
95
|
--cluster=#{context[:cluster]} \
|
94
96
|
--disk-size=#{disk_size} \
|
95
97
|
--image-type=#{image_type} \
|
@@ -97,7 +99,7 @@ module Seira
|
|
97
99
|
--num-nodes=#{num_nodes} \
|
98
100
|
--service-account=#{service_account}"
|
99
101
|
|
100
|
-
if
|
102
|
+
if gcloud(command, conext: context, format: :boolean)
|
101
103
|
puts 'New pool created successfully'
|
102
104
|
else
|
103
105
|
puts 'Failed to create new pool'
|
@@ -112,7 +114,7 @@ module Seira
|
|
112
114
|
nodes = nodes_for_pool(node_pool_name)
|
113
115
|
|
114
116
|
nodes.each do |node|
|
115
|
-
unless
|
117
|
+
unless kubectl("cordon #{node}", context: :none)
|
116
118
|
puts "Failed to cordon node #{node}"
|
117
119
|
exit(1)
|
118
120
|
end
|
@@ -135,7 +137,7 @@ module Seira
|
|
135
137
|
# --delete-local-data prevents failing due to presence of local data, which cannot be moved
|
136
138
|
# but is bad practice to use for anything that can't be lost
|
137
139
|
puts "Draining #{node}"
|
138
|
-
unless
|
140
|
+
unless kubectl("drain --force --ignore-daemonsets --delete-local-data #{node}", context: :none)
|
139
141
|
puts "Failed to drain node #{node}"
|
140
142
|
exit(1)
|
141
143
|
end
|
@@ -155,7 +157,7 @@ module Seira
|
|
155
157
|
|
156
158
|
exit(1) unless HighLine.agree "Node pool has successfully been cordoned and drained, and should be safe to delete. Continue deleting node pool #{node_pool_name}?"
|
157
159
|
|
158
|
-
if
|
160
|
+
if gcloud("container node-pools delete #{node_pool_name} --cluster #{context[:cluster]}", context: context, format: :boolean)
|
159
161
|
puts 'Node pool deleted successfully'
|
160
162
|
else
|
161
163
|
puts 'Failed to delete old pool'
|
@@ -165,11 +167,11 @@ module Seira
|
|
165
167
|
|
166
168
|
# TODO: Represent by a ruby object?
|
167
169
|
def node_pools
|
168
|
-
JSON.parse(
|
170
|
+
JSON.parse(gcloud("container node-pools list --cluster #{context[:cluster]}", context: context, format: :json))
|
169
171
|
end
|
170
172
|
|
171
173
|
def nodes_for_pool(pool_name)
|
172
|
-
|
174
|
+
kubectl("get nodes -l cloud.google.com/gke-nodepool=#{pool_name} -o name", context: :none, return_output: true).split("\n")
|
173
175
|
end
|
174
176
|
|
175
177
|
def fail_if_lone_node_pool
|
data/lib/seira/pods.rb
CHANGED
@@ -2,6 +2,8 @@ require 'json'
|
|
2
2
|
|
3
3
|
module Seira
|
4
4
|
class Pods
|
5
|
+
include Seira::Commands
|
6
|
+
|
5
7
|
VALID_ACTIONS = %w[help list delete logs top run connect].freeze
|
6
8
|
SUMMARY = "Manage your application's pods.".freeze
|
7
9
|
|
@@ -45,19 +47,19 @@ module Seira
|
|
45
47
|
end
|
46
48
|
|
47
49
|
def run_list
|
48
|
-
|
50
|
+
kubectl("get pods -o wide", context: context)
|
49
51
|
end
|
50
52
|
|
51
53
|
def run_delete
|
52
|
-
|
54
|
+
kubectl("delete pod #{pod_name}", context: context)
|
53
55
|
end
|
54
56
|
|
55
57
|
def run_logs
|
56
|
-
|
58
|
+
kubectl("logs #{pod_name} -c #{app}")
|
57
59
|
end
|
58
60
|
|
59
61
|
def run_top
|
60
|
-
|
62
|
+
kubectl("top pod #{pod_name} --containers", context: context)
|
61
63
|
end
|
62
64
|
|
63
65
|
def run_connect
|
data/lib/seira/proxy.rb
CHANGED
@@ -1,5 +1,7 @@
|
|
1
1
|
module Seira
|
2
2
|
class Proxy
|
3
|
+
include Seira::Commands
|
4
|
+
|
3
5
|
SUMMARY = "Open up the proxy UI for a given cluster.".freeze
|
4
6
|
|
5
7
|
def initialize
|
@@ -7,7 +9,7 @@ module Seira
|
|
7
9
|
|
8
10
|
def run
|
9
11
|
begin
|
10
|
-
|
12
|
+
kubectl("proxy", context: :none)
|
11
13
|
rescue
|
12
14
|
end
|
13
15
|
end
|
data/lib/seira/secrets.rb
CHANGED
@@ -8,6 +8,8 @@ require 'base64'
|
|
8
8
|
# TODO: Can we avoid writing to disk completely and instead pipe in raw json?
|
9
9
|
module Seira
|
10
10
|
class Secrets
|
11
|
+
include Seira::Commands
|
12
|
+
|
11
13
|
VALID_ACTIONS = %w[help get set unset list list-decoded].freeze
|
12
14
|
PGBOUNCER_SECRETS_NAME = 'pgbouncer-secrets'.freeze
|
13
15
|
SUMMARY = "Manage your application's secrets and environment variables.".freeze
|
@@ -45,7 +47,7 @@ module Seira
|
|
45
47
|
|
46
48
|
def copy_secret_across_namespace(key:, to:, from:)
|
47
49
|
puts "Copying the #{key} secret from namespace #{from} to #{to}."
|
48
|
-
json_string =
|
50
|
+
json_string = kubectl("get secret #{key} -o json", context: context, return_output: true)
|
49
51
|
secrets = JSON.parse(json_string)
|
50
52
|
|
51
53
|
# At this point we would preferably simply do a write_secrets call, but the metadata is highly coupled to old
|
@@ -145,10 +147,10 @@ module Seira
|
|
145
147
|
end
|
146
148
|
|
147
149
|
# The command we use depends on if it already exists or not
|
148
|
-
secret_exists =
|
150
|
+
secret_exists = kubectl("get secret #{secret_name}", context: context) # TODO: Do not log, pipe output to dev/null
|
149
151
|
command = secret_exists ? "replace" : "create"
|
150
152
|
|
151
|
-
if
|
153
|
+
if kubectl("#{command} -f #{file_name}", context: context)
|
152
154
|
puts "Successfully created/replaced #{secret_name} secret #{key} in cluster #{Seira::Cluster.current_cluster}"
|
153
155
|
else
|
154
156
|
puts "Failed to update secret"
|
@@ -158,7 +160,7 @@ module Seira
|
|
158
160
|
|
159
161
|
# Returns the still-base64encoded secrets hashmap
|
160
162
|
def fetch_current_secrets
|
161
|
-
json_string =
|
163
|
+
json_string = kubectl("get secret #{main_secret_name} -o json", context: context, return_output: true)
|
162
164
|
json = JSON.parse(json_string)
|
163
165
|
fail "Unexpected Kind" unless json['kind'] == 'Secret'
|
164
166
|
json
|
data/lib/seira/settings.rb
CHANGED
@@ -4,7 +4,7 @@ require 'yaml'
|
|
4
4
|
module Seira
|
5
5
|
class Settings
|
6
6
|
DEFAULT_CONFIG_PATH = '.seira.yml'.freeze
|
7
|
-
|
7
|
+
|
8
8
|
attr_reader :config_path
|
9
9
|
|
10
10
|
def initialize(config_path: DEFAULT_CONFIG_PATH)
|
@@ -40,6 +40,10 @@ module Seira
|
|
40
40
|
settings['seira']['clusters']
|
41
41
|
end
|
42
42
|
|
43
|
+
def log_link_format
|
44
|
+
settings['seira']['log_link_format']
|
45
|
+
end
|
46
|
+
|
43
47
|
def full_cluster_name_for_shorthand(shorthand)
|
44
48
|
return shorthand if valid_cluster_names.include?(shorthand)
|
45
49
|
|
data/lib/seira/version.rb
CHANGED
data/seira.gemspec
CHANGED
@@ -26,5 +26,5 @@ Gem::Specification.new do |spec|
|
|
26
26
|
spec.add_development_dependency "bundler", "~> 1.14"
|
27
27
|
spec.add_development_dependency "rake", "~> 10.0"
|
28
28
|
spec.add_development_dependency "rspec", "~> 3.0"
|
29
|
-
spec.add_development_dependency "rubocop", "0.
|
29
|
+
spec.add_development_dependency "rubocop", "0.54.0"
|
30
30
|
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.3.
|
4
|
+
version: 0.3.6
|
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-
|
11
|
+
date: 2018-04-10 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: highline
|
@@ -86,14 +86,14 @@ dependencies:
|
|
86
86
|
requirements:
|
87
87
|
- - '='
|
88
88
|
- !ruby/object:Gem::Version
|
89
|
-
version: 0.
|
89
|
+
version: 0.54.0
|
90
90
|
type: :development
|
91
91
|
prerelease: false
|
92
92
|
version_requirements: !ruby/object:Gem::Requirement
|
93
93
|
requirements:
|
94
94
|
- - '='
|
95
95
|
- !ruby/object:Gem::Version
|
96
|
-
version: 0.
|
96
|
+
version: 0.54.0
|
97
97
|
description: An opinionated library for building applications on Kubernetes.
|
98
98
|
email:
|
99
99
|
- scott@joinhandshake.com
|
@@ -120,6 +120,9 @@ files:
|
|
120
120
|
- lib/seira.rb
|
121
121
|
- lib/seira/app.rb
|
122
122
|
- lib/seira/cluster.rb
|
123
|
+
- lib/seira/commands.rb
|
124
|
+
- lib/seira/commands/gcloud.rb
|
125
|
+
- lib/seira/commands/kubectl.rb
|
123
126
|
- lib/seira/db.rb
|
124
127
|
- lib/seira/db/create.rb
|
125
128
|
- lib/seira/jobs.rb
|