stork 0.1.0.pre

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.
Files changed (103) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +21 -0
  3. data/.ruby-gemset +1 -0
  4. data/.ruby-version +1 -0
  5. data/.travis.yml +10 -0
  6. data/Gemfile +4 -0
  7. data/Gemfile.lock +75 -0
  8. data/LICENSE +201 -0
  9. data/README.md +61 -0
  10. data/Rakefile +25 -0
  11. data/bin/stork +97 -0
  12. data/bin/storkctl +62 -0
  13. data/lib/stork/builder.rb +104 -0
  14. data/lib/stork/client/plugins/host_install.rb +19 -0
  15. data/lib/stork/client/plugins/host_list.rb +12 -0
  16. data/lib/stork/client/plugins/host_localboot.rb +19 -0
  17. data/lib/stork/client/plugins/host_show.rb +19 -0
  18. data/lib/stork/collection/base.rb +45 -0
  19. data/lib/stork/collection/chefs.rb +6 -0
  20. data/lib/stork/collection/distros.rb +6 -0
  21. data/lib/stork/collection/hosts.rb +6 -0
  22. data/lib/stork/collection/layouts.rb +6 -0
  23. data/lib/stork/collection/networks.rb +6 -0
  24. data/lib/stork/collection/snippets.rb +6 -0
  25. data/lib/stork/collection/templates.rb +6 -0
  26. data/lib/stork/collections.rb +38 -0
  27. data/lib/stork/configuration.rb +98 -0
  28. data/lib/stork/deploy/command.rb +59 -0
  29. data/lib/stork/deploy/install_script.rb +39 -0
  30. data/lib/stork/deploy/kickstart_binding.rb +222 -0
  31. data/lib/stork/deploy/section.rb +30 -0
  32. data/lib/stork/deploy/snippet_binding.rb +49 -0
  33. data/lib/stork/plugin.rb +54 -0
  34. data/lib/stork/pxe.rb +70 -0
  35. data/lib/stork/resource/base.rb +42 -0
  36. data/lib/stork/resource/chef.rb +81 -0
  37. data/lib/stork/resource/delegator.rb +50 -0
  38. data/lib/stork/resource/distro.rb +30 -0
  39. data/lib/stork/resource/firewall.rb +47 -0
  40. data/lib/stork/resource/host.rb +202 -0
  41. data/lib/stork/resource/interface.rb +125 -0
  42. data/lib/stork/resource/layout.rb +50 -0
  43. data/lib/stork/resource/logical_volume.rb +49 -0
  44. data/lib/stork/resource/network.rb +36 -0
  45. data/lib/stork/resource/partition.rb +47 -0
  46. data/lib/stork/resource/password.rb +26 -0
  47. data/lib/stork/resource/repo.rb +29 -0
  48. data/lib/stork/resource/snippet.rb +20 -0
  49. data/lib/stork/resource/template.rb +24 -0
  50. data/lib/stork/resource/timezone.rb +31 -0
  51. data/lib/stork/resource/volume_group.rb +31 -0
  52. data/lib/stork/resources.rb +17 -0
  53. data/lib/stork/server/application.rb +127 -0
  54. data/lib/stork/server/control.rb +38 -0
  55. data/lib/stork/version.rb +4 -0
  56. data/lib/stork.rb +30 -0
  57. data/specs/builder_spec.rb +14 -0
  58. data/specs/collections_spec.rb +72 -0
  59. data/specs/configuration_spec.rb +151 -0
  60. data/specs/keys/snakeoil-root.pem +27 -0
  61. data/specs/keys/snakeoil-validation.pem +27 -0
  62. data/specs/kickstart_spec.rb +36 -0
  63. data/specs/pxe_spec.rb +57 -0
  64. data/specs/resource_chef_spec.rb +122 -0
  65. data/specs/resource_distro_spec.rb +38 -0
  66. data/specs/resource_firewall_spec.rb +71 -0
  67. data/specs/resource_host_spec.rb +323 -0
  68. data/specs/resource_interface_spec.rb +158 -0
  69. data/specs/resource_layout_spec.rb +78 -0
  70. data/specs/resource_logical_volume_spec.rb +44 -0
  71. data/specs/resource_network_spec.rb +45 -0
  72. data/specs/resource_partition_spec.rb +51 -0
  73. data/specs/resource_password_spec.rb +10 -0
  74. data/specs/resource_repo_spec.rb +43 -0
  75. data/specs/resource_snippet_spec.rb +10 -0
  76. data/specs/resource_template_spec.rb +5 -0
  77. data/specs/resource_timezone_spec.rb +5 -0
  78. data/specs/resource_volume_group_spec.rb +23 -0
  79. data/specs/scripts/kssetup.sh +24 -0
  80. data/specs/scripts/ksvalidate.sh +21 -0
  81. data/specs/server_spec.rb +84 -0
  82. data/specs/spec_helper.rb +33 -0
  83. data/specs/stork/authorized_keys +1 -0
  84. data/specs/stork/bundles/chefs/default.rb +9 -0
  85. data/specs/stork/bundles/distros/centos.rb +5 -0
  86. data/specs/stork/bundles/hosts/example.org.rb +66 -0
  87. data/specs/stork/bundles/layouts/home.rb +33 -0
  88. data/specs/stork/bundles/networks/local.rb +6 -0
  89. data/specs/stork/bundles/networks/org.rb +7 -0
  90. data/specs/stork/bundles/snippets/authorized-keys.erb +10 -0
  91. data/specs/stork/bundles/snippets/chef-bootstrap.erb +104 -0
  92. data/specs/stork/bundles/snippets/chef-reconfigure.erb +22 -0
  93. data/specs/stork/bundles/snippets/etc-hosts.erb +6 -0
  94. data/specs/stork/bundles/snippets/network-config.erb +7 -0
  95. data/specs/stork/bundles/snippets/noop.erb +1 -0
  96. data/specs/stork/bundles/snippets/notify.erb +2 -0
  97. data/specs/stork/bundles/snippets/ntp.erb +2 -0
  98. data/specs/stork/bundles/snippets/resolv-conf.erb +6 -0
  99. data/specs/stork/bundles/snippets/setup.erb +1 -0
  100. data/specs/stork/bundles/templates/default.ks.erb +25 -0
  101. data/specs/stork/config.rb +11 -0
  102. data/stork.gemspec +35 -0
  103. metadata +329 -0
