preflight_check 0.1.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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 39343dc954e21f4d4cf2b2231f6225ea95de7e4b
4
+ data.tar.gz: 4d1f5e1a5013bd4e02176145618322bd3e890018
5
+ SHA512:
6
+ metadata.gz: 4f979fffa1fffed13916d097999af3e40332519414c8c78667edb936337bff049c6fb4423407e6b3b466a91f516cc81ee4a71b0c3866e6da0a8a0a6263b102cb
7
+ data.tar.gz: d57e7844232c55a44f03dada5e5d3466a71e22c93cf32a8732322bbce4e4720939da758efa8a70dd34633029ae414aa1fbadc0a1c5446ce7f31a2f75468a2894
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --format documentation
2
+ --color
data/.ruby-version ADDED
@@ -0,0 +1 @@
1
+ 2.1.3
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in preflight_check.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,33 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ preflight_check (0.0.1)
5
+ colorize
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ colorize (0.7.7)
11
+ diff-lcs (1.2.5)
12
+ rake (10.4.2)
13
+ rspec (3.1.0)
14
+ rspec-core (~> 3.1.0)
15
+ rspec-expectations (~> 3.1.0)
16
+ rspec-mocks (~> 3.1.0)
17
+ rspec-core (3.1.7)
18
+ rspec-support (~> 3.1.0)
19
+ rspec-expectations (3.1.2)
20
+ diff-lcs (>= 1.2.0, < 2.0)
21
+ rspec-support (~> 3.1.0)
22
+ rspec-mocks (3.1.3)
23
+ rspec-support (~> 3.1.0)
24
+ rspec-support (3.1.2)
25
+
26
+ PLATFORMS
27
+ ruby
28
+
29
+ DEPENDENCIES
30
+ bundler (~> 1.7)
31
+ preflight_check!
32
+ rake (~> 10.0)
33
+ rspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2016 Max Gordon
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,35 @@
1
+ [![Circle CI](https://circleci.com/gh/postazure/preflight_check.svg?style=svg)](https://circleci.com/gh/postazure/preflight_check)
2
+ [![Gem Version](https://badge.fury.io/rb/preflight_check.svg)](https://badge.fury.io/rb/preflight_check)
3
+
4
+ # PreflightCheck
5
+
6
+ Validates that you have defined all of the resources that your jobs are depending on.
7
+ Validates that all declared resources are consumed.
8
+
9
+ ![Success Screenshot](https://github.com/postazure/preflight_check/blob/master/doc-support/success_use_screenshot.png "Success Use")
10
+ ![Failure Screenshot](https://github.com/postazure/preflight_check/blob/master/doc-support/failure_use_screenshot.png "Failure Use")
11
+
12
+ ## Installation
13
+
14
+ install it as:
15
+
16
+ $ gem install preflight_check
17
+
18
+ ### Usage
19
+
20
+ $ preflight_check /path/to/config_file.yml
21
+
22
+ ## Contributing
23
+
24
+ 1. Fork it ( https://github.com/[my-github-username]/preflight_check/fork )
25
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
26
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
27
+ 4. Push to the branch (`git push origin my-new-feature`)
28
+ 5. Create a new Pull Request
29
+
30
+
31
+ ## Coming Soon
32
+
33
+ 1. Wrap concourse cli `fly` command to validate then run `fly`
34
+ 1. Check that tasks are defined
35
+ 1. Check concourse config file structure to ensure everything that is declared exists
data/Rakefile ADDED
@@ -0,0 +1,7 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
7
+
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+ require 'preflight_check'
3
+
4
+ require 'colorize'
5
+
6
+ def print_failed_jobs(result)
7
+ puts "\nMissing Dependencies".underline
8
+ result.failed_jobs.each do |job|
9
+ job.missing_dependencies.each do |dep|
10
+ puts " Job: '#{job.name}', is missing resource: #{dep.name}"
11
+ end
12
+ end
13
+ end
14
+
15
+ def print_failed_resources(result)
16
+ puts "\nUnused Resources Declared".underline
17
+ result.failed_resources.each do |resource|
18
+ puts " Resource: '#{resource.name}' was defined, but never used."
19
+ end
20
+ end
21
+
22
+
23
+ def get_manifest_path
24
+ path = ARGV.first
25
+ if path.nil? || path.empty?
26
+ puts 'Include path to yml as command line argument.'.yellow
27
+ puts 'ie. $ validate_manifest /path/to/file.yml'.yellow
28
+ exit
29
+ end
30
+ path
31
+ end
32
+
33
+ def run
34
+ path = get_manifest_path
35
+ concourse_yml_validator = ConcourseYmlValidator.new
36
+ result = concourse_yml_validator.validate(path: path)
37
+ puts "\n"
38
+ if result.success?
39
+ puts "SUCCESS, Maifest '#{path}' is valid.".green
40
+ puts 'You are cleared for takeoff.'
41
+
42
+ else
43
+ puts "FAILURE, Manifest '#{path}' is invalid.".red
44
+ print_failed_jobs(result) unless result.failed_jobs.empty?
45
+ print_failed_resources(result) unless result.failed_resources.empty?
46
+ end
47
+ puts "\n"
48
+ end
49
+
50
+ run
@@ -0,0 +1,70 @@
1
+ resources:
2
+ - name: main-project
3
+ type: git
4
+ source:
5
+ uri: git@github.com:something/sample.git
6
+
7
+ - name: ci-config-repo
8
+ type: git
9
+ source:
10
+ uri: git@github.com:something/sample-other.git
11
+
12
+ - name: cached-jar-1
13
+ type: s3
14
+ source:
15
+ bucket: concourse-instance-name
16
+ versioned_file: cached-jar-1.jar
17
+
18
+ - name: cached-jar-2
19
+ type: s3
20
+ source:
21
+ bucket: concourse-instance-name
22
+ versioned_file: cached-jar-1.jar
23
+
24
+
25
+ jobs:
26
+ - name: build-jar-1
27
+ plan:
28
+ - get: ci-config-repo
29
+ trigger: true
30
+ - get: main-project
31
+ trigger: true
32
+ - task: build-jar-1-task
33
+ file: ci-config-repo/tasks/build-jar-1-task/build-jar-1-task.yml
34
+ - put: cached-jar-1
35
+ params:
36
+ file: jar/project-0.0.1-SNAPSHOT.jar
37
+
38
+ - name: build-jar-2
39
+ plan:
40
+ - get: ci-config-repo
41
+ trigger: true
42
+ - get: main-project
43
+ trigger: true
44
+ - task: build-jar-2-task
45
+ file: ci-config-repo/tasks/build-jar-2-task/build-jar-2-task.yml
46
+ - put: cached-jar-2
47
+ params:
48
+ file: jar/project-0.0.1-SNAPSHOT.jar
49
+
50
+ - name: deploy-jar-1
51
+ plan:
52
+ - get: main-project
53
+ passed: [build-jar-1]
54
+ - get: ci-config-repo
55
+ - get: cached-jar-1
56
+ passed: [build-jar-1]
57
+ trigger: true
58
+ - task: deploy-jar-1-task
59
+ file: ci-config-repo/tasks/deploy-jar-1-task/deploy-jar-1-task.yml
60
+
61
+ - name: deploy-jar-2
62
+ plan:
63
+ - get: main-project
64
+ passed: [deploy-jar-2]
65
+ - get: ci-config-repo
66
+ - get: cached-jar-2
67
+ passed: [deploy-jar-2]
68
+ trigger: true
69
+ - task: deploy-jar-2-task
70
+ file: ci-config-repo/tasks/deploy-jar-2-task/deploy-jar-2-task.yml
@@ -0,0 +1,76 @@
1
+ resources:
2
+ - name: main-project
3
+ type: git
4
+ source:
5
+ uri: git@github.com:something/sample.git
6
+
7
+ - name: ci-config-repo
8
+ type: git
9
+ source:
10
+ uri: git@github.com:something/sample-other.git
11
+
12
+ - name: cached-jar-1
13
+ type: s3
14
+ source:
15
+ bucket: concourse-instance-name
16
+ versioned_file: cached-jar-1.jar
17
+
18
+ - name: misssssspelled-thingz
19
+ type: s3
20
+ source:
21
+ bucket: concourse-instance-name
22
+ versioned_file: cached-jar-1.jar
23
+
24
+ - name: extra-thing
25
+ type: s3
26
+ source:
27
+ bucket: concourse-instance-name
28
+ versioned_file: cached-jar-1.jar
29
+
30
+
31
+ jobs:
32
+ - name: build-jar-1
33
+ plan:
34
+ - get: ci-config-repo
35
+ trigger: true
36
+ - get: main-project
37
+ trigger: true
38
+ - task: build-jar-1-task
39
+ file: ci-config-repo/tasks/build-jar-1-task/build-jar-1-task.yml
40
+ - put: cached-jar-1
41
+ params:
42
+ file: jar/project-0.0.1-SNAPSHOT.jar
43
+
44
+ - name: build-jar-2
45
+ plan:
46
+ - get: ci-config-repo
47
+ trigger: true
48
+ - get: main-project
49
+ trigger: true
50
+ - task: build-jar-2-task
51
+ file: ci-config-repo/tasks/build-jar-2-task/build-jar-2-task.yml
52
+ - put: cached-jar-2
53
+ params:
54
+ file: jar/project-0.0.1-SNAPSHOT.jar
55
+
56
+ - name: deploy-jar-1
57
+ plan:
58
+ - get: main-project
59
+ passed: [build-jar-1]
60
+ - get: ci-config-repo
61
+ - get: cached-jar-1
62
+ passed: [build-jar-1]
63
+ trigger: true
64
+ - task: deploy-jar-1-task
65
+ file: ci-config-repo/tasks/deploy-jar-1-task/deploy-jar-1-task.yml
66
+
67
+ - name: deploy-jar-2
68
+ plan:
69
+ - get: main-project
70
+ passed: [deploy-jar-2]
71
+ - get: ci-config-repo
72
+ - get: cached-jar-2
73
+ passed: [deploy-jar-2]
74
+ trigger: true
75
+ - task: deploy-jar-2-task
76
+ file: ci-config-repo/tasks/deploy-jar-2-task/deploy-jar-2-task.yml
@@ -0,0 +1,48 @@
1
+ require_relative 'validator_result'
2
+
3
+ class ConcourseCorrelator
4
+ def process_jobs(jobs:, resources:)
5
+
6
+ jobs.each { |job| mark_missing_dependencies(job, resources) }
7
+
8
+ used_resources = build_utilized_resource_list(jobs)
9
+
10
+ failed_resources = []
11
+ resources.each do |resource|
12
+ consumed_resource = used_resources.find do |used_resource_name|
13
+ used_resource_name == resource.name
14
+ end
15
+ if consumed_resource.nil?
16
+ failed_resources << resource
17
+ end
18
+ end
19
+
20
+ failed_jobs = jobs.reject(&:success?)
21
+ ValidatorResult.new(failed_jobs: failed_jobs, failed_resources: failed_resources)
22
+ end
23
+
24
+ def build_utilized_resource_list(jobs)
25
+ utilized_resource_names = []
26
+ jobs.each do |job|
27
+ job.dependencies.each do |dep|
28
+ utilized_resource_names << dep.name
29
+ end
30
+ end
31
+ utilized_resource_names
32
+ end
33
+
34
+ def mark_missing_dependencies(job, resources)
35
+ job.dependencies.each do |dep|
36
+ if dep_is_defined_as_resource(dep, resources)
37
+ dep.defined = true
38
+ else
39
+ job.mark_as_failed
40
+ dep.defined = false
41
+ end
42
+ end
43
+ end
44
+
45
+ def dep_is_defined_as_resource(dep, resources)
46
+ resources.find { |resource| resource.name == dep.name }
47
+ end
48
+ end
@@ -0,0 +1,20 @@
1
+ class ConcourseJob
2
+ attr_accessor :name, :dependencies
3
+ def initialize(name:, dependencies:)
4
+ @name = name
5
+ @dependencies = dependencies
6
+ @success = true
7
+ end
8
+
9
+ def mark_as_failed
10
+ @success = false
11
+ end
12
+
13
+ def success?
14
+ @success
15
+ end
16
+
17
+ def missing_dependencies
18
+ dependencies.reject(&:defined?)
19
+ end
20
+ end
@@ -0,0 +1,15 @@
1
+ class ConcourseJobDependency
2
+ attr_accessor :name
3
+ def initialize(name:)
4
+ @name = name
5
+ @is_defined = nil
6
+ end
7
+
8
+ def defined= value
9
+ @is_defined = value
10
+ end
11
+
12
+ def defined?
13
+ @is_defined
14
+ end
15
+ end
@@ -0,0 +1,13 @@
1
+ require_relative 'concourse_job'
2
+ require_relative 'concourse_job_dependency'
3
+
4
+ class ConcourseJobFactory
5
+ def build(job_info)
6
+ dependencies = []
7
+ job_info['plan'].each do |dependency|
8
+ name = dependency['get'] || dependency['put']
9
+ dependencies << ConcourseJobDependency.new(name: name) unless name.nil?
10
+ end
11
+ ConcourseJob.new(name: job_info['name'], dependencies: dependencies)
12
+ end
13
+ end
@@ -0,0 +1,6 @@
1
+ class ConcourseResource
2
+ attr_accessor :name
3
+ def initialize(name:)
4
+ @name = name
5
+ end
6
+ end
@@ -0,0 +1,37 @@
1
+ require 'yaml'
2
+ require_relative 'concourse_resource'
3
+ require_relative 'concourse_job_factory'
4
+ require_relative 'concourse_correlator'
5
+
6
+
7
+ class ConcourseYmlValidator
8
+ def validate(path:)
9
+ yml_info = read_yml_file(path)
10
+ resources = init_resources(yml_info['resources'])
11
+ jobs = init_job(yml_info['jobs'])
12
+
13
+ correlator = ConcourseCorrelator.new
14
+ correlator.process_jobs(jobs: jobs, resources: resources)
15
+ end
16
+
17
+ private
18
+ def read_yml_file(path)
19
+ file = File.open(path, 'r')
20
+ contents = file.read
21
+ file.close
22
+
23
+ YAML.load(contents)
24
+ end
25
+
26
+ def init_resources(yaml_resources)
27
+ yaml_resources.map do |resource|
28
+ ConcourseResource.new(name: resource['name'])
29
+ end
30
+ end
31
+
32
+ def init_job(yaml_job)
33
+ yaml_job.map do |job|
34
+ ConcourseJobFactory.new.build(job)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,12 @@
1
+ class ValidatorResult
2
+ attr_accessor :failed_jobs, :failed_resources
3
+
4
+ def initialize(failed_jobs:, failed_resources:)
5
+ @failed_jobs = failed_jobs
6
+ @failed_resources = failed_resources
7
+ end
8
+
9
+ def success?
10
+ failed_jobs.empty? && failed_resources.empty?
11
+ end
12
+ end
@@ -0,0 +1,3 @@
1
+ module PreflightCheck
2
+ VERSION = "0.1.0"
3
+ end
@@ -0,0 +1,7 @@
1
+ require "preflight_check/version"
2
+ require_relative '../lib/preflight_check/concourse_yml_validator'
3
+
4
+ module PreflightCheck
5
+ class Validator < ConcourseYmlValidator
6
+ end
7
+ end
@@ -0,0 +1,26 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'preflight_check/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "preflight_check"
8
+ spec.version = PreflightCheck::VERSION
9
+ spec.authors = ["Max Gordon"]
10
+ spec.email = ["gordonmaxc@gmail.com"]
11
+ spec.summary = 'Pre-flight check your concourse config yaml before taking off.'
12
+ spec.description = "Validates that you have defined all of the resources that your jobs are depending on.\nValidates that all declared resources are consumed."
13
+ spec.homepage = ""
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ spec.add_development_dependency "rspec"
24
+
25
+ spec.add_dependency "colorize"
26
+ end
@@ -0,0 +1,50 @@
1
+ require_relative '../lib/preflight_check/concourse_yml_validator'
2
+
3
+ describe ConcourseCorrelator do
4
+ let(:job_list) { [ConcourseJob.new(name: 'foo_job', dependencies: [ConcourseJobDependency.new(name: 'bar_resource')])] }
5
+
6
+ describe '#proccess_jobs' do
7
+
8
+ context 'when jobs and resources match' do
9
+ let(:resource_list) { [ConcourseResource.new(name: 'bar_resource')] }
10
+
11
+ it 'should ensure that each job has all the dependencies that it specifies' do
12
+ expect(subject.process_jobs(jobs: job_list, resources: resource_list)).to be_a ValidatorResult
13
+ end
14
+ end
15
+
16
+ context 'when resources are missing' do
17
+ let(:resource_list) { [] }
18
+
19
+ it 'should ensure that each job has all the dependencies that it specifies' do
20
+ result = subject.process_jobs(jobs: job_list, resources: resource_list)
21
+ expect(result.success?).to be false
22
+
23
+ job = result.failed_jobs.first
24
+ expect(job.name).to eq 'foo_job'
25
+
26
+ missing_dep = job.missing_dependencies.first
27
+ expect(missing_dep.name).to eq 'bar_resource'
28
+ end
29
+ end
30
+
31
+ context 'when there are extra resources' do
32
+ let(:resource_list) do
33
+ [
34
+ ConcourseResource.new(name: 'bar_resource'),
35
+ ConcourseResource.new(name: 'extra-resource')
36
+ ]
37
+ end
38
+
39
+ it 'should ensure that all resources are used by jobs' do
40
+ result = subject.process_jobs(jobs: job_list, resources: resource_list)
41
+
42
+ expect(result.success?).to be false
43
+ expect(result.failed_resources.length).to eq 1
44
+
45
+ failed_resource = result.failed_resources.first
46
+ expect(failed_resource.name).to eq 'extra-resource'
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,45 @@
1
+ describe ConcourseJobFactory do
2
+ describe '#build' do
3
+
4
+ context 'when "get"' do
5
+ let(:jobs_yml) do
6
+ <<-YML
7
+ jobs:
8
+ - name: build-jar-1
9
+ plan:
10
+ - get: ci-config-repo
11
+ trigger: true
12
+ YML
13
+ end
14
+ it 'should build' do
15
+ job = subject.build(YAML.load(jobs_yml)['jobs'].first)
16
+
17
+ expect(job.name).to eq 'build-jar-1'
18
+
19
+ resource = job.dependencies.first
20
+ expect(resource.name).to eq 'ci-config-repo'
21
+ end
22
+ end
23
+
24
+ context 'when "put"' do
25
+ let(:jobs_yml) do
26
+ <<-YML
27
+ jobs:
28
+ - name: build-jar-1
29
+ plan:
30
+ - put: ci-config-repo
31
+ trigger: true
32
+ YML
33
+ end
34
+
35
+ it 'should build' do
36
+ job = subject.build(YAML.load(jobs_yml)['jobs'].first)
37
+
38
+ expect(job.name).to eq 'build-jar-1'
39
+
40
+ resource = job.dependencies.first
41
+ expect(resource.name).to eq 'ci-config-repo'
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,50 @@
1
+ describe ConcourseYmlValidator do
2
+ context 'when jobs have all their resources' do
3
+ let(:path_to_manifest) { File.join(File.dirname(__FILE__), 'support/yml_with_balanced_resources.yml') }
4
+
5
+ it 'should return a ValidatorResult' do
6
+ validator_result = subject.validate(path: path_to_manifest)
7
+ expect(validator_result).to be_a ValidatorResult
8
+ end
9
+
10
+ it 'should validate that all the "resources" exist for all the "jobs"' do
11
+ validator_result = subject.validate(path: path_to_manifest)
12
+ expect(validator_result.success?).to be true
13
+ end
14
+ end
15
+
16
+ context 'when jobs are missing resources' do
17
+ let(:path_to_manifest) { File.join(File.dirname(__FILE__), 'support/yml_with_missing_resources.yml') }
18
+
19
+ it 'should validate that all the "resources" exist for all the "jobs"' do
20
+ validator_result = subject.validate(path: path_to_manifest)
21
+ expect(validator_result.success?).to be false
22
+ end
23
+
24
+ it 'should have a result failed jobs with their missing deps marked' do
25
+ validator_result = subject.validate(path: path_to_manifest)
26
+
27
+ expect(validator_result.failed_jobs.size).to eq 2
28
+ expect(validator_result.failed_jobs.map(&:name)).to contain_exactly('build-jar-2', 'deploy-jar-2')
29
+ expect(validator_result.failed_jobs[0].missing_dependencies.first.name).to eq 'cached-jar-2'
30
+ expect(validator_result.failed_jobs[1].missing_dependencies.first.name).to eq 'cached-jar-2'
31
+ end
32
+ end
33
+
34
+ context 'when there are extra resources defined' do
35
+ let(:path_to_manifest) { File.join(File.dirname(__FILE__), 'support/yml_with_extra_resources.yml') }
36
+
37
+ it 'should fail because there are resources that are defined and not used' do
38
+ validator_result = subject.validate(path: path_to_manifest)
39
+ expect(validator_result.success?).to be false
40
+ end
41
+
42
+ it 'should have a result that includes extra resources' do
43
+ validator_result = subject.validate(path: path_to_manifest)
44
+
45
+ expect(validator_result.failed_resources.size).to eq 1
46
+ failed_resource = validator_result.failed_resources.first
47
+ expect(failed_resource.name).to eq 'extra-resource'
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe PreflightCheck do
4
+ it 'has a version number' do
5
+ expect(PreflightCheck::VERSION).not_to be nil
6
+ end
7
+ end
@@ -0,0 +1,2 @@
1
+ $LOAD_PATH.unshift File.expand_path('../../lib', __FILE__)
2
+ require 'preflight_check'
@@ -0,0 +1,70 @@
1
+ resources:
2
+ - name: main-project
3
+ type: git
4
+ source:
5
+ uri: git@github.com:something/sample.git
6
+
7
+ - name: ci-config-repo
8
+ type: git
9
+ source:
10
+ uri: git@github.com:something/sample-other.git
11
+
12
+ - name: cached-jar-1
13
+ type: s3
14
+ source:
15
+ bucket: concourse-instance-name
16
+ versioned_file: cached-jar-1.jar
17
+
18
+ - name: cached-jar-2
19
+ type: s3
20
+ source:
21
+ bucket: concourse-instance-name
22
+ versioned_file: cached-jar-1.jar
23
+
24
+
25
+ jobs:
26
+ - name: build-jar-1
27
+ plan:
28
+ - get: ci-config-repo
29
+ trigger: true
30
+ - get: main-project
31
+ trigger: true
32
+ - task: build-jar-1-task
33
+ file: ci-config-repo/tasks/build-jar-1-task/build-jar-1-task.yml
34
+ - put: cached-jar-1
35
+ params:
36
+ file: jar/project-0.0.1-SNAPSHOT.jar
37
+
38
+ - name: build-jar-2
39
+ plan:
40
+ - get: ci-config-repo
41
+ trigger: true
42
+ - get: main-project
43
+ trigger: true
44
+ - task: build-jar-2-task
45
+ file: ci-config-repo/tasks/build-jar-2-task/build-jar-2-task.yml
46
+ - put: cached-jar-2
47
+ params:
48
+ file: jar/project-0.0.1-SNAPSHOT.jar
49
+
50
+ - name: deploy-jar-1
51
+ plan:
52
+ - get: main-project
53
+ passed: [build-jar-1]
54
+ - get: ci-config-repo
55
+ - get: cached-jar-1
56
+ passed: [build-jar-1]
57
+ trigger: true
58
+ - task: deploy-jar-1-task
59
+ file: ci-config-repo/tasks/deploy-jar-1-task/deploy-jar-1-task.yml
60
+
61
+ - name: deploy-jar-2
62
+ plan:
63
+ - get: main-project
64
+ passed: [deploy-jar-2]
65
+ - get: ci-config-repo
66
+ - get: cached-jar-2
67
+ passed: [deploy-jar-2]
68
+ trigger: true
69
+ - task: deploy-jar-2-task
70
+ file: ci-config-repo/tasks/deploy-jar-2-task/deploy-jar-2-task.yml
@@ -0,0 +1,76 @@
1
+ resources:
2
+ - name: main-project
3
+ type: git
4
+ source:
5
+ uri: git@github.com:something/sample.git
6
+
7
+ - name: ci-config-repo
8
+ type: git
9
+ source:
10
+ uri: git@github.com:something/sample-other.git
11
+
12
+ - name: cached-jar-1
13
+ type: s3
14
+ source:
15
+ bucket: concourse-instance-name
16
+ versioned_file: cached-jar-1.jar
17
+
18
+ - name: cached-jar-2
19
+ type: s3
20
+ source:
21
+ bucket: concourse-instance-name
22
+ versioned_file: cached-jar-1.jar
23
+
24
+ - name: extra-resource
25
+ type: s3
26
+ source:
27
+ bucket: concourse-instance-name
28
+ versioned_file: cached-jar-1.jar
29
+
30
+
31
+ jobs:
32
+ - name: build-jar-1
33
+ plan:
34
+ - get: ci-config-repo
35
+ trigger: true
36
+ - get: main-project
37
+ trigger: true
38
+ - task: build-jar-1-task
39
+ file: ci-config-repo/tasks/build-jar-1-task/build-jar-1-task.yml
40
+ - put: cached-jar-1
41
+ params:
42
+ file: jar/project-0.0.1-SNAPSHOT.jar
43
+
44
+ - name: build-jar-2
45
+ plan:
46
+ - get: ci-config-repo
47
+ trigger: true
48
+ - get: main-project
49
+ trigger: true
50
+ - task: build-jar-2-task
51
+ file: ci-config-repo/tasks/build-jar-2-task/build-jar-2-task.yml
52
+ - put: cached-jar-2
53
+ params:
54
+ file: jar/project-0.0.1-SNAPSHOT.jar
55
+
56
+ - name: deploy-jar-1
57
+ plan:
58
+ - get: main-project
59
+ passed: [build-jar-1]
60
+ - get: ci-config-repo
61
+ - get: cached-jar-1
62
+ passed: [build-jar-1]
63
+ trigger: true
64
+ - task: deploy-jar-1-task
65
+ file: ci-config-repo/tasks/deploy-jar-1-task/deploy-jar-1-task.yml
66
+
67
+ - name: deploy-jar-2
68
+ plan:
69
+ - get: main-project
70
+ passed: [deploy-jar-2]
71
+ - get: ci-config-repo
72
+ - get: cached-jar-2
73
+ passed: [deploy-jar-2]
74
+ trigger: true
75
+ - task: deploy-jar-2-task
76
+ file: ci-config-repo/tasks/deploy-jar-2-task/deploy-jar-2-task.yml
@@ -0,0 +1,71 @@
1
+ resources:
2
+ - name: main-project
3
+ type: git
4
+ source:
5
+ uri: git@github.com:something/sample.git
6
+
7
+ - name: ci-config-repo
8
+ type: git
9
+ source:
10
+ uri: git@github.com:something/sample-other.git
11
+
12
+ - name: cached-jar-1
13
+ type: s3
14
+ source:
15
+ bucket: concourse-instance-name
16
+ versioned_file: cached-jar-1.jar
17
+
18
+ # MISSING RESOURCE
19
+ #- name: cached-jar-2
20
+ # type: s3
21
+ # source:
22
+ # bucket: concourse-instance-name
23
+ # versioned_file: cached-jar-1.jar
24
+
25
+
26
+ jobs:
27
+ - name: build-jar-1
28
+ plan:
29
+ - get: ci-config-repo
30
+ trigger: true
31
+ - get: main-project
32
+ trigger: true
33
+ - task: build-jar-1-task
34
+ file: ci-config-repo/tasks/build-jar-1-task/build-jar-1-task.yml
35
+ - put: cached-jar-1
36
+ params:
37
+ file: jar/project-0.0.1-SNAPSHOT.jar
38
+
39
+ - name: build-jar-2
40
+ plan:
41
+ - get: ci-config-repo
42
+ trigger: true
43
+ - get: main-project
44
+ trigger: true
45
+ - task: build-jar-2-task
46
+ file: ci-config-repo/tasks/build-jar-2-task/build-jar-2-task.yml
47
+ - put: cached-jar-2
48
+ params:
49
+ file: jar/project-0.0.1-SNAPSHOT.jar
50
+
51
+ - name: deploy-jar-1
52
+ plan:
53
+ - get: main-project
54
+ passed: [build-jar-1]
55
+ - get: ci-config-repo
56
+ - get: cached-jar-1
57
+ passed: [build-jar-1]
58
+ trigger: true
59
+ - task: deploy-jar-1-task
60
+ file: ci-config-repo/tasks/deploy-jar-1-task/deploy-jar-1-task.yml
61
+
62
+ - name: deploy-jar-2
63
+ plan:
64
+ - get: main-project
65
+ passed: [deploy-jar-2]
66
+ - get: ci-config-repo
67
+ - get: cached-jar-2
68
+ passed: [deploy-jar-2]
69
+ trigger: true
70
+ - task: deploy-jar-2-task
71
+ file: ci-config-repo/tasks/deploy-jar-2-task/deploy-jar-2-task.yml
@@ -0,0 +1,39 @@
1
+ describe ValidatorResult do
2
+ let(:a_resource) { ConcourseResource.new(name: 'foo') }
3
+
4
+ let( :failed_job ) do
5
+ ConcourseJob.new(
6
+ name: 'failed job', dependencies: [ConcourseJobDependency.new(name: 'not defined')]
7
+ )
8
+ end
9
+
10
+ describe '#success?' do
11
+ it 'should return true when there are no failed jobs' do
12
+ result = ValidatorResult.new(failed_jobs: [], failed_resources: [])
13
+ expect(result).to be_success
14
+ end
15
+
16
+ it 'should return false when there are failed jobs' do
17
+ result = ValidatorResult.new(failed_jobs: [failed_job], failed_resources: [])
18
+ expect(result).to_not be_success
19
+ end
20
+
21
+ it 'should return false when there are failed resources' do
22
+ result = ValidatorResult.new(failed_jobs: [], failed_resources: [a_resource])
23
+ expect(result).to_not be_success
24
+ end
25
+
26
+ it 'should return false when there are both failed resources and failed jobs' do
27
+ result = ValidatorResult.new(failed_jobs: [failed_job], failed_resources: [a_resource])
28
+ expect(result).to_not be_success
29
+ end
30
+ end
31
+
32
+ describe '#failed_jobs' do
33
+ it 'should return a list of all jobs that are missing dependencies' do
34
+ result = ValidatorResult.new(failed_jobs: [failed_job], failed_resources: [])
35
+
36
+ expect(result.failed_jobs).to contain_exactly failed_job
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'colorize'
4
+ require_relative './lib/preflight_check/concourse_yml_validator'
5
+
6
+ def print_failed_jobs(result)
7
+ puts "\nMissing Dependencies".underline
8
+ result.failed_jobs.each do |job|
9
+ job.missing_dependencies.each do |dep|
10
+ puts " Job: '#{job.name}', is missing resource: #{dep.name}"
11
+ end
12
+ end
13
+ end
14
+
15
+ def print_failed_resources(result)
16
+ puts "\nUnused Resources Declared".underline
17
+ result.failed_resources.each do |resource|
18
+ puts " Resource: '#{resource.name}' was defined, but never used."
19
+ end
20
+ end
21
+
22
+
23
+ def get_manifest_path
24
+ path = ARGV.first
25
+ if path.nil? || path.empty?
26
+ puts 'Include path to yml as command line argument.'.yellow
27
+ puts 'ie. $ validate_manifest /path/to/file.yml'.yellow
28
+ exit
29
+ end
30
+ path
31
+ end
32
+
33
+ def run
34
+ path = get_manifest_path
35
+ concourse_yml_validator = ConcourseYmlValidator.new
36
+ result = concourse_yml_validator.validate(path: path)
37
+ puts "\n"
38
+ if result.success?
39
+ puts "SUCCESS, Maifest '#{path}' is valid.".green
40
+ puts 'You are cleared for takeoff.'
41
+
42
+ else
43
+ puts "FAILURE, Manifest '#{path}' is invalid.".red
44
+ print_failed_jobs(result) unless result.failed_jobs.empty?
45
+ print_failed_resources(result) unless result.failed_resources.empty?
46
+ end
47
+ puts "\n"
48
+ end
49
+
50
+ run
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: preflight_check
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Max Gordon
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2016-05-08 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: colorize
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ description: |-
70
+ Validates that you have defined all of the resources that your jobs are depending on.
71
+ Validates that all declared resources are consumed.
72
+ email:
73
+ - gordonmaxc@gmail.com
74
+ executables:
75
+ - preflight_check
76
+ extensions: []
77
+ extra_rdoc_files: []
78
+ files:
79
+ - ".gitignore"
80
+ - ".rspec"
81
+ - ".ruby-version"
82
+ - Gemfile
83
+ - Gemfile.lock
84
+ - LICENSE.txt
85
+ - README.md
86
+ - Rakefile
87
+ - bin/preflight_check
88
+ - doc-support/balanced_manifest.yml
89
+ - doc-support/failure_use_screenshot.png
90
+ - doc-support/success_use_screenshot.png
91
+ - doc-support/unbalanced_manifest.yml
92
+ - lib/preflight_check.rb
93
+ - lib/preflight_check/concourse_correlator.rb
94
+ - lib/preflight_check/concourse_job.rb
95
+ - lib/preflight_check/concourse_job_dependency.rb
96
+ - lib/preflight_check/concourse_job_factory.rb
97
+ - lib/preflight_check/concourse_resource.rb
98
+ - lib/preflight_check/concourse_yml_validator.rb
99
+ - lib/preflight_check/validator_result.rb
100
+ - lib/preflight_check/version.rb
101
+ - preflight_check.gemspec
102
+ - spec/concourse_correlator_spec.rb
103
+ - spec/concourse_job_factory_spec.rb
104
+ - spec/concourse_yml_validator_spec.rb
105
+ - spec/preflight_check_spec.rb
106
+ - spec/spec_helper.rb
107
+ - spec/support/yml_with_balanced_resources.yml
108
+ - spec/support/yml_with_extra_resources.yml
109
+ - spec/support/yml_with_missing_resources.yml
110
+ - spec/validator_result_spec.rb
111
+ - validate_manifest.rb
112
+ homepage: ''
113
+ licenses:
114
+ - MIT
115
+ metadata: {}
116
+ post_install_message:
117
+ rdoc_options: []
118
+ require_paths:
119
+ - lib
120
+ required_ruby_version: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ required_rubygems_version: !ruby/object:Gem::Requirement
126
+ requirements:
127
+ - - ">="
128
+ - !ruby/object:Gem::Version
129
+ version: '0'
130
+ requirements: []
131
+ rubyforge_project:
132
+ rubygems_version: 2.2.2
133
+ signing_key:
134
+ specification_version: 4
135
+ summary: Pre-flight check your concourse config yaml before taking off.
136
+ test_files:
137
+ - spec/concourse_correlator_spec.rb
138
+ - spec/concourse_job_factory_spec.rb
139
+ - spec/concourse_yml_validator_spec.rb
140
+ - spec/preflight_check_spec.rb
141
+ - spec/spec_helper.rb
142
+ - spec/support/yml_with_balanced_resources.yml
143
+ - spec/support/yml_with_extra_resources.yml
144
+ - spec/support/yml_with_missing_resources.yml
145
+ - spec/validator_result_spec.rb