seira 0.3.3 → 0.3.6
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/.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
|