From 242916bc6ddc65196727c5bfe910ba5c69a005d7 Mon Sep 17 00:00:00 2001 From: Yu-Hung Lin Date: Mon, 27 Jun 2016 07:51:01 -0700 Subject: [PATCH] More refactoring - Codeclimate and related tooling yml/configs. Setup instructions [here](https://github.com/codeclimate/codeclimate). Details on simplecov [here](https://github.com/colszowka/simplecov). - cleanup dotenv usage - `.env` -> `.env.sample`, `.env.test`. This allow better seperate of test environment specifics from the actual code base, and can let us cook up a more reasonable sample `.env` file for either templating/getting started examples. - cleaned up pry usage: should only be required if the RACK_ENV = 'development' - Freed up the `Terraform` class/module namespace for usage later. - Rake task cleanup - Prometheus specific testing is now not exported as part of `spec_tasks` - Removed the extra `*.rake` redirection to define the exported rake tasks more programatically without excessive nesting. - Gemspec cleanup: only export gem necessary files. - Feature: stdlib Logger is now used for logging to STDOUT. - Terraform reworking: Logic is now split from the basic CLI command wrapping. - Tools moved to `state_stores` for better organizational grouping (and to prepare from namespacing later) - Handling terraform `-var` for both post and pre 0.7.0 --- .codeclimate.yml | 28 + .env.sample | 7 +- .env.test | 6 + .gitignore | 2 + .reek | 14 + .rubocop.yml | 1156 +++++++++++++++++ Gemfile | 2 +- Gemfile.lock | 44 +- Rakefile | 31 +- TODO.md | 14 +- bin/console | 8 +- circle.yml | 1 - prometheus.gemspec | 13 +- prometheus.yaml => prometheus_sample.yaml | 0 ruby/lib/prometheus-unifio.rb | 19 +- ruby/lib/prometheus-unifio/core/bootstrap.rb | 10 +- .../core/cli_wrappers/popen_wrapper.rb | 36 + .../core/cli_wrappers/terraform.yml | 22 + .../core/cli_wrappers/terraform_cli.rb | 98 ++ .../core/data_stores/hiera.rb | 19 +- .../core/entities/context.rb | 6 +- .../prometheus-unifio/core/entities/input.rb | 9 + .../prometheus-unifio/core/entities/stack.rb | 11 +- .../core/entities/state_store.rb | 4 +- .../core/repositories/context_repository.rb | 2 +- .../core/repositories/input_repository.rb | 4 +- .../core/repositories/stack_repository.rb | 42 +- .../repositories/state_store_repository.rb | 6 +- .../core/services/hiera_syntax_service.rb | 2 +- .../core/services/terraform_stack_tasks.rb | 172 +++ .../core/state_stores}/atlas.rb | 23 +- .../core/state_stores}/consul.rb | 24 +- .../core/state_stores}/s3.rb | 23 +- .../prometheus-unifio/environment_tasks.rb | 169 ++- .../prometheus-unifio/rake/environment.rake | 163 --- ruby/lib/prometheus-unifio/rake/rspec.rake | 51 - .../prometheus-unifio/rake/rspec/envs_spec.rb | 26 +- .../prometheus-unifio/rake/rspec/yaml_spec.rb | 2 +- ruby/lib/prometheus-unifio/spec_tasks.rb | 53 +- ruby/lib/prometheus-unifio/version.rb | 2 +- ruby/lib/tools/terraform.rb | 115 -- spec/core/cli_wrappers/terraform_cli_spec.rb | 175 +++ spec/core/entities/context_spec.rb | 2 + spec/core/entities/input_spec.rb | 29 +- spec/core/entities/stack_spec.rb | 5 +- .../repositories/stack_repository_spec.rb | 15 +- .../state_stores}/atlas_spec.rb | 20 +- .../state_stores}/consul_spec.rb | 30 +- spec/{s3 => core/state_stores}/s3_spec.rb | 12 +- spec/fabricators/context_fabricator.rb | 3 +- spec/fabricators/environment_fabricator.rb | 1 - spec/fabricators/input_fabricator.rb | 1 - spec/fabricators/stack_fabricator.rb | 1 - spec/fabricators/state_store_fabricator.rb | 1 - spec/fixtures/data/envs-bad-state.yaml | 5 + spec/{ => fixtures}/data/envs.yaml | 0 spec/{ => fixtures}/data/envs/example.yaml | 0 .../data/stacks/example-artifact_test.yaml | 0 .../data/stacks/example-module_test.yaml | 0 .../data/stacks/example-myapp.yaml | 4 + .../atlas/artifact_response.json | 0 .../mock_responses}/atlas/state_response.json | 0 .../consul/empty_response.json | 0 .../mock_responses}/consul/key_response.json | 0 .../consul/state_response.json | 0 .../mock_responses}/s3/key_response.json | 0 .../mock_responses}/s3/state_response.json | 0 spec/fixtures/packer/.gitkeep | 0 spec/fixtures/prometheus_bad_state.yaml | 13 + spec/{ => fixtures}/prometheus_spec.yaml | 2 +- .../terraform}/myapp/main.tf | 0 .../terraform}/myapp2/main.tf | 0 spec/rake/environment_rake_spec.rb | 251 ---- spec/rake/environment_tasks_spec.rb | 365 ++++++ spec/rake/spec_tasks_spec.rb | 11 + spec/shared_contexts/rake.rb | 18 +- spec/spec_helper.rb | 9 +- spec/terraform/terraform_spec.rb | 122 -- 78 files changed, 2636 insertions(+), 898 deletions(-) create mode 100644 .codeclimate.yml create mode 100644 .env.test create mode 100644 .reek create mode 100644 .rubocop.yml rename prometheus.yaml => prometheus_sample.yaml (100%) create mode 100644 ruby/lib/prometheus-unifio/core/cli_wrappers/popen_wrapper.rb create mode 100644 ruby/lib/prometheus-unifio/core/cli_wrappers/terraform.yml create mode 100644 ruby/lib/prometheus-unifio/core/cli_wrappers/terraform_cli.rb create mode 100644 ruby/lib/prometheus-unifio/core/services/terraform_stack_tasks.rb rename ruby/lib/{tools => prometheus-unifio/core/state_stores}/atlas.rb (95%) rename ruby/lib/{tools => prometheus-unifio/core/state_stores}/consul.rb (92%) rename ruby/lib/{tools => prometheus-unifio/core/state_stores}/s3.rb (93%) delete mode 100644 ruby/lib/prometheus-unifio/rake/environment.rake delete mode 100644 ruby/lib/prometheus-unifio/rake/rspec.rake delete mode 100644 ruby/lib/tools/terraform.rb create mode 100644 spec/core/cli_wrappers/terraform_cli_spec.rb rename spec/{atlas => core/state_stores}/atlas_spec.rb (84%) rename spec/{consul => core/state_stores}/consul_spec.rb (74%) rename spec/{s3 => core/state_stores}/s3_spec.rb (87%) create mode 100644 spec/fixtures/data/envs-bad-state.yaml rename spec/{ => fixtures}/data/envs.yaml (100%) rename spec/{ => fixtures}/data/envs/example.yaml (100%) rename spec/{ => fixtures}/data/stacks/example-artifact_test.yaml (100%) rename spec/{ => fixtures}/data/stacks/example-module_test.yaml (100%) rename spec/{ => fixtures}/data/stacks/example-myapp.yaml (76%) rename spec/{ => fixtures/mock_responses}/atlas/artifact_response.json (100%) rename spec/{ => fixtures/mock_responses}/atlas/state_response.json (100%) rename spec/{ => fixtures/mock_responses}/consul/empty_response.json (100%) rename spec/{ => fixtures/mock_responses}/consul/key_response.json (100%) rename spec/{ => fixtures/mock_responses}/consul/state_response.json (100%) rename spec/{ => fixtures/mock_responses}/s3/key_response.json (100%) rename spec/{ => fixtures/mock_responses}/s3/state_response.json (100%) create mode 100644 spec/fixtures/packer/.gitkeep create mode 100644 spec/fixtures/prometheus_bad_state.yaml rename spec/{ => fixtures}/prometheus_spec.yaml (87%) rename spec/{environment => fixtures/terraform}/myapp/main.tf (100%) rename spec/{environment => fixtures/terraform}/myapp2/main.tf (100%) delete mode 100644 spec/rake/environment_rake_spec.rb create mode 100644 spec/rake/environment_tasks_spec.rb create mode 100644 spec/rake/spec_tasks_spec.rb delete mode 100644 spec/terraform/terraform_spec.rb diff --git a/.codeclimate.yml b/.codeclimate.yml new file mode 100644 index 0000000..d5def6b --- /dev/null +++ b/.codeclimate.yml @@ -0,0 +1,28 @@ +--- +engines: + bundler-audit: + enabled: true + csslint: + enabled: false + duplication: + enabled: true + config: + languages: + ruby: + mass_threshold: 30 + eslint: + enabled: false + fixme: + enabled: true + reek: + enabled: true + rubocop: + enabled: true +ratings: + paths: + - Gemfile.lock + - "**.inc" + - "**.module" + - "**.rb" +exclude_paths: +- spec/ diff --git a/.env.sample b/.env.sample index 3789118..4f67c69 100644 --- a/.env.sample +++ b/.env.sample @@ -1,6 +1,3 @@ -PROMETHEUS_CONFIG = "spec/prometheus_spec.yaml" -PROMETHEUS_TERRAFORM_DIR = "spec/environment" -PROMETHEUS_PACKER_DIR = "spec/environment" -TERRAFORM_STUB = true +PROMETHEUS_WORKSPACE = $PWD AWS_REGION = "us-west-2" -ATLAS_TOKEN = "" +ATLAS_TOKEN = "" diff --git a/.env.test b/.env.test new file mode 100644 index 0000000..cc2c1fa --- /dev/null +++ b/.env.test @@ -0,0 +1,6 @@ +PROMETHEUS_CONFIG = "spec/fixtures/prometheus_spec.yaml" +PROMETHEUS_TERRAFORM_DIR = "spec/fixtures/terraform" +PROMETHEUS_PACKER_DIR = "spec/fixtures/packer" +AWS_REGION = "us-west-2" +ATLAS_TOKEN = "" +RAKE_ENV = "test" diff --git a/.gitignore b/.gitignore index 8a326e8..ce99d77 100644 --- a/.gitignore +++ b/.gitignore @@ -2,3 +2,5 @@ spec/reports/* **/terraform.tfstate* data/dev/* .env +coverage/* +.DS_Store diff --git a/.reek b/.reek new file mode 100644 index 0000000..ceef0b3 --- /dev/null +++ b/.reek @@ -0,0 +1,14 @@ +--- +TooManyStatements: + enabled: true + exclude: + - initialize + max_statements: 7 +IrresponsibleModule: + enabled: false +DuplicateMethodCall: + max_calls: 3 +NestedIterators: + max_allowed_nesting: 2 +UtilityFunction: + public_methods_only: true diff --git a/.rubocop.yml b/.rubocop.yml new file mode 100644 index 0000000..3f1d222 --- /dev/null +++ b/.rubocop.yml @@ -0,0 +1,1156 @@ +AllCops: + DisabledByDefault: true + +#################### Lint ################################ + +Lint/AmbiguousOperator: + Description: >- + Checks for ambiguous operators in the first argument of a + method invocation without parentheses. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-as-args' + Enabled: true + +Lint/AmbiguousRegexpLiteral: + Description: >- + Checks for ambiguous regexp literals in the first argument of + a method invocation without parenthesis. + Enabled: true + +Lint/AssignmentInCondition: + Description: "Don't use assignment in conditions." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#safe-assignment-in-condition' + Enabled: true + +Lint/BlockAlignment: + Description: 'Align block ends correctly.' + Enabled: true + +Lint/CircularArgumentReference: + Description: "Don't refer to the keyword argument in the default value." + Enabled: true + +Lint/ConditionPosition: + Description: >- + Checks for condition placed in a confusing position relative to + the keyword. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#same-line-condition' + Enabled: true + +Lint/Debugger: + Description: 'Check for debugger calls.' + Enabled: true + +Lint/DefEndAlignment: + Description: 'Align ends corresponding to defs correctly.' + Enabled: true + +Lint/DeprecatedClassMethods: + Description: 'Check for deprecated class method calls.' + Enabled: true + +Lint/DuplicateMethods: + Description: 'Check for duplicate methods calls.' + Enabled: true + +Lint/EachWithObjectArgument: + Description: 'Check for immutable argument given to each_with_object.' + Enabled: true + +Lint/ElseLayout: + Description: 'Check for odd code arrangement in an else block.' + Enabled: true + +Lint/EmptyEnsure: + Description: 'Checks for empty ensure block.' + Enabled: true + +Lint/EmptyInterpolation: + Description: 'Checks for empty string interpolation.' + Enabled: true + +Lint/EndAlignment: + Description: 'Align ends correctly.' + Enabled: true + +Lint/EndInMethod: + Description: 'END blocks should not be placed inside method definitions.' + Enabled: true + +Lint/EnsureReturn: + Description: 'Do not use return in an ensure block.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-return-ensure' + Enabled: true + +Lint/Eval: + Description: 'The use of eval represents a serious security risk.' + Enabled: true + +Lint/FormatParameterMismatch: + Description: 'The number of parameters to format/sprint must match the fields.' + Enabled: true + +Lint/HandleExceptions: + Description: "Don't suppress exception." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#dont-hide-exceptions' + Enabled: true + +Lint/InvalidCharacterLiteral: + Description: >- + Checks for invalid character literals with a non-escaped + whitespace character. + Enabled: true + +Lint/LiteralInCondition: + Description: 'Checks of literals used in conditions.' + Enabled: true + +Lint/LiteralInInterpolation: + Description: 'Checks for literals used in interpolation.' + Enabled: true + +Lint/Loop: + Description: >- + Use Kernel#loop with break rather than begin/end/until or + begin/end/while for post-loop tests. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#loop-with-break' + Enabled: true + +Lint/NestedMethodDefinition: + Description: 'Do not use nested method definitions.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-methods' + Enabled: true + +Lint/NonLocalExitFromIterator: + Description: 'Do not use return in iterator to cause non-local exit.' + Enabled: true + +Lint/ParenthesesAsGroupedExpression: + Description: >- + Checks for method calls with a space before the opening + parenthesis. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' + Enabled: true + +Lint/RequireParentheses: + Description: >- + Use parentheses in the method call to avoid confusion + about precedence. + Enabled: true + +Lint/RescueException: + Description: 'Avoid rescuing the Exception class.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-blind-rescues' + Enabled: true + +Lint/ShadowingOuterLocalVariable: + Description: >- + Do not use the same name as outer local variable + for block arguments or block local variables. + Enabled: true + +Lint/StringConversionInInterpolation: + Description: 'Checks for Object#to_s usage in string interpolation.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-to-s' + Enabled: true + +Lint/UnderscorePrefixedVariableName: + Description: 'Do not use prefix `_` for a variable that is used.' + Enabled: true + +Lint/UnneededDisable: + Description: >- + Checks for rubocop:disable comments that can be removed. + Note: this cop is not disabled when disabling all cops. + It must be explicitly disabled. + Enabled: true + +Lint/UnusedBlockArgument: + Description: 'Checks for unused block arguments.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' + Enabled: true + +Lint/UnusedMethodArgument: + Description: 'Checks for unused method arguments.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' + Enabled: true + +Lint/UnreachableCode: + Description: 'Unreachable code.' + Enabled: true + +Lint/UselessAccessModifier: + Description: 'Checks for useless access modifiers.' + Enabled: true + +Lint/UselessAssignment: + Description: 'Checks for useless assignment to a local variable.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscore-unused-vars' + Enabled: true + +Lint/UselessComparison: + Description: 'Checks for comparison of something with itself.' + Enabled: true + +Lint/UselessElseWithoutRescue: + Description: 'Checks for useless `else` in `begin..end` without `rescue`.' + Enabled: true + +Lint/UselessSetterCall: + Description: 'Checks for useless setter call to a local variable.' + Enabled: true + +Lint/Void: + Description: 'Possible use of operator/literal/variable in void context.' + Enabled: true + +###################### Metrics #################################### + +Metrics/AbcSize: + Description: >- + A calculated magnitude based on number of assignments, + branches, and conditions. + Reference: 'http://c2.com/cgi/wiki?AbcMetric' + Enabled: false + Max: 20 + +Metrics/BlockNesting: + Description: 'Avoid excessive block nesting' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#three-is-the-number-thou-shalt-count' + Enabled: true + Max: 4 + +Metrics/ClassLength: + Description: 'Avoid classes longer than 250 lines of code.' + Enabled: true + Max: 250 + +Metrics/CyclomaticComplexity: + Description: >- + A complexity metric that is strongly correlated to the number + of test cases needed to validate a method. + Enabled: true + +Metrics/LineLength: + Description: 'Limit lines to 80 characters.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#80-character-limits' + Enabled: false + +Metrics/MethodLength: + Description: 'Avoid methods longer than 30 lines of code.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#short-methods' + Enabled: true + Max: 30 + +Metrics/ModuleLength: + Description: 'Avoid modules longer than 250 lines of code.' + Enabled: true + Max: 250 + +Metrics/ParameterLists: + Description: 'Avoid parameter lists longer than three or four parameters.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#too-many-params' + Enabled: true + +Metrics/PerceivedComplexity: + Description: >- + A complexity metric geared towards measuring complexity for a + human reader. + Enabled: false + +##################### Performance ############################# + +Performance/Count: + Description: >- + Use `count` instead of `select...size`, `reject...size`, + `select...count`, `reject...count`, `select...length`, + and `reject...length`. + Enabled: true + +Performance/Detect: + Description: >- + Use `detect` instead of `select.first`, `find_all.first`, + `select.last`, and `find_all.last`. + Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerabledetect-vs-enumerableselectfirst-code' + Enabled: true + +Performance/FlatMap: + Description: >- + Use `Enumerable#flat_map` + instead of `Enumerable#map...Array#flatten(1)` + or `Enumberable#collect..Array#flatten(1)` + Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablemaparrayflatten-vs-enumerableflat_map-code' + Enabled: true + EnabledForFlattenWithoutParams: false + # If enabled, this cop will warn about usages of + # `flatten` being called without any parameters. + # This can be dangerous since `flat_map` will only flatten 1 level, and + # `flatten` without any parameters can flatten multiple levels. + +Performance/ReverseEach: + Description: 'Use `reverse_each` instead of `reverse.each`.' + Reference: 'https://github.com/JuanitoFatas/fast-ruby#enumerablereverseeach-vs-enumerablereverse_each-code' + Enabled: true + +Performance/Sample: + Description: >- + Use `sample` instead of `shuffle.first`, + `shuffle.last`, and `shuffle[Fixnum]`. + Reference: 'https://github.com/JuanitoFatas/fast-ruby#arrayshufflefirst-vs-arraysample-code' + Enabled: true + +Performance/Size: + Description: >- + Use `size` instead of `count` for counting + the number of elements in `Array` and `Hash`. + Reference: 'https://github.com/JuanitoFatas/fast-ruby#arraycount-vs-arraysize-code' + Enabled: true + +Performance/StringReplacement: + Description: >- + Use `tr` instead of `gsub` when you are replacing the same + number of characters. Use `delete` instead of `gsub` when + you are deleting characters. + Reference: 'https://github.com/JuanitoFatas/fast-ruby#stringgsub-vs-stringtr-code' + Enabled: true + +##################### Rails ################################## + +Rails/ActionFilter: + Description: 'Enforces consistent use of action filter methods.' + Enabled: false + +Rails/Date: + Description: >- + Checks the correct usage of date aware methods, + such as Date.today, Date.current etc. + Enabled: false + +Rails/Delegate: + Description: 'Prefer delegate method for delegations.' + Enabled: false + +Rails/FindBy: + Description: 'Prefer find_by over where.first.' + Enabled: false + +Rails/FindEach: + Description: 'Prefer all.find_each over all.find.' + Enabled: false + +Rails/HasAndBelongsToMany: + Description: 'Prefer has_many :through to has_and_belongs_to_many.' + Enabled: false + +Rails/Output: + Description: 'Checks for calls to puts, print, etc.' + Enabled: false + +Rails/ReadWriteAttribute: + Description: >- + Checks for read_attribute(:attr) and + write_attribute(:attr, val). + Enabled: false + +Rails/ScopeArgs: + Description: 'Checks the arguments of ActiveRecord scopes.' + Enabled: false + +Rails/TimeZone: + Description: 'Checks the correct usage of time zone aware methods.' + StyleGuide: 'https://github.com/bbatsov/rails-style-guide#time' + Reference: 'http://danilenko.org/2012/7/6/rails_timezones' + Enabled: false + +Rails/Validation: + Description: 'Use validates :attribute, hash of validations.' + Enabled: false + +################## Style ################################# + +Style/AccessModifierIndentation: + Description: Check indentation of private/protected visibility modifiers. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-public-private-protected' + Enabled: false + +Style/AccessorMethodName: + Description: Check the naming of accessor methods for get_/set_. + Enabled: false + +Style/Alias: + Description: 'Use alias_method instead of alias.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#alias-method' + Enabled: false + +Style/AlignArray: + Description: >- + Align the elements of an array literal if they span more than + one line. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#align-multiline-arrays' + Enabled: false + +Style/AlignHash: + Description: >- + Align the elements of a hash literal if they span more than + one line. + Enabled: false + +Style/AlignParameters: + Description: >- + Align the parameters of a method call if they span more + than one line. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-double-indent' + Enabled: false + +Style/AndOr: + Description: 'Use &&/|| instead of and/or.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-and-or-or' + Enabled: false + +Style/ArrayJoin: + Description: 'Use Array#join instead of Array#*.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#array-join' + Enabled: false + +Style/AsciiComments: + Description: 'Use only ascii symbols in comments.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-comments' + Enabled: false + +Style/AsciiIdentifiers: + Description: 'Use only ascii symbols in identifiers.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#english-identifiers' + Enabled: false + +Style/Attr: + Description: 'Checks for uses of Module#attr.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr' + Enabled: false + +Style/BeginBlock: + Description: 'Avoid the use of BEGIN blocks.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-BEGIN-blocks' + Enabled: false + +Style/BarePercentLiterals: + Description: 'Checks if usage of %() or %Q() matches configuration.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q-shorthand' + Enabled: false + +Style/BlockComments: + Description: 'Do not use block comments.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-block-comments' + Enabled: false + +Style/BlockEndNewline: + Description: 'Put end statement of multiline block on its own line.' + Enabled: false + +Style/BlockDelimiters: + Description: >- + Avoid using {...} for multi-line blocks (multiline chaining is + always ugly). + Prefer {...} over do...end for single-line blocks. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' + Enabled: false + +Style/BracesAroundHashParameters: + Description: 'Enforce braces style around hash parameters.' + Enabled: false + +Style/CaseEquality: + Description: 'Avoid explicit use of the case equality operator(===).' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-case-equality' + Enabled: false + +Style/CaseIndentation: + Description: 'Indentation of when in a case/when/[else/]end.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#indent-when-to-case' + Enabled: false + +Style/CharacterLiteral: + Description: 'Checks for uses of character literals.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-character-literals' + Enabled: false + +Style/ClassAndModuleCamelCase: + Description: 'Use CamelCase for classes and modules.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#camelcase-classes' + Enabled: false + +Style/ClassAndModuleChildren: + Description: 'Checks style of children classes and modules.' + Enabled: false + +Style/ClassCheck: + Description: 'Enforces consistent use of `Object#is_a?` or `Object#kind_of?`.' + Enabled: false + +Style/ClassMethods: + Description: 'Use self when defining module/class methods.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#def-self-class-methods' + Enabled: false + +Style/ClassVars: + Description: 'Avoid the use of class variables.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-class-vars' + Enabled: false + +Style/ClosingParenthesisIndentation: + Description: 'Checks the indentation of hanging closing parentheses.' + Enabled: false + +Style/ColonMethodCall: + Description: 'Do not use :: for method call.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#double-colons' + Enabled: false + +Style/CommandLiteral: + Description: 'Use `` or %x around command literals.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-x' + Enabled: false + +Style/CommentAnnotation: + Description: 'Checks formatting of annotation comments.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#annotate-keywords' + Enabled: false + +Style/CommentIndentation: + Description: 'Indentation of comments.' + Enabled: false + +Style/ConstantName: + Description: 'Constants should use SCREAMING_SNAKE_CASE.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#screaming-snake-case' + Enabled: false + +Style/DefWithParentheses: + Description: 'Use def with parentheses when there are arguments.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens' + Enabled: false + +Style/DeprecatedHashMethods: + Description: 'Checks for use of deprecated Hash methods.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-key' + Enabled: false + +Style/Documentation: + Description: 'Document classes and non-namespace modules.' + Enabled: false + +Style/DotPosition: + Description: 'Checks the position of the dot in multi-line method calls.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-multi-line-chains' + Enabled: false + +Style/DoubleNegation: + Description: 'Checks for uses of double negation (!!).' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-bang-bang' + Enabled: false + +Style/EachWithObject: + Description: 'Prefer `each_with_object` over `inject` or `reduce`.' + Enabled: false + +Style/ElseAlignment: + Description: 'Align elses and elsifs correctly.' + Enabled: false + +Style/EmptyElse: + Description: 'Avoid empty else-clauses.' + Enabled: false + +Style/EmptyLineBetweenDefs: + Description: 'Use empty lines between defs.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#empty-lines-between-methods' + Enabled: false + +Style/EmptyLines: + Description: "Don't use several empty lines in a row." + Enabled: false + +Style/EmptyLinesAroundAccessModifier: + Description: "Keep blank lines around access modifiers." + Enabled: false + +Style/EmptyLinesAroundBlockBody: + Description: "Keeps track of empty lines around block bodies." + Enabled: false + +Style/EmptyLinesAroundClassBody: + Description: "Keeps track of empty lines around class bodies." + Enabled: false + +Style/EmptyLinesAroundModuleBody: + Description: "Keeps track of empty lines around module bodies." + Enabled: false + +Style/EmptyLinesAroundMethodBody: + Description: "Keeps track of empty lines around method bodies." + Enabled: false + +Style/EmptyLiteral: + Description: 'Prefer literals to Array.new/Hash.new/String.new.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#literal-array-hash' + Enabled: false + +Style/EndBlock: + Description: 'Avoid the use of END blocks.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-END-blocks' + Enabled: false + +Style/EndOfLine: + Description: 'Use Unix-style line endings.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#crlf' + Enabled: false + +Style/EvenOdd: + Description: 'Favor the use of Fixnum#even? && Fixnum#odd?' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' + Enabled: false + +Style/ExtraSpacing: + Description: 'Do not use unnecessary spacing.' + Enabled: false + +Style/FileName: + Description: 'Use snake_case for source file names.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-files' + Enabled: false + +Style/InitialIndentation: + Description: >- + Checks the indentation of the first non-blank non-comment line in a file. + Enabled: false + +Style/FirstParameterIndentation: + Description: 'Checks the indentation of the first parameter in a method call.' + Enabled: false + +Style/FlipFlop: + Description: 'Checks for flip flops' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-flip-flops' + Enabled: false + +Style/For: + Description: 'Checks use of for or each in multiline loops.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-for-loops' + Enabled: false + +Style/FormatString: + Description: 'Enforce the use of Kernel#sprintf, Kernel#format or String#%.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#sprintf' + Enabled: false + +Style/GlobalVars: + Description: 'Do not introduce global variables.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#instance-vars' + Reference: 'http://www.zenspider.com/Languages/Ruby/QuickRef.html' + Enabled: false + +Style/GuardClause: + Description: 'Check for conditionals that can be replaced with guard clauses' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' + Enabled: false + +Style/HashSyntax: + Description: >- + Prefer Ruby 1.9 hash syntax { a: 1, b: 2 } over 1.8 syntax + { :a => 1, :b => 2 }. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-literals' + Enabled: false + +Style/IfUnlessModifier: + Description: >- + Favor modifier if/unless usage when you have a + single-line body. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#if-as-a-modifier' + Enabled: false + +Style/IfWithSemicolon: + Description: 'Do not use if x; .... Use the ternary operator instead.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon-ifs' + Enabled: false + +Style/IndentationConsistency: + Description: 'Keep indentation straight.' + Enabled: false + +Style/IndentationWidth: + Description: 'Use 2 spaces for indentation.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation' + Enabled: false + +Style/IndentArray: + Description: >- + Checks the indentation of the first element in an array + literal. + Enabled: false + +Style/IndentHash: + Description: 'Checks the indentation of the first key in a hash literal.' + Enabled: false + +Style/InfiniteLoop: + Description: 'Use Kernel#loop for infinite loops.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#infinite-loop' + Enabled: false + +Style/Lambda: + Description: 'Use the new lambda literal syntax for single-line blocks.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#lambda-multi-line' + Enabled: false + +Style/LambdaCall: + Description: 'Use lambda.call(...) instead of lambda.(...).' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc-call' + Enabled: false + +Style/LeadingCommentSpace: + Description: 'Comments should start with a space.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#hash-space' + Enabled: false + +Style/LineEndConcatenation: + Description: >- + Use \ instead of + or << to concatenate two string literals at + line end. + Enabled: false + +Style/MethodCallParentheses: + Description: 'Do not use parentheses for method calls with no arguments.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-args-no-parens' + Enabled: false + +Style/MethodDefParentheses: + Description: >- + Checks if the method definitions have or don't have + parentheses. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#method-parens' + Enabled: false + +Style/MethodName: + Description: 'Use the configured style when naming methods.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars' + Enabled: false + +Style/ModuleFunction: + Description: 'Checks for usage of `extend self` in modules.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#module-function' + Enabled: false + +Style/MultilineBlockChain: + Description: 'Avoid multi-line chains of blocks.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#single-line-blocks' + Enabled: false + +Style/MultilineBlockLayout: + Description: 'Ensures newlines after multiline block do statements.' + Enabled: false + +Style/MultilineIfThen: + Description: 'Do not use then for multi-line if/unless.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-then' + Enabled: false + +Style/MultilineOperationIndentation: + Description: >- + Checks indentation of binary operations that span more than + one line. + Enabled: false + +Style/MultilineTernaryOperator: + Description: >- + Avoid multi-line ?: (the ternary operator); + use if/unless instead. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-ternary' + Enabled: false + +Style/NegatedIf: + Description: >- + Favor unless over if for negative conditions + (or control flow or). + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#unless-for-negatives' + Enabled: false + +Style/NegatedWhile: + Description: 'Favor until over while for negative conditions.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#until-for-negatives' + Enabled: false + +Style/NestedTernaryOperator: + Description: 'Use one expression per branch in a ternary operator.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-ternary' + Enabled: false + +Style/Next: + Description: 'Use `next` to skip iteration instead of a condition at the end.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-nested-conditionals' + Enabled: false + +Style/NilComparison: + Description: 'Prefer x.nil? to x == nil.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#predicate-methods' + Enabled: false + +Style/NonNilCheck: + Description: 'Checks for redundant nil checks.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-non-nil-checks' + Enabled: false + +Style/Not: + Description: 'Use ! instead of not.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bang-not-not' + Enabled: false + +Style/NumericLiterals: + Description: >- + Add underscores to large numeric literals to improve their + readability. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#underscores-in-numerics' + Enabled: false + +Style/OneLineConditional: + Description: >- + Favor the ternary operator(?:) over + if/then/else/end constructs. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#ternary-operator' + Enabled: false + +Style/OpMethod: + Description: 'When defining binary operators, name the argument other.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#other-arg' + Enabled: false + +Style/OptionalArguments: + Description: >- + Checks for optional arguments that do not appear at the end + of the argument list + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#optional-arguments' + Enabled: false + +Style/ParallelAssignment: + Description: >- + Check for simple usages of parallel assignment. + It will only warn when the number of variables + matches on both sides of the assignment. + This also provides performance benefits + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parallel-assignment' + Enabled: false + +Style/ParenthesesAroundCondition: + Description: >- + Don't use parentheses around the condition of an + if/unless/while. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-parens-if' + Enabled: false + +Style/PercentLiteralDelimiters: + Description: 'Use `%`-literal delimiters consistently' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-literal-braces' + Enabled: false + +Style/PercentQLiterals: + Description: 'Checks if uses of %Q/%q match the configured preference.' + Enabled: false + +Style/PerlBackrefs: + Description: 'Avoid Perl-style regex back references.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-perl-regexp-last-matchers' + Enabled: false + +Style/PredicateName: + Description: 'Check the names of predicate methods.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#bool-methods-qmark' + Enabled: false + +Style/Proc: + Description: 'Use proc instead of Proc.new.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#proc' + Enabled: false + +Style/RaiseArgs: + Description: 'Checks the arguments passed to raise/fail.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#exception-class-messages' + Enabled: false + +Style/RedundantBegin: + Description: "Don't use begin blocks when they are not needed." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#begin-implicit' + Enabled: false + +Style/RedundantException: + Description: "Checks for an obsolete RuntimeException argument in raise/fail." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-runtimeerror' + Enabled: false + +Style/RedundantReturn: + Description: "Don't use return where it's not required." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-explicit-return' + Enabled: false + +Style/RedundantSelf: + Description: "Don't use self where it's not needed." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-self-unless-required' + Enabled: false + +Style/RegexpLiteral: + Description: 'Use / or %r around regular expressions.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-r' + Enabled: false + +Style/RescueEnsureAlignment: + Description: 'Align rescues and ensures correctly.' + Enabled: false + +Style/RescueModifier: + Description: 'Avoid using rescue in its modifier form.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-rescue-modifiers' + Enabled: false + +Style/SelfAssignment: + Description: >- + Checks for places where self-assignment shorthand should have + been used. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#self-assignment' + Enabled: false + +Style/Semicolon: + Description: "Don't use semicolons to terminate expressions." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-semicolon' + Enabled: false + +Style/SignalException: + Description: 'Checks for proper usage of fail and raise.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#fail-method' + Enabled: false + +Style/SingleLineBlockParams: + Description: 'Enforces the names of some block params.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#reduce-blocks' + Enabled: false + +Style/SingleLineMethods: + Description: 'Avoid single-line methods.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-single-line-methods' + Enabled: false + +Style/SpaceBeforeFirstArg: + Description: >- + Checks that exactly one space is used between a method name + and the first argument for method calls without parentheses. + Enabled: true + +Style/SpaceAfterColon: + Description: 'Use spaces after colons.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' + Enabled: false + +Style/SpaceAfterComma: + Description: 'Use spaces after commas.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' + Enabled: false + +Style/SpaceAroundKeyword: + Description: 'Use spaces around keywords.' + Enabled: false + +Style/SpaceAfterMethodName: + Description: >- + Do not put a space between a method name and the opening + parenthesis in a method definition. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#parens-no-spaces' + Enabled: false + +Style/SpaceAfterNot: + Description: Tracks redundant space after the ! operator. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-bang' + Enabled: false + +Style/SpaceAfterSemicolon: + Description: 'Use spaces after semicolons.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' + Enabled: false + +Style/SpaceBeforeBlockBraces: + Description: >- + Checks that the left block brace has or doesn't have space + before it. + Enabled: false + +Style/SpaceBeforeComma: + Description: 'No spaces before commas.' + Enabled: false + +Style/SpaceBeforeComment: + Description: >- + Checks for missing space between code and a comment on the + same line. + Enabled: false + +Style/SpaceBeforeSemicolon: + Description: 'No spaces before semicolons.' + Enabled: false + +Style/SpaceInsideBlockBraces: + Description: >- + Checks that block braces have or don't have surrounding space. + For blocks taking parameters, checks that the left brace has + or doesn't have trailing space. + Enabled: false + +Style/SpaceAroundBlockParameters: + Description: 'Checks the spacing inside and after block parameters pipes.' + Enabled: false + +Style/SpaceAroundEqualsInParameterDefault: + Description: >- + Checks that the equals signs in parameter default assignments + have or don't have surrounding space depending on + configuration. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-around-equals' + Enabled: false + +Style/SpaceAroundOperators: + Description: 'Use a single space around operators.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' + Enabled: false + +Style/SpaceInsideBrackets: + Description: 'No spaces after [ or before ].' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces' + Enabled: false + +Style/SpaceInsideHashLiteralBraces: + Description: "Use spaces inside hash literal braces - or don't." + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-operators' + Enabled: false + +Style/SpaceInsideParens: + Description: 'No spaces after ( or before ).' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-spaces-braces' + Enabled: false + +Style/SpaceInsideRangeLiteral: + Description: 'No spaces inside range literals.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-space-inside-range-literals' + Enabled: false + +Style/SpaceInsideStringInterpolation: + Description: 'Checks for padding/surrounding spaces inside string interpolation.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#string-interpolation' + Enabled: false + +Style/SpecialGlobalVars: + Description: 'Avoid Perl-style global variables.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-cryptic-perlisms' + Enabled: false + +Style/StringLiterals: + Description: 'Checks if uses of quotes match the configured preference.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#consistent-string-literals' + Enabled: false + +Style/StringLiteralsInInterpolation: + Description: >- + Checks if uses of quotes inside expressions in interpolated + strings match the configured preference. + Enabled: false + +Style/StructInheritance: + Description: 'Checks for inheritance from Struct.new.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-extend-struct-new' + Enabled: false + +Style/SymbolLiteral: + Description: 'Use plain symbols instead of string symbols when possible.' + Enabled: false + +Style/SymbolProc: + Description: 'Use symbols as procs instead of blocks when possible.' + Enabled: false + +Style/Tab: + Description: 'No hard tabs.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#spaces-indentation' + Enabled: false + +Style/TrailingBlankLines: + Description: 'Checks trailing blank lines and final newline.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#newline-eof' + Enabled: false + +Style/TrailingCommaInArguments: + Description: 'Checks for trailing comma in parameter lists.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-params-comma' + Enabled: false + +Style/TrailingCommaInLiteral: + Description: 'Checks for trailing comma in literals.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-array-commas' + Enabled: false + +Style/TrailingWhitespace: + Description: 'Avoid trailing whitespace.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-trailing-whitespace' + Enabled: false + +Style/TrivialAccessors: + Description: 'Prefer attr_* methods to trivial readers/writers.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#attr_family' + Enabled: false + +Style/UnlessElse: + Description: >- + Do not use unless with else. Rewrite these with the positive + case first. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-else-with-unless' + Enabled: false + +Style/UnneededCapitalW: + Description: 'Checks for %W when interpolation is not needed.' + Enabled: false + +Style/UnneededPercentQ: + Description: 'Checks for %q/%Q when single quotes or double quotes would do.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-q' + Enabled: false + +Style/TrailingUnderscoreVariable: + Description: >- + Checks for the usage of unneeded trailing underscores at the + end of parallel variable assignment. + Enabled: false + +Style/VariableInterpolation: + Description: >- + Don't interpolate global, instance and class variables + directly in strings. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#curlies-interpolate' + Enabled: false + +Style/VariableName: + Description: 'Use the configured style when naming variables.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#snake-case-symbols-methods-vars' + Enabled: false + +Style/WhenThen: + Description: 'Use when x then ... for one-line cases.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#one-line-cases' + Enabled: false + +Style/WhileUntilDo: + Description: 'Checks for redundant do after while or until.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#no-multiline-while-do' + Enabled: false + +Style/WhileUntilModifier: + Description: >- + Favor modifier while/until usage when you have a + single-line body. + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#while-as-a-modifier' + Enabled: false + +Style/WordArray: + Description: 'Use %w or %W for arrays of words.' + StyleGuide: 'https://github.com/bbatsov/ruby-style-guide#percent-w' + Enabled: false diff --git a/Gemfile b/Gemfile index c80ee36..b4e2a20 100644 --- a/Gemfile +++ b/Gemfile @@ -1,3 +1,3 @@ -source "http://rubygems.org" +source "https://rubygems.org" gemspec diff --git a/Gemfile.lock b/Gemfile.lock index b9462b8..2f7ab5a 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,7 +1,7 @@ PATH remote: . specs: - prometheus-unifio (0.1.4) + prometheus-unifio (0.3.0.beta1) activemodel (~> 4.2.6) activesupport (~> 4.2.6) aws-sdk (~> 2.3.8) @@ -10,27 +10,29 @@ PATH json (~> 1.8.3) rake (>= 11.1.2) rest-client (~> 2.0.0.rc3) + semantic (~> 1.4.1) virtus (~> 1.0.5) GEM - remote: http://rubygems.org/ + remote: https://rubygems.org/ specs: - activemodel (4.2.6) - activesupport (= 4.2.6) + activemodel (4.2.7.1) + activesupport (= 4.2.7.1) builder (~> 3.1) - activesupport (4.2.6) + activesupport (4.2.7.1) i18n (~> 0.7) json (~> 1.7, >= 1.7.7) minitest (~> 5.1) thread_safe (~> 0.3, >= 0.3.4) tzinfo (~> 1.1) addressable (2.4.0) - aws-sdk (2.3.13) - aws-sdk-resources (= 2.3.13) - aws-sdk-core (2.3.13) + awesome_print (1.7.0) + aws-sdk (2.3.22) + aws-sdk-resources (= 2.3.22) + aws-sdk-core (2.3.22) jmespath (~> 1.0) - aws-sdk-resources (2.3.13) - aws-sdk-core (= 2.3.13) + aws-sdk-resources (2.3.22) + aws-sdk-core (= 2.3.22) axiom-types (0.1.1) descendants_tracker (~> 0.0.4) ice_nine (~> 0.11.0) @@ -51,7 +53,8 @@ GEM descendants_tracker (0.0.4) thread_safe (~> 0.3, >= 0.3.1) diff-lcs (1.2.5) - domain_name (0.5.20160310) + docile (1.1.5) + domain_name (0.5.20160615) unf (>= 0.0.5, < 1.0.0) dotenv (2.1.1) equalizer (0.0.11) @@ -65,17 +68,14 @@ GEM netrc (~> 0.10.0) thor (>= 0.14.0, < 1.0.0.pre) hashdiff (0.3.0) - hiera (3.2.0) - json_pure + hiera (3.2.1) highline (1.6.21) http-cookie (1.0.2) domain_name (~> 0.5) i18n (0.7.0) ice_nine (0.11.2) - jmespath (1.2.4) - json_pure (>= 1.8.1) + jmespath (1.3.1) json (1.8.3) - json_pure (1.8.3) method_source (0.8.2) mime-types (3.1) mime-types-data (~> 3.2015) @@ -96,7 +96,7 @@ GEM byebug (~> 9.0) pry (~> 0.10) rake (11.2.2) - rest-client (2.0.0.rc3) + rest-client (2.0.0) http-cookie (>= 1.0.2, < 2.0) mime-types (>= 1.16, < 4.0) netrc (~> 0.8) @@ -117,12 +117,18 @@ GEM rspec-support (~> 3.4.0) rspec-support (3.4.1) safe_yaml (1.0.4) + semantic (1.4.1) serverspec (2.36.0) multi_json rspec (~> 3.0) rspec-its specinfra (~> 2.53) sfl (2.2) + simplecov (0.12.0) + docile (~> 1.1.0) + json (>= 1.8, < 3) + simplecov-html (~> 0.10.0) + simplecov-html (0.10.0) slop (3.6.0) specinfra (2.57.5) net-scp @@ -150,6 +156,7 @@ PLATFORMS ruby DEPENDENCIES + awesome_print (~> 1.7.0) bundler (>= 1.9.0) ci_reporter_rspec (~> 1.0.0) dotenv (~> 2.1.0) @@ -159,7 +166,8 @@ DEPENDENCIES pry-byebug (~> 3.4.0) rspec (~> 3.4.0) serverspec (~> 2.36.0) + simplecov (~> 0.12.0) webmock (~> 2.0.3) BUNDLED WITH - 1.11.2 + 1.12.5 diff --git a/Rakefile b/Rakefile index 75da9a5..fef5a4e 100644 --- a/Rakefile +++ b/Rakefile @@ -1,11 +1,36 @@ # When we're ready to release to rubygems # require "bundler/gem_tasks" require 'dotenv' +require 'rspec/core/rake_task' +require 'ci/reporter/rake/rspec' -# Load environment variables Dotenv.load -require_relative 'ruby/lib/prometheus-unifio/environment_tasks' -require_relative 'ruby/lib/prometheus-unifio/spec_tasks' +require_relative 'ruby/lib/prometheus-unifio' +#require_relative File.join(PrometheusUnifio::GEM_ROOT, 'environment_tasks') +#require_relative File.join(PrometheusUnifio::GEM_ROOT, 'spec_tasks') + +namespace :spec do + # TODO: Might be able to just use the default rspec spec? + desc 'Run Prometheus tests' + RSpec::Core::RakeTask.new(:prometheus) do |t| + t.pattern = "#{File.join(PrometheusUnifio::PROJECT_ROOT, 'spec/**/*_spec.rb')}" + t.rspec_opts = "--color --format documentation" + t.verbose = true + end + + desc 'Run CircleCI friendly Prometheus tests' + RSpec::Core::RakeTask.new(:circleci) do |t| + t.pattern = "#{File.join(PrometheusUnifio::PROJECT_ROOT, 'spec/**/*_spec.rb')}" + t.rspec_opts = "--color --format documentation --tag ~native" + t.verbose = true + end +end + +desc 'Run all spec tests' +task :spec => 'spec:prometheus' + +desc 'Run Prometheus tests' +task "ci:prometheus" => ['ci:setup:rspec', 'spec:circleci'] task :default => :spec diff --git a/TODO.md b/TODO.md index 1287dc6..2aaf7cd 100644 --- a/TODO.md +++ b/TODO.md @@ -10,24 +10,14 @@ rake rspec:apply # Apply changes to the rspec environment all, ci, and rspec will likely all conflict. Might be able to ignore the rspec rake if it's only internal to prometheus - `ruby/lib` might need to get shuffled to `?/lib/prometheus-unifio` -- ~~Top level namespacing: Prometheus -> PrometheusUnifio~~ -- entities/terraform namespace - individual state-stores: would be nice if it had something to describe the backing input types -- ~~check taskname, namespaces don't have spaces~~ -- ~~stack public method surface area is a bit high~~ - stack caching opportunities if they're called multiple times in the rake tasks - consider httparty vs rest-client -- ~~decoupling~~ - - ~~hieradb::client -> is really a data_store/data_backend~~ - - ~~missing a decoupling repository layer that can absorb the env_reader elements~~ - - ~~push the hiera specifics out of stack and into the repository layer~~ - local code climate might be good while i'm shuffling stuff about -- ~~Repositories are likely to see some churn. Keeping them simple and stateless for now until it bcomes too much of a chore to pass the items around~~ - Use the invalid yaml syntax to figure out more things in rake tasks that needs to be lazy loaded - Like the idea of maybe splitting out the syntax elements to a different gem. Gem installation & management would become easier - missing spec around reports? - Need to gem-friendly the namespace all under one umbrella -- leverage a separate .env.spec to point fixtures to spec/fixtures/*, should be able to sweep up spec/environment, prometheus_spec.yaml, root promethus.yaml, data -- free up terraform module namespace -- Remove liberal use of pry - Look into muting the reporter as dev default: https://github.com/ci-reporter/ci_reporter +- Look into getting HieraDB wrapper to read .yml files +- Consolidate ENV usage/definition into top level Prometheus module diff --git a/bin/console b/bin/console index 5c377b8..6038921 100755 --- a/bin/console +++ b/bin/console @@ -1,11 +1,15 @@ #!/usr/bin/env ruby +ENV['RAKE_ENV'] ||= 'development' + require "bundler/setup" require "pry" require "dotenv" require 'active_support/core_ext/string/inflections' +require 'awesome_print' -Dotenv.load -require_relative '../ruby/lib/prometheus-unifio/core/bootstrap' +Dotenv.load(*%w(.env .env.sample)) +require_relative '../ruby/lib/prometheus-unifio' +require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/bootstrap') Pry.start diff --git a/circle.yml b/circle.yml index a5ed8df..2895597 100644 --- a/circle.yml +++ b/circle.yml @@ -10,7 +10,6 @@ machine: test: pre: - docker pull unifio/terraform:latest - - cp ~/prometheus/.env.sample ~/prometheus/.env override: - bundle exec rake ci:prometheus - gem build prometheus.gemspec diff --git a/prometheus.gemspec b/prometheus.gemspec index fcb8b1f..1330ec6 100644 --- a/prometheus.gemspec +++ b/prometheus.gemspec @@ -1,5 +1,7 @@ # coding: utf-8 -lib = File.expand_path('../ruby/lib', __FILE__) +gem_root = "ruby/lib" +lib = File.expand_path(File.join("..", gem_root), __FILE__) + $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) require 'prometheus-unifio/version' require 'prometheus-unifio/helpers/spec_dependencies' @@ -23,11 +25,9 @@ Gem::Specification.new do |spec| raise "RubyGems 2.0 or newer is required to protect against public gem pushes." end - dirs_to_ignore = %w(bin test spec features images).join("|") - spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^("#{dirs_to_ignore}")/}) } - spec.bindir = "bin" + spec.files = Dir["*.md", "#{gem_root}/**/*"] spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) } - spec.require_paths = ["ruby/lib"] + spec.require_paths = [ gem_root ] spec.add_dependency "deep_merge", "~> 1.0.1" spec.add_dependency "hiera", "~> 3.2.0" @@ -38,10 +38,12 @@ Gem::Specification.new do |spec| spec.add_dependency "virtus", "~> 1.0.5" spec.add_dependency "activesupport", "~> 4.2.6" spec.add_dependency "activemodel", "~> 4.2.6" + spec.add_dependency "semantic", "~> 1.4.1" PrometheusUnifio::Helpers::SpecDependencies.dependencies.each do |name, requirement| spec.add_development_dependency name, requirement end + spec.add_development_dependency "awesome_print", "~> 1.7.0" spec.add_development_dependency "bundler", ">= 1.9.0" spec.add_development_dependency "dotenv", "~> 2.1.0" spec.add_development_dependency "pry-byebug", "~> 3.4.0" @@ -49,4 +51,5 @@ Gem::Specification.new do |spec| spec.add_development_dependency "webmock", "~> 2.0.3" spec.add_development_dependency "gemfury", "~> 0.6.0" spec.add_development_dependency "fabrication", "~> 2.15.2" + spec.add_development_dependency "simplecov", "~> 0.12.0" end diff --git a/prometheus.yaml b/prometheus_sample.yaml similarity index 100% rename from prometheus.yaml rename to prometheus_sample.yaml diff --git a/ruby/lib/prometheus-unifio.rb b/ruby/lib/prometheus-unifio.rb index cd0f113..99a6fb2 100644 --- a/ruby/lib/prometheus-unifio.rb +++ b/ruby/lib/prometheus-unifio.rb @@ -1,14 +1,31 @@ require "prometheus-unifio/version" -require 'pry' +require "logger" +require 'active_support/core_ext/object/blank' +if %w(development test).include?(ENV['RAKE_ENV']) + require 'pry' + require 'awesome_print' +end + +# :reek:TooManyConstants module PrometheusUnifio # Configurable constants WORKSPACE = File.expand_path(ENV['PROMETHEUS_WORKSPACE'] || '../../../', __FILE__) CONFIG = File.join(WORKSPACE, ENV['PROMETHEUS_CONFIG'] || 'prometheus.yaml') + # TODO: could use better naming PACKER = File.join(WORKSPACE, ENV['PROMETHEUS_PACKER_DIR'] || 'packer') TERRAFORM = File.join(WORKSPACE, ENV['PROMETHEUS_TERRAFORM_DIR'] || 'terraform') + # should be able to deprecate this with prometheus bundled inside the container + TF_IMG = ENV['TERRAFORM_IMG'] || "" + TF_CMD = ENV['TERRAFORM_CMD'] || "terraform" + TERRAFORM_VERSION = ENV['TERRAFORM_VERSION'] || `#{TF_CMD} #{TF_IMG} version`.split("\n", 2)[0].gsub('Terraform v','') + # Internal constants PROJECT_ROOT = File.expand_path('../../../', __FILE__).freeze GEM_ROOT = File.expand_path('prometheus-unifio', File.dirname(__FILE__)).freeze + # look into logger-colors + LOGGER = Logger.new(STDOUT) + # TODO: make the level configurable + LOGGER.level = Logger::WARN end diff --git a/ruby/lib/prometheus-unifio/core/bootstrap.rb b/ruby/lib/prometheus-unifio/core/bootstrap.rb index 975302a..9054bf8 100644 --- a/ruby/lib/prometheus-unifio/core/bootstrap.rb +++ b/ruby/lib/prometheus-unifio/core/bootstrap.rb @@ -2,14 +2,6 @@ require_relative '../../prometheus-unifio' -# TODO: Slowly building up to eventually just turn this to auto require everything under core -require_relative 'data_stores/hiera' - +Dir[File.expand_path("cli_wrappers/**/*.rb", File.dirname(__FILE__))].sort.each { |file| require file } Dir[File.expand_path("entities/**/*.rb", File.dirname(__FILE__))].sort.each { |file| require file } Dir[File.expand_path("repositories/**/*.rb", File.dirname(__FILE__))].sort.each { |file| require file } - -require_relative 'services/hiera_syntax_service' - -Dir[File.expand_path('../../tools/*.rb', File.dirname(__FILE__))].each do |file| - require file -end diff --git a/ruby/lib/prometheus-unifio/core/cli_wrappers/popen_wrapper.rb b/ruby/lib/prometheus-unifio/core/cli_wrappers/popen_wrapper.rb new file mode 100644 index 0000000..590a82d --- /dev/null +++ b/ruby/lib/prometheus-unifio/core/cli_wrappers/popen_wrapper.rb @@ -0,0 +1,36 @@ +# A WIP, this will eventually wrap Open3#popen3 to provide better cli control +# +#require 'open3' +require_relative '../../../prometheus-unifio' + +class PopenWrapper + def self.run(cmds, path, args) + # TODO: terraform cmd env switch + #cmd = "terraform #{[*cmds].join(' ')}" + + # TODO: implement path prefix for the docker runs, see @tf_cmd + cmd_string = [*cmds] + cmd_string += [*args] unless args.blank? + cmd_string << path unless path.blank? + + #TODO debug command string maybe + #TODO debug command args maybe + + # TODO: read through http://ruby-doc.org/stdlib-2.0.0/libdoc/tmpdir/rdoc/Dir.html to see how to make this optionally persist + #stdout, stderr, status = Open3.capture3(ENV, "terraform", *cmd_string, chdir: tmp_dir) + # TODO: cmd escape issues with -var. + #stdout, stderr, status = Open3.capture3(ENV, cmd_string.join(' '), chdir: path) + #if status != 0 + #raise RuntimeError, "Command failed with status (#{status}): \n#{stderr}\n#{stdout}" + #else + #puts stdout + #stdout.strip + #end + + run_cmd = cmd_string.join(' ') + PrometheusUnifio::LOGGER.warn "---" + PrometheusUnifio::LOGGER.warn run_cmd + + Kernel.system(ENV.to_h, run_cmd) + end +end diff --git a/ruby/lib/prometheus-unifio/core/cli_wrappers/terraform.yml b/ruby/lib/prometheus-unifio/core/cli_wrappers/terraform.yml new file mode 100644 index 0000000..5e99937 --- /dev/null +++ b/ruby/lib/prometheus-unifio/core/cli_wrappers/terraform.yml @@ -0,0 +1,22 @@ +--- +version: Terraform v0.6.16 +commands: + apply: + destroy: + fmt: + get: + graph: + #init: #too messy + #output: #handle manually (required arg) + plan: + push: + refresh: + remote: + config: + pull: + push: + show: + #taint: #handle manually (required arg) + #untaint: #handle manually (required arg) + validate: + version: diff --git a/ruby/lib/prometheus-unifio/core/cli_wrappers/terraform_cli.rb b/ruby/lib/prometheus-unifio/core/cli_wrappers/terraform_cli.rb new file mode 100644 index 0000000..e196af6 --- /dev/null +++ b/ruby/lib/prometheus-unifio/core/cli_wrappers/terraform_cli.rb @@ -0,0 +1,98 @@ +require 'yaml' +require 'fileutils' +require 'tmpdir' +require 'active_support/core_ext/object/blank' +require 'open3' + +require_relative '../../../prometheus-unifio' +require_relative 'popen_wrapper' + +class TerraformCli + def self.require_init() + cmds_yml = File.expand_path("terraform.yml", __dir__) + init_terraform_cmds(cmds_yml) + end + + # :reek:BooleanParameter + def self.terraform_clean(path, dry_run: false, verbose: true) + # standard run shouldn't need this since it does a chdir on a temp dir anyway + # something about cln_cmd when working with docker images + targets = [ File.join(File.expand_path(path), ".terraform") ] + + Dir.glob(File.join(File.expand_path(path), "*.tfstate*")) + + FileUtils.rm_rf(targets, { + noop: dry_run, + verbose: verbose, + secure: true, + }) + end + + def self.terraform_check_style(path) + if PrometheusUnifio::TF_IMG.blank? + output, status = Open3.capture2e(ENV, PrometheusUnifio::TF_CMD, "fmt", "-write=false", path) + else + parent, base = Pathname.new(path).split + output, status = Open3.capture2e(ENV, "#{PrometheusUnifio::TF_CMD} -v #{parent}:/data -w /data/#{base} #{PrometheusUnifio::TF_IMG} fmt -write=false") + end + return false unless status.success? + output = output.split("\n") + (output.size == 0) + end + + def self.terraform_output(output_var, args='') + raise "TODO: implement me" + end + + def self.terraform_taint(resource_name, args='') + raise "TODO: implement me" + end + + def self.terraform_untaint(resource_name, args='') + raise "TODO: implement me" + end + + + class << self + private + + # The only args that should be automated are ones that only expect some DIR/PATH as it's only + # required arg, most other things need a little bit more manual definition. + def init_terraform_cmds(file) + definition = YAML.load_file(file) + + definition['commands'].each do |cmd, sub_hash| + if sub_hash.blank? + terraform_cmd = "terraform_#{cmd}" + + next if respond_to?(terraform_cmd.to_sym) + define_singleton_method(terraform_cmd) do |path=Dir.pwd(), args: ''| + if PrometheusUnifio::TF_IMG.blank? + PopenWrapper.run([PrometheusUnifio::TF_CMD, cmd], path, args) + else + parent, base = Pathname.new(path).split + PopenWrapper.run([PrometheusUnifio::TF_CMD, "-v #{parent}:/data -w /data/#{base} #{PrometheusUnifio::TF_IMG}", cmd], '', args) + end + end + elsif sub_hash.is_a?(Hash) + sub_hash.keys.each do |sub_command| + terraform_cmd = "terraform_#{cmd}_#{sub_command}" + + next if respond_to?(terraform_cmd.to_sym) + define_singleton_method(terraform_cmd) do |path=Dir.pwd(), args: ''| + if PrometheusUnifio::TF_IMG.blank? + PopenWrapper.run([PrometheusUnifio::TF_CMD, cmd, sub_command], path, args) + else + parent, base = Pathname.new(path).split + PopenWrapper.run([PrometheusUnifio::TF_CMD, "-v #{parent}:/data -w /data/#{base} #{PrometheusUnifio::TF_IMG}", cmd, sub_command], '', args) + end + end + end + else + raise "Invalid yml context" + end + end + end + end +end + +TerraformCli.require_init diff --git a/ruby/lib/prometheus-unifio/core/data_stores/hiera.rb b/ruby/lib/prometheus-unifio/core/data_stores/hiera.rb index bdf917a..a2de3cc 100644 --- a/ruby/lib/prometheus-unifio/core/data_stores/hiera.rb +++ b/ruby/lib/prometheus-unifio/core/data_stores/hiera.rb @@ -1,16 +1,23 @@ +require_relative '../../../prometheus-unifio' require 'yaml' require 'hiera' module HieraDB - - # maybe HieraWrapper + # TODO: maybe HieraWrapper + # :reek:DataClump class Client attr_reader :scope def initialize(config, scope = {}) @config = config - @client = Hiera.new(:config => config) @scope = scope + + begin + @client = Hiera.new(:config => config) + rescue RuntimeError => e + PrometheusUnifio::LOGGER.error e.message + exit 1 + end end def initialize_scope(scope) @@ -29,11 +36,13 @@ def lookup(key, default = nil, scope = @scope) end def hash_lookup(key, default = nil, scope = @scope) - @client.lookup(key, default, scope, order_overide = nil, resolution_type = :hash) + # https://github.com/puppetlabs/hiera/blob/d7ed74f4eec8f4fb1aa84cd0e158a595f86debd4/lib/hiera/backend.rb#L241 + # def lookup(key, default, scope, order_override, resolution_type, context = {:recurse_guard => nil}) + @client.lookup(key, default, scope, nil, :hash) end def array_lookup(key, default = nil, scope = @scope) - @client.lookup(key, default, scope, order_overide = nil, resolution_type = :array) + @client.lookup(key, default, scope, nil, :array) end end end diff --git a/ruby/lib/prometheus-unifio/core/entities/context.rb b/ruby/lib/prometheus-unifio/core/entities/context.rb index a511414..06ef0d3 100644 --- a/ruby/lib/prometheus-unifio/core/entities/context.rb +++ b/ruby/lib/prometheus-unifio/core/entities/context.rb @@ -7,7 +7,7 @@ class Context include ActiveModel::Validations attribute :name, String, default: '' - attribute :value, Array, default: [] + attribute :values, Array, default: [] validates! :name, format: { without: /\s+/, @@ -22,4 +22,8 @@ def initialize(attributes = {}, *args) def namespace name.empty? ? '' : "#{name}:" end + + def to_command_options + values.map { |value| "-target=\"#{value}\"" } + end end diff --git a/ruby/lib/prometheus-unifio/core/entities/input.rb b/ruby/lib/prometheus-unifio/core/entities/input.rb index 0c3822b..baeb879 100644 --- a/ruby/lib/prometheus-unifio/core/entities/input.rb +++ b/ruby/lib/prometheus-unifio/core/entities/input.rb @@ -25,11 +25,20 @@ def value backend::lookup(subcategory, raw_value) end + def to_command_option + if Semantic::Version.new(PrometheusUnifio::TERRAFORM_VERSION) >= Semantic::Version.new("0.7.0") + "-var '#{name}=\"#{value}\"'" + else + "-var #{name}=\"#{value}\"" + end + end + private def value_is_local? !raw_value.is_a?(Hash) end + # :reek:FeatureEnvy def parse_type pieces = raw_value.stringify_keys.fetch('type').split('.', 2) return [ pieces.first.camelize.constantize, pieces[1] ] diff --git a/ruby/lib/prometheus-unifio/core/entities/stack.rb b/ruby/lib/prometheus-unifio/core/entities/stack.rb index b55f326..2528680 100644 --- a/ruby/lib/prometheus-unifio/core/entities/stack.rb +++ b/ruby/lib/prometheus-unifio/core/entities/stack.rb @@ -1,5 +1,6 @@ require 'virtus' require 'active_model' +require 'semantic' require_relative '../../../prometheus-unifio' require_relative 'state_store' @@ -23,7 +24,7 @@ class Stack message: "Stack %{attribute}: \"%{value}\" cannot contain spaces" } - def initialize(attributes = {}, *args) + def initialize(attributes = {}, *arguments) super self.valid? end @@ -32,11 +33,7 @@ def full_name "#{environment_name}-#{name}" end - def materialize_inputs - return_hash = {} - inputs.each do |input| - return_hash[input.name] = input.value - end - return return_hash + def materialize_cmd_inputs + inputs.map(&:to_command_option) end end diff --git a/ruby/lib/prometheus-unifio/core/entities/state_store.rb b/ruby/lib/prometheus-unifio/core/entities/state_store.rb index da5bc1c..04b4686 100644 --- a/ruby/lib/prometheus-unifio/core/entities/state_store.rb +++ b/ruby/lib/prometheus-unifio/core/entities/state_store.rb @@ -4,7 +4,7 @@ require 'active_model' # check to see how this works with plugins -Dir[File.expand_path('../../../tools/*.rb', File.dirname(__FILE__))].each do |file| +Dir[File.expand_path('../state_stores/*.rb', File.dirname(__FILE__))].each do |file| require file end @@ -27,11 +27,13 @@ def name params.fetch('name') end + # :reek:FeatureEnvy def params=(params) super(params.stringify_keys) end #TODO: prep different backend for plugins + # :reek:FeatureEnvy def backend=(backend_name) super(backend_name.camelize.constantize) end diff --git a/ruby/lib/prometheus-unifio/core/repositories/context_repository.rb b/ruby/lib/prometheus-unifio/core/repositories/context_repository.rb index 64b1862..88951bd 100644 --- a/ruby/lib/prometheus-unifio/core/repositories/context_repository.rb +++ b/ruby/lib/prometheus-unifio/core/repositories/context_repository.rb @@ -5,7 +5,7 @@ def self.query_by_namespace(data_store, namespace) targets = data_store.hash_lookup("#{namespace}::targets", {}) if targets.present? - return targets.map { |k,v| Context.new(name: k, value: v) } + return targets.map { |name, values| Context.new(name: name, values: values) } else return Context.new() end diff --git a/ruby/lib/prometheus-unifio/core/repositories/input_repository.rb b/ruby/lib/prometheus-unifio/core/repositories/input_repository.rb index b5bdc6c..8909de4 100644 --- a/ruby/lib/prometheus-unifio/core/repositories/input_repository.rb +++ b/ruby/lib/prometheus-unifio/core/repositories/input_repository.rb @@ -2,8 +2,8 @@ class InputRepository def self.query_by_namespace(data_store, namespace) - data_store.hash_lookup("#{namespace}::vars", {}).map do |k,v| - Input.new(name: k, raw_value: v) + data_store.hash_lookup("#{namespace}::vars", {}).map do |name, raw_value| + Input.new(name: name, raw_value: raw_value) end end end diff --git a/ruby/lib/prometheus-unifio/core/repositories/stack_repository.rb b/ruby/lib/prometheus-unifio/core/repositories/stack_repository.rb index e6a231a..9a7964d 100644 --- a/ruby/lib/prometheus-unifio/core/repositories/stack_repository.rb +++ b/ruby/lib/prometheus-unifio/core/repositories/stack_repository.rb @@ -28,27 +28,33 @@ def self.find(data_store, environment_name, stack_name) ) end - private - # stack.tf_module - def self.lookup_shared_namespace(data_store) - stack_name = data_store.scope['stack'] - mod = data_store.lookup("#{stack_name}::module", nil) - mod || stack_name - end - - def self.find_args_by_namespace(data_store, namespace) - args = data_store.lookup("#{namespace}::args", nil) - return (args != nil) ? args : "" - end + class << self + private + # stack.tf_module + def lookup_shared_namespace(data_store) + stack_name = data_store.scope['stack'] + mod = data_store.lookup("#{stack_name}::module", nil) + mod || stack_name + end - def self.validate_stack_scope(data_store, arguments) - required_args = %w(environment stack) - if required_args & arguments.stringify_keys.keys != required_args - raise "Missing required args: #{required_args} in arguments: #{arguments}" + # maybe arg_string instead of args + def find_args_by_namespace(data_store, namespace) + args = data_store.lookup("#{namespace}::args", nil) + return (args != nil) ? args : "" end - if data_store.lookup("#{arguments['stack']}::state", nil, arguments).nil? - raise "#{arguments['stack']} stack missing 'state' hash of the #{arguments['environment']} environment" + # :reek:FeatureEnvy + # :reek:NilCheck + def validate_stack_scope(data_store, arguments) + #TODO: might not need this arg check + required_args = %w(environment stack) + if required_args & arguments.stringify_keys.keys != required_args + raise "Missing required args: #{required_args} in arguments: #{arguments}" + end + + if data_store.lookup("#{arguments['stack']}::state", nil, arguments).nil? + raise "#{arguments['stack']} stack missing 'state' hash of the #{arguments['environment']} environment" + end end end end diff --git a/ruby/lib/prometheus-unifio/core/repositories/state_store_repository.rb b/ruby/lib/prometheus-unifio/core/repositories/state_store_repository.rb index 86d2cf2..e2a76d5 100644 --- a/ruby/lib/prometheus-unifio/core/repositories/state_store_repository.rb +++ b/ruby/lib/prometheus-unifio/core/repositories/state_store_repository.rb @@ -4,10 +4,10 @@ class StateStoreRepository def self.query_by_stack_name(data_store, stack_name) stores = data_store.lookup("#{stack_name}::state", nil) raise "State store array cannot be empty" if stores.empty? - stores.map do |s| + stores.map do |store| StateStore.new( - backend: s.keys.first, - params: s.values.first + backend: store.keys.first, + params: store.values.first ) end end diff --git a/ruby/lib/prometheus-unifio/core/services/hiera_syntax_service.rb b/ruby/lib/prometheus-unifio/core/services/hiera_syntax_service.rb index b8110eb..9129d77 100644 --- a/ruby/lib/prometheus-unifio/core/services/hiera_syntax_service.rb +++ b/ruby/lib/prometheus-unifio/core/services/hiera_syntax_service.rb @@ -12,6 +12,6 @@ def self.check_yaml(filelist) end end - errors.map { |e| e.to_s } + errors.map { |error| error.to_s } end end diff --git a/ruby/lib/prometheus-unifio/core/services/terraform_stack_tasks.rb b/ruby/lib/prometheus-unifio/core/services/terraform_stack_tasks.rb new file mode 100644 index 0000000..0843d8d --- /dev/null +++ b/ruby/lib/prometheus-unifio/core/services/terraform_stack_tasks.rb @@ -0,0 +1,172 @@ +require_relative '../../../prometheus-unifio' +require_relative '../repositories/environment_repository' +require_relative '../cli_wrappers/terraform_cli' + +class TerraformStackTasks + + def initialize(stack) + @path = File.expand_path(File.join(PrometheusUnifio::TERRAFORM, stack.tf_module)) + + # Primary state store assumption + @store_args = stack.state_stores.first.get_config + @stack = stack + end + + def stack_name + stack.name + end + + def environment_name + stack.environment_name + end + + def stack_clean + TerraformCli.terraform_clean(path) + end + + def stack_format + TerraformCli.terraform_fmt(path) + end + + # :reek:TooManyStatements + def stack_verify + TerraformCli.terraform_clean(path) + + Dir.mktmpdir do |tmpdir| + Dir.chdir(tmpdir) do + logger.info "In #{tmpdir}:" + TerraformCli.terraform_get(path) + TerraformCli.terraform_validate(path) + args = collect_args(stack.materialize_cmd_inputs, + "-input=false", + "-module-depth=-1", + stack.args) + + TerraformCli.terraform_plan(path, args: args) + end + end + end + + # :reek:TooManyStatements + def stack_sync + # might want some control/logic around which one is the source of truth other than the first + TerraformCli.terraform_clean(path) + + Dir.mktmpdir do |tmpdir| + Dir.chdir(tmpdir) do + logger.info "In #{tmpdir}:" + TerraformCli.terraform_remote_config(args: store_args) + + stack.state_stores.drop(1).each do |store| + TerraformCli.terraform_remote_config(args: '-disable') + TerraformCli.terraform_remote_config(args: "#{store.get_config} -pull=false") + TerraformCli.terraform_remote_push(path) + end + end + end + end + + # :reek:TooManyStatements + def context_plan(*additional_args) + TerraformCli.terraform_clean(path) + + Dir.mktmpdir do |tmpdir| + Dir.chdir(tmpdir) do + logger.info "In #{tmpdir}:" + TerraformCli.terraform_get(path) + + TerraformCli.terraform_remote_config(args: store_args.split(" ")) + TerraformCli.terraform_remote_config(args: ["-disable"]) + args = collect_args(stack.materialize_cmd_inputs, + "-input=false", + "-module-depth=-1", + stack.args, + additional_args) + + TerraformCli.terraform_plan(path, args: args) + end + end + end + + # :reek:TooManyStatements + def context_plan_destroy(*additional_args) + TerraformCli.terraform_clean(path) + + Dir.mktmpdir do |tmpdir| + Dir.chdir(tmpdir) do + logger.info "In #{tmpdir}:" + TerraformCli.terraform_get(path) + + TerraformCli.terraform_remote_config(args: store_args.split(" ")) + TerraformCli.terraform_remote_config(args: ["-disable"]) + args = collect_args(stack.materialize_cmd_inputs, + "-destroy", + "-input=false", + "-module-depth=-1", + stack.args, + additional_args) + + TerraformCli.terraform_plan(path, args: args) + end + end + end + + # :reek:TooManyStatements + def context_apply(*additional_args) + TerraformCli.terraform_clean(path) + + Dir.mktmpdir do |tmpdir| + Dir.chdir(tmpdir) do + logger.info "In #{tmpdir}:" + + TerraformCli.terraform_get(path) + TerraformCli.terraform_remote_config(args: store_args) + apply_args = collect_args(stack.materialize_cmd_inputs, + stack.args, + additional_args) + args = collect_args(apply_args, + "-input=false", + "-module-depth=-1") + + TerraformCli.terraform_plan(path, args: args) + TerraformCli.terraform_apply(path, args: apply_args) + end + end + end + + # :reek:TooManyStatements + def context_destroy(*additional_args) + TerraformCli.terraform_clean(path) + + Dir.mktmpdir do |tmpdir| + Dir.chdir(tmpdir) do + logger.info "In #{tmpdir}:" + + TerraformCli.terraform_get(path) + TerraformCli.terraform_remote_config(args: store_args) + destroy_args = collect_args(stack.materialize_cmd_inputs, + stack.args, + additional_args) + args = collect_args(destroy_args, + "-destroy", + "-input=false", + "-module-depth=-1") + + TerraformCli.terraform_plan(path, args: args) + TerraformCli.terraform_destroy(path, args: destroy_args) + end + end + end + + private + attr_reader :path, :stack, :store_args + + def logger + PrometheusUnifio::LOGGER + end + + # :reek:FeatureEnvy + def collect_args(*args) + args.flatten.compact.reject(&:empty?).map(&:strip) + end +end diff --git a/ruby/lib/tools/atlas.rb b/ruby/lib/prometheus-unifio/core/state_stores/atlas.rb similarity index 95% rename from ruby/lib/tools/atlas.rb rename to ruby/lib/prometheus-unifio/core/state_stores/atlas.rb index 48835b2..335a628 100644 --- a/ruby/lib/tools/atlas.rb +++ b/ruby/lib/prometheus-unifio/core/state_stores/atlas.rb @@ -97,17 +97,18 @@ def self.ensure_atlas_token_set end # Return module capabilities - def self.has_key_read? - return true - end - - def self.has_key_write? - return false - end - - def self.has_state_read? - return true - end + # TODO: maybe a state_store mixin later + #def self.has_key_read? + #return true + #end + + #def self.has_key_write? + #return false + #end + + #def self.has_state_read? + #return true + #end def self.has_state_store? return true diff --git a/ruby/lib/tools/consul.rb b/ruby/lib/prometheus-unifio/core/state_stores/consul.rb similarity index 92% rename from ruby/lib/tools/consul.rb rename to ruby/lib/prometheus-unifio/core/state_stores/consul.rb index dbc1e69..a4f60b6 100644 --- a/ruby/lib/tools/consul.rb +++ b/ruby/lib/prometheus-unifio/core/state_stores/consul.rb @@ -49,6 +49,7 @@ def self.get_key(name) if encoded != nil Base64.decode64(encoded) else + # TODO: not sure if this is the right failure to raise fail "Requested key '#{name}' not found" end end @@ -87,17 +88,18 @@ def self.get_state_store(params) end # Return module capabilities - def self.has_key_read? - return true - end - - def self.has_key_write? - return false - end - - def self.has_state_read? - return true - end + # TODO: maybe a state_store mixin later + #def self.has_key_read? + #return true + #end + + #def self.has_key_write? + #return false + #end + + #def self.has_state_read? + #return true + #end def self.has_state_store? return true diff --git a/ruby/lib/tools/s3.rb b/ruby/lib/prometheus-unifio/core/state_stores/s3.rb similarity index 93% rename from ruby/lib/tools/s3.rb rename to ruby/lib/prometheus-unifio/core/state_stores/s3.rb index 5c650cd..aa6b9e6 100644 --- a/ruby/lib/tools/s3.rb +++ b/ruby/lib/prometheus-unifio/core/state_stores/s3.rb @@ -80,17 +80,18 @@ def self.get_state_store(params) end # Return module capabilities - def self.has_key_read? - return true - end - - def self.has_key_write? - return false - end - - def self.has_state_read? - return true - end + # TODO: maybe a state_store mixin later + #def self.has_key_read? + #return true + #end + + #def self.has_key_write? + #return false + #end + + #def self.has_state_read? + #return true + #end def self.has_state_store? return true diff --git a/ruby/lib/prometheus-unifio/environment_tasks.rb b/ruby/lib/prometheus-unifio/environment_tasks.rb index a7f6cc4..40e4ebe 100644 --- a/ruby/lib/prometheus-unifio/environment_tasks.rb +++ b/ruby/lib/prometheus-unifio/environment_tasks.rb @@ -1,5 +1,170 @@ require 'rake' +require_relative '../prometheus-unifio' +require_relative 'core/services/terraform_stack_tasks' -prometheus_rake_dir = File.join(File.dirname(__FILE__), 'rake') +class EnvironmentTasks + include Rake::DSL -load "#{prometheus_rake_dir}/environment.rake" + attr_reader :environments, :logger + + def initialize + @environments = EnvironmentRepository.all + @logger = PrometheusUnifio::LOGGER + end + + # :reek:NestedIterators + # :reek:TooManyStatements + def run + all_namespace_terraform_tasks + environments.each do |environment| + environment_namespace_terraform_tasks(environment) + + environment.stacks.each do |stack| + tf_tasks = TerraformStackTasks.new(stack) + stack_namespace_terraform_tasks(tf_tasks) + + stack.contexts.each do |context| + context_namespace_terraform_tasks(tf_tasks, context) + end + end + end + end + + private + # :reek:TooManyStatements + def context_namespace_terraform_tasks(tf_tasks, context) + target_args = context.to_command_options + context_name = context.name + stack_name = tf_tasks.stack_name + environment_name = tf_tasks.environment_name + + desc "Create execution plan for the #{generate_rake_taskname(stack_name, context_name)} stack(:context) of the #{environment_name} environment" + task generate_rake_taskname(environment_name, stack_name, context_name, "plan") do + tf_tasks.context_plan(target_args) + end + + desc "Create destruction plan for the #{generate_rake_taskname(stack_name, context_name)} stack(:context) of the #{environment_name} environment" + task generate_rake_taskname(environment_name, stack_name, context_name, "plan_destroy") do + tf_tasks.context_plan_destroy(target_args) + end + + desc "Apply changes to the #{generate_rake_taskname(stack_name, context_name)} stack(:context) of the #{environment_name} environment" + task generate_rake_taskname(environment_name, stack_name, context_name, "apply") do + tf_tasks.context_apply(target_args) + end + + desc "Destroy the #{generate_rake_taskname(stack_name, context_name)} stack(:context) of the #{environment_name} environment" + task generate_rake_taskname(environment_name, stack_name, context_name, "destroy") do + tf_tasks.context_destroy(target_args) + end + end + + # :reek:TooManyStatements + def stack_namespace_terraform_tasks(tf_tasks) + stack_name = tf_tasks.stack_name + environment_name = tf_tasks.environment_name + + desc "Clean the #{stack_name} stack of the #{environment_name} environment" + task generate_rake_taskname(environment_name, stack_name, "clean") do + tf_tasks.stack_clean + end + + desc "Format the #{stack_name} stack of the #{environment_name} environment" + task generate_rake_taskname(environment_name, stack_name, "format") do + tf_tasks.stack_format + end + + desc "Synchronize state stores for the #{stack_name} stack of the #{environment_name} environment" + task generate_rake_taskname(environment_name, stack_name, "sync") do + tf_tasks.stack_sync + end + + desc "Verify the #{stack_name} stack of the #{environment_name} environment" + # Maybe verify_local to highlight that it skips pulling in remote state + task generate_rake_taskname(environment_name, stack_name, "verify") do + tf_tasks.stack_verify + end + end + + # :reek:TooManyStatements + # :reek:DuplicateMethodCall + # :reek:FeatureEnvy + # rubocop:disable Metrics/MethodLength + def environment_namespace_terraform_tasks(environ) + desc "Clean the #{environ.name} environment" + task "#{environ.name}:clean" do + environ.stacks.each { |stack| execute_rake_task(environ.name, stack.name, "clean") } + end + + desc "Verify the #{environ.name} environment" + task "#{environ.name}:verify" do + environ.stacks.each { |stack| execute_rake_task(environ.name, stack.name, "verify") } + end + + desc "Create execution plan for the #{environ.name} environment" + task "#{environ.name}:plan" do + environ.stacks.each do |stack| + stack.contexts.each do |context| + execute_rake_task(environ.name, stack.name, context.name, "plan") + end + end + end + + desc "Create destruction plan for the #{environ.name} environment" + task "#{environ.name}:plan_destroy" do + environ.stacks.each do |stack| + stack.contexts.each do |context| + execute_rake_task(environ.name, stack.name, context.name, "plan_destroy") + end + end + end + + desc "Apply changes to the #{environ.name} environment" + task "#{environ.name}:apply" do + environ.stacks.each do |stack| + stack.contexts.each do |context| + execute_rake_task(environ.name, stack.name, context.name, "apply") + end + end + end + + desc "Destroy the #{environ.name} environment" + task "#{environ.name}:destroy" do + environ.stacks.each do |stack| + stack.contexts.each do |context| + execute_rake_task(environ.name, stack.name, context.name, "destroy") + end + end + end + + desc "Synchronize state stores for the #{environ.name} environment" + task "#{environ.name}:sync" do + environ.stacks.each { |stack| execute_rake_task(environ.name, stack.name, "sync") } + end + end + + # :reek:TooManyStatements + def all_namespace_terraform_tasks + desc "Clean all environments" + task "all:clean" do + environments.each { |environ| execute_rake_task(environ.name, "clean") } + end + + desc "Verify all environments" + task "all:verify" do + environments.each { |environ| execute_rake_task(environ.name, "verify") } + end + end + + def generate_rake_taskname(*args) + args.delete_if(&:empty?).map(&:to_s).join(":") + end + + def execute_rake_task(*args) + task_name = generate_rake_taskname(*args) + logger.info "rake #{task_name}" + Rake::Task[task_name].execute + end +end + +EnvironmentTasks.new.run diff --git a/ruby/lib/prometheus-unifio/rake/environment.rake b/ruby/lib/prometheus-unifio/rake/environment.rake deleted file mode 100644 index 8de6f9d..0000000 --- a/ruby/lib/prometheus-unifio/rake/environment.rake +++ /dev/null @@ -1,163 +0,0 @@ -require_relative '../core/bootstrap' - -desc "Clean all environments" -task "all:clean" do - EnvironmentRepository.all.each do |environ| - Rake::Task["#{environ.name}:clean"].execute - end -end - -desc "Verify all environments" -task "all:verify" do - EnvironmentRepository.all.each do |environ| - Rake::Task["#{environ.name}:verify"].execute - end -end - -EnvironmentRepository.all.each do |environ| - namespace environ.name do - environ.stacks.each do |stack| - - tf = Terraform::Stack.new(stack.tf_module) - - # Pull primary state store - store_args = stack.state_stores.first.get_config - - # Stack tasks - namespace stack.name do - - desc "Clean the #{stack.name} stack of the #{environ.name} environment" - task :clean do - tf.clean - end - - desc "Format the #{stack.name} stack of the #{environ.name} environment" - task :format do - tf.fmt - end - - desc "Verify the #{stack.name} stack of the #{environ.name} environment" - task :verify do - input_args = tf.parse_vars(stack.materialize_inputs) - tf.clean - tf.get - tf.validate - tf.plan("#{input_args} -input=false -module-depth=-1 #{stack.args}".strip) - end - - stack.contexts.each do |context| - - target_args = tf.parse_targets(context.value) - - desc "Create execution plan for the #{stack.name} stack of the #{environ.name} environment" - task "#{context.namespace}plan".strip do - input_args = tf.parse_vars(stack.materialize_inputs) - tf.clean - tf.remote_config(store_args) - tf.get - tf.plan("#{input_args} -input=false -module-depth=-1 #{stack.args} #{target_args}".strip) - end - - desc "Create destruction plan for the #{stack.name} stack of the #{environ.name} environment" - task "#{context.namespace}plan_destroy" do - input_args = tf.parse_vars(stack.materialize_inputs) - tf.clean - tf.get - tf.plan("#{input_args} -destroy -input=false -module-depth=-1 #{stack.args} #{target_args}".strip) - end - - desc "Apply changes to the #{stack.name} stack of the #{environ.name} environment" - task "#{context.namespace}apply" do - input_args = tf.parse_vars(stack.materialize_inputs) - tf.clean - tf.remote_config(store_args) - tf.get - tf.plan("#{input_args} -input=false -module-depth=-1 #{stack.args} #{target_args}".strip) - tf.apply("#{input_args} #{stack.args} #{target_args}".strip) - end - - desc "Apply changes to the #{stack.name} stack of the #{environ.name} environment" - task "#{context.namespace}destroy" do - input_args = tf.parse_vars(stack.materialize_inputs) - tf.clean - tf.remote_config(store_args) - tf.get - tf.plan("#{input_args} -destroy -input=false -module-depth=-1 #{stack.args} #{target_args}".strip) - tf.destroy("#{input_args} #{stack.args} #{target_args}".strip) - end - end - - desc "Synchronize state stores for the #{stack.name} stack of the #{environ.name} environment" - task :sync do - input_args = tf.parse_vars(stack.materialize_inputs) - tf.clean - tf.remote_config(store_args) - - stack.state_stores.drop(1).each do |store| - tf.remote_config('-disable') - tf.remote_config("#{store.get_config} -pull=false") - tf.remote_push - end - end - end - end - - # Environment tasks - desc "Clean the #{environ.name} environment" - task :clean do - environ.stacks.each do |stack| - Rake::Task["#{environ.name}:#{stack.name}:clean"].execute - end - end - - desc "Verify the #{environ.name} environment" - task :verify do - environ.stacks.each do |stack| - Rake::Task["#{environ.name}:#{stack.name}:verify"].execute - end - end - - desc "Create execution plan for the #{environ.name} environment" - task :plan do - environ.stacks.each do |stack| - stack.contexts.each do |context| - Rake::Task["#{environ.name}:#{stack.name}:#{context.namespace}plan"].execute - end - end - end - - desc "Create destruction plan for the #{environ.name} environment" - task :plan_destroy do - environ.stacks.each do |stack| - stack.contexts.each do |context| - Rake::Task["#{environ.name}:#{stack.name}:#{context.namespace}plan_destroy"].execute - end - end - end - - desc "Apply changes to the #{environ.name} environment" - task :apply do - environ.stacks.each do |stack| - stack.contexts.each do |context| - Rake::Task["#{environ.name}:#{stack.name}:#{context.namespace}apply"].execute - end - end - end - - desc "Destroy the #{environ.name} environment" - task :destroy do - environ.stacks.reverse.each do |stack| - stack.contexts.each do |context| - Rake::Task["#{environ.name}:#{stack.name}:#{context.namespace}destroy"].execute - end - end - end - - desc "Synchronize state stores for the #{environ.name} environment" - task :sync do - environ.stacks.each do |stack| - Rake::Task["#{environ.name}:#{stack.name}:sync"].execute - end - end - end -end diff --git a/ruby/lib/prometheus-unifio/rake/rspec.rake b/ruby/lib/prometheus-unifio/rake/rspec.rake deleted file mode 100644 index e7a8f1c..0000000 --- a/ruby/lib/prometheus-unifio/rake/rspec.rake +++ /dev/null @@ -1,51 +0,0 @@ -require 'rspec/core/rake_task' -require 'ci/reporter/rake/rspec' - -require_relative '../../prometheus-unifio' - -task :ci => ['ci:setup:rspec', 'spec:envs'] -task :spec => 'spec:prometheus' - -desc 'Run all spec tests' - -namespace :spec do - - # extract into spec helper. - desc 'Run Prometheus tests' - RSpec::Core::RakeTask.new(:prometheus) do |t| - t.pattern = "#{File.join(PrometheusUnifio::PROJECT_ROOT, 'spec/**/*_spec.rb')}" - t.rspec_opts = "--color --format documentation" - t.verbose = true - end - - desc "Verify environments" - RSpec::Core::RakeTask.new(:envs) do |t| - t.pattern = "#{File.join(PrometheusUnifio::GEM_ROOT, 'rake/rspec/envs_spec.rb')}" - t.rspec_opts = '--color --format documentation' - t.verbose = true - end - - desc 'Check syntax of all .yaml files' - RSpec::Core::RakeTask.new(:check_yaml) do |t| - t.pattern = "#{File.join(PrometheusUnifio::GEM_ROOT, 'rake/rspec/yaml_spec.rb')}" - t.rspec_opts = '--color --format documentation' - t.verbose = true - end - -end - -namespace :ci do - desc 'Clean spec/reports' - task :clean => ['ci:setup:rspec'] - - # extract into spec helper. - desc 'Run Prometheus tests' - task :prometheus => ['ci:setup:rspec', 'spec:prometheus'] - - desc 'Verify all environments' - task :envs => ['ci:setup:rspec', 'spec:envs'] - - desc 'Check syntax of all .yaml files' - task :check_yaml => ['ci:setup:rspec', 'spec:check_yaml'] - -end diff --git a/ruby/lib/prometheus-unifio/rake/rspec/envs_spec.rb b/ruby/lib/prometheus-unifio/rake/rspec/envs_spec.rb index 0dd9bb7..4698edf 100644 --- a/ruby/lib/prometheus-unifio/rake/rspec/envs_spec.rb +++ b/ruby/lib/prometheus-unifio/rake/rspec/envs_spec.rb @@ -1,42 +1,42 @@ require_relative '../../core/bootstrap' -test_env = 'example' envs = EnvironmentRepository.all # spec_helper if ENV['PROMETHEUS_TEST_ENVS'] test_envs = ENV['PROMETHEUS_TEST_ENVS'].split(',') - envs = envs.select { |environ| test_envs.include?(environ.to_s) } + envs = envs.select { |environ| test_envs.include?(environ.name.to_s) } end envs.each do |env| env.stacks.each do |stack| + path = File.expand_path(File.join(PrometheusUnifio::TERRAFORM, stack.tf_module)) describe "Verify #{env.name}:#{stack.name}" do before(:all) do - @tf = Terraform::Stack.new(stack.tf_module, stub: false) - @tf.clean - @tf.get + TerraformCli.terraform_clean(path) + TerraformCli.terraform_get(path) end it 'passes style check' do - expect { - @tf.check_style - }.to_not raise_error + expect(TerraformCli.terraform_check_style(path)).to be true end it 'passes validation' do expect { - @tf.validate + TerraformCli.terraform_validate(path) }.to_not raise_error end it 'passes execution' do - input_args = @tf.parse_vars(stack.materialize_inputs) - expect { - @tf.plan("#{input_args} -input=false -module-depth=-1 #{stack.args}".strip) - }.to_not raise_error + TerraformCli.terraform_remote_config(args: "-disable") + args = stack.materialize_cmd_inputs + [ + "-input=false", + "-module-depth=-1", + stack.args.strip, + ] + expect(TerraformCli.terraform_plan(path, args: args)).to be true end end end diff --git a/ruby/lib/prometheus-unifio/rake/rspec/yaml_spec.rb b/ruby/lib/prometheus-unifio/rake/rspec/yaml_spec.rb index 8dae200..334ec3e 100644 --- a/ruby/lib/prometheus-unifio/rake/rspec/yaml_spec.rb +++ b/ruby/lib/prometheus-unifio/rake/rspec/yaml_spec.rb @@ -3,7 +3,7 @@ RSpec.describe HieraSyntaxService do describe ".check_yaml" do - let(:files) { Dir.glob("#{PrometheusUnifio::WORKSPACE}/**/*.yaml") } + let(:files) { Dir.glob("#{PrometheusUnifio::WORKSPACE}/**/*.y*ml") } it 'passes syntax check' do errors = described_class.check_yaml(files) expect(errors).to be_empty diff --git a/ruby/lib/prometheus-unifio/spec_tasks.rb b/ruby/lib/prometheus-unifio/spec_tasks.rb index 2c3e99f..9eb8e10 100644 --- a/ruby/lib/prometheus-unifio/spec_tasks.rb +++ b/ruby/lib/prometheus-unifio/spec_tasks.rb @@ -1,8 +1,57 @@ require 'rake' +require_relative '../prometheus-unifio' require_relative 'helpers/spec_dependencies' # Check gem constraints before continuing PrometheusUnifio::Helpers::SpecDependencies.check_dependencies +require 'rspec/core/rake_task' +require 'ci/reporter/rake/rspec' -prometheus_rake_dir = File.join(File.dirname(__FILE__), 'rake') -load "#{prometheus_rake_dir}/rspec.rake" +class SpecTasks + extend Rake::DSL + + def self.run + ci_namespace_rspec_tasks + spec_namespace_rspec_tasks + end + + class << self + private + + # :reek:TooManyStatements + def ci_namespace_rspec_tasks + desc 'Run CI tests' + task "ci" => ['ci:setup:rspec', 'spec:envs'] + + desc 'Clean spec/reports' + task "ci:clean" => ['ci:setup:rspec'] + + desc 'Verify all environments' + task "ci:envs" => ['ci:setup:rspec', 'spec:envs'] + + desc 'Check syntax of all .yaml files' + task "ci:check_yaml" => ['ci:setup:rspec', 'spec:check_yaml'] + end + + # :reek:TooManyStatements + # :reek:FeatureEnvy + # :reek:UncommunicativeVariableName + def spec_namespace_rspec_tasks + desc "Verify environments" + RSpec::Core::RakeTask.new("spec:envs") do |t| + t.pattern = "#{File.join(PrometheusUnifio::GEM_ROOT, 'rake/rspec/envs_spec.rb')}" + t.rspec_opts = '--color --format documentation' + t.verbose = true + end + + desc 'Check syntax of all .yaml files' + RSpec::Core::RakeTask.new("spec:check_yaml") do |t| + t.pattern = "#{File.join(PrometheusUnifio::GEM_ROOT, 'rake/rspec/yaml_spec.rb')}" + t.rspec_opts = '--color --format documentation' + t.verbose = true + end + end + end +end + +SpecTasks.run diff --git a/ruby/lib/prometheus-unifio/version.rb b/ruby/lib/prometheus-unifio/version.rb index 0b4c9db..33b0502 100644 --- a/ruby/lib/prometheus-unifio/version.rb +++ b/ruby/lib/prometheus-unifio/version.rb @@ -1,3 +1,3 @@ module PrometheusUnifio - VERSION = "0.2.0" + VERSION = "0.3.0.beta1" end diff --git a/ruby/lib/tools/terraform.rb b/ruby/lib/tools/terraform.rb deleted file mode 100644 index 7e756ab..0000000 --- a/ruby/lib/tools/terraform.rb +++ /dev/null @@ -1,115 +0,0 @@ -require 'rake' -require_relative '../prometheus-unifio' - -module Terraform - STACKS_DIR = PrometheusUnifio::TERRAFORM - TF_STUB = ENV['TERRAFORM_STUB'] || "" - TF_ENV = ENV['TERRAFORM_ENV'] || "" - TF_IMG = ENV['TERRAFORM_IMG'] || "" - TF_CMD = ENV['TERRAFORM_CMD'] || "terraform" - - class Stack - def initialize(name, dir: STACKS_DIR, env: TF_ENV, img: TF_IMG, cmd: TF_CMD, stub: TF_STUB) - self.path = File.join(dir, name) - @env = env - @stub = !!(stub =~ /^(true|t|yes|y|1)$/i) - - if img.empty? - @tf_cmd = cmd - @cln_cmd = "/bin/sh -c" - else - @tf_cmd = "#{cmd} -v #{dir}:/data -w /data/#{name} #{img}" - @cln_cmd = "#{cmd} -v #{dir}:/data -w /data/#{name} --entrypoint=\"/bin/sh\" #{img} -c" - end - end - - def path=(path) - raise "#{path} doesn't exist" unless Dir.exists?(path) - @path = path - end - - def remote_config(args='') - run_cmd('remote', "config #{args}".strip) - end - - def remote_pull() - run_cmd('remote','pull') - end - - def remote_push() - run_cmd('remote','push') - end - - def get(args='') - run_cmd('get', args) - end - - def validate(args='') - run_cmd('validate', args) - end - - def plan(args='') - run_cmd('plan', args) - end - - def apply(args='') - run_cmd('apply', args) - end - - def destroy(args='') - run_cmd('destroy', args) - end - - def fmt(args='') - run_cmd('fmt', args) - end - - def clean() - Dir.chdir(@path) do - Rake.sh "#{@cln_cmd} \"rm -fr .terraform *.tfstate*\"" unless @stub - end - end - - def check_style() - Dir.chdir(@path) do - Rake.sh "test $(#{@tf_cmd} fmt -write=false | wc -l) -eq 0" unless @stub - end - end - - def parse_vars(vars) - vars.map { |var, value| "-var #{var}=\"#{value}\"" }.join(' ') - end - - def parse_targets(targets) - targets.map { |target| "-target=#{target}" }.join(' ') - end - - def run_cmd(cmd, args) - Dir.chdir(@path) do - run_rake_cmd cmd, args - end - end - - def run_rake_cmd(cmd, args='') - Rake.sh "#{@env} #{@tf_cmd} #{cmd} #{args}".strip unless @stub - end - end - - # Return module capabilities - def self.has_key_read? - return false - end - - def self.has_key_write? - return false - end - - def self.has_state_read? - return false - end - - def self.has_state_store? - return false - end - -end diff --git a/spec/core/cli_wrappers/terraform_cli_spec.rb b/spec/core/cli_wrappers/terraform_cli_spec.rb new file mode 100644 index 0000000..d33a16a --- /dev/null +++ b/spec/core/cli_wrappers/terraform_cli_spec.rb @@ -0,0 +1,175 @@ +require 'fileutils' +require 'tmpdir' +require 'active_support/core_ext/kernel/reporting' + +require 'spec_helper' +require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/cli_wrappers/terraform_cli') + +RSpec.describe TerraformCli do + before(:all) do + @tmp_dir = Dir.mktmpdir + end + after(:all) { FileUtils.remove_entry(@tmp_dir) } + before(:each) do + allow($stdout).to receive(:write) + end + + describe "when running native terraform", :native do + before(:each) do + ENV['TERRAFORM_IMG'] = "" + ENV['TERRAFORM_CMD'] = "terraform" + # force constants to re-init + Kernel.silence_warnings { + load File.join(PrometheusUnifio::GEM_ROOT, '../prometheus-unifio.rb') + } + end + + it "#terraform_apply" do + expected_args = [ENV.to_h, "terraform apply #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_apply(path = @tmp_dir)).to be true + end + + it "#terraform_destroy" do + expected_args = [ENV.to_h, "terraform destroy #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_destroy(path = @tmp_dir)).to be true + end + + it "#terraform_fmt" do + expected_args = [ENV.to_h, "terraform fmt #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_fmt(path = @tmp_dir)).to be true + end + + it "#terraform_get" do + expected_args = [ENV.to_h, "terraform get #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_get(path = @tmp_dir)).to be true + end + + it "#terraform_graph" do + expected_args = [ENV.to_h, "terraform graph #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_graph(path = @tmp_dir)).to be true + end + + it "#terraform_plan" do + expected_args = [ENV.to_h, "terraform plan #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_plan(path = @tmp_dir)).to be true + end + + it "#terraform_push" do + expected_args = [ENV.to_h, "terraform push #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_push(path = @tmp_dir)).to be true + end + + it "#terraform_refresh" do + expected_args = [ENV.to_h, "terraform refresh #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_refresh(path = @tmp_dir)).to be true + end + + it "#terraform_remote_config" do + expected_args = [ENV.to_h, "terraform remote config #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_remote_config(path = @tmp_dir)).to be true + end + + it "#terraform_remote_pull" do + expected_args = [ENV.to_h, "terraform remote pull #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_remote_pull(path = @tmp_dir)).to be true + end + + it "#terraform_remote_push" do + expected_args = [ENV.to_h, "terraform remote push #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_remote_push(path = @tmp_dir)).to be true + end + + it "#terraform_show" do + expected_args = [ENV.to_h, "terraform show #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_show(path = @tmp_dir)).to be true + end + + it "#terraform_validate" do + expected_args = [ENV.to_h, "terraform validate #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_validate(path = @tmp_dir)).to be true + end + + it "#terraform_version" do + expected_args = [ENV.to_h, "terraform version #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_version(path = @tmp_dir)).to be true + end + + pending "#terraform_clean" + + it "#terraform_check_style" do + expected_args = [ENV, "terraform", "fmt", "-write=false", @tmp_dir] + process_double = double("process_status") + allow(process_double).to receive(:success?).and_return(true) + expect(Open3).to receive(:capture2e).with(*expected_args).and_return(["", process_double]) + expect(described_class.terraform_check_style(path = @tmp_dir)).to be true + end + + it "executes terraform commands with custom settings" do + ENV['TERRAFORM_CMD'] = "/usr/local/bin/terraform" + # force constants to re-init + Kernel.silence_warnings { + # swallow all rescue from non-existant terraform binary above. + load File.join(PrometheusUnifio::GEM_ROOT, '../prometheus-unifio.rb') rescue nil + } + + expected_args = [ENV.to_h, "/usr/local/bin/terraform plan #{@tmp_dir}"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_plan(path = @tmp_dir)).to be true + end + end + + describe "executes terraform commands within a container", if: system('docker info > /dev/null 2>&1') do + before(:each) do + ENV['TERRAFORM_IMG'] = "unifio/terraform:latest" + ENV['TERRAFORM_CMD'] = "docker run -e ATLAS_TOKEN=$ATLAS_TOKEN --rm" + # force constants to re-init + Kernel.silence_warnings { + load File.join(PrometheusUnifio::GEM_ROOT, '../prometheus-unifio.rb') + } + end + + it "#terraform_plan" do + parent, base = Pathname.new(@tmp_dir).split + expected_args = [ENV.to_h, "#{ENV['TERRAFORM_CMD']} -v #{parent}:/data -w /data/#{base} #{ENV['TERRAFORM_IMG']} plan"] + expect(Kernel).to receive(:system).with(*expected_args).and_return(true) + expect(described_class.terraform_plan(path = @tmp_dir)).to be true + end + + it "#terraform_check_style" do + parent, base = Pathname.new(@tmp_dir).split + expected_args = [ENV, "#{ENV['TERRAFORM_CMD']} -v #{parent}:/data -w /data/#{base} #{ENV['TERRAFORM_IMG']} fmt -write=false"] + process_double = double("process_status") + allow(process_double).to receive(:success?).and_return(true) + expect(Open3).to receive(:capture2e).with(*expected_args).and_return(["", process_double]) + expect(described_class.terraform_check_style(path = @tmp_dir)).to be true + end + end + + pending "cleans up existing state data from the given stack directory" + #@cmd_test = Terraform::Stack.new(@stack_dir, dir: @parent_dir, env: "", img: "", cmd: "", stub: "false") + #cmd = "/bin/sh -c \"rm -fr .terraform *.tfstate*\"" + #expect(Rake).to receive(:sh).with(cmd).and_return(true) + #@cmd_test.clean + + #it "cleans up existing state data from the given stack directory within a container" do + # unnecesary, this was roundabout way to clean the files. + #@cmd_test = Terraform::Stack.new(@stack_dir, dir: @parent_dir, env: "", img: "unifio/terraform:latest", cmd: "docker run --rm", stub: "false") + #cmd = "docker run --rm -v #{@parent_dir}:/data -w /data/#{@stack_dir} --entrypoint=\"/bin/sh\" unifio/terraform:latest -c \"rm -fr .terraform *.tfstate*\"" + #expect(Rake).to receive(:sh).with(cmd).and_return(true) + #@cmd_test.clean + #end +end diff --git a/spec/core/entities/context_spec.rb b/spec/core/entities/context_spec.rb index d479989..02df4e5 100644 --- a/spec/core/entities/context_spec.rb +++ b/spec/core/entities/context_spec.rb @@ -18,4 +18,6 @@ expect(Fabricate(:context, name: '').namespace).to eq('') end end + + pending "#to_command_option" end diff --git a/spec/core/entities/input_spec.rb b/spec/core/entities/input_spec.rb index 051e573..6d0b1d9 100644 --- a/spec/core/entities/input_spec.rb +++ b/spec/core/entities/input_spec.rb @@ -12,13 +12,13 @@ end end - context "local input" do + context "with local input" do let(:raw_value) { "test" } it { expect(input.value).to eq(raw_value) } end - context "remote input" do + context "with remote input" do let(:test_backend_class) { TestBackend = Class.new(Object) } let(:raw_value) { { type: "test_backend.#{subcategory}", more: 'data' }.stringify_keys } @@ -33,5 +33,30 @@ expect(input.value).to eq(remote_value) expect(input.raw_value).to_not eq(remote_value) end + + pending "#to_command_option" + end + + describe "#to_command_option" do + let(:raw_value) { "test" } + before(:each) do + ENV['TERRAFORM_VERSION'] = tf_version + # force constants to re-init + Kernel.silence_warnings { + load File.join(PrometheusUnifio::GEM_ROOT, '../prometheus-unifio.rb') + } + end + + context "with Terraform Version < 0.7.0" do + let(:tf_version) { "0.6.5" } + + it { expect(input.to_command_option).to eq("-var input=\"test\"") } + end + + context "with Terraform Version >= 0.7.0" do + let(:tf_version) { "0.7.0" } + + it { expect(input.to_command_option).to eq("-var 'input=\"test\"'") } + end end end diff --git a/spec/core/entities/stack_spec.rb b/spec/core/entities/stack_spec.rb index d954884..d3a410b 100644 --- a/spec/core/entities/stack_spec.rb +++ b/spec/core/entities/stack_spec.rb @@ -17,8 +17,9 @@ end end - describe "#materialize_inputs" do - end + pending "#materialize_inputs" + + pending "#materialize_cmd_inputs" it "should accept empty args" do expect(Fabricate(:stack, args: '').args).to eql('') diff --git a/spec/core/repositories/stack_repository_spec.rb b/spec/core/repositories/stack_repository_spec.rb index 23418fc..0c4e4a4 100644 --- a/spec/core/repositories/stack_repository_spec.rb +++ b/spec/core/repositories/stack_repository_spec.rb @@ -1,8 +1,21 @@ require 'spec_helper' +require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/data_stores/hiera') require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/repositories/stack_repository') RSpec.describe StackRepository do describe ".find" do - pending + context "with a valid stack" do + pending + end + + context "with an invalid stack" do + let(:data_store) { HieraDB::Client.new("spec/fixtures/prometheus_bad_state.yaml") } + + it "raises an error" do + expect { + described_class.find(data_store, 'example', 'bad_state') + }.to raise_exception(RuntimeError, /missing 'state' hash/) + end + end end end diff --git a/spec/atlas/atlas_spec.rb b/spec/core/state_stores/atlas_spec.rb similarity index 84% rename from spec/atlas/atlas_spec.rb rename to spec/core/state_stores/atlas_spec.rb index 0176861..24cd6f5 100644 --- a/spec/atlas/atlas_spec.rb +++ b/spec/core/state_stores/atlas_spec.rb @@ -1,5 +1,5 @@ -require_relative '../../ruby/lib/tools/atlas.rb' -require 'webmock/rspec' +require 'spec_helper' +require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/state_stores/atlas') RSpec.describe Atlas do @@ -36,7 +36,7 @@ context "Artifact" do before(:each) do @stub = stub_request(:any, /#{Atlas::URL}.*/). - to_return(:body => File.new('./spec/atlas/artifact_response.json'), :status => 200) + to_return(:body => File.new('./spec/fixtures/mock_responses/atlas/artifact_response.json'), :status => 200) Atlas::reset_cache end @@ -57,7 +57,7 @@ it "is not returned if it does not contain the requested key" do @stub = stub_request(:any, /#{Atlas::URL}.*/). - to_return(:body => File.new('./spec/atlas/artifact_response.json'), :status => 200) + to_return(:body => File.new('./spec/fixtures/mock_responses/atlas/artifact_response.json'), :status => 200) Atlas::reset_cache expect { @@ -75,7 +75,7 @@ context "Stack" do before(:each) do @stub = stub_request(:any, /#{Atlas::URL}.*/). - to_return(:body => File.new('./spec/atlas/state_response.json'), :status => 200) + to_return(:body => File.new('./spec/fixtures/mock_responses/atlas/state_response.json'), :status => 200) Atlas::reset_cache end @@ -96,7 +96,7 @@ it "is not returned if it does not contain the requested output" do @stub = stub_request(:any, /#{Atlas::URL}.*/). - to_return(:body => File.new('./spec/atlas/state_response.json'), :status => 200) + to_return(:body => File.new('./spec/fixtures/mock_responses/atlas/state_response.json'), :status => 200) Atlas::reset_cache expect { @@ -128,7 +128,7 @@ context "Artifact" do before(:each) do @stub = stub_request(:any, /#{Atlas::URL}.*/). - to_return(:body => File.new('./spec/atlas/artifact_response.json'), :status => 200) + to_return(:body => File.new('./spec/fixtures/mock_responses/atlas/artifact_response.json'), :status => 200) Atlas::reset_cache @type = 'artifact' @params = { @@ -155,7 +155,7 @@ context "State" do before(:each) do @stub = stub_request(:any, /#{Atlas::URL}.*/). - to_return(:body => File.new('./spec/atlas/state_response.json'), :status => 200) + to_return(:body => File.new('./spec/fixtures/mock_responses/atlas/state_response.json'), :status => 200) Atlas::reset_cache @type = 'state' @params = { @@ -177,5 +177,9 @@ expect(result).to eql('vpc-12345678') end end + + it "invalid type" do + expect{ Atlas.lookup('invalid', {}) }.to raise_error(RuntimeError, /does not support the 'invalid' lookup type/) + end end end diff --git a/spec/consul/consul_spec.rb b/spec/core/state_stores/consul_spec.rb similarity index 74% rename from spec/consul/consul_spec.rb rename to spec/core/state_stores/consul_spec.rb index 02a2f03..8ced396 100644 --- a/spec/consul/consul_spec.rb +++ b/spec/core/state_stores/consul_spec.rb @@ -1,12 +1,15 @@ -require_relative '../../ruby/lib/tools/consul.rb' -require 'webmock/rspec' +require 'spec_helper' +require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/state_stores/consul') RSpec.describe Consul do + it ".has_state_store?" do + expect(described_class.has_state_store?).to be true + end context "Key" do before(:each) do @stub = stub_request(:any, /#{Consul::URL}.*/). - to_return(:body => File.new('./spec/consul/key_response.json'), :status => 200) + to_return(:body => File.new('./spec/fixtures/mock_responses/consul/key_response.json'), :status => 200) Consul::reset_cache end @@ -22,22 +25,21 @@ expect { Consul::get_key('conf/example_ip') - }.to raise_error(RuntimeError) + }.to raise_error(RuntimeError, /Unable to retrieve key/) end it "is not returned if it does not contain the requested value" do @stub = stub_request(:any, /#{Consul::URL}.*/). - to_return(:body => File.new('./spec/consul/empty_response.json'), :status => 200) + to_return(:body => File.new('./spec/fixtures/mock_responses/consul/empty_response.json'), :status => 200) Consul::reset_cache expect { Consul::get_key('conf/example_cidr') - }.to raise_error(RuntimeError) + }.to raise_error(RuntimeError, /No results or unable to parse response/) end it "caches multiple requests" do - Consul::get_key('conf/example_ip') - Consul::get_key('conf/example_ip') + 2.times { Consul::get_key('conf/example_ip') } expect(@stub).to have_been_requested.once end end @@ -45,7 +47,7 @@ context "Stack" do before(:each) do @stub = stub_request(:any, /#{Consul::URL}.*/). - to_return(:body => File.new('./spec/consul/state_response.json'), :status => 200) + to_return(:body => File.new('./spec/fixtures/mock_responses/consul/state_response.json'), :status => 200) Consul::reset_cache end @@ -66,7 +68,7 @@ it "is not returned if it does not contain the requested output" do @stub = stub_request(:any, /#{Consul::URL}.*/). - to_return(:body => File.new('./spec/consul/state_response.json'), :status => 200) + to_return(:body => File.new('./spec/fixtures/mock_responses/consul/state_response.json'), :status => 200) Consul::reset_cache expect { @@ -98,7 +100,7 @@ context "Key" do before(:each) do @stub = stub_request(:any, /#{Consul::URL}.*/). - to_return(:body => File.new('./spec/consul/key_response.json'), :status => 200) + to_return(:body => File.new('./spec/fixtures/mock_responses/consul/key_response.json'), :status => 200) Consul::reset_cache @type = 'key' @params = { @@ -123,7 +125,7 @@ context "State" do before(:each) do @stub = stub_request(:any, /#{Consul::URL}.*/). - to_return(:body => File.new('./spec/consul/state_response.json'), :status => 200) + to_return(:body => File.new('./spec/fixtures/mock_responses/consul/state_response.json'), :status => 200) Consul::reset_cache @type = 'state' @params = { @@ -145,5 +147,9 @@ expect(result).to eql('vpc-12345678') end end + + it "invalid type" do + expect{ Consul.lookup('invalid', {}) }.to raise_error(RuntimeError, /does not support the 'invalid' lookup type/) + end end end diff --git a/spec/s3/s3_spec.rb b/spec/core/state_stores/s3_spec.rb similarity index 87% rename from spec/s3/s3_spec.rb rename to spec/core/state_stores/s3_spec.rb index 13ed70c..1d2efc4 100644 --- a/spec/s3/s3_spec.rb +++ b/spec/core/state_stores/s3_spec.rb @@ -1,5 +1,5 @@ -require_relative '../../ruby/lib/tools/s3.rb' -require 'aws-sdk' +require 'spec_helper' +require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/state_stores/s3') RSpec.describe S3 do @@ -7,7 +7,7 @@ before(:all) do Aws.config[:s3] = { stub_responses: { - get_object: { body: File.new('./spec/s3/key_response.json') } + get_object: { body: File.new('./spec/fixtures/mock_responses/s3/key_response.json') } } } @client = S3::Client.new(region: 'us-east-1') @@ -46,7 +46,7 @@ before(:all) do Aws.config[:s3] = { stub_responses: { - get_object: { body: File.new('./spec/s3/state_response.json') } + get_object: { body: File.new('./spec/fixtures/mock_responses/s3/state_response.json') } } } @client = S3::Client.new(region: 'us-east-1') @@ -80,7 +80,7 @@ before(:all) do Aws.config[:s3] = { stub_responses: { - get_object: { body: File.new('./spec/s3/key_response.json') } + get_object: { body: File.new('./spec/fixtures/mock_responses/s3/key_response.json') } } } @client = S3::Client.new(region: 'us-east-1') @@ -113,7 +113,7 @@ before(:all) do Aws.config[:s3] = { stub_responses: { - get_object: { body: File.new('./spec/s3/state_response.json') } + get_object: { body: File.new('./spec/fixtures/mock_responses/s3/state_response.json') } } } @client = S3::Client.new(region: 'us-east-1') diff --git a/spec/fabricators/context_fabricator.rb b/spec/fabricators/context_fabricator.rb index 381434a..8aeab1c 100644 --- a/spec/fabricators/context_fabricator.rb +++ b/spec/fabricators/context_fabricator.rb @@ -1,9 +1,8 @@ -require_relative '../../ruby/lib/prometheus-unifio' require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/entities/context') Fabricator(:context) do name "example_context" - value [] + values [] after_build(&:valid?) end diff --git a/spec/fabricators/environment_fabricator.rb b/spec/fabricators/environment_fabricator.rb index d0e72fe..fef5e5d 100644 --- a/spec/fabricators/environment_fabricator.rb +++ b/spec/fabricators/environment_fabricator.rb @@ -1,4 +1,3 @@ -require_relative '../../ruby/lib/prometheus-unifio' require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/entities/environment') Fabricator(:environment) do diff --git a/spec/fabricators/input_fabricator.rb b/spec/fabricators/input_fabricator.rb index 8d3825f..f20502c 100644 --- a/spec/fabricators/input_fabricator.rb +++ b/spec/fabricators/input_fabricator.rb @@ -1,4 +1,3 @@ -require_relative '../../ruby/lib/prometheus-unifio' require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/entities/input') Fabricator(:input) do diff --git a/spec/fabricators/stack_fabricator.rb b/spec/fabricators/stack_fabricator.rb index 13ad122..98eebe7 100644 --- a/spec/fabricators/stack_fabricator.rb +++ b/spec/fabricators/stack_fabricator.rb @@ -1,4 +1,3 @@ -require_relative '../../ruby/lib/prometheus-unifio' require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/entities/stack') Fabricator(:stack) do diff --git a/spec/fabricators/state_store_fabricator.rb b/spec/fabricators/state_store_fabricator.rb index c0e20c1..b36c7ff 100644 --- a/spec/fabricators/state_store_fabricator.rb +++ b/spec/fabricators/state_store_fabricator.rb @@ -1,4 +1,3 @@ -require_relative '../../ruby/lib/prometheus-unifio' require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/entities/state_store') Fabricator(:state_store) do diff --git a/spec/fixtures/data/envs-bad-state.yaml b/spec/fixtures/data/envs-bad-state.yaml new file mode 100644 index 0000000..a0c0ac5 --- /dev/null +++ b/spec/fixtures/data/envs-bad-state.yaml @@ -0,0 +1,5 @@ +# Deployment environments + +environments: + example: + - 'bad_state' diff --git a/spec/data/envs.yaml b/spec/fixtures/data/envs.yaml similarity index 100% rename from spec/data/envs.yaml rename to spec/fixtures/data/envs.yaml diff --git a/spec/data/envs/example.yaml b/spec/fixtures/data/envs/example.yaml similarity index 100% rename from spec/data/envs/example.yaml rename to spec/fixtures/data/envs/example.yaml diff --git a/spec/data/stacks/example-artifact_test.yaml b/spec/fixtures/data/stacks/example-artifact_test.yaml similarity index 100% rename from spec/data/stacks/example-artifact_test.yaml rename to spec/fixtures/data/stacks/example-artifact_test.yaml diff --git a/spec/data/stacks/example-module_test.yaml b/spec/fixtures/data/stacks/example-module_test.yaml similarity index 100% rename from spec/data/stacks/example-module_test.yaml rename to spec/fixtures/data/stacks/example-module_test.yaml diff --git a/spec/data/stacks/example-myapp.yaml b/spec/fixtures/data/stacks/example-myapp.yaml similarity index 76% rename from spec/data/stacks/example-myapp.yaml rename to spec/fixtures/data/stacks/example-myapp.yaml index bae7623..5d6b4bb 100644 --- a/spec/data/stacks/example-myapp.yaml +++ b/spec/fixtures/data/stacks/example-myapp.yaml @@ -3,6 +3,10 @@ myapp::state: - atlas: name: 'example/myapp' + - s3: + bucket: 'some_bucket' + name: 'some_name' + region: 'some_region' # Arguments myapp::args: '-no-color' diff --git a/spec/atlas/artifact_response.json b/spec/fixtures/mock_responses/atlas/artifact_response.json similarity index 100% rename from spec/atlas/artifact_response.json rename to spec/fixtures/mock_responses/atlas/artifact_response.json diff --git a/spec/atlas/state_response.json b/spec/fixtures/mock_responses/atlas/state_response.json similarity index 100% rename from spec/atlas/state_response.json rename to spec/fixtures/mock_responses/atlas/state_response.json diff --git a/spec/consul/empty_response.json b/spec/fixtures/mock_responses/consul/empty_response.json similarity index 100% rename from spec/consul/empty_response.json rename to spec/fixtures/mock_responses/consul/empty_response.json diff --git a/spec/consul/key_response.json b/spec/fixtures/mock_responses/consul/key_response.json similarity index 100% rename from spec/consul/key_response.json rename to spec/fixtures/mock_responses/consul/key_response.json diff --git a/spec/consul/state_response.json b/spec/fixtures/mock_responses/consul/state_response.json similarity index 100% rename from spec/consul/state_response.json rename to spec/fixtures/mock_responses/consul/state_response.json diff --git a/spec/s3/key_response.json b/spec/fixtures/mock_responses/s3/key_response.json similarity index 100% rename from spec/s3/key_response.json rename to spec/fixtures/mock_responses/s3/key_response.json diff --git a/spec/s3/state_response.json b/spec/fixtures/mock_responses/s3/state_response.json similarity index 100% rename from spec/s3/state_response.json rename to spec/fixtures/mock_responses/s3/state_response.json diff --git a/spec/fixtures/packer/.gitkeep b/spec/fixtures/packer/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/spec/fixtures/prometheus_bad_state.yaml b/spec/fixtures/prometheus_bad_state.yaml new file mode 100644 index 0000000..0613b7f --- /dev/null +++ b/spec/fixtures/prometheus_bad_state.yaml @@ -0,0 +1,13 @@ +--- +:backends: + - yaml + +:logger: noop + +:merge_behavior: 'deeper' + +:hierarchy: + - "envs-bad-state" + +:yaml: + :datadir: spec/fixtures/data diff --git a/spec/prometheus_spec.yaml b/spec/fixtures/prometheus_spec.yaml similarity index 87% rename from spec/prometheus_spec.yaml rename to spec/fixtures/prometheus_spec.yaml index 651e7f7..ecbcfb5 100644 --- a/spec/prometheus_spec.yaml +++ b/spec/fixtures/prometheus_spec.yaml @@ -15,4 +15,4 @@ - "envs" :yaml: - :datadir: spec/data + :datadir: spec/fixtures/data diff --git a/spec/environment/myapp/main.tf b/spec/fixtures/terraform/myapp/main.tf similarity index 100% rename from spec/environment/myapp/main.tf rename to spec/fixtures/terraform/myapp/main.tf diff --git a/spec/environment/myapp2/main.tf b/spec/fixtures/terraform/myapp2/main.tf similarity index 100% rename from spec/environment/myapp2/main.tf rename to spec/fixtures/terraform/myapp2/main.tf diff --git a/spec/rake/environment_rake_spec.rb b/spec/rake/environment_rake_spec.rb deleted file mode 100644 index 1ab7ac6..0000000 --- a/spec/rake/environment_rake_spec.rb +++ /dev/null @@ -1,251 +0,0 @@ -require 'spec_helper' -require_relative '../shared_contexts/rake.rb' - -describe "example:myapp:clean" do - include_context "rake" - - it "cleans the workspace" do - expect_any_instance_of(Terraform::Stack).to receive(:clean) - subject.invoke - end -end - -describe "example:myapp:format" do - include_context "rake" - - it "formats the workspace" do - expect_any_instance_of(Terraform::Stack).to receive(:fmt) - subject.invoke - end -end - -describe "example:myapp:verify" do - include_context "rake" - - it "cleans the workspace" do - expect_any_instance_of(Terraform::Stack).to receive(:clean) - subject.invoke - end - - it "gets Terraform modules" do - expect_any_instance_of(Terraform::Stack).to receive(:get) - subject.invoke - end - - it "executes template validation" do - expect_any_instance_of(Terraform::Stack).to receive(:validate) - subject.invoke - end - - it "executes a plan" do - expect_any_instance_of(Terraform::Stack).to receive(:plan).with("-var label=\"test\" -input=false -module-depth=-1 -no-color") - subject.invoke - end -end - -describe "example:myapp:az0:plan" do - include_context "rake" - - it "cleans the workspace" do - expect_any_instance_of(Terraform::Stack).to receive(:clean) - subject.invoke - end - - it "configures remote state" do - expect_any_instance_of(Terraform::Stack).to receive(:remote_config) - subject.invoke - end - - it "gets Terraform modules" do - expect_any_instance_of(Terraform::Stack).to receive(:get) - subject.invoke - end - - it "executes a plan" do - expect_any_instance_of(Terraform::Stack).to receive(:plan).with("-var label=\"test\" -input=false -module-depth=-1 -no-color -target=module.az0") - subject.invoke - end -end - -describe "example:myapp:az0:plan_destroy" do - include_context "rake" - - it "cleans the workspace" do - expect_any_instance_of(Terraform::Stack).to receive(:clean) - subject.invoke - end - - it "gets Terraform modules" do - expect_any_instance_of(Terraform::Stack).to receive(:get) - subject.invoke - end - - it "executes a plan" do - expect_any_instance_of(Terraform::Stack).to receive(:plan).with("-var label=\"test\" -destroy -input=false -module-depth=-1 -no-color -target=module.az0") - subject.invoke - end -end - -describe "example:myapp:az0:apply" do - include_context "rake" - - it "cleans the workspace" do - expect_any_instance_of(Terraform::Stack).to receive(:clean) - subject.invoke - end - - it "configures remote state" do - expect_any_instance_of(Terraform::Stack).to receive(:remote_config) - subject.invoke - end - - it "gets Terraform modules" do - expect_any_instance_of(Terraform::Stack).to receive(:get) - subject.invoke - end - - it "executes a plan" do - expect_any_instance_of(Terraform::Stack).to receive(:plan).with("-var label=\"test\" -input=false -module-depth=-1 -no-color -target=module.az0") - subject.invoke - end - - it "executes an apply" do - expect_any_instance_of(Terraform::Stack).to receive(:apply).with("-var label=\"test\" -no-color -target=module.az0") - subject.invoke - end -end - -describe "example:myapp:az0:destroy" do - include_context "rake" - - it "cleans the workspace" do - expect_any_instance_of(Terraform::Stack).to receive(:clean) - subject.invoke - end - - it "configures remote state" do - expect_any_instance_of(Terraform::Stack).to receive(:remote_config) - subject.invoke - end - - it "gets Terraform modules" do - expect_any_instance_of(Terraform::Stack).to receive(:get) - subject.invoke - end - - it "executes a plan" do - expect_any_instance_of(Terraform::Stack).to receive(:plan).with("-var label=\"test\" -destroy -input=false -module-depth=-1 -no-color -target=module.az0") - subject.invoke - end - - it "executes a destroy" do - expect_any_instance_of(Terraform::Stack).to receive(:destroy).with("-var label=\"test\" -no-color -target=module.az0") - subject.invoke - end -end - -describe "example:myapp:az1:plan" do - include_context "rake" - - it "executes a plan" do - expect_any_instance_of(Terraform::Stack).to receive(:plan).with("-var label=\"test\" -input=false -module-depth=-1 -no-color -target=module.az1 -target=module.common.aws_eip.myapp") - subject.invoke - end -end - -describe "example:myapp:az1:plan_destroy" do - include_context "rake" - - it "executes a plan" do - expect_any_instance_of(Terraform::Stack).to receive(:plan).with("-var label=\"test\" -destroy -input=false -module-depth=-1 -no-color -target=module.az1 -target=module.common.aws_eip.myapp") - subject.invoke - end -end - -describe "example:myapp:az1:apply" do - include_context "rake" - - it "executes a plan" do - expect_any_instance_of(Terraform::Stack).to receive(:plan).with("-var label=\"test\" -input=false -module-depth=-1 -no-color -target=module.az1 -target=module.common.aws_eip.myapp") - subject.invoke - end - - it "executes an apply" do - expect_any_instance_of(Terraform::Stack).to receive(:apply).with("-var label=\"test\" -no-color -target=module.az1 -target=module.common.aws_eip.myapp") - subject.invoke - end -end - -describe "example:myapp:az1:destroy" do - include_context "rake" - - it "executes a plan" do - expect_any_instance_of(Terraform::Stack).to receive(:plan).with("-var label=\"test\" -destroy -input=false -module-depth=-1 -no-color -target=module.az1 -target=module.common.aws_eip.myapp") - subject.invoke - end - - it "executes a destroy" do - expect_any_instance_of(Terraform::Stack).to receive(:destroy).with("-var label=\"test\" -no-color -target=module.az1 -target=module.common.aws_eip.myapp") - subject.invoke - end -end - -describe "example:myapp:sync" do - include_context "rake" - - it "cleans the workspace" do - expect_any_instance_of(Terraform::Stack).to receive(:clean) - subject.invoke - end - - it "configures remote state" do - expect_any_instance_of(Terraform::Stack).to receive(:remote_config) - subject.invoke - end -end - -describe "example:module_test:plan" do - include_context "rake" - - it "executes a plan" do - expect_any_instance_of(Terraform::Stack).to receive(:plan).with("-input=false -module-depth=-1") - subject.invoke - end -end - -describe "example:module_test:plan_destroy" do - include_context "rake" - - it "executes a plan" do - expect_any_instance_of(Terraform::Stack).to receive(:plan).with("-destroy -input=false -module-depth=-1") - subject.invoke - end -end - -describe "example:module_test:apply" do - include_context "rake" - - it "executes a plan" do - expect_any_instance_of(Terraform::Stack).to receive(:plan).with("-input=false -module-depth=-1") - subject.invoke - end - - it "executes an apply" do - expect_any_instance_of(Terraform::Stack).to receive(:apply).with("") - subject.invoke - end -end - -describe "example:module_test:destroy" do - include_context "rake" - - it "executes a plan" do - expect_any_instance_of(Terraform::Stack).to receive(:plan).with("-destroy -input=false -module-depth=-1") - subject.invoke - end - - it "executes a destroy" do - expect_any_instance_of(Terraform::Stack).to receive(:destroy).with("") - subject.invoke - end -end diff --git a/spec/rake/environment_tasks_spec.rb b/spec/rake/environment_tasks_spec.rb new file mode 100644 index 0000000..bc6739e --- /dev/null +++ b/spec/rake/environment_tasks_spec.rb @@ -0,0 +1,365 @@ +require 'spec_helper' +require 'active_support/core_ext/kernel/reporting' + +require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/cli_wrappers/terraform_cli') +require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/cli_wrappers/popen_wrapper') +require_relative File.join(PrometheusUnifio::GEM_ROOT, 'environment_tasks') +require_relative '../shared_contexts/rake.rb' + +describe EnvironmentTasks do + let(:task_files) { "environment_tasks.rb" } + + before(:each) do + Kernel.silence_warnings { + PrometheusUnifio::TERRAFORM_VERSION = "0.7.0" + } + allow(PopenWrapper).to receive(:run).and_return(true) + # suppress FileUtils verbose + allow($stderr).to receive(:write) + end + + describe "example:myapp:clean" do + include_context "rake" + + it "cleans the workspace" do + expect(TerraformCli).to receive(:terraform_clean) + subject.invoke + end + end + + describe "example:myapp:format" do + include_context "rake" + + it "formats the workspace" do + expect(TerraformCli).to receive(:terraform_fmt) + subject.invoke + end + end + + describe "example:myapp:verify" do + include_context "rake" + + it "cleans the workspace" do + expect(TerraformCli).to receive(:terraform_clean) + subject.invoke + end + + it "gets Terraform modules" do + expect(TerraformCli).to receive(:terraform_get) + subject.invoke + end + + it "executes template validation" do + expect(TerraformCli).to receive(:terraform_validate) + subject.invoke + end + + it "executes a plan" do + expect(TerraformCli).to receive(:terraform_plan).with(anything, hash_including(args: [ + "-var 'label=\"test\"'", + "-input=false", + "-module-depth=-1", + "-no-color" + ])) + subject.invoke + end + end + + describe "example:myapp:az0:plan" do + include_context "rake" + + it "cleans the workspace" do + expect(TerraformCli).to receive(:terraform_clean) + subject.invoke + end + + it "configures remote state" do + expect(TerraformCli).to receive(:terraform_remote_config).at_most(:twice) + subject.invoke + end + + it "gets Terraform modules" do + expect(TerraformCli).to receive(:terraform_get) + subject.invoke + end + + it "executes a plan" do + expect(TerraformCli).to receive(:terraform_plan).with(anything, hash_including(args: [ + "-var 'label=\"test\"'", + "-input=false", + "-module-depth=-1", + "-no-color", + "-target=\"module.az0\"" + ])) + subject.invoke + end + end + + describe "example:myapp:az0:plan_destroy" do + include_context "rake" + + it "cleans the workspace" do + expect(TerraformCli).to receive(:terraform_clean) + subject.invoke + end + + it "gets Terraform modules" do + expect(TerraformCli).to receive(:terraform_get) + subject.invoke + end + + it "executes a plan" do + expect(TerraformCli).to receive(:terraform_plan).with(anything, hash_including(args: [ + "-var 'label=\"test\"'", + "-destroy", + "-input=false", + "-module-depth=-1", + "-no-color", + "-target=\"module.az0\"" + ])) + subject.invoke + end + end + + describe "example:myapp:az0:apply" do + include_context "rake" + + it "cleans the workspace" do + expect(TerraformCli).to receive(:terraform_clean) + subject.invoke + end + + it "configures remote state" do + expect(TerraformCli).to receive(:terraform_remote_config) + subject.invoke + end + + it "gets Terraform modules" do + expect(TerraformCli).to receive(:terraform_get) + subject.invoke + end + + it "executes a plan" do + expect(TerraformCli).to receive(:terraform_plan).with(anything, hash_including(args: array_including( + "-var 'label=\"test\"'", + "-input=false", + "-module-depth=-1", + "-no-color", + "-target=\"module.az0\"" + ))) + subject.invoke + end + + it "executes an apply" do + expect(TerraformCli).to receive(:terraform_apply).with(anything, hash_including(args: [ + "-var 'label=\"test\"'", + "-no-color", + "-target=\"module.az0\"" + ])) + subject.invoke + end + end + + describe "example:myapp:az0:destroy" do + include_context "rake" + + it "cleans the workspace" do + expect(TerraformCli).to receive(:terraform_clean) + subject.invoke + end + + it "configures remote state" do + expect(TerraformCli).to receive(:terraform_remote_config) + subject.invoke + end + + it "gets Terraform modules" do + expect(TerraformCli).to receive(:terraform_get) + subject.invoke + end + + it "executes a plan" do + expect(TerraformCli).to receive(:terraform_plan).with(anything, hash_including(args: array_including( + "-var 'label=\"test\"'", + "-destroy", + "-input=false", + "-module-depth=-1", + "-no-color", + "-target=\"module.az0\"" + ))) + subject.invoke + end + + it "executes a destroy" do + expect(TerraformCli).to receive(:terraform_destroy).with(anything, hash_including(args: [ + "-var 'label=\"test\"'", + "-no-color", + "-target=\"module.az0\"" + ])) + subject.invoke + end + end + + describe "example:myapp:az1:plan" do + include_context "rake" + + it "executes a plan" do + expect(TerraformCli).to receive(:terraform_plan).with(anything, hash_including(args: [ + "-var 'label=\"test\"'", + "-input=false", + "-module-depth=-1", + "-no-color", + "-target=\"module.az1\"", + "-target=\"module.common.aws_eip.myapp\"" + ])) + subject.invoke + end + end + + describe "example:myapp:az1:plan_destroy" do + include_context "rake" + + it "executes a plan" do + expect(TerraformCli).to receive(:terraform_plan).with(anything, hash_including(args: [ + "-var 'label=\"test\"'", + "-destroy", + "-input=false", + "-module-depth=-1", + "-no-color", + "-target=\"module.az1\"", + "-target=\"module.common.aws_eip.myapp\"" + ])) + subject.invoke + end + end + + describe "example:myapp:az1:apply" do + include_context "rake" + + it "executes a plan" do + expect(TerraformCli).to receive(:terraform_plan).with(anything, hash_including(args: array_including( + "-var 'label=\"test\"'", + "-input=false", + "-module-depth=-1", + "-no-color", + "-target=\"module.az1\"", + "-target=\"module.common.aws_eip.myapp\"" + ))) + subject.invoke + end + + it "executes an apply" do + expect(TerraformCli).to receive(:terraform_apply).with(anything, hash_including(args: [ + "-var 'label=\"test\"'", + "-no-color", + "-target=\"module.az1\"", + "-target=\"module.common.aws_eip.myapp\"" + ])) + subject.invoke + end + end + + describe "example:myapp:az1:destroy" do + include_context "rake" + + it "executes a plan" do + expect(TerraformCli).to receive(:terraform_plan).with(anything, hash_including(args: array_including( + "-var 'label=\"test\"'", + "-destroy", + "-input=false", + "-module-depth=-1", + "-no-color", + "-target=\"module.az1\"", + "-target=\"module.common.aws_eip.myapp\"" + ))) + subject.invoke + end + + it "executes a destroy" do + expect(TerraformCli).to receive(:terraform_destroy).with(anything, hash_including(args: [ + "-var 'label=\"test\"'", + "-no-color", + "-target=\"module.az1\"", + "-target=\"module.common.aws_eip.myapp\"" + ])) + subject.invoke + end + end + + describe "example:myapp:sync" do + include_context "rake" + + it "cleans the workspace" do + expect(TerraformCli).to receive(:terraform_clean) + subject.invoke + end + + it "should sync state from the primary remote state" do + expect(TerraformCli).to receive(:terraform_remote_config).with(args: /-backend=Atlas/) + expect(TerraformCli).to receive(:terraform_remote_config).with(args: '-disable') + expect(TerraformCli).to receive(:terraform_remote_config).with(args: /-backend=s3/) + expect(TerraformCli).to receive(:terraform_remote_push) + subject.invoke + end + end + + describe "example:module_test:plan" do + include_context "rake" + + it "executes a plan" do + expect(TerraformCli).to receive(:terraform_plan).with(anything, hash_including(args: [ + "-input=false", + "-module-depth=-1" + ])) + subject.invoke + end + end + + describe "example:module_test:plan_destroy" do + include_context "rake" + + it "executes a plan" do + expect(TerraformCli).to receive(:terraform_plan).with(anything, hash_including(args: [ + "-destroy", + "-input=false", + "-module-depth=-1" + ])) + subject.invoke + end + end + + describe "example:module_test:apply" do + include_context "rake" + + it "executes a plan" do + expect(TerraformCli).to receive(:terraform_plan).with(anything, hash_including(args: [ + "-input=false", + "-module-depth=-1" + ])) + subject.invoke + end + + it "executes an apply" do + expect(TerraformCli).to receive(:terraform_apply).with(anything, hash_including(args: [])) + subject.invoke + end + end + + describe "example:module_test:destroy" do + include_context "rake" + + it "executes a plan" do + expect(TerraformCli).to receive(:terraform_plan).with(anything, hash_including(args: [ + "-destroy", + "-input=false", + "-module-depth=-1" + ])) + subject.invoke + end + + it "executes a destroy" do + expect(TerraformCli).to receive(:terraform_destroy).with(anything, hash_including(args: [])) + subject.invoke + end + end +end diff --git a/spec/rake/spec_tasks_spec.rb b/spec/rake/spec_tasks_spec.rb new file mode 100644 index 0000000..ee483bd --- /dev/null +++ b/spec/rake/spec_tasks_spec.rb @@ -0,0 +1,11 @@ +require 'spec_helper' +require 'active_support/core_ext/kernel/reporting' + +#require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/cli_wrappers/terraform_cli') +#require_relative File.join(PrometheusUnifio::GEM_ROOT, 'core/cli_wrappers/popen_wrapper') +require_relative File.join(PrometheusUnifio::GEM_ROOT, 'spec_tasks') +require_relative '../shared_contexts/rake.rb' + +describe SpecTasks do + pending +end diff --git a/spec/shared_contexts/rake.rb b/spec/shared_contexts/rake.rb index 3786006..a1215e6 100644 --- a/spec/shared_contexts/rake.rb +++ b/spec/shared_contexts/rake.rb @@ -1,19 +1,23 @@ require "rake" -require 'pathname' +require_relative '../../ruby/lib/prometheus-unifio' shared_context "rake" do let(:rake) { Rake::Application.new } - let(:task_name) { self.class.top_level_description } - let(:task_path) { "lib/prometheus-unifio/rake/environment" } - let(:task_root) { Pathname.new("#{Dir.pwd}/ruby") } + let(:task_name) { self.class.description } + let(:task_root) { PrometheusUnifio::GEM_ROOT } + subject { rake[task_name] } - def loaded_files_excluding_current_rake_file - $".reject {|file| file == task_root.join("#{task_path}.rake").to_s } + def file_globs_to_paths(globs) + [*globs].map { |glob| Dir.glob(File.absolute_path(glob, task_root)) }.flatten end before do Rake.application = rake - Rake.application.rake_require(task_path, [task_root.to_s], loaded_files_excluding_current_rake_file) + @task_files = task_files || "rake/*.{rb,rake}" + task_paths = file_globs_to_paths(@task_files) + + task_paths.each { |path| Rake.application.add_import(path) } + Rake.application.load_imports end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 4f39711..6f53d72 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -2,8 +2,11 @@ require 'dotenv' require 'fabrication' +require 'webmock/rspec' +require 'simplecov' -Dotenv.load -Fabrication.manager.load_definitions - +Dotenv.load(*%w(.env .env.test)) +SimpleCov.start require_relative '../ruby/lib/prometheus-unifio' + +Fabrication.manager.load_definitions diff --git a/spec/terraform/terraform_spec.rb b/spec/terraform/terraform_spec.rb deleted file mode 100644 index 39add4e..0000000 --- a/spec/terraform/terraform_spec.rb +++ /dev/null @@ -1,122 +0,0 @@ -require 'tmpdir' -require_relative '../../ruby/lib/tools/terraform.rb' - -include Terraform - -describe Terraform::Stack do - - before(:each) do - dir = Dir.mktmpdir - @parent_dir = File.dirname(dir) - @stack_dir = File.basename(dir) - @stack = Terraform::Stack.new(@stack_dir, dir: @parent_dir) - end - - it "returns a stack given a directory that exists" do - expect(@stack).to be_a(Terraform::Stack) - end - - it "raises an exception if the given directory does not exist" do - expect { Terraform::Stack.new('doesnotexist') }.to raise_error(RuntimeError) - end - - it "executes terraform remote config" do - expect(@stack).to receive(:run_rake_cmd).with('remote', 'config') - @stack.remote_config - end - - it "executes terraform remote pull" do - expect(@stack).to receive(:run_rake_cmd).with('remote', 'pull') - @stack.remote_pull - end - - it "executes terraform remote push" do - expect(@stack).to receive(:run_rake_cmd).with('remote', 'push') - @stack.remote_push - end - - it "executes terraform validate" do - expect(@stack).to receive(:run_rake_cmd).with('validate', '') - @stack.validate - end - - it "executes terraform plan" do - expect(@stack).to receive(:run_rake_cmd).with('plan', '') - @stack.plan - end - - it "executes terraform apply" do - expect(@stack).to receive(:run_rake_cmd).with('apply', '') - @stack.apply - end - - it "executes terraform destroy" do - expect(@stack).to receive(:run_rake_cmd).with('destroy', '') - @stack.destroy - end - - it "executes terraform fmt" do - expect(@stack).to receive(:run_rake_cmd).with('fmt', '') - @stack.fmt - end - - it "executes terraform commands with custom settings" do - @cmd_test = Terraform::Stack.new(@stack_dir, dir: @parent_dir, env: "TEST=thisisatest", img: "", cmd: "/usr/local/bin/terraform", stub: "false") - cmd = "TEST=thisisatest /usr/local/bin/terraform plan" - expect(Rake).to receive(:sh).with(cmd).and_return(true) - @cmd_test.plan - end - - it "executes terraform commands within a container" do - @cmd_test = Terraform::Stack.new(@stack_dir, dir: @parent_dir, env: "", img: "unifio/terraform:latest", cmd: "docker run --rm", stub: "false") - cmd = "docker run --rm -v #{@parent_dir}:/data -w /data/#{@stack_dir} unifio/terraform:latest plan" - expect(Rake).to receive(:sh).with(cmd).and_return(true) - @cmd_test.plan - end - - it "cleans up existing state data from the given stack directory" do - @cmd_test = Terraform::Stack.new(@stack_dir, dir: @parent_dir, env: "", img: "", cmd: "", stub: "false") - cmd = "/bin/sh -c \"rm -fr .terraform *.tfstate*\"" - expect(Rake).to receive(:sh).with(cmd).and_return(true) - @cmd_test.clean - end - - it "cleans up existing state data from the given stack directory within a container" do - @cmd_test = Terraform::Stack.new(@stack_dir, dir: @parent_dir, env: "", img: "unifio/terraform:latest", cmd: "docker run --rm", stub: "false") - cmd = "docker run --rm -v #{@parent_dir}:/data -w /data/#{@stack_dir} --entrypoint=\"/bin/sh\" unifio/terraform:latest -c \"rm -fr .terraform *.tfstate*\"" - expect(Rake).to receive(:sh).with(cmd).and_return(true) - @cmd_test.clean - end - - it "processes Terraform inputs" do - vars = { - 'environment' => 'testing' - } - - inputs = @stack.parse_vars(vars) - expect(inputs).to eql('-var environment="testing"') - end - - it "processes empty inputs" do - vars = {} - - inputs = @stack.parse_vars(vars) - expect(inputs).to eql('') - end - - it "processes Terraform targets" do - targets = [ - 'module.az0' - ] - - inputs = @stack.parse_targets(targets) - expect(inputs).to eql('-target=module.az0') - end - - it "processes empty targets" do - targets = [] - - inputs = @stack.parse_targets(targets) - expect(inputs).to eql('') - end -end