souschef 0.4.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +22 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +22 -0
  5. data/README.md +177 -0
  6. data/Rakefile +1 -0
  7. data/bin/souschef +33 -0
  8. data/data/chefspec/chefspec.erb +6 -0
  9. data/data/chefspec/spec_helper.rb +8 -0
  10. data/data/gemfile.yml +23 -0
  11. data/data/license.erb +3 -0
  12. data/data/metadata.erb +7 -0
  13. data/data/rakefile.erb +39 -0
  14. data/data/readme.erb +47 -0
  15. data/data/recipe/recipe.erb +6 -0
  16. data/data/rubocop/rubocop.yml +19 -0
  17. data/data/serverspec/serverspec.erb +2 -0
  18. data/data/serverspec/serverspec_helper.rb +10 -0
  19. data/data/testkitchen/kitchen.default.erb +22 -0
  20. data/lib/souschef.rb +16 -0
  21. data/lib/souschef/berkshelf.rb +93 -0
  22. data/lib/souschef/config.rb +20 -0
  23. data/lib/souschef/configure/file.rb +46 -0
  24. data/lib/souschef/gemfile.rb +42 -0
  25. data/lib/souschef/print.rb +40 -0
  26. data/lib/souschef/runner.rb +104 -0
  27. data/lib/souschef/scaffold.rb +145 -0
  28. data/lib/souschef/template.rb +26 -0
  29. data/lib/souschef/template/base.rb +69 -0
  30. data/lib/souschef/template/chefspec.rb +26 -0
  31. data/lib/souschef/template/license.rb +25 -0
  32. data/lib/souschef/template/metadata.rb +30 -0
  33. data/lib/souschef/template/rakefile.rb +23 -0
  34. data/lib/souschef/template/readme.rb +24 -0
  35. data/lib/souschef/template/rubocop.rb +22 -0
  36. data/lib/souschef/template/serverspec.rb +27 -0
  37. data/lib/souschef/testkitchen.rb +135 -0
  38. data/lib/souschef/testkitchen/docker.rb +97 -0
  39. data/lib/souschef/testkitchen/solusvm.rb +101 -0
  40. data/lib/souschef/testkitchen/virtualbox.rb +66 -0
  41. data/lib/souschef/version.rb +4 -0
  42. data/souschef.gemspec +33 -0
  43. metadata +254 -0
