tiller 0.1.0 → 0.1.2
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/bin/tiller +50 -38
- data/examples/lib/tiller/data/dummy.rb +12 -14
- data/examples/lib/tiller/data/network.rb +7 -9
- data/examples/lib/tiller/template/dummy.rb +6 -9
- data/lib/tiller/data/environment.rb +4 -6
- data/lib/tiller/data/file.rb +10 -13
- data/lib/tiller/data/random.rb +10 -13
- data/lib/tiller/datasource.rb +19 -18
- data/lib/tiller/template/file.rb +6 -9
- data/lib/tiller/templatesource.rb +9 -8
- 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: 86fc7d0cf3873002d5592553f0f5149cf252f09a
|
4
|
+
data.tar.gz: b1d175a16e5ed95836d026b2c031d850a909dcd2
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 7014d7b4591b1611c949c8a9096f02310ebac01fde5b5ec6410390456be54dcf9c46765f242954cc59d1262d8b9493bc8e776671fc337e195d669c79f09397a7
|
7
|
+
data.tar.gz: bc6d9b5e25f7844ff07ff5325d63623950fafe3eb57228109090d570844ebe9dbf84d4ad250db6417a65f17488ad1905c26f496d868f9ddfc552f9b06ef0f4fd
|
data/bin/tiller
CHANGED
@@ -1,9 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
# Tiller - Dynamic configuration generator, intended for use in Dockerfiles
|
3
|
-
# Named from the first ship-building (Docker) related term I could find that
|
3
|
+
# Named from the first ship-building (Docker) related term I could find that
|
4
|
+
# didn't have an existing gem named after it!
|
4
5
|
# Mark Round <github@markround.com>
|
5
6
|
|
6
|
-
VERSION='0.1.
|
7
|
+
VERSION = '0.1.2'
|
7
8
|
|
8
9
|
require 'erb'
|
9
10
|
require 'ostruct'
|
@@ -24,25 +25,26 @@ def warn_merge(key, old, new, type, source)
|
|
24
25
|
new
|
25
26
|
end
|
26
27
|
|
28
|
+
# And we're on our way...
|
27
29
|
module Tiller
|
28
|
-
|
29
|
-
#
|
30
|
+
# Set these two environment variables if you want to debug a configuration
|
31
|
+
# in a temporary directory.
|
30
32
|
# EG: $ tiller_base=/tmp tiller_lib=/tmp/lib ./tiller
|
31
33
|
config = {
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
34
|
+
:tiller_base => (ENV['tiller_base'].nil?) ? '/etc/tiller' : ENV['tiller_base'],
|
35
|
+
:tiller_lib => (ENV['tiller_lib'].nil?) ? '/usr/local/lib' : ENV['tiller_lib'],
|
36
|
+
# This is the main variable, usually the only one you pass into Docker.
|
37
|
+
:environment => (ENV['environment'].nil?) ? 'production' : ENV['environment']
|
36
38
|
}
|
37
39
|
|
40
|
+
# Add tiller_lib to the LOAD PATH so we can pull in user-defined plugins
|
38
41
|
$LOAD_PATH.unshift(config[:tiller_lib]) unless $LOAD_PATH.include?(config[:tiller_lib])
|
39
42
|
|
40
|
-
# User-provided sources.
|
41
43
|
require 'tiller/templatesource.rb'
|
42
44
|
require 'tiller/datasource.rb'
|
43
45
|
|
44
46
|
# Load the common YAML configuration file
|
45
|
-
config[:common_config] = YAML
|
47
|
+
config[:common_config] = YAML.load(open(File.join(config[:tiller_base], 'common.yaml')))
|
46
48
|
|
47
49
|
puts "tiller v#{VERSION} (https://github.com/markround/tiller) <github@markround.com>"
|
48
50
|
puts "Using configuration from #{config[:tiller_base]}"
|
@@ -61,83 +63,93 @@ module Tiller
|
|
61
63
|
puts 'Data sources loaded ' + DataSource.subclasses.to_s
|
62
64
|
|
63
65
|
# Get all Templates for the given environment
|
64
|
-
templates =
|
66
|
+
templates = Hash.new
|
65
67
|
TemplateSource.subclasses.each do |template_class|
|
66
68
|
ts = template_class.new(config)
|
67
69
|
ts.templates.each do |t|
|
68
|
-
templates[t]=ts.template(t)
|
70
|
+
templates[t] = ts.template(t)
|
69
71
|
end
|
70
72
|
end
|
71
73
|
|
72
|
-
puts "Templates to build #{templates.keys
|
74
|
+
puts "Templates to build #{templates.keys}"
|
73
75
|
|
74
|
-
# Now go through all our data sources and start to assemble our
|
75
|
-
# As hashes are getting merged, new values will take precedence over
|
76
|
-
#
|
77
|
-
|
76
|
+
# Now go through all our data sources and start to assemble our global_values
|
77
|
+
# hash. As hashes are getting merged, new values will take precedence over
|
78
|
+
# older ones, and a warning will be displayed.
|
79
|
+
# We also add in 'environment' to start with as it's very useful for all
|
80
|
+
# templates.
|
81
|
+
global_values = { 'environment' => config[:environment] }
|
78
82
|
DataSource.subclasses.each do |data_class|
|
79
83
|
global_values.merge!(data_class.new(config).global_values) do |key, old, new|
|
80
84
|
warn_merge(key, old, new, 'global', data_class.to_s)
|
81
85
|
end
|
82
86
|
end
|
83
87
|
|
84
|
-
# Now we go through each template we've identified, and get the
|
88
|
+
# Now we go through each template we've identified, and get the
|
89
|
+
# values for each one.
|
85
90
|
templates.each do |template, content|
|
86
91
|
values = Hash.new
|
87
92
|
target_values = Hash.new
|
88
|
-
puts "Building template #{template}"
|
89
93
|
|
90
|
-
# Now we populate the hash with values from each DataSource, warning if we
|
94
|
+
# Now we populate the hash with values from each DataSource, warning if we
|
95
|
+
# get duplicate values.
|
91
96
|
DataSource.subclasses.each do |data_class|
|
92
97
|
dc = data_class.new(config)
|
93
98
|
values.merge!(dc.values(template)) do |key, old, new|
|
94
99
|
warn_merge(key, old, new, 'data', data_class.to_s)
|
95
100
|
end
|
96
101
|
|
97
|
-
# Now get target_values (where the file should be installed to,
|
102
|
+
# Now get target_values (where the file should be installed to,
|
103
|
+
# permissions and so on)
|
98
104
|
target_values.merge!(dc.target_values(template)) do |key, old, new|
|
99
105
|
warn_merge(key, old, new, 'target', data_class.to_s)
|
100
106
|
end
|
101
107
|
end
|
102
108
|
|
103
|
-
# If our data source returned no values (e.g. we don't build this template
|
104
|
-
#
|
109
|
+
# If our data source returned no values (e.g. we don't build this template
|
110
|
+
# for this environment), we move onto the next one.
|
105
111
|
next if target_values.empty?
|
106
112
|
|
107
113
|
# Now, we build the template
|
114
|
+
puts "Building template #{template}"
|
108
115
|
tiller = values.merge(global_values) do |key, old, new|
|
109
116
|
warn_merge(key, old, new, 'global and local', 'merged configuration')
|
110
117
|
end
|
111
118
|
|
112
|
-
# Use an OpenStruct namespace, as it's way easier than faffing around with
|
113
|
-
# non-existing values just get replaced by <nil>
|
119
|
+
# Use an OpenStruct namespace, as it's way easier than faffing around with
|
120
|
+
# manual binding, and also non-existing values just get replaced by <nil>
|
121
|
+
# instead of failing on errors.
|
114
122
|
ns = OpenStruct.new(tiller)
|
115
123
|
parsed_template = ERB.new(content).result(ns.instance_eval { binding })
|
116
124
|
|
117
|
-
# Write the template, and also create the directory path if it
|
125
|
+
# Write the template, and also create the directory path if it
|
126
|
+
# doesn't exist.
|
118
127
|
target_path = File.dirname(target_values['target'])
|
119
|
-
FileUtils.mkdir_p(target_path)
|
120
|
-
target = open(target_values['target'],
|
128
|
+
FileUtils.mkdir_p(target_path) unless File.directory?(target_path)
|
129
|
+
target = open(target_values['target'], 'w')
|
121
130
|
target.puts(parsed_template)
|
122
131
|
target.close
|
123
132
|
|
124
133
|
# Set permissions if we are running as root
|
125
|
-
if Process::Sys.geteuid == 0
|
134
|
+
if Process::Sys.geteuid == 0
|
126
135
|
puts "Setting ownership/permissions on #{target_values['target']}"
|
127
|
-
|
128
|
-
|
129
|
-
|
136
|
+
if target_values.key?('perms')
|
137
|
+
FileUtils.chmod(target_values['perms'], target_values['target'])
|
138
|
+
end
|
139
|
+
# Don't need to check for the presence of these, as they're ignored
|
140
|
+
# if they are null.
|
141
|
+
FileUtils.chown(target_values['user'], target_values['group'],
|
142
|
+
target_values['target'])
|
130
143
|
else
|
131
|
-
puts
|
144
|
+
puts 'Not running as root, so not setting ownership/permissions on ' \
|
145
|
+
"#{target_values['target']}"
|
132
146
|
end
|
133
147
|
|
134
148
|
end
|
135
149
|
|
136
|
-
# All templates created, so let's handover to the replacement process then
|
137
|
-
|
150
|
+
# All templates created, so let's handover to the replacement process then
|
151
|
+
# it's home in time for tea and medals.
|
152
|
+
puts 'Template generation completed, about to exec replacement process.'
|
138
153
|
puts "Calling #{config[:common_config]['exec']}..."
|
139
154
|
exec(config[:common_config]['exec'])
|
140
|
-
|
141
155
|
end
|
142
|
-
|
143
|
-
|
@@ -1,27 +1,25 @@
|
|
1
|
-
# This is a "dummy" datasource for Tiller. All it does is provide a single
|
2
|
-
#
|
3
|
-
|
1
|
+
# This is a "dummy" datasource for Tiller. All it does is provide a single
|
2
|
+
# global and local value, and shows how you can provide a hash of target values
|
3
|
+
# to configure where templates are written.
|
4
4
|
class DummyDataSource < Tiller::DataSource
|
5
|
-
|
6
5
|
def global_values
|
7
6
|
{ 'dummy_global' => 'dummy global replacement text' }
|
8
7
|
end
|
9
8
|
|
10
|
-
def values(
|
9
|
+
def values(_template_name)
|
11
10
|
{ 'dummy' => 'dummy replacement text' }
|
12
11
|
end
|
13
12
|
|
14
|
-
# Dummy values, useful for testing. Will just result in the template being
|
15
|
-
# Remember that perms must be in octal
|
16
|
-
# Note that as you have the environment name in the
|
17
|
-
# for different environments.
|
13
|
+
# Dummy values, useful for testing. Will just result in the template being
|
14
|
+
# built and written to /tmp/dummy. Remember that perms must be in octal
|
15
|
+
# (no quotes). Note that as you have the environment name in the @config hash,
|
16
|
+
# you can always return different values for different environments.
|
18
17
|
def target_values(template_name)
|
19
18
|
{
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
19
|
+
'target' => "/tmp/dummy/#{template_name}",
|
20
|
+
'user' => 'root',
|
21
|
+
'group' => 'root',
|
22
|
+
'perms' => 0644
|
24
23
|
}
|
25
24
|
end
|
26
|
-
|
27
25
|
end
|
@@ -1,17 +1,15 @@
|
|
1
1
|
require 'socket'
|
2
|
-
|
3
|
-
#
|
4
|
-
#
|
5
|
-
# You can then do things like <%= fqdn %> or <%= ipv4_addresses[0][3] %> in
|
6
|
-
|
2
|
+
# This is a quick example of a global datasource for Tiller.
|
3
|
+
# It shows how you might provide some basic network-related information to your
|
4
|
+
# templates. It's a super quick and hacky, but serves as a useful example.
|
5
|
+
# You can then do things like <%= fqdn %> or <%= ipv4_addresses[0][3] %> in
|
6
|
+
# your templates.
|
7
7
|
class NetworkDataSource < Tiller::DataSource
|
8
|
-
|
9
8
|
def global_values
|
10
9
|
# Note, these rely on DNS being available and configured correctly!
|
11
10
|
{
|
12
|
-
|
13
|
-
|
11
|
+
'fqdn' => Socket.gethostbyname(Socket.gethostname).first,
|
12
|
+
'ipv4_addresses' => Socket.getaddrinfo(Socket.gethostbyname(Socket.gethostname).first, nil, :INET)
|
14
13
|
}
|
15
14
|
end
|
16
|
-
|
17
15
|
end
|
@@ -1,18 +1,15 @@
|
|
1
1
|
# This is another quick example of how to create a template source.
|
2
|
-
|
3
2
|
class DummyTemplateSource < Tiller::TemplateSource
|
4
|
-
|
5
3
|
# Just provide a single dummy template
|
6
4
|
def templates
|
7
|
-
[
|
5
|
+
['dummy.erb']
|
8
6
|
end
|
9
7
|
|
10
8
|
# And return some sample ERB content.
|
11
|
-
# Note that as you have the environment name in the
|
12
|
-
# for different environments.
|
13
|
-
def template(
|
14
|
-
"This is a dummy template! <%= dummy %> \n
|
9
|
+
# Note that as you have the environment name in the @config hash, you can
|
10
|
+
# always return different templates for different environments.
|
11
|
+
def template(_template_name)
|
12
|
+
"This is a dummy template! <%= dummy %> \n "\
|
13
|
+
'Here is a global value <%= dummy_global %>'
|
15
14
|
end
|
16
|
-
|
17
15
|
end
|
18
|
-
|
@@ -1,12 +1,10 @@
|
|
1
|
-
# Environment datasource for Tiller. This extracts all environment variables,
|
2
|
-
#
|
3
|
-
|
1
|
+
# Environment datasource for Tiller. This extracts all environment variables,
|
2
|
+
# and makes them available to templates by converting to lowercase and
|
3
|
+
# preceeding them with env_. E.G. env_home, env_logname and so on.
|
4
4
|
class EnvironmentDataSource < Tiller::DataSource
|
5
|
-
|
6
5
|
def global_values
|
7
6
|
values = Hash.new
|
8
|
-
ENV.each { |k,v| values["env_#{k.downcase}"] = v }
|
7
|
+
ENV.each { |k, v| values["env_#{k.downcase}"] = v }
|
9
8
|
values
|
10
9
|
end
|
11
|
-
|
12
10
|
end
|
data/lib/tiller/data/file.rb
CHANGED
@@ -1,26 +1,23 @@
|
|
1
|
-
|
2
|
-
#
|
3
|
-
#
|
1
|
+
require 'yaml'
|
2
|
+
# File datasource for Tiller. This works the same way as the default behaviour
|
3
|
+
# in Runner.rb - it loads your <environment>.yaml file and pulls data from it.
|
4
|
+
# See examples/etc/tiller/environments/production.yaml to see what this file
|
5
|
+
# looks like.
|
4
6
|
#
|
5
7
|
# We also don't provide any global values, just ones specific to a template.
|
6
|
-
|
7
|
-
|
8
|
-
require 'yaml'
|
9
|
-
|
10
8
|
class FileDataSource < Tiller::DataSource
|
11
|
-
|
12
9
|
# Open and parse the environment file
|
13
10
|
def setup
|
14
|
-
env_file = File.join(
|
15
|
-
|
11
|
+
env_file = File.join(@config[:tiller_base], 'environments',
|
12
|
+
"#{@config[:environment]}.yaml")
|
13
|
+
@config_hash = YAML.load(open(env_file))
|
16
14
|
end
|
17
15
|
|
18
16
|
def values(template_name)
|
19
|
-
@config_hash.
|
17
|
+
@config_hash.key?(template_name) ? @config_hash[template_name]['config'] : Hash.new
|
20
18
|
end
|
21
19
|
|
22
20
|
def target_values(template_name)
|
23
|
-
@config_hash.
|
21
|
+
@config_hash.key?(template_name) ? @config_hash[template_name] : Hash.new
|
24
22
|
end
|
25
|
-
|
26
23
|
end
|
data/lib/tiller/data/random.rb
CHANGED
@@ -1,20 +1,17 @@
|
|
1
|
-
# Random datasource for Tiller. Provides Base64, UUID and other useful random strings
|
2
|
-
|
3
1
|
require 'securerandom'
|
4
|
-
|
2
|
+
# Random datasource for Tiller. Provides Base64, UUID and other useful
|
3
|
+
# random strings.
|
5
4
|
class RandomDataSource < Tiller::DataSource
|
6
|
-
|
7
5
|
def global_values
|
8
6
|
{
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
7
|
+
'random_base64' => SecureRandom.base64,
|
8
|
+
'random_hex' => SecureRandom.hex,
|
9
|
+
'random_bytes' => SecureRandom.random_bytes,
|
10
|
+
'random_number_10' => SecureRandom.random_number(10),
|
11
|
+
'random_number_100' => SecureRandom.random_number(100),
|
12
|
+
'random_number_1000' => SecureRandom.random_number(1000),
|
13
|
+
'random_urlsafe_base64' => SecureRandom.urlsafe_base64,
|
14
|
+
'random_uuid' => SecureRandom.uuid
|
17
15
|
}
|
18
16
|
end
|
19
|
-
|
20
17
|
end
|
data/lib/tiller/datasource.rb
CHANGED
@@ -1,20 +1,20 @@
|
|
1
1
|
# Tiller data source base class.
|
2
|
-
#
|
3
|
-
# Subclasses provide global_values and/or values (things local to a specific template) and target_values (meta data
|
4
|
-
# about a template, e.g. target location, permissions, owner and so on)
|
5
|
-
|
6
2
|
module Tiller
|
3
|
+
# Subclasses provide global_values and/or values (things local to a specific
|
4
|
+
# template) and target_values (meta data about a template, e.g. target
|
5
|
+
# location, permissions, owner and so on)
|
7
6
|
class DataSource
|
8
|
-
|
9
|
-
#
|
10
|
-
|
7
|
+
# All subclasses get this, which is a hash containing tiller_base,
|
8
|
+
# tiller_lib and environment.
|
9
|
+
@config = Array.new
|
11
10
|
|
12
11
|
def initialize(config)
|
13
|
-
|
14
|
-
|
12
|
+
@config = config
|
13
|
+
setup
|
15
14
|
end
|
16
15
|
|
17
|
-
# This is where any post-initialisation logic happens
|
16
|
+
# This is where any post-initialisation logic happens
|
17
|
+
# (connecting to a database etc.)
|
18
18
|
def setup
|
19
19
|
end
|
20
20
|
|
@@ -22,26 +22,27 @@ module Tiller
|
|
22
22
|
Hash.new
|
23
23
|
end
|
24
24
|
|
25
|
-
# We should always return a hash; if we have no data for the given
|
26
|
-
|
25
|
+
# We should always return a hash; if we have no data for the given
|
26
|
+
# template, just return an empty hash.
|
27
|
+
def values(_template_name)
|
27
28
|
Hash.new
|
28
29
|
end
|
29
30
|
|
30
31
|
# This should provide a hash similar to this example :
|
31
|
-
#{
|
32
|
+
# {
|
32
33
|
# 'target' => "/tmp/#{template_name}",
|
33
34
|
# 'user' => 'root',
|
34
35
|
# 'group' => 'root',
|
35
36
|
# 'perms' => '0644'
|
36
|
-
#}
|
37
|
-
# Again, we should always return a hash; if we have no data for the given
|
38
|
-
|
37
|
+
# }
|
38
|
+
# Again, we should always return a hash; if we have no data for the given
|
39
|
+
# template, just return an empty hash.
|
40
|
+
def target_values(_template_name)
|
39
41
|
Hash.new
|
40
42
|
end
|
41
43
|
|
42
44
|
def ping
|
43
|
-
|
45
|
+
'ping!' + @config.to_s
|
44
46
|
end
|
45
|
-
|
46
47
|
end
|
47
48
|
end
|
data/lib/tiller/template/file.rb
CHANGED
@@ -1,15 +1,14 @@
|
|
1
|
-
# File template datasource for Tiller. This works the same way that Runner.rb
|
2
|
-
# present under /etc/tiller/templates
|
3
|
-
|
1
|
+
# File template datasource for Tiller. This works the same way that Runner.rb
|
2
|
+
# used to - it returns templates files present under /etc/tiller/templates
|
3
|
+
# (or wherever the tiller_base environment is set).
|
4
4
|
class FileTemplateSource < Tiller::TemplateSource
|
5
|
-
|
6
5
|
def initialize(config)
|
7
6
|
super
|
8
|
-
@template_dir = File.join(
|
7
|
+
@template_dir = File.join(@config[:tiller_base], 'templates/')
|
9
8
|
end
|
10
9
|
|
11
|
-
# Simply return a list of all the templates in the $tiller_base/templates
|
12
|
-
#
|
10
|
+
# Simply return a list of all the templates in the $tiller_base/templates
|
11
|
+
# directory with the preceeding directory path stripped off
|
13
12
|
def templates
|
14
13
|
Dir.glob(File.join(@template_dir, '**', '*.erb')).each do |t|
|
15
14
|
t.sub!(@template_dir, '')
|
@@ -20,6 +19,4 @@ class FileTemplateSource < Tiller::TemplateSource
|
|
20
19
|
def template(template_name)
|
21
20
|
open(File.join(@template_dir, template_name)).read
|
22
21
|
end
|
23
|
-
|
24
22
|
end
|
25
|
-
|
@@ -1,17 +1,18 @@
|
|
1
1
|
# Tiller template source base class
|
2
|
-
# Subclasses provide templates (an array), and individual template contents (a string containing ERB data)
|
3
|
-
|
4
2
|
module Tiller
|
3
|
+
# Subclasses provide templates (an array), and individual template contents
|
4
|
+
# (a string containing ERB data)
|
5
5
|
class TemplateSource
|
6
|
-
|
7
|
-
#
|
8
|
-
|
6
|
+
# All subclasses get this, which is a hash containing tiller_base,
|
7
|
+
# tiller_lib and environment.
|
8
|
+
@config = Array.new
|
9
9
|
|
10
10
|
def initialize(config)
|
11
|
-
|
11
|
+
@config = config
|
12
12
|
end
|
13
13
|
|
14
|
-
# This is where any post-initialisation logic happens
|
14
|
+
# This is where any post-initialisation logic happens
|
15
|
+
# (connecting to a database etc.)
|
15
16
|
def setup
|
16
17
|
end
|
17
18
|
|
@@ -24,7 +25,7 @@ module Tiller
|
|
24
25
|
end
|
25
26
|
|
26
27
|
def ping
|
27
|
-
|
28
|
+
'ping!' + @config.to_s
|
28
29
|
end
|
29
30
|
end
|
30
31
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: tiller
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Mark Round
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-08-
|
11
|
+
date: 2014-08-20 00:00:00.000000000 Z
|
12
12
|
dependencies: []
|
13
13
|
description: A tool to create configuration files in Docker containers from a variety
|
14
14
|
of sources. See https://github.com/markround/tiller for examples and documentation.
|