@@ -0,0 +1,49 @@
1
+ module Stork
2
+ module Deploy
3
+ class SnippetBinding
4
+ attr_reader :host
5
+
6
+ def initialize(configuration, host)
7
+ @host = host
8
+ @configuration = configuration
9
+ end
10
+
11
+ def get_binding
12
+ binding
13
+ end
14
+
15
+ def chef
16
+ host.chef
17
+ end
18
+
19
+ def authorized_keys
20
+ File.read(@configuration.authorized_keys_file)
21
+ end
22
+
23
+ def first_boot_content
24
+ run_list = { 'run_list' => host.run_list }
25
+ run_list.to_json
26
+ end
27
+
28
+ def nameservers
29
+ host.interfaces.map { |x| x.nameservers }.uniq.flatten
30
+ end
31
+
32
+ def search_paths
33
+ host.interfaces.map { |x| x.search_paths }.uniq.flatten
34
+ end
35
+
36
+ def midwife_server
37
+ @configuration.server
38
+ end
39
+
40
+ def midwife_port
41
+ @configuration.port
42
+ end
43
+
44
+ def midwife_bind
45
+ @configuration.bind
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,54 @@
1
+ require 'rest-client'
2
+ require 'highline/import'
3
+
4
+ module Stork
5
+ def self.plugins
6
+ @plugins || []
7
+ end
8
+
9
+ def self.add_plugin(plugin)
10
+ plugins << plugin
11
+ end
12
+
13
+ class Plugin
14
+ attr_reader :options
15
+ attr_reader :args
16
+ attr_reader :stork
17
+
18
+ def self.banner(str)
19
+ @banner = str
20
+ end
21
+
22
+ def self.help
23
+ @banner
24
+ end
25
+
26
+ def initialize(configuration, options, args)
27
+ @action = nil
28
+ @configuration = configuration
29
+ @options = options
30
+ @args = args
31
+ @stork = "http://#{configuration.server}:#{configuration.port}"
32
+ end
33
+
34
+ def fetch(path)
35
+ response = RestClient.get "#{@stork}/api/v1/#{path}"
36
+ data = JSON.parse(response)
37
+ end
38
+
39
+ def show(key, data, options = {})
40
+ padding = options.has_key?('pad') ? options['pad'] : 16
41
+ name = key.split('_').map{|k| k.capitalize}.join(' ').ljust(padding)
42
+ if data[key].is_a?(Array)
43
+ value = data[key].join(', ')
44
+ else
45
+ value = data[key]
46
+ end
47
+ say("<%= color('#{name}:', CYAN) %> #{value}")
48
+ end
49
+
50
+ def run
51
+ puts "No content"
52
+ end
53
+ end
54
+ end
data/lib/stork/pxe.rb ADDED
@@ -0,0 +1,70 @@
1
+ module Stork
2
+ class PXE
3
+ attr_reader :initrd, :kernel, :kickstart, :path, :hostname, :mac
4
+
5
+ # def initialize(server, path, hostname, mac, kernel, initrd)
6
+ # @host = hostname
7
+ # @initrd = initrd
8
+ # @kernel = kernel
9
+ # @path = path
10
+ # @mac = mac
11
+ # @kickstart = "http://#{server}/ks/#{hostname}"
12
+ # end
13
+
14
+ def initialize(host, server, port)
15
+ @hostname = host.name
16
+ @initrd = host.distro.image
17
+ @kernel = host.distro.kernel
18
+ @path = host.configuration.pxe_path
19
+ @mac = host.pxemac
20
+ @server = server
21
+ @port = port
22
+ @kickstart = "http://#{server}:#{port}/host/#{hostname}"
23
+ end
24
+
25
+ def localboot
26
+ write localboot_content
27
+ end
28
+
29
+ def install
30
+ write install_content
31
+ end
32
+
33
+ alias_method :default, :localboot
34
+
35
+ private
36
+ def install_content
37
+ <<-EOS
38
+ default install
39
+ prompt 0
40
+ timeout 1
41
+ label install
42
+ kernel #{kernel}
43
+ ipappend 2
44
+ append initrd=#{initrd} ksdevice=bootif priority=critical kssendmac ks=#{kickstart}
45
+ EOS
46
+ end
47
+
48
+ def localboot_content
49
+ <<-EOS
50
+ DEFAULT local
51
+ PROMPT 0
52
+ TIMEOUT 0
53
+ TOTALTIMEOUT 0
54
+ ONTIMEOUT local
55
+ LABEL local
56
+ LOCALBOOT -1
57
+ EOS
58
+ end
59
+
60
+ def write(str)
61
+ File.open("#{path}/#{pxefile}", 'w') do |f|
62
+ f.write(str)
63
+ end
64
+ end
65
+
66
+ def pxefile
67
+ '01-' + @mac.gsub(/[:]/, '-')
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,42 @@
1
+ module Stork
2
+ module Resource
3
+ class Base
4
+ attr_reader :name
5
+ attr_reader :options
6
+ attr_reader :configuration
7
+
8
+ def initialize(name = nil, options = {})
9
+ @name = name
10
+ @configuration = options[:configuration]
11
+ @options = options
12
+ setup
13
+ end
14
+
15
+ def setup
16
+ end
17
+
18
+ def validate!
19
+ end
20
+
21
+ def hashify
22
+ {'name' => @name}
23
+ end
24
+
25
+ def require_value(attr)
26
+ fail SyntaxError, "#{attr} is required" if send(attr).nil?
27
+ end
28
+
29
+ def self.build(name = nil, options = {}, &block)
30
+ inst = new(name, options)
31
+ klass_name = self.to_s.split('::').last
32
+ klass = const_get("#{klass_name}Delegator")
33
+ delegator = klass.new(inst, options)
34
+ delegator.instance_eval(&block) if block_given?
35
+ inst.validate!
36
+ inst
37
+ end
38
+
39
+ alias_method :id, :name
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,81 @@
1
+ module Stork
2
+ module Resource
3
+ class Chef < Base
4
+ attr_accessor :url
5
+ attr_accessor :version
6
+ attr_accessor :client_name
7
+ attr_accessor :validator_name
8
+ attr_accessor :client_key
9
+ attr_accessor :validation_key
10
+ attr_accessor :encrypted_data_bag_secret
11
+
12
+ def setup
13
+ @url = nil
14
+ @version = nil
15
+ @client_name = "root"
16
+ @validator_name = "chef-validator"
17
+ @validation_key = nil
18
+ @client_key = nil
19
+ @encrypted_data_bag_secret = nil
20
+ end
21
+
22
+ def validate!
23
+ # I should make sure that I'm at least getting the url,
24
+ # version, and the files.
25
+ require_value(:url)
26
+ require_value(:version)
27
+ require_value(:validation_key)
28
+ require_value(:client_key)
29
+ end
30
+
31
+ def client_content
32
+ <<-EOF
33
+ log_level :auto
34
+ log_location STDOUT
35
+ chef_server_url '#{url}'
36
+ validation_client_name '#{validator_name}'
37
+ # Using default node name (fqdn)
38
+ EOF
39
+ end
40
+
41
+ def knife_content
42
+ <<-EOF
43
+ log_level :info
44
+ log_location STDOUT
45
+ node_name '#{client_name}'
46
+ client_key '/root/.chef/#{client_name}.pem'
47
+ validation_client_name '#{validator_name}'
48
+ validation_key '/etc/chef/validation.pem'
49
+ chef_server_url '#{url}'
50
+ cache_type 'BasicFile'
51
+ cache_options( :path => '/root/.chef/checksums' )
52
+ EOF
53
+ end
54
+
55
+ class ChefDelegator < Stork::Resource::Delegator
56
+ file :client_key
57
+ file :validation_key
58
+
59
+ def url(url)
60
+ delegated.url = url
61
+ end
62
+
63
+ def version(version)
64
+ delegated.version = version
65
+ end
66
+
67
+ def client_name(client_name)
68
+ delegated.client_name = client_name
69
+ end
70
+
71
+ def validator_name(validator_name)
72
+ delegated.validator_name = validator_name
73
+ end
74
+
75
+ def encrypted_data_bag_secret(secret)
76
+ delegated.encrypted_data_bag_secret = secret
77
+ end
78
+ end
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,50 @@
1
+ module Stork
2
+ module Resource
3
+ class Delegator
4
+ attr_reader :collection
5
+ attr_reader :configuration
6
+ attr_reader :delegated
7
+
8
+ def initialize(inst, options)
9
+ @delegated = inst
10
+ @collection = options[:collection]
11
+ @configuration = options[:configuration]
12
+ end
13
+
14
+ def get_collection_item(name, value)
15
+ obj = collection.send(name).get(value)
16
+ raise SyntaxError, "Could not find #{value} in #{name}" unless obj
17
+ obj
18
+ end
19
+
20
+ def self.flag(name, options = {})
21
+ create_boolean_delegators(name, options)
22
+ end
23
+
24
+ def self.file(name, options = {})
25
+ create_file_delegators(name, options)
26
+ end
27
+
28
+ def self.create_boolean_delegators(name, options)
29
+ negate_name = options.has_key?(:negate) ? options[:negate] : "no#{name}"
30
+ self.class_eval <<-EOS, __FILE__, __LINE__
31
+ def #{name}
32
+ @delegated.#{name} = true
33
+ end
34
+
35
+ def #{negate_name}
36
+ @delegated.#{name} = false
37
+ end
38
+ EOS
39
+ end
40
+
41
+ def self.create_file_delegators(name, options)
42
+ self.class_eval <<-EOS, __FILE__, __LINE__
43
+ def #{name}(value)
44
+ @delegated.#{name} = ::File.read(value)
45
+ end
46
+ EOS
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,30 @@
1
+ module Stork
2
+ module Resource
3
+ class Distro < Base
4
+ attr_accessor :kernel
5
+ attr_accessor :image
6
+ attr_accessor :url
7
+
8
+ def hashify
9
+ attrs = {}
10
+ attrs['kernel'] = kernel
11
+ attrs['image'] = image
12
+ attrs['url'] = url
13
+ end
14
+
15
+ class DistroDelegator < Stork::Resource::Delegator
16
+ def kernel(kernel)
17
+ delegated.kernel = kernel
18
+ end
19
+
20
+ def image(image)
21
+ delegated.image = image
22
+ end
23
+
24
+ def url(url)
25
+ delegated.url = url
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,47 @@
1
+ module Stork
2
+ module Resource
3
+ class Firewall < Base
4
+ attr_accessor :enabled
5
+ attr_accessor :ssh
6
+ attr_accessor :telnet
7
+ attr_accessor :smtp
8
+ attr_accessor :http
9
+ attr_accessor :ftp
10
+ attr_accessor :trusted_devices
11
+ attr_accessor :allowed_ports
12
+
13
+ def setup
14
+ @enabled = true
15
+ @ssh = true
16
+ @telnet = false
17
+ @smtp = false
18
+ @http = false
19
+ @ftp = false
20
+ @trusted_devices = Array.new
21
+ @allowed_ports = Array.new
22
+ end
23
+
24
+ class FirewallDelegator < Stork::Resource::Delegator
25
+ flag :enabled, negate: :disable
26
+ flag :ssh
27
+ flag :telnet
28
+ flag :smtp
29
+ flag :http
30
+ flag :ftp
31
+
32
+ def trusted_device(device)
33
+ delegated.trusted_devices << device
34
+ end
35
+
36
+ alias_method :trusted, :trusted_device
37
+ alias_method :trust, :trusted_device
38
+
39
+ def allowed_port(port)
40
+ delegated.allowed_ports << port
41
+ end
42
+
43
+ alias_method :allow, :allowed_port
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,202 @@
1
+ module Stork
2
+ module Resource
3
+ class Host < Base
4
+ attr_accessor :layout
5
+ attr_accessor :template
6
+ attr_accessor :chef
7
+ attr_accessor :pxemac
8
+ attr_accessor :pre_snippets
9
+ attr_accessor :post_snippets
10
+ attr_accessor :interfaces
11
+ attr_accessor :distro
12
+ attr_accessor :timezone
13
+ attr_accessor :firewall
14
+ attr_accessor :password
15
+ attr_accessor :selinux
16
+ attr_accessor :packages
17
+ attr_accessor :run_list
18
+ attr_accessor :repos
19
+ attr_accessor :stork
20
+
21
+ def setup
22
+ @layout = nil
23
+ @template = nil
24
+ @distro = nil
25
+
26
+ @chef = nil
27
+ @pxemac = nil
28
+ @selinux = 'enforcing'
29
+ @stork = configuration ? configuration.server : 'localhost'
30
+
31
+ @pre_snippets = Array.new
32
+ @post_snippets = Array.new
33
+ @interfaces = Array.new
34
+ @run_list = Array.new
35
+ @repos = Array.new
36
+ @packages = default_packages
37
+
38
+ @timezone = Timezone.new("America/Los_Angeles")
39
+ @firewall = Firewall.new # get from config
40
+ @password = Password.new
41
+ end
42
+
43
+ def hashify
44
+ attrs = {}
45
+ attrs['name'] = name
46
+
47
+ attrs['distro'] = distro ? distro.name : ''
48
+ attrs['template'] = template ? template.name : ''
49
+ attrs['chef'] = chef ? chef.name : ''
50
+
51
+ attrs['layout'] = layout.hashify
52
+ attrs['interfaces'] = interfaces.map{|i| i.hashify}
53
+ attrs['pre_snippets'] = pre_snippets.map{|s| s.name}
54
+ attrs['post_snippets'] = post_snippets.map{|s| s.name}
55
+
56
+ attrs['repos'] = repos.map{|r| r.name}
57
+ attrs['run_list'] = run_list
58
+ attrs['packages'] = packages
59
+ attrs['timezone'] = timezone.zone
60
+ attrs['selinux'] = selinux
61
+ attrs
62
+ end
63
+
64
+ def validate!
65
+ require_value(:layout)
66
+ require_value(:template)
67
+ require_value(:distro)
68
+ require_value(:layout)
69
+ require_value(:layout)
70
+ end
71
+
72
+ def default_packages
73
+ %w(
74
+ @core
75
+ curl
76
+ openssh-clients
77
+ openssh-server
78
+ finger
79
+ pciutils
80
+ yum
81
+ at
82
+ acpid
83
+ vixie-cron
84
+ cronie-noanacron
85
+ crontabs
86
+ logrotate
87
+ ntp
88
+ ntpdate
89
+ tmpwatch
90
+ rsync
91
+ mailx
92
+ which
93
+ wget
94
+ man
95
+ )
96
+ end
97
+
98
+ def deploy
99
+ Stork::Deploy::Kickstart.new(self, configuration)
100
+ end
101
+
102
+ class HostDelegator < Stork::Resource::Delegator
103
+ def layout(value, &block)
104
+ if block_given?
105
+ delegated.layout = Layout.build(value, &block)
106
+ else
107
+ delegated.layout = collection.layouts.get(value)
108
+ end
109
+ end
110
+
111
+ def template(value)
112
+ template = collection.templates.get(value)
113
+ unless template
114
+ fail SyntaxError, "The #{value} template was not found"
115
+ end
116
+ delegated.template = template
117
+ end
118
+
119
+ def chef(value, &block)
120
+ if block_given?
121
+ delegated.chef = Chef.build(value, &block)
122
+ else
123
+ delegated.chef = collection.chefs.get(value)
124
+ end
125
+ end
126
+
127
+ def pxemac(value)
128
+ delegated.pxemac = value
129
+ end
130
+
131
+ def stork(value)
132
+ delegated.stork = value
133
+ end
134
+
135
+ def pre_snippet(value)
136
+ snippet = collection.snippets.get(value)
137
+ unless snippet
138
+ fail SyntaxError, "The #{value} snippet was not found"
139
+ end
140
+ delegated.pre_snippets << snippet
141
+ end
142
+
143
+ def post_snippet(value)
144
+ snippet = collection.snippets.get(value)
145
+ unless snippet
146
+ fail SyntaxError, "The #{value} snippet was not found"
147
+ end
148
+ delegated.post_snippets << snippet
149
+ end
150
+
151
+ def interface(value, &block)
152
+ delegated.interfaces << Interface.build(value, collection: collection, &block)
153
+ end
154
+
155
+ def distro(value, &block)
156
+ if block_given?
157
+ delegated.distro = Distro.build(value, &block)
158
+ else
159
+ delegated.distro = collection.distros.get(value)
160
+ end
161
+ end
162
+
163
+ def firewall(&block)
164
+ unless block_given?
165
+ fail SyntaxError, 'Firewall requires a block'
166
+ end
167
+ delegated.firewall = Firewall.build(&block)
168
+ end
169
+
170
+ def password(&block)
171
+ unless block_given?
172
+ fail SyntaxError, 'Password requires a block'
173
+ end
174
+ delegated.password = Password.build(&block)
175
+ end
176
+
177
+ def selinux(value)
178
+ delegated.selinux = value.to_s
179
+ end
180
+
181
+ def run_list(value)
182
+ if value.is_a?(String)
183
+ list = value.split(',')
184
+ else
185
+ list = value
186
+ end
187
+ delegated.run_list |= list
188
+ end
189
+
190
+ def repo(name, args = {})
191
+ delegated.repos << Repo.build(name, args)
192
+ end
193
+
194
+ def package(name)
195
+ delegated.packages << name
196
+ end
197
+ end
198
+
199
+ alias_method :id, :name
200
+ end
201
+ end
202
+ end