diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 2240a97..f1f88cc 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -1,281 +1,282 @@ # Contribution guidelines ## Table of contents * [Contributing](#contributing) * [Writing proper commits - short version](#writing-proper-commits-short-version) * [Writing proper commits - long version](#writing-proper-commits-long-version) * [Dependencies](#dependencies) * [Note for OS X users](#note-for-os-x-users) * [The test matrix](#the-test-matrix) * [Syntax and style](#syntax-and-style) * [Running the unit tests](#running-the-unit-tests) * [Unit tests in docker](#unit-tests-in-docker) * [Integration tests](#integration-tests) This module has grown over time based on a range of contributions from people using it. If you follow these contributing guidelines your patch will likely make it into a release a little more quickly. ## Contributing Please note that this project is released with a Contributor Code of Conduct. By participating in this project you agree to abide by its terms. [Contributor Code of Conduct](https://voxpupuli.org/coc/). * Fork the repo. * Create a separate branch for your change. * We only take pull requests with passing tests, and documentation. [travis-ci](http://travis-ci.org) runs the tests for us. You can also execute them locally. This is explained [in a later section](#the-test-matrix). * Checkout [our docs](https://voxpupuli.org/docs/reviewing_pr/) we use to review a module and the [official styleguide](https://puppet.com/docs/puppet/6.0/style_guide.html). They provide some guidance for new code that might help you before you submit a pull request. * Add a test for your change. Only refactoring and documentation changes require no new tests. If you are adding functionality or fixing a bug, please add a test. * Squash your commits down into logical components. Make sure to rebase against our current master. * Push the branch to your fork and submit a pull request. Please be prepared to repeat some of these steps as our contributors review your code. +Also consider sending in your profile code that calls this component module as an acceptance test or provide it via an issue. This helps reviewers a lot to test your use case and prevents future regressions! + ## Writing proper commits - short version * Make commits of logical units. * Check for unnecessary whitespace with "git diff --check" before committing. * Commit using Unix line endings (check the settings around "crlf" in git-config(1)). * Do not check in commented out code or unneeded files. * The first line of the commit message should be a short description (50 characters is the soft limit, excluding ticket number(s)), and should skip the full stop. * Associate the issue in the message. The first line should include the issue number in the form "(#XXXX) Rest of message". * The body should provide a meaningful commit message, which: *uses the imperative, present tense: `change`, not `changed` or `changes`. * includes motivation for the change, and contrasts its implementation with the previous behavior. * Make sure that you have tests for the bug you are fixing, or feature you are adding. * Make sure the test suites passes after your commit: * When introducing a new feature, make sure it is properly documented in the README.md ## Writing proper commits - long version 1. Make separate commits for logically separate changes. Please break your commits down into logically consistent units which include new or changed tests relevant to the rest of the change. The goal of doing this is to make the diff easier to read for whoever is reviewing your code. In general, the easier your diff is to read, the more likely someone will be happy to review it and get it into the code base. If you are going to refactor a piece of code, please do so as a separate commit from your feature or bug fix changes. We also really appreciate changes that include tests to make sure the bug is not re-introduced, and that the feature is not accidentally broken. Describe the technical detail of the change(s). If your description starts to get too long, that is a good sign that you probably need to split up your commit into more finely grained pieces. Commits which plainly describe the things which help reviewers check the patch and future developers understand the code are much more likely to be merged in with a minimum of bike-shedding or requested changes. Ideally, the commit message would include information, and be in a form suitable for inclusion in the release notes for the version of Puppet that includes them. Please also check that you are not introducing any trailing whitespace or other "whitespace errors". You can do this by running "git diff --check" on your changes before you commit. 2. Sending your patches To submit your changes via a GitHub pull request, we _highly_ recommend that you have them on a topic branch, instead of directly on `master`. It makes things much easier to keep track of, especially if you decide to work on another thing before your first change is merged in. GitHub has some pretty good [general documentation](http://help.github.com/) on using their site. They also have documentation on [creating pull requests](http://help.github.com/send-pull-requests/). In general, after pushing your topic branch up to your repository on GitHub, you can switch to the branch in the GitHub UI and click "Pull Request" towards the top of the page in order to open a pull request. 3. Update the related GitHub issue. If there is a GitHub issue associated with the change you submitted, then you should update the ticket to include the location of your branch, along with any other commentary you may wish to make. ## Dependencies The testing and development tools have a bunch of dependencies, all managed by [bundler](http://bundler.io/) according to the [Puppet support matrix](http://docs.puppetlabs.com/guides/platforms.html#ruby-versions). By default the tests use a baseline version of Puppet. If you have Ruby 2.x or want a specific version of Puppet, you must set an environment variable such as: ```sh export PUPPET_VERSION="~> 5.5.6" ``` You can install all needed gems for spec tests into the modules directory by running: ```sh bundle install --path .vendor/ --without development system_tests release --jobs "$(nproc)" ``` If you also want to run acceptance tests: ```sh bundle install --path .vendor/ --with system_tests --without development release --jobs "$(nproc)" ``` Our all in one solution if you don't know if you need to install or update gems: ```sh bundle install --path .vendor/ --with system_tests --without development release --jobs "$(nproc)"; bundle update; bundle clean ``` As an alternative to the `--jobs "$(nproc)` parameter, you can set an environment variable: ```sh BUNDLE_JOBS="$(nproc)" ``` ### Note for OS X users `nproc` isn't a valid command under OS x. As an alternative, you can do: ```sh --jobs "$(sysctl -n hw.ncpu)" ``` ## The test matrix ### Syntax and style The test suite will run [Puppet Lint](http://puppet-lint.com/) and [Puppet Syntax](https://github.com/gds-operations/puppet-syntax) to check various syntax and style things. You can run these locally with: ```sh bundle exec rake lint bundle exec rake validate ``` It will also run some [Rubocop](http://batsov.com/rubocop/) tests against it. You can run those locally ahead of time with: ```sh bundle exec rake rubocop ``` ### Running the unit tests The unit test suite covers most of the code, as mentioned above please add tests if you're adding new functionality. If you've not used [rspec-puppet](http://rspec-puppet.com/) before then feel free to ask about how best to test your new feature. To run the linter, the syntax checker and the unit tests: ```sh bundle exec rake test ``` To run your all the unit tests ```sh bundle exec rake spec ``` To run a specific spec test set the `SPEC` variable: ```sh bundle exec rake spec SPEC=spec/foo_spec.rb ``` #### Unit tests in docker Some people don't want to run the dependencies locally or don't want to install ruby. We ship a Dockerfile that enables you to run all unit tests and linting. You only need to run: ```sh docker build . ``` Please ensure that a docker daemon is running and that your user has the permission to talk to it. You can specify a remote docker host by setting the `DOCKER_HOST` environment variable. it will copy the content of the module into the docker image. So it will not work if a Gemfile.lock exists. ### Integration tests The unit tests just check the code runs, not that it does exactly what we want on a real machine. For that we're using [beaker](https://github.com/puppetlabs/beaker). This fires up a new virtual machine (using vagrant) and runs a series of simple tests against it after applying the module. You can run this with: ```sh -bundle exec rake acceptance +bundle exec rake beaker ``` This will run the tests on the module's default nodeset. You can override the nodeset used, e.g., ```sh -BEAKER_set=centos-7-x64 bundle exec rake acceptance +BEAKER_set=centos-7-x64 bundle exec rake beaker ``` There are default rake tasks for the various acceptance test modules, e.g., ```sh bundle exec rake beaker:centos-7-x64 bundle exec rake beaker:ssh:centos-7-x64 ``` If you don't want to have to recreate the virtual machine every time you can use `BEAKER_destroy=no` and `BEAKER_provision=no`. On the first run you will at least need `BEAKER_provision` set to yes (the default). The Vagrantfile for the created virtual machines will be in `.vagrant/beaker_vagrant_files`. Beaker also supports docker containers. We also use that in our automated CI pipeline at [travis-ci](http://travis-ci.org). To use that instead of Vagrant: ```sh PUPPET_INSTALL_TYPE=agent BEAKER_IS_PE=no BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_debug=true BEAKER_setfile=debian10-64{hypervisor=docker} BEAKER_destroy=yes bundle exec rake beaker ``` You can replace the string `debian10` with any common operating system. The following strings are known to work: * ubuntu1604 * ubuntu1804 -* debian8 +* ubuntu2004 * debian9 * debian10 -* centos6 * centos7 * centos8 The easiest way to debug in a docker container is to open a shell: ```sh docker exec -it -u root ${container_id_or_name} bash ``` The source of this file is in our [modulesync_config](https://github.com/voxpupuli/modulesync_config/blob/master/moduleroot/.github/CONTRIBUTING.md.erb) repository. diff --git a/.github/SECURITY.md b/.github/SECURITY.md new file mode 100644 index 0000000..cacadf2 --- /dev/null +++ b/.github/SECURITY.md @@ -0,0 +1,3 @@ +# Vox Pupuli Security Policy + +Our vulnerabilities reporting process is at https://voxpupuli.org/security/ diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..b4f47e8 --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,75 @@ +name: CI + +on: pull_request + +jobs: + setup_matrix: + name: 'Setup Test Matrix' + runs-on: ubuntu-latest + outputs: + beaker_setfiles: ${{ steps.get-outputs.outputs.beaker_setfiles }} + puppet_major_versions: ${{ steps.get-outputs.outputs.puppet_major_versions }} + puppet_unit_test_matrix: ${{ steps.get-outputs.outputs.puppet_unit_test_matrix }} + env: + BUNDLE_WITHOUT: development:test:release + steps: + - uses: actions/checkout@v2 + - name: Setup ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.7' + bundler-cache: true + - name: Run rake validate + run: bundle exec rake validate + - name: Setup Test Matrix + id: get-outputs + run: bundle exec metadata2gha --use-fqdn --pidfile-workaround false + + unit: + needs: setup_matrix + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + include: ${{fromJson(needs.setup_matrix.outputs.puppet_unit_test_matrix)}} + env: + BUNDLE_WITHOUT: development:system_tests:release + PUPPET_VERSION: "~> ${{ matrix.puppet }}.0" + name: Puppet ${{ matrix.puppet }} (Ruby ${{ matrix.ruby }}) + steps: + - uses: actions/checkout@v2 + - name: Setup ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: ${{ matrix.ruby }} + bundler-cache: true + - name: Run tests + run: bundle exec rake + + acceptance: + needs: setup_matrix + runs-on: ubuntu-latest + env: + BUNDLE_WITHOUT: development:test:release + strategy: + fail-fast: false + matrix: + setfile: ${{fromJson(needs.setup_matrix.outputs.beaker_setfiles)}} + puppet: ${{fromJson(needs.setup_matrix.outputs.puppet_major_versions)}} + name: ${{ matrix.puppet.name }} - ${{ matrix.setfile.name }} + steps: + - name: Enable IPv6 on docker + run: | + echo '{"ipv6":true,"fixed-cidr-v6":"2001:db8:1::/64"}' | sudo tee /etc/docker/daemon.json + sudo service docker restart + - uses: actions/checkout@v2 + - name: Setup ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.7' + bundler-cache: true + - name: Run tests + run: bundle exec rake beaker + env: + BEAKER_PUPPET_COLLECTION: ${{ matrix.puppet.collection }} + BEAKER_setfile: ${{ matrix.setfile.value }} diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml new file mode 100644 index 0000000..68b8528 --- /dev/null +++ b/.github/workflows/release.yml @@ -0,0 +1,29 @@ +name: Release + +on: + push: + tags: + - '*' + +env: + BUNDLE_WITHOUT: development:test:system_tests + +jobs: + deploy: + name: 'deploy to forge' + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@v2 + - name: Setup Ruby + uses: ruby/setup-ruby@v1 + with: + ruby-version: '2.7' + bundler-cache: true + - name: Build and Deploy + env: + # Configure secrets here: + # https://docs.github.com/en/free-pro-team@latest/actions/reference/encrypted-secrets + BLACKSMITH_FORGE_USERNAME: '${{ secrets.PUPPET_FORGE_USERNAME }}' + BLACKSMITH_FORGE_API_KEY: '${{ secrets.PUPPET_FORGE_API_KEY }}' + run: bundle exec rake module:push diff --git a/.msync.yml b/.msync.yml index 8864fc0..a0770a8 100644 --- a/.msync.yml +++ b/.msync.yml @@ -1 +1,2 @@ -modulesync_config_version: '2.12.0' +--- +modulesync_config_version: '4.0.0' diff --git a/.overcommit.yml b/.overcommit.yml index 1b03fad..0af0fdc 100644 --- a/.overcommit.yml +++ b/.overcommit.yml @@ -1,64 +1,64 @@ # Managed by https://github.com/voxpupuli/modulesync_configs # # Hooks are only enabled if you take action. # # To enable the hooks run: # # ``` # bundle exec overcommit --install # # ensure .overcommit.yml does not harm to you and then # bundle exec overcommit --sign # ``` # # (it will manage the .git/hooks directory): # # Examples howto skip a test for a commit or push: # # ``` # SKIP=RuboCop git commit # SKIP=PuppetLint git commit # SKIP=RakeTask git push # ``` # # Don't invoke overcommit at all: # # ``` # OVERCOMMIT_DISABLE=1 git commit # ``` # # Read more about overcommit: https://github.com/brigade/overcommit # # To manage this config yourself in your module add # # ``` # .overcommit.yml: # unmanaged: true # ``` # # to your modules .sync.yml config --- PreCommit: RuboCop: enabled: true description: 'Runs rubocop on modified files only' command: ['bundle', 'exec', 'rubocop'] PuppetLint: enabled: true description: 'Runs puppet-lint on modified files only' command: ['bundle', 'exec', 'puppet-lint'] YamlSyntax: enabled: true JsonSyntax: enabled: true TrailingWhitespace: enabled: true PrePush: RakeTarget: enabled: true description: 'Run rake targets' targets: - 'validate' - 'test' - 'rubocop' - command: [ 'bundle', 'exec', 'rake' ] + command: ['bundle', 'exec', 'rake'] diff --git a/.pmtignore b/.pmtignore index 4e6d54b..33a8c65 100644 --- a/.pmtignore +++ b/.pmtignore @@ -1,21 +1,34 @@ docs/ pkg/ +Gemfile Gemfile.lock Gemfile.local vendor/ .vendor/ -spec/fixtures/manifests/ -spec/fixtures/modules/ +spec/ +Rakefile .vagrant/ .bundle/ .ruby-version coverage/ log/ .idea/ .dependencies/ +.github/ .librarian/ Puppetfile.lock *.iml +.editorconfig +.fixtures.yml +.gitignore +.msync.yml +.overcommit.yml +.pmtignore +.rspec +.rspec_parallel +.rubocop.yml +.sync.yml .*.sw? .yardoc/ +.yardopts Dockerfile diff --git a/.rubocop.yml b/.rubocop.yml index 0703f3b..198a359 100644 --- a/.rubocop.yml +++ b/.rubocop.yml @@ -1,550 +1,3 @@ -require: rubocop-rspec -AllCops: -# Puppet Server 5 defaults to jruby 1.7 so TargetRubyVersion must stay at 1.9 until we drop support for puppet 5 - TargetRubyVersion: 1.9 - Include: - - ./**/*.rb - Exclude: - - files/**/* - - vendor/**/* - - .vendor/**/* - - pkg/**/* - - spec/fixtures/**/* - - Gemfile - - Rakefile - - Guardfile - - Vagrantfile -Lint/ConditionPosition: - Enabled: True - -Lint/ElseLayout: - Enabled: True - -Lint/UnreachableCode: - Enabled: True - -Lint/UselessComparison: - Enabled: True - -Lint/EnsureReturn: - Enabled: True - -Lint/HandleExceptions: - Enabled: True - -Lint/LiteralInCondition: - Enabled: True - -Lint/ShadowingOuterLocalVariable: - Enabled: True - -Lint/LiteralInInterpolation: - Enabled: True - -Style/HashSyntax: - Enabled: True - -Style/RedundantReturn: - Enabled: True - -Layout/EndOfLine: - Enabled: False - -Lint/AmbiguousOperator: - Enabled: True - -Lint/AssignmentInCondition: - Enabled: True - -Layout/SpaceBeforeComment: - Enabled: True - -Style/AndOr: - Enabled: True - -Style/RedundantSelf: - Enabled: True - -Metrics/BlockLength: - Enabled: False - -# Method length is not necessarily an indicator of code quality -Metrics/MethodLength: - Enabled: False - -# Module length is not necessarily an indicator of code quality -Metrics/ModuleLength: - Enabled: False - -Style/WhileUntilModifier: - Enabled: True - -Lint/AmbiguousRegexpLiteral: - Enabled: True - -Security/Eval: - Enabled: True - -Lint/BlockAlignment: - Enabled: True - -Lint/DefEndAlignment: - Enabled: True - -Lint/EndAlignment: - Enabled: True - -Lint/DeprecatedClassMethods: - Enabled: True - -Lint/Loop: - Enabled: True - -Lint/ParenthesesAsGroupedExpression: - Enabled: True - -Lint/RescueException: - Enabled: True - -Lint/StringConversionInInterpolation: - Enabled: True - -Lint/UnusedBlockArgument: - Enabled: True - -Lint/UnusedMethodArgument: - Enabled: True - -Lint/UselessAccessModifier: - Enabled: True - -Lint/UselessAssignment: - Enabled: True - -Lint/Void: - Enabled: True - -Layout/AccessModifierIndentation: - Enabled: True - -Style/AccessorMethodName: - Enabled: True - -Style/Alias: - Enabled: True - -Layout/AlignArray: - Enabled: True - -Layout/AlignHash: - Enabled: True - -Layout/AlignParameters: - Enabled: True - -Metrics/BlockNesting: - Enabled: True - -Style/AsciiComments: - Enabled: True - -Style/Attr: - Enabled: True - -Style/BracesAroundHashParameters: - Enabled: True - -Style/CaseEquality: - Enabled: True - -Layout/CaseIndentation: - Enabled: True - -Style/CharacterLiteral: - Enabled: True - -Style/ClassAndModuleCamelCase: - Enabled: True - -Style/ClassAndModuleChildren: - Enabled: False - -Style/ClassCheck: - Enabled: True - -# Class length is not necessarily an indicator of code quality -Metrics/ClassLength: - Enabled: False - -Style/ClassMethods: - Enabled: True - -Style/ClassVars: - Enabled: True - -Style/WhenThen: - Enabled: True - -Style/WordArray: - Enabled: True - -Style/UnneededPercentQ: - Enabled: True - -Layout/Tab: - Enabled: True - -Layout/SpaceBeforeSemicolon: - Enabled: True - -Layout/TrailingBlankLines: - Enabled: True - -Layout/SpaceInsideBlockBraces: - Enabled: True - -Layout/SpaceInsideBrackets: - Enabled: True - -Layout/SpaceInsideHashLiteralBraces: - Enabled: True - -Layout/SpaceInsideParens: - Enabled: True - -Layout/LeadingCommentSpace: - Enabled: True - -Layout/SpaceBeforeFirstArg: - Enabled: True - -Layout/SpaceAfterColon: - Enabled: True - -Layout/SpaceAfterComma: - Enabled: True - -Layout/SpaceAfterMethodName: - Enabled: True - -Layout/SpaceAfterNot: - Enabled: True - -Layout/SpaceAfterSemicolon: - Enabled: True - -Layout/SpaceAroundEqualsInParameterDefault: - Enabled: True - -Layout/SpaceAroundOperators: - Enabled: True - -Layout/SpaceBeforeBlockBraces: - Enabled: True - -Layout/SpaceBeforeComma: - Enabled: True - -Style/CollectionMethods: - Enabled: True - -Layout/CommentIndentation: - Enabled: True - -Style/ColonMethodCall: - Enabled: True - -Style/CommentAnnotation: - Enabled: True - -# 'Complexity' is very relative -Metrics/CyclomaticComplexity: - Enabled: False - -Style/ConstantName: - Enabled: True - -Style/Documentation: - Enabled: False - -Style/DefWithParentheses: - Enabled: True - -Style/PreferredHashMethods: - Enabled: True - -Layout/DotPosition: - EnforcedStyle: trailing - -Style/DoubleNegation: - Enabled: True - -Style/EachWithObject: - Enabled: True - -Layout/EmptyLineBetweenDefs: - Enabled: True - -Layout/IndentArray: - Enabled: True - -Layout/IndentHash: - Enabled: True - -Layout/IndentationConsistency: - Enabled: True - -Layout/IndentationWidth: - Enabled: True - -Layout/EmptyLines: - Enabled: True - -Layout/EmptyLinesAroundAccessModifier: - Enabled: True - -Style/EmptyLiteral: - Enabled: True - -# Configuration parameters: AllowURI, URISchemes. -Metrics/LineLength: - Enabled: False - -Style/MethodCallWithoutArgsParentheses: - Enabled: True - -Style/MethodDefParentheses: - Enabled: True - -Style/LineEndConcatenation: - Enabled: True - -Layout/TrailingWhitespace: - Enabled: True - -Style/StringLiterals: - Enabled: True - -Style/TrailingCommaInArguments: - Enabled: True - -Style/TrailingCommaInLiteral: - Enabled: True - -Style/GlobalVars: - Enabled: True - -Style/GuardClause: - Enabled: True - -Style/IfUnlessModifier: - Enabled: True - -Style/MultilineIfThen: - Enabled: True - -Style/NegatedIf: - Enabled: True - -Style/NegatedWhile: - Enabled: True - -Style/Next: - Enabled: True - -Style/SingleLineBlockParams: - Enabled: True - -Style/SingleLineMethods: - Enabled: True - -Style/SpecialGlobalVars: - Enabled: True - -Style/TrivialAccessors: - Enabled: True - -Style/UnlessElse: - Enabled: True - -Style/VariableInterpolation: - Enabled: True - -Style/VariableName: - Enabled: True - -Style/WhileUntilDo: - Enabled: True - -Style/EvenOdd: - Enabled: True - -Style/FileName: - Enabled: True - -Style/For: - Enabled: True - -Style/Lambda: - Enabled: True - -Style/MethodName: - Enabled: True - -Style/MultilineTernaryOperator: - Enabled: True - -Style/NestedTernaryOperator: - Enabled: True - -Style/NilComparison: - Enabled: True - -Style/FormatString: - Enabled: True - -Style/MultilineBlockChain: - Enabled: True - -Style/Semicolon: - Enabled: True - -Style/SignalException: - Enabled: True - -Style/NonNilCheck: - Enabled: True - -Style/Not: - Enabled: True - -Style/NumericLiterals: - Enabled: True - -Style/OneLineConditional: - Enabled: True - -Style/OpMethod: - Enabled: True - -Style/ParenthesesAroundCondition: - Enabled: True - -Style/PercentLiteralDelimiters: - Enabled: True - -Style/PerlBackrefs: - Enabled: True - -Style/PredicateName: - Enabled: True - -Style/RedundantException: - Enabled: True - -Style/SelfAssignment: - Enabled: True - -Style/Proc: - Enabled: True - -Style/RaiseArgs: - Enabled: True - -Style/RedundantBegin: - Enabled: True - -Style/RescueModifier: - Enabled: True - -# based on https://github.com/voxpupuli/modulesync_config/issues/168 -Style/RegexpLiteral: - EnforcedStyle: percent_r - Enabled: True - -Lint/UnderscorePrefixedVariableName: - Enabled: True - -Metrics/ParameterLists: - Enabled: False - -Lint/RequireParentheses: - Enabled: True - -Style/ModuleFunction: - Enabled: True - -Lint/Debugger: - Enabled: True - -Style/IfWithSemicolon: - Enabled: True - -Style/Encoding: - Enabled: True - -Style/BlockDelimiters: - Enabled: True - -Layout/MultilineBlockLayout: - Enabled: True - -# 'Complexity' is very relative -Metrics/AbcSize: - Enabled: False - -# 'Complexity' is very relative -Metrics/PerceivedComplexity: - Enabled: False - -Lint/UselessAssignment: - Enabled: True - -Layout/ClosingParenthesisIndentation: - Enabled: True - -# RSpec - -RSpec/BeforeAfterAll: - Exclude: - - spec/acceptance/**/* - -# We don't use rspec in this way -RSpec/DescribeClass: - Enabled: False - -# Example length is not necessarily an indicator of code quality -RSpec/ExampleLength: - Enabled: False - -RSpec/NamedSubject: - Enabled: False - -# disabled for now since they cause a lot of issues -# these issues aren't easy to fix -RSpec/RepeatedDescription: - Enabled: False - -RSpec/NestedGroups: - Enabled: False - -# this is broken on ruby1.9 -Layout/IndentHeredoc: - Enabled: False - -# disable Yaml safe_load. This is needed to support ruby2.0.0 development envs -Security/YAMLLoad: - Enabled: false - -# This affects hiera interpolation, as well as some configs that we push. -Style/FormatStringToken: - Enabled: false - -# This is useful, but sometimes a little too picky about where unit tests files -# are located. -RSpec/FilePath: - Enabled: false - -# silence it to support older jruby (<= v1.9) which can not handle e.g. %i[] -Style/WordArray: - Enabled: false +--- +inherit_gem: + voxpupuli-test: rubocop.yml diff --git a/.sync.yml b/.sync.yml index 7b7ea57..9ad8634 100644 --- a/.sync.yml +++ b/.sync.yml @@ -1,15 +1,7 @@ --- -.travis.yml: - secure: "C+dXd27/doW1JnKqv+UDHL6/HBI5Te+xGsaoPkqhpO4iXfMxJrTi6sBWaEuyNTeHIwN8wOWHaW9qKEqTuYZ0/k+k3SWFmZpi3jSh79Y8WDoYK7+DsbKQ6xZy54Sh56YIYXfkaLQYYYrqKKksZ57h/NYU/hzu9h8oH+PnXfGT6i/KH9C4z8IR9qTxIiErrJHUMni9CryTTbtGV9pIj0QTZP+OQLoE660J8/uMsfTQDK9/NqQ2nsTaQ12SS9xg6MmmOX7W4NhRrOyIi0sd7eR0dRIoVf0TwfYhw8Yi4aDuTZIHNtcHiyfAVsvh3RpAr5d01GjXEr5lj0XyBOu8t1U3BzPU7LrJRrwE+ZmP72L39vrT7rmDRz9pDU7fVIOiRtqRLEJRDIW4I2ISkwBdzFKX8UTozTkPEmt9Iy+uKX5n2y8re2KFaseXWxTeVJbHh+DsJQ/hWsUNrHcl2dKmtgo7xHSonmevnATVV1vUbwvswm1oEiAFRdhF4gNdC8I6OGlVzmzwMbwqiGUk/nRBQeMEbyeRQV54QV3zteuBiHc8neQPR+QiD7diR2d4JhmDbr6xop+Bat2SVQqLg2IdAJ/Qu1Aor7mxhmPqiz35SWkbaaoDJYbWrCBeQ+jos4s2jCDY7OJEXW8ng9gPO71W/lcybMWzihUw6fA9w/riHe37Ez0=" - docker_sets: - - set: debian8-64 - - set: debian9-64 - - set: debian10-64 - - set: ubuntu1604-64 - - set: ubuntu1804-64 - - set: centos6-64 - - set: centos7-64 Gemfile: optional: ':test': - gem: 'toml' +spec/spec_helper_acceptance.rb: + unmanaged: false diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index a2d892e..0000000 --- a/.travis.yml +++ /dev/null @@ -1,103 +0,0 @@ ---- -dist: bionic -language: ruby -cache: bundler -before_install: - - yes | gem update --system - - bundle --version -script: - - 'bundle exec rake $CHECK' -matrix: - fast_finish: true - include: - - rvm: 2.4.4 - bundler_args: --without system_tests development release - env: PUPPET_VERSION="~> 5.0" CHECK=test - - rvm: 2.5.3 - bundler_args: --without system_tests development release - env: PUPPET_VERSION="~> 6.0" CHECK=test_with_coveralls - - rvm: 2.5.3 - bundler_args: --without system_tests development release - env: PUPPET_VERSION="~> 6.0" CHECK=rubocop - - rvm: 2.4.4 - bundler_args: --without system_tests development release - env: PUPPET_VERSION="~> 5.0" CHECK=build DEPLOY_TO_FORGE=yes - - rvm: 2.5.3 - bundler_args: --without development release - env: PUPPET_INSTALL_TYPE=agent BEAKER_PUPPET_COLLECTION=puppet5 BEAKER_debug=true BEAKER_setfile=debian8-64 BEAKER_HYPERVISOR=docker CHECK=beaker - services: docker - - rvm: 2.5.3 - bundler_args: --without development release - env: PUPPET_INSTALL_TYPE=agent BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_debug=true BEAKER_setfile=debian8-64 BEAKER_HYPERVISOR=docker CHECK=beaker - services: docker - - rvm: 2.5.3 - bundler_args: --without development release - env: PUPPET_INSTALL_TYPE=agent BEAKER_PUPPET_COLLECTION=puppet5 BEAKER_debug=true BEAKER_setfile=debian9-64 BEAKER_HYPERVISOR=docker CHECK=beaker - services: docker - - rvm: 2.5.3 - bundler_args: --without development release - env: PUPPET_INSTALL_TYPE=agent BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_debug=true BEAKER_setfile=debian9-64 BEAKER_HYPERVISOR=docker CHECK=beaker - services: docker - - rvm: 2.5.3 - bundler_args: --without development release - env: PUPPET_INSTALL_TYPE=agent BEAKER_PUPPET_COLLECTION=puppet5 BEAKER_debug=true BEAKER_setfile=debian10-64 BEAKER_HYPERVISOR=docker CHECK=beaker - services: docker - - rvm: 2.5.3 - bundler_args: --without development release - env: PUPPET_INSTALL_TYPE=agent BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_debug=true BEAKER_setfile=debian10-64 BEAKER_HYPERVISOR=docker CHECK=beaker - services: docker - - rvm: 2.5.3 - bundler_args: --without development release - env: PUPPET_INSTALL_TYPE=agent BEAKER_PUPPET_COLLECTION=puppet5 BEAKER_debug=true BEAKER_setfile=ubuntu1604-64 BEAKER_HYPERVISOR=docker CHECK=beaker - services: docker - - rvm: 2.5.3 - bundler_args: --without development release - env: PUPPET_INSTALL_TYPE=agent BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_debug=true BEAKER_setfile=ubuntu1604-64 BEAKER_HYPERVISOR=docker CHECK=beaker - services: docker - - rvm: 2.5.3 - bundler_args: --without development release - env: PUPPET_INSTALL_TYPE=agent BEAKER_PUPPET_COLLECTION=puppet5 BEAKER_debug=true BEAKER_setfile=ubuntu1804-64 BEAKER_HYPERVISOR=docker CHECK=beaker - services: docker - - rvm: 2.5.3 - bundler_args: --without development release - env: PUPPET_INSTALL_TYPE=agent BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_debug=true BEAKER_setfile=ubuntu1804-64 BEAKER_HYPERVISOR=docker CHECK=beaker - services: docker - - rvm: 2.5.3 - bundler_args: --without development release - env: PUPPET_INSTALL_TYPE=agent BEAKER_PUPPET_COLLECTION=puppet5 BEAKER_debug=true BEAKER_setfile=centos6-64 BEAKER_HYPERVISOR=docker CHECK=beaker - services: docker - - rvm: 2.5.3 - bundler_args: --without development release - env: PUPPET_INSTALL_TYPE=agent BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_debug=true BEAKER_setfile=centos6-64 BEAKER_HYPERVISOR=docker CHECK=beaker - services: docker - - rvm: 2.5.3 - bundler_args: --without development release - env: PUPPET_INSTALL_TYPE=agent BEAKER_PUPPET_COLLECTION=puppet5 BEAKER_debug=true BEAKER_setfile=centos7-64 BEAKER_HYPERVISOR=docker CHECK=beaker - services: docker - - rvm: 2.5.3 - bundler_args: --without development release - env: PUPPET_INSTALL_TYPE=agent BEAKER_PUPPET_COLLECTION=puppet6 BEAKER_debug=true BEAKER_setfile=centos7-64 BEAKER_HYPERVISOR=docker CHECK=beaker - services: docker -branches: - only: - - master - - /^v\d/ -notifications: - email: false - webhooks: https://voxpupu.li/incoming/travis - irc: - on_success: always - on_failure: always - channels: - - "chat.freenode.org#voxpupuli-notifications" -deploy: - provider: puppetforge - user: puppet - password: - secure: "C+dXd27/doW1JnKqv+UDHL6/HBI5Te+xGsaoPkqhpO4iXfMxJrTi6sBWaEuyNTeHIwN8wOWHaW9qKEqTuYZ0/k+k3SWFmZpi3jSh79Y8WDoYK7+DsbKQ6xZy54Sh56YIYXfkaLQYYYrqKKksZ57h/NYU/hzu9h8oH+PnXfGT6i/KH9C4z8IR9qTxIiErrJHUMni9CryTTbtGV9pIj0QTZP+OQLoE660J8/uMsfTQDK9/NqQ2nsTaQ12SS9xg6MmmOX7W4NhRrOyIi0sd7eR0dRIoVf0TwfYhw8Yi4aDuTZIHNtcHiyfAVsvh3RpAr5d01GjXEr5lj0XyBOu8t1U3BzPU7LrJRrwE+ZmP72L39vrT7rmDRz9pDU7fVIOiRtqRLEJRDIW4I2ISkwBdzFKX8UTozTkPEmt9Iy+uKX5n2y8re2KFaseXWxTeVJbHh+DsJQ/hWsUNrHcl2dKmtgo7xHSonmevnATVV1vUbwvswm1oEiAFRdhF4gNdC8I6OGlVzmzwMbwqiGUk/nRBQeMEbyeRQV54QV3zteuBiHc8neQPR+QiD7diR2d4JhmDbr6xop+Bat2SVQqLg2IdAJ/Qu1Aor7mxhmPqiz35SWkbaaoDJYbWrCBeQ+jos4s2jCDY7OJEXW8ng9gPO71W/lcybMWzihUw6fA9w/riHe37Ez0=" - on: - tags: true - # all_branches is required to use tags - all_branches: true - # Only publish the build marked with "DEPLOY_TO_FORGE" - condition: "$DEPLOY_TO_FORGE = yes" diff --git a/CHANGELOG.md b/CHANGELOG.md index 7857984..d498d82 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,440 +1,471 @@ # Changelog All notable changes to this project will be documented in this file. Each new release typically also includes the latest modulesync defaults. These should not affect the functionality of the module. +## [v7.0.0](https://github.com/voxpupuli/puppet-grafana/tree/v7.0.0) (2020-08-24) + +[Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v6.1.0...v7.0.0) + +**Breaking changes:** + +- drop Ubuntu 14.04 support [\#192](https://github.com/voxpupuli/puppet-grafana/pull/192) ([bastelfreak](https://github.com/bastelfreak)) + +**Implemented enhancements:** + +- add SLES support [\#220](https://github.com/voxpupuli/puppet-grafana/pull/220) ([tuxmea](https://github.com/tuxmea)) +- Support for teams, dashboard permissions and memberships [\#210](https://github.com/voxpupuli/puppet-grafana/pull/210) ([DavidReubenWhite](https://github.com/DavidReubenWhite)) +- Add mechanism to make API changes once API is available [\#208](https://github.com/voxpupuli/puppet-grafana/pull/208) ([treydock](https://github.com/treydock)) +- Update list of supported operating systems [\#204](https://github.com/voxpupuli/puppet-grafana/pull/204) ([dhoppe](https://github.com/dhoppe)) +- allow connecting to multiple LDAP services [\#199](https://github.com/voxpupuli/puppet-grafana/pull/199) ([unki](https://github.com/unki)) + +**Fixed bugs:** + +- Code in maifests/service.pp refers to code from manifests/params.pp [\#206](https://github.com/voxpupuli/puppet-grafana/issues/206) +- Grafana 5.0.3 Users passwords being set and datasources created on every puppet run [\#104](https://github.com/voxpupuli/puppet-grafana/issues/104) +- Clean up code, because params.pp has been removed [\#214](https://github.com/voxpupuli/puppet-grafana/pull/214) ([dhoppe](https://github.com/dhoppe)) + +**Closed issues:** + +- Grafana folder example doc update [\#197](https://github.com/voxpupuli/puppet-grafana/issues/197) + +**Merged pull requests:** + +- Fix `grafana_user` `password` idempotency [\#211](https://github.com/voxpupuli/puppet-grafana/pull/211) ([alexjfisher](https://github.com/alexjfisher)) +- Support managing folder permissions [\#207](https://github.com/voxpupuli/puppet-grafana/pull/207) ([treydock](https://github.com/treydock)) +- \#197 Minor Doc correction - grafana\_folder [\#198](https://github.com/voxpupuli/puppet-grafana/pull/198) ([RandellP](https://github.com/RandellP)) +- Do not restart grafana on provisioned dashboard updates [\#196](https://github.com/voxpupuli/puppet-grafana/pull/196) ([treydock](https://github.com/treydock)) +- Remove duplicate CONTRIBUTING.md file [\#193](https://github.com/voxpupuli/puppet-grafana/pull/193) ([dhoppe](https://github.com/dhoppe)) + ## [v6.1.0](https://github.com/voxpupuli/puppet-grafana/tree/v6.1.0) (2019-10-30) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v6.0.0...v6.1.0) **Implemented enhancements:** - Feature request: add basicAuth for grafana\_datasource [\#43](https://github.com/voxpupuli/puppet-grafana/issues/43) - Add FreeBSD 12 support [\#179](https://github.com/voxpupuli/puppet-grafana/pull/179) ([olevole](https://github.com/olevole)) - Update grafana\_dashboard resource for folders [\#172](https://github.com/voxpupuli/puppet-grafana/pull/172) ([alexconrey](https://github.com/alexconrey)) - Implement grafana\_folder resource type [\#170](https://github.com/voxpupuli/puppet-grafana/pull/170) ([alexconrey](https://github.com/alexconrey)) - Mark passwords as sensitive [\#165](https://github.com/voxpupuli/puppet-grafana/pull/165) ([alexjfisher](https://github.com/alexjfisher)) **Fixed bugs:** - Fix version, because 6.0.0-beta1 does not exist anymore [\#163](https://github.com/voxpupuli/puppet-grafana/pull/163) ([dhoppe](https://github.com/dhoppe)) - Fix value of variables base\_url and real\_archive\_source [\#161](https://github.com/voxpupuli/puppet-grafana/pull/161) ([dhoppe](https://github.com/dhoppe)) - Fix value of variable real\_package\_source [\#160](https://github.com/voxpupuli/puppet-grafana/pull/160) ([dhoppe](https://github.com/dhoppe)) **Closed issues:** - How to create Notification channels [\#188](https://github.com/voxpupuli/puppet-grafana/issues/188) - Cannot install puppet/grafana, most recent puppet/archive version is v4.2 [\#184](https://github.com/voxpupuli/puppet-grafana/issues/184) - \[UBUNTU 14.04\] Package not found [\#85](https://github.com/voxpupuli/puppet-grafana/issues/85) - Puppet module exposes passwords - current and previous in plane text during puppet runs [\#82](https://github.com/voxpupuli/puppet-grafana/issues/82) - using docker install with container\_cfg attempts to use incorrect permissions [\#52](https://github.com/voxpupuli/puppet-grafana/issues/52) - Hide sensitive data values [\#45](https://github.com/voxpupuli/puppet-grafana/issues/45) - Feature request: support auth.proxy config option [\#40](https://github.com/voxpupuli/puppet-grafana/issues/40) **Merged pull requests:** - Clean up acceptance spec helper [\#189](https://github.com/voxpupuli/puppet-grafana/pull/189) ([ekohl](https://github.com/ekohl)) - DOC Add Provisioning with dashboards from grafana.com [\#185](https://github.com/voxpupuli/puppet-grafana/pull/185) ([mfaure](https://github.com/mfaure)) - Allow puppet/archive 4.x and puppetlabs/stdlib 6.x [\#176](https://github.com/voxpupuli/puppet-grafana/pull/176) ([alexjfisher](https://github.com/alexjfisher)) - Corrected invalid database config example [\#169](https://github.com/voxpupuli/puppet-grafana/pull/169) ([Rovanion](https://github.com/Rovanion)) - Use data in modules instead of params.pp [\#167](https://github.com/voxpupuli/puppet-grafana/pull/167) ([dhoppe](https://github.com/dhoppe)) - Remove Puppet 3 specific syntax [\#166](https://github.com/voxpupuli/puppet-grafana/pull/166) ([dhoppe](https://github.com/dhoppe)) ## [v6.0.0](https://github.com/voxpupuli/puppet-grafana/tree/v6.0.0) (2019-02-14) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v5.0.0...v6.0.0) **Breaking changes:** - modulesync 2.5.1 and drop Puppet 4 [\#154](https://github.com/voxpupuli/puppet-grafana/pull/154) ([bastelfreak](https://github.com/bastelfreak)) **Implemented enhancements:** - Add a task for setting the admin user's password [\#148](https://github.com/voxpupuli/puppet-grafana/pull/148) ([genebean](https://github.com/genebean)) - Integration notification channels [\#144](https://github.com/voxpupuli/puppet-grafana/pull/144) ([jnguiot](https://github.com/jnguiot)) **Fixed bugs:** - Update repo\_name Enum for new 'beta' repo [\#155](https://github.com/voxpupuli/puppet-grafana/pull/155) ([JayH5](https://github.com/JayH5)) - Fix \#152 : multi arch send out a notice [\#153](https://github.com/voxpupuli/puppet-grafana/pull/153) ([elfranne](https://github.com/elfranne)) - fixes repo url and key [\#150](https://github.com/voxpupuli/puppet-grafana/pull/150) ([crazymind1337](https://github.com/crazymind1337)) **Closed issues:** - multi arch send out a notice [\#152](https://github.com/voxpupuli/puppet-grafana/issues/152) - Package Repo moved to packages.grafana.com [\#149](https://github.com/voxpupuli/puppet-grafana/issues/149) - install\_mode archive fails if $data\_dir is not manually created [\#142](https://github.com/voxpupuli/puppet-grafana/issues/142) **Merged pull requests:** - include classes without leading :: [\#157](https://github.com/voxpupuli/puppet-grafana/pull/157) ([bastelfreak](https://github.com/bastelfreak)) - replace deprecated has\_key\(\) with `in` [\#147](https://github.com/voxpupuli/puppet-grafana/pull/147) ([bastelfreak](https://github.com/bastelfreak)) - archive install\_method creates data\_dir [\#143](https://github.com/voxpupuli/puppet-grafana/pull/143) ([othalla](https://github.com/othalla)) - Fix folder typos [\#140](https://github.com/voxpupuli/puppet-grafana/pull/140) ([pfree](https://github.com/pfree)) ## [v5.0.0](https://github.com/voxpupuli/puppet-grafana/tree/v5.0.0) (2018-10-06) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v4.5.0...v5.0.0) **Breaking changes:** - Change default of version parameter to 'installed' [\#126](https://github.com/voxpupuli/puppet-grafana/pull/126) ([baurmatt](https://github.com/baurmatt)) **Implemented enhancements:** - removing value restriction on grafana\_datasource so any custom plugin can be used [\#136](https://github.com/voxpupuli/puppet-grafana/pull/136) ([lukebigum](https://github.com/lukebigum)) - add --repo option to grafana\_cli plugin install [\#132](https://github.com/voxpupuli/puppet-grafana/pull/132) ([rwuest](https://github.com/rwuest)) - Parametrize provisioning file names [\#128](https://github.com/voxpupuli/puppet-grafana/pull/128) ([kazeborja](https://github.com/kazeborja)) **Closed issues:** - Version parameter should default to 'installed' [\#125](https://github.com/voxpupuli/puppet-grafana/issues/125) **Merged pull requests:** - modulesync 2.1.0 and allow puppet 6.x [\#137](https://github.com/voxpupuli/puppet-grafana/pull/137) ([bastelfreak](https://github.com/bastelfreak)) - allow puppetlabs/stdlib 5.x and puppetlabs/apt 6.x [\#134](https://github.com/voxpupuli/puppet-grafana/pull/134) ([bastelfreak](https://github.com/bastelfreak)) ## [v4.5.0](https://github.com/voxpupuli/puppet-grafana/tree/v4.5.0) (2018-07-16) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v4.4.1...v4.5.0) **Implemented enhancements:** - Use provisioning backend for dashboards, providers [\#103](https://github.com/voxpupuli/puppet-grafana/issues/103) - Feature: Add grafana provisioning to this module. [\#120](https://github.com/voxpupuli/puppet-grafana/pull/120) ([drshawnkwang](https://github.com/drshawnkwang)) **Closed issues:** - Any plan to update module to use the grafana provisioning by yaml files ? [\#122](https://github.com/voxpupuli/puppet-grafana/issues/122) ## [v4.4.1](https://github.com/voxpupuli/puppet-grafana/tree/v4.4.1) (2018-07-04) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v4.4.0...v4.4.1) **Fixed bugs:** - Fix dependency in provisioning plugins [\#118](https://github.com/voxpupuli/puppet-grafana/pull/118) ([drshawnkwang](https://github.com/drshawnkwang)) **Closed issues:** - grafana plugin install/check breaks catalog run when grafana-server service is not running [\#79](https://github.com/voxpupuli/puppet-grafana/issues/79) ## [v4.4.0](https://github.com/voxpupuli/puppet-grafana/tree/v4.4.0) (2018-06-21) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v4.3.0...v4.4.0) **Implemented enhancements:** - Manage sysconfig files [\#115](https://github.com/voxpupuli/puppet-grafana/pull/115) ([ZeroPointEnergy](https://github.com/ZeroPointEnergy)) **Merged pull requests:** - bump archive upper version boundary to \<4.0.0 [\#116](https://github.com/voxpupuli/puppet-grafana/pull/116) ([bastelfreak](https://github.com/bastelfreak)) ## [v4.3.0](https://github.com/voxpupuli/puppet-grafana/tree/v4.3.0) (2018-06-18) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v4.2.0...v4.3.0) **Implemented enhancements:** - Add postgres support and secure\_json\_data support [\#105](https://github.com/voxpupuli/puppet-grafana/pull/105) ([Faffnir](https://github.com/Faffnir)) **Fixed bugs:** - Update release codename from jessie to stretch. [\#113](https://github.com/voxpupuli/puppet-grafana/pull/113) ([drshawnkwang](https://github.com/drshawnkwang)) **Closed issues:** - puppet-grafana Debian repository should use codename stretch [\#112](https://github.com/voxpupuli/puppet-grafana/issues/112) **Merged pull requests:** - drop EOL OSs; fix puppet version range [\#109](https://github.com/voxpupuli/puppet-grafana/pull/109) ([bastelfreak](https://github.com/bastelfreak)) - Rely on beaker-hostgenerator for docker nodesets [\#108](https://github.com/voxpupuli/puppet-grafana/pull/108) ([ekohl](https://github.com/ekohl)) - switch from topscope facts to $facts hash [\#102](https://github.com/voxpupuli/puppet-grafana/pull/102) ([bastelfreak](https://github.com/bastelfreak)) - Update README.md [\#99](https://github.com/voxpupuli/puppet-grafana/pull/99) ([cclloyd](https://github.com/cclloyd)) ## [v4.2.0](https://github.com/voxpupuli/puppet-grafana/tree/v4.2.0) (2018-03-06) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v4.1.1...v4.2.0) **Implemented enhancements:** - Create organization [\#71](https://github.com/voxpupuli/puppet-grafana/issues/71) - Expand organization property for dashboards [\#94](https://github.com/voxpupuli/puppet-grafana/pull/94) ([brandonrdn](https://github.com/brandonrdn)) - Add grafana\_api\_path to allow for API sub-paths [\#93](https://github.com/voxpupuli/puppet-grafana/pull/93) ([brandonrdn](https://github.com/brandonrdn)) ## [v4.1.1](https://github.com/voxpupuli/puppet-grafana/tree/v4.1.1) (2018-02-21) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v4.1.0...v4.1.1) **Fixed bugs:** - grafana\_datasource provider with\_credentials\(\) returns is\_default value [\#89](https://github.com/voxpupuli/puppet-grafana/issues/89) +- fix datasource provider error [\#90](https://github.com/voxpupuli/puppet-grafana/pull/90) ([brandonrdn](https://github.com/brandonrdn)) ## [v4.1.0](https://github.com/voxpupuli/puppet-grafana/tree/v4.1.0) (2018-02-03) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v4.0.3...v4.1.0) **Implemented enhancements:** - \(SIMP-4206\) Added organization provider and updated datasource provider [\#86](https://github.com/voxpupuli/puppet-grafana/pull/86) ([heliocentric](https://github.com/heliocentric)) -**Fixed bugs:** - -- fix datasource provider error [\#90](https://github.com/voxpupuli/puppet-grafana/pull/90) ([brandonrdn](https://github.com/brandonrdn)) - **Closed issues:** - "Could not autoload" error in grafana\_dashboard with ruby 2.4 on Centos 6 [\#83](https://github.com/voxpupuli/puppet-grafana/issues/83) ## [v4.0.3](https://github.com/voxpupuli/puppet-grafana/tree/v4.0.3) (2017-12-09) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v4.0.2...v4.0.3) **Closed issues:** - Apt key add gets called every run [\#77](https://github.com/voxpupuli/puppet-grafana/issues/77) - Getting rid or changing the url check for grafana datasource url's [\#75](https://github.com/voxpupuli/puppet-grafana/issues/75) **Merged pull requests:** - Update readme with examples of using datasource and dashboard [\#80](https://github.com/voxpupuli/puppet-grafana/pull/80) ([devcfgc](https://github.com/devcfgc)) - Removing the datasource url check as it leads to errors with postgres… [\#76](https://github.com/voxpupuli/puppet-grafana/pull/76) ([Faffnir](https://github.com/Faffnir)) ## [v4.0.2](https://github.com/voxpupuli/puppet-grafana/tree/v4.0.2) (2017-10-12) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v4.0.1...v4.0.2) **Implemented enhancements:** - bump archive upper boundary to work with latest versions [\#73](https://github.com/voxpupuli/puppet-grafana/pull/73) ([bastelfreak](https://github.com/bastelfreak)) - add debian 8 and 9 support [\#72](https://github.com/voxpupuli/puppet-grafana/pull/72) ([bastelfreak](https://github.com/bastelfreak)) **Merged pull requests:** - fix typo in metadata \(redhat 6 twice vs 6/7\) [\#69](https://github.com/voxpupuli/puppet-grafana/pull/69) ([wyardley](https://github.com/wyardley)) ## [v4.0.1](https://github.com/voxpupuli/puppet-grafana/tree/v4.0.1) (2017-09-22) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v4.0.0...v4.0.1) **Fixed bugs:** - Module doesn't work on Ubuntu Xenial [\#56](https://github.com/voxpupuli/puppet-grafana/issues/56) ## [v4.0.0](https://github.com/voxpupuli/puppet-grafana/tree/v4.0.0) (2017-09-20) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v3.0.0...v4.0.0) **Breaking changes:** - BREAKING: Switch to Puppet Data Types \(ldap\_cfg is now undef when disabled\) [\#66](https://github.com/voxpupuli/puppet-grafana/pull/66) ([wyardley](https://github.com/wyardley)) - BREAKING: Create grafana\_plugin resource type and change grafana::plugins [\#63](https://github.com/voxpupuli/puppet-grafana/pull/63) ([wyardley](https://github.com/wyardley)) - BREAKING: Update default Grafana version to 4.5.1 and improve acceptance tests [\#61](https://github.com/voxpupuli/puppet-grafana/pull/61) ([wyardley](https://github.com/wyardley)) **Implemented enhancements:** - grafana\_user custom resource [\#60](https://github.com/voxpupuli/puppet-grafana/pull/60) ([atward](https://github.com/atward)) - Support newer versions of puppetlabs/apt module [\#53](https://github.com/voxpupuli/puppet-grafana/pull/53) ([ghoneycutt](https://github.com/ghoneycutt)) - Support custom plugins [\#44](https://github.com/voxpupuli/puppet-grafana/pull/44) ([bastelfreak](https://github.com/bastelfreak)) **Fixed bugs:** - gpg key error on CentOS 7 with default params [\#59](https://github.com/voxpupuli/puppet-grafana/issues/59) - wget called even if not necessary [\#54](https://github.com/voxpupuli/puppet-grafana/issues/54) - Fix typo in provider [\#58](https://github.com/voxpupuli/puppet-grafana/pull/58) ([atward](https://github.com/atward)) **Closed issues:** - install\_method 'docker" ignores all other configurations [\#51](https://github.com/voxpupuli/puppet-grafana/issues/51) - Usable for Grafana 4.x? [\#37](https://github.com/voxpupuli/puppet-grafana/issues/37) - Remove docker dependency [\#22](https://github.com/voxpupuli/puppet-grafana/issues/22) **Merged pull requests:** - Update README.md [\#67](https://github.com/voxpupuli/puppet-grafana/pull/67) ([wyardley](https://github.com/wyardley)) - Get rid of the dependency on 'wget' module in favor of puppet-archive [\#65](https://github.com/voxpupuli/puppet-grafana/pull/65) ([wyardley](https://github.com/wyardley)) - Remove licenses from the top of files [\#64](https://github.com/voxpupuli/puppet-grafana/pull/64) ([wyardley](https://github.com/wyardley)) - Release 4.0.0 [\#62](https://github.com/voxpupuli/puppet-grafana/pull/62) ([bastelfreak](https://github.com/bastelfreak)) - Always use jessie apt repo when osfamily is Debian. [\#41](https://github.com/voxpupuli/puppet-grafana/pull/41) ([furhouse](https://github.com/furhouse)) ## [v3.0.0](https://github.com/voxpupuli/puppet-grafana/tree/v3.0.0) (2017-03-29) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v2.6.3...v3.0.0) **Implemented enhancements:** - implement package\_ensure param for archlinux [\#34](https://github.com/voxpupuli/puppet-grafana/pull/34) ([bastelfreak](https://github.com/bastelfreak)) **Fixed bugs:** -- FIX configuration file ownership [\#30](https://github.com/voxpupuli/puppet-grafana/pull/30) ([cassianoleal](https://github.com/cassianoleal)) +- FIX configuration file ownership [\#30](https://github.com/voxpupuli/puppet-grafana/pull/30) ([c10l](https://github.com/c10l)) **Closed issues:** - Configured grafana debian repo should contain current distribution [\#27](https://github.com/voxpupuli/puppet-grafana/issues/27) - Error while creating dashboard [\#25](https://github.com/voxpupuli/puppet-grafana/issues/25) **Merged pull requests:** - Bump version, Update changelog [\#38](https://github.com/voxpupuli/puppet-grafana/pull/38) ([dhoppe](https://github.com/dhoppe)) - Debian and RedHat based operating systems should use the repository by default [\#36](https://github.com/voxpupuli/puppet-grafana/pull/36) ([dhoppe](https://github.com/dhoppe)) - Add support for archlinux [\#32](https://github.com/voxpupuli/puppet-grafana/pull/32) ([bastelfreak](https://github.com/bastelfreak)) -- Fix grafana\_dashboards [\#31](https://github.com/voxpupuli/puppet-grafana/pull/31) ([cassianoleal](https://github.com/cassianoleal)) +- Fix grafana\_dashboards [\#31](https://github.com/voxpupuli/puppet-grafana/pull/31) ([c10l](https://github.com/c10l)) - supoort jessie for install method repo [\#28](https://github.com/voxpupuli/puppet-grafana/pull/28) ([roock](https://github.com/roock)) - Use operatinsystemmajrelease fact in repo url [\#24](https://github.com/voxpupuli/puppet-grafana/pull/24) ([mirekys](https://github.com/mirekys)) - The puppet 4-only release will start at 3.0.0 [\#21](https://github.com/voxpupuli/puppet-grafana/pull/21) ([rnelson0](https://github.com/rnelson0)) ## [v2.6.3](https://github.com/voxpupuli/puppet-grafana/tree/v2.6.3) (2017-01-18) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v2.6.2...v2.6.3) ## [v2.6.2](https://github.com/voxpupuli/puppet-grafana/tree/v2.6.2) (2017-01-18) [Full Changelog](https://github.com/voxpupuli/puppet-grafana/compare/v2.6.1...v2.6.2) **Merged pull requests:** - release 2.6.2 \(optimistic, i know ;\) [\#20](https://github.com/voxpupuli/puppet-grafana/pull/20) ([igalic](https://github.com/igalic)) ## v2.6.1 (2017-01-18) Just a notice: The next release will be a major one without Puppet 3 support! This is the last Release that supports it! ## Releasing v2.6.0 (2017-01-18) **Enhancements** * add two types & provider: `grafana_datasource` & `grafana_dashboard` these type allow configuration of the datasource and the dashboard against the API * allow configuration of `repo_name` for all installation methods * be more conservative when installing from docker, while also allowing users to override our `stable` choice **Fixes** * ensure correct ownership of downloaded artefact * fix use-before definition of `$version`: https://github.com/bfraser/puppet-grafana/issues/87 **Behind The Scenes** * switch to voxpupuli/archive from camptocamp **Changes since forking from bfraser/puppet-grafana** * Add CONTRIBUTING.MD as well as our issues, spec etc… templates * update README and other files to point to forked repository * Rubocop and ruby-lint style-fixes! * test with puppet > 4.x ## 2.5.0 (2015-10-31) **Enhancements** - Support for [Grafana 2.5](http://grafana.org/blog/2015/10/28/Grafana-2-5-Released.html). This is just a version bump to reflect that Grafana 2.5 is now installed by default - [PR #58](https://github.com/bfraser/puppet-grafana/pull/58) Sort ```cfg``` keys so ```config.ini``` content is not updated every Puppet run **Fixes** - [Issue #52](https://github.com/bfraser/puppet-grafana/issues/52) Version logic moved to ```init.pp``` so overriding the version of Grafana to install works as intended **Behind The Scenes** - [PR #59](https://github.com/bfraser/puppet-grafana/pull/59) More specific version requirements in ```metadata.json``` due to use of ```contain``` function - [PR #61](https://github.com/bfraser/puppet-grafana/pull/61) Fixed typos in ```metadata.json``` ## 2.1.0 (2015-08-07) **Enhancements** - Support for [Grafana 2.1](http://grafana.org/blog/2015/08/04/Grafana-2-1-Released.html) - [Issue #40](https://github.com/bfraser/puppet-grafana/issues/40) Support for [LDAP integration](http://docs.grafana.org/v2.1/installation/ldap/) - [PR #34](https://github.com/bfraser/puppet-grafana/pull/34) Support for 'repo' install method to install packages from [packagecloud](https://packagecloud.io/grafana) repositories - Addition of boolean parameter ```manage_package_repo``` to control whether the module will manage the package repository when using the 'repo' install method. See README.md for details - [PR #39](https://github.com/bfraser/puppet-grafana/pull/39) Ability to ensure a specific package version is installed when using the 'repo' install method **Fixes** - [Issue #37](https://github.com/bfraser/puppet-grafana/issues/37) Archive install method: check if user and service are already defined before attempting to define them - [Issue #42](https://github.com/bfraser/puppet-grafana/issues/42) Package versioning for RPM / yum systems - [Issue #45](https://github.com/bfraser/puppet-grafana/issues/45) Fix resource dependency issues when ```manage_package_repo``` is false **Behind The Scenes** - Use 40 character GPG key ID for packagecloud apt repository ## 2.0.2 (2015-04-30) **Enhancements** - Support for Grafana 2.0. Users of Grafana 1.x should stick to version 1.x of the Puppet module - Support 'archive', 'docker' and 'package' install methods - Ability to supply a hash of parameters to the Docker container when using 'docker' install method - [PR #24](https://github.com/bfraser/puppet-grafana/pull/24) Ability to configure Grafana using configuration hash parameter ```cfg``` **Behind The Scenes** - Update module operatingsystem support, tags, Puppet requirements - Tests for 'archive' and 'package' install methods ## 1.0.1 (2015-02-27) **Enhancements** - New parameter for Grafana admin password **Fixes** - Package install method now makes use of install_dir for config.js path **Behind The Scenes** - Add archive module to .fixtures.yml - Unquote booleans to make lint happy - Fix license identifier and unbounded dependencies in module metadata - Allow Travis to fail on Ruby 1.8.7 - More Puppet versions are tested by Travis ## 1.0.0 (2014-12-16) **Enhancements** - Add max_search_results parameter - Install Grafana 1.9.0 by default **Documentation** - Add download_url and install_method parameters to README **Behind The Scenes** - [Issue #6](https://github.com/bfraser/puppet-grafana/issues/6) Replace gini/archive dependency with camptocamp/archive - Addition of CHANGELOG - Style fixes - Removal of vagrant-wrapper gem - Fancy badges for build status ## 0.2.2 (2014-10-27) **Enhancements** - Add default_route parameter to manage start dashboard **Fixes** - Symlink behavior **Behind The Scenes** - [Issue #9](https://github.com/bfraser/puppet-grafana/issues/9) Remove stdlib inclusion from manifest ## 0.2.1 (2014-10-14) **Enhancements** - Support for multiple datasources - Install Grafana 1.8.1 by default **Behind The Scenes** - Added RSpec tests - Add stdlib as a module dependency - Add operating system compatibility ## 0.1.3 (2014-07-03) **Enhancements** - Added support for InfluxDB ## 0.1.2 (2014-06-30) First release on the Puppet Forge \* *This Changelog was automatically generated by [github_changelog_generator](https://github.com/github-changelog-generator/github-changelog-generator)* diff --git a/Gemfile b/Gemfile index 2128b39..fdedfcb 100644 --- a/Gemfile +++ b/Gemfile @@ -1,69 +1,34 @@ source ENV['GEM_SOURCE'] || "https://rubygems.org" -def location_for(place, fake_version = nil) - if place =~ /^(git[:@][^#]*)#(.*)/ - [fake_version, { :git => $1, :branch => $2, :require => false }].compact - elsif place =~ /^file:\/\/(.*)/ - ['>= 0', { :path => File.expand_path($1), :require => false }] - else - [place, { :require => false }] - end -end - group :test do - gem 'voxpupuli-test', '>= 1.0.0', :require => false - gem 'coveralls', :require => false - gem 'simplecov-console', :require => false - gem 'toml', :require => false + gem 'voxpupuli-test', '~> 2.1', :require => false + gem 'coveralls', :require => false + gem 'simplecov-console', :require => false + gem 'toml', :require => false end group :development do - gem 'travis', :require => false - gem 'travis-lint', :require => false gem 'guard-rake', :require => false gem 'overcommit', '>= 0.39.1', :require => false end group :system_tests do - gem 'winrm', :require => false - if beaker_version = ENV['BEAKER_VERSION'] - gem 'beaker', *location_for(beaker_version) - else - gem 'beaker', '>= 4.2.0', :require => false - end - if beaker_rspec_version = ENV['BEAKER_RSPEC_VERSION'] - gem 'beaker-rspec', *location_for(beaker_rspec_version) - else - gem 'beaker-rspec', :require => false - end - gem 'serverspec', :require => false - gem 'beaker-hostgenerator', '>= 1.1.22', :require => false - gem 'beaker-docker', :require => false - gem 'beaker-puppet', :require => false - gem 'beaker-puppet_install_helper', :require => false - gem 'beaker-module_install_helper', :require => false - gem 'rbnacl', '>= 4', :require => false - gem 'rbnacl-libsodium', :require => false - gem 'bcrypt_pbkdf', :require => false - gem 'ed25519', :require => false + gem 'puppet_metadata', '~> 0.3.0', :require => false + gem 'voxpupuli-acceptance', :require => false end group :release do gem 'github_changelog_generator', :require => false, :git => 'https://github.com/voxpupuli/github-changelog-generator', :branch => 'voxpupuli_essential_fixes' gem 'puppet-blacksmith', :require => false gem 'voxpupuli-release', :require => false gem 'puppet-strings', '>= 2.2', :require => false end +gem 'puppetlabs_spec_helper', '~> 2.0', :require => false +gem 'rake', :require => false +gem 'facter', ENV['FACTER_GEM_VERSION'], :require => false, :groups => [:test] - -if facterversion = ENV['FACTER_GEM_VERSION'] - gem 'facter', facterversion.to_s, :require => false, :groups => [:test] -else - gem 'facter', :require => false, :groups => [:test] -end - -ENV['PUPPET_VERSION'].nil? ? puppetversion = '~> 6.0' : puppetversion = ENV['PUPPET_VERSION'].to_s +puppetversion = ENV['PUPPET_VERSION'] || '~> 6.0' gem 'puppet', puppetversion, :require => false, :groups => [:test] # vim: syntax=ruby diff --git a/README.md b/README.md index bade725..a0f49e6 100644 --- a/README.md +++ b/README.md @@ -1,1191 +1,1196 @@ # grafana [![Build Status](https://travis-ci.org/voxpupuli/puppet-grafana.png?branch=master)](https://travis-ci.org/voxpupuli/puppet-grafana) [![Code Coverage](https://coveralls.io/repos/github/voxpupuli/puppet-grafana/badge.svg?branch=master)](https://coveralls.io/github/voxpupuli/puppet-grafana) [![Puppet Forge](https://img.shields.io/puppetforge/v/puppet/grafana.svg)](https://forge.puppetlabs.com/puppet/grafana) [![Puppet Forge - downloads](https://img.shields.io/puppetforge/dt/puppet/grafana.svg)](https://forge.puppetlabs.com/puppet/grafana) [![Puppet Forge - endorsement](https://img.shields.io/puppetforge/e/puppet/grafana.svg)](https://forge.puppetlabs.com/puppet/grafana) [![Puppet Forge - scores](https://img.shields.io/puppetforge/f/puppet/grafana.svg)](https://forge.puppetlabs.com/puppet/grafana) #### Table of Contents 1. [Overview](#overview) 1. [Module Description](#module-description) 1. [Setup](#setup) * [Requirements](#requirements) * [Beginning with Grafana](#beginning-with-grafana) 1. [Usage](#usage) * [Classes and Defined Types](#classes-and-defined-types) * [Advanced usage](#advanced-usage) 1. [Tasks](#tasks) 1. [Limitations](#limitations) 1. [Copyright and License](#copyright-and-license) ## Overview This module installs Grafana, a dashboard and graph editor for Graphite, InfluxDB and OpenTSDB. ## Module Description Version 2.x of this module is designed to work with version 2.x of Grafana. If you would like to continue to use Grafana 1.x, please use version 1.x of this module. ## Setup This module will: * Install Grafana using your preferred method: package (default), Docker container, or tar archive * Allow you to override the version of Grafana to be installed, and / or the package source * Perform basic configuration of Grafana ### Requirements * If using an operating system of the Debian-based family, and the "repo" `install_method`, you will need to ensure that [puppetlabs-apt](https://forge.puppet.com/puppetlabs/apt) version 4.x is installed. * If using Docker, you will need the [garethr/docker](https://forge.puppet.com/garethr/docker) module version 5.x ### Beginning with Grafana To install Grafana with the default parameters: ```puppet class { 'grafana': } ``` This assumes that you want to install Grafana using the 'package' method. To establish customized parameters: ```puppet class { 'grafana': install_method => 'docker', } ``` ## Usage ### Classes and Defined Types #### Class: `grafana` The Grafana module's primary class, `grafana`, guides the basic setup of Grafana on your system. ```puppet class { 'grafana': } ``` **Parameters within `grafana`:** ##### `archive_source` The download location of a tarball to use with the 'archive' install method. Defaults to the URL of the latest version of Grafana available at the time of module release. ##### `cfg_location` Configures the location to which the Grafana configuration is written. The default location is '/etc/grafana/grafana.ini'. ##### `cfg` Manages the Grafana configuration file. Grafana comes with its own default settings in a different configuration file (/opt/grafana/current/conf/defaults.ini), therefore this module does not supply any defaults. This parameter only accepts a hash as its value. Keys with hashes as values will generate sections, any other values are just plain values. The example below will result in... ```puppet class { 'grafana': cfg => { app_mode => 'production', server => { http_port => 8080, }, database => { type => 'mysql', host => '127.0.0.1:3306', name => 'grafana', user => 'root', password => '', }, users => { allow_sign_up => false, }, }, } ``` ...the following Grafana configuration: ```ini # This file is managed by Puppet, any changes will be overwritten app_mode = production [server] http_port = 8080 [database] type = sqlite3 host = 127.0.0.1:3306 name = grafana user = root password = [users] allow_sign_up = false ``` Some minor notes: * If you want empty values, just use an empty string. * Keys that contains dots (like auth.google) need to be quoted. * The order of the keys in this hash is the same as they will be written to the configuration file. So settings that do not fall under a section will have to come before any sections in the hash. #### `ldap_cfg` ##### TOML note This option **requires** the [toml](https://github.com/toml-lang/toml) gem. Either install the gem using puppet's native gem provider, [puppetserver_gem](https://forge.puppetlabs.com/puppetlabs/puppetserver_gem), [pe_gem](https://forge.puppetlabs.com/puppetlabs/pe_gem), [pe_puppetserver_gem](https://forge.puppetlabs.com/puppetlabs/pe_puppetserver_gem), or manually using one of the following: ``` # apply or puppet-master gem install toml # PE apply /opt/puppet/bin/gem install toml # AIO or PE puppetserver /opt/puppet/bin/puppetserver gem install toml ``` ##### cfg note This option by itself is not sufficient to enable LDAP configuration as it must be enabled in the main configuration file. Enable it in cfg with: ``` 'auth.ldap' => { enabled => 'true', config_file => '/etc/grafana/ldap.toml', }, ``` #### Integer note Puppet may convert integers into strings while parsing the hash and converting into toml. This can be worked around by appending 0 to an integer. Example: ``` port => 636+0, ``` Manages the Grafana LDAP configuration file. This hash is directly translated into the corresponding TOML file, allowing for full flexibility in generating the configuration. See the [LDAP documentation](http://docs.grafana.org/v2.1/installation/ldap/) for more information. #### Example LDAP config ``` ldap_cfg => { servers => [ { host => 'ldapserver1.domain1.com', port => 636+0, use_ssl => true, search_filter => '(sAMAccountName=%s)', search_base_dns => [ 'dc=domain1,dc=com' ], bind_dn => 'user@domain1.com', bind_password => 'passwordhere', }, ], 'servers.attributes' => { name => 'givenName', surname => 'sn', username => 'sAMAccountName', member_of => 'memberOf', email => 'email', } }, ``` If you want to connect to multiple LDAP servers using different configurations, use an array to enwrap the configurations as shown below. ``` ldap_cfg => [ { servers => [ { host => 'ldapserver1.domain1.com', port => 636+0, use_ssl => true, search_filter => '(sAMAccountName=%s)', search_base_dns => [ 'dc=domain1,dc=com' ], bind_dn => 'user@domain1.com', bind_password => 'passwordhere', }, ], 'servers.attributes' => { name => 'givenName', surname => 'sn', username => 'sAMAccountName', member_of => 'memberOf', email => 'email', }, 'servers.group_mappings' => [ { group_dn => cn=grafana_viewers,ou=groups,dc=domain1,dc=com org_role: Viewer } ], }, { servers => [ { host => 'ldapserver2.domain2.com', port => 389+0, use_ssl => false, start_tls => true, search_filter => '(uid=%s)', search_base_dns => [ 'dc=domain2,dc=com' ], bind_dn => 'user@domain2.com', bind_password => 'passwordhere', }, ], 'servers.attributes' => { name => 'givenName', surname => 'sn', username => 'uid', member_of => 'memberOf', email => 'mail', } 'servers.group_mappings' => [ { 'group_dn' => 'cn=grafana_admins,ou=groups,dc=domain2,dc=com', 'org_role' => 'Admin', 'grafana_admin' => true, } ], }, ] ##### # or in hiera-yaml style grafana::ldap_cfg: - servers: - host: ldapserver1.domain1.com port: 636 use_ssl: true search_filter: '(sAMAccountName=%s)' search_base_dns: ['dc=domain1,dc=com'] bind_dn: 'user@domain1.com' bind_password: 'passwordhere' servers.attributes: name: givenName surname: sn username: sAMAccountName member_of: memberOf email: email servers.group_mappings: - group_dn: cn=grafana_viewers,ou=groups,dc=domain1,dc=com org_role: Viewer - servers: - host: ldapserver2.domain2.com port: 389 use_ssl: false start_tls: true search_filter: '(uid=%s)', search_base_dns: ['dc=domain2,dc=com'] bind_dn: 'user@domain2.com' bind_password: 'passwordhere' servers.attributes: name: givenName surname: sn username: uid member_of: memberOf email: mail servers.group_mappings: - group_dn: cn=grafana_admins,ou=groups,dc=domain2,dc=com org_role: Admin grafana_admin: true ##### ``` ##### `container_cfg` Boolean to control whether a configuration file should be generated when using the 'docker' install method. If 'true', use the 'cfg' and 'cfg_location' parameters to control creation of the file. Defaults to false. ##### `container_params` A hash of parameters to use when creating the Docker container. For use with the 'docker' install method. Refer to documentation of the 'docker::run' resource in the [garethr-docker](https://github.com/garethr/garethr-docker) module for details of available parameters. Defaults to: ```puppet container_params => { 'image' => 'grafana/grafana:latest', 'ports' => '3000:3000' } ``` ##### `data_dir` The directory Grafana will use for storing its data. Defaults to '/var/lib/grafana'. ##### `install_dir` The installation directory to be used with the 'archive' install method. Defaults to '/usr/share/grafana'. ##### `install_method` Controls which method to use for installing Grafana. Valid options are: 'archive', 'docker', 'repo' and 'package'. The default is 'package'. If you wish to use the 'docker' installation method, you will need to include the 'docker' class in your node's manifest / profile. If you wish to use the 'repo' installation method, you can control whether the official Grafana repositories will be used. See `manage_package_repo` below for details. ##### `manage_package_repo` Boolean. When using the 'repo' installation method, controls whether the official Grafana repositories are enabled on your host. If true, the official Grafana repositories will be enabled. If false, the module assumes you are managing your own package repository and will not set one up for you. Defaults to true. ##### `plugins` Hash. This is a passthrough to call `create_resources()` on the `grafana_plugin` resource type. ##### `package_name` The name of the package managed with the 'package' install method. Defaults to 'grafana'. ##### `package_source` The download location of a package to be used with the 'package' install method. Defaults to the URL of the latest version of Grafana available at the time of module release. ##### `provisioning_datasources` A Hash which is converted to YAML for grafana to provision data sources. See [provisioning grafana](http://docs.grafana.org/administration/provisioning/) for details and example config file. Requires grafana > v5.0.0. This is very useful with Hiera as you can provide a yaml hash/dictionary which will effectively 'passthrough' to grafana. See **Advanced Usage** for examples. ##### `provisioning_dashboards` A Hash which is converted to YAML for grafana to provision dashboards. See [provisioning grafana](http://docs.grafana.org/administration/provisioning/) for details and example config file. Requires grafana > v5.0.0. This is very useful with Hiera as you can provide a yaml hash/dictionary which will effectively 'passthrough' to grafana. See **Advanced Usage** for examples. N.B. A option named `puppetsource` may be given in the `options` hash which is not part of grafana's syntax. This option will be extracted from the hash, and used to "source" a directory of dashboards. See **Advanced Usage** for details. #### `provisioning_dashboards_file` A String that is used as the target file name for the dashabords provisioning file. This way the module can be used to generate placeholder files so password can be sepecified in a different iteration, avoiding them to be put in the module code. #### `provisioning_datasources_file` A String that is used as the target file name for the datasources provisioning file. This way the module can be used to generate placeholder files so password can be sepecified in a different iteration, avoiding them to be put in the module code. ##### `rpm_iteration` Used when installing Grafana from package ('package' or 'repo' install methods) on Red Hat based systems. Defaults to '1'. It should not be necessary to change this in most cases. ##### `service_name` The name of the service managed with the 'archive' and 'package' install methods. Defaults to 'grafana-server'. ##### `version` The version of Grafana to install and manage. Defaults to 'installed' ##### `sysconfig_location` The RPM and DEB packages bring with them the default environment files for the services. The default location of this file for Debian is /etc/default/grafana-server and for RedHat /etc/sysconfig/grafana-server. ##### `sysconfig` A hash of environment variables for the service. This only has an effect for installations with RPM and DEB packages (if install_method is set to 'package' or 'repo'). Example: ```puppet sysconfig => { 'http_proxy' => 'http://proxy.example.com', } ``` ### Advanced usage The archive install method will create the user and a "command line" service by default. There are no extra parameters to manage user/service for archive. However, both check to see if they are defined before defining. This way you can create your own user and service with your own specifications. (sort of overriding) The service can be a bit tricky, in this example below, the class sensu_install::grafana::service creates a startup script and a service{'grafana-server':} Example: ```puppet user { 'grafana': ensure => present, uid => '1234', } -> class { 'grafana': install_method => 'archive', } include sensu_install::grafana::service # run your service after install/config but before grafana::service Class[::grafana::install] -> Class[sensu_install::grafana::service] -> Class[::grafana::service] ``` #### Using a sub-path for Grafana API If you are using a sub-path for the Grafana API, you will need to set the `grafana_api_path` parameter for the following custom types: - `grafana_dashboard` - `grafana_datasource` - `grafana_organization` - `grafana_user` - `grafana_folder` - `grafana_team` - `grafana_membership` +- `grafana_dashboard_permission` For instance, if your sub-path is `/grafana`, the `grafana_api_path` must be set to `/grafana/api`. Do not add a trailing `/` (slash) at the end of the value. If you are not using sub-paths, you do not need to set this parameter. #### Custom Types and Providers The module includes several custom types: #### `grafana_organization` In order to use the organization resource, add the following to your manifest: ```puppet grafana_organization { 'example_org': grafana_url => 'http://localhost:3000', grafana_user => 'admin', grafana_password => '5ecretPassw0rd', } ``` `grafana_url`, `grafana_user`, and `grafana_password` are required to create organizations via the API. `name` is optional if the name will differ from example_org above. `address` is an optional parameter that requires a hash. Address settings are `{"address1":"","address2":"","city":"","zipCode":"","state":"","country":""}` #### `grafana_team` In order to use the team resource, add the following to your manifest: ```puppet grafana_team { 'example_team': + ensure => 'present', grafana_url => 'http://localhost:3000', grafana_user => 'admin', grafana_password => '5ecretPassw0rd', home_dashboard => 'example_dashboard', organization => 'example_org', } ``` Organziation must exist if specified. `grafana_url`, `grafana_user`, and `grafana_password` are required to create teams via the API. +`ensure` is required. If the resource should be `present` or `absent` + `name` is optional if the name will differ from example_team above. `home_dashboard` is optional. Sets the home dashboard for team. Dashboard must exist. `organization` is optional. Defaults to `Main org.` -`ensure` is optional. If the resource should be `present` or `absent` - #### `grafana_dashboard_permission` In order to use the dashboard permission resource, add one the following to your manifest: add permissions for user: ```puppet grafana_dashboard_permission { 'example_user_permission': + ensure => 'present', grafana_url => 'http://localhost:3000', grafana_user => 'admin', grafana_password => '5ecretPassw0rd', dashboard => 'example_dashboard', user => 'example_user', organization => 'example_org', } ``` add permissions for team: ```puppet grafana_dashboard_permission { 'example_team_permission': + ensure => 'present', grafana_url => 'http://localhost:3000', grafana_user => 'admin', grafana_password => '5ecretPassw0rd', dashboard => 'example_dashboard', team => 'example_team', organization => 'example_org', } ``` Organziation, team, user and dashboard must exist if specified. `grafana_url`, `grafana_user`, and `grafana_password` are required to create teams via the API. +`ensure` is required. If the resource should be `present` or `absent` + `dashboard` is required. The dashboard to set permissions for. `user` is required if `team` not set. The user to add permissions for. `team` is required if `user` not set. the team to add permissions for. `name` is optional if the name will differ from example_team above. `organization` is optional. Defaults to `Main org.` -`ensure` is optional. If the resource should be `present` or `absent` - #### `grafana_membership` In order to use the membership resource, add the following to your manifest: ```puppet grafana_membership { 'example_membership': + ensure => 'present', grafana_url => 'http://localhost:3000', grafana_user => 'admin', grafana_password => '5ecretPassw0rd', - membership_type => 'team', - organization => 'example_org', - target_name => 'example_team', - user_name => 'example_user', - role => 'Viewer' + membership_type => 'team', + organization => 'example_org', + target_name => 'example_team', + user_name => 'example_user', + role => 'Viewer' } } ``` A membership is the concept of a user belonging to a target - either a `team` or an `organization` The user and target must both exist for a membership to be created `grafana_url`, `grafana_user`, and `grafana_password` are required to create memberships via the API. +`ensure` is required. If the resource should be `present` or `absent` + `membership_type` is required. Either `team` or `organization` `target_name` is required. Specifies the target of the membership. `user_name` is required. Specifies the user that is the focus of the membership. `role` is required. Specifies what rights to grant the user. Either `Viewer`, `Editor` or `Admin` `organization` is optional when using the `membership_type` of `team`. Defaults to `Main org.` -`ensure` is optional. If the resource should be `present` or `absent` - #### `grafana_dashboard` In order to use the dashboard resource, add the following to your manifest: ```puppet grafana_dashboard { 'example_dashboard': grafana_url => 'http://localhost:3000', grafana_user => 'admin', grafana_password => '5ecretPassw0rd', grafana_api_path => '/grafana/api', folder => 'folder-name', organization => 'NewOrg', content => template('path/to/exported/file.json'), } ``` `content` must be valid JSON, and is parsed before imported. `grafana_user` and `grafana_password` are optional, and required when authentication is enabled in Grafana. `grafana_api_path` is optional, and only used when using sub-paths for the API. `organization` is optional, and used when creating a dashboard for a specific organization. `folder` is an optional parameter, but the folder resource must exist. Example: Make sure the `grafana-server` service is up and running before creating the `grafana_dashboard` definition. One option is to use the `http_conn_validator` from the [healthcheck](https://forge.puppet.com/puppet/healthcheck) module ```puppet http_conn_validator { 'grafana-conn-validator' : host => 'localhost', port => '3000', use_ssl => false, test_url => '/public/img/grafana_icon.svg', require => Class['grafana'], } -> grafana_dashboard { 'example_dashboard': grafana_url => 'http://localhost:3000', grafana_user => 'admin', grafana_password => '5ecretPassw0rd', content => template('path/to/exported/file.json'), } ``` ##### `grafana_datasource` In order to use the datasource resource, add the following to your manifest: ```puppet grafana_datasource { 'influxdb': grafana_url => 'http://localhost:3000', grafana_user => 'admin', grafana_password => '5ecretPassw0rd', grafana_api_path => '/grafana/api', type => 'influxdb', organization => 'NewOrg', url => 'http://localhost:8086', user => 'admin', password => '1nFlux5ecret', database => 'graphite', access_mode => 'proxy', is_default => true, json_data => template('path/to/additional/config.json'), secure_json_data => template('path/to/additional/secure/config.json') } ``` Available types are: influxdb, elasticsearch, graphite, cloudwatch, mysql, opentsdb, postgres and prometheus `organization` is used to set which organization a datasource will be created on. If this parameter is not set, it will default to organization ID 1 (Main Org. by default). If the default org is deleted, organizations will need to be specified. Access mode determines how Grafana connects to the datasource, either `direct` from the browser, or `proxy` to send requests via grafana. Setting `basic_auth` to `true` will allow use of the `basic_auth_user` and `basic_auth_password` params. Authentication is optional, as are `database` and `grafana_api_path`; additional `json_data` and `secure_json_data` can be provided to allow custom configuration options. Example: Make sure the `grafana-server` service is up and running before creating the `grafana_datasource` definition. One option is to use the `http_conn_validator` from the [healthcheck](https://forge.puppet.com/puppet/healthcheck) module ```puppet http_conn_validator { 'grafana-conn-validator' : host => 'localhost', port => '3000', use_ssl => false, test_url => '/public/img/grafana_icon.svg', require => Class['grafana'], } -> grafana_datasource { 'influxdb': grafana_url => 'http://localhost:3000', grafana_user => 'admin', grafana_password => '5ecretPassw0rd', type => 'influxdb', url => 'http://localhost:8086', user => 'admin', password => '1nFlux5ecret', database => 'graphite', access_mode => 'proxy', is_default => true, json_data => template('path/to/additional/config.json'), } ``` Note that the `database` is dynamic, setting things other than "database" for separate types. Ex: for Elasticsearch it will set the Index Name. **`jsonData` Settings** Note that there are separate options for json_data / secure_json_data based on the type of datasource you create. ##### **Elasticsearch** `esVersion` - Required, either 2 or 5, set as a bare number. `timeField` - Required. By default this is @timestamp, but without setting it in jsonData, the datasource won't work without refreshing it in the GUI. `timeInterval` - Optional. A lower limit for the auto group by time interval. Recommended to be set to write frequency, for example "1m" if your data is written every minute. Example: ```puppet json_data => {"esVersion":5,"timeField":"@timestamp","timeInterval":"1m"} ``` ##### **CloudWatch** `authType` - Required. Options are `Access & Secret Key`, `Credentials File`, or `ARN`. -"keys" = Access & Secret Key -"credentials" = Credentials File -"arn" = ARN *When setting authType to `credentials`, the `database` param will set the Credentials Profile Name.* *When setting authType to `arn`, another jsonData value of `assumeRoleARN` is available, which is not required for other authType settings* `customMetricsNamespaces` - Optional. Namespaces of Custom Metrics, separated by commas within double quotes. `defaultRegion` - Required. Options are "ap-northeast-(1 or 2)", "ap-southeast-(1 or 2)", "ap-south-1", "ca-central-1", "cn-north-1", "eu-central-1", "eu-west-(1 or 2)", "sa-east-(1 or 2)", "us-east-(1 or 2)", "us-gov-west-1", "us-west-(1 or 2)". `timeField` Example: ```puppet {"authType":"arn","assumeRoleARN":"arn:aws:iam:*","customMetricsNamespaces":"Namespace1,Namespace2","defaultRegion":"us-east-1","timeField":"@timestamp"} ``` ##### **Graphite** `graphiteVersion` - Required. Available versions are `0.9` or `1.0`. `tlsAuth` - Set to `true` or `false` `tlsAuthWithCACert` - Set to `true` or `false` Example: ```puppet {"graphiteVersion":"0.9","tlsAuth":true,"tlsAuthWithCACert":false} ``` ##### **OpenTSDB** `tsdbResolution` - Required. Options are `1` or `2`. `1` = second `2` = millisecond `tsdbVersion` - Required. Options are `1`, `2`, or `3`. `1`    =    <=2.1 `2`    =    ==2.2 `3`    =    ==2.3 Example: ```puppet {"tsdbResolution:1,"tsdbVersion":3} ``` ##### **InfluxDB** N/A ##### **MySQL** N/A ##### **Prometheus** N/A ##### `grafana_plugin` An example is provided for convenience; for more details, please view the puppet strings docs. ```puppet grafana_plugin { 'grafana-simple-json-datasource': ensure => present, } ``` It is possible to specify a custom plugin repository to install a plugin. This will use the --repo option for plugin installation with grafana_cli. ```puppet grafana_plugin { 'grafana-simple-json-datasource': ensure => present, repo => 'https://nexus.company.com/grafana/plugins', } ``` ##### `grafana_folder` Creates and manages Grafana folders via the API. The following example creates a folder named 'folder1': ```puppet grafana_folder { 'folder1': ensure => present, grafana_url => 'http://localhost:3000', grafana_api_path => '/grafana/api', grafana_user => 'admin', grafana_password => '5ecretPassw0rd', } ``` `grafana_api_path` is only required if using sub-paths for the API ##### `grafana::user` Creates and manages a global grafana user via the API. ```puppet grafana_user { 'username': grafana_url => 'http://localhost:3000', grafana_api_path => '/grafana/api', grafana_user => 'admin', grafana_password => '5ecretPassw0rd', full_name => 'John Doe', password => 'Us3r5ecret', email => 'john@example.com', } ``` `grafana_api_path` is only required if using sub-paths for the API ##### `grafana::notification` Creates and manages a global alert notification channel via the API. ```puppet grafana_notification { 'channelname': grafana_url => 'http://localhost:3000', grafana_api_path => '/grafana/api', grafana_user => 'admin', grafana_password => '5ecretPassw0rd', name => 'channelname', type => 'email', is_default => false, send_reminder => false, frequency => '20m', settings => { addresses => "alerts@example.com; it@example.com" } } ``` `grafana_api_path` is only required if using sub-paths for the API Notification types and related settingsi (cf doc Grafana : https://github.com/grafana/grafana/blob/master/docs/sources/alerting/notifications.md ) : - email: - addresses: "example.com" - hipchat: - apikey : "0a0a0a0a0a0a0a0a0a0a0a" - autoResolve : true - httpMethod : "POST" - uploadImage : true - url : "https://grafana.hipchat.com" - kafka: - autoResolve : true - httpMethod : "POST" - kafkaRestProxy: "http://localhost:8082" - kafkaTopic : "topic1" - uploadImage : true - LINE: - autoResolve: true - httpMethod : "POST" - token : "token" - uploadImage: true - teams (Microsoft Teams): - autoResolve : true - httpMethod : "POST" - uploadImage :true - url : "http://example.com" - pagerduty: - autoResolve : true - httpMethod : POST - integrationKey :"0a0a0a0a0a" - uploadImage : true - prometheus-alertmanager: - autoResolve : true - httpMethod : "POST" - uploadImage : true - url : "http://localhost:9093" - sensu: - autoResolve : true - handler : "default", - httpMethod : "POST" - uploadImage : true - url : "http://sensu-api.local:4567/results" - slack: - autoResolve : true - httpMethod : "POST" - uploadImage : true - url : "http://slack.com/" - token : "0a0a0a0a0a0a0a0a0a0a0a" - threema: - api_secret : "0a0a0a0a0a0a0a0a0a0a0a" - autoResolve : true - gateway_id : "*3MAGWID" - httpMethod : "POST" - recipient_id: "YOUR3MID" - uploadImage : true - discord: - autoResolve : true, - httpMethod : "POST" - uploadImage : true - url : "https://example.com" - webhook: - autoResolve : true - httpMethod : "POST" - uploadImage : false - url : "http://localhost:8080" - telegram: - autoResolve : true - bottoken : "0a0a0a0a0a0a" - chatid : "789789789" - httpMethod : "POST" - uploadImage : true #### Provisioning Grafana [Grafana documentation on provisioning](http://docs.grafana.org/administration/provisioning/). This module will provision grafana by placing yaml files into `/etc/grafana/provisioning/datasources` and `/etc/grafana/provisioning/dashboards` by default. ##### Example datasource A puppet hash example for Prometheus. The module will place the hash as a yaml file into `/etc/gafana/provisioning/datasources/puppetprovisioned.yaml`. ```puppet class { 'grafana': provisioning_datasources => { apiVersion => 1, datasources => [ { name => 'Prometheus', type => 'prometheus', access => 'proxy', url => 'http://localhost:9090/prometheus', isDefault => true, }, ], } } ``` Here is the same configuration example as a hiera hash. ```yaml grafana::provisioning_datasources: apiVersion: 1 datasources: - name: 'Prometheus' type: 'prometheus' access: 'proxy' url: 'http://localhost:9090/prometheus' isDefault: true ``` ##### Example dashboard An example puppet hash for provisioning dashboards. The module will place the hash as a yaml file into `/etc/grafana/provisioning/dashboards/puppetprovisioned.yaml` by default. More details follow the examples. ```puppet class { 'grafana': provisioning_dashboards => { apiVersion => 1, providers => [ { name => 'default', orgId => 1, folder => '', type => 'file', disableDeletion => true, options => { path => '/var/lib/grafana/dashboards', puppetsource => 'puppet:///modules/my_custom_module/dashboards', }, }, ], } } ``` Here is the same configuraiton example as a hiera hash. ```yaml grafana::provisioning_dashboards: apiVersion: 1 providers: - name: 'default' orgId: 1 folder: '' type: file disableDeletion: true options: path: '/var/lib/grafana/dashboards' puppetsource: 'puppet:///modules/my_custom_module/dashboards' ``` In both examples above a non-grafana option named `puppetsource` has been used. When this module finds that the provisioning_dashboards hash contains keys `path` and `puppetsource` in the `options` subhash, it will do the following. * It will create the path found in `options['path']`. Note: puppet will only create the final directory of the path unless the parameter `create_subdirs_provisioning` is set to true: this defaults to false. * It will use `puppetsource` as the file resource's 'source' for the directory. * It removes the `puppetsource` key from the `options` subhash, so the subsequent yaml file for gafana does not contain this key. (The `path` key will remain.) This feature allows you to define a custom module, and place any dashboards you want provisioned in the its `files/` directory. In the example above you would put dashboards into `my_custom_module/files/dashboards` and puppet-grafana will create `/var/lib/grafana/dashboards` and provision it with the contents of `my_custom_module/files/dashboards`. Puppet's file resource may also be given a `file://` URI which may point to a locally available directory on the filesystem, typically the filesystem of the puppetserver/master. Thus you may specify a local directory with grafana dashboards you wish to provision into grafana. ##### Provisioning with dashboards from grafana.com GrafanaLabs provides lots of [dashboards that may be reused](https://grafana.com/grafana/dashboards). Those ones are **not directly usable** for provisioning (this is a Grafana issue, not a Puppet one). In order to have a "provisionable" dashboard in JSON format, you have to prepare it before adding it in your Puppet code. Here are the steps to follow: 1. Use a Grafana instance 1. Import the desired dashboard 1. Define its datasource 1. From the dashboard view: * Click the "Share dashboard" icon (top left corner of screen) * Select the "Export" tab, * Activate "Export for sharing externally" * Click "Save to file" 1. In the JSON file: * Remove the keys `__imports` and `__requires` * Replace all `${DS_PROMETHEUS}` by your datasource name 1. Once saved, you may place this JSON file in your `puppet:///modules/my_custom_module/dashboards` directory **Note:** This procedure have been tested with Grafana 6.x. It may not work for any dashboard, depending on how it's been coded. Dashboards known to be "provisionable": * [Node Exporter Server Metric](https://grafana.com/dashboards/405) * [Prometheus Blackbox Exporter](https://grafana.com/dashboards/7587) Dashboards known not to be "provisionable": * [HTTP Services Status](https://grafana.com/dashboards/4859) ## Tasks ### `change_grafana_admin_password` `old_password`: the old admin password `new_password`: the password you want to use for the admin user `uri`: `http` or `https` `port`: the port Grafana runs on locally This task can be used to change the password for the admin user in grafana ## Limitations This module has been tested on Ubuntu 14.04, using each of the 'archive', 'docker' and 'package' installation methods. Other configurations should work with minimal, if any, additional effort. ## Development This module is a fork of [bfraser/grafana](https://github.com/bfraser/puppet-grafana) maintained by [Vox Pupuli](https://voxpupuli.org/). Vox Pupuli welcomes new contributions to this module, especially those that include documentation and rspec tests. We are happy to provide guidance if necessary. Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for more details. ### Authors * Bill Fraser * Vox Pupuli Team ## Copyright and License Copyright (C) 2015 Bill Fraser Bill can be contacted at: fraser@pythian.com Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. diff --git a/REFERENCE.md b/REFERENCE.md new file mode 100644 index 0000000..f7eabcb --- /dev/null +++ b/REFERENCE.md @@ -0,0 +1,1556 @@ +# Reference + + + +## Table of Contents + +### Classes + +* [`grafana`](#grafana): == Class: grafana Installs and configures Grafana. === Parameters [*archive_source*] Download location of tarball to be used with the 'arch +* [`grafana::config`](#grafanaconfig): == Class grafana::config This class is called from grafana +* [`grafana::install`](#grafanainstall): == Class grafana::install +* [`grafana::service`](#grafanaservice): == Class grafana::service This class is meant to be called from grafana It ensure the service is running +* [`grafana::validator`](#grafanavalidator): Manage grafana_conn_validator resource + +### Resource types + +* [`grafana_conn_validator`](#grafana_conn_validator): Verify connectivity to the Grafana API +* [`grafana_dashboard`](#grafana_dashboard): Manage dashboards in Grafana +* [`grafana_dashboard_permission`](#grafana_dashboard_permission): Manage dashboard permissions in Grafana +* [`grafana_datasource`](#grafana_datasource): Manage datasources in Grafana +* [`grafana_folder`](#grafana_folder): Manage folders in Grafana +* [`grafana_ldap_config`](#grafana_ldap_config): Manage Grafana LDAP configuration +* [`grafana_ldap_group_mapping`](#grafana_ldap_group_mapping): Map an LDAP group to a Grafana role. +* [`grafana_ldap_server`](#grafana_ldap_server): Manage Grafana LDAP servers for LDAP authentication. +* [`grafana_membership`](#grafana_membership): Manage resource memberships in Grafana +* [`grafana_notification`](#grafana_notification): Manage notification in Grafana +* [`grafana_organization`](#grafana_organization): Manage organizations in Grafana +* [`grafana_plugin`](#grafana_plugin): manages grafana plugins +* [`grafana_team`](#grafana_team): Manage teams in Grafana +* [`grafana_user`](#grafana_user): Manage users in Grafana + +### Functions + +* [`grafana::deep_find_and_remove`](#grafanadeep_find_and_remove): == Function: deep_find_and_remove This function takes a hash as input, along with a string (key). Additionally, the optional removekey (defa +* [`grafana::get_sub_paths`](#grafanaget_sub_paths): == Function get_sub_paths This function receives an input path as an input parameter, and returns an array of the subpaths in the input, exc + +### Tasks + +* [`change_grafana_admin_password`](#change_grafana_admin_password): Change the Grafana admin user's password + +## Classes + +### `grafana` + +== Class: grafana + +Installs and configures Grafana. + +=== Parameters +[*archive_source*] +Download location of tarball to be used with the 'archive' install method. +Defaults to the URL of the latest version of Grafana available at the time of module release. + +[*container_cfg*] +Boolean. Determines whether a configuration file should be generated when using the 'docker' install method. +If true, use the `cfg` and `cfg_location` parameters to control creation of the file. +Defaults to false. + +[*container_params*] +Hash of parameters to use when creating the Docker container. For use with the 'docker' install method. +Refer to documentation of the `docker::run` resource in the `garethr-docker` module for details of available parameters. +Defaults to: + + container_params => { + 'image' => 'grafana/grafana:latest', + 'ports' => '3000' + } + +[*data_dir*] +The directory Grafana will use for storing its data. +Defaults to '/var/lib/grafana'. + +[*install_dir*] +Installation directory to be used with the 'archive' install method. +Defaults to '/usr/share/grafana'. + +[*install_method*] +Set to 'archive' to install Grafana using the tar archive. +Set to 'docker' to install Grafana using the official Docker container. +Set to 'package' to install Grafana using .deb or .rpm packages. +Set to 'repo' to install Grafana using an apt or yum repository. +Defaults to 'package'. + +[*manage_package_repo*] +If true this will setup the official grafana repositories on your host. Defaults to true. + +[*package_name*] +The name of the package managed with the 'package' install method. +Defaults to 'grafana'. + +[*package_source*] +Download location of package to be used with the 'package' install method. +Defaults to the URL of the latest version of Grafana available at the time of module release. + +[*service_name*] +The name of the service managed with the 'archive' and 'package' install methods. +Defaults to 'grafana-server'. + +[*version*] +The version of Grafana to install and manage. +Defaults to 'installed' + +[*repo_name*] +When using 'repo' install_method, the repo to look for packages in. +Set to 'stable' to install only stable versions +Set to 'beta' to install beta versions +Defaults to stable. + +[*plugins*] +A hash of plugins to be passed to `create_resources`, wraps around the +`grafana_plugin` resource. + +[*provisioning_dashboards*] +Hash of dashboards to provision into grafana. grafana > v5.0.0 +required. Hash will be converted into YAML and used by grafana to +provision dashboards. + +[*provisioning_datasources*] +Hash of datasources to provision into grafana, grafana > v5.0.0 +required. Hash will be converted into YAML and used by granfana to +configure datasources. + +[*provisioning_dashboards_file*] +String with the fully qualified path to place the provisioning file +for dashboards, only used if provisioning_dashboards is specified. +Defaults to '/etc/grafana/provisioning/dashboards/puppetprovisioned.yaml' + +[*provisioning_datasources_file*] +String with the fully qualified path to place the provisioning file +for datasources, only used if provisioning_datasources is specified. +Default to '/etc/grafana/provisioning/datasources/puppetprovisioned.yaml' + +[*create_subdirs_provisioning*] +Boolean, defaults to false. If true puppet will create any +subdirectories in the given path when provisioning dashboards. + +[*sysconfig_location*] +Location of the sysconfig file for the environment of the grafana-server service. +This is only used when the install_method is 'package' or 'repo'. + +[*sysconfig*] +A hash of environment variables for the grafana-server service + +Example: + sysconfig => { 'http_proxy' => 'http://proxy.example.com/' } + +[*ldap_servers*] +A hash of ldap_servers to be passed to `create_resources`, wraps around the +`grafana_ldap_server` resource. + +[*ldap_group_mappings*] +A hash of ldap_servers to be passed to `create_resources`, wraps around the +`grafana_ldap_group_mapping` resource. + +[*toml_manage_package*] +ruby-toml is required to generate the TOML-based LDAP config for Grafana. +Defaults to true. Set to false if you manage package- or gem-install +somewhere else. + +[*toml_package_name*] +Name of the software-package providing the TOML parser library. +Defaults to ruby-toml. + +[*toml_package_ensure*] +Ensure the package-resource - e.g. installed, absent, etc. +https://puppet.com/docs/puppet/latest/types/package.html#package-attribute-ensure +Defaults to present + +[*toml_package_provider*] +The package-provider used to install the TOML parser library. +Defaults to undef, to let Puppet decide. See +https://puppet.com/docs/puppet/latest/types/package.html#package-attribute-provider + +=== Examples + + class { '::grafana': + install_method => 'docker', + } + +#### Parameters + +The following parameters are available in the `grafana` class. + +##### `archive_source` + +Data type: `Optional[String]` + + + +##### `cfg_location` + +Data type: `String` + + + +##### `cfg` + +Data type: `Hash` + + + +##### `ldap_cfg` + +Data type: `Optional[Variant[Hash,Array]]` + + + +##### `container_cfg` + +Data type: `Boolean` + + + +##### `container_params` + +Data type: `Hash` + + + +##### `docker_image` + +Data type: `String` + + + +##### `docker_ports` + +Data type: `String` + + + +##### `data_dir` + +Data type: `String` + + + +##### `install_dir` + +Data type: `String` + + + +##### `install_method` + +Data type: `String` + + + +##### `manage_package_repo` + +Data type: `Boolean` + + + +##### `package_name` + +Data type: `String` + + + +##### `package_source` + +Data type: `Optional[String]` + + + +##### `repo_name` + +Data type: `Enum['stable', 'beta']` + + + +##### `rpm_iteration` + +Data type: `String` + + + +##### `service_name` + +Data type: `String` + + + +##### `version` + +Data type: `String` + + + +##### `plugins` + +Data type: `Hash` + + + +##### `provisioning_dashboards` + +Data type: `Hash` + + + +##### `provisioning_datasources` + +Data type: `Hash` + + + +##### `provisioning_dashboards_file` + +Data type: `String` + + + +##### `provisioning_datasources_file` + +Data type: `String` + + + +##### `create_subdirs_provisioning` + +Data type: `Boolean` + + + +##### `sysconfig_location` + +Data type: `Optional[String]` + + + +##### `sysconfig` + +Data type: `Optional[Hash]` + + + +##### `ldap_servers` + +Data type: `Hash[String[1], Hash]` + + + +##### `ldap_group_mappings` + +Data type: `Hash[String[1], Hash]` + + + +##### `toml_manage_package` + +Data type: `Boolean` + + + +##### `toml_package_name` + +Data type: `String[1]` + + + +##### `toml_package_ensure` + +Data type: `String[1]` + + + +##### `toml_package_provider` + +Data type: `Optional[String[1]]` + + + +### `grafana::config` + +== Class grafana::config + +This class is called from grafana + +### `grafana::install` + +== Class grafana::install + +### `grafana::service` + +== Class grafana::service + +This class is meant to be called from grafana +It ensure the service is running + +### `grafana::validator` + +Manage grafana_conn_validator resource + +#### Parameters + +The following parameters are available in the `grafana::validator` class. + +##### `grafana_url` + +Data type: `Stdlib::HTTPUrl` + +Grafana URL. + +Default value: `'http://localhost:3000'` + +##### `grafana_api_path` + +Data type: `Stdlib::Absolutepath` + +API path to validate with. + +Default value: `'/api/health'` + +## Resource types + +### `grafana_conn_validator` + +Verify connectivity to the Grafana API + +#### Properties + +The following properties are available in the `grafana_conn_validator` type. + +##### `ensure` + +Valid values: `present`, `absent` + +The basic property that the resource should be in. + +Default value: `present` + +#### Parameters + +The following parameters are available in the `grafana_conn_validator` type. + +##### `grafana_api_path` + +The absolute path to the API endpoint + +Default value: `/api/health` + +##### `grafana_url` + +The URL of the Grafana server + +Default value: `http://localhost:3000` + +##### `name` + +namevar + +Arbitrary name of this resource + +##### `provider` + +The specific backend to use for this `grafana_conn_validator` resource. You will seldom need to specify this --- Puppet +will usually discover the appropriate provider for your platform. + +##### `timeout` + +How long to wait for the API to be available + +Default value: `20` + +### `grafana_dashboard` + +Manage dashboards in Grafana + +#### Properties + +The following properties are available in the `grafana_dashboard` type. + +##### `content` + +The JSON representation of the dashboard. + +##### `ensure` + +Valid values: `present`, `absent` + +The basic property that the resource should be in. + +Default value: `present` + +#### Parameters + +The following parameters are available in the `grafana_dashboard` type. + +##### `folder` + +The folder to place the dashboard in (optional) + +##### `grafana_api_path` + +The absolute path to the API endpoint + +Default value: `/api` + +##### `grafana_password` + +The password for the Grafana server (optional) + +##### `grafana_url` + +The URL of the Grafana server + +Default value: `''` + +##### `grafana_user` + +The username for the Grafana server (optional) + +##### `organization` + +The organization name to create the datasource on + +Default value: `1` + +##### `provider` + +The specific backend to use for this `grafana_dashboard` resource. You will seldom need to specify this --- Puppet will +usually discover the appropriate provider for your platform. + +##### `title` + +The title of the dashboard. + +### `grafana_dashboard_permission` + +Manage dashboard permissions in Grafana + +#### Properties + +The following properties are available in the `grafana_dashboard_permission` type. + +##### `ensure` + +Valid values: `present`, `absent` + +The basic property that the resource should be in. + +Default value: `present` + +##### `permission` + +Valid values: `Admin`, `Edit`, `View` + +The role to apply + +#### Parameters + +The following parameters are available in the `grafana_dashboard_permission` type. + +##### `dashboard` + +Dashboard to modify permissions for + +##### `grafana_api_path` + +The absolute path to the API endpoint + +Default value: `/api` + +##### `grafana_password` + +The password for the Grafana server + +##### `grafana_url` + +The URL of the Grafana server + +Default value: `''` + +##### `grafana_user` + +The username for the Grafana server + +##### `name` + +namevar + +The name of the permission. + +##### `organization` + +The name of the organization to add permission for + +Default value: `Main Org.` + +##### `provider` + +The specific backend to use for this `grafana_dashboard_permission` resource. You will seldom need to specify this --- +Puppet will usually discover the appropriate provider for your platform. + +##### `team` + +Team to add the permission for + +##### `user` + +User to add the permission for + +### `grafana_datasource` + +Manage datasources in Grafana + +#### Properties + +The following properties are available in the `grafana_datasource` type. + +##### `access_mode` + +Valid values: `direct`, `proxy` + +Whether the datasource is accessed directly or not by the clients + +Default value: `direct` + +##### `basic_auth` + +Valid values: ``true``, ``false`` + +Whether basic auth is enabled or not + +Default value: `false` + +##### `basic_auth_password` + +The password for basic auth if enabled + +Default value: `''` + +##### `basic_auth_user` + +The username for basic auth if enabled + +Default value: `''` + +##### `database` + +The name of the database (optional) + +##### `ensure` + +Valid values: `present`, `absent` + +The basic property that the resource should be in. + +Default value: `present` + +##### `is_default` + +Valid values: ``true``, ``false`` + +Whether the datasource is the default one + +Default value: `false` + +##### `json_data` + +Additional JSON data to configure the datasource (optional) + +##### `password` + +The password for the datasource (optional) + +##### `secure_json_data` + +Additional secure JSON data to configure the datasource (optional) + +##### `type` + +The datasource type + +##### `url` + +The URL/Endpoint of the datasource + +##### `user` + +The username for the datasource (optional) + +##### `with_credentials` + +Valid values: ``true``, ``false`` + +Whether credentials such as cookies or auth headers should be sent with cross-site requests + +Default value: `false` + +#### Parameters + +The following parameters are available in the `grafana_datasource` type. + +##### `grafana_api_path` + +The absolute path to the API endpoint + +Default value: `/api` + +##### `grafana_password` + +The password for the Grafana server + +##### `grafana_url` + +The URL of the Grafana server + +Default value: `''` + +##### `grafana_user` + +The username for the Grafana server + +##### `name` + +namevar + +The name of the datasource. + +##### `organization` + +The organization name to create the datasource on + +Default value: `1` + +##### `provider` + +The specific backend to use for this `grafana_datasource` resource. You will seldom need to specify this --- Puppet will +usually discover the appropriate provider for your platform. + +### `grafana_folder` + +Manage folders in Grafana + +#### Properties + +The following properties are available in the `grafana_folder` type. + +##### `ensure` + +Valid values: `present`, `absent` + +The basic property that the resource should be in. + +Default value: `present` + +##### `permissions` + +The permissions of the folder + +#### Parameters + +The following parameters are available in the `grafana_folder` type. + +##### `grafana_api_path` + +The absolute path to the API endpoint + +Default value: `/api` + +##### `grafana_password` + +The password for the Grafana server (optional) + +##### `grafana_url` + +The URL of the Grafana server + +Default value: `''` + +##### `grafana_user` + +The username for the Grafana server (optional) + +##### `organization` + +The organization name to create the folder on + +Default value: `1` + +##### `provider` + +The specific backend to use for this `grafana_folder` resource. You will seldom need to specify this --- Puppet will +usually discover the appropriate provider for your platform. + +##### `title` + +The title of the folder + +##### `uid` + +UID of the folder + +### `grafana_ldap_config` + +Manage Grafana LDAP configuration + +#### Parameters + +The following parameters are available in the `grafana_ldap_config` type. + +##### `backup` + +Valid values: ``true``, ``false``, `yes`, `no` + +Backup existing files before replacing them into the file-bucket + +Default value: ``false`` + +##### `group` + +Group of the LDAP configuration file either as String or Integer (default: grafana) + +Default value: `grafana` + +##### `mode` + +File-permissions mode of the LDAP configuration file as String + +Default value: `0440` + +##### `owner` + +Owner of the LDAP configuration-file either as String or Integer (default: root) + +Default value: `root` + +##### `replace` + +Valid values: ``true``, ``false``, `yes`, `no` + +Replace existing files + +Default value: ``true`` + +##### `title` + +Path to ldap.toml + +##### `validate_cmd` + +A command to validate the new Grafana LDAP configuration before actually replacing it + +### `grafana_ldap_group_mapping` + +Map an LDAP group to a Grafana role. + +#### Parameters + +The following parameters are available in the `grafana_ldap_group_mapping` type. + +##### `grafana_admin` + +Valid values: ``true``, ``false``, `yes`, `no` + +Additonal flag for Grafana > v5.3 to signal admin-role to Grafana + +Default value: ``false`` + +##### `group_dn` + +The LDAP distinguished-name of the group + +##### `ldap_server_name` + +The LDAP server config to apply the group-mappings on + +##### `org_role` + +Valid values: `Admin`, `Editor`, `Viewer` + +The Grafana role the shall be assigned to this group + +##### `title` + +A unique identifier of the resource + +### `grafana_ldap_server` + +Manage Grafana LDAP servers for LDAP authentication. + +#### Parameters + +The following parameters are available in the `grafana_ldap_server` type. + +##### `attributes` + +Mapping LDAP attributes to their Grafana user-account-properties (optional) + +##### `bind_dn` + +If the LDAP server requires authentication (i.e. non-anonymous), provide the distinguished-name (dn) here (optional) + +##### `bind_password` + +If the LDAP server requires authentication (i.e. non-anonymous), provide the password (optional) + +##### `client_cert` + +If the LDAP server requires certificate-based authentication, specify the client's certificate (for TLS and SSL, +optional) + +##### `client_key` + +If the LDAP server requires certificate-based authentication, specify the client's certificate (for TLS and SSL, +optional) + +##### `group_search_base_dns` + +The base-dn to be used when querying LDAP for group-accounts (optional) + +##### `group_search_filter` + +A search-filter to be used when querying LDAP for group-accounts (optional) + +##### `group_search_filter_user_attribute` + +The attribute to be used to locate matching user-accounts in the group (optional) + +##### `hosts` + +The servers to perform LDAP authentication at + +##### `port` + +The port to connect at the LDAP servers (389 for TLS/plaintext, 636 for SSL [ldaps], optional) + +Default value: `389` + +##### `root_ca_cert` + +The root ca-certificate to verify the LDAP server's SSL certificate against (for TLS and SSL, optional) + +Default value: `/etc/ssl/certs/ca-certificates.crt` + +##### `search_base_dns` + +The one or more base-dn to be used when querying LDAP for user-accounts (optional) + +Default value: `[]` + +##### `search_filter` + +A search-filter to be used when querying LDAP for user-accounts (optional) + +##### `ssl_skip_verify` + +Valid values: ``true``, ``false``, `yes`, `no` + +Set to true to disable verification of the LDAP server's SSL certificate (for TLS and SSL, optional) + +Default value: ``false`` + +##### `start_tls` + +Valid values: ``true``, ``false``, `yes`, `no` + +Set to true if you want to perform LDAP via a TLS-connection (not meant to be for SSL, optional) + +Default value: ``true`` + +##### `title` + +A unique identified for this LDAP server. + +##### `use_ssl` + +Valid values: ``true``, ``false``, `yes`, `no` + +Set to true if you want to perform LDAP via a SSL-connection (not meant to be for TLS, optional) + +Default value: ``false`` + +### `grafana_membership` + +Manage resource memberships in Grafana + +#### Properties + +The following properties are available in the `grafana_membership` type. + +##### `ensure` + +Valid values: `present`, `absent` + +The basic property that the resource should be in. + +Default value: `present` + +##### `role` + +Valid values: `Admin`, `Editor`, `Viewer` + +The role to apply to the membership (Admin, Editor, Viewer) + +#### Parameters + +The following parameters are available in the `grafana_membership` type. + +##### `grafana_api_path` + +The absolute path to the API endpoint + +Default value: `/api` + +##### `grafana_password` + +The password for the Grafana server + +##### `grafana_url` + +The URL of the Grafana server + +Default value: `''` + +##### `grafana_user` + +The username for the Grafana server + +##### `membership_type` + +Valid values: `organization`, `team` + +The underlying type of the membership (organization, team) + +##### `name` + +namevar + +The name of the membership. + +##### `organization` + +The name of the organization to add membership for (team only) + +Default value: `Main Org.` + +##### `provider` + +The specific backend to use for this `grafana_membership` resource. You will seldom need to specify this --- Puppet will +usually discover the appropriate provider for your platform. + +##### `target_name` + +The name of the target to add membership for + +##### `user_name` + +The name of the user to add membership for + +### `grafana_notification` + +Manage notification in Grafana + +#### Properties + +The following properties are available in the `grafana_notification` type. + +##### `ensure` + +Valid values: `present`, `absent` + +The basic property that the resource should be in. + +Default value: `present` + +##### `frequency` + +The notification reminder frequency + +##### `is_default` + +Valid values: ``true``, ``false`` + +Whether the notification is the default one + +Default value: `false` + +##### `send_reminder` + +Valid values: ``true``, ``false`` + +Whether automatic message resending is enabled or not + +Default value: `false` + +##### `settings` + +Additional JSON data to configure the notification + +##### `type` + +The notification type + +#### Parameters + +The following parameters are available in the `grafana_notification` type. + +##### `grafana_api_path` + +The absolute path to the API endpoint + +Default value: `/api` + +##### `grafana_password` + +The password for the Grafana server + +##### `grafana_url` + +The URL of the Grafana server + +Default value: `''` + +##### `grafana_user` + +The username for the Grafana server + +##### `name` + +namevar + +The name of the notification. + +##### `provider` + +The specific backend to use for this `grafana_notification` resource. You will seldom need to specify this --- Puppet +will usually discover the appropriate provider for your platform. + +### `grafana_organization` + +Manage organizations in Grafana + +#### Properties + +The following properties are available in the `grafana_organization` type. + +##### `address` + +Additional JSON data to configure the organization address (optional) + +##### `ensure` + +Valid values: `present`, `absent` + +The basic property that the resource should be in. + +Default value: `present` + +##### `id` + +The ID of the organization + +#### Parameters + +The following parameters are available in the `grafana_organization` type. + +##### `grafana_api_path` + +The absolute path to the API endpoint + +Default value: `/api` + +##### `grafana_password` + +The password for the Grafana server + +##### `grafana_url` + +The URL of the Grafana server + +Default value: `''` + +##### `grafana_user` + +The username for the Grafana server + +##### `name` + +namevar + +The name of the organization. + +##### `provider` + +The specific backend to use for this `grafana_organization` resource. You will seldom need to specify this --- Puppet +will usually discover the appropriate provider for your platform. + +### `grafana_plugin` + +manages grafana plugins + +#### Examples + +##### Install a grafana plugin + +```puppet +grafana_plugin { 'grafana-simple-json-datasource': } +``` + +##### Install a grafana plugin from different repo + +```puppet +grafana_plugin { 'grafana-simple-json-datasource': + ensure => present, + repo => 'https://nexus.company.com/grafana/plugins', +} +``` + +##### Uninstall a grafana plugin + +```puppet +grafana_plugin { 'grafana-simple-json-datasource': + ensure => absent, +} +``` + +##### Show resources + +```puppet +$ puppet resource grafana_plugin +``` + +#### Properties + +The following properties are available in the `grafana_plugin` type. + +##### `ensure` + +Valid values: `present`, `absent` + +The basic property that the resource should be in. + +Default value: `present` + +#### Parameters + +The following parameters are available in the `grafana_plugin` type. + +##### `name` + +Valid values: `%r{^\S+$}` + +namevar + +The name of the plugin to enable + +##### `provider` + +The specific backend to use for this `grafana_plugin` resource. You will seldom need to specify this --- Puppet will +usually discover the appropriate provider for your platform. + +##### `repo` + +The URL of an internal plugin server + +### `grafana_team` + +Manage teams in Grafana + +#### Properties + +The following properties are available in the `grafana_team` type. + +##### `ensure` + +Valid values: `present`, `absent` + +The basic property that the resource should be in. + +Default value: `present` + +##### `home_dashboard` + +The id or name of the home dashboard + +##### `theme` + +The theme to use for the team + +##### `timezone` + +The timezone to use for the team + +#### Parameters + +The following parameters are available in the `grafana_team` type. + +##### `email` + +The email for the team + +Default value: `''` + +##### `grafana_api_path` + +The absolute path to the API endpoint + +Default value: `/api` + +##### `grafana_password` + +The password for the Grafana server + +##### `grafana_url` + +The URL of the Grafana server + +Default value: `''` + +##### `grafana_user` + +The username for the Grafana server + +##### `name` + +namevar + +The name of the team + +##### `organization` + +The organization the team belongs to + +##### `provider` + +The specific backend to use for this `grafana_team` resource. You will seldom need to specify this --- Puppet will +usually discover the appropriate provider for your platform. + +### `grafana_user` + +Manage users in Grafana + +#### Properties + +The following properties are available in the `grafana_user` type. + +##### `email` + +The email for the user + +##### `ensure` + +Valid values: `present`, `absent` + +The basic property that the resource should be in. + +Default value: `present` + +##### `is_admin` + +Valid values: ``true``, ``false`` + +Whether the user is a grafana admin + +Default value: `false` + +##### `password` + +The password for the user + +##### `theme` + +The theme for the user + +#### Parameters + +The following parameters are available in the `grafana_user` type. + +##### `full_name` + +The full name of the user. + +##### `grafana_api_path` + +The absolute path to the API endpoint + +Default value: `/api` + +##### `grafana_password` + +The password for the Grafana server + +##### `grafana_url` + +The URL of the Grafana server + +Default value: `''` + +##### `grafana_user` + +The username for the Grafana server + +##### `name` + +namevar + +The username of the user. + +##### `provider` + +The specific backend to use for this `grafana_user` resource. You will seldom need to specify this --- Puppet will +usually discover the appropriate provider for your platform. + +## Functions + +### `grafana::deep_find_and_remove` + +Type: Ruby 4.x API + +== Function: deep_find_and_remove + +This function takes a hash as input, along with a string +(key). Additionally, the optional removekey (defaults to +'puppetsource') is a parameter. + +The purpose of this function is to extract the 'options' subhash +from the array of hashes given to provision dashboards. This options +subhash may contain a path and source which puppet will use for +provisioning: creating the path and applying the files from the +source. + +Additionally, if the key 'puppetsource' exists in the sub-hash, it +will be deleted from the structure. Thus the output of this function +may be used in yaml format for grafana's provisioning +configuration file for dashboards. + +#### `grafana::deep_find_and_remove(String $key, Hash $object, Optional[String] $removekey)` + +== Function: deep_find_and_remove + +This function takes a hash as input, along with a string +(key). Additionally, the optional removekey (defaults to +'puppetsource') is a parameter. + +The purpose of this function is to extract the 'options' subhash +from the array of hashes given to provision dashboards. This options +subhash may contain a path and source which puppet will use for +provisioning: creating the path and applying the files from the +source. + +Additionally, if the key 'puppetsource' exists in the sub-hash, it +will be deleted from the structure. Thus the output of this function +may be used in yaml format for grafana's provisioning +configuration file for dashboards. + +Returns: `Array` + +##### `key` + +Data type: `String` + + + +##### `object` + +Data type: `Hash` + + + +##### `removekey` + +Data type: `Optional[String]` + + + +### `grafana::get_sub_paths` + +Type: Ruby 4.x API + +== Function get_sub_paths + +This function receives an input path as an input parameter, and +returns an array of the subpaths in the input, excluding the input +path itself. The function will attempt to ignore any extra slashes +in the path given. + +This function will only work on UNIX paths with forward slashes (/). + +Examples: +input = '/var/lib/grafana/dashboards' +output = [ '/var', '/var/lib', '/var/lib/grafana'/ ] + +input = '/opt' +output = [] + +input = '/first/second/' +output = [ '/first' ] + +#### `grafana::get_sub_paths(String $inputpath)` + +== Function get_sub_paths + +This function receives an input path as an input parameter, and +returns an array of the subpaths in the input, excluding the input +path itself. The function will attempt to ignore any extra slashes +in the path given. + +This function will only work on UNIX paths with forward slashes (/). + +Examples: +input = '/var/lib/grafana/dashboards' +output = [ '/var', '/var/lib', '/var/lib/grafana'/ ] + +input = '/opt' +output = [] + +input = '/first/second/' +output = [ '/first' ] + +Returns: `Array` + +##### `inputpath` + +Data type: `String` + + + +## Tasks + +### `change_grafana_admin_password` + +Change the Grafana admin user's password + +**Supports noop?** false + +#### Parameters + +##### `old_password` + +Data type: `Optional[String[1]]` + +The old admin password + +##### `new_password` + +Data type: `Optional[String[1]]` + +The new admin password + +##### `uri` + +Data type: `Enum['http','https']` + +http or https + +##### `port` + +Data type: `Integer` + +The port Grafana is running on + diff --git a/Rakefile b/Rakefile index b450fe7..d1bf749 100644 --- a/Rakefile +++ b/Rakefile @@ -1,54 +1,61 @@ -require 'voxpupuli/test/rake' +# Attempt to load voxupuli-test (which pulls in puppetlabs_spec_helper), +# otherwise attempt to load it directly. +begin + require 'voxpupuli/test/rake' +rescue LoadError + require 'puppetlabs_spec_helper/rake_tasks' +end # load optional tasks for releases # only available if gem group releases is installed begin require 'voxpupuli/release/rake_tasks' rescue LoadError end desc "Run main 'test' task and report merged results to coveralls" task test_with_coveralls: [:test] do if Dir.exist?(File.expand_path('../lib', __FILE__)) require 'coveralls/rake/task' Coveralls::RakeTask.new Rake::Task['coveralls:push'].invoke else puts 'Skipping reporting to coveralls. Module has no lib dir' end end desc 'Generate REFERENCE.md' task :reference, [:debug, :backtrace] do |t, args| patterns = '' Rake::Task['strings:generate:reference'].invoke(patterns, args[:debug], args[:backtrace]) end begin require 'github_changelog_generator/task' + require 'puppet_blacksmith' GitHubChangelogGenerator::RakeTask.new :changelog do |config| version = (Blacksmith::Modulefile.new).version config.future_release = "v#{version}" if version =~ /^\d+\.\d+.\d+$/ config.header = "# Changelog\n\nAll notable changes to this project will be documented in this file.\nEach new release typically also includes the latest modulesync defaults.\nThese should not affect the functionality of the module." config.exclude_labels = %w{duplicate question invalid wontfix wont-fix modulesync skip-changelog} config.user = 'voxpupuli' metadata_json = File.join(File.dirname(__FILE__), 'metadata.json') metadata = JSON.load(File.read(metadata_json)) config.project = metadata['name'] end # Workaround for https://github.com/github-changelog-generator/github-changelog-generator/issues/715 require 'rbconfig' if RbConfig::CONFIG['host_os'] =~ /linux/ task :changelog do puts 'Fixing line endings...' changelog_file = File.join(__dir__, 'CHANGELOG.md') changelog_txt = File.read(changelog_file) new_contents = changelog_txt.gsub(%r{\r\n}, "\n") File.open(changelog_file, "w") {|file| file.puts new_contents } end end rescue LoadError end # vim: syntax=ruby diff --git a/data/family/Suse.yaml b/data/family/Suse.yaml new file mode 100644 index 0000000..dba995b --- /dev/null +++ b/data/family/Suse.yaml @@ -0,0 +1,3 @@ +--- +grafana::install_method: 'package' +grafana::sysconfig_location: '/etc/sysconfig/grafana-server' diff --git a/lib/puppet/provider/grafana_dashboard/grafana.rb b/lib/puppet/provider/grafana_dashboard/grafana.rb index 0add4c2..bdfd77e 100644 --- a/lib/puppet/provider/grafana_dashboard/grafana.rb +++ b/lib/puppet/provider/grafana_dashboard/grafana.rb @@ -1,167 +1,172 @@ # Copyright 2015 Mirantis, Inc. # require 'json' require File.expand_path(File.join(File.dirname(__FILE__), '..', 'grafana')) # Note: this class doesn't implement the self.instances and self.prefetch # methods because the Grafana API doesn't allow to retrieve the dashboards and # all their properties in a single call. Puppet::Type.type(:grafana_dashboard).provide(:grafana, parent: Puppet::Provider::Grafana) do desc 'Support for Grafana dashboards stored into Grafana' defaultfor kernel: 'Linux' def organization resource[:organization] end def grafana_api_path resource[:grafana_api_path] end def fetch_organizations response = send_request('GET', format('%s/orgs', resource[:grafana_api_path])) if response.code != '200' raise format('Fail to retrieve organizations (HTTP response: %s/%s)', response.code, response.body) end begin fetch_organizations = JSON.parse(response.body) fetch_organizations.map { |x| x['id'] }.map do |id| response = send_request 'GET', format('%s/orgs/%s', resource[:grafana_api_path], id) if response.code != '200' raise format('Failed to retrieve organization %d (HTTP response: %s/%s)', id, response.code, response.body) end fetch_organization = JSON.parse(response.body) { id: fetch_organization['id'], name: fetch_organization['name'] } end rescue JSON::ParserError raise format('Failed to parse response: %s', response.body) end end def fetch_organization unless @fetch_organization @fetch_organization = if resource[:organization].is_a?(Numeric) || resource[:organization].match(%r{^[0-9]*$}) fetch_organizations.find { |x| x[:id] == resource[:organization] } else fetch_organizations.find { |x| x[:name] == resource[:organization] } end end @fetch_organization end def folders response = send_request('GET', format('%s/folders', resource[:grafana_api_path])) if response.code != '200' raise format('Fail to retrieve the folders (HTTP response: %s/%s)', response.code, response.body) end begin @folders = JSON.parse(response.body) rescue JSON::ParserError raise format('Fail to parse folders (HTTP response: %s/%s)', response.code, response.body) end end def find_folder folders unless @folders begin @folders.each do |folder| @folder = folder if folder['title'] == resource[:folder] end raise format('Folder not found: %s', resource[:folder]) unless @folder['title'] == resource[:folder] rescue JSON::ParserError raise format('Fail to parse folder %s: %s', resource[:folder], response.body) end end # Return the list of dashboards def dashboards + # change organizations + response = send_request 'POST', format('%s/user/using/%s', resource[:grafana_api_path], fetch_organization[:id]) + unless response.code == '200' + raise format('Failed to switch to org %s (HTTP response: %s/%s)', fetch_organization[:id], response.code, response.body) + end response = send_request('GET', format('%s/search', resource[:grafana_api_path]), nil, q: '', starred: false) if response.code != '200' raise format('Fail to retrieve the dashboards (HTTP response: %s/%s)', response.code, response.body) end begin JSON.parse(response.body) rescue JSON::ParserError raise format('Fail to parse dashboards (HTTP response: %s/%s)', response.code, response.body) end end # Return the dashboard matching with the resource's title def find_dashboard return unless dashboards.find { |x| x['title'] == resource[:title] } response = send_request('GET', format('%s/dashboards/db/%s', resource[:grafana_api_path], slug)) if response.code != '200' raise format('Fail to retrieve dashboard %s (HTTP response: %s/%s)', resource[:title], response.code, response.body) end begin # Cache the dashboard's content @dashboard = JSON.parse(response.body)['dashboard'] rescue JSON::ParserError raise format('Fail to parse dashboard %s: %s', resource[:title], response.body) end end def save_dashboard(dashboard) find_folder if resource[:folder] # change organizations response = send_request 'POST', format('%s/user/using/%s', resource[:grafana_api_path], fetch_organization[:id]) unless response.code == '200' raise format('Failed to switch to org %s (HTTP response: %s/%s)', fetch_organization[:id], response.code, response.body) end data = { dashboard: dashboard.merge('title' => resource[:title], 'id' => @dashboard ? @dashboard['id'] : nil, 'version' => @dashboard ? @dashboard['version'] + 1 : 0), folderId: @folder ? @folder['id'] : nil, overwrite: !@dashboard.nil? } response = send_request('POST', format('%s/dashboards/db', resource[:grafana_api_path]), data) return unless (response.code != '200') && (response.code != '412') raise format('Fail to save dashboard %s (HTTP response: %s/%s', resource[:name], response.code, response.body) end def slug resource[:title].downcase.gsub(%r{[ \+]+}, '-').gsub(%r{[^\w\- ]}, '') end def content @dashboard.reject { |k, _| k =~ %r{^id|version|title$} } end def content=(value) save_dashboard(value) end def create save_dashboard(resource[:content]) end def destroy response = send_request('DELETE', format('%s/dashboards/db/%s', resource[:grafana_api_path], slug)) return unless response.code != '200' raise Puppet::Error, format('Failed to delete dashboard %s (HTTP response: %s/%s', resource[:title], response.code, response.body) end def exists? find_dashboard end end diff --git a/lib/puppet/provider/grafana_team/grafana.rb b/lib/puppet/provider/grafana_team/grafana.rb index 3719347..37ca5bb 100644 --- a/lib/puppet/provider/grafana_team/grafana.rb +++ b/lib/puppet/provider/grafana_team/grafana.rb @@ -1,233 +1,231 @@ # frozen_string_literal: true require 'json' require File.expand_path(File.join(File.dirname(__FILE__), '..', 'grafana')) Puppet::Type.type(:grafana_team).provide(:grafana, parent: Puppet::Provider::Grafana) do desc 'Support for Grafana permissions' defaultfor kernel: 'Linux' def raise_on_error(code, message) raise message if code != '200' end def parse_response(data) JSON.parse(data) rescue JSON::ParserError raise format('Fail to parse response: %s', response.body) end def map_organizations(ids) ids.map do |id| response = send_request 'GET', format('%s/orgs/%s', resource[:grafana_api_path], id) raise_on_error(response.code, format('Failed to retrieve organization %d (HTTP response: %s/%s)', id, response.code, response.body)) organization = parse_response(response.body) { id: organization['id'], name: organization['name'] } end end def organizations response = send_request('GET', format('%s/orgs', resource[:grafana_api_path])) raise_on_error(response.code, format('Fail to retrieve organizations (HTTP response: %s/%s)', response.code, response.body)) organizations = JSON.parse(response.body) map_organizations(organizations.map { |x| x['id'] }) end def organization return @organization if @organization - org = resource[:organization] || resource[:target_name] + org = resource[:organization] key = org.is_a?(Numeric) || org.match(%r{/^[0-9]*$/}) ? :id : :name @organization = organizations.find { |x| x[key] == org } end def map_teams(teams) teams['teams'].map do |team| { id: team['id'], name: team['name'], organization: team['orgId'], membercount: team['membercount'], permission: team['permission'], email: team['email'] } end end def teams return [] unless organization set_current_organization response = send_request('GET', format('%s/teams/search', resource[:grafana_api_path])) raise_on_error(response.code, format('Fail to retrieve teams (HTTP response: %s/%s)', response.code, response.body)) teams = parse_response(response.body) map_teams(teams) end def team @team ||= teams.find { |x| x[:name] == resource[:name] } end def map_preferences(preferences) { theme: preferences['theme'], home_dashboard: preferences['homeDashboardId'], timezone: preferences['timezone'] } end def preferences team unless @team return if @preferences response = send_request('GET', format('%s/teams/%s/preferences', resource[:grafana_api_path], @team[:id])) raise_on_error(response.code, format('Fail to retrieve teams (HTTP response: %s/%s)', response.code, response.body)) preferences = parse_response(response.body) @preferences = map_preferences(preferences) end def setup_save_preferences_data endpoint = format('%s/teams/%s/preferences', resource[:grafana_api_path], @team[:id]) dash = get_dashboard(resource[:home_dashboard]) request_data = { theme: resource[:theme], homeDashboardId: dash[:id], timezone: resource[:timezone] } ['PUT', endpoint, request_data] end def save_preferences team unless @team set_current_organization setup_save_preferences_data response = send_request(*setup_save_preferences_data) # TODO: Raise on error? return if response.code == '200' || response.code == '412' raise format('Failed to update team %s, (HTTP response: %s/%s)', resource, response.code, response.body) end def set_current_organization response = send_request 'POST', format('%s/user/using/%s', resource[:grafana_api_path], organization[:id]) return if response.code == '200' raise format('Failed to switch to org %s (HTTP response: %s/%s)', organization[:id], response.code, response.body) end def home_dashboard preferences unless @preferences dash = get_dashboard(@preferences[:home_dashboard]) return dash[:name] if dash nil end def home_dashboard=(value) resource[:home_dashboard] = value save_preferences end def setup_search_path(ident) if ident.is_a?(Numeric) || ident.match(%r{/^[0-9]*$/}) { dashboardIds: ident, type: 'dash-db' } else { query: ident, type: 'dash-db' } end end def get_dashboard(ident) set_current_organization return { id: 0, name: 'Default' } if ident == 0 # rubocop:disable Style/NumericPredicate search_path = setup_search_path(ident) response = send_request('GET', format('%s/search', resource[:grafana_api_path]), nil, search_path) raise_on_error(response.code, format('Fail to retrieve dashboars (HTTP response: %s/%s)', response.code, response.body)) dashboard = parse_response(response.body) format_dashboard(dashboard) end def format_dashboard(dashboard) return { id: 0, name: 'Default' } unless dashboard.first { id: dashboard.first['id'], name: dashboard.first['title'] } end def theme preferences unless @preferences return @preferences[:theme] if @preferences nil end def theme=(value) resource[:theme] = value save_preferences end def timezone preferences unless @preferences return @preferences[:timezone] if @preferences nil end def timezone=(value) resource[:timezone] = value save_preferences end def setup_save_team_data verb = 'POST' endpoint = format('%s/teams', resource[:grafana_api_path]) request_data = { name: resource[:name], email: resource[:email] } if exists? verb = 'PUT' endpoint = format('%s/teams/%s', resource[:grafana_api_path], @team[:id]) end [verb, endpoint, request_data] end def save_team set_current_organization response = send_request(*setup_save_team_data) raise_on_error(response.code, format('Failed to update team %s, (HTTP response: %s/%s)', resource, response.code, response.body)) end def create save_team save_preferences end def destroy return unless team response = send_request('DELETE', format('%s/teams/%s', resource[:grafana_api_path], @team[:id])) - return unless response.code != '200' - - raise Puppet::Error, format('Failed to delete team %s (HTTP response: %s/%s)', resource, response.code, response.body) + raise_on_error(response.code, format('Failed to delete team %s (HTTP response: %s/%s)', resource, response.code, response.body)) end def exists? team return true if @team && @team[:name] == resource[:name] false end end diff --git a/lib/puppet/type/grafana_dashboard_permission.rb b/lib/puppet/type/grafana_dashboard_permission.rb index b626414..2642103 100644 --- a/lib/puppet/type/grafana_dashboard_permission.rb +++ b/lib/puppet/type/grafana_dashboard_permission.rb @@ -1,87 +1,91 @@ # frozen_string_literal: true Puppet::Type.newtype(:grafana_dashboard_permission) do @doc = 'Manage dashboard permissions in Grafana' ensurable newparam(:name, namevar: true) do desc 'The name of the permission.' end newparam(:user) do desc 'User to add the permission for' validate do |value| raise ArgumentError, 'Only user or team can be set, not both' if value && @resource[:team] end end newparam(:team) do desc 'Team to add the permission for' validate do |value| raise ArgumentError, 'Only user or team can be set, not both' if value && @resource[:user] end end newparam(:dashboard) do desc 'Dashboard to modify permissions for' end newparam(:organization) do desc 'The name of the organization to add permission for' defaultto 'Main Org.' end newparam(:grafana_api_path) do desc 'The absolute path to the API endpoint' defaultto '/api' validate do |value| raise ArgumentError, format('%s is not a valid API path', value) unless value =~ %r{^/.*/?api$} end end newparam(:grafana_url) do desc 'The URL of the Grafana server' defaultto '' validate do |value| raise ArgumentError, format('%s is not a valid URL', value) unless value =~ %r{^https?://} end end newparam(:grafana_user) do desc 'The username for the Grafana server' end newparam(:grafana_password) do desc 'The password for the Grafana server' end newproperty(:permission) do desc 'The role to apply' newvalues(:Admin, :Edit, :View) end autorequire(:service) do 'grafana-server' end autorequire(:grafana_organization) do catalog.resources.select { |r| r.is_a?(Puppet::Type.type(:grafana_organization)) } end autorequire(:grafana_user) do catalog.resources.select { |r| r.is_a?(Puppet::Type.type(:grafana_user)) } end autorequire(:grafana_team) do catalog.resources.select { |r| r.is_a?(Puppet::Type.type(:grafana_team)) } end autorequire(:grafana_dashboard) do catalog.resources.select { |r| r.is_a?(Puppet::Type.type(:grafana_dashboard)) } end + + autorequire(:grafana_conn_validator) do + 'grafana' + end end diff --git a/lib/puppet/type/grafana_ldap_config.rb b/lib/puppet/type/grafana_ldap_config.rb index f007d32..23b4f04 100644 --- a/lib/puppet/type/grafana_ldap_config.rb +++ b/lib/puppet/type/grafana_ldap_config.rb @@ -1,184 +1,185 @@ require 'toml' +require 'puppet/parameter/boolean' Puppet::Type.newtype(:grafana_ldap_config) do @doc = 'Manage Grafana LDAP configuration' @toml_header = <<-EOF # # Grafana LDAP configuration # # generated by Puppet module puppet-grafana # https://github.com/voxpupuli/puppet-grafana # # *** Edit at your own peril *** # # ############################################# # EOF # currently not ensurable as we are not parsing the LDAP toml config. # ensurable newparam(:title, namevar: true) do desc 'Path to ldap.toml' validate do |value| raise ArgumentError, _('name/title must be a String') unless value.is_a?(String) end end newparam(:owner) do desc 'Owner of the LDAP configuration-file either as String or Integer (default: root)' defaultto 'root' validate do |value| raise ArgumentError, _('owner must be a String or Integer') unless value.is_a?(String) || value.is_a?(Integer) end end newparam(:group) do desc 'Group of the LDAP configuration file either as String or Integer (default: grafana)' defaultto 'grafana' validate do |value| raise ArgumentError, _('group must be a String or Integer') unless value.is_a?(String) || value.is_a?(Integer) end end newparam(:mode) do desc 'File-permissions mode of the LDAP configuration file as String' defaultto '0440' validate do |value| raise ArgumentError, _('file-permissions must be a String') unless value.is_a?(String) raise ArgumentError, _('file-permissions must be a String') if value.empty? # regex-pattern stolen from here - all credis to them! # https://github.com/puppetlabs/puppetlabs-stdlib/blob/master/types/filemode.pp # currently disabled, as it fails when implicitly called. # # raise ArgumentError, _('file-permissions is not valid') unless value.to_s.match(%r{/\A(([0-7]{1,4})|(([ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=][0-7]+)(,([ugoa]*([-+=]([rwxXst]*|[ugo]))+|[-+=][0-7]+))*))\z/}) end end newparam(:replace, boolean: true, parent: Puppet::Parameter::Boolean) do desc 'Replace existing files' defaultto true end newparam(:backup, boolean: true, parent: Puppet::Parameter::Boolean) do desc 'Backup existing files before replacing them into the file-bucket' defaultto false end newparam(:validate_cmd) do desc 'A command to validate the new Grafana LDAP configuration before actually replacing it' validate do |value| raise ArgumentError, _('validate_cmd must be a String or undef') unless value.nil? || value.is_a?(String) end end def ldap_servers catalog.resources.each_with_object({}) do |resource, memo| next unless resource.is_a?(Puppet::Type.type(:grafana_ldap_server)) next unless resource[:name].is_a?(String) memo[resource[:name]] = resource memo end end def should_content return @generated_config if @generated_config @generated_config = {} ldap_servers.each do |server_k, server_v| # convert symbols to strings server_params = Hash[server_v.original_parameters.map { |k, v| [k.to_s, v] }] server_attributes = server_params['attributes'] server_params.delete('attributes') # grafana-syntax for multiple hosts is a space-separate list. server_params['host'] = server_params['hosts'].join(' ') server_params.delete('hosts') server_group_mappings = server_v.group_mappings server_block = { 'servers' => [server_params], 'servers.attributes' => server_attributes, 'servers.group_mappings' => server_group_mappings }.compact @generated_config[server_k] = server_block end @generated_config.compact end def generate file_opts = {} # currently not ensurable # file_opts = { # ensure: (self[:ensure] == :absent) ? :absent : :file, # } [:name, :owner, :group, :mode, :replace, :backup, # this we have currently not implemented # :selinux_ignore_defaults, # :selrange, # :selrole, # :seltype, # :seluser, # :show_diff, :validate_cmd].each do |param| file_opts[param] = self[param] unless self[param].nil? end metaparams = Puppet::Type.metaparams - excluded_metaparams = ['before', 'notify', 'require', 'subscribe', 'tag'] + excluded_metaparams = %w[before notify require subscribe tag] metaparams.reject! { |param| excluded_metaparams.include? param } metaparams.each do |metaparam| file_opts[metaparam] = self[metaparam] unless self[metaparam].nil? end [Puppet::Type.type(:file).new(file_opts)] end def eval_generate ldap_servers = should_content if !ldap_servers.nil? && !ldap_servers.empty? toml_contents = [] toml_contents << @toml_header toml_contents << ldap_servers.map do |k, v| str = [] str << "\n\n" str << <<-EOF # # #{k} # EOF str << TOML::Generator.new(v).body str.join end catalog.resource("File[#{self[:name]}]")[:content] = toml_contents.join end [catalog.resource("File[#{self[:name]}]")] end autonotify(:class) do 'grafana::service' end end diff --git a/lib/puppet/type/grafana_membership.rb b/lib/puppet/type/grafana_membership.rb index a49eecc..a391d2a 100644 --- a/lib/puppet/type/grafana_membership.rb +++ b/lib/puppet/type/grafana_membership.rb @@ -1,76 +1,80 @@ # frozen_string_literal: true Puppet::Type.newtype(:grafana_membership) do @doc = 'Manage resource memberships in Grafana' ensurable newparam(:name, namevar: true) do desc 'The name of the membership.' end newparam(:user_name) do desc 'The name of the user to add membership for' end newparam(:target_name) do desc 'The name of the target to add membership for' end newparam(:organization) do desc 'The name of the organization to add membership for (team only)' defaultto 'Main Org.' end newparam(:grafana_api_path) do desc 'The absolute path to the API endpoint' defaultto '/api' validate do |value| raise ArgumentError, format('%s is not a valid API path', value) unless value =~ %r{^/.*/?api$} end end newparam(:grafana_url) do desc 'The URL of the Grafana server' defaultto '' validate do |value| raise ArgumentError, format('%s is not a valid URL', value) unless value =~ %r{^https?://} end end newparam(:grafana_user) do desc 'The username for the Grafana server' end newparam(:grafana_password) do desc 'The password for the Grafana server' end newparam(:membership_type) do desc 'The underlying type of the membership (organization, team)' newvalues(:organization, :team) end newproperty(:role) do desc 'The role to apply to the membership (Admin, Editor, Viewer)' newvalues(:Admin, :Editor, :Viewer) end autorequire(:service) do 'grafana-server' end autorequire(:grafana_organization) do catalog.resources.select { |r| r.is_a?(Puppet::Type.type(:grafana_organization)) } end autorequire(:grafana_team) do catalog.resources.select { |r| r.is_a?(Puppet::Type.type(:grafana_team)) } end autorequire(:grafana_membership) do catalog.resources.select { |r| r.is_a?(Puppet::Type.type(:grafana_membership)) && r['membership_type'] == :organization } if self[:membership_type] == :team end + + autorequire(:grafana_conn_validator) do + 'grafana' + end end diff --git a/lib/puppet/type/grafana_team.rb b/lib/puppet/type/grafana_team.rb index e99c915..b623173 100644 --- a/lib/puppet/type/grafana_team.rb +++ b/lib/puppet/type/grafana_team.rb @@ -1,74 +1,80 @@ # frozen_string_literal: true Puppet::Type.newtype(:grafana_team) do @doc = 'Manage teams in Grafana' ensurable newparam(:name, namevar: true) do desc 'The name of the team' end newparam(:grafana_api_path) do desc 'The absolute path to the API endpoint' defaultto '/api' validate do |value| unless value =~ %r{^/.*/?api$} raise ArgumentError, format('%s is not a valid API path', value) end end end newparam(:grafana_url) do desc 'The URL of the Grafana server' defaultto '' validate do |value| unless value =~ %r{^https?://} raise ArgumentError, format('%s is not a valid URL', value) end end end newparam(:grafana_user) do desc 'The username for the Grafana server' end newparam(:grafana_password) do desc 'The password for the Grafana server' end newparam(:organization) do desc 'The organization the team belongs to' + defaultto 'Main Org.' end newparam(:email) do desc 'The email for the team' defaultto '' end newproperty(:home_dashboard) do desc 'The id or name of the home dashboard' + defaultto 'Default' end newproperty(:theme) do desc 'The theme to use for the team' end newproperty(:timezone) do desc 'The timezone to use for the team' end autorequire(:service) do 'grafana-server' end autorequire(:grafana_dashboard) do catalog.resources.select { |r| r.is_a?(Puppet::Type.type(:grafana_dashboard)) } end autorequire(:grafana_organization) do catalog.resources.select { |r| r.is_a?(Puppet::Type.type(:grafana_organization)) } end + + autorequire(:grafana_conn_validator) do + 'grafana' + end end diff --git a/manifests/config.pp b/manifests/config.pp index 580bebf..606e464 100644 --- a/manifests/config.pp +++ b/manifests/config.pp @@ -1,163 +1,166 @@ # == Class grafana::config # # This class is called from grafana # class grafana::config { case $grafana::install_method { 'docker': { if $grafana::container_cfg { $cfg = $grafana::cfg $myprovision = false - file { 'grafana.ini': + file { 'grafana.ini': ensure => file, path => $grafana::cfg_location, content => template('grafana/config.ini.erb'), owner => 'grafana', group => 'grafana', notify => Class['grafana::service'], } } } 'package','repo': { $cfg = $grafana::cfg $myprovision = true file { 'grafana.ini': ensure => file, path => $grafana::cfg_location, content => template('grafana/config.ini.erb'), owner => 'grafana', group => 'grafana', notify => Class['grafana::service'], } $sysconfig = $grafana::sysconfig $sysconfig_location = $grafana::sysconfig_location if $sysconfig_location and $sysconfig { $changes = $sysconfig.map |$key, $value| { "set ${key} ${value}" } augeas { 'sysconfig/grafana-server': - context => "/files${$sysconfig_location}", + context => "/files${sysconfig_location}", changes => $changes, notify => Class['grafana::service'], } } file { "${grafana::data_dir}/plugins": ensure => directory, owner => 'grafana', group => 'grafana', mode => '0750', } } 'archive': { $cfg = $grafana::cfg $myprovision = true file { "${grafana::install_dir}/conf/custom.ini": ensure => file, content => template('grafana/config.ini.erb'), owner => 'grafana', group => 'grafana', notify => Class['grafana::service'], } file { [$grafana::data_dir, "${grafana::data_dir}/plugins"]: ensure => directory, owner => 'grafana', group => 'grafana', mode => '0750', } } default: { fail("Installation method ${grafana::install_method} not supported") } } if $grafana::ldap_cfg { if $grafana::ldap_cfg =~ Array { $ldap_cfg_ary = $grafana::ldap_cfg } else { $ldap_cfg_ary = [$grafana::ldap_cfg] } $template_body = [ "<% scope['ldap_cfg_ary'].each do |v| %>", "<%= require 'toml'; TOML::Generator.new(v).body %>\n", '<% end %>', ] $ldap_cfg_toml = inline_template($template_body.join('')) file { '/etc/grafana/ldap.toml': ensure => file, content => $ldap_cfg_toml, owner => 'grafana', group => 'grafana', notify => Class['grafana::service'], } } # If grafana version is > 5.0.0, and the install method is package, # repo, or archive, then use the provisioning feature. Dashboards # and datasources are placed in # /etc/grafana/provisioning/[dashboards|datasources] by default. # --dashboards-- if ((versioncmp($grafana::version, '5.0.0') >= 0) and ($myprovision)) { $pdashboards = $grafana::provisioning_dashboards if (length($pdashboards) >= 1 ) { $dashboardpaths = flatten(grafana::deep_find_and_remove('options', $pdashboards)) # template uses: # - pdashboards file { $grafana::provisioning_dashboards_file: ensure => file, owner => 'grafana', group => 'grafana', mode => '0640', content => epp('grafana/pdashboards.yaml.epp'), notify => Class['grafana::service'], } # Loop over all providers, extract the paths and create # directories for each path of dashboards. $dashboardpaths.each | Integer $index, Hash $options | { if ('path' in $options) { # get sub paths of 'path' and create subdirs if necessary $subpaths = grafana::get_sub_paths($options['path']) if ($grafana::create_subdirs_provisioning and (length($subpaths) >= 1)) { file { $subpaths : ensure => directory, before => File[$options['path']], } } - file { $options['path'] : - ensure => directory, - owner => 'grafana', - group => 'grafana', - mode => '0750', - recurse => true, - purge => true, - source => $options['puppetsource'], + if $options['puppetsource'] { + file { $options['path'] : + ensure => directory, + owner => 'grafana', + group => 'grafana', + mode => '0750', + recurse => true, + purge => true, + source => $options['puppetsource'], + sourceselect => 'all', + } } } } } # --datasources-- $pdatasources = $grafana::provisioning_datasources if (length($pdatasources) >= 1) { # template uses: # - pdatasources file { $grafana::provisioning_datasources_file: ensure => file, owner => 'grafana', group => 'grafana', mode => '0640', content => epp('grafana/pdatasources.yaml.epp'), notify => Class['grafana::service'], } } } } diff --git a/manifests/install.pp b/manifests/install.pp index e13b848..66efe90 100644 --- a/manifests/install.pp +++ b/manifests/install.pp @@ -1,207 +1,214 @@ # == Class grafana::install # class grafana::install { $base_url = 'https://dl.grafana.com/oss/release' if $grafana::archive_source != undef { $real_archive_source = $grafana::archive_source } else { $real_archive_source = "${base_url}/grafana-${grafana::version}.linux-amd64.tar.gz" } if $grafana::package_source != undef { $real_package_source = $grafana::package_source } else { $real_package_source = $facts['os']['family'] ? { - /(RedHat|Amazon)/ => "${base_url}/grafana-${grafana::version}-${grafana::rpm_iteration}.x86_64.rpm", - 'Debian' => "${base_url}/grafana_${grafana::version}_amd64.deb", - default => $real_archive_source, + /(RedHat|Amazon|Suse)/ => "${base_url}/grafana-${grafana::version}-${grafana::rpm_iteration}.x86_64.rpm", + 'Debian' => "${base_url}/grafana_${grafana::version}_amd64.deb", + default => $real_archive_source, } } case $grafana::install_method { 'docker': { docker::image { 'grafana/grafana': image_tag => $grafana::version, require => Class['docker'], } } 'package': { case $facts['os']['family'] { 'Debian': { package { 'libfontconfig1': ensure => present, } archive { '/tmp/grafana.deb': source => $real_package_source, } package { 'grafana': ensure => present, name => $grafana::package_name, provider => 'dpkg', source => '/tmp/grafana.deb', require => [Archive['/tmp/grafana.deb'],Package['libfontconfig1']], } } - 'RedHat': { + 'RedHat', 'Suse': { package { 'fontconfig': ensure => present, } + $install_options = $facts['os']['family'] ? { + 'Suse' => ['--nodeps'], + default => undef, + } package { 'grafana': - ensure => present, - name => $grafana::package_name, - provider => 'rpm', - source => $real_package_source, - require => Package['fontconfig'], + ensure => present, + name => $grafana::package_name, + provider => 'rpm', + install_options => $install_options, + source => $real_package_source, + require => Package['fontconfig'], } } 'FreeBSD': { package { 'grafana': ensure => present, name => $grafana::package_name, provider => 'pkgng', } } default: { fail("${facts['os']['family']} not supported") } } } 'repo': { case $facts['os']['family'] { 'Debian': { package { 'libfontconfig1': ensure => present, } if ( $grafana::manage_package_repo ) { if !defined(Class['apt']) { include apt } apt::source { 'grafana': location => 'https://packages.grafana.com/oss/deb', release => $grafana::repo_name, architecture => 'amd64,arm64,armhf', repos => 'main', key => { 'id' => '4E40DDF6D76E284A4A6780E48C8C34C524098CB6', 'source' => 'https://packages.grafana.com/gpg.key', }, before => Package['grafana'], } Class['apt::update'] -> Package['grafana'] } package { 'grafana': ensure => $grafana::version, name => $grafana::package_name, require => Package['libfontconfig1'], } } 'RedHat': { package { 'fontconfig': ensure => present, } if ( $grafana::manage_package_repo ) { # http://docs.grafana.org/installation/rpm/#install-via-yum-repository $baseurl = $grafana::repo_name ? { 'stable' => 'https://packages.grafana.com/oss/rpm', 'beta' => 'https://packages.grafana.com/oss/rpm-beta', } yumrepo { 'grafana': ensure => 'absent', before => Package['grafana'], } yumrepo { "grafana-${grafana::repo_name}": descr => "grafana-${grafana::repo_name} repo", baseurl => $baseurl, gpgcheck => 1, gpgkey => 'https://packages.grafana.com/gpg.key', enabled => 1, before => Package['grafana'], } } if $grafana::version =~ /(installed|latest|present)/ { $real_version = $grafana::version } else { $real_version = "${grafana::version}-${grafana::rpm_iteration}" } package { 'grafana': ensure => $real_version, name => $grafana::package_name, require => Package['fontconfig'], } } 'Archlinux': { if $grafana::manage_package_repo { fail('manage_package_repo is not supported on Archlinux') } package { 'grafana': ensure => 'present', # pacman provider doesn't have feature versionable name => $grafana::package_name, } } 'FreeBSD': { package { 'grafana': ensure => 'present', # pkgng provider doesn't have feature versionable name => $grafana::package_name, } } default: { fail("${facts['os']['name']} not supported") } } } 'archive': { # create log directory /var/log/grafana (or parameterize) if !defined(User['grafana']) { user { 'grafana': ensure => present, home => $grafana::install_dir, } } file { $grafana::install_dir: ensure => directory, group => 'grafana', owner => 'grafana', require => User['grafana'], } archive { '/tmp/grafana.tar.gz': ensure => present, extract => true, extract_command => 'tar xfz %s --strip-components=1', extract_path => $grafana::install_dir, source => $real_archive_source, user => 'grafana', group => 'grafana', cleanup => true, require => File[$grafana::install_dir], } } default: { fail("Installation method ${grafana::install_method} not supported") } } if $grafana::toml_manage_package and !empty($grafana::ldap_servers) { - ensure_packages(['toml-pkg'], { + ensure_packages(['toml-pkg'], + { ensure => $grafana::toml_package_ensure, name => $grafana::toml_package_name, provider => $grafana::toml_package_provider, - }) + } + ) Package['toml-pkg'] -> Grafana_ldap_config <||> } } diff --git a/metadata.json b/metadata.json index 5b056c3..0360118 100644 --- a/metadata.json +++ b/metadata.json @@ -1,72 +1,75 @@ { "name": "puppet-grafana", - "version": "6.1.1-rc0", + "version": "7.0.1-rc0", "author": "Vox Pupuli", "summary": "This module provides Grafana, a dashboard and graph editor for Graphite and InfluxDB.", "license": "Apache-2.0", "source": "https://github.com/voxpupuli/puppet-grafana.git", "project_page": "https://github.com/voxpupuli/puppet-grafana", "issue_url": "https://github.com/voxpupuli/puppet-grafana/issues", "dependencies": [ { "name": "puppet/archive", "version_requirement": ">= 1.0.1 < 5.0.0" }, { "name": "puppetlabs/stdlib", "version_requirement": ">= 4.20.0 < 7.0.0" } ], "operatingsystem_support": [ { "operatingsystem": "Debian", "operatingsystemrelease": [ - "8", "9", "10" ] }, { "operatingsystem": "Ubuntu", "operatingsystemrelease": [ "16.04", "18.04" ] }, { "operatingsystem": "RedHat", "operatingsystemrelease": [ - "6", "7" ] }, { "operatingsystem": "CentOS", "operatingsystemrelease": [ - "6", "7" ] }, { "operatingsystem": "Archlinux" }, { "operatingsystem": "FreeBSD", "operatingsystemrelease": [ "12" ] + }, + { + "operatingsystem": "SLES", + "operatingsystemrelease": [ + "12" + ] } ], "requirements": [ { "name": "puppet", "version_requirement": ">= 5.5.8 < 7.0.0" } ], "tags": [ "grafana", "graphite", "influxdb", "monitoring" ] } diff --git a/spec/acceptance/class_spec.rb b/spec/acceptance/class_spec.rb index cc512af..9cabed9 100644 --- a/spec/acceptance/class_spec.rb +++ b/spec/acceptance/class_spec.rb @@ -1,142 +1,145 @@ require 'spec_helper_acceptance' describe 'grafana class' do # Create dummy module directorty shell('mkdir -p /etc/puppetlabs/code/environments/production/modules/my_custom_module/files/dashboards') context 'default parameters' do + before do + install_module_from_forge('puppetlabs/apt', '>= 7.5.0 < 8.0.0') + end # Using puppet_apply as a helper it 'works idempotently with no errors' do pp = <<-EOS class { 'grafana': } EOS # Run it twice and test for idempotency apply_manifest(pp, catch_failures: true) apply_manifest(pp, catch_changes: true) end describe package('grafana') do it { is_expected.to be_installed } end describe service('grafana-server') do it { is_expected.to be_enabled } it { is_expected.to be_running } end end context 'with fancy dashboard config' do it 'works idempotently with no errors' do pp = <<-EOS class { 'grafana': provisioning_datasources => { apiVersion => 1, datasources => [ { name => 'Prometheus', type => 'prometheus', access => 'proxy', url => 'http://localhost:9090/prometheus', isDefault => false, }, ], }, provisioning_dashboards => { apiVersion => 1, providers => [ { name => 'default', orgId => 1, folder => '', type => 'file', disableDeletion => true, options => { path => '/var/lib/grafana/dashboards', puppetsource => 'puppet:///modules/my_custom_module/dashboards', }, }, ], } } EOS # Run it twice and test for idempotency apply_manifest(pp, catch_failures: true) apply_manifest(pp, catch_changes: true) end end context 'with fancy dashboard config and custom target file' do it 'works idempotently with no errors' do pp = <<-EOS class { 'grafana': provisioning_datasources => { apiVersion => 1, datasources => [ { name => 'Prometheus', type => 'prometheus', access => 'proxy', url => 'http://localhost:9090/prometheus', isDefault => false, }, ], }, provisioning_dashboards => { apiVersion => 1, providers => [ { name => 'default', orgId => 1, folder => '', type => 'file', disableDeletion => true, options => { path => '/var/lib/grafana/dashboards', puppetsource => 'puppet:///modules/my_custom_module/dashboards', }, }, ], }, provisioning_dashboards_file => '/etc/grafana/provisioning/dashboards/dashboard.yaml', provisioning_datasources_file => '/etc/grafana/provisioning/datasources/datasources.yaml' } EOS # Run it twice and test for idempotency apply_manifest(pp, catch_failures: true) apply_manifest(pp, catch_changes: true) end end context 'beta release' do it 'works idempotently with no errors' do case fact('os.family') when 'Debian' pp = <<-EOS class { 'grafana': version => '6.0.0-beta3', repo_name => 'beta', } EOS when 'RedHat' pp = <<-EOS class { 'grafana': version => '6.0.0', rpm_iteration => 'beta3', repo_name => 'beta', } EOS end # Run it twice and test for idempotency apply_manifest(pp, catch_failures: true) apply_manifest(pp, catch_changes: true) end describe package('grafana') do it { is_expected.to be_installed } end end end diff --git a/spec/acceptance/grafana_team_spec.rb b/spec/acceptance/grafana_team_spec.rb new file mode 100644 index 0000000..833323a --- /dev/null +++ b/spec/acceptance/grafana_team_spec.rb @@ -0,0 +1,159 @@ +require 'spec_helper_acceptance' + +describe 'grafana_team' do + context 'setup grafana server' do + it 'runs successfully' do + pp = <<-EOS + class { 'grafana': + cfg => { + security => { + admin_user => 'admin', + admin_password => 'admin' + } + } + } + EOS + apply_manifest(pp, catch_failures: true) + apply_manifest(pp, catch_changes: true) + end + end + + context 'create team resource on `Main Org.`' do + it 'creates the team' do + pp = <<-EOS + include grafana::validator + grafana_team { 'example-team': + ensure => present, + grafana_url => 'http://localhost:3000', + grafana_user => 'admin', + grafana_password => 'admin', + } + EOS + apply_manifest(pp, catch_failures: true) + apply_manifest(pp, catch_changes: true) + end + + it 'has created the example team' do + shell('curl --user admin:admin http://localhost:3000/api/teams/search?name=example-team') do |f| + expect(f.stdout).to match(%r{example-team}) + end + end + + it 'has set default home dashboard' do + shell('curl --user admin:admin http://localhost:3000/api/teams/1/preferences') do |f| + data = JSON.parse(f.stdout) + expect(data).to include('homeDashboardId' => 0) + end + end + end + + context 'updates team resource' do + it 'creates dashboard and sets team home dashboard' do + pp = <<-EOS + include grafana::validator + grafana_dashboard { 'example-dashboard': + ensure => present, + grafana_url => 'http://localhost:3000', + grafana_user => 'admin', + grafana_password => 'admin', + content => '{"uid": "zyx986bc"}', + } + grafana_team { 'example-team': + ensure => present, + grafana_url => 'http://localhost:3000', + grafana_user => 'admin', + grafana_password => 'admin', + home_dashboard => 'example-dashboard', + } + EOS + apply_manifest(pp, catch_failures: true) + apply_manifest(pp, catch_changes: true) + end + + it 'has updated the example team home dashboard' do + shell('curl --user admin:admin http://localhost:3000/api/teams/1/preferences') do |f| + data = JSON.parse(f.stdout) + expect(data['homeDashboardId']).not_to eq(0) + end + end + end + + context 'create team resource on seperate organization' do + it 'creates organization and team' do + pp = <<-EOS + include grafana::validator + grafana_organization { 'example-organization': + ensure => present, + grafana_url => 'http://localhost:3000', + grafana_user => 'admin', + grafana_password => 'admin', + } + grafana_team { 'example-team-on-org': + ensure => present, + grafana_url => 'http://localhost:3000', + grafana_user => 'admin', + grafana_password => 'admin', + organization => 'example-organization', + } + EOS + apply_manifest(pp, catch_failures: true) + apply_manifest(pp, catch_changes: true) + end + + it 'creates team on organization' do + shell('curl --user admin:admin -X POST http://localhost:3000/api/user/using/2 && '\ + 'curl --user admin:admin http://localhost:3000/api/teams/search?name=example-team-on-org') do |f| + expect(f.stdout).to match(%r{example-team-on-org}) + end + end + end + + context 'destroy resources' do + it 'destroys the teams, dashboard, and organization' do + pp = <<-EOS + include grafana::validator + grafana_team { 'example-team': + ensure => absent, + grafana_url => 'http://localhost:3000', + grafana_user => 'admin', + grafana_password => 'admin', + } + grafana_team { 'example-team-on-org': + ensure => absent, + grafana_url => 'http://localhost:3000', + grafana_user => 'admin', + grafana_password => 'admin', + organization => 'example-organization', + } + grafana_dashboard { 'example-dashboard': + ensure => absent, + grafana_url => 'http://localhost:3000', + grafana_user => 'admin', + grafana_password => 'admin', + } + grafana_organization { 'example-organization': + ensure => absent, + grafana_url => 'http://localhost:3000', + grafana_user => 'admin', + grafana_password => 'admin', + } + EOS + apply_manifest(pp, catch_failures: true) + apply_manifest(pp, catch_changes: true) + end + + it 'has no example-team' do + shell('curl --user admin:admin -X POST http://localhost:3000/api/user/using/1 && '\ + 'curl --user admin:admin http://localhost:3000/api/teams/search') do |f| + expect(f.stdout).not_to match(%r{example-team}) + end + end + + it 'has no example-team-on-org' do + shell('curl --user admin:admin -X POST http://localhost:3000/api/user/using/2 && '\ + 'curl --user admin:admin http://localhost:3000/api/teams') do |f| + expect(f.stdout).not_to match(%r{example-team-on-org}) + end + end + end +end diff --git a/spec/acceptance/nodesets/archlinux-2-x64.yml b/spec/acceptance/nodesets/archlinux-2-x64.yml deleted file mode 100644 index 89b6300..0000000 --- a/spec/acceptance/nodesets/archlinux-2-x64.yml +++ /dev/null @@ -1,13 +0,0 @@ ---- -# This file is managed via modulesync -# https://github.com/voxpupuli/modulesync -# https://github.com/voxpupuli/modulesync_config -HOSTS: - archlinux-2-x64: - roles: - - master - platform: archlinux-2-x64 - box: archlinux/archlinux - hypervisor: vagrant -CONFIG: - type: foss diff --git a/spec/acceptance/nodesets/ec2/amazonlinux-2016091.yml b/spec/acceptance/nodesets/ec2/amazonlinux-2016091.yml deleted file mode 100644 index 19dd43e..0000000 --- a/spec/acceptance/nodesets/ec2/amazonlinux-2016091.yml +++ /dev/null @@ -1,31 +0,0 @@ ---- -# This file is managed via modulesync -# https://github.com/voxpupuli/modulesync -# https://github.com/voxpupuli/modulesync_config -# -# Additional ~/.fog config file with AWS EC2 credentials -# required. -# -# see: https://github.com/puppetlabs/beaker/blob/master/docs/how_to/hypervisors/ec2.md -# -# Amazon Linux is not a RHEL clone. -# -HOSTS: - amazonlinux-2016091-x64: - roles: - - master - platform: centos-6-x86_64 - hypervisor: ec2 - # refers to image_tempaltes.yaml AMI[vmname] entry: - vmname: amazonlinux-2016091-eu-central-1 - # refers to image_tempaltes.yaml entry inside AMI[vmname][:image]: - snapshot: aio - # t2.micro is free tier eligible (https://aws.amazon.com/en/free/): - amisize: t2.micro - # required so that beaker sanitizes sshd_config and root authorized_keys: - user: ec2-user -CONFIG: - type: aio - :ec2_yaml: spec/acceptance/nodesets/ec2/image_templates.yaml -... -# vim: syntax=yaml diff --git a/spec/acceptance/nodesets/ec2/image_templates.yaml b/spec/acceptance/nodesets/ec2/image_templates.yaml deleted file mode 100644 index e50593e..0000000 --- a/spec/acceptance/nodesets/ec2/image_templates.yaml +++ /dev/null @@ -1,34 +0,0 @@ -# This file is managed via modulesync -# https://github.com/voxpupuli/modulesync -# https://github.com/voxpupuli/modulesync_config -# -# see also: https://github.com/puppetlabs/beaker/blob/master/docs/how_to/hypervisors/ec2.md -# -# Hint: image IDs (ami-*) for the same image are different per location. -# -AMI: - # Amazon Linux AMI 2016.09.1 (HVM), SSD Volume Type - amazonlinux-2016091-eu-central-1: - :image: - :aio: ami-af0fc0c0 - :region: eu-central-1 - # Red Hat Enterprise Linux 7.3 (HVM), SSD Volume Type - rhel-73-eu-central-1: - :image: - :aio: ami-e4c63e8b - :region: eu-central-1 - # SUSE Linux Enterprise Server 12 SP2 (HVM), SSD Volume Type - sles-12sp2-eu-central-1: - :image: - :aio: ami-c425e4ab - :region: eu-central-1 - # Ubuntu Server 16.04 LTS (HVM), SSD Volume Type - ubuntu-1604-eu-central-1: - :image: - :aio: ami-fe408091 - :region: eu-central-1 - # Microsoft Windows Server 2016 Base - windows-2016-base-eu-central-1: - :image: - :aio: ami-88ec20e7 - :region: eu-central-1 diff --git a/spec/acceptance/nodesets/ec2/rhel-73-x64.yml b/spec/acceptance/nodesets/ec2/rhel-73-x64.yml deleted file mode 100644 index 7fac823..0000000 --- a/spec/acceptance/nodesets/ec2/rhel-73-x64.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- -# This file is managed via modulesync -# https://github.com/voxpupuli/modulesync -# https://github.com/voxpupuli/modulesync_config -# -# Additional ~/.fog config file with AWS EC2 credentials -# required. -# -# see: https://github.com/puppetlabs/beaker/blob/master/docs/how_to/hypervisors/ec2.md -# -HOSTS: - rhel-73-x64: - roles: - - master - platform: el-7-x86_64 - hypervisor: ec2 - # refers to image_tempaltes.yaml AMI[vmname] entry: - vmname: rhel-73-eu-central-1 - # refers to image_tempaltes.yaml entry inside AMI[vmname][:image]: - snapshot: aio - # t2.micro is free tier eligible (https://aws.amazon.com/en/free/): - amisize: t2.micro - # required so that beaker sanitizes sshd_config and root authorized_keys: - user: ec2-user -CONFIG: - type: aio - :ec2_yaml: spec/acceptance/nodesets/ec2/image_templates.yaml -... -# vim: syntax=yaml diff --git a/spec/acceptance/nodesets/ec2/sles-12sp2-x64.yml b/spec/acceptance/nodesets/ec2/sles-12sp2-x64.yml deleted file mode 100644 index 8542154..0000000 --- a/spec/acceptance/nodesets/ec2/sles-12sp2-x64.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- -# This file is managed via modulesync -# https://github.com/voxpupuli/modulesync -# https://github.com/voxpupuli/modulesync_config -# -# Additional ~/.fog config file with AWS EC2 credentials -# required. -# -# see: https://github.com/puppetlabs/beaker/blob/master/docs/how_to/hypervisors/ec2.md -# -HOSTS: - sles-12sp2-x64: - roles: - - master - platform: sles-12-x86_64 - hypervisor: ec2 - # refers to image_tempaltes.yaml AMI[vmname] entry: - vmname: sles-12sp2-eu-central-1 - # refers to image_tempaltes.yaml entry inside AMI[vmname][:image]: - snapshot: aio - # t2.micro is free tier eligible (https://aws.amazon.com/en/free/): - amisize: t2.micro - # required so that beaker sanitizes sshd_config and root authorized_keys: - user: ec2-user -CONFIG: - type: aio - :ec2_yaml: spec/acceptance/nodesets/ec2/image_templates.yaml -... -# vim: syntax=yaml diff --git a/spec/acceptance/nodesets/ec2/ubuntu-1604-x64.yml b/spec/acceptance/nodesets/ec2/ubuntu-1604-x64.yml deleted file mode 100644 index 9cf59d5..0000000 --- a/spec/acceptance/nodesets/ec2/ubuntu-1604-x64.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- -# This file is managed via modulesync -# https://github.com/voxpupuli/modulesync -# https://github.com/voxpupuli/modulesync_config -# -# Additional ~/.fog config file with AWS EC2 credentials -# required. -# -# see: https://github.com/puppetlabs/beaker/blob/master/docs/how_to/hypervisors/ec2.md -# -HOSTS: - ubuntu-1604-x64: - roles: - - master - platform: ubuntu-16.04-amd64 - hypervisor: ec2 - # refers to image_tempaltes.yaml AMI[vmname] entry: - vmname: ubuntu-1604-eu-central-1 - # refers to image_tempaltes.yaml entry inside AMI[vmname][:image]: - snapshot: aio - # t2.micro is free tier eligible (https://aws.amazon.com/en/free/): - amisize: t2.micro - # required so that beaker sanitizes sshd_config and root authorized_keys: - user: ubuntu -CONFIG: - type: aio - :ec2_yaml: spec/acceptance/nodesets/ec2/image_templates.yaml -... -# vim: syntax=yaml diff --git a/spec/acceptance/nodesets/ec2/windows-2016-base-x64.yml b/spec/acceptance/nodesets/ec2/windows-2016-base-x64.yml deleted file mode 100644 index 0932e29..0000000 --- a/spec/acceptance/nodesets/ec2/windows-2016-base-x64.yml +++ /dev/null @@ -1,29 +0,0 @@ ---- -# This file is managed via modulesync -# https://github.com/voxpupuli/modulesync -# https://github.com/voxpupuli/modulesync_config -# -# Additional ~/.fog config file with AWS EC2 credentials -# required. -# -# see: https://github.com/puppetlabs/beaker/blob/master/docs/how_to/hypervisors/ec2.md -# -HOSTS: - windows-2016-base-x64: - roles: - - master - platform: windows-2016-64 - hypervisor: ec2 - # refers to image_tempaltes.yaml AMI[vmname] entry: - vmname: windows-2016-base-eu-central-1 - # refers to image_tempaltes.yaml entry inside AMI[vmname][:image]: - snapshot: aio - # t2.micro is free tier eligible (https://aws.amazon.com/en/free/): - amisize: t2.micro - # required so that beaker sanitizes sshd_config and root authorized_keys: - user: ec2-user -CONFIG: - type: aio - :ec2_yaml: spec/acceptance/nodesets/ec2/image_templates.yaml -... -# vim: syntax=yaml diff --git a/spec/classes/grafana_spec.rb b/spec/classes/grafana_spec.rb index 2b53905..79e873b 100644 --- a/spec/classes/grafana_spec.rb +++ b/spec/classes/grafana_spec.rb @@ -1,404 +1,465 @@ require 'spec_helper' describe 'grafana' do on_supported_os.each do |os, facts| context "on #{os}" do let(:facts) do facts end context 'with default values' do it { is_expected.to compile.with_all_deps } it { is_expected.to contain_class('grafana') } it { is_expected.to contain_class('grafana::install').that_comes_before('Class[grafana::config]') } it { is_expected.to contain_class('grafana::config').that_notifies('Class[grafana::service]') } it { is_expected.to contain_class('grafana::service') } end context 'with parameter install_method is set to package' do let(:params) do { install_method: 'package', version: '5.4.2' } end case facts[:osfamily] when 'Debian' download_location = '/tmp/grafana.deb' describe 'use archive to fetch the package to a temporary location' do it do is_expected.to contain_archive('/tmp/grafana.deb').with_source( 'https://dl.grafana.com/oss/release/grafana_5.4.2_amd64.deb' ) end it { is_expected.to contain_archive('/tmp/grafana.deb').that_comes_before('Package[grafana]') } end describe 'install dependencies first' do it { is_expected.to contain_package('libfontconfig1').with_ensure('present').that_comes_before('Package[grafana]') } end describe 'install the package' do it { is_expected.to contain_package('grafana').with_provider('dpkg') } it { is_expected.to contain_package('grafana').with_source(download_location) } end when 'RedHat' describe 'install dependencies first' do it { is_expected.to contain_package('fontconfig').with_ensure('present').that_comes_before('Package[grafana]') } end describe 'install the package' do it { is_expected.to contain_package('grafana').with_provider('rpm') } end end end context 'with some plugins passed in' do let(:params) do { plugins: { 'grafana-wizzle' => { 'ensure' => 'present' }, 'grafana-woozle' => { 'ensure' => 'absent' }, 'grafana-plugin' => { 'ensure' => 'present', 'repo' => 'https://nexus.company.com/grafana/plugins' } } } end it { is_expected.to contain_grafana_plugin('grafana-wizzle').with(ensure: 'present') } it { is_expected.to contain_grafana_plugin('grafana-woozle').with(ensure: 'absent').that_notifies('Class[grafana::service]') } describe 'install plugin with pluginurl' do it { is_expected.to contain_grafana_plugin('grafana-plugin').with(ensure: 'present', repo: 'https://nexus.company.com/grafana/plugins') } end end context 'with parameter install_method is set to repo' do let(:params) do { install_method: 'repo' } end case facts[:osfamily] when 'Debian' describe 'install apt repo dependencies first' do it { is_expected.to contain_class('apt') } it { is_expected.to contain_apt__source('grafana').with(release: 'stable', repos: 'main', location: 'https://packages.grafana.com/oss/deb') } it { is_expected.to contain_apt__source('grafana').that_comes_before('Package[grafana]') } end describe 'install dependencies first' do it { is_expected.to contain_package('libfontconfig1').with_ensure('present').that_comes_before('Package[grafana]') } end describe 'install the package' do it { is_expected.to contain_package('grafana').with_ensure('installed') } end when 'RedHat' describe 'yum repo dependencies first' do it { is_expected.to contain_yumrepo('grafana-stable').with(baseurl: 'https://packages.grafana.com/oss/rpm', gpgkey: 'https://packages.grafana.com/gpg.key', enabled: 1) } it { is_expected.to contain_yumrepo('grafana-stable').that_comes_before('Package[grafana]') } end describe 'install dependencies first' do it { is_expected.to contain_package('fontconfig').with_ensure('present').that_comes_before('Package[grafana]') } end describe 'install the package' do it { is_expected.to contain_package('grafana').with_ensure('installed') } end end end context 'with parameter install_method is set to repo and manage_package_repo is set to false' do let(:params) do { install_method: 'repo', manage_package_repo: false, version: 'present' } end case facts[:osfamily] when 'Debian' describe 'install dependencies first' do it { is_expected.to contain_package('libfontconfig1').with_ensure('present').that_comes_before('Package[grafana]') } end describe 'install the package' do it { is_expected.to contain_package('grafana').with_ensure('present') } end when 'RedHat' describe 'install dependencies first' do it { is_expected.to contain_package('fontconfig').with_ensure('present').that_comes_before('Package[grafana]') } end describe 'install the package' do it { is_expected.to contain_package('grafana').with_ensure('present') } end when 'Archlinux' describe 'install the package' do it { is_expected.to contain_package('grafana').with_ensure('present') } end end end context 'with parameter install_method is set to archive' do let(:params) do { install_method: 'archive', version: '5.4.2' } end install_dir = '/usr/share/grafana' service_config = '/usr/share/grafana/conf/custom.ini' archive_source = 'https://dl.grafana.com/oss/release/grafana-5.4.2.linux-amd64.tar.gz' describe 'extract archive to install_dir' do it { is_expected.to contain_archive('/tmp/grafana.tar.gz').with_ensure('present') } it { is_expected.to contain_archive('/tmp/grafana.tar.gz').with_source(archive_source) } it { is_expected.to contain_archive('/tmp/grafana.tar.gz').with_extract_path(install_dir) } end describe 'create grafana user' do it { is_expected.to contain_user('grafana').with_ensure('present').with_home(install_dir) } it { is_expected.to contain_user('grafana').that_comes_before('File[/usr/share/grafana]') } end case facts[:osfamily] when 'Archlinux' describe 'create data_dir' do it { is_expected.to contain_file('/var/lib/grafana').with_ensure('directory') } end when 'Debian' describe 'create data_dir' do it { is_expected.to contain_file('/var/lib/grafana').with_ensure('directory') } end when 'FreBSD' describe 'create data_dir' do it { is_expected.to contain_file('/var/db/grafana').with_ensure('directory') } end when 'RedHat' describe 'create data_dir' do it { is_expected.to contain_file('/var/lib/grafana').with_ensure('directory') } end end describe 'manage install_dir' do it { is_expected.to contain_file(install_dir).with_ensure('directory') } it { is_expected.to contain_file(install_dir).with_group('grafana').with_owner('grafana') } end describe 'configure grafana' do it { is_expected.to contain_file(service_config).with_ensure('file') } end describe 'run grafana as service' do it { is_expected.to contain_service('grafana').with_ensure('running').with_provider('base') } it { is_expected.to contain_service('grafana').with_hasrestart(false).with_hasstatus(false) } end context 'when user already defined' do let(:pre_condition) do 'user{"grafana": ensure => present, }' end describe 'do NOT create grafana user' do it { is_expected.not_to contain_user('grafana').with_ensure('present').with_home(install_dir) } end end context 'when service already defined' do let(:pre_condition) do 'service{"grafana": ensure => running, name => "grafana-server", hasrestart => true, hasstatus => true, }' end describe 'do NOT run service' do it { is_expected.not_to contain_service('grafana').with_hasrestart(false).with_hasstatus(false) } end end end context 'invalid parameters' do context 'cfg' do describe 'should not raise an error when cfg parameter is a hash' do let(:params) do { cfg: {} } end it { is_expected.to contain_package('grafana') } end end end context 'configuration file' do describe 'should not contain any configuration when cfg param is empty' do it { is_expected.to contain_file('grafana.ini').with_content("# This file is managed by Puppet, any changes will be overwritten\n\n") } end describe 'should correctly transform cfg param entries to Grafana configuration' do let(:params) do { cfg: { 'app_mode' => 'production', 'section' => { 'string' => 'production', 'number' => 8080, 'boolean' => false, 'empty' => '' } }, ldap_cfg: { 'servers' => [ { 'host' => 'server1', 'use_ssl' => true, 'search_filter' => '(sAMAccountName=%s)', 'search_base_dns' => ['dc=domain1,dc=com'] } ], 'servers.attributes' => { 'name' => 'givenName', 'surname' => 'sn', 'username' => 'sAMAccountName', 'member_of' => 'memberOf', 'email' => 'email' } } } end expected = "# This file is managed by Puppet, any changes will be overwritten\n\n"\ "app_mode = production\n\n"\ "[section]\n"\ "boolean = false\n"\ "empty = \n"\ "number = 8080\n"\ "string = production\n" it { is_expected.to contain_file('grafana.ini').with_content(expected) } ldap_expected = "\n[[servers]]\n"\ "host = \"server1\"\n"\ "search_base_dns = [\"dc=domain1,dc=com\"]\n"\ "search_filter = \"(sAMAccountName=%s)\"\n"\ "use_ssl = true\n"\ "\n"\ "[servers.attributes]\n"\ "email = \"email\"\n"\ "member_of = \"memberOf\"\n"\ "name = \"givenName\"\n"\ "surname = \"sn\"\n"\ "username = \"sAMAccountName\"\n"\ "\n" it { is_expected.to contain_file('/etc/grafana/ldap.toml').with_content(ldap_expected) } end end context 'multiple ldap configuration' do describe 'should correctly transform ldap config param into Grafana ldap.toml' do let(:params) do { cfg: {}, ldap_cfg: [ { 'servers' => [ { 'host' => 'server1a server1b', 'use_ssl' => true, 'search_filter' => '(sAMAccountName=%s)', 'search_base_dns' => ['dc=domain1,dc=com'] } ], 'servers.attributes' => { 'name' => 'givenName', 'surname' => 'sn', 'username' => 'sAMAccountName', 'member_of' => 'memberOf', 'email' => 'email' } }, { 'servers' => [ { 'host' => 'server2a server2b', 'use_ssl' => true, 'search_filter' => '(sAMAccountName=%s)', 'search_base_dns' => ['dc=domain2,dc=com'] } ], 'servers.attributes' => { 'name' => 'givenName', 'surname' => 'sn', 'username' => 'sAMAccountName', 'member_of' => 'memberOf', 'email' => 'email' } } ] } end ldap_expected = "\n[[servers]]\n"\ "host = \"server1a server1b\"\n"\ "search_base_dns = [\"dc=domain1,dc=com\"]\n"\ "search_filter = \"(sAMAccountName=%s)\"\n"\ "use_ssl = true\n"\ "\n"\ "[servers.attributes]\n"\ "email = \"email\"\n"\ "member_of = \"memberOf\"\n"\ "name = \"givenName\"\n"\ "surname = \"sn\"\n"\ "username = \"sAMAccountName\"\n"\ "\n"\ "\n[[servers]]\n"\ "host = \"server2a server2b\"\n"\ "search_base_dns = [\"dc=domain2,dc=com\"]\n"\ "search_filter = \"(sAMAccountName=%s)\"\n"\ "use_ssl = true\n"\ "\n"\ "[servers.attributes]\n"\ "email = \"email\"\n"\ "member_of = \"memberOf\"\n"\ "name = \"givenName\"\n"\ "surname = \"sn\"\n"\ "username = \"sAMAccountName\"\n"\ "\n" it { is_expected.to contain_file('/etc/grafana/ldap.toml').with_content(ldap_expected) } end end + context 'provisioning_dashboards defined' do + let(:params) do + { + version: '6.0.0', + provisioning_dashboards: { + apiVersion: 1, + providers: [ + { + name: 'default', + orgId: 1, + folder: '', + type: 'file', + disableDeletion: true, + options: { + path: '/var/lib/grafana/dashboards', + puppetsource: 'puppet:///modules/my_custom_module/dashboards' + } + } + ] + } + } + end + + it do + is_expected.to contain_file('/var/lib/grafana/dashboards').with( + ensure: 'directory', + owner: 'grafana', + group: 'grafana', + mode: '0750', + recurse: true, + purge: true, + source: 'puppet:///modules/my_custom_module/dashboards' + ) + end + + context 'without puppetsource defined' do + let(:params) do + { + version: '6.0.0', + provisioning_dashboards: { + apiVersion: 1, + providers: [ + { + name: 'default', + orgId: 1, + folder: '', + type: 'file', + disableDeletion: true, + options: { + path: '/var/lib/grafana/dashboards' + } + } + ] + } + } + end + + it { is_expected.not_to contain_file('/var/lib/grafana/dashboards') } + end + end + context 'sysconfig environment variables' do let(:params) do { install_method: 'repo', sysconfig: { http_proxy: 'http://proxy.example.com/' } } end case facts[:osfamily] when 'Debian' describe 'Add the environment variable to the config file' do it { is_expected.to contain_augeas('sysconfig/grafana-server').with_context('/files/etc/default/grafana-server') } it { is_expected.to contain_augeas('sysconfig/grafana-server').with_changes(['set http_proxy http://proxy.example.com/']) } end when 'RedHat' describe 'Add the environment variable to the config file' do it { is_expected.to contain_augeas('sysconfig/grafana-server').with_context('/files/etc/sysconfig/grafana-server') } it { is_expected.to contain_augeas('sysconfig/grafana-server').with_changes(['set http_proxy http://proxy.example.com/']) } end end end end end end diff --git a/spec/grafana_dashboard_permission_type_spec.rb b/spec/grafana_dashboard_permission_type_spec.rb index 0341a88..ebb4fc6 100644 --- a/spec/grafana_dashboard_permission_type_spec.rb +++ b/spec/grafana_dashboard_permission_type_spec.rb @@ -1,62 +1,72 @@ require 'spec_helper' describe Puppet::Type.type(:grafana_dashboard_permission) do let(:gpermission) do described_class.new( title: 'foo_title', grafana_url: 'http://example.com/', grafana_api_path: '/api', user: 'foo_user', dashboard: 'foo_dashboard', permission: 'View', ensure: :present ) end context 'when setting parameters' do it "fails if grafana_url isn't HTTP-based" do expect do described_class.new title: 'foo_title', name: 'foo', grafana_url: 'example.com', ensure: :present end.to raise_error(Puppet::Error, %r{not a valid URL}) end it "fails if grafana_api_path isn't properly formed" do expect do described_class.new title: 'foo_title', grafana_url: 'http://example.com', grafana_api_path: '/invalidpath', ensure: :present end.to raise_error(Puppet::Error, %r{not a valid API path}) end it 'fails if both user and team set' do expect do described_class.new title: 'foo title', user: 'foo_user', team: 'foo_team' end.to raise_error(Puppet::Error, %r{Only user or team can be set, not both}) end - - # rubocop:disable RSpec/MultipleExpectations it 'accepts valid parameters' do expect(gpermission[:user]).to eq('foo_user') expect(gpermission[:grafana_api_path]).to eq('/api') expect(gpermission[:grafana_url]).to eq('http://example.com/') expect(gpermission[:dashboard]).to eq('foo_dashboard') end # rubocop:enable RSpec/MultipleExpectations it 'autorequires the grafana-server for proper ordering' do catalog = Puppet::Resource::Catalog.new service = Puppet::Type.type(:service).new(name: 'grafana-server') catalog.add_resource service catalog.add_resource gpermission relationship = gpermission.autorequire.find do |rel| (rel.source.to_s == 'Service[grafana-server]') && (rel.target.to_s == gpermission.to_s) end expect(relationship).to be_a Puppet::Relationship end it 'does not autorequire the service it is not managed' do catalog = Puppet::Resource::Catalog.new catalog.add_resource gpermission expect(gpermission.autorequire).to be_empty end + + it 'autorequires grafana_conn_validator' do + catalog = Puppet::Resource::Catalog.new + validator = Puppet::Type.type(:grafana_conn_validator).new(name: 'grafana') + catalog.add_resource validator + catalog.add_resource gpermission + + relationship = gpermission.autorequire.find do |rel| + (rel.source.to_s == 'Grafana_conn_validator[grafana]') && (rel.target.to_s == gpermission.to_s) + end + expect(relationship).to be_a Puppet::Relationship + end end end diff --git a/spec/grafana_membership_type_spec.rb b/spec/grafana_membership_type_spec.rb index 31ded64..4886757 100644 --- a/spec/grafana_membership_type_spec.rb +++ b/spec/grafana_membership_type_spec.rb @@ -1,64 +1,74 @@ require 'spec_helper' describe Puppet::Type.type(:grafana_membership) do let(:gmembership) do described_class.new( title: 'foo_title', user_name: 'foo_user', target_name: 'foo_target', grafana_url: 'http://example.com/', grafana_api_path: '/api', membership_type: 'organization', role: 'Viewer', ensure: :present ) end context 'when setting parameters' do it "fails if grafana_url isn't HTTP-based" do expect do described_class.new title: 'foo_title', name: 'foo', grafana_url: 'example.com', ensure: :present end.to raise_error(Puppet::Error, %r{not a valid URL}) end it "fails if grafana_api_path isn't properly formed" do expect do described_class.new title: 'foo_title', grafana_url: 'http://example.com', grafana_api_path: '/invalidpath', ensure: :present end.to raise_error(Puppet::Error, %r{not a valid API path}) end it 'fails if membership type not valid' do expect do described_class.new title: 'foo title', membership_type: 'foo' end.to raise_error(Puppet::Error, %r{Invalid value "foo"}) end - - # rubocop:disable RSpec/MultipleExpectations it 'accepts valid parameters' do expect(gmembership[:user_name]).to eq('foo_user') expect(gmembership[:target_name]).to eq('foo_target') expect(gmembership[:grafana_api_path]).to eq('/api') expect(gmembership[:grafana_url]).to eq('http://example.com/') expect(gmembership[:membership_type]).to eq(:organization) end # rubocop:enable RSpec/MultipleExpectations it 'autorequires the grafana-server for proper ordering' do catalog = Puppet::Resource::Catalog.new service = Puppet::Type.type(:service).new(name: 'grafana-server') catalog.add_resource service catalog.add_resource gmembership relationship = gmembership.autorequire.find do |rel| (rel.source.to_s == 'Service[grafana-server]') && (rel.target.to_s == gmembership.to_s) end expect(relationship).to be_a Puppet::Relationship end it 'does not autorequire the service it is not managed' do catalog = Puppet::Resource::Catalog.new catalog.add_resource gmembership expect(gmembership.autorequire).to be_empty end + + it 'autorequires grafana_conn_validator' do + catalog = Puppet::Resource::Catalog.new + validator = Puppet::Type.type(:grafana_conn_validator).new(name: 'grafana') + catalog.add_resource validator + catalog.add_resource gmembership + + relationship = gmembership.autorequire.find do |rel| + (rel.source.to_s == 'Grafana_conn_validator[grafana]') && (rel.target.to_s == gmembership.to_s) + end + expect(relationship).to be_a Puppet::Relationship + end end end diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index b2b2704..d266f6b 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -1,18 +1,18 @@ # This file is managed via modulesync # https://github.com/voxpupuli/modulesync # https://github.com/voxpupuli/modulesync_config # puppetlabs_spec_helper will set up coverage if the env variable is set. # We want to do this if lib exists and it hasn't been explicitly set. ENV['COVERAGE'] ||= 'yes' if Dir.exist?(File.expand_path('../../lib', __FILE__)) require 'voxpupuli/test/spec_helper' if File.exist?(File.join(__dir__, 'default_module_facts.yml')) - facts = YAML.load(File.read(File.join(__dir__, 'default_module_facts.yml'))) + facts = YAML.safe_load(File.read(File.join(__dir__, 'default_module_facts.yml'))) if facts facts.each do |name, value| add_custom_fact name.to_sym, value end end end diff --git a/spec/spec_helper_acceptance.rb b/spec/spec_helper_acceptance.rb index d2d9537..bec34fd 100644 --- a/spec/spec_helper_acceptance.rb +++ b/spec/spec_helper_acceptance.rb @@ -1,24 +1,6 @@ -require 'beaker-rspec' -require 'beaker-puppet' -require 'beaker/puppet_install_helper' -require 'beaker/module_install_helper' +# This file is completely managed via modulesync +require 'voxpupuli/acceptance/spec_helper_acceptance' -run_puppet_install_helper unless ENV['BEAKER_provision'] == 'no' +configure_beaker -RSpec.configure do |c| - # Readable test descriptions - c.formatter = :documentation - - # Configure all nodes in nodeset - c.before :suite do - install_module - install_module_dependencies - - hosts.each do |host| - if fact_on(host, 'osfamily') == 'Debian' - # Install additional modules for soft deps - install_module_from_forge('puppetlabs-apt', '>= 4.1.0 < 8.0.0') - end - end - end -end +Dir['./spec/support/acceptance/**/*.rb'].sort.each { |f| require f } diff --git a/spec/unit/puppet/provider/grafana_plugin/grafana_cli_spec.rb b/spec/unit/puppet/provider/grafana_plugin/grafana_cli_spec.rb index a2a59fb..68c52a9 100644 --- a/spec/unit/puppet/provider/grafana_plugin/grafana_cli_spec.rb +++ b/spec/unit/puppet/provider/grafana_plugin/grafana_cli_spec.rb @@ -1,74 +1,73 @@ require 'spec_helper' provider_class = Puppet::Type.type(:grafana_plugin).provider(:grafana_cli) describe provider_class do let(:resource) do Puppet::Type::Grafana_plugin.new( name: 'grafana-wizzle' ) end let(:provider) { provider_class.new(resource) } describe '#instances' do let(:plugins_ls_two) do # rubocop:disable Layout/TrailingWhitespace <<-PLUGINS installed plugins: grafana-simple-json-datasource @ 1.3.4 jdbranham-diagram-panel @ 1.4.0 Restart grafana after installing plugins . PLUGINS # rubocop:enable Layout/TrailingWhitespace end let(:plugins_ls_none) do <<-PLUGINS Restart grafana after installing plugins . PLUGINS end - # rubocop:disable RSpec/MultipleExpectations it 'has the correct names' do allow(provider_class).to receive(:grafana_cli).with('plugins', 'ls').and_return(plugins_ls_two) expect(provider_class.instances.map(&:name)).to match_array(['grafana-simple-json-datasource', 'jdbranham-diagram-panel']) expect(provider_class).to have_received(:grafana_cli) end it 'does not match if there are no plugins' do allow(provider_class).to receive(:grafana_cli).with('plugins', 'ls').and_return(plugins_ls_none) expect(provider_class.instances.size).to eq(0) expect(provider.exists?).to eq(false) expect(provider_class).to have_received(:grafana_cli) end # rubocop:enable RSpec/MultipleExpectations end it '#create' do allow(provider).to receive(:grafana_cli) provider.create expect(provider).to have_received(:grafana_cli).with('plugins', 'install', 'grafana-wizzle') end it '#destroy' do allow(provider).to receive(:grafana_cli) provider.destroy expect(provider).to have_received(:grafana_cli).with('plugins', 'uninstall', 'grafana-wizzle') end describe 'create with repo' do let(:resource) do Puppet::Type::Grafana_plugin.new( name: 'grafana-plugin', repo: 'https://nexus.company.com/grafana/plugins' ) end it '#create with repo' do allow(provider).to receive(:grafana_cli) provider.create expect(provider).to have_received(:grafana_cli).with('--repo https://nexus.company.com/grafana/plugins', 'plugins', 'install', 'grafana-plugin') end end end diff --git a/spec/unit/puppet/type/grafana_dashboard_type_spec.rb b/spec/unit/puppet/type/grafana_dashboard_type_spec.rb index 18f9a88..32b76e6 100644 --- a/spec/unit/puppet/type/grafana_dashboard_type_spec.rb +++ b/spec/unit/puppet/type/grafana_dashboard_type_spec.rb @@ -1,63 +1,72 @@ # Copyright 2015 Mirantis, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'spec_helper' describe Puppet::Type.type(:grafana_dashboard) do let(:gdashboard) do described_class.new name: 'foo', grafana_url: 'http://example.com/', content: '{}', ensure: :present end context 'when setting parameters' do it "fails if grafana_url isn't HTTP-based" do expect do described_class.new name: 'foo', grafana_url: 'example.com', content: '{}', ensure: :present end.to raise_error(Puppet::Error, %r{not a valid URL}) end it "fails if content isn't provided" do expect do described_class.new name: 'foo', grafana_url: 'http://example.com', ensure: :present end.to raise_error(Puppet::Error, %r{content is required}) end it "fails if content isn't JSON" do expect do described_class.new name: 'foo', grafana_url: 'http://example.com/', content: '{invalid', ensure: :present end.to raise_error(Puppet::Error, %r{Invalid JSON}) end - - # rubocop:disable RSpec/MultipleExpectations it 'accepts valid parameters' do expect(gdashboard[:name]).to eq('foo') expect(gdashboard[:grafana_url]).to eq('http://example.com/') expect(gdashboard[:content]).to eq({}) end it 'autorequires the grafana-server for proper ordering' do catalog = Puppet::Resource::Catalog.new service = Puppet::Type.type(:service).new(name: 'grafana-server') catalog.add_resource service catalog.add_resource gdashboard relationship = gdashboard.autorequire.find do |rel| (rel.source.to_s == 'Service[grafana-server]') && (rel.target.to_s == gdashboard.to_s) end expect(relationship).to be_a Puppet::Relationship end it 'does not autorequire the service it is not managed' do catalog = Puppet::Resource::Catalog.new catalog.add_resource gdashboard expect(gdashboard.autorequire).to be_empty end + it 'autorequires grafana_conn_validator' do + catalog = Puppet::Resource::Catalog.new + validator = Puppet::Type.type(:grafana_conn_validator).new(name: 'grafana') + catalog.add_resource validator + catalog.add_resource gdashboard + + relationship = gdashboard.autorequire.find do |rel| + (rel.source.to_s == 'Grafana_conn_validator[grafana]') && (rel.target.to_s == gdashboard.to_s) + end + expect(relationship).to be_a Puppet::Relationship + end end end diff --git a/spec/unit/puppet/type/grafana_datasource_type_spec.rb b/spec/unit/puppet/type/grafana_datasource_type_spec.rb index 1bad7e8..80f75e3 100644 --- a/spec/unit/puppet/type/grafana_datasource_type_spec.rb +++ b/spec/unit/puppet/type/grafana_datasource_type_spec.rb @@ -1,95 +1,94 @@ # Copyright 2015 Mirantis, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'spec_helper' describe Puppet::Type.type(:grafana_datasource) do let(:gdatasource) do described_class.new( name: 'foo', grafana_url: 'http://example.com', url: 'http://es.example.com', type: 'elasticsearch', organization: 'test_org', access_mode: 'proxy', is_default: true, basic_auth: true, basic_auth_user: 'user', basic_auth_password: 'password', with_credentials: true, database: 'test_db', user: 'db_user', password: 'db_password', json_data: { esVersion: 5, timeField: '@timestamp', timeInterval: '1m' }, secure_json_data: { password: '5ecretPassw0rd' } ) end context 'when setting parameters' do it "fails if grafana_url isn't HTTP-based" do expect do described_class.new name: 'foo', grafana_url: 'example.com', content: '{}', ensure: :present end.to raise_error(Puppet::Error, %r{not a valid URL}) end it "fails if json_data isn't valid" do expect do described_class.new name: 'foo', grafana_url: 'http://example.com', json_data: 'invalid', ensure: :present end.to raise_error(Puppet::Error, %r{json_data should be a Hash}) end it "fails if secure_json_data isn't valid" do expect do described_class.new name: 'foo', grafana_url: 'http://example.com', secure_json_data: 'invalid', ensure: :present end.to raise_error(Puppet::Error, %r{json_data should be a Hash}) end - # rubocop:disable RSpec/MultipleExpectations it 'accepts valid parameters' do expect(gdatasource[:name]).to eq('foo') expect(gdatasource[:grafana_url]).to eq('http://example.com') expect(gdatasource[:url]).to eq('http://es.example.com') expect(gdatasource[:type]).to eq('elasticsearch') expect(gdatasource[:organization]).to eq('test_org') expect(gdatasource[:access_mode]).to eq(:proxy) expect(gdatasource[:is_default]).to eq(:true) expect(gdatasource[:basic_auth]).to eq(:true) expect(gdatasource[:basic_auth_user]).to eq('user') expect(gdatasource[:basic_auth_password]).to eq('password') expect(gdatasource[:with_credentials]).to eq(:true) expect(gdatasource[:database]).to eq('test_db') expect(gdatasource[:user]).to eq('db_user') expect(gdatasource[:password]).to eq('db_password') expect(gdatasource[:json_data]).to eq(esVersion: 5, timeField: '@timestamp', timeInterval: '1m') expect(gdatasource[:secure_json_data]).to eq(password: '5ecretPassw0rd') end # rubocop:enable RSpec/MultipleExpectations it 'autorequires the grafana-server for proper ordering' do catalog = Puppet::Resource::Catalog.new service = Puppet::Type.type(:service).new(name: 'grafana-server') catalog.add_resource service catalog.add_resource gdatasource relationship = gdatasource.autorequire.find do |rel| (rel.source.to_s == 'Service[grafana-server]') && (rel.target.to_s == gdatasource.to_s) end expect(relationship).to be_a Puppet::Relationship end it 'does not autorequire the service it is not managed' do catalog = Puppet::Resource::Catalog.new catalog.add_resource gdatasource expect(gdatasource.autorequire).to be_empty end end end diff --git a/spec/unit/puppet/type/grafana_folder_type_spec.rb b/spec/unit/puppet/type/grafana_folder_type_spec.rb index bb26999..f3ffa10 100644 --- a/spec/unit/puppet/type/grafana_folder_type_spec.rb +++ b/spec/unit/puppet/type/grafana_folder_type_spec.rb @@ -1,56 +1,65 @@ # Copyright 2015 Mirantis, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'spec_helper' describe Puppet::Type.type(:grafana_folder) do let(:gfolder) do described_class.new name: 'foo', grafana_url: 'http://example.com/', ensure: :present end context 'when setting parameters' do it "fails if grafana_url isn't HTTP-based" do expect do described_class.new name: 'foo', grafana_url: 'example.com', ensure: :present end.to raise_error(Puppet::Error, %r{not a valid URL}) end it "fails if grafana_api_path isn't properly formed" do expect do described_class.new name: 'foo', grafana_url: 'http://example.com', grafana_api_path: '/invalidpath', ensure: :present end.to raise_error(Puppet::Error, %r{not a valid API path}) end - - # rubocop:disable RSpec/MultipleExpectations it 'accepts valid parameters' do expect(gfolder[:name]).to eq('foo') expect(gfolder[:grafana_url]).to eq('http://example.com/') end it 'autorequires the grafana-server for proper ordering' do catalog = Puppet::Resource::Catalog.new service = Puppet::Type.type(:service).new(name: 'grafana-server') catalog.add_resource service catalog.add_resource gfolder relationship = gfolder.autorequire.find do |rel| (rel.source.to_s == 'Service[grafana-server]') && (rel.target.to_s == gfolder.to_s) end expect(relationship).to be_a Puppet::Relationship end it 'does not autorequire the service it is not managed' do catalog = Puppet::Resource::Catalog.new catalog.add_resource gfolder expect(gfolder.autorequire).to be_empty end + it 'autorequires grafana_conn_validator' do + catalog = Puppet::Resource::Catalog.new + validator = Puppet::Type.type(:grafana_conn_validator).new(name: 'grafana') + catalog.add_resource validator + catalog.add_resource gfolder + + relationship = gfolder.autorequire.find do |rel| + (rel.source.to_s == 'Grafana_conn_validator[grafana]') && (rel.target.to_s == gfolder.to_s) + end + expect(relationship).to be_a Puppet::Relationship + end end end diff --git a/spec/unit/puppet/type/grafana_ldap_config_spec.rb b/spec/unit/puppet/type/grafana_ldap_config_spec.rb index e8a32ed..c917c28 100644 --- a/spec/unit/puppet/type/grafana_ldap_config_spec.rb +++ b/spec/unit/puppet/type/grafana_ldap_config_spec.rb @@ -1,164 +1,164 @@ # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' - +# rubocop:disable RSpec/VoidExpect describe Puppet::Type.type(:grafana_ldap_config) do # resource title context 'validate resource title' do it 'fails if title is not set' do expect do described_class.new name: nil end.to raise_error(Puppet::Error, %r{Title or name must be provided}) end it 'fails if title is not a string' do expect do described_class.new name: 123 end.to raise_error(Puppet::ResourceError, %r{must be a String}) end end # owner context 'validate owner' do it 'fails if owner is wrong type' do expect do described_class.new name: 'foo_bar', owner: true end.to raise_error(Puppet::ResourceError, %r{must be a String or Integer}) end it 'succeeds if owner is string' do expect do described_class.new name: 'foo_bar', owner: 'foo' end end it 'succeeds if owner is numeric' do expect do described_class.new name: 'foo_bar', owner: 111 end end end # group context 'validate group' do it 'fails if group is wrong type' do expect do described_class.new name: 'foo_bar', group: true end.to raise_error(Puppet::ResourceError, %r{must be a String or Integer}) end it 'succeeds if group is string' do expect do described_class.new name: 'foo_bar', group: 'foo' end end it 'succeeds if group is numeric' do expect do described_class.new name: 'foo_bar', owner: 111 end end end # mode context 'validate mode' do it 'fails if mode is wrong type' do expect do described_class.new name: 'foo_bar', mode: 123 end.to raise_error(Puppet::ResourceError, %r{must be a String}) end it 'fails if mode is empty' do expect do described_class.new name: 'foo_bar', mode: '' end.to raise_error(Puppet::ResourceError, %r{must be a String}) end # currently disabled # it 'fails if mode is invalid' do # expect do # described_class.new name: 'foo_bar', mode: 'abcd' # end.to raise_error(Puppet::ResourceError, %r{is not valid}) # end it 'succeeds if mode is string' do expect do described_class.new name: 'foo_bar', mode: '0755' end end end # replace context 'validate replace' do it 'fails if replace is not a boolean' do expect do described_class.new name: 'foo_bar', replace: 'bla' end.to raise_error(Puppet::ResourceError, %r{Valid values are}) end it 'succeeds if replace' do expect do described_class.new name: 'foo_bar', replace: true end end end # backup context 'validate backup' do it 'fails if backup is not a boolean' do expect do described_class.new name: 'foo_bar', backup: 'bla' end.to raise_error(Puppet::ResourceError, %r{Valid values are}) end it 'succeeds if backup' do expect do described_class.new name: 'foo_bar', backup: true end end end # validate_cmd context 'validate validate_cmd' do it 'fails if validate_cmd is wrong type' do expect do described_class.new name: 'foo_bar', validate_cmd: 123 end.to raise_error(Puppet::Error, %r{must be a String}) end it 'succeeds if group is string' do expect do described_class.new name: 'foo_bar', validate_cmd: '0755' end end end # ldap_servers context 'validate ldap_servers' do it 'correctly returns the declared LDAP servers' do catalog = Puppet::Resource::Catalog.new server = Puppet::Type.type(:grafana_ldap_server).new( name: 'ldap.example.com', hosts: ['ldap.example.com'], search_base_dns: ['ou=auth'] ) config = Puppet::Type.type(:grafana_ldap_config).new name: 'ldap1' catalog.add_resource server catalog.add_resource config expect(config.ldap_servers.keys).to include('ldap.example.com') end end end diff --git a/spec/unit/puppet/type/grafana_ldap_group_mapping_spec.rb b/spec/unit/puppet/type/grafana_ldap_group_mapping_spec.rb index 410be52..81d33b4 100644 --- a/spec/unit/puppet/type/grafana_ldap_group_mapping_spec.rb +++ b/spec/unit/puppet/type/grafana_ldap_group_mapping_spec.rb @@ -1,151 +1,151 @@ # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' - +# rubocop:disable RSpec/VoidExpect describe Puppet::Type.type(:grafana_ldap_group_mapping) do # resource title context 'validate resource title' do it 'fails if title is not set' do expect do described_class.new name: nil end.to raise_error(Puppet::Error, %r{Title or name must be provided}) end it 'fails if title is empty' do expect do described_class.new name: '' end.to raise_error(RuntimeError, %r{needs to be a non-empty string}) end it 'fails if title is not a string' do expect do described_class.new name: 123 end.to raise_error(Puppet::ResourceError, %r{must be a String}) end end # ldap_server_name context 'validate ldap_server_name' do it 'fails if ldap_server_name is not set' do expect do described_class.new name: 'foo_bar', ldap_server_name: nil, group_dn: 'bar' end.to raise_error(Puppet::Error, %r{Got nil value for}) end it 'fails if ldap_server_name is empty' do expect do described_class.new name: 'foo_bar', ldap_server_name: '', group_dn: 'bar' end.to raise_error(RuntimeError, %r{needs to be a non-empty string}) end it 'fails if ldap_server_name is not a string' do expect do described_class.new name: '123_bar', ldap_server_name: 123, group_dn: 'bar' end.to raise_error(Puppet::ResourceError, %r{must be a String}) end end # group_dn context 'validate group_dn' do it 'fails if group_dn is not set' do expect do described_class.new name: 'foo_bar', ldap_server_name: 'foo', group_dn: nil end.to raise_error(Puppet::Error, %r{Got nil value for}) end it 'fails if group_dn is empty' do expect do described_class.new name: 'foo_bar', ldap_server_name: 'foo', group_dn: '' end.to raise_error(RuntimeError, %r{needs to be a non-empty string}) end it 'fails if group_dn is not a string' do expect do described_class.new name: 'foo_123', ldap_server_name: 'foo', group_dn: 123 end.to raise_error(Puppet::ResourceError, %r{must be a String}) end end # org_role context 'validate org_role' do it 'fails if org_role is not set' do expect do described_class.new name: 'foo_bar', ldap_server_name: 'foo', group_dn: 'bar', org_role: nil end.to raise_error(Puppet::Error, %r{Got nil value for}) end it 'fails if org_role is not a string' do expect do described_class.new name: 'foo_bar', ldap_server_name: 'foo', group_dn: 'bar', org_role: 123 end.to raise_error(Puppet::ResourceError, %r{Valid values are}) end it 'fails if org_role is an unknown role' do expect do described_class.new name: 'foo_bar', ldap_server_name: 'foo', group_dn: 'bar', org_role: 'bla' end.to raise_error(Puppet::Error, %r{Valid values are}) end it 'succeeds if all is correct' do expect do described_class.new name: 'foo_bar', ldap_server_name: 'foo', group_dn: 'bar', org_role: 'Editor' end end end # grafana_admin context 'validate grafana_admin' do it 'fails if org_role is not a boolean' do expect do described_class.new name: 'foo_bar', ldap_server_name: 'foo', group_dn: 'bar', org_role: 'Admin', grafana_admin: 'bla' end.to raise_error(Puppet::ResourceError, %r{Valid values are}) end it 'succeeds if grafana_admin' do expect do described_class.new name: 'foo_bar', ldap_server_name: 'foo', group_dn: 'bar', org_role: 'Admin', grafana_admin: true end end end context 'valid viewer' do let(:group_mapping) do described_class.new name: 'foo_bar', ldap_server_name: 'foo', group_dn: 'bar', org_role: 'Viewer', grafana_admin: false end it 'given all parameters' do expect(group_mapping[:org_role]).to eq(:Viewer) end end context 'valid editor' do let(:group_mapping) do described_class.new name: 'foo_bar', ldap_server_name: 'foo', group_dn: 'bar', org_role: 'Editor', grafana_admin: false end it 'given all parameters' do expect(group_mapping[:org_role]).to eq(:Editor) end end context 'valid admin' do let(:group_mapping) do described_class.new name: 'foo_bar', ldap_server_name: 'foo', group_dn: 'bar', org_role: 'Admin', grafana_admin: true end it 'given all parameters' do expect(group_mapping[:org_role]).to eq(:Admin) end end end diff --git a/spec/unit/puppet/type/grafana_ldap_server_spec.rb b/spec/unit/puppet/type/grafana_ldap_server_spec.rb index 0706c0d..8cff019 100644 --- a/spec/unit/puppet/type/grafana_ldap_server_spec.rb +++ b/spec/unit/puppet/type/grafana_ldap_server_spec.rb @@ -1,337 +1,337 @@ # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. # require 'spec_helper' - +# rubocop:disable RSpec/VoidExpect describe Puppet::Type.type(:grafana_ldap_server) do # resource title context 'validate resource title' do it 'fails if title is not set' do expect do described_class.new name: nil end.to raise_error(Puppet::Error, %r{Title or name must be provided}) end it 'fails if title is empty' do expect do described_class.new name: '' end.to raise_error(RuntimeError, %r{must not be empty}) end it 'fails if title is not a string' do expect do described_class.new name: 123 end.to raise_error(Puppet::ResourceError, %r{must be a String}) end end # hosts context 'validate hosts' do it 'fails if hosts is not set' do expect do described_class.new name: 'server1', hosts: nil end.to raise_error(Puppet::Error, %r{Got nil value for}) end it 'fails if hosts is not an array' do expect do described_class.new name: 'server1', hosts: '' end.to raise_error(RuntimeError, %r{must be an Array}) end it 'fails if hosts is empty' do expect do described_class.new name: 'server1', hosts: [] end.to raise_error(RuntimeError, %r{must not be empty}) end end # port context 'validate port' do it 'fails if port is empty' do expect do described_class.new name: 'server1', hosts: ['server1'], port: 0 end.to raise_error(RuntimeError, %r{must be an Integer within}) end it 'fails if port is a string' do expect do described_class.new name: 'server1', hosts: ['server1'], port: '123' end.to raise_error(Puppet::ResourceError, %r{must be an Integer within}) end it 'fails if port is greater than 65535' do expect do described_class.new name: 'server1', hosts: ['server1'], port: 123_456 end.to raise_error(Puppet::ResourceError, %r{must be an Integer within}) end end # use_ssl context 'validate use_ssl' do it 'fails if use_ssl is not boolean' do expect do described_class.new name: 'server1', hosts: ['server1'], use_ssl: 'foobar' end.to raise_error(Puppet::Error, %r{Valid values are true}) end it 'succeeds if all is correct' do expect do described_class.new name: 'server1', hosts: ['server1'], use_ssl: true end end end # start_tls context 'validate start_tls' do it 'fails if start_tls is not boolean' do expect do described_class.new name: 'server1', hosts: ['server1'], start_tls: 'foobar' end.to raise_error(Puppet::Error, %r{Valid values are true}) end it 'succeeds if all is correct' do expect do described_class.new name: 'server1', hosts: ['server1'], start_tls: true end end end # ssl_skip_verify context 'validate ssl_skip_verify' do it 'fails if ssl_skip_verify is not boolean' do expect do described_class.new name: 'server1', hosts: ['server1'], ssl_skip_verify: 'foobar' end.to raise_error(Puppet::Error, %r{Valid values are true}) end it 'succeeds if all is correct' do expect do described_class.new name: 'server1', hosts: ['server1'], ssl_skip_verify: true end end end # root_ca_cert context 'validate root_ca_cert' do it 'fails if root_ca_cert is empty' do expect do described_class.new name: 'server1', hosts: ['server1'], root_ca_cert: '' end.to raise_error(RuntimeError, %r{must be set when SSL}) end it 'fails if root_ca_cert is not a string' do expect do described_class.new name: 'server1', hosts: ['server1'], root_ca_cert: 12_345 end.to raise_error(Puppet::Error, %r{must be a String}) end it 'succeeds if all is correct' do expect do described_class.new name: 'server1', hosts: ['server1'], root_ca_cert: '/etc/ssl/certs/ca-certificate.crt' end end end # client_cert context 'validate client_cert' do it 'fails if client_cert is not a string' do expect do described_class.new name: 'server1', hosts: ['server1'], client_cert: 12_345 end.to raise_error(Puppet::Error, %r{must be a String}) end it 'succeeds if all is correct' do expect do described_class.new name: 'server1', hosts: ['server1'], client_cert: '/etc/ssl/host.crt' end end end # client_key context 'validate client_key' do it 'fails if client_key is not a string' do expect do described_class.new name: 'server1', hosts: ['server1'], client_key: 12_345 end.to raise_error(Puppet::Error, %r{must be a String}) end it 'succeeds if all is correct' do expect do described_class.new name: 'server1', hosts: ['server1'], client_key: '/etc/ssl/certs/ca-certificate.crt' end end end # bind_dn context 'validate bind_dn' do it 'fails if bind_dn is not a string' do expect do described_class.new name: 'server1', hosts: ['server1'], bind_dn: 12_345 end.to raise_error(Puppet::Error, %r{must be a String}) end it 'succeeds if all is correct' do expect do described_class.new name: 'server1', hosts: ['server1'], bind_dn: 'cn=Admin', search_base_dns: ['ou=users'] end end end # bind_password context 'validate bind_password' do it 'fails if bind_password is not a string' do expect do described_class.new name: 'server1', hosts: ['server1'], bind_password: 12_345 end.to raise_error(Puppet::Error, %r{must be a String}) end it 'succeeds if all is correct' do expect do described_class.new name: 'server1', hosts: ['server1'], bind_password: 'foobar', search_base_dns: ['ou=users'] end end end # search_filter context 'validate search_filter' do it 'fails if search_filter is not a string' do expect do described_class.new name: 'server1', hosts: ['server1'], search_filter: 12_345 end.to raise_error(Puppet::Error, %r{must be a String}) end it 'succeeds if all is correct' do expect do described_class.new name: 'server1', hosts: ['server1'], search_filter: 'uid=%u', search_base_dns: ['ou=users'] end end end # search_base_dns context 'validate search_base_dns' do it 'fails if search_base_dns is not an array' do expect do described_class.new name: 'server1', hosts: ['server1'], search_base_dns: 12_345 end.to raise_error(Puppet::Error, %r{must be an Array}) end it 'fails if search_base_dns array members are not strings' do expect do described_class.new name: 'server1', hosts: ['server1'], search_base_dns: [12_345] end.to raise_error(Puppet::Error, %r{must be a String}) end it 'fails if search_base_dns array is empty' do expect do described_class.new name: 'server1', hosts: ['server1'], search_base_dns: [] end.to raise_error(RuntimeError, %r{needs to contain at least one LDAP base-dn}) end it 'succeeds if all is correct' do expect do described_class.new name: 'server1', hosts: ['server1'], search_base_dns: ['ou=users'] end end end # group_search_filter context 'validate group_search_filter' do it 'fails if group_search_filter is not a string' do expect do described_class.new name: 'server1', hosts: ['server1'], group_search_filter: 12_345 end.to raise_error(Puppet::Error, %r{must be a String}) end it 'succeeds if all is correct' do expect do described_class.new name: 'server1', hosts: ['server1'], group_search_filter: 'cn=adminsgroup', search_base_dns: ['ou=users'] end end end # group_search_filter_user_attribute context 'validate group_search_filter_user_attribute' do it 'fails if group_search_filter_user_attribute is not a string' do expect do described_class.new name: 'server1', hosts: ['server1'], group_search_filter_user_attribute: 12_345 end.to raise_error(Puppet::Error, %r{must be a String}) end it 'succeeds if all is correct' do expect do described_class.new name: 'server1', hosts: ['server1'], group_search_filter_user_attribute: 'dn', search_base_dns: ['ou=users'] end end end # group_search_base_dns context 'validate group_search_base_dns' do it 'fails if group_search_base_dns is not an array' do expect do described_class.new name: 'server1', hosts: ['server1'], group_search_base_dns: 12_345 end.to raise_error(Puppet::Error, %r{must be an Array}) end it 'fails if group_search_base_dns array members are not strings' do expect do described_class.new name: 'server1', hosts: ['server1'], group_search_base_dns: [12_345] end.to raise_error(Puppet::Error, %r{must be a String}) end it 'fails if group_search_base_dns array is empty' do expect do described_class.new name: 'server1', hosts: ['server1'], group_search_base_dns: [], search_base_dns: ['ou=auth'] end.to raise_error(RuntimeError, %r{needs to contain at least one LDAP base-dn}) end it 'succeeds if all is correct' do expect do described_class.new name: 'server1', hosts: ['server1'], group_search_base_dns: ['ou=users'] end end end # attributes context 'validate attributes' do it 'fails if attributes is not a hash' do expect do described_class.new name: 'server1', hosts: ['server1'], attributes: [12_345] end.to raise_error(Puppet::Error, %r{must be a Hash}) end it 'fails if unknown attribute' do expect do described_class.new name: 'server1', hosts: ['server1'], attributes: { 'foo' => 'bar' } end.to raise_error(Puppet::Error, %r{contains an unknown key}) end it 'fails if wrong key type' do expect do described_class.new name: 'server1', hosts: ['server1'], attributes: { 12_345 => 'bar' } end.to raise_error(Puppet::Error, %r{must be Strings}) end it 'fails if wrong value type' do expect do described_class.new name: 'server1', hosts: ['server1'], attributes: { 'surname' => {} } end.to raise_error(Puppet::Error, %r{must be Strings}) end it 'succeeds if all is correct' do expect do described_class.new name: 'server1', hosts: ['server1'], attributes: { 'username' => 'uid' } end end end end diff --git a/spec/unit/puppet/type/grafana_notification_type_spec.rb b/spec/unit/puppet/type/grafana_notification_type_spec.rb index d85489b..e1b9953 100644 --- a/spec/unit/puppet/type/grafana_notification_type_spec.rb +++ b/spec/unit/puppet/type/grafana_notification_type_spec.rb @@ -1,72 +1,70 @@ # Copyright 2015 Mirantis, Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'spec_helper' describe Puppet::Type.type(:grafana_notification) do let(:gnotification) do described_class.new( name: 'foo', grafana_url: 'http://example.com', type: 'email', is_default: true, send_reminder: true, frequency: '20m', settings: { adresses: 'test@example.com' } ) end context 'when setting parameters' do it "fails if grafana_url isn't HTTP-based" do expect do described_class.new name: 'foo', grafana_url: 'example.com', content: '{}' end.to raise_error(Puppet::Error, %r{not a valid URL}) end it "fails if settings isn't valid" do expect do described_class.new name: 'foo', grafana_url: 'http://example.com', settings: 'invalid' end.to raise_error(Puppet::Error, %r{settings should be a Hash}) end - - # rubocop:disable RSpec/MultipleExpectations it 'accepts valid parameters' do expect(gnotification[:name]).to eq('foo') expect(gnotification[:grafana_url]).to eq('http://example.com') expect(gnotification[:type]).to eq('email') expect(gnotification[:is_default]).to eq(:true) expect(gnotification[:send_reminder]).to eq(:true) expect(gnotification[:frequency]).to eq('20m') expect(gnotification[:settings]).to eq(adresses: 'test@example.com') end # rubocop:enable RSpec/MultipleExpectations it 'autorequires the grafana-server for proper ordering' do catalog = Puppet::Resource::Catalog.new service = Puppet::Type.type(:service).new(name: 'grafana-server') catalog.add_resource service catalog.add_resource gnotification relationship = gnotification.autorequire.find do |rel| (rel.source.to_s == 'Service[grafana-server]') && (rel.target.to_s == gnotification.to_s) end expect(relationship).to be_a Puppet::Relationship end it 'does not autorequire the service it is not managed' do catalog = Puppet::Resource::Catalog.new catalog.add_resource gnotification expect(gnotification.autorequire).to be_empty end end end diff --git a/spec/unit/puppet/type/grafana_organization_type_spec.rb b/spec/unit/puppet/type/grafana_organization_type_spec.rb index 522ec0e..e195920 100644 --- a/spec/unit/puppet/type/grafana_organization_type_spec.rb +++ b/spec/unit/puppet/type/grafana_organization_type_spec.rb @@ -1,55 +1,65 @@ require 'spec_helper' describe Puppet::Type.type(:grafana_organization) do let(:gorganization) do described_class.new( name: 'foo', grafana_url: 'http://example.com', grafana_user: 'admin', grafana_password: 'admin', address: { address1: 'test address1', address2: 'test address2', city: 'CityName', state: 'NewState', zipcode: '12345', country: 'USA' } ) end context 'when setting parameters' do it "fails if json_data isn't valid" do expect do described_class.new name: 'foo', address: 'invalid address' end.to raise_error(Puppet::Error, %r{address should be a Hash!}) end it "fails if grafana_url isn't HTTP-based" do expect do described_class.new name: 'foo', grafana_url: 'example.com', content: '{}', ensure: :present end.to raise_error(Puppet::Error, %r{not a valid URL}) end - - # rubocop:disable RSpec/MultipleExpectations it 'accepts valid parameters' do expect(gorganization[:name]).to eq('foo') expect(gorganization[:grafana_user]).to eq('admin') expect(gorganization[:grafana_password]).to eq('admin') expect(gorganization[:grafana_url]).to eq('http://example.com') expect(gorganization[:address]).to eq(address1: 'test address1', address2: 'test address2', city: 'CityName', state: 'NewState', zipcode: '12345', country: 'USA') end # rubocop:enable RSpec/MultipleExpectations it 'autorequires the grafana-server for proper ordering' do catalog = Puppet::Resource::Catalog.new service = Puppet::Type.type(:service).new(name: 'grafana-server') catalog.add_resource service catalog.add_resource gorganization relationship = gorganization.autorequire.find do |rel| (rel.source.to_s == 'Service[grafana-server]') && (rel.target.to_s == gorganization.to_s) end expect(relationship).to be_a Puppet::Relationship end it 'does not autorequire the service it is not managed' do catalog = Puppet::Resource::Catalog.new catalog.add_resource gorganization expect(gorganization.autorequire).to be_empty end + + it 'autorequires grafana_conn_validator' do + catalog = Puppet::Resource::Catalog.new + validator = Puppet::Type.type(:grafana_conn_validator).new(name: 'grafana') + catalog.add_resource validator + catalog.add_resource gorganization + + relationship = gorganization.autorequire.find do |rel| + (rel.source.to_s == 'Grafana_conn_validator[grafana]') && (rel.target.to_s == gorganization.to_s) + end + expect(relationship).to be_a Puppet::Relationship + end end end diff --git a/spec/unit/puppet/type/grafana_team_type_spec.rb b/spec/unit/puppet/type/grafana_team_type_spec.rb index 0f01c1b..e4cea6a 100644 --- a/spec/unit/puppet/type/grafana_team_type_spec.rb +++ b/spec/unit/puppet/type/grafana_team_type_spec.rb @@ -1,51 +1,61 @@ require 'spec_helper' describe Puppet::Type.type(:grafana_team) do let(:gteam) do described_class.new( name: 'foo', grafana_url: 'http://example.com', grafana_user: 'admin', grafana_password: 'admin', home_dashboard: 'foo_dashboard', organization: 'foo_organization' ) end context 'when setting parameters' do it "fails if grafana_url isn't HTTP-based" do expect do described_class.new name: 'foo', grafana_url: 'example.com', content: '{}', ensure: :present end.to raise_error(Puppet::Error, %r{not a valid URL}) end - - # rubocop:disable RSpec/MultipleExpectations it 'accepts valid parameters' do expect(gteam[:name]).to eq('foo') expect(gteam[:grafana_user]).to eq('admin') expect(gteam[:grafana_password]).to eq('admin') expect(gteam[:grafana_url]).to eq('http://example.com') expect(gteam[:home_dashboard]).to eq('foo_dashboard') expect(gteam[:organization]).to eq('foo_organization') end # rubocop:enable RSpec/MultipleExpectations it 'autorequires the grafana-server for proper ordering' do catalog = Puppet::Resource::Catalog.new service = Puppet::Type.type(:service).new(name: 'grafana-server') catalog.add_resource service catalog.add_resource gteam relationship = gteam.autorequire.find do |rel| (rel.source.to_s == 'Service[grafana-server]') && (rel.target.to_s == gteam.to_s) end expect(relationship).to be_a Puppet::Relationship end it 'does not autorequire the service it is not managed' do catalog = Puppet::Resource::Catalog.new catalog.add_resource gteam expect(gteam.autorequire).to be_empty end + + it 'autorequires grafana_conn_validator' do + catalog = Puppet::Resource::Catalog.new + validator = Puppet::Type.type(:grafana_conn_validator).new(name: 'grafana') + catalog.add_resource validator + catalog.add_resource gteam + + relationship = gteam.autorequire.find do |rel| + (rel.source.to_s == 'Grafana_conn_validator[grafana]') && (rel.target.to_s == gteam.to_s) + end + expect(relationship).to be_a Puppet::Relationship + end end end diff --git a/spec/unit/puppet/type/grafana_user_type_spec.rb b/spec/unit/puppet/type/grafana_user_type_spec.rb index f8f63b9..c45aa90 100644 --- a/spec/unit/puppet/type/grafana_user_type_spec.rb +++ b/spec/unit/puppet/type/grafana_user_type_spec.rb @@ -1,50 +1,59 @@ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. # You may obtain a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # See the License for the specific language governing permissions and # limitations under the License. require 'spec_helper' describe Puppet::Type.type(:grafana_user) do let(:guser) do described_class.new name: 'test', full_name: 'Mr tester', password: 't3st', grafana_url: 'http://example.com/' end context 'when setting parameters' do it "fails if grafana_url isn't HTTP-based" do expect do described_class.new name: 'test', grafana_url: 'example.com' end.to raise_error(Puppet::Error, %r{not a valid URL}) end - - # rubocop:disable RSpec/MultipleExpectations it 'accepts valid parameters' do expect(guser[:name]).to eq('test') expect(guser[:full_name]).to eq('Mr tester') expect(guser[:password]).to eq('t3st') expect(guser[:grafana_url]).to eq('http://example.com/') end it 'autorequires the grafana-server for proper ordering' do catalog = Puppet::Resource::Catalog.new service = Puppet::Type.type(:service).new(name: 'grafana-server') catalog.add_resource service catalog.add_resource guser relationship = guser.autorequire.find do |rel| (rel.source.to_s == 'Service[grafana-server]') && (rel.target.to_s == guser.to_s) end expect(relationship).to be_a Puppet::Relationship end it 'does not autorequire the service it is not managed' do catalog = Puppet::Resource::Catalog.new catalog.add_resource guser expect(guser.autorequire).to be_empty end + it 'autorequires grafana_conn_validator' do + catalog = Puppet::Resource::Catalog.new + validator = Puppet::Type.type(:grafana_conn_validator).new(name: 'grafana') + catalog.add_resource validator + catalog.add_resource guser + + relationship = guser.autorequire.find do |rel| + (rel.source.to_s == 'Grafana_conn_validator[grafana]') && (rel.target.to_s == guser.to_s) + end + expect(relationship).to be_a Puppet::Relationship + end end end