puppet-ghostbuster 0.4.5 → 0.5.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 49c229d0aa6aae767948d12cad86f06a8afe5e66
4
- data.tar.gz: 92ef9f461b7e676d33c0127113545d38a9de4415
3
+ metadata.gz: 54bed6f908a3bb9aaae26087248857c8390028b7
4
+ data.tar.gz: 3904dae59b8117dfd6233afaf73688ed232c2e6b
5
5
  SHA512:
6
- metadata.gz: 8959c6a8b06e371120ffaddfdeb9ff2576998d3964bbcae991212102033d71e1ec3fa3375bd31067f27fc7062edc0f3bed07d6f0fbf5b4c80d055a147645185b
7
- data.tar.gz: 37a00822a2869c26881a44bf88f0639c509635a39d1d1b74e233dec348d7afa878ddf4cfa4f18650dc24395a65d66145fbfbdf635902d977b2545117679db2d5
6
+ metadata.gz: 0b6a6044ce239fd2aef83b436994b5bff3bb85f5214c3e5537317fa853414c090b517d7e4eea2853df38acab5d5ca1fa6a881366f8817f8f9ff20dd4a9a2b394
7
+ data.tar.gz: 68abfdd84fd16af1b06a32c06b6173b90b9b50bf4024d26eb4aca4026d27cf5b75b17ed5bb1ca26b588a5896466678f3067a303eb94620f038dce084f68e4887
data/.travis.yml CHANGED
@@ -1,6 +1,9 @@
1
+ ---
1
2
  language: ruby
2
3
  sudo: false
3
4
  cache: bundler
5
+ script:
6
+ bundle exec rake spec
4
7
  rvm:
5
8
  - 2.1.5
6
9
  matrix:
data/CHANGELOG.md CHANGED
@@ -1,5 +1,12 @@
1
1
  # Change Log
2
2
 
