souschef 0.4.0

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 (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