hippo-cli 1.1.2 → 1.2.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
- checksums.yaml.gz.sig +0 -0
- data/bin/hippo +1 -1
- data/cli/apply_config.rb +0 -4
- data/cli/apply_services.rb +0 -4
- data/cli/console.rb +0 -4
- data/cli/create.rb +9 -13
- data/cli/deploy.rb +0 -4
- data/cli/install.rb +0 -4
- data/cli/key.rb +1 -4
- data/cli/kubectl.rb +0 -4
- data/cli/logs.rb +0 -4
- data/cli/objects.rb +0 -4
- data/cli/package_install.rb +0 -4
- data/cli/package_list.rb +0 -4
- data/cli/package_notes.rb +0 -4
- data/cli/package_test.rb +0 -4
- data/cli/package_uninstall.rb +0 -4
- data/cli/package_upgrade.rb +0 -4
- data/cli/package_values.rb +0 -4
- data/cli/prepare.rb +0 -4
- data/cli/readme.rb +18 -0
- data/cli/run.rb +0 -4
- data/cli/secrets.rb +0 -4
- data/cli/setup.rb +71 -0
- data/cli/stages.rb +4 -7
- data/cli/status.rb +0 -4
- data/cli/tidy.rb +33 -0
- data/cli/update.rb +22 -0
- data/cli/vars.rb +0 -4
- data/lib/hippo/cli.rb +31 -27
- data/lib/hippo/image.rb +1 -0
- data/lib/hippo/manifest.rb +13 -17
- data/lib/hippo/repository_tag.rb +3 -2
- data/lib/hippo/secret_manager.rb +20 -19
- data/lib/hippo/stage.rb +75 -39
- data/lib/hippo/util.rb +61 -1
- data/lib/hippo/version.rb +1 -1
- data/lib/hippo/working_directory.rb +180 -0
- data/lib/hippo.rb +7 -0
- data/template/Hippofile +1 -2
- data.tar.gz.sig +0 -0
- metadata +7 -2
- metadata.gz.sig +0 -0
data/lib/hippo/cli.rb
CHANGED
@@ -3,28 +3,32 @@
|
|
3
3
|
require 'securerandom'
|
4
4
|
require 'hippo/manifest'
|
5
5
|
require 'hippo/deployment_monitor'
|
6
|
+
require 'hippo/working_directory'
|
6
7
|
|
7
8
|
module Hippo
|
8
9
|
class CLI
|
9
|
-
attr_reader :
|
10
|
+
attr_reader :wd
|
10
11
|
attr_reader :stage
|
11
12
|
|
12
13
|
# Initialize a new CLI instance
|
13
14
|
#
|
14
|
-
# @param
|
15
|
-
# @param stage [Hippo::Stage]
|
15
|
+
# @param wd [Hippo::WorkingDirectory]
|
16
16
|
# @return [Hippo::CLI]
|
17
|
-
def initialize(
|
18
|
-
@
|
17
|
+
def initialize(wd, stage)
|
18
|
+
@wd = wd
|
19
19
|
@stage = stage
|
20
20
|
end
|
21
21
|
|
22
|
+
def manifest
|
23
|
+
wd.manifest
|
24
|
+
end
|
25
|
+
|
22
26
|
# Verify image existence
|
23
27
|
#
|
24
28
|
# @return [void]
|
25
29
|
def verify_image_existence
|
26
30
|
missing = 0
|
27
|
-
|
31
|
+
stage.images.each do |_, image|
|
28
32
|
if image.exists?
|
29
33
|
puts "Image for #{image.name} exists at #{image.image_url}"
|
30
34
|
else
|
@@ -43,7 +47,7 @@ module Hippo
|
|
43
47
|
#
|
44
48
|
# @return [void]
|
45
49
|
def preflight
|
46
|
-
if
|
50
|
+
if stage.context.nil?
|
47
51
|
puts "\e[33mStage does not specify a context. The current context specified"
|
48
52
|
puts "by the kubectl config will be used (#{Hippo.current_kubectl_context}).\e[0m"
|
49
53
|
puts
|
@@ -61,9 +65,9 @@ module Hippo
|
|
61
65
|
{
|
62
66
|
'kind' => 'Namespace',
|
63
67
|
'apiVersion' => 'v1',
|
64
|
-
'metadata' => { 'name' =>
|
68
|
+
'metadata' => { 'name' => stage.namespace, 'labels' => { 'name' => stage.namespace } }
|
65
69
|
},
|
66
|
-
|
70
|
+
stage
|
67
71
|
)
|
68
72
|
apply([od], 'namespace')
|
69
73
|
end
|
@@ -72,19 +76,19 @@ module Hippo
|
|
72
76
|
#
|
73
77
|
# @return [void]
|
74
78
|
def apply_config
|
75
|
-
apply(
|
79
|
+
apply(stage.configs, 'configuration')
|
76
80
|
end
|
77
81
|
|
78
82
|
# Install all packages
|
79
83
|
#
|
80
84
|
# @return [void]
|
81
85
|
def install_all_packages
|
82
|
-
if
|
86
|
+
if stage.packages.empty?
|
83
87
|
puts 'There are no packages to install'
|
84
88
|
return
|
85
89
|
end
|
86
90
|
|
87
|
-
|
91
|
+
stage.packages.values.each do |package|
|
88
92
|
if package.installed?
|
89
93
|
puts "#{package.name} is already installed. Upgrading..."
|
90
94
|
package.upgrade
|
@@ -94,14 +98,14 @@ module Hippo
|
|
94
98
|
end
|
95
99
|
end
|
96
100
|
|
97
|
-
puts "Finished with #{
|
101
|
+
puts "Finished with #{stage.packages.size} #{stage.packages.size == 1 ? 'package' : 'packages'}"
|
98
102
|
end
|
99
103
|
|
100
104
|
# Apply all services, ingresses and policies
|
101
105
|
#
|
102
106
|
# @return [void]
|
103
107
|
def apply_services
|
104
|
-
apply(
|
108
|
+
apply(stage.services, 'service')
|
105
109
|
end
|
106
110
|
|
107
111
|
# Run all deploy jobs
|
@@ -123,7 +127,7 @@ module Hippo
|
|
123
127
|
# @return [void]
|
124
128
|
def deploy
|
125
129
|
deployment_id = SecureRandom.hex(6)
|
126
|
-
deployments =
|
130
|
+
deployments = stage.deployments
|
127
131
|
if deployments.empty?
|
128
132
|
puts 'There are no deployment objects defined'
|
129
133
|
return true
|
@@ -138,7 +142,7 @@ module Hippo
|
|
138
142
|
apply(deployments, 'deployment')
|
139
143
|
puts 'Waiting for all deployments to roll out...'
|
140
144
|
|
141
|
-
monitor = DeploymentMonitor.new(
|
145
|
+
monitor = DeploymentMonitor.new(stage, deployment_id)
|
142
146
|
monitor.on_success do |poll|
|
143
147
|
if poll.replica_sets.size == 1
|
144
148
|
puts "\e[32mDeployment rolled out successfully\e[0m"
|
@@ -158,8 +162,8 @@ module Hippo
|
|
158
162
|
poll.pending.each do |rs|
|
159
163
|
puts
|
160
164
|
name = rs.name.split('-').first
|
161
|
-
puts " hippo #{
|
162
|
-
puts " hippo #{
|
165
|
+
puts " hippo #{stage.name} kubectl -- describe deployment \e[35m#{name}\e[0m"
|
166
|
+
puts " hippo #{stage.name} kubectl -- logs deployment/\e[35m#{name}\e[0m --all-containers"
|
163
167
|
end
|
164
168
|
puts
|
165
169
|
end
|
@@ -174,25 +178,25 @@ module Hippo
|
|
174
178
|
puts "No #{type} objects found to apply"
|
175
179
|
else
|
176
180
|
puts "Applying #{objects.size} #{type} #{objects.size == 1 ? 'object' : 'objects'}"
|
177
|
-
|
181
|
+
stage.apply(objects)
|
178
182
|
end
|
179
183
|
end
|
180
184
|
|
181
185
|
def run_jobs(type)
|
182
186
|
puts "Running #{type} jobs"
|
183
|
-
jobs =
|
187
|
+
jobs = stage.jobs(type)
|
184
188
|
if jobs.empty?
|
185
189
|
puts "There are no #{type} jobs to run"
|
186
190
|
return true
|
187
191
|
end
|
188
192
|
|
189
193
|
jobs.each do |job|
|
190
|
-
|
194
|
+
stage.delete('job', job.name)
|
191
195
|
end
|
192
196
|
|
193
197
|
applied_jobs = apply(jobs, 'deploy job')
|
194
198
|
|
195
|
-
timeout, jobs =
|
199
|
+
timeout, jobs = stage.wait_for_jobs(applied_jobs.keys)
|
196
200
|
success_jobs = []
|
197
201
|
failed_jobs = []
|
198
202
|
jobs.each do |job|
|
@@ -221,22 +225,22 @@ module Hippo
|
|
221
225
|
else
|
222
226
|
'❌'
|
223
227
|
end
|
224
|
-
puts " #{icon} hippo #{
|
228
|
+
puts " #{icon} hippo #{stage.name} kubectl -- logs job/#{job.name}"
|
225
229
|
end
|
226
230
|
puts
|
227
231
|
result
|
228
232
|
end
|
229
233
|
|
230
234
|
class << self
|
231
|
-
def setup(
|
232
|
-
|
235
|
+
def setup(_context)
|
236
|
+
wd = Hippo::WorkingDirectory.new
|
233
237
|
|
234
|
-
stage =
|
238
|
+
stage = wd.stages[CURRENT_STAGE]
|
235
239
|
if stage.nil?
|
236
240
|
raise Error, "Invalid stage name `#{CURRENT_STAGE}`. Check this has been defined in in your stages directory with a matching name?"
|
237
241
|
end
|
238
242
|
|
239
|
-
new(
|
243
|
+
new(wd, stage)
|
240
244
|
end
|
241
245
|
end
|
242
246
|
end
|
data/lib/hippo/image.rb
CHANGED
data/lib/hippo/manifest.rb
CHANGED
@@ -51,6 +51,18 @@ module Hippo
|
|
51
51
|
end
|
52
52
|
end
|
53
53
|
|
54
|
+
def readme
|
55
|
+
return @readme if instance_variable_defined?('@readme')
|
56
|
+
|
57
|
+
path = File.join(@root, 'readme.txt')
|
58
|
+
unless File.file?(path)
|
59
|
+
@readme = nil
|
60
|
+
return
|
61
|
+
end
|
62
|
+
|
63
|
+
@readme = File.read(path)
|
64
|
+
end
|
65
|
+
|
54
66
|
def template_vars
|
55
67
|
{
|
56
68
|
'name' => name
|
@@ -63,29 +75,13 @@ module Hippo
|
|
63
75
|
@options['images']
|
64
76
|
end
|
65
77
|
|
66
|
-
# Load all stages that are available in the manifest
|
67
|
-
#
|
68
|
-
# @return [Hash<Symbol, Hippo::Stage>]
|
69
|
-
def stages
|
70
|
-
objects('stages').each_with_object({}) do |(_, objects), hash|
|
71
|
-
objects.each do |obj|
|
72
|
-
stage = Stage.new(self, obj)
|
73
|
-
hash[stage.name] = stage
|
74
|
-
end
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
78
|
# Load all YAML objects at a given path and return them.
|
79
79
|
#
|
80
80
|
# @param path [String]
|
81
81
|
# @param decorator [Proc] an optional parser to run across the raw YAML file
|
82
82
|
# @return [Array<Hash>]
|
83
83
|
def objects(path, decorator: nil)
|
84
|
-
|
85
|
-
files.each_with_object({}) do |path, objects|
|
86
|
-
file = Util.load_yaml_from_file(path, decorator: decorator)
|
87
|
-
objects[path.sub(%r{\A#{@root}/}, '')] = file
|
88
|
-
end
|
84
|
+
Util.load_objects_from_path(File.join(@root, path, '*.{yaml,yml}'), decorator: decorator)
|
89
85
|
end
|
90
86
|
end
|
91
87
|
end
|
data/lib/hippo/repository_tag.rb
CHANGED
@@ -30,8 +30,9 @@ module Hippo
|
|
30
30
|
return nil if @options['url'].nil?
|
31
31
|
|
32
32
|
@remote_refs ||= begin
|
33
|
-
|
34
|
-
|
33
|
+
Util.action "Getting remote refs from #{@options['url']}..." do
|
34
|
+
Git.ls_remote(@options['url'])
|
35
|
+
end
|
35
36
|
end
|
36
37
|
end
|
37
38
|
end
|
data/lib/hippo/secret_manager.rb
CHANGED
@@ -15,17 +15,7 @@ module Hippo
|
|
15
15
|
CIPHER = OpenSSL::Cipher.new('aes-256-gcm')
|
16
16
|
|
17
17
|
def path
|
18
|
-
File.join(@stage.
|
19
|
-
end
|
20
|
-
|
21
|
-
def secret(name)
|
22
|
-
Secret.new(self, name)
|
23
|
-
end
|
24
|
-
|
25
|
-
def secrets
|
26
|
-
Dir[File.join(root, '*.{yml,yaml}')].map do |path|
|
27
|
-
secret(path.split('/').last.sub(/\.ya?ml\z/, ''))
|
28
|
-
end
|
18
|
+
File.join(@stage.config_root, 'secrets.yaml')
|
29
19
|
end
|
30
20
|
|
31
21
|
# Download the current key from the Kubernetes API and set it as the
|
@@ -35,13 +25,24 @@ module Hippo
|
|
35
25
|
def download_key
|
36
26
|
return if @key
|
37
27
|
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
28
|
+
Util.action 'Downloading secret encryiption key...' do |state|
|
29
|
+
begin
|
30
|
+
value = @stage.get('secret', 'hippo-secret-key').first
|
31
|
+
|
32
|
+
if value.nil? || value.dig('data', 'key').nil?
|
33
|
+
state.call('not found')
|
34
|
+
return
|
35
|
+
end
|
36
|
+
|
37
|
+
@key = Base64.decode64(Base64.decode64(value['data']['key']))
|
38
|
+
rescue Hippo::Error => e
|
39
|
+
if e.message =~ /not found/
|
40
|
+
state.call('not found')
|
41
|
+
else
|
42
|
+
raise
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
45
46
|
end
|
46
47
|
|
47
48
|
# Is there a key availale in this manager?
|
@@ -118,7 +119,7 @@ module Hippo
|
|
118
119
|
|
119
120
|
return if exists?
|
120
121
|
|
121
|
-
yaml = { 'example' => 'This is an example
|
122
|
+
yaml = { 'example' => 'This is an example secret2!' }.to_yaml
|
122
123
|
FileUtils.mkdir_p(File.dirname(path))
|
123
124
|
File.open(path, 'w') { |f| f.write(encrypt(yaml)) }
|
124
125
|
end
|
data/lib/hippo/stage.rb
CHANGED
@@ -8,13 +8,19 @@ require 'hippo/liquid_filters'
|
|
8
8
|
|
9
9
|
module Hippo
|
10
10
|
class Stage
|
11
|
-
attr_reader :
|
11
|
+
attr_reader :wd
|
12
|
+
attr_reader :config_root
|
12
13
|
|
13
|
-
def initialize(
|
14
|
-
@
|
14
|
+
def initialize(wd, config_root, options)
|
15
|
+
@wd = wd
|
16
|
+
@config_root = config_root
|
15
17
|
@options = options
|
16
18
|
end
|
17
19
|
|
20
|
+
def manifest
|
21
|
+
wd.manifest
|
22
|
+
end
|
23
|
+
|
18
24
|
def name
|
19
25
|
@options['name']
|
20
26
|
end
|
@@ -40,7 +46,7 @@ module Hippo
|
|
40
46
|
end
|
41
47
|
|
42
48
|
def images
|
43
|
-
@images ||=
|
49
|
+
@images ||= manifest.images.deep_merge(@options['images'] || {}).each_with_object({}) do |(key, image), hash|
|
44
50
|
hash[key] = Image.new(key, image)
|
45
51
|
end
|
46
52
|
end
|
@@ -49,14 +55,14 @@ module Hippo
|
|
49
55
|
def template_vars
|
50
56
|
@template_vars ||= begin
|
51
57
|
{
|
52
|
-
'manifest' =>
|
58
|
+
'manifest' => manifest.template_vars,
|
53
59
|
'stage-name' => name,
|
54
60
|
'branch' => branch,
|
55
61
|
'image-tag' => image_tag,
|
56
62
|
'namespace' => namespace,
|
57
63
|
'context' => context,
|
58
64
|
'images' => images.values.each_with_object({}) { |image, hash| hash[image.name] = image.template_vars },
|
59
|
-
'config' =>
|
65
|
+
'config' => manifest.config.deep_merge(config),
|
60
66
|
'secrets' => secret_manager.all
|
61
67
|
}
|
62
68
|
end
|
@@ -75,8 +81,44 @@ module Hippo
|
|
75
81
|
end
|
76
82
|
end
|
77
83
|
|
84
|
+
def readme
|
85
|
+
return unless manifest.readme
|
86
|
+
|
87
|
+
decorator.call(manifest.readme)
|
88
|
+
end
|
89
|
+
|
90
|
+
# Return an array of objects that currently exist on the kubernetesa
|
91
|
+
# API.
|
92
|
+
#
|
93
|
+
# @return [Array<Hash>]
|
94
|
+
def live_objects(pruneable_only: false)
|
95
|
+
los = get(all_objects.keys.join(','), '--selector', 'app.kubernetes.io/managed-by=hippo')
|
96
|
+
los.each_with_object([]) do |live_obj, array|
|
97
|
+
local = all_objects.dig(live_obj.kind, live_obj.name)
|
98
|
+
pruneable = local.nil? && (live_obj.kind != 'Secret' && live_obj.name != 'hippo-secret-key')
|
99
|
+
|
100
|
+
next if pruneable_only && !pruneable
|
101
|
+
|
102
|
+
array << {
|
103
|
+
live: live_obj,
|
104
|
+
local: local,
|
105
|
+
pruneable: pruneable
|
106
|
+
}
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
# Remove any objects which are prunable
|
111
|
+
#
|
112
|
+
# @return [void]
|
113
|
+
def delete_pruneable_objects
|
114
|
+
live_objects(pruneable_only: true).each do |object|
|
115
|
+
object = object[:live]
|
116
|
+
delete(object.kind, object.name)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
78
120
|
def objects(path)
|
79
|
-
|
121
|
+
manifest.objects(path, decorator: decorator)
|
80
122
|
end
|
81
123
|
|
82
124
|
def secret_manager
|
@@ -112,6 +154,19 @@ module Hippo
|
|
112
154
|
@jobs[type] ||= Util.create_object_definitions(objects("jobs/#{type}"), self)
|
113
155
|
end
|
114
156
|
|
157
|
+
# Return an array of all objects that should be managed by Hippo
|
158
|
+
#
|
159
|
+
# @return [Hash]
|
160
|
+
def all_objects
|
161
|
+
@all_objects ||= begin
|
162
|
+
all = (deployments | services | configs | jobs('install') | jobs('deploy'))
|
163
|
+
all.each_with_object({}) do |object, hash|
|
164
|
+
hash[object.kind] ||= {}
|
165
|
+
hash[object.kind][object.name] = object
|
166
|
+
end
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
115
170
|
# Return a hash of all packages available in the stage
|
116
171
|
#
|
117
172
|
# @return [Hash<String, Hippo::Package>]
|
@@ -145,33 +200,17 @@ module Hippo
|
|
145
200
|
# @param objects [Array<Hippo::ObjectDefinition>]
|
146
201
|
# @return [Hash]
|
147
202
|
def apply(objects)
|
148
|
-
yaml_to_apply = objects.map(&:yaml_to_apply).join("\n")
|
149
|
-
|
150
203
|
command = ['kubectl']
|
151
204
|
command += ['--context', context] if context
|
152
205
|
command += ['apply', '-f', '-']
|
153
|
-
Open3.popen3(command.join(' ')) do |stdin, stdout, stderr, wt|
|
154
|
-
stdin.puts yaml_to_apply
|
155
|
-
stdin.close
|
156
206
|
|
157
|
-
|
158
|
-
stderr = stderr.read.strip
|
207
|
+
yaml_to_apply = objects.map(&:yaml_to_apply).join("\n")
|
159
208
|
|
160
|
-
|
161
|
-
stdout.split("\n").each_with_object({}) do |line, hash|
|
162
|
-
next unless line =~ %r{\A([\w\/\-\.]+) (\w+)\z}
|
209
|
+
stdout, stderr, status = Open3.capture3(command.join(' '), stdin_data: yaml_to_apply + "\n")
|
163
210
|
|
164
|
-
|
165
|
-
status = Regexp.last_match(2)
|
166
|
-
hash[object] = status
|
211
|
+
raise Error, "[kubectl] #{stderr}" unless status.success?
|
167
212
|
|
168
|
-
|
169
|
-
puts "\e[37m====> #{object} #{status}\e[0m"
|
170
|
-
end
|
171
|
-
else
|
172
|
-
raise Error, "[kubectl] #{stderr}"
|
173
|
-
end
|
174
|
-
end
|
213
|
+
Util.parse_kubectl_apply_lines(stdout)
|
175
214
|
end
|
176
215
|
|
177
216
|
# Get some data from the kubernetes API
|
@@ -194,19 +233,16 @@ module Hippo
|
|
194
233
|
# @return [Boolean]
|
195
234
|
def delete(*names)
|
196
235
|
command = kubectl('delete', *names)
|
197
|
-
Open3.
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
236
|
+
stdout, stderr, status = Open3.capture3(*command)
|
237
|
+
if status.success?
|
238
|
+
Util.parse_kubectl_apply_lines(stdout)
|
239
|
+
true
|
240
|
+
else
|
241
|
+
stderr = stderr.read
|
242
|
+
if stderr =~ /\" not found$/
|
243
|
+
false
|
203
244
|
else
|
204
|
-
|
205
|
-
if stderr =~ /\" not found$/
|
206
|
-
false
|
207
|
-
else
|
208
|
-
raise Error, "[kutectl] #{stderr}"
|
209
|
-
end
|
245
|
+
raise Error, "[kutectl] #{stderr}"
|
210
246
|
end
|
211
247
|
end
|
212
248
|
end
|
data/lib/hippo/util.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
# frozen_string_literal: true
|
2
2
|
|
3
3
|
require 'yaml'
|
4
|
+
require 'open3'
|
4
5
|
require 'hippo/error'
|
5
6
|
require 'hippo/object_definition'
|
6
7
|
|
@@ -30,6 +31,14 @@ module Hippo
|
|
30
31
|
end
|
31
32
|
end
|
32
33
|
|
34
|
+
def load_objects_from_path(path, decorator: nil)
|
35
|
+
files = Dir[path]
|
36
|
+
files.each_with_object({}) do |path, objects|
|
37
|
+
file = load_yaml_from_file(path, decorator: decorator)
|
38
|
+
objects[path] = file
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
33
42
|
def create_object_definitions(hash, stage, required_kinds: nil, clean: false)
|
34
43
|
index = 0
|
35
44
|
hash.each_with_object([]) do |(path, objects), array|
|
@@ -55,13 +64,17 @@ module Hippo
|
|
55
64
|
end
|
56
65
|
|
57
66
|
def open_in_editor(name, contents)
|
67
|
+
if ENV['EDITOR'].nil?
|
68
|
+
raise Error, 'No EDITOR environment variable has been defined'
|
69
|
+
end
|
70
|
+
|
58
71
|
tmp_root = File.join(ENV['HOME'], '.hippo')
|
59
72
|
FileUtils.mkdir_p(tmp_root)
|
60
73
|
begin
|
61
74
|
tmpfile = Tempfile.new([name, '.yaml'], tmp_root)
|
62
75
|
tmpfile.write(contents)
|
63
76
|
tmpfile.close
|
64
|
-
system("#{ENV['EDITOR']} #{tmpfile.path}")
|
77
|
+
Kernel.system("#{ENV['EDITOR']} #{tmpfile.path}")
|
65
78
|
tmpfile.open
|
66
79
|
tmpfile.read
|
67
80
|
ensure
|
@@ -102,6 +115,53 @@ module Hippo
|
|
102
115
|
response = response.to_s.strip
|
103
116
|
response.empty? ? default : response
|
104
117
|
end
|
118
|
+
|
119
|
+
def system(command, stdin_data: nil)
|
120
|
+
stdout, stderr, status = Open3.capture3(command, stdin_data: stdin_data)
|
121
|
+
unless status.success?
|
122
|
+
raise Error, "Command failed to execute: #{stderr}"
|
123
|
+
end
|
124
|
+
|
125
|
+
stdout
|
126
|
+
end
|
127
|
+
|
128
|
+
def action(message)
|
129
|
+
$stdout.print message
|
130
|
+
complete_state = 'done'
|
131
|
+
color = '32'
|
132
|
+
passed_proc = proc do |value|
|
133
|
+
complete_state = value
|
134
|
+
color = '33'
|
135
|
+
end
|
136
|
+
return_value = yield(passed_proc)
|
137
|
+
puts " \e[#{color}m#{complete_state}\e[0m"
|
138
|
+
return_value
|
139
|
+
rescue StandardError => e
|
140
|
+
puts " \e[31merror\e[0m"
|
141
|
+
raise
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
def self.parse_kubectl_apply_lines(stdout)
|
146
|
+
stdout.split("\n").each_with_object({}) do |line, hash|
|
147
|
+
if line =~ %r{\A([\w\/\-\.]+) (\w+)\z}
|
148
|
+
object = Regexp.last_match(1)
|
149
|
+
status = Regexp.last_match(2)
|
150
|
+
elsif line =~ %r{\A[\w\.\/\-]+ \"([\w\-]+)\" (\w+)\z}
|
151
|
+
object = Regexp.last_match(1)
|
152
|
+
status = Regexp.last_match(2)
|
153
|
+
else
|
154
|
+
next
|
155
|
+
end
|
156
|
+
hash[object] = status
|
157
|
+
|
158
|
+
color = '32'
|
159
|
+
color = '31' if status == 'deleted'
|
160
|
+
color = '33' if status == 'configured'
|
161
|
+
|
162
|
+
status = "\e[#{color}m#{status}\e[0m" unless status == 'unchanged'
|
163
|
+
puts "\e[37m====> #{object} #{status}\e[0m"
|
164
|
+
end
|
105
165
|
end
|
106
166
|
end
|
107
167
|
end
|
data/lib/hippo/version.rb
CHANGED