monkey_king 0.1.5 → 0.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
- data/Gemfile +1 -0
- data/README.md +140 -20
- data/lib/monkey_king/clone_command.rb +12 -2
- data/lib/monkey_king/parser.rb +115 -50
- data/lib/monkey_king/version.rb +1 -1
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: c24e35b55ba57f034926c86a82c3a095b46addeb
|
4
|
+
data.tar.gz: 254ebb7b6fcff6acc074a2dbcd5b88ea49668d2f
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 626b160499b2067a239196a0351b38fb63b4d377e0c85b54092d1ae466128a118800098ff860a7a10b7128b78e8ced7b9ff999dd700cc9f395f69dcc00c72516
|
7
|
+
data.tar.gz: d4a51f2ee54a8d9010397c49a61b05ec6bd3ad597291b54109f79cdbb15c860e2a80d7acb031ce5aeb5d14905031e3f3310846ecee73c5edc3058d8d4f49cac2
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -2,30 +2,32 @@
|
|
2
2
|
|
3
3
|
Monkey king is a tool which is initially designed for generating deployment manifests for a bosh deployment based on an existing deployment, and it could also be used for other purposes like performing functions on keys/values in yaml files based on yaml tags.
|
4
4
|
|
5
|
+
Here is some scenarios about how it works, some of these examples are in the `./fixtures/` directory:
|
5
6
|
|
6
|
-
Here is some scenarios about how it works:
|
7
7
|
## Using secret generation
|
8
|
-
The `!MK:secret` directive generates a random secret
|
9
|
-
|
8
|
+
The `!MK:secret(<password_length>)` directive generates a random secret using [a-Z,0-9] with the length specified in the parameter.
|
9
|
+
|
10
|
+
Before:
|
11
|
+
|
10
12
|
```
|
11
13
|
---
|
12
14
|
meta:
|
13
|
-
secret: !MK:secret
|
14
|
-
another_secret: !MK:secret
|
15
|
+
secret: !MK:secret(12) replace_me
|
16
|
+
another_secret: !MK:secret replace_me
|
15
17
|
not_secret: not_secret
|
16
18
|
```
|
17
19
|
|
18
|
-
|
20
|
+
After:
|
19
21
|
```
|
20
22
|
---
|
21
23
|
meta:
|
22
|
-
secret: !MK:secret
|
23
|
-
another_secret: !MK:secret
|
24
|
+
secret: !MK:secret(12) SomeRandomPass
|
25
|
+
another_secret: !MK:secret() AnotherRandomPass
|
24
26
|
not_secret: not_secret
|
25
27
|
```
|
26
28
|
|
27
29
|
## Using environment variables
|
28
|
-
The `!MK:env
|
30
|
+
The `!MK:env(<variable_name>)` directive pulls values from environment variables and replaces the tagged keys/values
|
29
31
|
|
30
32
|
Given:
|
31
33
|
|
@@ -40,10 +42,10 @@ Before:
|
|
40
42
|
meta1:
|
41
43
|
not_secret: not_secret
|
42
44
|
layer1:
|
43
|
-
- id1: !MK:env
|
45
|
+
- id1: !MK:env(id1) id1_before
|
44
46
|
- layer2:
|
45
|
-
- id2: !MK:env
|
46
|
-
- !MK:env
|
47
|
+
- id2: !MK:env(id2) id2_before
|
48
|
+
- !MK:env(id1) id3: !MK:env:id1 id1_before
|
47
49
|
```
|
48
50
|
|
49
51
|
After:
|
@@ -52,13 +54,110 @@ After:
|
|
52
54
|
meta1:
|
53
55
|
not_secret: not_secret
|
54
56
|
layer1:
|
55
|
-
- id1: !MK:env
|
57
|
+
- id1: !MK:env(id1) id1_from_env
|
56
58
|
- layer2:
|
57
|
-
- id2: !MK:env
|
58
|
-
- !MK:env
|
59
|
+
- id2: !MK:env(id2) id2_from_env
|
60
|
+
- !MK:env(id1) id1_from_env: !MK:env:id1 id1_from_env
|
61
|
+
```
|
62
|
+
|
63
|
+
## Using Read and Write
|
64
|
+
The `!MK:read(<variable_name>)` and `!MK:write(<variable_name>,<value>)` directive is used to save the generated value and use it later.
|
65
|
+
|
66
|
+
**NOTE: at this time, reads must be ordered after writes in the YAML document until we implement a dependency graph.**
|
67
|
+
|
68
|
+
Before:
|
69
|
+
|
70
|
+
```
|
71
|
+
---
|
72
|
+
meta:
|
73
|
+
secret: !MK:write(nat_secret,secret(12)) replace_me
|
74
|
+
same_secret_again: !MK:read(nat_secret) replace_me
|
75
|
+
```
|
76
|
+
|
77
|
+
After:
|
78
|
+
|
79
|
+
```
|
80
|
+
---
|
81
|
+
meta:
|
82
|
+
secret: !MK:write(nat_secret,secret(12)) SAME_PASSWORD_HERE
|
83
|
+
same_secret_again: !MK:read(nat_secret) SAME_PASSWORD_HERE
|
84
|
+
```
|
85
|
+
|
86
|
+
# Using string format
|
87
|
+
The `!MK:format(<variable_1>,<variable_2>,...,<string>)` directive can be used to format the string given in the yaml field. This is usally used with `write_value` directive which store the template to a variable.
|
88
|
+
|
89
|
+
**NOTE: The string literal must be defined outside of the YAML tag, as there is a limited set of allowed characters in YAML tags. See usage of `nat_template` in example below**
|
90
|
+
|
91
|
+
Given:
|
92
|
+
|
93
|
+
```
|
94
|
+
export NAT_HOST=10.10.0.6
|
95
|
+
```
|
96
|
+
|
97
|
+
Before:
|
98
|
+
|
99
|
+
```
|
100
|
+
---
|
101
|
+
nat_template: write_value(TEMPLATE) https://%s
|
102
|
+
meta:
|
103
|
+
nat_url: !MK:format(env(NAT_HOST),read(TEMPLATE)) replaceme
|
104
|
+
```
|
105
|
+
|
106
|
+
After:
|
107
|
+
```
|
108
|
+
---
|
109
|
+
nat_template: write_value(TEMPLATE) https://%s
|
110
|
+
meta:
|
111
|
+
nat_url: !MK:format(env(NAT_HOST),read(TEMPLATE)) https://10.10.0.6
|
112
|
+
```
|
113
|
+
|
114
|
+
## Combine them all
|
115
|
+
You can combine the directive in a LISP-like syntax to create more poweful usages:
|
116
|
+
|
117
|
+
####Example:
|
118
|
+
|
119
|
+
Given:
|
120
|
+
|
121
|
+
```
|
122
|
+
export NATS_USER=nats_user
|
123
|
+
export NATS_HOST=10.10.0.6
|
124
|
+
```
|
125
|
+
|
126
|
+
Before:
|
127
|
+
|
128
|
+
```
|
129
|
+
nat_template_1: !MK:write_value(NAT_TEMPLATE_1) https://%s:%s@%s
|
130
|
+
nat_template_2: !MK:write_value(NAT_TEMPLATE_2) '%s/info'
|
131
|
+
|
132
|
+
meta1:
|
133
|
+
not_secret: not_secret
|
134
|
+
layer1:
|
135
|
+
- nat_user: !MK:write(NATS_USER,env(NATS_USER)) replaceme
|
136
|
+
- nat_host: !MK:write(NATS_HOST,env(NATS_HOST)) replaceme
|
137
|
+
- nat_password: !MK:write(NATS_PASSWORD,secret(12)) replaceme
|
138
|
+
layer2:
|
139
|
+
- nat_connection: !MK:write(NATS_STRING,format(read(NATS_USER),read(NATS_PASSWORD),read(NATS_HOST),read(NAT_TEMPLATE_1)))
|
140
|
+
- info_endpoint: !MK:format(read(NATS_STRING),read(NAT_TEMPLATE_2))
|
59
141
|
```
|
60
142
|
|
61
143
|
|
144
|
+
After:
|
145
|
+
|
146
|
+
```
|
147
|
+
---
|
148
|
+
nat_template_1: !MK:write_value(NAT_TEMPLATE_1) https://%s:%s@%s
|
149
|
+
nat_template_2: !MK:write_value(NAT_TEMPLATE_2) '%s/info'
|
150
|
+
meta1:
|
151
|
+
not_secret: not_secret
|
152
|
+
layer1:
|
153
|
+
- nat_user: !MK:write(NATS_USER,env(NATS_USER)) nats_user
|
154
|
+
- nat_host: !MK:write(NATS_HOST,env(NATS_HOST)) 10.10.0.6
|
155
|
+
- nat_password: !MK:write(NATS_PASSWORD,secret(12)) WxhUE4RoJXYF
|
156
|
+
layer2:
|
157
|
+
- nat_connection: !MK:write(NATS_STRING,format(read(NATS_USER),read(NATS_PASSWORD),read(NATS_HOST),read(NAT_TEMPLATE_1))) https://nats_user:WxhUE4RoJXYF@10.10.0.6
|
158
|
+
- info_endpoint: !MK:format(read(NATS_STRING),read(NAT_TEMPLATE_2)) https://nats_user:WxhUE4RoJXYF@10.10.0.6/info
|
159
|
+
```
|
160
|
+
|
62
161
|
## Installation
|
63
162
|
|
64
163
|
Add this line to your application's Gemfile:
|
@@ -74,14 +173,35 @@ And then execute:
|
|
74
173
|
Or install it yourself as:
|
75
174
|
|
76
175
|
$ gem install monkey_king
|
176
|
+
|
177
|
+
**Or if you want the cutting edge**
|
178
|
+
|
179
|
+
```
|
180
|
+
git clone https://github.com/pivotal-cloudops/monkey_king.git
|
181
|
+
cd monkey_king
|
182
|
+
bundle exec mk
|
183
|
+
```
|
184
|
+
|
185
|
+
## Try it Out:
|
186
|
+
|
187
|
+
You can create a yaml file (example: demo.yml in ~/tmp) with the 'MK' yaml tags as described earlier.
|
188
|
+
|
189
|
+
Then run:
|
190
|
+
|
191
|
+
```
|
192
|
+
cd monkey_king
|
193
|
+
bundle exec mk demo ~/tmp/demo.yml
|
194
|
+
```
|
195
|
+
|
77
196
|
|
78
|
-
## Usage
|
197
|
+
## Full Usage
|
79
198
|
```
|
80
|
-
$ mk
|
199
|
+
$ mk
|
81
200
|
Commands:
|
82
|
-
help [COMMAND]
|
83
|
-
clone REPO DIR
|
84
|
-
replace GLOBS...
|
201
|
+
help [COMMAND] Help!
|
202
|
+
clone REPO DIR use MK to clone github repo and transform
|
203
|
+
replace GLOBS... Do MK transform for existing directory(ies)
|
204
|
+
demo FILE Demo MK transform for one file```
|
85
205
|
```
|
86
206
|
```
|
87
207
|
$ mk help clone
|
@@ -4,7 +4,7 @@ require 'highline/import'
|
|
4
4
|
module MonkeyKing
|
5
5
|
class CloneCommand < Mothership
|
6
6
|
|
7
|
-
desc "
|
7
|
+
desc "use MK to clone github repo and transform"
|
8
8
|
input :repo, :argument => true
|
9
9
|
input :dir, :argument => [:splat, true]
|
10
10
|
def clone
|
@@ -23,6 +23,7 @@ module MonkeyKing
|
|
23
23
|
|
24
24
|
deployment_yaml_files.each do |file|
|
25
25
|
puts "Transforming #{file}..."
|
26
|
+
MonkeyKing.variables = {}
|
26
27
|
transformed_content = parser.transform(file)
|
27
28
|
File.open(file, "w") do |overwrite_file|
|
28
29
|
overwrite_file.write transformed_content
|
@@ -31,7 +32,7 @@ module MonkeyKing
|
|
31
32
|
puts "Done."
|
32
33
|
end
|
33
34
|
|
34
|
-
desc "
|
35
|
+
desc "Do MK transform for existing directory(ies)"
|
35
36
|
input :globs, :argument => :splat
|
36
37
|
def replace
|
37
38
|
globs = input[:globs]
|
@@ -47,6 +48,7 @@ module MonkeyKing
|
|
47
48
|
extension = file.split('.').last
|
48
49
|
if extension == 'yml'
|
49
50
|
puts "Transforming #{file}..."
|
51
|
+
MonkeyKing.variables = {}
|
50
52
|
transformed_content = parser.transform(file)
|
51
53
|
File.open(file, "w") do |overwrite_file|
|
52
54
|
overwrite_file.write transformed_content
|
@@ -58,5 +60,13 @@ module MonkeyKing
|
|
58
60
|
puts "Done."
|
59
61
|
end
|
60
62
|
|
63
|
+
desc "Demo MK transform for one file"
|
64
|
+
input :file, :argument => true
|
65
|
+
def demo
|
66
|
+
file = input[:file]
|
67
|
+
parser = MonkeyKing::Parser.new
|
68
|
+
puts parser.transform(file)
|
69
|
+
end
|
70
|
+
|
61
71
|
end
|
62
72
|
end
|
data/lib/monkey_king/parser.rb
CHANGED
@@ -1,84 +1,150 @@
|
|
1
1
|
require 'yaml'
|
2
|
+
require 'securerandom'
|
3
|
+
require 'sexpistol'
|
4
|
+
require 'pry'
|
2
5
|
|
3
6
|
module MonkeyKing
|
4
|
-
|
5
|
-
yaml_tag '!MK:secret'
|
7
|
+
@@variables = {}
|
6
8
|
|
7
|
-
|
9
|
+
def self.variables
|
10
|
+
@@variables
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.variables=(value)
|
14
|
+
@@variables = value
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.set_variable(name, value)
|
18
|
+
@@variables[name] = value
|
19
|
+
return value
|
20
|
+
end
|
21
|
+
|
22
|
+
class FunctionTag
|
23
|
+
attr_accessor :scalar
|
8
24
|
|
9
|
-
def
|
10
|
-
self.
|
25
|
+
def register(tag)
|
26
|
+
self.class.send(:yaml_tag, tag)
|
11
27
|
end
|
12
28
|
|
13
|
-
def init_with(
|
14
|
-
|
15
|
-
when :scalar
|
16
|
-
self.secret = coder.scalar
|
17
|
-
else
|
29
|
+
def init_with(coder)
|
30
|
+
unless coder.type == :scalar
|
18
31
|
raise "Dunno how to handle #{coder.type} for #{coder.inspect}"
|
19
32
|
end
|
33
|
+
self.scalar = coder.scalar
|
20
34
|
end
|
21
35
|
|
22
|
-
def encode_with(
|
36
|
+
def encode_with(coder)
|
23
37
|
coder.style = Psych::Nodes::Mapping::FLOW
|
24
|
-
|
38
|
+
s_expression = coder.tag.sub(/^!MK:/, '')
|
39
|
+
s_expression.gsub!(/,/, ' ')
|
40
|
+
expression_tree = Sexpistol.new.parse_string(s_expression)
|
41
|
+
coder.scalar = expand(expression_tree).first
|
25
42
|
end
|
26
43
|
|
27
|
-
protected def secret=( str )
|
28
|
-
@secret= str
|
29
|
-
end
|
30
44
|
|
31
|
-
def
|
32
|
-
|
45
|
+
def argment_count_checking(params_count, legit_count, function_name)
|
46
|
+
if params_count > legit_count
|
47
|
+
raise "too many arguments for #{function_name} function (#{params_count} of #{legit_count})"
|
48
|
+
elsif params_count < legit_count
|
49
|
+
raise "not enough arguments for #{function_name} function (#{params_count} of #{legit_count})"
|
50
|
+
end
|
33
51
|
end
|
34
|
-
end
|
35
52
|
|
36
|
-
|
37
|
-
|
53
|
+
def expand(expression)
|
54
|
+
if expression.is_a? Array
|
55
|
+
function_array = []
|
56
|
+
expression.each do |ex|
|
57
|
+
function_array << expand(ex)
|
58
|
+
end
|
38
59
|
|
39
|
-
|
40
|
-
|
41
|
-
|
60
|
+
process_array = []
|
61
|
+
|
62
|
+
while !function_array.empty? do
|
63
|
+
key_word = function_array.shift
|
64
|
+
case key_word
|
65
|
+
when :write
|
66
|
+
params = function_array.shift
|
67
|
+
argment_count_checking(params.size, 2, key_word)
|
68
|
+
key = params.first
|
69
|
+
value = params.last
|
70
|
+
raise "attempting to redefine immutable variable #{key}, exiting" unless MonkeyKing.variables[key].nil?
|
71
|
+
process_array << MonkeyKing.set_variable(key, value.to_s)
|
72
|
+
when :read
|
73
|
+
params = function_array.shift
|
74
|
+
argment_count_checking(params.size, 1, key_word)
|
75
|
+
key = params.first
|
76
|
+
raise "unresolved variables #{key}" if MonkeyKing.variables[key].nil?
|
77
|
+
process_array << MonkeyKing.variables[key]
|
78
|
+
when :secret
|
79
|
+
params = function_array.shift
|
80
|
+
argment_count_checking(params.size, 1, key_word)
|
81
|
+
raise "argument error for secret function: got #{params.first.class} instead of Fixnum" if !params.first.is_a? Fixnum
|
82
|
+
length = params.first
|
83
|
+
process_array << gen_secret(length)
|
84
|
+
when :env
|
85
|
+
params = function_array.shift
|
86
|
+
argment_count_checking(params.size, 1, key_word)
|
87
|
+
key = params.first.to_s
|
88
|
+
process_array << get_env(key)
|
89
|
+
when :write_value
|
90
|
+
params = function_array.shift
|
91
|
+
argment_count_checking(params.size, 1, key_word)
|
92
|
+
key = params.first
|
93
|
+
process_array << MonkeyKing.set_variable(key, self.scalar)
|
94
|
+
when :format
|
95
|
+
params = function_array.shift
|
96
|
+
formating_string = params.pop
|
97
|
+
raise('format template not found') if formating_string.nil?
|
98
|
+
replace_count = formating_string.scan(/%s/).count
|
99
|
+
argment_count_checking(params.size, replace_count, key_word)
|
100
|
+
params.each do |param|
|
101
|
+
formating_string = formating_string.sub(/%s/, param.to_s)
|
102
|
+
end
|
103
|
+
process_array << formating_string
|
104
|
+
else
|
105
|
+
process_array << key_word
|
106
|
+
end
|
107
|
+
end
|
108
|
+
return process_array
|
109
|
+
|
110
|
+
elsif expression.is_a? Symbol
|
111
|
+
return expression
|
112
|
+
elsif expression.is_a? Numeric
|
113
|
+
return expression
|
114
|
+
elsif expression.is_a? String
|
115
|
+
return expression
|
116
|
+
else
|
117
|
+
raise "unknown expression #{expression}"
|
42
118
|
end
|
43
119
|
end
|
44
120
|
|
45
|
-
def
|
46
|
-
|
47
|
-
|
48
|
-
end
|
121
|
+
def get_env(key)
|
122
|
+
raise "#{key} not found in env" if ENV[key].nil?
|
123
|
+
return ENV[key]
|
49
124
|
end
|
50
125
|
|
51
|
-
def
|
52
|
-
|
53
|
-
tag=coder.tag.split(':')[2]
|
54
|
-
if ENV[tag].nil?
|
55
|
-
raise "#{tag} not found in env"
|
56
|
-
end
|
57
|
-
coder.scalar = ENV[tag]
|
126
|
+
def gen_secret(length)
|
127
|
+
return [*('a'..'z'),*('0'..'9'),*('A'..'Z')].shuffle[0,length].join
|
58
128
|
end
|
59
129
|
end
|
60
130
|
|
61
131
|
class Parser
|
132
|
+
|
62
133
|
def transform(yaml_file)
|
134
|
+
function_tag_instances = {}
|
63
135
|
tags = get_tags(yaml_file)
|
64
|
-
|
136
|
+
|
65
137
|
tags.each do |tag|
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
random_string = [*('a'..'z')].shuffle[0,32].join
|
74
|
-
Object.const_set("EnvTag#{class_name}#{random_string}", tag_class)
|
75
|
-
|
76
|
-
tag_instance = tag_class.new
|
77
|
-
tag_instance.register(tag)
|
78
|
-
env_tag_instances[tag] = tag_instance
|
79
|
-
end
|
138
|
+
if tag =~ /!MK:/
|
139
|
+
tag_class = Class.new(FunctionTag)
|
140
|
+
random_string = SecureRandom.uuid.gsub(/-/, '')
|
141
|
+
Object.const_set("FunctionTag#{random_string}", tag_class)
|
142
|
+
tag_instance = tag_class.new
|
143
|
+
tag_instance.register(tag)
|
144
|
+
function_tag_instances[tag] = tag_instance
|
80
145
|
end
|
81
146
|
end
|
147
|
+
|
82
148
|
yaml = YAML.load_file(yaml_file)
|
83
149
|
yaml.to_yaml
|
84
150
|
end
|
@@ -97,5 +163,4 @@ module MonkeyKing
|
|
97
163
|
tags.uniq
|
98
164
|
end
|
99
165
|
end
|
100
|
-
|
101
166
|
end
|
data/lib/monkey_king/version.rb
CHANGED
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: monkey_king
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.2.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- cloudops_hosted
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2016-03-
|
11
|
+
date: 2016-03-25 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: mothership
|