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 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,8 @@
1
+ {
2
+ "client": {
3
+ "name": "<%= fqdn %>",
4
+ "address": "<%= ipv4_addresses[0][3] %>",
5
+ "subscriptions": <%= sensu_subscriptions %>,
6
+ "safe_mode": false
7
+ }
8
+ }
@@ -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: []