@@ -0,0 +1,2 @@
1
+ # Serverspec testing for <%= @recipe %> recipe
2
+ require 'spec_helper'
@@ -0,0 +1,10 @@
1
+ require 'serverspec'
2
+ require 'pathname'
3
+ include Serverspec::Helper::Exec
4
+ include Serverspec::Helper::DetectOS
5
+
6
+ RSpec.configure do |c|
7
+ c.before :all do
8
+ c.os = backend(Serverspec::Commands::Base).check_os
9
+ end
10
+ end
@@ -0,0 +1,22 @@
1
+ ---
2
+ driver:
3
+ name: vagrant
4
+ customize:
5
+ memory: 1024
6
+
7
+ provisioner:
8
+ name: chef_zero
9
+ require_chef_omnibus: latest
10
+
11
+ platforms:
12
+ - name: centos-5.10
13
+ driver_config:
14
+ box: centos-5.10-min-x86_64
15
+ - name: centos-6.4
16
+ driver_config:
17
+ box: centos-6.5-x86_64
18
+ suites:
19
+ - name: default
20
+ run_list:
21
+ - recipe[<%= @cookbook %>::default]
22
+ attributes:
@@ -0,0 +1,16 @@
1
+ require 'erb'
2
+ require 'yaml'
3
+ require 'open3'
4
+ require 'trollop'
5
+ require 'colorize'
6
+ require 'ruth'
7
+ require 'souschef/runner'
8
+ require 'souschef/configure/file'
9
+ require 'souschef/config'
10
+ require 'souschef/berkshelf'
11
+ require 'souschef/scaffold'
12
+ require 'souschef/gemfile'
13
+ require 'souschef/testkitchen'
14
+ require 'souschef/template'
15
+ require 'souschef/print'
16
+ require 'souschef/version'
@@ -0,0 +1,93 @@
1
+ module Souschef
2
+ # Includes slave functions that do all the work
3
+ class Berkshelf
4
+ attr_accessor :opts, :cookbook_dir
5
+
6
+ def initialize(opts)
7
+ @opts = opts
8
+ end
9
+
10
+ # Public - Tell Berkshelf to create a cookbook
11
+ #
12
+ # Returns nil
13
+ def berks_create
14
+ remove_old_readme
15
+ Souschef::Print.info 'Creating cookbook structure' if @opts[:verbose]
16
+
17
+ i, o, e, w = Open3.popen3(berks_cmd)
18
+ print_open3_stdout(o) if @opts[:verbose]
19
+ remove_vagrantfile
20
+ print_open3_stdout(e)
21
+ i.close
22
+ fail 'Berks failed' unless w.value == 0
23
+ end
24
+
25
+ private
26
+
27
+ # Private - Returns berkshelf command depending on the path
28
+ #
29
+ # Returns String
30
+ def berks_cmd
31
+ if @opts[:path] == Dir.pwd
32
+ "#{which_berks} cookbook #{@opts[:cookbook]} ."
33
+ else
34
+ path = @opts[:path].gsub("#{Dir.pwd}/", '')
35
+ "#{which_berks} cookbook #{@opts[:cookbook]} #{path}"
36
+ end
37
+ end
38
+
39
+ # Private - Remove README from cookbook dir
40
+ #
41
+ # Returns nil
42
+ def remove_old_readme
43
+ readme = File.join(@opts[:path], 'README.md')
44
+ File.delete(readme) if File.exist?(readme)
45
+ end
46
+
47
+ # Private - Remove README from cookbook dir
48
+ #
49
+ # Returns nil
50
+ def remove_vagrantfile
51
+ vagrantfile = File.join(@opts[:path], 'Vagrantfile')
52
+ File.delete(vagrantfile) if File.exist?(vagrantfile)
53
+ end
54
+
55
+ # Private - Obtain berks executable full path
56
+ #
57
+ # Returns String
58
+ def which_berks
59
+ i, o, e, w = Open3.popen3('which berks')
60
+ fail 'berks executable not found on system' if w.value.to_i > 0
61
+ i.close
62
+ e.close
63
+ o.read.chomp
64
+ end
65
+
66
+ # Private - Print out Open3 STDOUT stream
67
+ #
68
+ # stdout - Open3 stdout stream
69
+ #
70
+ # Return nil
71
+ def print_open3_stdout(stdout)
72
+ stdout.read.split("\n").each { |msg| puts msg.colorize(:green) }
73
+ end
74
+
75
+ # Private - Print out a colorized message
76
+ #
77
+ # msg - String message
78
+ #
79
+ # Returns nil
80
+ def colour(msg)
81
+ puts msg.colorize(:red)
82
+ end
83
+
84
+ # Private - Print out info message
85
+ #
86
+ # msg - String message
87
+ #
88
+ # Returns nil
89
+ def info_msg(msg)
90
+ puts msg.colorize(:yellow)
91
+ end
92
+ end
93
+ end
@@ -0,0 +1,20 @@
1
+ module Souschef
2
+ # Loads Souschef configuration YAML
3
+ class Config
4
+ # Public - Reads the configuration file
5
+ #
6
+ # Returns Hash
7
+ def self.read
8
+ verify_file
9
+ YAML.load_file(File.expand_path('~/.souschef.yml'))
10
+ end
11
+
12
+ # Private - Checks if we have a configuraiton file
13
+ #
14
+ # Returns nil
15
+ def self.verify_file
16
+ conf = File.expand_path('~/.souschef.yml')
17
+ fail "'~/.souschef.yml' missing!" unless File.exist?(conf)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,46 @@
1
+ module Souschef
2
+ class Configure
3
+ # Create Configuration file
4
+ class Yaml
5
+ attr_accessor :opts, :data
6
+
7
+ def initialize(opts)
8
+ @opts = opts
9
+ @souschef = File.expand_path('~/.souschef.yml')
10
+ read_configuration
11
+ add_values
12
+ write_configuration
13
+ end
14
+
15
+ private
16
+
17
+ # Private - Add CLI passed data into the configuration
18
+ #
19
+ # Return nil
20
+ def add_values
21
+ @data[@opts[:profile]] = { maintainer: @opts[:maintainer],
22
+ maintainer_email: @opts[:maintainer_email],
23
+ license: @opts[:license] }
24
+ end
25
+
26
+ # Private - Read configuration file, if it exists, otherwise define @data
27
+ # as a empty Hash
28
+ #
29
+ # Returns Hash
30
+ def read_configuration
31
+ if File.exist?(@souschef)
32
+ @data ||= YAML.load_file(@souschef)
33
+ else
34
+ @data = {}
35
+ end
36
+ end
37
+
38
+ # Private - Write down configuration file
39
+ #
40
+ # Returns nil
41
+ def write_configuration
42
+ File.open(@souschef, 'w+') { |fd| fd.write(@data.to_yaml) }
43
+ end
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,42 @@
1
+ module Souschef
2
+ # Gemfile generator
3
+ class Gemfile < Ruth::Gemfile::Yaml
4
+ attr_accessor :opts
5
+
6
+ def initialize(opts)
7
+ @opts = opts
8
+ super(yaml_file_location)
9
+ end
10
+
11
+ # Public - Writes down the Gemfile location
12
+ #
13
+ # Returns nil
14
+ def write
15
+ Souschef::Print.info 'Populating Gemfile' if @opts[:verbose]
16
+ write_gemfile
17
+ end
18
+
19
+ private
20
+
21
+ # Private - Return location of YAML file
22
+ #
23
+ # Returns String
24
+ def yaml_file_location
25
+ File.expand_path('../../../data/gemfile.yml', __FILE__)
26
+ end
27
+
28
+ # Private - Return location of Gemfile
29
+ #
30
+ # Returns string
31
+ def gemfile_location
32
+ File.join(@opts[:path], 'Gemfile')
33
+ end
34
+
35
+ # Private - Write Gemfile data
36
+ #
37
+ # Returns nil
38
+ def write_gemfile
39
+ File.open(gemfile_location, 'w') { |file| file.write(output) }
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,40 @@
1
+ module Souschef
2
+ # Prit out colorized information
3
+ class Print
4
+ # Public - Print info messate
5
+ #
6
+ # msg - String message
7
+ #
8
+ # Returns nil
9
+ def self.info(msg)
10
+ puts "~> #{msg}".colorize(:green)
11
+ end
12
+
13
+ # Public - Print header message
14
+ #
15
+ # msg - String message
16
+ #
17
+ # Returns nil
18
+ def self.header(msg)
19
+ puts "[*] #{msg}".colorize(:cyan)
20
+ end
21
+
22
+ # Public - Print error messate
23
+ #
24
+ # msg - String message
25
+ #
26
+ # Returns nil
27
+ def self.error(msg)
28
+ puts "#{msg}".colorize(:red)
29
+ end
30
+
31
+ # Public - Print warning messate
32
+ #
33
+ # msg - String message
34
+ #
35
+ # Returns nil
36
+ def self.warning(msg)
37
+ puts "#{msg}".colorize(:orange)
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,104 @@
1
+ module Souschef
2
+ # Runner library that does all the hard work
3
+ class Runner
4
+ attr_accessor :opts
5
+
6
+ def initialize(opts)
7
+ @opts = opts
8
+ adjust_cb_path
9
+ load_configuration unless @opts[:configure]
10
+ end
11
+
12
+ # Public - Run Souschef
13
+ #
14
+ # Returns nil
15
+ def run
16
+ Souschef::Print.header "Using Souschef profile: #{opts[:profile]}"
17
+
18
+ if @opts[:scaffold]
19
+ Souschef::Scaffold.new(@opts).start
20
+ elsif @opts[:configure]
21
+ verify_configure_input
22
+ Souschef::Configure::Yaml.new(@opts)
23
+ else
24
+ Souschef::Print.header "Starting cookbook creation...\n"
25
+ cookbook_runlist
26
+ end
27
+ end
28
+
29
+ private
30
+
31
+ # Private - Create cookbook run list
32
+ #
33
+ # Returns nil
34
+ def cookbook_runlist
35
+ verify_cookbook_creation
36
+
37
+ Souschef::Print.header 'Berkshelf configuration'
38
+ Souschef::Berkshelf.new(@opts).berks_create
39
+ Souschef::Print.header 'Configure gemfile'
40
+ Souschef::Gemfile.new(@opts).write
41
+ Souschef::Print.header 'Create essential template files'
42
+ Souschef::Template.run(@opts)
43
+ # Mock Scaffold to generate default recipe and tests
44
+
45
+ Souschef::Print.header 'Create default recipe and tests'
46
+ Souschef::Scaffold.new(path: @opts[:path],
47
+ recipe: 'default',
48
+ profile: @opts[:profile],
49
+ force: true,
50
+ verbose: @opts[:verbose]).start
51
+
52
+ Souschef::Print.header 'Testkitchen configuration'
53
+ Souschef::Testkitchen.new(@opts).setup
54
+
55
+ Souschef::Print.header "Don't forget to run bundle install!"
56
+ end
57
+
58
+ # Private - Verify @opts values
59
+ #
60
+ # Return nil
61
+ def verify_configure_input
62
+ if @opts[:maintainer_given] || @opts[:maintainer_email_given] ||
63
+ @opts[:license_given]
64
+ unless @opts[:configure]
65
+ fail 'Please check Souschef options, you are missing --configure'
66
+ end
67
+ else
68
+ fail 'Please check if you have all options for configuration selected'
69
+ end
70
+ end
71
+
72
+ # Private - Verify cookbook creation options
73
+ #
74
+ # Return nil
75
+ def verify_cookbook_creation
76
+ fail 'You need to specify the cookbook name' if @opts[:cookbook].nil?
77
+ end
78
+
79
+ # Private - Verify Scaffold input
80
+ #
81
+ # Return nil
82
+ def verify_scaffold_input
83
+ fail 'Recipe name is missing for scaffold creation' if @opts[:recipe].nil?
84
+ end
85
+
86
+ # Private - Adjust cookbook path
87
+ #
88
+ # Return nil
89
+ def adjust_cb_path
90
+ if @opts[:path]
91
+ @opts[path] = File.join(Dir.pwd, @opts[:path])
92
+ else
93
+ @opts[:path] = Dir.pwd
94
+ end
95
+ end
96
+
97
+ # Private - Load contents of .souschef.yml configuration
98
+ #
99
+ # Returns nil
100
+ def load_configuration
101
+ @opts[:souschef] = Souschef::Config.read[@opts[:profile]]
102
+ end
103
+ end
104
+ end
@@ -0,0 +1,145 @@
1
+ require 'chef/cookbook/metadata'
2
+ require 'fileutils'
3
+
4
+ module Souschef
5
+ # Automagically create needed files
6
+ class Scaffold
7
+ attr_accessor :opts, :dir, :recipe, :recipe_file, :cookbook, :metadata
8
+
9
+ def initialize(opts)
10
+ @opts = opts
11
+ @dir = Dir.pwd
12
+ metadata_info
13
+ end
14
+
15
+ def start
16
+ check_cookbook_name
17
+ check_for_metadata unless @opts[:force]
18
+ process_templates
19
+ end
20
+
21
+ private
22
+
23
+ # Private - Chef if cookbook name is set
24
+ #
25
+ # Returns nil
26
+ def check_cookbook_name
27
+ fail 'Please specify the recipe name' if @opts[:recipe].nil?
28
+ end
29
+
30
+ # Private - Read Chef metadata
31
+ #
32
+ # Returns String
33
+ def metadata_info
34
+ meta = File.join(@opts[:path], 'metadata.rb')
35
+ @metadata = Chef::Cookbook::Metadata.new
36
+ @metadata.from_file(meta)
37
+ end
38
+
39
+ # Private - Process tempaltes
40
+ #
41
+ # Return inl
42
+ def process_templates
43
+ %w( recipe serverspec chefspec ).each { |type| create_recipe_file(type) }
44
+ end
45
+
46
+ # Private - Creates recipe file based on the input
47
+ #
48
+ # Retunrns nil
49
+ def create_recipe_file(type)
50
+ source = template_location(type)
51
+ Souschef::Print.info("Create #{@opts[:recipe]}[#{type}] from #{source}"
52
+ ) if @opts[:verbose]
53
+ check_for_directories(type)
54
+ write_file(return_file_location(type), generate_template(source))
55
+ end
56
+
57
+ # Private - Generate the template
58
+ #
59
+ # Return String
60
+ def generate_template(source)
61
+ rfile = ERB.new(File.read(source))
62
+ @recipe = @opts[:recipe]
63
+ @cookbook = @metadata.name
64
+ @maintainer = @metadata.maintainer
65
+ @license = @metadata.license
66
+ @year = Time.now.year
67
+
68
+ rfile.result(binding)
69
+ end
70
+
71
+ # Private - Return location of the template file, depending if custom
72
+ # configuration is set under ~/.souschef/%profile%/ or use the default
73
+ # template provided by Souschef gem.
74
+ #
75
+ # Returns String
76
+ def template_location(type)
77
+ local = File.expand_path(
78
+ "~/.souschef/#{@opts[:profile]}/#{type}/#{type}.erb", __FILE__)
79
+ bundled = File.expand_path("../../../data/#{type}/#{type}.erb", __FILE__)
80
+ File.exist?(local) ? local : bundled
81
+ end
82
+
83
+ # Private - Return location of directories
84
+ #
85
+ # Returns Hash
86
+ def return_directories
87
+ { recipe: File.join(@opts[:path], 'recipes'),
88
+ serverspec: File.join(@opts[:path], 'test', 'integration', 'default',
89
+ 'serverspec', 'localhost'),
90
+ chefspec: File.join(@opts[:path], 'spec', 'unit') }
91
+ end
92
+
93
+ # Private - Get path to the recipes file
94
+ #
95
+ # Return String
96
+ def return_file_location(type)
97
+ case type
98
+ when 'recipe'
99
+ File.join(return_directories[:recipe], "#{@opts[:recipe]}.rb")
100
+ when 'serverspec'
101
+ File.join(return_directories[:serverspec], "#{opts[:recipe]}_spec.rb")
102
+ when 'chefspec'
103
+ File.join(return_directories[:chefspec], "#{@opts[:recipe]}_spec.rb")
104
+ end
105
+ end
106
+
107
+ # Private - Check if directories exist
108
+ #
109
+ # Return nil
110
+ def check_for_directories(type)
111
+ dir = return_directories[type.to_sym]
112
+
113
+ if @opts[:versbose]
114
+ unless File.directory?(dir)
115
+ Souschef::Print.info "Creating missing directory #{dir}"
116
+ end
117
+ end
118
+ FileUtils.mkdir_p dir unless File.directory?(dir)
119
+ end
120
+
121
+ # Private - Locate metadata.rb
122
+ #
123
+ # Return nil
124
+ def check_for_metadata
125
+ meta = File.join(@opts[:path], 'metadata.rb')
126
+ fail 'Please return to the root of your cookbook' unless File.exist?(meta)
127
+ end
128
+
129
+ # Private - Write file
130
+ #
131
+ # Returns nil
132
+ def write_file(file, data)
133
+ check_for_file(file) unless @opts[:force]
134
+ fd = File.open(file, 'w')
135
+ fd.write(data)
136
+ end
137
+
138
+ # Private - Exit if file exists
139
+ #
140
+ # Returns nil
141
+ def check_for_file(file)
142
+ fail "#{file} already exists. Frozen in fear!" if File.exist?(file)
143
+ end
144
+ end
145
+ end