hippo-cli 1.1.0 → 1.1.1
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.tar.gz.sig +0 -0
- data/bin/hippo +4 -4
- data/cli/apply_config.rb +1 -0
- data/cli/apply_services.rb +1 -0
- data/cli/console.rb +2 -0
- data/cli/create.rb +83 -0
- data/cli/deploy.rb +2 -0
- data/cli/init.rb +1 -1
- data/cli/install.rb +3 -1
- data/cli/key.rb +34 -0
- data/cli/kubectl.rb +2 -0
- data/cli/logs.rb +56 -0
- data/cli/objects.rb +50 -0
- data/cli/package_install.rb +30 -0
- data/cli/package_list.rb +40 -0
- data/cli/package_notes.rb +23 -0
- data/cli/package_test.rb +21 -0
- data/cli/package_uninstall.rb +27 -0
- data/cli/package_upgrade.rb +30 -0
- data/cli/package_values.rb +22 -0
- data/cli/prepare.rb +18 -0
- data/cli/run.rb +37 -0
- data/cli/secrets.rb +24 -0
- data/cli/stages.rb +26 -0
- data/cli/status.rb +25 -0
- data/cli/vars.rb +20 -0
- data/cli/version.rb +8 -0
- data/lib/hippo.rb +12 -0
- data/lib/hippo/bootstrap_parser.rb +64 -0
- data/lib/hippo/cli.rb +47 -14
- data/lib/hippo/extensions.rb +9 -0
- data/lib/hippo/image.rb +35 -31
- data/lib/hippo/manifest.rb +17 -7
- data/lib/hippo/object_definition.rb +18 -5
- data/lib/hippo/package.rb +124 -0
- data/lib/hippo/repository_tag.rb +38 -0
- data/lib/hippo/secret_manager.rb +61 -14
- data/lib/hippo/stage.rb +60 -26
- data/lib/hippo/util.rb +34 -0
- data/lib/hippo/version.rb +1 -1
- data/template/Hippofile +10 -2
- metadata +44 -13
- metadata.gz.sig +0 -0
- data/cli/secrets_edit.rb +0 -34
- data/cli/secrets_key.rb +0 -20
- data/lib/hippo/secret.rb +0 -112
- data/template/config/env-vars.yaml +0 -7
- data/template/deployments/web.yaml +0 -29
- data/template/deployments/worker.yaml +0 -26
- data/template/jobs/deploy/db-migration.yaml +0 -22
- data/template/jobs/install/load-schema.yaml +0 -22
- data/template/services/main.ingress.yaml +0 -13
- data/template/services/web.svc.yaml +0 -11
- data/template/stages/production.yaml +0 -7
data/lib/hippo/stage.rb
CHANGED
@@ -3,6 +3,7 @@
|
|
3
3
|
require 'liquid'
|
4
4
|
require 'open3'
|
5
5
|
require 'hippo/secret_manager'
|
6
|
+
require 'hippo/package'
|
6
7
|
|
7
8
|
module Hippo
|
8
9
|
class Stage
|
@@ -21,6 +22,10 @@ module Hippo
|
|
21
22
|
@options['branch']
|
22
23
|
end
|
23
24
|
|
25
|
+
def image_tag
|
26
|
+
@options['image-tag']
|
27
|
+
end
|
28
|
+
|
24
29
|
def namespace
|
25
30
|
@options['namespace']
|
26
31
|
end
|
@@ -29,31 +34,43 @@ module Hippo
|
|
29
34
|
@options['context']
|
30
35
|
end
|
31
36
|
|
32
|
-
def
|
33
|
-
@options['
|
37
|
+
def config
|
38
|
+
@options['config']
|
39
|
+
end
|
40
|
+
|
41
|
+
def images
|
42
|
+
@images ||= @manifest.images.deep_merge(@options['images'] || {}).each_with_object({}) do |(key, image), hash|
|
43
|
+
hash[key] = Image.new(key, image)
|
44
|
+
end
|
34
45
|
end
|
35
46
|
|
36
47
|
# These are the vars to represent this
|
37
48
|
def template_vars
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
49
|
+
@template_vars ||= begin
|
50
|
+
{
|
51
|
+
'manifest' => @manifest.template_vars,
|
52
|
+
'stage-name' => name,
|
53
|
+
'branch' => branch,
|
54
|
+
'image-tag' => image_tag,
|
55
|
+
'namespace' => namespace,
|
56
|
+
'context' => context,
|
57
|
+
'images' => images.values.each_with_object({}) { |image, hash| hash[image.name] = image.template_vars },
|
58
|
+
'config' => @manifest.config.deep_merge(config),
|
59
|
+
'secrets' => secret_manager.all
|
60
|
+
}
|
61
|
+
end
|
46
62
|
end
|
47
63
|
|
48
64
|
# Return a new decorator object that can be passed to objects that
|
49
65
|
# would like to decorator things.
|
50
66
|
def decorator
|
51
67
|
proc do |data|
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
68
|
+
begin
|
69
|
+
template = Liquid::Template.parse(data)
|
70
|
+
template.render(template_vars)
|
71
|
+
rescue Liquid::SyntaxError => e
|
72
|
+
raise Error, "Template error: #{e.message}"
|
73
|
+
end
|
57
74
|
end
|
58
75
|
end
|
59
76
|
|
@@ -69,28 +86,46 @@ module Hippo
|
|
69
86
|
#
|
70
87
|
# @return [Hash<String,Hippo::ObjectDefinition>]
|
71
88
|
def deployments
|
72
|
-
Util.create_object_definitions(objects('deployments'), self, required_kinds: ['Deployment'])
|
89
|
+
@deployments ||= Util.create_object_definitions(objects('deployments'), self, required_kinds: ['Deployment'])
|
73
90
|
end
|
74
91
|
|
75
92
|
# Return an array of all services/ingresses for this stage
|
76
93
|
#
|
77
94
|
# @return [Hash<String,Hippo::ObjectDefinition>]
|
78
95
|
def services
|
79
|
-
Util.create_object_definitions(objects('services'), self, required_kinds: %w[Service Ingress NetworkPolicy])
|
96
|
+
@services ||= Util.create_object_definitions(objects('services'), self, required_kinds: %w[Service Ingress NetworkPolicy])
|
80
97
|
end
|
81
98
|
|
82
99
|
# Return an array of all configuration objects
|
83
100
|
#
|
84
101
|
# @return [Hash<String,Hippo::ObjectDefinition>]
|
85
102
|
def configs
|
86
|
-
Util.create_object_definitions(objects('config'), self)
|
103
|
+
@configs ||= Util.create_object_definitions(objects('config'), self)
|
87
104
|
end
|
88
105
|
|
89
106
|
# Return an array of all job objects
|
90
107
|
#
|
91
108
|
# @return [Hash<String,Hippo::ObjectDefinition>]
|
92
109
|
def jobs(type)
|
93
|
-
|
110
|
+
@jobs ||= {}
|
111
|
+
@jobs[type] ||= Util.create_object_definitions(objects("jobs/#{type}"), self)
|
112
|
+
end
|
113
|
+
|
114
|
+
# Return a hash of all packages available in the stage
|
115
|
+
#
|
116
|
+
# @return [Hash<String, Hippo::Package>]
|
117
|
+
def packages
|
118
|
+
@packages ||= objects('packages').values.each_with_object({}) do |package_hash, hash|
|
119
|
+
package = Package.new(package_hash.first, self)
|
120
|
+
hash[package.name] = package
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
# Return any package values that have been defined
|
125
|
+
#
|
126
|
+
# @return [Hash]
|
127
|
+
def overridden_package_values
|
128
|
+
@options['packages'] || {}
|
94
129
|
end
|
95
130
|
|
96
131
|
# Return a kubectl command ready for use within this stage's
|
@@ -109,7 +144,7 @@ module Hippo
|
|
109
144
|
# @param objects [Array<Hippo::ObjectDefinition>]
|
110
145
|
# @return [Hash]
|
111
146
|
def apply(objects)
|
112
|
-
yaml_to_apply = objects.map(&:
|
147
|
+
yaml_to_apply = objects.map(&:yaml_to_apply).join("\n")
|
113
148
|
|
114
149
|
command = ['kubectl']
|
115
150
|
command += ['--context', context] if context
|
@@ -144,13 +179,12 @@ module Hippo
|
|
144
179
|
# @return [Array<Hippo::ObjectDefinition>]
|
145
180
|
def get(*names)
|
146
181
|
command = kubectl('get', '-o', 'yaml', *names)
|
147
|
-
Open3.
|
148
|
-
|
182
|
+
stdout, stderr, status = Open3.capture3(*command)
|
183
|
+
raise Error, "[kubectl] #{stderr}" unless status.success?
|
149
184
|
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
end
|
185
|
+
yaml = YAML.safe_load(stdout, permitted_classes: [Time])
|
186
|
+
yaml = yaml['items'] || [yaml]
|
187
|
+
yaml.map { |y| ObjectDefinition.new(y, self, clean: true) }
|
154
188
|
end
|
155
189
|
|
156
190
|
# Delete an object from the kubernetes API
|
data/lib/hippo/util.rb
CHANGED
@@ -68,6 +68,40 @@ module Hippo
|
|
68
68
|
tmpfile.unlink
|
69
69
|
end
|
70
70
|
end
|
71
|
+
|
72
|
+
def confirm(question)
|
73
|
+
response = ask(question)
|
74
|
+
if %w[yes y].include?(response.downcase)
|
75
|
+
puts
|
76
|
+
true
|
77
|
+
else
|
78
|
+
false
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def select(question, items)
|
83
|
+
items.each_with_index do |item, index|
|
84
|
+
puts "#{index + 1}) #{item}"
|
85
|
+
end
|
86
|
+
selected_item = nil
|
87
|
+
|
88
|
+
until selected_item
|
89
|
+
response = ask(question)
|
90
|
+
selected_item = items[response.to_i - 1]
|
91
|
+
if selected_item.nil?
|
92
|
+
puts "\e[31mThat is not a valid option. Try again.\e[0m"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
|
96
|
+
selected_item
|
97
|
+
end
|
98
|
+
|
99
|
+
def ask(question, default: nil)
|
100
|
+
puts "\e[35m#{question}\e[0m" + (default ? " [#{default}]" : '')
|
101
|
+
response = STDIN.gets
|
102
|
+
response = response.to_s.strip
|
103
|
+
response.empty? ? default : response
|
104
|
+
end
|
71
105
|
end
|
72
106
|
end
|
73
107
|
end
|
data/lib/hippo/version.rb
CHANGED
data/template/Hippofile
CHANGED
@@ -7,8 +7,16 @@ name: myapp
|
|
7
7
|
|
8
8
|
images:
|
9
9
|
main:
|
10
|
-
|
11
|
-
|
10
|
+
host: index.docker.io
|
11
|
+
name: myorg/myapp
|
12
|
+
tag: latest
|
13
|
+
# Alternatively, load the tag name from the current HEAD commit of a
|
14
|
+
# branch of a remote repository...
|
15
|
+
#
|
16
|
+
# tag:
|
17
|
+
# fromRepository:
|
18
|
+
# url: git@github.com:myorg/myapp
|
19
|
+
# branch: master
|
12
20
|
|
13
21
|
# If you wish, you can define a console command that allows you to easil
|
14
22
|
# open a console using `hippo [stage] console`
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: hippo-cli
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.1.
|
4
|
+
version: 1.1.1
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Adam Cooke
|
@@ -30,7 +30,7 @@ cert_chain:
|
|
30
30
|
3wUJNGnT5XYq+qvTqmjkTSTfdGvZCM63C6bGdN5CAyMokGOOatGqyCMAONolWnfC
|
31
31
|
gm3t2GWWrxY=
|
32
32
|
-----END CERTIFICATE-----
|
33
|
-
date: 2020-02-
|
33
|
+
date: 2020-02-10 00:00:00.000000000 Z
|
34
34
|
dependencies:
|
35
35
|
- !ruby/object:Gem::Dependency
|
36
36
|
name: encryptor
|
@@ -92,6 +92,26 @@ dependencies:
|
|
92
92
|
- - "<"
|
93
93
|
- !ruby/object:Gem::Version
|
94
94
|
version: '5.0'
|
95
|
+
- !ruby/object:Gem::Dependency
|
96
|
+
name: secure_random_string
|
97
|
+
requirement: !ruby/object:Gem::Requirement
|
98
|
+
requirements:
|
99
|
+
- - ">="
|
100
|
+
- !ruby/object:Gem::Version
|
101
|
+
version: '1.0'
|
102
|
+
- - "<"
|
103
|
+
- !ruby/object:Gem::Version
|
104
|
+
version: '2.0'
|
105
|
+
type: :runtime
|
106
|
+
prerelease: false
|
107
|
+
version_requirements: !ruby/object:Gem::Requirement
|
108
|
+
requirements:
|
109
|
+
- - ">="
|
110
|
+
- !ruby/object:Gem::Version
|
111
|
+
version: '1.0'
|
112
|
+
- - "<"
|
113
|
+
- !ruby/object:Gem::Version
|
114
|
+
version: '2.0'
|
95
115
|
- !ruby/object:Gem::Dependency
|
96
116
|
name: swamp-cli
|
97
117
|
requirement: !ruby/object:Gem::Requirement
|
@@ -124,34 +144,45 @@ files:
|
|
124
144
|
- cli/apply_config.rb
|
125
145
|
- cli/apply_services.rb
|
126
146
|
- cli/console.rb
|
147
|
+
- cli/create.rb
|
127
148
|
- cli/deploy.rb
|
128
149
|
- cli/help.rb
|
129
150
|
- cli/init.rb
|
130
151
|
- cli/install.rb
|
152
|
+
- cli/key.rb
|
131
153
|
- cli/kubectl.rb
|
132
|
-
- cli/
|
133
|
-
- cli/
|
154
|
+
- cli/logs.rb
|
155
|
+
- cli/objects.rb
|
156
|
+
- cli/package_install.rb
|
157
|
+
- cli/package_list.rb
|
158
|
+
- cli/package_notes.rb
|
159
|
+
- cli/package_test.rb
|
160
|
+
- cli/package_uninstall.rb
|
161
|
+
- cli/package_upgrade.rb
|
162
|
+
- cli/package_values.rb
|
163
|
+
- cli/prepare.rb
|
164
|
+
- cli/run.rb
|
165
|
+
- cli/secrets.rb
|
166
|
+
- cli/stages.rb
|
167
|
+
- cli/status.rb
|
168
|
+
- cli/vars.rb
|
169
|
+
- cli/version.rb
|
134
170
|
- lib/hippo.rb
|
171
|
+
- lib/hippo/bootstrap_parser.rb
|
135
172
|
- lib/hippo/cli.rb
|
136
173
|
- lib/hippo/deployment_monitor.rb
|
137
174
|
- lib/hippo/error.rb
|
175
|
+
- lib/hippo/extensions.rb
|
138
176
|
- lib/hippo/image.rb
|
139
177
|
- lib/hippo/manifest.rb
|
140
178
|
- lib/hippo/object_definition.rb
|
141
|
-
- lib/hippo/
|
179
|
+
- lib/hippo/package.rb
|
180
|
+
- lib/hippo/repository_tag.rb
|
142
181
|
- lib/hippo/secret_manager.rb
|
143
182
|
- lib/hippo/stage.rb
|
144
183
|
- lib/hippo/util.rb
|
145
184
|
- lib/hippo/version.rb
|
146
185
|
- template/Hippofile
|
147
|
-
- template/config/env-vars.yaml
|
148
|
-
- template/deployments/web.yaml
|
149
|
-
- template/deployments/worker.yaml
|
150
|
-
- template/jobs/deploy/db-migration.yaml
|
151
|
-
- template/jobs/install/load-schema.yaml
|
152
|
-
- template/services/main.ingress.yaml
|
153
|
-
- template/services/web.svc.yaml
|
154
|
-
- template/stages/production.yaml
|
155
186
|
homepage: https://github.com/adamcooke/hippo
|
156
187
|
licenses:
|
157
188
|
- MIT
|
metadata.gz.sig
CHANGED
Binary file
|
data/cli/secrets_edit.rb
DELETED
@@ -1,34 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
command :'secrets:edit' do
|
4
|
-
desc 'Create/edit an encrypted secrets file'
|
5
|
-
|
6
|
-
action do |context|
|
7
|
-
require 'hippo/cli'
|
8
|
-
cli = Hippo::CLI.setup(context)
|
9
|
-
|
10
|
-
secret_name = context.args[0]
|
11
|
-
raise Hippo::Error, 'You must provide a secret name' if secret_name.nil?
|
12
|
-
|
13
|
-
manager = cli.stage.secret_manager
|
14
|
-
unless manager.key_available?
|
15
|
-
puts "\e[31mNo key has been published for this stage yet.\e[0m"
|
16
|
-
puts "Use `hippo #{cli.stage.name} secrets:key --generate` to generate one."
|
17
|
-
exit 2
|
18
|
-
end
|
19
|
-
|
20
|
-
secret = manager.secret(secret_name)
|
21
|
-
if secret.exists?
|
22
|
-
secret.edit
|
23
|
-
else
|
24
|
-
puts "No secret exists at #{secret.path}. Would you like to create one?"
|
25
|
-
response = STDIN.gets.strip.downcase.strip
|
26
|
-
if %w[y yes please].include?(response)
|
27
|
-
secret.create
|
28
|
-
secret.edit
|
29
|
-
else
|
30
|
-
puts 'Not a problem. You can make it later.'
|
31
|
-
end
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
data/cli/secrets_key.rb
DELETED
@@ -1,20 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
command :'secrets:key' do
|
4
|
-
desc 'Display/generate details about the secret encryption key'
|
5
|
-
|
6
|
-
action do |context|
|
7
|
-
require 'hippo/cli'
|
8
|
-
cli = Hippo::CLI.setup(context)
|
9
|
-
sm = cli.stage.secret_manager
|
10
|
-
if sm.key_available?
|
11
|
-
puts 'Secret encryption key is stored in secret/hippo-secret-key.'
|
12
|
-
else
|
13
|
-
puts 'Secret encryption key has not been generated yet.'
|
14
|
-
puts 'Generate a new using:'
|
15
|
-
puts
|
16
|
-
puts " hippo #{cli.stage.name} secrets:key --generate"
|
17
|
-
puts
|
18
|
-
end
|
19
|
-
end
|
20
|
-
end
|
data/lib/hippo/secret.rb
DELETED
@@ -1,112 +0,0 @@
|
|
1
|
-
# frozen_string_literal: true
|
2
|
-
|
3
|
-
module Hippo
|
4
|
-
class Secret
|
5
|
-
HEADER = [
|
6
|
-
'# This file is encrypted and managed by Hippo.',
|
7
|
-
'# Use `hippo [stage] secrets:edit [name]` to make changes to it.',
|
8
|
-
'#',
|
9
|
-
'# Note: this cannot be applied directly to your Kubernetes server because',
|
10
|
-
'# HippoEncryptedSecret is not a valid object. It will be automatically ',
|
11
|
-
'# converted to a Secret when it is applied by Hippo.'
|
12
|
-
].join("\n")
|
13
|
-
|
14
|
-
EDIT_HEADER = [
|
15
|
-
'# This file has been unencrypted for you to edit it.',
|
16
|
-
'# Make your changes and close your edit to re-encrypt and save the file.',
|
17
|
-
'# You can change the apiVersion or add any additional metadata.',
|
18
|
-
'#',
|
19
|
-
'# You should not change the kind of document, it should be HippoEncryptedSecret.'
|
20
|
-
].join("\n")
|
21
|
-
|
22
|
-
def initialize(manager, name)
|
23
|
-
@manager = manager
|
24
|
-
@name = name
|
25
|
-
end
|
26
|
-
|
27
|
-
# Return the path where this secret is stored
|
28
|
-
#
|
29
|
-
# @return [String]
|
30
|
-
def path
|
31
|
-
File.join(@manager.root, "#{@name}.yaml")
|
32
|
-
end
|
33
|
-
|
34
|
-
# Does this secret exist yet?
|
35
|
-
#
|
36
|
-
# @return [Boolean]
|
37
|
-
def exists?
|
38
|
-
File.file?(path)
|
39
|
-
end
|
40
|
-
|
41
|
-
# Create a new empty secret file on the file system
|
42
|
-
#
|
43
|
-
# @return [void]
|
44
|
-
def create
|
45
|
-
return if exists?
|
46
|
-
|
47
|
-
od = ObjectDefinition.new(
|
48
|
-
{
|
49
|
-
'kind' => 'HippoEncryptedSecret',
|
50
|
-
'apiVersion' => 'v1',
|
51
|
-
'metadata' => {
|
52
|
-
'name' => @name
|
53
|
-
},
|
54
|
-
'data' => {
|
55
|
-
'example-value' => @manager.encrypt('This is an example encrypted value!')
|
56
|
-
}
|
57
|
-
},
|
58
|
-
@manager.stage,
|
59
|
-
clean: true
|
60
|
-
)
|
61
|
-
File.open(path, 'w') { |f| f.write(HEADER + "\n" + od.yaml) }
|
62
|
-
end
|
63
|
-
|
64
|
-
# Read the value from the file and decrypt all values that are present
|
65
|
-
#
|
66
|
-
# @return [String]
|
67
|
-
def editable_yaml
|
68
|
-
return unless exists?
|
69
|
-
|
70
|
-
objects = Util.load_yaml_from_file(path)
|
71
|
-
objects.each do |hash|
|
72
|
-
hash['data'].each do |key, value|
|
73
|
-
hash['data'][key] = @manager.decrypt(value)
|
74
|
-
end
|
75
|
-
end
|
76
|
-
|
77
|
-
objects.map(&:to_yaml).join("\n---\n")
|
78
|
-
end
|
79
|
-
|
80
|
-
# Edit this secret
|
81
|
-
#
|
82
|
-
# @return [void]
|
83
|
-
def edit
|
84
|
-
contents = Util.open_in_editor("secret-#{@name}", EDIT_HEADER + "\n" + editable_yaml)
|
85
|
-
yamls = Util.load_yaml_from_data(contents)
|
86
|
-
ods = Util.create_object_definitions({ 'secret' => yamls }, @manager.stage, required_kinds: ['HippoEncryptedSecret'], clean: true)
|
87
|
-
ods.each do |od|
|
88
|
-
od['data'].each do |key, value|
|
89
|
-
od['data'][key] = @manager.encrypt(value)
|
90
|
-
end
|
91
|
-
end
|
92
|
-
File.open(path, 'w') { |f| f.write(HEADER + "\n" + ods.map(&:yaml).join("\n---\n")) }
|
93
|
-
rescue StandardError => e
|
94
|
-
puts "Failed to edit secret (#{e.message})"
|
95
|
-
retry
|
96
|
-
end
|
97
|
-
|
98
|
-
# Return this secret as it can be exported to kubernetes
|
99
|
-
#
|
100
|
-
# @return [Array<ObjectDefinition>]
|
101
|
-
def applyable_yaml
|
102
|
-
objects = Util.load_yaml_from_file(path)
|
103
|
-
objects = objects.each do |hash|
|
104
|
-
hash['kind'] = 'Secret'
|
105
|
-
hash['data'].each do |key, value|
|
106
|
-
hash['data'][key] = Base64.encode64(@manager.decrypt(value)).gsub("\n", '')
|
107
|
-
end
|
108
|
-
end
|
109
|
-
Util.create_object_definitions({ 'secret' => objects }, @manager.stage)
|
110
|
-
end
|
111
|
-
end
|
112
|
-
end
|