bourdain 1.2.13

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 (51) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/Gemfile +11 -0
  4. data/Rakefile +19 -0
  5. data/Readme.md +1 -0
  6. data/TODO.md +41 -0
  7. data/VERSION +1 -0
  8. data/bin/tony +54 -0
  9. data/bin/tony-bump +3 -0
  10. data/bin/tony-check +3 -0
  11. data/bin/tony-generate +3 -0
  12. data/bourdain.gemspec +25 -0
  13. data/install-or-upgrade.sh +4 -0
  14. data/lib/bourdain/helpers/config.rb +12 -0
  15. data/lib/bourdain/helpers/locals.rb +17 -0
  16. data/lib/bourdain/helpers/logger.rb +41 -0
  17. data/lib/bourdain/helpers/parser.rb +76 -0
  18. data/lib/bourdain/helpers/registry.rb +53 -0
  19. data/lib/bourdain/helpers.rb +32 -0
  20. data/lib/bourdain/metadata.rb +19 -0
  21. data/lib/bourdain/resources/checks/bourdain.rb +71 -0
  22. data/lib/bourdain/resources/checks/chef.rb +35 -0
  23. data/lib/bourdain/resources/checks/cookbooks.rb +97 -0
  24. data/lib/bourdain/resources/checks/hooks.rb +67 -0
  25. data/lib/bourdain/resources/checks/ssh_config.rb +61 -0
  26. data/lib/bourdain/resources/checks.rb +36 -0
  27. data/lib/bourdain/resources/commands/bump.rb +74 -0
  28. data/lib/bourdain/resources/commands/check.rb +103 -0
  29. data/lib/bourdain/resources/commands/generate.rb +33 -0
  30. data/lib/bourdain/resources/commands.rb +10 -0
  31. data/lib/bourdain/resources/generators/attribute.rb +43 -0
  32. data/lib/bourdain/resources/generators/cookbook.rb +125 -0
  33. data/lib/bourdain/resources/generators/recipe.rb +54 -0
  34. data/lib/bourdain/resources/generators.rb +22 -0
  35. data/lib/bourdain/resources.rb +104 -0
  36. data/lib/bourdain.rb +3 -0
  37. data/templates/chef/environment.json +8 -0
  38. data/templates/chef/role.json +9 -0
  39. data/templates/cookbook/Berksfile +3 -0
  40. data/templates/cookbook/Readme.md +1 -0
  41. data/templates/cookbook/VERSION +1 -0
  42. data/templates/cookbook/Vagrantfile +19 -0
  43. data/templates/cookbook/attributes/example.rb +24 -0
  44. data/templates/cookbook/busser_minitest.rb +7 -0
  45. data/templates/cookbook/gitignore +26 -0
  46. data/templates/cookbook/kitchen.yml +23 -0
  47. data/templates/cookbook/metadata.rb +10 -0
  48. data/templates/cookbook/pre-commit +80 -0
  49. data/templates/cookbook/recipes/example.rb +38 -0
  50. data/test.sh +12 -0
  51. metadata +165 -0
