seira 0.3.3 → 0.3.6

Sign up to get free protection for your applications and to get access to all the features.
@@ -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 = "gcloud beta sql instances create #{name}"
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 system(create_command)
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 system("gcloud sql users set-password postgres '' --instance=#{name} --password=#{root_password}")
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 system("gcloud sql users create proxyuser '' --instance=#{name} --password=#{proxyuser_password}")
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
- write_database_env(key: "DATABASE_URL", db_user: 'proxyuser', db_password: proxyuser_password)
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(Secrets.new(app: app, action: 'get', args: [], context: context).get('DATABASE_URL'))
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
- puts `kubectl create secret generic #{pgbouncer_secret_name} --namespace #{app} --from-literal=DB_USER=#{db_user} --from-literal=DB_PASSWORD=#{db_password}`
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
- puts `kubectl get jobs --namespace=#{app} -o wide`
44
+ kubectl("get jobs -o wide", context: context)
43
45
  end
44
46
 
45
47
  def run_delete
46
- puts `kubectl delete job #{job_name} --namespace=#{app}`
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
- puts "Running 'kubectl apply -f #{destination}'"
113
- system("kubectl apply -f #{destination}")
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(`kubectl --namespace=#{app} get job #{unique_name} -o json`)
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
- system("kubectl delete job #{unique_name} -n #{app}")
143
+ kubectl("delete job #{unique_name}", context: context)
141
144
  end
142
145
  end
143
146
  end
@@ -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
- puts `gcloud container node-pools list --cluster #{context[:cluster]}`
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
- "gcloud container node-pools create #{new_pool_name} \
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 system(command)
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 system("kubectl cordon #{node}")
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 system("kubectl drain --force --ignore-daemonsets --delete-local-data #{node}")
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 system("gcloud container node-pools delete #{node_pool_name} --cluster #{context[:cluster]}")
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(`gcloud container node-pools list --cluster #{context[:cluster]} --format json`)
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
- `kubectl get nodes -l cloud.google.com/gke-nodepool=#{pool_name} -o name`.split("\n")
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
- puts `kubectl get pods --namespace=#{app} -o wide`
50
+ kubectl("get pods -o wide", context: context)
49
51
  end
50
52
 
51
53
  def run_delete
52
- puts `kubectl delete pod #{pod_name} --namespace=#{app}`
54
+ kubectl("delete pod #{pod_name}", context: context)
53
55
  end
54
56
 
55
57
  def run_logs
56
- puts `kubectl logs #{pod_name} --namespace=#{app} -c #{app}`
58
+ kubectl("logs #{pod_name} -c #{app}")
57
59
  end
58
60
 
59
61
  def run_top
60
- puts `kubectl top pod #{pod_name} --namespace=#{app} --containers`
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
- system("kubectl proxy")
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 = `kubectl get secret #{key} --namespace #{from} -o json`
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 = system("kubectl get secret #{secret_name} --namespace #{app} > /dev/null")
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 system("kubectl #{command} --namespace #{app} -f #{file_name}")
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 = `kubectl get secret #{main_secret_name} --namespace #{app} -o json`
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
@@ -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
@@ -1,3 +1,3 @@
1
1
  module Seira
2
- VERSION = "0.3.3".freeze
2
+ VERSION = "0.3.6".freeze
3
3
  end
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.51.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.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-03-09 00:00:00.000000000 Z
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.51.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.51.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