tiller 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
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: []