tiller 0.0.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 +7 -0
- data/bin/tiller +138 -0
- data/examples/etc/tiller/common.yaml +36 -0
- data/examples/etc/tiller/environments/production.yaml +22 -0
- data/examples/etc/tiller/environments/staging.yaml +24 -0
- data/examples/etc/tiller/templates/sensu_client.erb +8 -0
- data/examples/lib/tiller/data/dummy.rb +30 -0
- data/examples/lib/tiller/data/network.rb +19 -0
- data/examples/lib/tiller/template/dummy.rb +18 -0
- data/lib/tiller/data/file.rb +24 -0
- data/lib/tiller/datasource.rb +39 -0
- data/lib/tiller/template/file.rb +25 -0
- data/lib/tiller/templatesource.rb +26 -0
- metadata +58 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: c984c96a5012b983e926379da35c40b3cb993918
|
4
|
+
data.tar.gz: 2cd4ae2955051ed74ec95721da45c2e7866b1641
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 59028394e8ea01c10541d58c5f4896cbce8459210f2150017205280a6a9d811b9e471cd07b6d95051ded1c81bae1ae54ce304b23502a99d865159e43293878af
|
7
|
+
data.tar.gz: 05a6142da60107e38aa00120f166c051d0f82b9c72150e2b8a88a07041efad6776f4f4f68c8c04633f29b1b3760055fdb95be4bae3cee6e3d78f05987c3244af
|
data/bin/tiller
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# Tiller - Dynamic configuration generator, intended for use in Dockerfiles
|
3
|
+
# Named from the first ship-building (Docker) related term I could find that didn't have an existing gem named after it!
|
4
|
+
# Mark Round <github@markround.com>
|
5
|
+
|
6
|
+
VERSION='0.0.2'
|
7
|
+
|
8
|
+
require 'erb'
|
9
|
+
require 'ostruct'
|
10
|
+
require 'yaml'
|
11
|
+
require 'fileutils'
|
12
|
+
require 'pp'
|
13
|
+
|
14
|
+
# This is needed so we can enumarate all the loaded plugins later
|
15
|
+
class Class
|
16
|
+
def subclasses
|
17
|
+
ObjectSpace.each_object(Class).select { |c| c < self }
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def warn_merge(key, old, new, type, source)
|
22
|
+
puts "Warning, merging duplicate #{type} values."
|
23
|
+
puts "#{key} => '#{old}' being replaced by : '#{new}' from #{source}"
|
24
|
+
new
|
25
|
+
end
|
26
|
+
|
27
|
+
module Tiller
|
28
|
+
|
29
|
+
# Set these two environment variables if you want to debug a configuration in a temporary directory.
|
30
|
+
# EG: $ tiller_base=/tmp tiller_lib=/tmp/lib ./tiller
|
31
|
+
config = {
|
32
|
+
:tiller_base => (ENV['tiller_base'].nil?) ? '/etc/tiller' : ENV['tiller_base'],
|
33
|
+
:tiller_lib => (ENV['tiller_lib'].nil?) ? '/usr/local/lib' : ENV['tiller_lib'],
|
34
|
+
# This is the main variable, usually the only one you pass into Docker.
|
35
|
+
:environment => (ENV['environment'].nil?) ? 'production' : ENV['environment']
|
36
|
+
}
|
37
|
+
|
38
|
+
$LOAD_PATH.unshift(config[:tiller_lib]) unless $LOAD_PATH.include?(config[:tiller_lib])
|
39
|
+
|
40
|
+
# User-provided sources.
|
41
|
+
require 'tiller/templatesource.rb'
|
42
|
+
require 'tiller/datasource.rb'
|
43
|
+
|
44
|
+
# Load the common YAML configuration file
|
45
|
+
config[:common_config] = YAML::load(open(File.join(config[:tiller_base], 'common.yaml')))
|
46
|
+
|
47
|
+
puts "tiller v#{VERSION} (https://github.com/markround/tiller) <github@markround.com>"
|
48
|
+
puts "Using configuration from #{config[:tiller_base]}"
|
49
|
+
puts "Using plugins from #{config[:tiller_lib]}/tiller"
|
50
|
+
puts "Using environment #{config[:environment]}"
|
51
|
+
|
52
|
+
# Now load all our plugins
|
53
|
+
template_sources = Array.new
|
54
|
+
data_sources = Array.new
|
55
|
+
template_sources |= config[:common_config]['template_sources']
|
56
|
+
data_sources |= config[:common_config]['data_sources']
|
57
|
+
template_sources.each { |t| require "tiller/template/#{t}.rb" }
|
58
|
+
data_sources.each { |t| require "tiller/data/#{t}.rb" }
|
59
|
+
|
60
|
+
puts 'Template sources loaded ' + TemplateSource.subclasses.to_s
|
61
|
+
puts 'Data sources loaded ' + DataSource.subclasses.to_s
|
62
|
+
|
63
|
+
# Get all Templates for the given environment
|
64
|
+
templates = {}
|
65
|
+
TemplateSource.subclasses.each do |template_class|
|
66
|
+
ts = template_class.new(config)
|
67
|
+
ts.templates.each do |t|
|
68
|
+
templates[t]=ts.template(t)
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
puts "Templates to build #{templates.keys.to_s}"
|
73
|
+
|
74
|
+
# Now go through all our data sources and start to assemble our "tiller_global" hash.
|
75
|
+
# As hashes are getting merged, new values will take precedence over older ones, and a warning will be displayed.
|
76
|
+
# Add in environment ti start with as it's very useful for all templates.
|
77
|
+
global_values = {'environment' => config[:environment]}
|
78
|
+
DataSource.subclasses.each do |data_class|
|
79
|
+
global_values.merge!(data_class.new(config).global_values) do |key, old, new|
|
80
|
+
warn_merge(key, old, new, 'global', data_class.to_s)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
# Now we go through each template we've identified, and get the values for each one.
|
85
|
+
templates.each do |template, content|
|
86
|
+
values = Hash.new
|
87
|
+
target_values = Hash.new
|
88
|
+
puts "Building template #{template}"
|
89
|
+
|
90
|
+
# Now we populate the hash with values from each DataSource, warning if we get duplicate values.
|
91
|
+
DataSource.subclasses.each do |data_class|
|
92
|
+
dc = data_class.new(config)
|
93
|
+
values.merge!(dc.values(template)) do |key, old, new|
|
94
|
+
warn_merge(key, old, new, 'data', data_class.to_s)
|
95
|
+
end
|
96
|
+
|
97
|
+
# Now get target_values (where the file should be installed to, permissions and so on)
|
98
|
+
target_values.merge!(dc.target_values(template)) do |key, old, new|
|
99
|
+
warn_merge(key, old, new, 'target', data_class.to_s)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
103
|
+
# Now, we build the template
|
104
|
+
tiller = values.merge(global_values) do |key, old, new|
|
105
|
+
warn_merge(key, old, new, 'global and local', 'merged configuration')
|
106
|
+
end
|
107
|
+
|
108
|
+
# Use an OpenStruct namespace, as it's way easier than faffing around with manual binding, and also
|
109
|
+
# non-existing values just get replaced by <nil> instead of failing on errors.
|
110
|
+
ns = OpenStruct.new(tiller)
|
111
|
+
parsed_template = ERB.new(content).result(ns.instance_eval { binding })
|
112
|
+
|
113
|
+
# Write the template, and also create the directory path if it doesn't exist.
|
114
|
+
target_path = File.dirname(target_values['target'])
|
115
|
+
FileUtils.mkdir_p(target_path) if not File.directory?(target_path)
|
116
|
+
target = open(target_values['target'], "w")
|
117
|
+
target.puts(parsed_template)
|
118
|
+
target.close
|
119
|
+
|
120
|
+
# Set permissions if we are running as root
|
121
|
+
if Process::Sys.geteuid == 0 then
|
122
|
+
puts "Setting ownership/permissions on #{target_values['target']}"
|
123
|
+
FileUtils.chmod(target_values['perms'], target_values['target']) if target_values.has_key('perms')
|
124
|
+
FileUtils.chown(target_values['user'], target_values['group'], target_values['target'])
|
125
|
+
else
|
126
|
+
puts "Not running as root, so not setting ownership/permissions on #{target_values['target']}"
|
127
|
+
end
|
128
|
+
|
129
|
+
end
|
130
|
+
|
131
|
+
# All templates created, so let's handover to the replacement process then it's home in time for tea and medals.
|
132
|
+
puts "Template generation completed, about to exec replacement process."
|
133
|
+
puts "Calling #{config[:common_config]['exec']}..."
|
134
|
+
exec(config[:common_config]['exec'])
|
135
|
+
|
136
|
+
end
|
137
|
+
|
138
|
+
|
@@ -0,0 +1,36 @@
|
|
1
|
+
# Example configuration file, demonstrating how to use Tiller.
|
2
|
+
#
|
3
|
+
# These examples create two templates with different values depending on if you set the environment to
|
4
|
+
# "production" or "staging".
|
5
|
+
#
|
6
|
+
# This would be called with the following environment variables set (e.g. from a bash prompt) :
|
7
|
+
#
|
8
|
+
# tiller_base=$PWD/examples/etc/tiller tiller_lib=$PWD/examples/lib tiller
|
9
|
+
#
|
10
|
+
# (Tiller uses the "production" environment if none is set). Or, for staging:
|
11
|
+
#
|
12
|
+
# environment=staging tiller_base=$PWD/examples/etc/tiller tiller_lib=$PWD/examples/lib tiller
|
13
|
+
#
|
14
|
+
# See the documentation for more details on how to use this in a Dockerfile.
|
15
|
+
|
16
|
+
# exec: The executable to handover to after execution. In a Dockerfile, this would probably be /usr/bin/supervisord
|
17
|
+
# Or some other daemon. For testing, /usr/bin/date (or similar) is a useful one-shot command that just exits,
|
18
|
+
# so you can inspect your templates.
|
19
|
+
exec: date
|
20
|
+
|
21
|
+
# data_sources: Sources of data. Here, we're pulling in the provided FileDataSource (ships with this gem), and also the
|
22
|
+
# NetworkDataSource under examples/lib/tiller. This is found because we set tiller_lib when we called the tiller
|
23
|
+
# executable. We're not pulling in the example DummyDataSource, because it also provides target_values, so would
|
24
|
+
# overwrite the target_values from FileDataSource - you should only have one DataSource providing this information,
|
25
|
+
# otherwise your templates may end up in strange places you didn't expect!
|
26
|
+
data_sources:
|
27
|
+
- file
|
28
|
+
- network
|
29
|
+
|
30
|
+
|
31
|
+
# template_sources: Sources of templates, in priority order (first takes precendence). Again using the provided
|
32
|
+
# FileTemplateSource (uses templates under <tiller_base>/templates), and also because we set tiller_lib, we can use
|
33
|
+
# DummyTemplateSource which creates a single "dummy.erb" template.
|
34
|
+
template_sources:
|
35
|
+
- file
|
36
|
+
- dummy
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# Here is an example configuration file for your production environment.
|
2
|
+
|
3
|
+
# First, we define the values for an example sensu_client template :
|
4
|
+
sensu_client.erb:
|
5
|
+
target: /etc/sensu/conf.d/client.json
|
6
|
+
config:
|
7
|
+
sensu_host: 'sensu.example.com'
|
8
|
+
sensu_subscriptions:
|
9
|
+
- common
|
10
|
+
- physical
|
11
|
+
- mongo
|
12
|
+
|
13
|
+
# This is a dummy template provided by the DummyTemplateSource. You'll notice that because we're not pulling in
|
14
|
+
# DummyDataSource, the dummy_global variable is not set, but the template is still built without an error (the
|
15
|
+
# value is just left blank).
|
16
|
+
dummy.erb:
|
17
|
+
target: /tmp/files/dummy.txt
|
18
|
+
owner: root
|
19
|
+
group: wheel
|
20
|
+
perms: 0644
|
21
|
+
config:
|
22
|
+
dummy: 'Text from a filesource - production environment'
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# Here is an example configuration file for your staging environment.
|
2
|
+
|
3
|
+
# First, we define the values for an example sensu_client template. In this example, we changed to including a "virtual"
|
4
|
+
# subscription instead of "physical", which was set in the production environment.
|
5
|
+
sensu_client.erb:
|
6
|
+
target: /etc/sensu/conf.d/client.json
|
7
|
+
config:
|
8
|
+
sensu_host: 'sensu.example.com'
|
9
|
+
sensu_subscriptions:
|
10
|
+
- common
|
11
|
+
- virtual
|
12
|
+
- mongo
|
13
|
+
|
14
|
+
# This is a dummy template provided by the DummyTemplateSource. You'll notice that because we're not pulling in
|
15
|
+
# DummyDataSource, the dummy_global variable is not set, but the template is still built without an error (the
|
16
|
+
# value is just left blank). Here we also write the file to a different location that the one in the production
|
17
|
+
# environment.
|
18
|
+
dummy.erb:
|
19
|
+
target: /tmp/files/dummy-staging.txt
|
20
|
+
owner: root
|
21
|
+
group: wheel
|
22
|
+
perms: 0644
|
23
|
+
config:
|
24
|
+
dummy: 'Text from a filesource - staging environment'
|
@@ -0,0 +1,30 @@
|
|
1
|
+
# This is a "dummy" datasource for Tiller. All it does is provide a single global and local value,
|
2
|
+
# And shows how you can provide a hash of target values to configure where templates are written.
|
3
|
+
|
4
|
+
class DummyDataSource < Tiller::DataSource
|
5
|
+
|
6
|
+
def initialize(config)
|
7
|
+
super
|
8
|
+
@global_values = {
|
9
|
+
'dummy_global' => 'dummy global replacement text'
|
10
|
+
}
|
11
|
+
end
|
12
|
+
|
13
|
+
def values(template_name)
|
14
|
+
{ 'dummy' => 'dummy replacement text' }
|
15
|
+
end
|
16
|
+
|
17
|
+
# Dummy values, useful for testing. Will just result in the template being built and written to /tmp/dummy.
|
18
|
+
# Remember that perms must be in octal (no quotes).
|
19
|
+
# Note that as you have the environment name in the @@config hash, you can always return different values
|
20
|
+
# for different environments.
|
21
|
+
def target_values(template_name)
|
22
|
+
{
|
23
|
+
'target' => "/tmp/dummy/#{template_name}",
|
24
|
+
'user' => 'root',
|
25
|
+
'group' => 'root',
|
26
|
+
'perms' => 0644
|
27
|
+
}
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'socket'
|
2
|
+
|
3
|
+
# This is a quick example of a global datasource for Tiller. It shows how you might provide some basic
|
4
|
+
# network-related information to your 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 your templates.
|
6
|
+
|
7
|
+
class NetworkDataSource < Tiller::DataSource
|
8
|
+
|
9
|
+
def initialize(config)
|
10
|
+
super
|
11
|
+
# Note, these rely on DNS being available and configured correctly!
|
12
|
+
@global_values = {
|
13
|
+
'fqdn' => Socket.gethostbyname(Socket.gethostname).first,
|
14
|
+
'ipv4_addresses' => Socket.getaddrinfo(Socket.gethostbyname(Socket.gethostname).first, nil, :INET)
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
|
19
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# This is another quick example of how to create a template source.
|
2
|
+
|
3
|
+
class DummyTemplateSource < Tiller::TemplateSource
|
4
|
+
|
5
|
+
# Just provide a single dummy template
|
6
|
+
def templates
|
7
|
+
["dummy.erb"]
|
8
|
+
end
|
9
|
+
|
10
|
+
# And return some sample ERB content.
|
11
|
+
# Note that as you have the environment name in the @@config hash, you can always return different templates
|
12
|
+
# for different environments.
|
13
|
+
def template(template_name)
|
14
|
+
"This is a dummy template! <%= dummy %> \n Here is a global value <%= dummy_global %>"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
|
@@ -0,0 +1,24 @@
|
|
1
|
+
# File datasource for Tiller. This works the same way as the default behaviour in Runner.rb - it loads your
|
2
|
+
# <environment>.yaml file and provides data from it. See examples/etc/tiller/environments/production.yaml to see
|
3
|
+
# what this file looks like.
|
4
|
+
|
5
|
+
require 'yaml'
|
6
|
+
|
7
|
+
class FileDataSource < Tiller::DataSource
|
8
|
+
|
9
|
+
# We don't provide any global values, just ones specific to a template.
|
10
|
+
def initialize(config)
|
11
|
+
super
|
12
|
+
env_file = File.join(@@config[:tiller_base] , "environments" , "#{@@config[:environment]}.yaml")
|
13
|
+
@config_hash = YAML::load(open(env_file))
|
14
|
+
end
|
15
|
+
|
16
|
+
def values(template_name)
|
17
|
+
@config_hash[template_name]['config']
|
18
|
+
end
|
19
|
+
|
20
|
+
def target_values(template_name)
|
21
|
+
@config_hash[template_name]
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
@@ -0,0 +1,39 @@
|
|
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
|
+
module Tiller
|
7
|
+
class DataSource
|
8
|
+
|
9
|
+
# All subclasses get this, which is a hash containing tiller_base, tiller_lib and environment.
|
10
|
+
@@config = Array.new
|
11
|
+
|
12
|
+
def initialize(config)
|
13
|
+
@@config = config
|
14
|
+
@global_values = Hash.new
|
15
|
+
end
|
16
|
+
|
17
|
+
attr_reader :global_values
|
18
|
+
|
19
|
+
def values(template_name)
|
20
|
+
Hash.new
|
21
|
+
end
|
22
|
+
|
23
|
+
# This should provide a hash similar to this example :
|
24
|
+
#{
|
25
|
+
# 'target' => "/tmp/#{template_name}",
|
26
|
+
# 'user' => 'root',
|
27
|
+
# 'group' => 'root',
|
28
|
+
# 'perms' => '0644'
|
29
|
+
#}
|
30
|
+
def target_values(template_name)
|
31
|
+
Hash.new
|
32
|
+
end
|
33
|
+
|
34
|
+
def ping
|
35
|
+
"ping!" + @@config.to_s
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
# File template datasource for Tiller. This works the same way that Runner.rb used to - it returns templates files
|
2
|
+
# present under /etc/tiller/templates (or wherever the tiller_base environment is set).
|
3
|
+
|
4
|
+
class FileTemplateSource < Tiller::TemplateSource
|
5
|
+
|
6
|
+
def initialize(config)
|
7
|
+
super
|
8
|
+
@template_dir = File.join(@@config[:tiller_base] , 'templates/')
|
9
|
+
end
|
10
|
+
|
11
|
+
# Simply return a list of all the templates in the $tiller_base/templates directory
|
12
|
+
# With the preceeding directory path stripped off
|
13
|
+
def templates
|
14
|
+
Dir.glob(File.join(@template_dir , '**' , '*.erb')).each do |t|
|
15
|
+
t.sub!(@template_dir , '')
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Just open and return the file
|
20
|
+
def template(template_name)
|
21
|
+
open(File.join(@template_dir , template_name)).read
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
|
@@ -0,0 +1,26 @@
|
|
1
|
+
# Tiller template source base class
|
2
|
+
# Subclasses provide templates (an array), and individual template contents (a string containing ERB data)
|
3
|
+
|
4
|
+
module Tiller
|
5
|
+
class TemplateSource
|
6
|
+
|
7
|
+
# All subclasses get this, which is a hash containing tiller_base, tiller_lib and environment.
|
8
|
+
@@config = Array.new
|
9
|
+
|
10
|
+
def initialize(config)
|
11
|
+
@@config = config
|
12
|
+
end
|
13
|
+
|
14
|
+
def templates
|
15
|
+
Hash.new
|
16
|
+
end
|
17
|
+
|
18
|
+
def template
|
19
|
+
String.new
|
20
|
+
end
|
21
|
+
|
22
|
+
def ping
|
23
|
+
"ping!" + @@config.to_s
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
metadata
ADDED
@@ -0,0 +1,58 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tiller
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.2
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mark Round
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2014-07-18 00:00:00.000000000 Z
|
12
|
+
dependencies: []
|
13
|
+
description: A tool to create configuration files in Docker containers from a variety
|
14
|
+
of sources
|
15
|
+
email: github@markround.com
|
16
|
+
executables:
|
17
|
+
- tiller
|
18
|
+
extensions: []
|
19
|
+
extra_rdoc_files: []
|
20
|
+
files:
|
21
|
+
- bin/tiller
|
22
|
+
- examples/etc/tiller/common.yaml
|
23
|
+
- examples/etc/tiller/environments/production.yaml
|
24
|
+
- examples/etc/tiller/environments/staging.yaml
|
25
|
+
- examples/etc/tiller/templates/sensu_client.erb
|
26
|
+
- examples/lib/tiller/data/dummy.rb
|
27
|
+
- examples/lib/tiller/data/network.rb
|
28
|
+
- examples/lib/tiller/template/dummy.rb
|
29
|
+
- lib/tiller/data/file.rb
|
30
|
+
- lib/tiller/datasource.rb
|
31
|
+
- lib/tiller/template/file.rb
|
32
|
+
- lib/tiller/templatesource.rb
|
33
|
+
homepage: http://www.markround.com
|
34
|
+
licenses:
|
35
|
+
- MIT
|
36
|
+
metadata:
|
37
|
+
source: https://github.com/markround/tiller
|
38
|
+
post_install_message:
|
39
|
+
rdoc_options: []
|
40
|
+
require_paths:
|
41
|
+
- lib
|
42
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
43
|
+
requirements:
|
44
|
+
- - ">="
|
45
|
+
- !ruby/object:Gem::Version
|
46
|
+
version: '0'
|
47
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
48
|
+
requirements:
|
49
|
+
- - ">="
|
50
|
+
- !ruby/object:Gem::Version
|
51
|
+
version: '0'
|
52
|
+
requirements: []
|
53
|
+
rubyforge_project:
|
54
|
+
rubygems_version: 2.2.2
|
55
|
+
signing_key:
|
56
|
+
specification_version: 4
|
57
|
+
summary: Dynamic configuration generation for Docker
|
58
|
+
test_files: []
|