puppet-ghostbuster 0.4.5 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
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