@@ -0,0 +1,67 @@
1
+ module Bourdain
2
+ module Checks
3
+
4
+ class HooksCheck < Check
5
+ usage :check, <<-END
6
+ Check the local copy of Kitchen hooks
7
+ hooks
8
+ END
9
+
10
+
11
+ def initialize _
12
+ super []
13
+ return unless require_chef!
14
+ check_hooks!
15
+ end
16
+
17
+
18
+
19
+ private
20
+ def check_hooks!
21
+ # Ensure we have an up-to-date copy of Kitchen hooks
22
+ hooks = File.join('utils', 'hooks')
23
+
24
+ ahead_of_remote = false
25
+ unless Dir.exists? hooks
26
+ log.warn "Hm, I don't see a copy of Kitchen hooks. I'll fix that for you..."
27
+ %x| git clone git@git.bluejeansnet.com:kitchen-hooks.git #{hooks} |
28
+ log.info "Your copy of Kitchen hooks is now up-to-date."
29
+
30
+ else
31
+ if youre_dirty? hooks
32
+ log.warn "Your copy of Kitchen hooks is dirty."
33
+ elsif youre_ahead? hooks
34
+ log.warn "Your copy of Kitchen hooks is ahead of the remote."
35
+ ahead_of_remote = true
36
+ elsif youre_behind? hooks
37
+ log.warn "Hey, looks like your copy of Kitchen hooks is out-of-date. I'll fix that for you..."
38
+ Dir.chdir(bourdain) { %x| git pull | }
39
+ log.info "Your copy of Kitchen hooks is now up-to-date."
40
+ else
41
+ log.info "Your copy of Kitchen hooks looks up-to-date."
42
+ end
43
+ end
44
+
45
+ matches = File.read('Gemfile.lock').match(/kitchen_hooks \((.*?)\)$/)
46
+
47
+ if matches.nil?
48
+ log.warn "Couldn't find Kitchen hooks in your Gemfile lock. Something is amiss..."
49
+ error!
50
+
51
+ else
52
+ repo_version = matches[1].strip
53
+ local_version = File.read(File.join(hooks, 'VERSION')).strip
54
+
55
+ if repo_version == local_version
56
+ log.info "Your Chef repo is bundled with the latest Kitchen hooks."
57
+ elsif ahead_of_remote
58
+ # Don't worry about it, we've already warned them
59
+ elsif local_version < repo_version
60
+ log.error 'Your Chef repo is bundled with an older Kitchen hooks. Try "bundle update".'
61
+ error!
62
+ end
63
+ end
64
+ end
65
+ end
66
+ end
67
+ end
@@ -0,0 +1,61 @@
1
+ require 'json'
2
+
3
+ module Bourdain
4
+ module Checks
5
+
6
+ class SSHConfigCheck < Check
7
+ usage :check, <<-END
8
+ Ensure all nodes are enterened into the local SSH config
9
+ ssh_config
10
+ END
11
+
12
+
13
+ def initialize cookbook_config
14
+ super []
15
+ return unless require_chef!
16
+ check_ssh_config!
17
+ end
18
+
19
+
20
+
21
+ private
22
+ def check_ssh_config! ssh_config=File.join(ENV['HOME'], '.ssh', 'config')
23
+ unless File::exist?(ssh_config)
24
+ log.warn 'Skipping SSH config check. File does not exist: %s' % ssh_config.inspect
25
+ return
26
+ end
27
+
28
+ nodes = Dir[File.join('nodes', '*.json')]
29
+ nodes = nodes.map do |path|
30
+ JSON::parse File.read(path)
31
+ end
32
+
33
+ config = File.read(ssh_config)
34
+
35
+ hosts = []
36
+ config.lines.select do |l|
37
+ l =~ /Host (\S+)/
38
+ hosts << $1 if $1
39
+ end
40
+
41
+ unconfigured_nodes = nodes.select do |n|
42
+ !hosts.include? n['name']
43
+ end
44
+
45
+ File.open(ssh_config, 'a') do |f|
46
+ f.puts unless unconfigured_nodes.empty?
47
+ unconfigured_nodes.each do |n|
48
+ log.info 'Adding %s to %s' % [ n['name'].inspect, ssh_config ]
49
+ f.puts "Host %s" % n['name']
50
+ f.puts "\tHostName\t%s" % n['name']
51
+ f.puts "\tPort\t22"
52
+ f.puts "\tUser\tvagrant"
53
+ f.puts "\tStrictHostKeyChecking\tno"
54
+ f.puts "\tIdentityFile\t~/.vagrant.d/insecure_private_key"
55
+ f.puts
56
+ end
57
+ end
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,36 @@
1
+ require 'erb'
2
+ require 'fileutils'
3
+
4
+
5
+ module Bourdain
6
+ module Checks
7
+ class Check < Bourdain::Resource
8
+ protected
9
+ def work_tree repo
10
+ "--work-tree=#{File.join(Dir.pwd, repo)}"
11
+ end
12
+
13
+ def git_dir repo
14
+ "--git-dir=#{File.join(Dir.pwd, repo, '.git')}"
15
+ end
16
+
17
+ def youre_behind? repo
18
+ `git #{git_dir repo} log ..origin/master --oneline`.split("\n").length > 0
19
+ end
20
+
21
+ def youre_ahead? repo
22
+ `git #{git_dir repo} log origin/master.. --oneline`.split("\n").length > 0
23
+ end
24
+
25
+ def youre_dirty? repo
26
+ `git #{git_dir repo} #{work_tree repo} diff HEAD --numstat`.split("\n").length > 0
27
+ end
28
+ end
29
+ end
30
+ end
31
+
32
+ require_relative 'checks/chef'
33
+ require_relative 'checks/hooks'
34
+ require_relative 'checks/bourdain'
35
+ require_relative 'checks/cookbooks'
36
+ require_relative 'checks/ssh_config'
@@ -0,0 +1,74 @@
1
+ module Bourdain
2
+ module Commands
3
+
4
+ class BumpCommand < Command
5
+ usage :command, <<-END
6
+ Bump a VERSION file (patch by default)
7
+ bump [<options>] [<version-spec=major|minor|patch|x.y.z>]
8
+ END
9
+
10
+ def initialize argv
11
+ super
12
+ version_spec = argv.shift
13
+ version_spec = 'patch' if version_spec.nil?
14
+ verify! version_spec
15
+ return unless require_versionfile!
16
+ current_version = get_cookbook_version
17
+ new_version = bump_version version_spec, current_version
18
+ set_cookbook_version new_version
19
+ log.info "Bumped to #{new_version}."
20
+ end
21
+
22
+
23
+ private
24
+ def set_cookbook_version version
25
+ File.open('VERSION', 'w') { |f| f.write version }
26
+ end
27
+
28
+ def get_cookbook_version
29
+ File.open('VERSION').read.strip
30
+ end
31
+
32
+ def verify! version_spec
33
+ case version_spec
34
+ when /major/
35
+ :major
36
+ when /minor/
37
+ :minor
38
+ when /patch/
39
+ :patch
40
+ when /\d+\.\d+\.\d+/
41
+ :manual
42
+ else
43
+ Trollop::die 'Invalid <version-spec> provided'
44
+ end
45
+ end
46
+
47
+ def bump_version version_spec, version
48
+ type = verify! version_spec
49
+
50
+ # I'm not really that thinky, so I stole this from knife-spork:
51
+ #
52
+ # https://github.com/jonlives/knife-spork/blob/master/lib/chef/knife/spork-bump.rb
53
+ #
54
+ if type == :manual
55
+ version_array = version_spec.split('.')
56
+
57
+ else
58
+ field = case type
59
+ when :patch ; 2
60
+ when :minor ; 1
61
+ when :major ; 0
62
+ end
63
+
64
+ version_array = version.split('.').map(&:to_i)
65
+ version_array[field] += 1
66
+ ((field + 1)..2).each { |i| version_array[i] = 0 }
67
+ end
68
+
69
+ version_array.join('.')
70
+ end
71
+ end
72
+
73
+ end
74
+ end
@@ -0,0 +1,103 @@
1
+ require 'json'
2
+ require 'fileutils'
3
+
4
+ require 'pmap'
5
+ require 'gitlab'
6
+
7
+
8
+ module Bourdain
9
+ module Commands
10
+
11
+ class CheckCommand < Command
12
+ usage :command, <<-END
13
+ Check the state of the Kitchen, Bourdain and other things
14
+ check [<options>] [<check>]
15
+ <check>
16
+ END
17
+
18
+
19
+
20
+ def initialize argv
21
+ super
22
+ checks = Bourdain::Registry.specs('check')
23
+ maybe_check_position = checks.map { |c| argv.index c[:name].to_s }.compact.shift
24
+ check_position = maybe_check_position.nil? ? argv.length : maybe_check_position
25
+
26
+ options = argv.slice(0, check_position)
27
+ argv = argv.slice(check_position, argv.length)
28
+
29
+ super options
30
+
31
+ begin
32
+ gitlab_token = Bourdain::Config.items[:gitlab][:token]
33
+ rescue
34
+ log.fatal "Couldn't find your GitLab token in your config, bailing!"
35
+ error!
36
+ return
37
+ end
38
+
39
+ Gitlab.configure do |gitlab|
40
+ gitlab.endpoint = "http://#{Bourdain::GITLAB_HOST}/api/v3"
41
+ gitlab.private_token = gitlab_token
42
+ end
43
+
44
+ check = argv.shift
45
+
46
+ unless check.nil?
47
+ check = Bourdain::Registry.klass('check', check)
48
+ Trollop::die 'Invalid <check> provided' if check.nil?
49
+ end
50
+
51
+ return unless require_chef!
52
+
53
+ if check.nil?
54
+ @status = Bourdain::Registry.map('check') do |c|
55
+ c.new(cookbook_config_from_gitlab).status
56
+ end.sort.last
57
+ else
58
+ @status = check.new(cookbook_config_from_gitlab).status
59
+ end
60
+ end
61
+
62
+
63
+
64
+ private
65
+ def cookbook_config_from_gitlab
66
+ cookbook_configs = []
67
+
68
+ # Have to page results as GitLab absolutely will not return more than
69
+ # 100 results per page (even if we ask for more)
70
+ 1.upto(25) do |page|
71
+ Gitlab.projects(per_page: 50, page: page).each do |project|
72
+ namespace, name = project.name_with_namespace.split(' / ', 2)
73
+ namespace = nil if name.nil?
74
+ next unless namespace == 'chef'
75
+ next if %w[ chef kitchen bourdain ].include? name
76
+
77
+ cookbook_name = name.sub(/^\w+?_/, '')
78
+ cookbook_path = name.sub(/^(\w+?)_/, '\1s/bjn_')
79
+
80
+ # Fork names shouldn't have the bjn_ prefix
81
+ if name =~ /^fork_/
82
+ cookbook_name.sub!(/(bjn|fork)_/, '')
83
+ cookbook_path.sub!(/(bjn|fork)_/, '')
84
+ end
85
+
86
+ name =~ /^(\w+?)_/
87
+ cookbook_type = $1.to_sym
88
+
89
+ cookbook_configs << {
90
+ name: cookbook_name,
91
+ type: cookbook_type,
92
+ path: cookbook_path,
93
+ repo: "#{namespace}/#{name}.git",
94
+ project: project
95
+ }
96
+ end
97
+ end
98
+
99
+ cookbook_configs
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,33 @@
1
+ module Bourdain
2
+ module Commands
3
+
4
+ class GenerateCommand < Command
5
+ usage :command, <<-END
6
+ Generate various things. Take that, Chef DK!
7
+ generate [<options>] <generator>
8
+ <generator>
9
+ END
10
+
11
+ def initialize argv
12
+ generators = Bourdain::Registry.specs('generator')
13
+ maybe_generator_position = generators.map { |c| argv.index c[:name].to_s }.compact.shift
14
+ generator_position = maybe_generator_position.nil? ? argv.length : maybe_generator_position
15
+ generator_name = argv[generator_position]
16
+
17
+ options = argv.slice(0, generator_position)
18
+ argv = argv.slice(generator_position, argv.length)
19
+
20
+ super options
21
+
22
+ generator = nil
23
+ generator = argv.shift unless maybe_generator_position.nil?
24
+ Trollop::die 'Invalid <generator> provided' if generator.nil?
25
+ generator = Bourdain::Registry.klass('generator', generator_name)
26
+ Trollop::die 'Invalid <generator> provided' if generator.nil?
27
+
28
+ @status = generator.new(argv).status
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,10 @@
1
+ module Bourdain
2
+ module Commands
3
+ class Command < Bourdain::Resource
4
+ end
5
+ end
6
+ end
7
+
8
+ require_relative 'commands/bump'
9
+ require_relative 'commands/check'
10
+ require_relative 'commands/generate'
@@ -0,0 +1,43 @@
1
+ module Bourdain
2
+ module Generators
3
+
4
+ class AttributeGenerator < Generator
5
+ usage :generator, <<-END
6
+ Generate a new attribute file in the current cookbook
7
+ attribute [<options>] <name>
8
+ END
9
+
10
+
11
+ def initialize argv
12
+ super
13
+
14
+ name = argv.shift
15
+ Trollop::die 'No <name> provided' if name.nil?
16
+ Trollop::die 'Invalid <name> provided' unless valid? name
17
+ name = normalized(name)
18
+
19
+ return unless require_cookbook!
20
+
21
+ FileUtils.mkdir_p 'attributes'
22
+ path = File.join('attributes', name + '.rb')
23
+
24
+ if File::exists? path
25
+ log.warn "Attribute file already exists. Doing nothing."
26
+ return
27
+ end
28
+
29
+ cookbook_name = File.basename Dir.pwd
30
+
31
+ apply_template path, \
32
+ template: %w[ cookbook attributes example.rb ],
33
+ locals: {
34
+ cookbook_name: cookbook_name,
35
+ name: name
36
+ }
37
+
38
+ log.info "Generated attribute file at #{path}"
39
+ end
40
+ end
41
+
42
+ end
43
+ end
@@ -0,0 +1,125 @@
1
+ require 'trollop'
2
+
3
+
4
+ module Bourdain
5
+ module Generators
6
+
7
+ class CookbookGenerator < Generator
8
+ usage :generator, <<-END
9
+ Generate a new cookbook in the current repo
10
+ cookbook [<options>] <path>
11
+ --dot-chef (-c) :: Generate a .chef directory (default: false)
12
+ --git-init :: Initialize a git repository (default: true)
13
+ --attributes :: Generate a default attribute file (default: true)
14
+ --recipe :: Generate a default recipe (default: true)
15
+ --berksfile :: Generate a Berksfile (default: true)
16
+ --gitignore (-i) :: Generate a .gitignore (default: true)
17
+ --metadata :: Generate cookbook metadata (default: true)
18
+ --readme (-m) :: Generate a Readme (default: true)
19
+ --kitchenfile :: Generate a Test Kitchen file (default: true)
20
+ --vagrantfile :: Generate a Vagrantfile (default: true)
21
+ --versionfile (-z) :: Generate a VERSION file (default: true)
22
+ --pre-commit :: Generate a pre-commit hook (default: true)
23
+ --description (-p) <s> :: Description for resources
24
+ END
25
+
26
+ def initialize argv
27
+ super
28
+
29
+ path = argv.shift
30
+ Trollop::die 'No <path> provided' if path.nil?
31
+ Trollop::die 'Invalid <path> provided' unless valid? path
32
+
33
+ return unless require_chef!
34
+
35
+ if Dir::exists? path
36
+ log.warn "Cookbook already exists. Doing nothing."
37
+ return
38
+ end
39
+
40
+ name = File.basename(normalized(path))
41
+
42
+ FileUtils.mkdir_p path
43
+
44
+ if opts[:dot_chef]
45
+ FileUtils.mkdir_p File.join(path, '.chef/data_bags')
46
+ FileUtils.mkdir_p File.join(path, '.chef/nodes')
47
+ end
48
+
49
+ if opts[:attributes]
50
+ FileUtils.mkdir_p File.join(path, 'attributes')
51
+ apply_template File.join(path, 'attributes', 'default.rb'), \
52
+ template: %w[ cookbook attributes example.rb ], locals: {
53
+ name: 'default', cookbook_name: name
54
+ }
55
+ end
56
+
57
+ if opts[:recipe]
58
+ FileUtils.mkdir_p File.join(path, 'recipes')
59
+ apply_template File.join(path, 'recipes', 'default.rb'), \
60
+ template: %w[ cookbook recipes example.rb ], locals: {
61
+ name: 'default', cookbook_name: name
62
+ }
63
+ end
64
+
65
+ if opts[:berksfile]
66
+ apply_template File.join(path, 'Berksfile'), \
67
+ template: %w[ cookbook Berksfile ]
68
+ end
69
+
70
+ if opts[:gitignore]
71
+ apply_template File.join(path, '.gitignore'), \
72
+ template: %w[ cookbook gitignore ]
73
+ end
74
+
75
+ if opts[:metadata]
76
+ apply_template File.join(path, 'metadata.rb'), \
77
+ template: %w[ cookbook metadata.rb ], locals: { name: name }
78
+ end
79
+
80
+ if opts[:readme]
81
+ apply_template File.join(path, 'Readme.md'), \
82
+ template: %w[ cookbook Readme.md ], locals: { name: name }
83
+ end
84
+
85
+ if opts[:kitchenfile]
86
+ minitest_path = File.join(path, 'test', 'integration', 'default', 'minitest')
87
+ FileUtils.mkdir_p minitest_path
88
+ apply_template File.join(minitest_path, 'test_default.rb'), \
89
+ template: %w[ cookbook busser_minitest.rb ], locals: {
90
+ cookbook_name: name, recipe_name: 'default'
91
+ }
92
+ apply_template File.join(path, '.kitchen.yml'), \
93
+ template: %w[ cookbook kitchen.yml ], locals: { name: name }
94
+ end
95
+
96
+ if opts[:vagrantfile]
97
+ apply_template File.join(path, 'Vagrantfile'), \
98
+ template: %w[ cookbook Vagrantfile ], locals: { name: name }
99
+ end
100
+
101
+ if opts[:versionfile]
102
+ apply_template File.join(path, 'VERSION'), \
103
+ template: %w[ cookbook VERSION ]
104
+ end
105
+
106
+ if opts[:pre_commit]
107
+ hooks = File.join(path, '.git', 'hooks')
108
+ FileUtils.mkdir_p hooks
109
+ pre_commit_hook = File.join(hooks, 'pre-commit')
110
+ apply_template pre_commit_hook, template: %w[ cookbook pre-commit ]
111
+ FileUtils.chmod 0755, pre_commit_hook
112
+ end
113
+
114
+ if opts[:git_init]
115
+ Dir.chdir(path) do
116
+ `git init .`
117
+ end
118
+ end
119
+
120
+ log.info "Generated cookbook at #{path}."
121
+ end
122
+ end
123
+
124
+ end
125
+ end
@@ -0,0 +1,54 @@
1
+ module Bourdain
2
+ module Generators
3
+
4
+ class RecipeGenerator < Generator
5
+ usage :generator, <<-END
6
+ Generate a new recipe in the current cookbook
7
+ recipe [<options>] <name>
8
+ --minitest :: Generate a minitest file (default: true)
9
+ END
10
+
11
+
12
+ def initialize argv
13
+ super
14
+
15
+ name = argv.shift
16
+ Trollop::die 'No <name> provided' if name.nil?
17
+ Trollop::die 'Invalid <name> provided' unless valid? name
18
+ name = normalized(name)
19
+
20
+ return unless require_cookbook!
21
+
22
+ FileUtils.mkdir_p 'recipes'
23
+ path = File.join('recipes', name + '.rb')
24
+
25
+ if File::exists? path
26
+ log.warn 'Recipe already exists. Doing nothing.'
27
+ return
28
+ end
29
+
30
+ cookbook_name = File.basename Dir.pwd
31
+
32
+ apply_template path, \
33
+ template: %w[ cookbook recipes example.rb ],
34
+ locals: {
35
+ cookbook_name: cookbook_name,
36
+ name: name
37
+ }
38
+
39
+ if opts[:minitest]
40
+ minitest_path = File.join('test', 'integration', 'default', 'minitest', "test_#{name}.rb")
41
+ FileUtils.mkdir_p File.dirname(minitest_path)
42
+ apply_template minitest_path, \
43
+ template: %w[ cookbook busser_minitest.rb ], locals: {
44
+ cookbook_name: cookbook_name, recipe_name: name
45
+ }
46
+ log.info "Generated test for recipe at #{minitest_path}."
47
+ end
48
+
49
+ log.info "Generated recipe at #{path}"
50
+ end
51
+ end
52
+
53
+ end
54
+ end
@@ -0,0 +1,22 @@
1
+ require 'erb'
2
+ require 'fileutils'
3
+
4
+
5
+ module Bourdain
6
+ module Generators
7
+ class Generator < Bourdain::Resource
8
+ protected
9
+ def valid? name
10
+ name =~ /[_a-z]+/
11
+ end
12
+
13
+ def normalized name
14
+ File::basename(name, '.rb')
15
+ end
16
+ end
17
+ end
18
+ end
19
+
20
+ require_relative 'generators/attribute'
21
+ require_relative 'generators/cookbook'
22
+ require_relative 'generators/recipe'