3
+ ## [0.5.0](https://rubygems.org/gems/puppet-ghostbuster/versions/0.5.0) (2016-05-10)
4
+ [Full Changelog](https://github.com/camptocamp/puppet-ghostbuster/compare/0.4.5...0.5.0)
5
+
6
+ **Implemented enhancements:**
7
+
8
+ - Major Reimplementation. Delivered as puppet-lint plugins.
9
+
3
10
  ## [0.4.5](https://rubygems.org/gems/puppet-ghostbuster/versions/0.4.5) (2016-05-04)
4
11
  [Full Changelog](https://github.com/camptocamp/puppet-ghostbuster/compare/0.4.4...0.4.5)
5
12
 
data/README.md CHANGED
@@ -17,82 +17,42 @@ This gems only support PuppetDB APi v4 (PuppetDB 3+)
17
17
  Usage
18
18
  -----
19
19
 
20
- If you want to read default options and private key from puppet configuration, this gem needs to have root (puppet) permissions.
21
- ```
22
- sudo bundle exec puppet-ghostbuster
20
+ ```shell
21
+ $ find . -type f -exec puppet-lint --only-checks ghostbuster_classes,ghostbuster_defines,ghostbuster_files,ghostbuster_templates {} \+
23
22
  ```
24
23
 
25
- You can add preconnect command with ``-p`` and server url with ``-s``.
24
+ Environment variables
25
+ ---------------------
26
26
 
27
- ``-h`` or ``--help`` for full options.
27
+ ### PUPPETDB_URL
28
28
 
29
- Example output
30
- --------------
31
- ```
32
- $ puppet-ghostbuster -s https://puppetdburl:8081 | jsonpp
33
- [
34
- {
35
- "title": "[GhostBuster] Class Foo::Install seems unused",
36
- "body": "./modules/foo/manifests/install.pp"
37
- },
38
- {
39
- "title": "[GhostBuster] Class Foo::Service seems unused",
40
- "body": "./modules/foo/manifests/service.pp"
41
- },
42
- {
43
- "title": "[GhostBuster] Class Foo seems unused",
44
- "body": "./modules/foo/manifests/init.pp"
45
- },
46
- {
47
- "title": "[GhostBuster] Define Bar:Baz seems unused",
48
- "body": "./modules/bar/manifests/baz.pp"
49
- },
50
- {
51
- "title": "[GhostBuster] Template modulename/foo.erb seems unused",
52
- "body": "./modules/modulename/templates/foo.erb"
53
- },
54
- {
55
- "title": "[GhostBuster] Template modulename/bar.erb seems unused",
56
- "body": "./modules/modulename/templates/bar.erb"
57
- },
58
- {
59
- "title": "[GhostBuster] Template modulename/baz.erb seems unused",
60
- "body": "./modules/modulename/templates/baz.erb"
61
- },
62
- {
63
- "title": "[GhostBuster] File foo/bar.txt seems unused",
64
- "body": "./modules/foo/files/bar.txt"
65
- },
66
- {
67
- "title": "[GhostBuster] File foo/baz.txt seems unused",
68
- "body": "./modules/foo/files/baz.txt"
69
- }
70
- ]
71
- ```
29
+ The url or the PuppetDB. Defaults to `http://puppetdb:8080`
30
+
31
+ ### PUPPETDB_CACERT_FILE
72
32
 
73
- How It Works
74
- ------------
75
- To find unused classes:
76
- 1. It search puppet code recursively starting (by default) from current directory.
77
- 2. For each .pp file found, it extract the class name
78
- 3. Query puppetdb (using cache) to find matching class in catalogs
79
- 4. Display the number of class, followed by the class name.
33
+ Your site’s CA certificate
80
34
 
81
- Same method applies to find unused defines.
35
+ ### PUPPETDB_CERT_FILE
82
36
 
83
- To find template and files, it has to loop twice, so this step can be longer.
37
+ An SSL certificate signed by your site’s Puppet CA
84
38
 
85
- .ghostbusterignore
86
- ------------------
39
+ ### PUPPETDB_KEY_FILE
87
40
 
88
- Puppet-ghostbuster supports `.ghostbusterignore` files with a list of path that
89
- will be excluded from the dead code detection. Useful for upstream modules where
90
- you are are not using everything.
41
+ The private key for that certificate
91
42
 
92
- Example of `.ghostbusterignore` file:
43
+ Example output
44
+ --------------
93
45
 
46
+ TODO
94
47
  ```
95
- modules/apache
96
- modules/mysql
97
- modules/mcollective/templates
48
+ $ find . -type f -exec puppet-lint --only-checks ghostbuster_classes,ghostbuster_defines,ghostbuster_files,ghostbuster_templates {} \+
49
+ ./modules/foo/manifests/install.pp - WARNING: Class Foo::Install seems unused on line 1
50
+ ./modules/foo/manifests/service.pp - WARNING: Class Foo::Service seems unused on line 1
51
+ ./modules/foo/manifests/init.pp - WARNING: Class Foo seems unused on line 1
52
+ ./modules/bar/manifests/baz.pp - WARNING: Define Bar::Baz seems unused on line 1
53
+ ./modules/modulename/templates/foo.erb - WARNING: Template modulename/foo.erb seems unused on line 1
54
+ ./modules/modulename/templates/bar.erb - WARNING: Template modulename/bar.erb seems unused on line 1
55
+ ./modules/modulename/templates/baz.erb - WARNING: Template modulename/baz.erb seems unused on line 1
56
+ ./modules/foo/files/bar.txt - WARNING: File foo/bar.txt seems unused on line 1
57
+ ./modules/foo/files/baz.txt - WARNING: File foo/baz.txt seems unused on line 1
98
58
  ```
data/Rakefile CHANGED
@@ -10,3 +10,5 @@ GitHubChangelogGenerator::RakeTask.new :changelog do |config|
10
10
  config.future_release = PuppetGhostbuster::VERSION
11
11
  config.release_url = "https://rubygems.org/gems/puppet-ghostbuster/versions/%s"
12
12
  end
13
+
14
+ task :default => :spec
@@ -0,0 +1,28 @@
1
+ require 'puppetdb'
2
+
3
+ class PuppetGhostbuster
4
+ class PuppetDB
5
+ def self.client
6
+ @@client ||= ::PuppetDB::Client.new({
7
+ :server => "#{ENV['PUPPETDB_URL'] || 'http://puppetdb:8080'}/pdb/query",
8
+ :pem => {
9
+ 'key' => ENV['PUPPETDB_KEY_FILE'],
10
+ 'cert' => ENV['PUPPETDB_CERT_FILE'],
11
+ 'ca_file' => ENV['PUPPETDB_CACERT_FILE'],
12
+ }
13
+ }, 4)
14
+ end
15
+
16
+ def client
17
+ self.class.client
18
+ end
19
+
20
+ def self.classes
21
+ @@classes ||= client.request('resources', [:'=', 'type', 'Class']).data.map { |r| r['title'] }.uniq
22
+ end
23
+
24
+ def classes
25
+ self.class.classes
26
+ end
27
+ end
28
+ end
@@ -1,3 +1,3 @@
1
1
  class PuppetGhostbuster
2
- VERSION = '0.4.5'
2
+ VERSION = '0.5.0'
3
3
  end
@@ -0,0 +1,22 @@
1
+ require 'puppet-ghostbuster/puppetdb'
2
+
3
+ PuppetLint.new_check(:ghostbuster_classes) do
4
+ def check
5
+ return if path.match(%r{^\./(:?[^/]+/){2}?manifests/.+$}).nil?
6
+
7
+ puppetdb = PuppetGhostbuster::PuppetDB.new
8
+
9
+ class_indexes.each do |class_idx|
10
+ title_token = class_idx[:name_token]
11
+ title = title_token.value.split('::').map(&:capitalize).join('::')
12
+
13
+ return if puppetdb.classes.include? title
14
+
15
+ notify :warning, {
16
+ :message => "Class #{title} seems unused",
17
+ :line => title_token.line,
18
+ :column => title_token.column,
19
+ }
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,23 @@
1
+ require 'puppet-ghostbuster/puppetdb'
2
+
3
+ PuppetLint.new_check(:ghostbuster_defines) do
4
+ def check
5
+ return if path.match(%r{^\./(:?[^/]+/){2}?manifests/.+$}).nil?
6
+
7
+ puppetdb = PuppetGhostbuster::PuppetDB.new
8
+
9
+ defined_type_indexes.each do |define_idx|
10
+ title_token = define_idx[:name_token]
11
+ type = title_token.value.split('::').map(&:capitalize).join('::')
12
+
13
+ return if puppetdb.client.request('resources', [:'=', 'type', type]).data.size > 0
14
+
15
+ notify :warning, {
16
+ :message => "Define #{type} seems unused",
17
+ :line => title_token.line,
18
+ :column => title_token.column,
19
+ }
20
+ end
21
+ end
22
+ end
23
+
@@ -0,0 +1,61 @@
1
+ require 'puppet-ghostbuster/puppetdb'
2
+
3
+ class PuppetLint::Checks
4
+ def load_data(path, content)
5
+ lexer = PuppetLint::Lexer.new
6
+ PuppetLint::Data.path = path
7
+ begin
8
+ PuppetLint::Data.manifest_lines = content.split("\n", -1)
9
+ PuppetLint::Data.tokens = lexer.tokenise(content)
10
+ PuppetLint::Data.parse_control_comments
11
+ rescue
12
+ PuppetLint::Data.tokens = []
13
+ end
14
+ end
15
+ end
16
+
17
+ PuppetLint.new_check(:ghostbuster_files) do
18
+ def manifests
19
+ Dir.glob('./**/manifests/**/*.pp')
20
+ end
21
+
22
+ def check
23
+ m = path.match(%r{.*/([^/]+)/files/(.+)$})
24
+ return if m.nil?
25
+
26
+ puppetdb = PuppetGhostbuster::PuppetDB.new
27
+
28
+ module_name, file_name = m.captures
29
+ return if puppetdb.client.request('resources', [:'=', ['parameter', 'source'], "puppet:///modules/#{module_name}/#{file_name}"],).data.size > 0
30
+
31
+ dir_name = File.dirname(file_name)
32
+ while dir_name != '.' do
33
+ return if puppetdb.client.request(
34
+ 'resources',
35
+ [:'and',
36
+ [:'or',
37
+ [:'=', ['parameter', 'source'], "puppet:///modules/#{module_name}/#{dir_name}"],
38
+ [:'=', ['parameter', 'source'], "puppet:///modules/#{module_name}/#{dir_name}/"],
39
+ ],
40
+ [:'=', ['parameter', 'recurse'], true],
41
+ ],
42
+ ).data.size > 0
43
+ dir_name = File.dirname(dir_name)
44
+ end
45
+
46
+ manifests.each do |manifest|
47
+ return if File.readlines(manifest).grep(%r{["']#{module_name}/#{file_name}["']}).size > 0
48
+ if match = manifest.match(%r{.*/([^/]+)/manifests/.+$})
49
+ if match.captures[0] == module_name
50
+ return if File.readlines(manifest).grep(/["']\$\{module_name\}\/#{file_name}["']/).size > 0
51
+ end
52
+ end
53
+ end
54
+
55
+ notify :warning, {
56
+ :message => "File #{module_name}/#{file_name} seems unused",
57
+ :line => 1,
58
+ :column => 1,
59
+ }
60
+ end
61
+ end
@@ -0,0 +1,41 @@
1
+ class PuppetLint::Checks
2
+ def load_data(path, content)
3
+ lexer = PuppetLint::Lexer.new
4
+ PuppetLint::Data.path = path
5
+ begin
6
+ PuppetLint::Data.manifest_lines = content.split("\n", -1)
7
+ PuppetLint::Data.tokens = lexer.tokenise(content)
8
+ PuppetLint::Data.parse_control_comments
9
+ rescue
10
+ PuppetLint::Data.tokens = []
11
+ end
12
+ end
13
+ end
14
+
15
+ PuppetLint.new_check(:ghostbuster_templates) do
16
+ def manifests
17
+ Dir.glob('./**/manifests/**/*.pp')
18
+ end
19
+
20
+ def check
21
+ m = path.match(%r{.*/([^/]+)/templates/(.+)$})
22
+ return if m.nil?
23
+
24
+ module_name, template_name = m.captures
25
+
26
+ manifests.each do |manifest|
27
+ return if File.readlines(manifest).grep(%r{["']#{module_name}/#{template_name}["']}).size > 0
28
+ if match = manifest.match(%r{.*/([^/]+)/manifests/.+$})
29
+ if match.captures[0] == module_name
30
+ return if File.readlines(manifest).grep(/["']\$\{module_name\}\/#{template_name}["']/).size > 0
31
+ end
32
+ end
33
+ end
34
+
35
+ notify :warning, {
36
+ :message => "Template #{module_name}/#{template_name} seems unused",
37
+ :line => 1,
38
+ :column => 1,
39
+ }
40
+ end
41
+ end
@@ -17,9 +17,12 @@ Gem::Specification.new do |s|
17
17
 
18
18
  s.add_development_dependency 'coveralls'
19
19
  s.add_development_dependency 'rake'
20
- s.add_development_dependency 'rspec'
20
+ s.add_development_dependency 'rspec', '~> 3.0'
21
+ s.add_development_dependency 'rspec-its', '~> 1.0'
22
+ s.add_development_dependency 'rspec-collection_matchers', '~> 1.0'
21
23
  s.add_development_dependency 'github_changelog_generator'
22
24
  s.add_runtime_dependency 'json'
23
25
  s.add_runtime_dependency 'puppet'
26
+ s.add_dependency 'puppet-lint', '~> 1.0'
24
27
  s.add_runtime_dependency 'puppetdb-ruby'
25
28
  end
@@ -0,0 +1,44 @@
1
+ require 'spec_helper'
2
+
3
+ class PuppetDBRequest
4
+ def initialize(data)
5
+ @data = data
6
+ end
7
+
8
+ def data
9
+ @data
10
+ end
11
+ end
12
+
13
+ describe 'ghostbuster_classes' do
14
+ #let(:path) { "./manifests/site.pp" }
15
+ let(:path) { "./modules/foo/manifests/init.pp" }
16
+
17
+ context 'with fix disabled' do
18
+
19
+ before :each do
20
+ expect(PuppetGhostbuster::PuppetDB).to \
21
+ receive(:classes).and_return(['Foo'])
22
+ end
23
+
24
+ context 'when class is used' do
25
+ let(:code) { "class foo {}" }
26
+
27
+ it 'should not detect any problem' do
28
+ expect(problems).to have(0).problems
29
+ end
30
+ end
31
+
32
+ context 'when class is not used' do
33
+ let(:code) { "class bar {}" }
34
+
35
+ it 'should detect one problem' do
36
+ expect(problems).to have(1).problems
37
+ end
38
+
39
+ it 'should create a warning' do
40
+ expect(problems).to contain_warning('Class Bar seems unused')
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,55 @@
1
+ require 'spec_helper'
2
+
3
+ class PuppetDBRequest
4
+ def initialize(data)
5
+ @data = data
6
+ end
7
+
8
+ def data
9
+ @data
10
+ end
11
+ end
12
+
13
+ describe 'ghostbuster_defines' do
14
+ let(:path) { "./modules/foo/manifests/init.pp" }
15
+
16
+ context 'with fix disabled' do
17
+
18
+ context 'when define is used' do
19
+ let(:code) { "define foo {}" }
20
+
21
+ before :each do
22
+ expect_any_instance_of(PuppetDB::Client).to \
23
+ receive(:request).with('resources', [:'=', 'type', 'Foo'])
24
+ .and_return(PuppetDBRequest.new([
25
+ {
26
+ 'type' => 'Foo',
27
+ 'title' => 'bar',
28
+ },
29
+ ]))
30
+ end
31
+
32
+ it 'should not detect any problem' do
33
+ expect(problems).to have(0).problems
34
+ end
35
+ end
36
+
37
+ context 'when define is not used' do
38
+ let(:code) { "define foo {}" }
39
+
40
+ before :each do
41
+ expect_any_instance_of(PuppetDB::Client).to \
42
+ receive(:request).with('resources', [:'=', 'type', 'Foo'])
43
+ .and_return(PuppetDBRequest.new([]))
44
+ end
45
+
46
+ it 'should detect one problem' do
47
+ expect(problems).to have(1).problems
48
+ end
49
+
50
+ it 'should create a warning' do
51
+ expect(problems).to contain_warning('Define Foo seems unused')
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,110 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'ghostbuster_files' do
4
+ let(:path) { "./modules/foo/files/bar/baz" }
5
+ let(:code) { "" }
6
+
7
+ context 'with fix disabled' do
8
+
9
+ context 'when file usage is found in puppetdb' do
10
+
11
+ before :each do
12
+ expect_any_instance_of(PuppetDB::Client).to\
13
+ receive(:request).with(
14
+ 'resources',
15
+ [:'=', ['parameter', 'source'], "puppet:///modules/foo/bar/baz"],
16
+ )
17
+ .and_return(PuppetDBRequest.new([{}]))
18
+ end
19
+
20
+ it 'should not detect any problem' do
21
+ expect(problems).to have(0).problems
22
+ end
23
+ end
24
+
25
+ context 'when file usage is not found in puppetdb' do
26
+
27
+ before :each do
28
+ expect_any_instance_of(PuppetDB::Client).to\
29
+ receive(:request).with(
30
+ 'resources',
31
+ [:'=', ['parameter', 'source'], "puppet:///modules/foo/bar/baz"],
32
+ )
33
+ .and_return(PuppetDBRequest.new([]))
34
+ end
35
+
36
+ context 'when parent directory with recurse => true usage is found in puppetdb' do
37
+ before :each do
38
+ expect_any_instance_of(PuppetDB::Client).to \
39
+ receive(:request).with(
40
+ 'resources',
41
+ [:'and',
42
+ [:'or',
43
+ [:'=', ['parameter', 'source'], "puppet:///modules/foo/bar"],
44
+ [:'=', ['parameter', 'source'], "puppet:///modules/foo/bar/"],
45
+ ],
46
+ [:'=', ['parameter', 'recurse'], true],
47
+ ],
48
+ )
49
+ .and_return(PuppetDBRequest.new([
50
+ {
51
+ },
52
+ ]))
53
+ end
54
+
55
+ it 'should not detect any problem' do
56
+ expect(problems).to have(0).problems
57
+ end
58
+ end
59
+
60
+ context 'when parent directory usage is not found in puppetdb' do
61
+ before :each do
62
+ expect_any_instance_of(PuppetDB::Client).to \
63
+ receive(:request).with(
64
+ 'resources',
65
+ [:'and',
66
+ [:'or',
67
+ [:'=', ['parameter', 'source'], "puppet:///modules/foo/bar"],
68
+ [:'=', ['parameter', 'source'], "puppet:///modules/foo/bar/"],
69
+ ],
70
+ [:'=', ['parameter', 'recurse'], true],
71
+ ],
72
+ )
73
+ .and_return(PuppetDBRequest.new([]))
74
+ end
75
+
76
+ context 'when file usage is found in manifests' do
77
+
78
+ before :each do
79
+ expect(Dir).to receive(:glob){["./modules/foo/manifests/bar.pp" ]}
80
+ end
81
+
82
+ context 'when using full module name syntax' do
83
+ it 'should not detect any problem' do
84
+ expect(File).to receive(:readlines).with("./modules/foo/manifests/bar.pp").and_return(["file{'foo':", " content => file('foo/bar/baz'),", " }"])
85
+ expect(problems).to have(0).problems
86
+ end
87
+ end
88
+
89
+ context 'when using $module_name syntax' do
90
+ it 'should not detect any problem' do
91
+ expect(File).to receive(:readlines).with("./modules/foo/manifests/bar.pp").and_return(["file{'foo':", " content => file('bar/foo'),", " }"])
92
+ expect(File).to receive(:readlines).with("./modules/foo/manifests/bar.pp").and_return(["file{'foo':", ' content => file("${module_name}/bar/baz"),', " }"])
93
+ expect(problems).to have(0).problems
94
+ end
95
+ end
96
+ end
97
+
98
+ context 'when file usage is not found in manifests' do
99
+ it 'should detect one problem' do
100
+ expect(problems).to have(1).problems
101
+ end
102
+
103
+ it 'should create a warning' do
104
+ expect(problems).to contain_warning("File foo/bar/baz seems unused")
105
+ end
106
+ end
107
+ end
108
+ end
109
+ end
110
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'ghostbuster_templates' do
4
+ let(:path) { "./modules/foo/templates/bar" }
5
+ let(:code) { "" }
6
+
7
+ context 'with fix disabled' do
8
+
9
+ context 'when template usage is found in manifests' do
10
+
11
+ before :each do
12
+ expect(Dir).to receive(:glob){["./modules/foo/manifests/bar.pp" ]}
13
+ end
14
+
15
+ context 'when using full module name syntax' do
16
+ it 'should not detect any problem' do
17
+ expect(File).to receive(:readlines).with("./modules/foo/manifests/bar.pp").and_return(["file{'foo':", " content => template('foo/bar'),", " }"])
18
+ expect(problems).to have(0).problems
19
+ end
20
+ end
21
+
22
+ context 'when using $module_name syntax' do
23
+ it 'should not detect any problem' do
24
+ expect(File).to receive(:readlines).with("./modules/foo/manifests/bar.pp").and_return(["file{'foo':", " content => template('bar/foo'),", " }"])
25
+ expect(File).to receive(:readlines).with("./modules/foo/manifests/bar.pp").and_return(["file{'foo':", ' content => template("${module_name}/bar"),', " }"])
26
+ expect(problems).to have(0).problems
27
+ end
28
+ end
29
+ end
30
+
31
+ context 'when template usage is not found in manifests' do
32
+ it 'should detect one problem' do
33
+ expect(problems).to have(1).problems
34
+ end
35
+
36
+ it 'should create a warning' do
37
+ expect(problems).to contain_warning("Template foo/bar seems unused")
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,3 @@
1
+ require 'puppet-lint'
2
+
3
+ PuppetLint::Plugins.load_spec_helper
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: puppet-ghostbuster
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.5
4
+ version: 0.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Camptocamp
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2016-05-04 00:00:00.000000000 Z
11
+ date: 2016-05-10 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: coveralls
@@ -42,16 +42,44 @@ dependencies:
42
42
  name: rspec
43
43
  requirement: !ruby/object:Gem::Requirement
44
44
  requirements:
45
- - - ">="
45
+ - - "~>"
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
47
+ version: '3.0'
48
48
  type: :development
49
49
  prerelease: false
50
50
  version_requirements: !ruby/object:Gem::Requirement
51
51
  requirements:
52
- - - ">="
52
+ - - "~>"
53
53
  - !ruby/object:Gem::Version
54
- version: '0'
54
+ version: '3.0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec-its
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '1.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '1.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-collection_matchers
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.0'
55
83
  - !ruby/object:Gem::Dependency
56
84
  name: github_changelog_generator
57
85
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +122,20 @@ dependencies:
94
122
  - - ">="
95
123
  - !ruby/object:Gem::Version
96
124
  version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: puppet-lint
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '1.0'
132
+ type: :runtime
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '1.0'
97
139
  - !ruby/object:Gem::Dependency
98
140
  name: puppetdb-ruby
99
141
  requirement: !ruby/object:Gem::Requirement
@@ -110,8 +152,7 @@ dependencies:
110
152
  version: '0'
111
153
  description: Try and find dead code in Puppet receipts
112
154
  email:
113
- executables:
114
- - puppet-ghostbuster
155
+ executables: []
115
156
  extensions: []
116
157
  extra_rdoc_files: []
117
158
  files:
@@ -122,13 +163,18 @@ files:
122
163
  - LICENSE
123
164
  - README.md
124
165
  - Rakefile
125
- - bin/puppet-ghostbuster
126
- - lib/puppet-ghostbuster.rb
127
- - lib/puppet-ghostbuster/bin.rb
128
- - lib/puppet-ghostbuster/configuration.rb
129
- - lib/puppet-ghostbuster/optparser.rb
166
+ - lib/puppet-ghostbuster/puppetdb.rb
130
167
  - lib/puppet-ghostbuster/version.rb
168
+ - lib/puppet-lint/plugins/check_ghostbuster_classes.rb
169
+ - lib/puppet-lint/plugins/check_ghostbuster_defines.rb
170
+ - lib/puppet-lint/plugins/check_ghostbuster_files.rb
171
+ - lib/puppet-lint/plugins/check_ghostbuster_templates.rb
131
172
  - puppet-ghostbuster.gemspec
173
+ - spec/puppet-lint/plugins/ghostbuster_classes_spec.rb
174
+ - spec/puppet-lint/plugins/ghostbuster_defines_spec.rb
175
+ - spec/puppet-lint/plugins/ghostbuster_files_spec.rb
176
+ - spec/puppet-lint/plugins/ghostbuster_templates_spec.rb
177
+ - spec/spec_helper.rb
132
178
  homepage: http://github.com/camptocamp/puppet-ghostbuster
133
179
  licenses:
134
180
  - Apache-2.0
@@ -1,7 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- $:.unshift File.join(File.dirname(__FILE__), *%w[.. lib])
4
-
5
- require 'puppet-ghostbuster'
6
-
7
- exit PuppetGhostbuster::Bin.new(ARGV).run
@@ -1,31 +0,0 @@
1
- require 'puppet-ghostbuster/optparser'
2
- class PuppetGhostbuster::Bin
3
- def initialize(args)
4
- @args = args
5
- end
6
-
7
- def run
8
- opts = PuppetGhostbuster::OptParser.build
9
-
10
- begin
11
- opts.parse!(@args)
12
- rescue OptionParser::InvalidOption
13
- puts "puppet-ghostbuster: #{$!.message}"
14
- puts "puppet-ghostbuster: try 'puppet-ghostbuster --help' for more information"
15
- return 1
16
- end
17
-
18
- if PuppetGhostbuster.configuration.display_version
19
- puts "puppet-ghostbuster #{PuppetGhostbuster::VERSION}"
20
- return 0
21
- end
22
-
23
- if @args[0].nil?
24
- PuppetGhostbuster.new().run
25
- else
26
- PuppetGhostbuster.new(@args[0]).run
27
- end
28
-
29
- exit 0
30
- end
31
- end
@@ -1,96 +0,0 @@
1
- class PuppetGhostbuster
2
- # Public: A singleton class to store the running configuration of
3
- # puppet-ghostbuster.
4
- class Configuration
5
-
6
- def initialize
7
- Puppet.initialize_settings
8
- end
9
-
10
- # Public: Catch situations where options are being set for the first time
11
- # and create the necessary methods to get & set the option in the future.
12
- #
13
- # args - An Array of values to set the option to.
14
- # method - The String name of the option.
15
- # block - Unused.
16
- #
17
- # Returns nothing.
18
- #
19
- # Signature
20
- #
21
- # <option>=(value)
22
- def method_missing(method, *args, &block)
23
- if method.to_s =~ /^(\w+)=$/
24
- option = $1
25
- add_option(option.to_s) if settings[option].nil?
26
- settings[option] = args[0]
27
- else
28
- nil
29
- end
30
- end
31
-
32
- # Internal: Add options to the PuppetGhostbuster::Configuration object from inside
33
- # the class.
34
- #
35
- # option - The String name of the option.
36
- #
37
- # Returns nothing.
38
- #
39
- # Signature
40
- #
41
- # <option>
42
- # <option>=(value)
43
- def add_option(option)
44
- self.class.add_option(option)
45
- end
46
-
47
- # Public: Add an option to the PuppetGhostbuster::Configuration object from
48
- # outside the class.
49
- #
50
- # option - The String name of the option.
51
- #
52
- # Returns nothing.
53
- #
54
- # Signature
55
- #
56
- # <option>
57
- # <option>=(value)
58
- def self.add_option(option)
59
- # Public: Set the value of the named option.
60
- #
61
- # value - The value to set the option to.
62
- #
63
- # Returns nothing.
64
- define_method("#{option}=") do |value|
65
- settings[option] = value
66
- end
67
-
68
- # Public: Get the value of the named option.
69
- #
70
- # Returns the value of the option.
71
- define_method(option) do
72
- settings[option]
73
- end
74
- end
75
-
76
- # Internal: Access the internal storage for settings.
77
- #
78
- # Returns a Hash containing all the settings.
79
- def settings
80
- @settings ||= {}
81
- end
82
-
83
- # Public: Clear the PuppetGhostbuster::Configuration storage and set some sane
84
- # default values.
85
- #
86
- # Returns nothing.
87
- def defaults
88
- settings.clear
89
- self.loglevel = Logger::INFO
90
- self.puppetdbserverurl = "https://#{Puppet[:server]}:8081"
91
- self.hostprivkey = Puppet[:hostprivkey]
92
- self.hostcert = Puppet[:hostcert]
93
- self.localcacert = Puppet[:localcacert]
94
- end
95
- end
96
- end
@@ -1,68 +0,0 @@
1
- require 'optparse'
2
-
3
- # Public: Contains the puppet-ghostbuster option parser so that it can be used easily
4
- # in multiple places.
5
- class PuppetGhostbuster::OptParser
6
- HELP_TEXT = <<-EOF
7
- puppet-ghostbuster
8
-
9
- Basic Command Line Usage:
10
- puppet-ghostbuster [OPTIONS] [PATH]
11
-
12
- PATH Path to the Root directory of puppet code. Current dir by default.
13
-
14
- Option:
15
- EOF
16
-
17
- # Public: Initialise a new puppet-ghostbuster OptionParser.
18
- #
19
- # Returns an OptionParser object.
20
- def self.build
21
- OptionParser.new do |opts|
22
- opts.banner = HELP_TEXT
23
-
24
- opts.on('--version', 'Display the current version.') do
25
- PuppetGhostbuster.configuration.display_version = true
26
- end
27
-
28
- opts.on('-c', '--config FILE', 'Load puppet-ghostbuster options from file.') do |file|
29
- opts.load(file)
30
- end
31
-
32
- opts.on('--log-level LEVEL', [:debug, :info, :warn, :error],
33
- 'The level of verbosity (debug, info, warn or error)') do |loglevel|
34
- PuppetGhostbuster.configuration.loglevel=Logger::DEBUG if loglevel==:debug
35
- PuppetGhostbuster.configuration.loglevel=Logger::INFO if loglevel==:info
36
- PuppetGhostbuster.configuration.loglevel=Logger::WARN if loglevel==:warn
37
- PuppetGhostbuster.configuration.loglevel=Logger::ERROR if loglevel==:error
38
- end
39
-
40
- opts.on('-s', '--puppetdburl SERVER',
41
- 'puppet db server url to connect to.',
42
- 'Defaults to the puppet server found in puppet configuration') do |s|
43
- PuppetGhostbuster.configuration.puppetdbserverurl = s
44
- end
45
-
46
- opts.on('--key FILE', 'Load private key from the given file.') do |key|
47
- PuppetGhostbuster.configuration.hostprivkey = key
48
- end
49
-
50
- opts.on('--cert FILE', 'Load cert from the given file.') do |cert|
51
- PuppetGhostbuster.configuration.hostcert = cert
52
- end
53
-
54
- opts.on('--ca FILE', 'Load local ca cert from the given file.') do |ca|
55
- PuppetGhostbuster.configuration.localcacert = ca
56
- end
57
-
58
- opts.load('/etc/puppet-ghostbuster.rc')
59
- begin
60
- opts.load(File.expand_path('~/.puppet-ghostbuster.rc')) if ENV['HOME']
61
- rescue Errno::EACCES
62
- # silently skip loading this file if HOME is set to a directory that
63
- # the user doesn't have read access to.
64
- end
65
- opts.load('.puppet-ghostbuster.rc')
66
- end
67
- end
68
- end
@@ -1,207 +0,0 @@
1
- require 'json'
2
- require 'puppet'
3
- require 'puppetdb'
4
- require 'optparse'
5
-
6
- require 'puppet-ghostbuster/version'
7
- require 'puppet-ghostbuster/bin'
8
- require 'puppet-ghostbuster/configuration'
9
-
10
- class PuppetGhostbuster
11
-
12
- attr_accessor :path
13
- @@issues = []
14
-
15
- def exclude(filelist)
16
- ignorefile = "#{path}/.ghostbusterignore"
17
- if File.exist?(ignorefile) then
18
- ignorelist = File.readlines(ignorefile).each {|l| l.chomp!}
19
- ignorelist.each do |ignorerule|
20
- filelist.reject! { |item| item =~ /^#{path}\/#{ignorerule}/ }
21
- end
22
- end
23
- filelist
24
- end
25
-
26
- def manifests
27
- exclude Dir["#{path}/**/manifests/**/*.pp"]
28
- end
29
-
30
- def templates
31
- exclude Dir["#{path}/**/templates/**/*"]
32
- end
33
-
34
- def files
35
- exclude Dir["#{path}/**/files/**/*"]
36
- end
37
-
38
- def self.configuration
39
- @configuration ||= PuppetGhostbuster::Configuration.new
40
- end
41
-
42
- def configuration
43
- self.class.configuration
44
- end
45
-
46
- def self.puppetdbserverfilename
47
- return configuration.puppetdbserverurl.gsub(/[:\/]/,'_')
48
- end
49
-
50
- def self.cache
51
- "/var/tmp/puppet-ghostbuster.#{puppetdbserverfilename}.cache"
52
- end
53
-
54
- def self.update_cache(value)
55
- File.open(cache, 'w') do |f|
56
- f.write(value)
57
- end
58
- value
59
- end
60
-
61
- def self.get_cache
62
- if File.exists?(cache)
63
- JSON.parse(File.read(cache))
64
- else
65
- false
66
- end
67
- end
68
-
69
- def self.client
70
- @@logger.debug "Connecting to puppet DB #{configuration.puppetdbserverurl}"
71
- PuppetDB::Client.new({
72
- :server => "#{configuration.puppetdbserverurl}/pdb/query",
73
- :pem => {
74
- 'key' => configuration.hostprivkey,
75
- 'cert' => configuration.hostcert,
76
- 'ca_file' => configuration.localcacert,
77
- }
78
- }, 4)
79
- end
80
-
81
- def self.used_classes
82
- return get_cache || update_cache(
83
- client.request(
84
- 'resources',
85
- [:'=', 'type', 'Class'],
86
- ).data.map { |resource|
87
- resource['title']
88
- }
89
- )
90
- end
91
-
92
- def find_unused_classes
93
- @@logger.info 'Now trying to find unused classes'
94
- manifests.each do |file|
95
- @@logger.debug " file #{file}."
96
- if File.symlink?(file)
97
- @@logger.warn " Skipping symlink #{file}"
98
- next
99
- end
100
- if c = File.readlines(file).grep(/^class\s+([^\s\(\{]+)/){$1}[0]
101
- class_name = c.split('::').map(&:capitalize).join('::')
102
- count = self.class.used_classes.select { |klass| klass == class_name }.size
103
- @@issues << { :title => "[GhostBuster] Class #{class_name} seems unused", :body => file } if count == 0
104
- end
105
- end
106
- end
107
-
108
- def find_unused_defines
109
- @@logger.info 'Now trying to find unused defines'
110
- manifests.each do |file|
111
- if File.symlink?(file)
112
- @@logger.warn " Skipping symlink #{file}"
113
- next
114
- end
115
- if d = File.readlines(file).grep(/^define\s+([^\s\(\{]+)/){$1}[0]
116
- define_name = d.split('::').map(&:capitalize).join('::')
117
- count = self.class.client.request('resources', [:'=', 'type', define_name]).data.size
118
- @@issues << { :title => "[GhostBuster] Define #{define_name} seems unused", :body => file } if count == 0
119
- end
120
- end
121
- end
122
-
123
- def find_unused_templates
124
- @@logger.info 'Now trying to find unused templates'
125
- templates.each do |template|
126
- next unless File.file?(template)
127
- module_name, template_name = template.match(/.*\/([^\/]+)\/templates\/(.+)$/).captures
128
- count = 0
129
- manifests.each do |manifest|
130
- if File.symlink?(manifest)
131
- @@logger.warn " Skipping symlink #{manifest}"
132
- next
133
- end
134
- if match = manifest.match(/.*\/([^\/]+)\/manifests\/.+$/)
135
- manifest_module_name = match.captures[0]
136
- count += File.readlines(manifest).grep(/["']\$\{module_name\}\/#{template_name}["']/).size if manifest_module_name == module_name
137
- end
138
- count += File.readlines(manifest).grep(/["']#{module_name}\/#{template_name}["']/).size
139
- end
140
- @@issues << { :title => "[GhostBuster] Template #{module_name}/#{template_name} seems unused", :body => template } if count == 0
141
- end
142
- end
143
-
144
- def find_unused_files
145
- @@logger.info 'Now trying to find unused files'
146
- files.each do |file|
147
- next unless File.file?(file)
148
- module_name, file_name = file.match(/.*\/([^\/]+)\/files\/(.+)$/).captures
149
- found = false
150
-
151
- found = true if self.class.client.request('resources', [:'=', ['parameter', 'source'], "puppet:///modules/#{module_name}/#{file_name}"],).data.size > 0
152
-
153
- dir_name = File.dirname(file_name)
154
- while dir_name != '.' do
155
- found = true if self.class.client.request(
156
- 'resources',
157
- [:'and',
158
- [:'or',
159
- [:'=', ['parameter', 'source'], "puppet:///modules/#{module_name}/#{dir_name}"],
160
- [:'=', ['parameter', 'source'], "puppet:///modules/#{module_name}/#{dir_name}/"],
161
- ],
162
- [:'=', ['parameter', 'recurse'], true],
163
- ],
164
- ).data.size > 0
165
- dir_name = File.dirname(dir_name)
166
- end
167
-
168
- manifests.each do |manifest|
169
- if File.symlink?(manifest)
170
- @@logger.warn " Skipping symlink #{manifest}"
171
- next
172
- end
173
-
174
- if match = manifest.match(/.*\/([^\/]+)\/manifests\/.+$/)
175
- manifest_module_name = match.captures[0]
176
- if manifest_module_name == module_name
177
- found = true if File.readlines(manifest).grep(/["']\$\{module_name\}\/#{file_name}["']/).size > 0
178
- break if found
179
- end
180
- end
181
- found = true if File.readlines(manifest).grep(/#{module_name}\/#{file_name}/).size > 0
182
- break if found
183
- end
184
-
185
- @@issues << { :title => "[GhostBuster] File #{module_name}/#{file_name} seems unused", :body => file } unless found
186
- end
187
- end
188
-
189
- def initialize(path = '.')
190
- self.path = path
191
- @@logger = Logger.new(STDERR).tap do |log|
192
- log.progname = 'PuppetGhostbuster'
193
- log.level=PuppetGhostbuster.configuration.loglevel
194
- end
195
- end
196
-
197
- def run
198
- find_unused_classes
199
- find_unused_defines
200
- find_unused_templates
201
- find_unused_files
202
- puts @@issues.to_json
203
- end
204
-
205
- end
206
-
207
- PuppetGhostbuster.configuration.defaults