monkey_king 0.1.5 → 0.2.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|