├── .editorconfig ├── .fixtures.yml ├── .github ├── CONTRIBUTING.md ├── ISSUE_TEMPLATE.md ├── PULL_REQUEST_TEMPLATE.md ├── labeler.yml ├── release.yml └── workflows │ ├── ci.yml │ ├── labeler.yml │ ├── prepare_release.yml │ └── release.yml ├── .gitignore ├── .msync.yml ├── .overcommit.yml ├── .pmtignore ├── .puppet-lint.rc ├── .rubocop.yml ├── .rubocop_todo.yml ├── .sync.yml ├── CHANGELOG.md ├── Gemfile ├── HACKING.md ├── HISTORY.md ├── LICENSE ├── NATIVE_TYPES_AND_PROVIDERS.md ├── README.md ├── REFERENCE.md ├── Rakefile ├── Vagrantfile ├── examples ├── FreeBSD.pp ├── OpenBSD.pp ├── RedHatEnterpriseServer.pp ├── Ubuntu.pp ├── job-configuration │ ├── build.pp │ └── templates │ │ └── build.xml.erb └── plugin-configuration │ ├── git.pp │ └── templates │ └── git.config.xml.erb ├── files └── puppet_helper.groovy ├── lib ├── facter │ └── jenkins.rb └── puppet │ ├── jenkins.rb │ ├── jenkins │ └── plugins.rb │ ├── parser │ └── functions │ │ ├── jenkins_port.rb │ │ └── jenkins_prefix.rb │ ├── provider │ ├── jenkins_authorization_strategy │ │ └── cli.rb │ ├── jenkins_credentials │ │ └── cli.rb │ ├── jenkins_job │ │ └── cli.rb │ ├── jenkins_num_executors │ │ └── cli.rb │ ├── jenkins_security_realm │ │ └── cli.rb │ ├── jenkins_slaveagent_port │ │ └── cli.rb │ └── jenkins_user │ │ └── cli.rb │ ├── type │ ├── jenkins_authorization_strategy.rb │ ├── jenkins_credentials.rb │ ├── jenkins_job.rb │ ├── jenkins_num_executors.rb │ ├── jenkins_security_realm.rb │ ├── jenkins_slaveagent_port.rb │ └── jenkins_user.rb │ └── x │ ├── jenkins.rb │ └── jenkins │ ├── config.rb │ ├── provider.rb │ ├── provider │ └── cli.rb │ ├── type.rb │ ├── type │ └── cli.rb │ └── util.rb ├── manifests ├── augeas.pp ├── cli.pp ├── cli │ ├── config.pp │ ├── exec.pp │ └── reload.pp ├── cli_helper.pp ├── config.pp ├── credentials.pp ├── direct_download.pp ├── firewall.pp ├── init.pp ├── job.pp ├── job │ ├── absent.pp │ └── present.pp ├── jobs.pp ├── master.pp ├── package.pp ├── params.pp ├── plugin.pp ├── plugins.pp ├── proxy.pp ├── repo.pp ├── repo │ ├── debian.pp │ ├── el.pp │ └── suse.pp ├── security.pp ├── service.pp ├── slave.pp ├── user.pp ├── user_setup.pp └── users.pp ├── metadata.json ├── spec ├── acceptance │ ├── class_spec.rb │ ├── hieradata │ │ ├── common.yaml │ │ └── family │ │ │ ├── Debian.yaml │ │ │ └── RedHat.yaml │ ├── job_spec.rb │ ├── plugin_spec.rb │ ├── slave_spec.rb │ └── xtypes │ │ ├── jenkins_credentials_spec.rb │ │ └── jenkins_job_spec.rb ├── classes │ ├── cli │ │ └── config_spec.rb │ ├── jenkins_cli_helper_spec.rb │ ├── jenkins_cli_spec.rb │ ├── jenkins_config_spec.rb │ ├── jenkins_direct_download_spec.rb │ ├── jenkins_firewall_spec.rb │ ├── jenkins_jobs_spec.rb │ ├── jenkins_master_spec.rb │ ├── jenkins_package_spec.rb │ ├── jenkins_plugins_spec.rb │ ├── jenkins_proxy_spec.rb │ ├── jenkins_repo_debian_spec.rb │ ├── jenkins_repo_el_spec.rb │ ├── jenkins_repo_spec.rb │ ├── jenkins_repo_suse_spec.rb │ ├── jenkins_security_spec.rb │ ├── jenkins_slave_spec.rb │ ├── jenkins_spec.rb │ ├── jenkins_user_setup_spec.rb │ └── jenkins_users_spec.rb ├── defines │ ├── jenkins_augeas_spec.rb │ ├── jenkins_cli_exec_spec.rb │ ├── jenkins_credentials_spec.rb │ ├── jenkins_job_present_spec.rb │ ├── jenkins_job_spec.rb │ ├── jenkins_plugin_spec.rb │ └── jenkins_user_spec.rb ├── fixtures │ └── testjob.xml ├── functions │ ├── jenkins_port_spec.rb │ └── jenkins_prefix_spec.rb ├── setup_acceptance_node.pp ├── spec_helper.rb ├── spec_helper_acceptance.rb └── unit │ ├── facter │ └── plugins_spec.rb │ ├── jenkins_plugins_spec.rb │ ├── jenkins_spec.rb │ └── puppet │ ├── provider │ ├── jenkins_authorization_strategy │ │ └── cli_spec.rb │ ├── jenkins_credentials │ │ └── cli_spec.rb │ ├── jenkins_job │ │ └── cli_spec.rb │ ├── jenkins_num_executors │ │ └── cli_spec.rb │ ├── jenkins_security_realm │ │ └── cli_spec.rb │ ├── jenkins_slaveagent_port │ │ └── cli_spec.rb │ └── jenkins_user │ │ └── cli_spec.rb │ ├── type │ ├── jenkins_authorization_strategy_spec.rb │ ├── jenkins_credentials_spec.rb │ ├── jenkins_job_spec.rb │ ├── jenkins_num_executors_spec.rb │ ├── jenkins_security_realm_spec.rb │ ├── jenkins_slaveagent_port_spec.rb │ └── jenkins_user_spec.rb │ └── x │ ├── jenkins │ ├── config_spec.rb │ ├── provider │ │ └── cli_spec.rb │ ├── type │ │ └── cli_spec.rb │ └── util_spec.rb │ ├── spec_jenkins_providers.rb │ └── spec_jenkins_types.rb ├── templates ├── jenkins-override.epp ├── jenkins-slave-defaults.erb ├── jenkins-slave-run.erb ├── jenkins-slave.service.erb ├── org.jenkins-ci.slave.jnlp.plist.epp └── proxy.xml.erb └── types └── tunnel.pp /.editorconfig: -------------------------------------------------------------------------------- 1 | # editorconfig.org 2 | 3 | # Managed by modulesync - DO NOT EDIT 4 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 5 | 6 | root = true 7 | 8 | [*] 9 | charset = utf-8 10 | end_of_line = lf 11 | indent_size = 2 12 | tab_width = 2 13 | indent_style = space 14 | insert_final_newline = true 15 | trim_trailing_whitespace = true 16 | -------------------------------------------------------------------------------- /.fixtures.yml: -------------------------------------------------------------------------------- 1 | --- 2 | fixtures: 3 | repositories: 4 | apt: "https://github.com/puppetlabs/puppetlabs-apt" 5 | stdlib: "https://github.com/puppetlabs/puppetlabs-stdlib" 6 | java: "https://github.com/puppetlabs/puppetlabs-java" 7 | zypprepo: "https://github.com/voxpupuli/puppet-zypprepo.git" 8 | archive: "https://github.com/voxpupuli/puppet-archive.git" 9 | systemd: "https://github.com/voxpupuli/puppet-systemd" 10 | yumrepo_core: https://github.com/puppetlabs/puppetlabs-yumrepo_core.git 11 | augeas_core: https://github.com/puppetlabs/puppetlabs-augeas_core.git 12 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 10 | 11 | ## Affected Puppet, Ruby, OS and module versions/distributions 12 | 13 | - Puppet: 14 | - Ruby: 15 | - Distribution: 16 | - Module version: 17 | 18 | ## How to reproduce (e.g Puppet code you use) 19 | 20 | ## What are you seeing 21 | 22 | ## What behaviour did you expect instead 23 | 24 | ## Output log 25 | 26 | ## Any additional information you'd like to impart 27 | -------------------------------------------------------------------------------- /.github/PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | 9 | #### Pull Request (PR) description 10 | 13 | 14 | #### This Pull Request (PR) fixes the following issues 15 | 21 | -------------------------------------------------------------------------------- /.github/labeler.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | skip-changelog: 6 | - head-branch: ['^release-*', 'release'] 7 | -------------------------------------------------------------------------------- /.github/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | # https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes 6 | 7 | changelog: 8 | exclude: 9 | labels: 10 | - duplicate 11 | - invalid 12 | - modulesync 13 | - question 14 | - skip-changelog 15 | - wont-fix 16 | - wontfix 17 | 18 | categories: 19 | - title: Breaking Changes 🛠 20 | labels: 21 | - backwards-incompatible 22 | 23 | - title: New Features 🎉 24 | labels: 25 | - enhancement 26 | 27 | - title: Bug Fixes 🐛 28 | labels: 29 | - bug 30 | 31 | - title: Documentation Updates 📚 32 | labels: 33 | - documentation 34 | - docs 35 | 36 | - title: Dependency Updates ⬆️ 37 | labels: 38 | - dependencies 39 | 40 | - title: Other Changes 41 | labels: 42 | - "*" 43 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | name: CI 6 | 7 | # yamllint disable-line rule:truthy 8 | on: 9 | pull_request: {} 10 | push: 11 | branches: 12 | - main 13 | - master 14 | 15 | concurrency: 16 | group: ${{ github.ref_name }} 17 | cancel-in-progress: true 18 | 19 | jobs: 20 | puppet: 21 | name: Puppet 22 | uses: voxpupuli/gha-puppet/.github/workflows/beaker.yml@v3 23 | -------------------------------------------------------------------------------- /.github/workflows/labeler.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | name: "Pull Request Labeler" 6 | 7 | # yamllint disable-line rule:truthy 8 | on: 9 | pull_request_target: {} 10 | 11 | jobs: 12 | labeler: 13 | permissions: 14 | contents: read 15 | pull-requests: write 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/labeler@v5 19 | -------------------------------------------------------------------------------- /.github/workflows/prepare_release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | name: 'Prepare Release' 6 | 7 | on: 8 | workflow_dispatch: 9 | inputs: 10 | version: 11 | description: 'Module version to be released. Must be a valid semver string without leading v. (1.2.3)' 12 | required: false 13 | 14 | jobs: 15 | release_prep: 16 | uses: 'voxpupuli/gha-puppet/.github/workflows/prepare_release.yml@v3' 17 | with: 18 | version: ${{ github.event.inputs.version }} 19 | allowed_owner: 'voxpupuli' 20 | secrets: 21 | # Configure secrets here: 22 | # https://docs.github.com/en/actions/security-guides/encrypted-secrets 23 | github_pat: '${{ secrets.PCCI_PAT_RELEASE_PREP }}' 24 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | name: Release 6 | 7 | # yamllint disable-line rule:truthy 8 | on: 9 | push: 10 | tags: 11 | - '*' 12 | 13 | jobs: 14 | release: 15 | name: Release 16 | uses: voxpupuli/gha-puppet/.github/workflows/release.yml@v3 17 | with: 18 | allowed_owner: 'voxpupuli' 19 | secrets: 20 | # Configure secrets here: 21 | # https://docs.github.com/en/actions/security-guides/encrypted-secrets 22 | username: ${{ secrets.PUPPET_FORGE_USERNAME }} 23 | api_key: ${{ secrets.PUPPET_FORGE_API_KEY }} 24 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Managed by modulesync - DO NOT EDIT 2 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 3 | 4 | /pkg/ 5 | /Gemfile.lock 6 | /Gemfile.local 7 | /vendor/ 8 | /.vendor/ 9 | /spec/fixtures/manifests/ 10 | /spec/fixtures/modules/ 11 | /.vagrant/ 12 | /.bundle/ 13 | /.ruby-version 14 | /coverage/ 15 | /log/ 16 | /.idea/ 17 | /.dependencies/ 18 | /.librarian/ 19 | /Puppetfile.lock 20 | *.iml 21 | .*.sw? 22 | /.yardoc/ 23 | /Guardfile 24 | bolt-debug.log 25 | .rerun.json 26 | -------------------------------------------------------------------------------- /.msync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | # Managed by modulesync - DO NOT EDIT 3 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 4 | 5 | modulesync_config_version: '9.4.0' 6 | -------------------------------------------------------------------------------- /.overcommit.yml: -------------------------------------------------------------------------------- 1 | # Managed by modulesync - DO NOT EDIT 2 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 3 | # 4 | # Hooks are only enabled if you take action. 5 | # 6 | # To enable the hooks run: 7 | # 8 | # ``` 9 | # bundle exec overcommit --install 10 | # # ensure .overcommit.yml does not harm to you and then 11 | # bundle exec overcommit --sign 12 | # ``` 13 | # 14 | # (it will manage the .git/hooks directory): 15 | # 16 | # Examples howto skip a test for a commit or push: 17 | # 18 | # ``` 19 | # SKIP=RuboCop git commit 20 | # SKIP=PuppetLint git commit 21 | # SKIP=RakeTask git push 22 | # ``` 23 | # 24 | # Don't invoke overcommit at all: 25 | # 26 | # ``` 27 | # OVERCOMMIT_DISABLE=1 git commit 28 | # ``` 29 | # 30 | # Read more about overcommit: https://github.com/brigade/overcommit 31 | # 32 | # To manage this config yourself in your module add 33 | # 34 | # ``` 35 | # .overcommit.yml: 36 | # unmanaged: true 37 | # ``` 38 | # 39 | # to your modules .sync.yml config 40 | --- 41 | PreCommit: 42 | RuboCop: 43 | enabled: true 44 | description: 'Runs rubocop on modified files only' 45 | command: ['bundle', 'exec', 'rubocop'] 46 | RakeTarget: 47 | enabled: true 48 | description: 'Runs lint on modified files only' 49 | targets: 50 | - 'lint' 51 | command: ['bundle', 'exec', 'rake'] 52 | YamlSyntax: 53 | enabled: true 54 | JsonSyntax: 55 | enabled: true 56 | TrailingWhitespace: 57 | enabled: true 58 | 59 | PrePush: 60 | RakeTarget: 61 | enabled: true 62 | description: 'Run rake targets' 63 | targets: 64 | - 'validate' 65 | - 'test' 66 | - 'rubocop' 67 | command: ['bundle', 'exec', 'rake'] 68 | -------------------------------------------------------------------------------- /.pmtignore: -------------------------------------------------------------------------------- 1 | # Managed by modulesync - DO NOT EDIT 2 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 3 | 4 | /docs/ 5 | /pkg/ 6 | /Gemfile 7 | /Gemfile.lock 8 | /Gemfile.local 9 | /vendor/ 10 | /.vendor/ 11 | /spec/ 12 | /Rakefile 13 | /.vagrant/ 14 | /.bundle/ 15 | /.ruby-version 16 | /coverage/ 17 | /log/ 18 | /.idea/ 19 | /.dependencies/ 20 | /.github/ 21 | /.librarian/ 22 | /Puppetfile.lock 23 | /Puppetfile 24 | *.iml 25 | /.editorconfig 26 | /.fixtures.yml 27 | /.gitignore 28 | /.msync.yml 29 | /.overcommit.yml 30 | /.pmtignore 31 | /.rspec 32 | /.rspec_parallel 33 | /.rubocop.yml 34 | /.sync.yml 35 | .*.sw? 36 | /.yardoc/ 37 | /.yardopts 38 | /Dockerfile 39 | /HISTORY.md 40 | -------------------------------------------------------------------------------- /.puppet-lint.rc: -------------------------------------------------------------------------------- 1 | # Managed by modulesync - DO NOT EDIT 2 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 3 | 4 | --fail-on-warnings 5 | --no-parameter_documentation-check 6 | --no-parameter_types-check 7 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | inherit_from: .rubocop_todo.yml 3 | 4 | inherit_gem: 5 | voxpupuli-test: rubocop.yml 6 | 7 | Lint/ConstantDefinitionInBlock: 8 | Enabled: false 9 | 10 | RSpec/ExpectInHook: 11 | Enabled: false 12 | 13 | RSpec/LeakyConstantDeclaration: 14 | Enabled: false 15 | 16 | RSpec/MessageSpies: 17 | Enabled: false 18 | 19 | RSpec/MultipleMemoizedHelpers: 20 | Enabled: false 21 | 22 | RSpec/StubbedMock: 23 | Enabled: false 24 | 25 | Style/ClassVars: 26 | Enabled: false 27 | 28 | Style/IfUnlessModifier: 29 | Enabled: false 30 | -------------------------------------------------------------------------------- /.rubocop_todo.yml: -------------------------------------------------------------------------------- 1 | # This configuration was generated by 2 | # `rubocop --auto-gen-config` 3 | # on 2023-08-17 21:33:46 UTC using RuboCop version 1.50.2. 4 | # The point is for the user to remove these configuration records 5 | # one by one as the offenses are removed from the code base. 6 | # Note that changes in the inspected code, or installation of new 7 | # versions of RuboCop, may require this file to be generated again. 8 | 9 | # Offense count: 9 10 | # This cop supports unsafe autocorrection (--autocorrect-all). 11 | RSpec/BeEq: 12 | Exclude: 13 | - 'spec/unit/puppet/provider/jenkins_job/cli_spec.rb' 14 | - 'spec/unit/puppet/x/jenkins/provider/cli_spec.rb' 15 | 16 | # Offense count: 2 17 | # This cop supports unsafe autocorrection (--autocorrect-all). 18 | # Configuration parameters: . 19 | # SupportedStyles: constant, string 20 | RSpec/VerifiedDoubleReference: 21 | EnforcedStyle: string 22 | 23 | # Offense count: 1 24 | # This cop supports unsafe autocorrection (--autocorrect-all). 25 | Style/SlicingWithRange: 26 | Exclude: 27 | - 'lib/puppet/jenkins/plugins.rb' 28 | -------------------------------------------------------------------------------- /.sync.yml: -------------------------------------------------------------------------------- 1 | --- 2 | .rubocop.yml: 3 | unmanaged: true 4 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # Managed by modulesync - DO NOT EDIT 2 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 3 | 4 | source ENV['GEM_SOURCE'] || 'https://rubygems.org' 5 | 6 | group :test do 7 | gem 'voxpupuli-test', '~> 9.0', :require => false 8 | gem 'coveralls', :require => false 9 | gem 'simplecov-console', :require => false 10 | gem 'puppet_metadata', '~> 4.0', :require => false 11 | end 12 | 13 | group :development do 14 | gem 'guard-rake', :require => false 15 | gem 'overcommit', '>= 0.39.1', :require => false 16 | end 17 | 18 | group :system_tests do 19 | gem 'voxpupuli-acceptance', '~> 3.0', :require => false 20 | end 21 | 22 | group :release do 23 | gem 'voxpupuli-release', '~> 3.0', :require => false 24 | end 25 | 26 | gem 'rake', :require => false 27 | gem 'facter', ENV['FACTER_GEM_VERSION'], :require => false, :groups => [:test] 28 | 29 | puppetversion = ENV['PUPPET_GEM_VERSION'] || [">= 7.24", "< 9"] 30 | gem 'puppet', puppetversion, :require => false, :groups => [:test] 31 | 32 | # vim: syntax=ruby 33 | -------------------------------------------------------------------------------- /HACKING.md: -------------------------------------------------------------------------------- 1 | # Developing/Contributing 2 | 3 | ## Testing 4 | 5 | This module has behavior tests written using [RSpec 2](https://www.relishapp.com/rspec), 6 | is syntax checked with [puppet-syntax](https://github.com/gds-operations/puppet-syntax), and style checked with [puppet-lint](http://puppet-lint.com/). 7 | The goal of these tests are to validate the expected behavior of the module. 8 | As more features and platform support are added to this module the tests 9 | provide an automated way to validate the expectations previous contributors 10 | have specified. 11 | 12 | In order to validate behavior setup fixtures with `rake spec_prep` and then 13 | execute code with `rake spec_standalone`. 14 | 15 | % rake spec_standalone 16 | (in /Users/jeff/vms/puppet/modules/jenkins) 17 | . 18 | Finished in 0.31279 seconds 19 | 1 example, 0 failures 20 | 21 | Lint, spec, and syntax checks can be run by using the default rake task by 22 | simply running 'rake'. 23 | 24 | ### Lint checking 25 | 26 | The lint checks require the `puppet-lint` gem to be installed. Running 27 | 'rake lint' will lint check all of the *.pp files to ensure they conform to the 28 | puppet style guide. 29 | 30 | ### RSpec Testing Requirements 31 | 32 | The spec tests require the `rspec-puppet` gem to be installed. Running 'rake spec' 33 | will automatically check out all of the modules in the .fixtures.yml needed to run 34 | the tests. 35 | 36 | ### Syntax checking 37 | 38 | The syntax checks require the `puppet-syntax` gem to be installed. Running 39 | 'rake syntax' will sytanx check the manifests and templates. 40 | 41 | ### Installing Testing Requirements 42 | 43 | To install the testing requirements: 44 | 45 | % gem install rspec-puppet puppet-lint puppet-syntax --no-ri --no-rdoc 46 | Successfully installed rspec-core-2.14.5 47 | Successfully installed diff-lcs-1.2.4 48 | Successfully installed rspec-expectations-2.14.3 49 | Successfully installed rspec-mocks-2.14.3 50 | Successfully installed rspec-2.14.1 51 | Successfully installed rspec-puppet-0.1.6 52 | Successfully installed puppet-lint-0.3.2 53 | Successfully installed rake-10.1.0 54 | Successfully installed puppet-syntax-1.1.0 55 | 10 gems installed 56 | 57 | ### Adding Tests 58 | 59 | Please see the [rspec-puppet](https://github.com/rodjek/rspec-puppet) project 60 | for information on writing tests. A basic test that validates the class is 61 | declared in the catalog is provided in the file 62 | `spec/classes/jenkins_spec.rb`. `rspec-puppet` automatically uses the top 63 | level description as the name of a module to include in the catalog. 64 | Resources may be validated in the catalog using: 65 | 66 | * `contain_class('myclass')` 67 | * `contain_service('sshd')` 68 | * `contain_file('/etc/puppet')` 69 | * `contain_package('puppet')` 70 | * And so forth for other Puppet resources. 71 | 72 | ## Acceptance testings 73 | 74 | Acceptance tests are setup using [Beaker](https://github.com/puppetlabs/beaker), which will spin up an instance (by default [Vagrant](https://www.vagrantup.com/), but also supports various VPC's), apply the puppet code against this spun up node and then uses [Serverspec](http://serverspec.org/) tests to validate behaviour. 75 | 76 | To run the tests: 77 | 78 | * `bundle exec rspec spec/acceptance/` 79 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | # Managed by modulesync - DO NOT EDIT 2 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 3 | 4 | # Attempt to load voxpupuli-test (which pulls in puppetlabs_spec_helper), 5 | # otherwise attempt to load it directly. 6 | begin 7 | require 'voxpupuli/test/rake' 8 | rescue LoadError 9 | begin 10 | require 'puppetlabs_spec_helper/rake_tasks' 11 | rescue LoadError 12 | end 13 | end 14 | 15 | # load optional tasks for acceptance 16 | # only available if gem group releases is installed 17 | begin 18 | require 'voxpupuli/acceptance/rake' 19 | rescue LoadError 20 | end 21 | 22 | # load optional tasks for releases 23 | # only available if gem group releases is installed 24 | begin 25 | require 'voxpupuli/release/rake_tasks' 26 | rescue LoadError 27 | # voxpupuli-release not present 28 | else 29 | GCGConfig.user = 'voxpupuli' 30 | GCGConfig.project = 'puppet-jenkins' 31 | end 32 | 33 | desc "Run main 'test' task and report merged results to coveralls" 34 | task test_with_coveralls: [:test] do 35 | if Dir.exist?(File.expand_path('../lib', __FILE__)) 36 | require 'coveralls/rake/task' 37 | Coveralls::RakeTask.new 38 | Rake::Task['coveralls:push'].invoke 39 | else 40 | puts 'Skipping reporting to coveralls. Module has no lib dir' 41 | end 42 | end 43 | 44 | # vim: syntax=ruby 45 | -------------------------------------------------------------------------------- /Vagrantfile: -------------------------------------------------------------------------------- 1 | require 'yaml' 2 | ENV['VAGRANT_DEFAULT_PROVIDER'] = 'aws' 3 | 4 | Vagrant.configure('2') do |config| 5 | access_key_id = File.read('.vagrant_key_id').chomp 6 | secret_access_key = File.read('.vagrant_secret_access_key').chomp 7 | keypair = File.read('.vagrant_keypair_name').chomp 8 | 9 | config.vm.box = 'dummy' 10 | 11 | Dir['spec/serverspec/*'].each do |dname| 12 | next unless File.directory?(dname) 13 | # Convert spec/serverspec/ubuntu-precise into 'ubuntu-precise' 14 | name = File.basename(dname) 15 | spec_config = YAML.load_file(File.join(dname + '/config.yml')) 16 | 17 | config.vm.synced_folder '.', '/vagrant/jenkins', type: 'rsync' 18 | 19 | config.vm.define(name) do |node| 20 | # This is a Vagrant-local hack to make sure we have properly udpated apt 21 | # caches since AWS machines are definitely going to have stale ones 22 | node.vm.provision 'shell', 23 | :inline => 'if [ ! -f "/apt-cached" ]; then apt-get update && touch /apt-cached; fi' 24 | node.vm.provision 'shell', 25 | :inline => 'ln -sf /tmp/vagrant-puppet-2/modules-0 /tmp/vagrant-puppet-2/modules-0/jenkins' 26 | 27 | node.vm.provision 'puppet' do |pp| 28 | pp.module_path = [ 29 | '.', 30 | 'spec/fixtures/modules', 31 | ] 32 | pp.manifests_path = "spec/serverspec/#{name}/manifests" 33 | end 34 | 35 | 36 | node.vm.provision :serverspec do |spec| 37 | spec.pattern = "spec/serverspec/#{name}/*_spec.rb" 38 | end 39 | 40 | node.vm.provider :aws do |aws, override| 41 | aws.access_key_id = access_key_id 42 | aws.secret_access_key = secret_access_key 43 | aws.keypair_name = keypair 44 | 45 | hostname = "vagrant-jenkins-#{name}" 46 | # Ensuring that our machines hostname is "correct" so Puppet will apply 47 | # the right resources to it 48 | aws.user_data = "#!/bin/sh 49 | echo '#{hostname}' > /etc/hostname; 50 | hostname '#{hostname}';" 51 | 52 | aws.tags = {:Name => hostname} 53 | 54 | # Ubuntu LTS 12.04 in us-west-2 with Puppet installed from the Puppet 55 | # Labs apt repository 56 | aws.ami = spec_config['ami'] 57 | aws.region = spec_config['region'] 58 | override.ssh.username = spec_config['username'] 59 | override.ssh.private_key_path = File.expand_path('~/.ssh/id_rsa') 60 | end 61 | end 62 | end 63 | end 64 | 65 | # vim: ft=ruby 66 | -------------------------------------------------------------------------------- /examples/FreeBSD.pp: -------------------------------------------------------------------------------- 1 | node default { 2 | # Requires Module zleslie-pkgng 3 | 4 | Package { 5 | provider => 'pkgng', 6 | } 7 | 8 | package { 'openjdk': 9 | ensure => installed, 10 | } 11 | -> class { 'jenkins': 12 | install_java => false, 13 | repo => false, 14 | } 15 | # Runs on Port 8180 16 | } 17 | -------------------------------------------------------------------------------- /examples/OpenBSD.pp: -------------------------------------------------------------------------------- 1 | node default { 2 | package { 'jre': 3 | ensure => installed, 4 | } 5 | -> class { 'jenkins': 6 | install_java => false, 7 | repo => false, 8 | } 9 | # Runs on Port 8000 10 | } 11 | -------------------------------------------------------------------------------- /examples/RedHatEnterpriseServer.pp: -------------------------------------------------------------------------------- 1 | node default { 2 | include jenkins 3 | 4 | jenkins::plugin { 5 | 'ansicolor' : 6 | version => '0.3.1'; 7 | } 8 | 9 | jenkins::job { 10 | 'build' : 11 | config => ' 12 | 13 | 14 | 15 | false 16 | 17 | 18 | true 19 | false 20 | false 21 | false 22 | 23 | false 24 | 25 | 26 | 27 | '; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/Ubuntu.pp: -------------------------------------------------------------------------------- 1 | node default { 2 | include jenkins 3 | 4 | jenkins::plugin { 5 | 'ansicolor' : 6 | version => '0.3.1'; 7 | } 8 | 9 | jenkins::job { 10 | 'build' : 11 | config => ' 12 | 13 | 14 | 15 | false 16 | 17 | 18 | true 19 | false 20 | false 21 | false 22 | 23 | false 24 | 25 | 26 | 27 | '; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /examples/job-configuration/build.pp: -------------------------------------------------------------------------------- 1 | # lint:ignore:autoloader_layout 2 | class jenkins::job::build ( 3 | # lint:endignore 4 | $config = undef, 5 | $jobname = $title, 6 | $ensure = 'present', 7 | ) { 8 | if $config == undef { 9 | $real_content = template('jenkins/job/build.xml.erb') 10 | } else { 11 | $real_content = $config 12 | } 13 | 14 | jenkins::job { 'build': 15 | ensure => $ensure, 16 | jobname => $jobname, 17 | config => $real_content, 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /examples/job-configuration/templates/build.xml.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | false 6 | 7 | 8 | true 9 | false 10 | false 11 | false 12 | 13 | false 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /examples/plugin-configuration/git.pp: -------------------------------------------------------------------------------- 1 | # lint:ignore:autoloader_layout 2 | class jenkins::plugin::git ( 3 | # lint:endignore 4 | $version = 0, 5 | $config_filename = 'hudson.plugins.git.GitSCM.xml', 6 | $config_content = undef, 7 | $git_name = 'Jenkins', 8 | $git_email = 'jenkins@example.net', 9 | Boolean $git_create_account = false, 10 | ) { 11 | if $config_content == undef { 12 | $real_content = template('jenkins/plugin/git.config.xml.erb') 13 | } else { 14 | $real_content = $config_content 15 | } 16 | 17 | jenkins::plugin { 'git': 18 | version => $version, 19 | config_filename => $config_filename, 20 | config_content => $real_content, 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /examples/plugin-configuration/templates/git.config.xml.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 1 4 | <%= @git_name %> 5 | <%= @git_email %> 6 | <%= @git_create_account %> 7 | 8 | -------------------------------------------------------------------------------- /lib/facter/jenkins.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # jenkins.rb 4 | # 5 | # Creates a fact 'jenkins_plugins' containing a comma-delimited string of all 6 | # jenkins plugins + versions. 7 | require 'facter' 8 | require_relative '../puppet/jenkins/plugins' 9 | 10 | Facter.add(:jenkins_plugins) do 11 | confine kernel: 'Linux' 12 | setcode do 13 | plugins = Puppet::Jenkins::Plugins.available 14 | plugins.keys.sort.map { |plugin| "#{plugin} #{plugins[plugin][:plugin_version]}" }.join(', ') 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/puppet/jenkins.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Puppet 4 | module Jenkins 5 | # @return [String] Full path to the Jenkins user's home directory 6 | def self.home_dir 7 | File.expand_path('~jenkins') 8 | rescue ArgumentError 9 | # The Jenkins user doesn't exist! 10 | nil 11 | end 12 | 13 | # @return [String] Full path to the Jenkins user's plugin directory 14 | def self.plugins_dir 15 | File.join(home_dir, 'plugins') 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /lib/puppet/jenkins/plugins.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../jenkins' 4 | 5 | module Puppet 6 | module Jenkins 7 | module Plugins 8 | # Return structured data for the given plugin manifest string 9 | # 10 | # @return [Hash] A hash containing symbolized manifest keys and their 11 | # string values 12 | # @return [NilClass] A nil if +manifest_str+ nil or an empty string 13 | def self.manifest_data(manifest_str) 14 | return {} if manifest_str.nil? || manifest_str.empty? 15 | 16 | data = {} 17 | manifest_str.split("\n").each do |line| 18 | next if line.empty? 19 | 20 | # Parse out "Plugin-Version: 1.2" for example 21 | parts = line.split(': ') 22 | 23 | # If the line starts with a space or we can't get at least two parts 24 | # (key and value), that means it's really just a word-wrap from the 25 | # previous line, and not a key, skip! 26 | next if parts.size < 2 27 | next if parts.first[0] == ' ' 28 | 29 | key = parts.first.downcase.tr('-', '_').chomp 30 | # Skip garbage keys 31 | next if key.nil? || key.empty? 32 | 33 | # Re-join any colon delimited strings in the value back together, 34 | # e.g.: "http://wiki.jenkins-ci.org/display/JENKINS/Ant+Plugin" 35 | value = parts[1..-1].join(':').chomp 36 | 37 | data[key.to_sym] = value 38 | end 39 | 40 | data 41 | end 42 | 43 | # @return [Hash] a +Hash+ containing a mapping of a plugin name to its 44 | # manifest data 45 | def self.available 46 | return {} unless exists? 47 | 48 | plugins = {} 49 | Dir.entries(Puppet::Jenkins.plugins_dir).each do |plugin| 50 | # Skip useless directories 51 | next if plugin == '..' 52 | next if plugin == '.' 53 | 54 | plugin_dir = File.join(Puppet::Jenkins.plugins_dir, plugin) 55 | # Without an unpacked plugin directory, we can't find a version 56 | next unless File.directory?(plugin_dir) 57 | 58 | manifest = File.join(plugin_dir, 'META-INF', 'MANIFEST.MF') 59 | begin 60 | manifest = manifest_data(File.read(manifest)) 61 | plugins[plugin] = manifest if manifest 62 | rescue StandardError 63 | # Nothing really to do about it, failing means no version which will 64 | # result in a new plugin if needed 65 | nil 66 | end 67 | end 68 | plugins 69 | end 70 | 71 | # Determine whether or not the jenkins plugin directory exists 72 | # 73 | # @return [Boolean] T 74 | def self.exists? 75 | home = Puppet::Jenkins.home_dir 76 | return false if home.nil? 77 | return false unless File.directory? Puppet::Jenkins.plugins_dir 78 | 79 | true 80 | end 81 | end 82 | end 83 | end 84 | -------------------------------------------------------------------------------- /lib/puppet/parser/functions/jenkins_port.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Puppet::Parser::Functions 4 | newfunction(:jenkins_port, type: :rvalue, doc: <<-ENDHEREDOC) do |_args| 5 | Return the configurad Jenkins port value 6 | (corresponds to /etc/defaults/jenkins -> JENKINS_PORT 7 | 8 | Example: 9 | 10 | $port = jenkins_port() 11 | ENDHEREDOC 12 | 13 | config_hash = lookupvar('jenkins::config_hash') 14 | config_hash&.dig('JENKINS_PORT', 'value') || 8080 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/puppet/parser/functions/jenkins_prefix.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Puppet::Parser::Functions 4 | newfunction(:jenkins_prefix, type: :rvalue, doc: <<-ENDHEREDOC) do |_args| 5 | Return the configured Jenkins prefix value 6 | (corresponds to /etc/defaults/jenkins -> PREFIX) 7 | 8 | Example: 9 | 10 | $prefix = jenkins_prefix() 11 | ENDHEREDOC 12 | 13 | config_hash = lookupvar('jenkins::config_hash') 14 | config_hash&.dig('PREFIX', 'value') || '' 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /lib/puppet/provider/jenkins_authorization_strategy/cli.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require File.join(File.dirname(__FILE__), '../../..', 'puppet/x/jenkins/util') 4 | require File.join(File.dirname(__FILE__), '../../..', 'puppet/x/jenkins/provider/cli') 5 | 6 | require 'json' 7 | 8 | Puppet::Type.type(:jenkins_authorization_strategy).provide(:cli, parent: Puppet::X::Jenkins::Provider::Cli) do 9 | mk_resource_methods 10 | 11 | def self.instances(catalog = nil) 12 | all = get_authorization_strategy(catalog) 13 | 14 | # we are assuming there is only one key hash 15 | Puppet.debug("#{sname} instances: #{all.keys}") 16 | 17 | [from_hash(all)] 18 | end 19 | 20 | def flush 21 | @property_hash = resource.to_hash unless resource.nil? 22 | 23 | case self.ensure 24 | when :present 25 | set_jenkins_instance 26 | when :absent 27 | set_strategy_unsecured 28 | else 29 | raise Puppet::Error, "invalid :ensure value: #{self.ensure}" 30 | end 31 | end 32 | 33 | private 34 | 35 | def self.from_hash(info) 36 | method_name = 'setAuthorizationStrategy' 37 | class_name = info[method_name].keys.first 38 | ctor_args = info[method_name][class_name] 39 | 40 | args = { 41 | name: class_name, 42 | ensure: :present, 43 | arguments: ctor_args 44 | } 45 | 46 | # map nil -> :undef 47 | args = Puppet::X::Jenkins::Util.undefize(args) 48 | new(args) 49 | end 50 | private_class_method :from_hash 51 | 52 | def to_hash 53 | ctor = {} 54 | 55 | ctor[name] = if arguments == :absent 56 | [] 57 | else 58 | arguments 59 | end 60 | Puppet.debug("to_hash arguments #{arguments}") 61 | 62 | info = { 'setAuthorizationStrategy' => ctor } 63 | # map :undef -> nil 64 | Puppet::X::Jenkins::Util.unundef(info) 65 | end 66 | 67 | # jenkins only supports a single configured security realm at a time 68 | def self.get_authorization_strategy(catalog = nil) 69 | raw = clihelper(['get_authorization_strategy'], catalog: catalog) 70 | 71 | begin 72 | JSON.parse(raw) 73 | rescue JSON::ParserError 74 | raise Puppet::Error, "unable to parse as JSON: #{raw}" 75 | end 76 | end 77 | private_class_method :get_authorization_strategy 78 | 79 | def set_jenkins_instance(input = nil) 80 | input ||= to_hash 81 | 82 | clihelper(['set_jenkins_instance'], stdinjson: input) 83 | end 84 | 85 | def set_strategy_unsecured 86 | input = { 87 | 'setAuthorizationStrategy' => { 88 | 'hudson.security.AuthorizationStrategy$Unsecured' => [] 89 | } 90 | } 91 | set_jenkins_instance(input) 92 | end 93 | end 94 | -------------------------------------------------------------------------------- /lib/puppet/provider/jenkins_credentials/cli.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'puppet/util/warnings' 4 | 5 | require 'json' 6 | 7 | require_relative '../../../puppet/x/jenkins/util' 8 | require File.join(File.dirname(__FILE__), '../../..', 'puppet/x/jenkins/provider/cli') 9 | 10 | Puppet::Type.type(:jenkins_credentials).provide(:cli, parent: Puppet::X::Jenkins::Provider::Cli) do 11 | mk_resource_methods 12 | 13 | def self.instances(catalog = nil) 14 | all = credentials_list_json(catalog) 15 | 16 | Puppet.debug("#{sname} instances: #{all.map { |i| i['id'] }}") 17 | 18 | all.map { |info| from_hash(info) } 19 | end 20 | 21 | def flush 22 | @property_hash = resource.to_hash unless resource.nil? 23 | 24 | case self.ensure 25 | when :present 26 | credentials_update_json 27 | when :absent 28 | credentials_delete_id 29 | else 30 | raise Puppet::Error, "invalid :ensure value: #{self.ensure}" 31 | end 32 | end 33 | 34 | private 35 | 36 | def self.copy_key(dst, src, key) 37 | dst[key.to_sym] = src[key.to_s] 38 | end 39 | private_class_method :copy_key 40 | 41 | def self.from_hash(info) 42 | # map nil -> :undef 43 | info = Puppet::X::Jenkins::Util.undefize(info) 44 | 45 | params = { 46 | name: info['id'], 47 | ensure: :present 48 | } 49 | 50 | %i[impl domain scope].each { |k| copy_key(params, info, k) } 51 | 52 | case info['impl'] 53 | when 'UsernamePasswordCredentialsImpl' 54 | %i[description username password].each { |k| copy_key(params, info, k) } 55 | when 'BasicSSHUserPrivateKey' 56 | %i[description username private_key passphrase].each { |k| copy_key(params, info, k) } 57 | when 'StringCredentialsImpl' 58 | %i[description secret].each { |k| copy_key(params, info, k) } 59 | when 'FileCredentialsImpl' 60 | %i[description file_name content].each { |k| copy_key(params, info, k) } 61 | when 'CertificateCredentialsImpl' 62 | %i[description password key_store_implementation].each { |k| copy_key(params, info, k) } 63 | when 'AWSCredentialsImpl' 64 | %i[description secret_key access_key].each { |k| copy_key(params, info, k) } 65 | when 'BrowserStackCredentials' 66 | %i[description username access_key].each { |k| copy_key(params, info, k) } 67 | when 'GitLabApiTokenImpl' 68 | %i[description api_token].each { |k| copy_key(params, info, k) } 69 | when 'ConduitCredentialsImpl' 70 | %i[description token url].each { |k| copy_key(params, info, k) } 71 | 72 | ksi = info['key_store_impl'] 73 | params['key_store_impl'] = ksi 74 | 75 | case ksi 76 | when 'UploadedKeyStoreSource' 77 | params[:content] = info['content'] 78 | when 'FileOnMasterKeyStoreSource' 79 | params[:source] = info['source'] 80 | else 81 | Puppet::Util::Warnings.debug_once "#{sname}: unsupported key_store_implementation class #{ksi}" 82 | end 83 | else 84 | Puppet::Util::Warnings.debug_once "#{sname}: unsupported implementation class #{info['impl']}" 85 | end 86 | 87 | new(params) 88 | end 89 | private_class_method :from_hash 90 | 91 | def to_hash 92 | info = { 'id' => name } 93 | 94 | properties = self.class.resource_type.validproperties 95 | properties.reject! { |x| x == :ensure } 96 | 97 | properties.each do |prop| 98 | value = @property_hash[prop] 99 | info[prop.to_s] = value unless value.nil? 100 | end 101 | 102 | # map :undef -> nil 103 | Puppet::X::Jenkins::Util.unundef(info) 104 | end 105 | 106 | # array of hashes for multiple "credentials" entries 107 | def self.credentials_list_json(catalog = nil) 108 | raw = clihelper(['credentials_list_json'], catalog: catalog) 109 | 110 | begin 111 | JSON.parse(raw) 112 | rescue JSON::ParserError 113 | raise Puppet::Error, 'Unable to parse Jenkins credentials list as JSON' 114 | end 115 | end 116 | private_class_method :credentials_list_json 117 | 118 | def credentials_update_json 119 | clihelper(['credentials_update_json'], stdinjson: to_hash) 120 | end 121 | 122 | def credentials_delete_id 123 | # name == "id" 124 | clihelper(['credentials_delete_id', name]) 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /lib/puppet/provider/jenkins_job/cli.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'puppet/util/warnings' 4 | 5 | require 'json' 6 | 7 | require File.join(File.dirname(__FILE__), '../../..', 'puppet/x/jenkins/util') 8 | require File.join(File.dirname(__FILE__), '../../..', 'puppet/x/jenkins/provider/cli') 9 | 10 | Puppet::Type.type(:jenkins_job).provide(:cli, parent: Puppet::X::Jenkins::Provider::Cli) do 11 | mk_resource_methods 12 | 13 | def self.instances(catalog = nil) 14 | jobs = job_list_json(catalog) 15 | 16 | Puppet.debug("#{sname} instances: #{jobs.map { |i| i['name'] }}") 17 | 18 | jobs.map do |job| 19 | new( 20 | name: job['name'], 21 | ensure: :present, 22 | config: job['config'], 23 | enable: job['enabled'] 24 | ) 25 | end 26 | end 27 | 28 | # ignore #create so we can differentiate in #flush between an update to an 29 | # existing job and creating a new one 30 | def create; end 31 | 32 | def flush 33 | update = false 34 | update = true if exists? 35 | 36 | @property_hash = resource.to_hash unless resource.nil? 37 | 38 | # XXX the enable property is being ignored on flush because this modifies 39 | # the configuration string and breaks idempotent. Should the property be 40 | # removed? 41 | case self.ensure 42 | when :present 43 | if update 44 | update_job if replace 45 | else 46 | create_job 47 | end 48 | when :absent 49 | delete_job 50 | else 51 | raise Puppet::Error, "invalid :ensure value: #{self.ensure}" 52 | end 53 | end 54 | 55 | private 56 | 57 | # currently unused 58 | def self.list_jobs(catalog = nil) 59 | cli(['list-jobs'], catalog: catalog).split 60 | end 61 | private_class_method :list_jobs 62 | 63 | def self.job_list_json(catalog = nil) 64 | raw = clihelper(['job_list_json'], catalog: catalog) 65 | 66 | begin 67 | JSON.parse(raw) 68 | rescue JSON::ParserError 69 | raise Puppet::Error, "unable to parse as JSON: #{raw}" 70 | end 71 | end 72 | private_class_method :job_list_json 73 | 74 | # currently unused 75 | def self.get_job(job, catalog = nil) 76 | cli(['get-job', job], catalog: catalog) 77 | end 78 | private_class_method :get_job 79 | 80 | # currently unused 81 | def self.job_enabled(job, catalog = nil) 82 | raw = clihelper(['job_enabled', job], catalog: catalog) 83 | raw =~ %r{true} ? true : false 84 | end 85 | private_class_method :job_enabled 86 | 87 | def create_job 88 | cli(['create-job', name], stdin: config) 89 | end 90 | 91 | def update_job 92 | cli(['update-job', name], stdin: config) 93 | end 94 | 95 | def delete_job 96 | cli(['delete-job', name]) 97 | end 98 | end 99 | -------------------------------------------------------------------------------- /lib/puppet/provider/jenkins_num_executors/cli.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require File.join(File.dirname(__FILE__), '../../..', 'puppet/x/jenkins/util') 4 | require File.join(File.dirname(__FILE__), '../../..', 'puppet/x/jenkins/provider/cli') 5 | 6 | Puppet::Type.type(:jenkins_num_executors).provide(:cli, parent: Puppet::X::Jenkins::Provider::Cli) do 7 | mk_resource_methods 8 | 9 | def self.instances(catalog = nil) 10 | n = get_num_executors(catalog) 11 | 12 | # there can be only one value 13 | Puppet.debug("#{sname} instances: #{n}") 14 | 15 | [new(name: n, ensure: :prsent)] 16 | end 17 | 18 | def flush 19 | case self.ensure 20 | when :present 21 | set_num_executors 22 | else 23 | raise Puppet::Error, "invalid :ensure value: #{self.ensure}" 24 | end 25 | end 26 | 27 | private 28 | 29 | def self.get_num_executors(catalog = nil) 30 | clihelper(['get_num_executors'], catalog: catalog).to_i 31 | end 32 | private_class_method :get_num_executors 33 | 34 | def set_num_executors 35 | clihelper(['set_num_executors', name]) 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/puppet/provider/jenkins_security_realm/cli.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require File.join(File.dirname(__FILE__), '../../..', 'puppet/x/jenkins/util') 4 | require File.join(File.dirname(__FILE__), '../../..', 'puppet/x/jenkins/provider/cli') 5 | 6 | require 'json' 7 | 8 | Puppet::Type.type(:jenkins_security_realm).provide(:cli, parent: Puppet::X::Jenkins::Provider::Cli) do 9 | mk_resource_methods 10 | 11 | def self.instances(catalog = nil) 12 | all = get_security_realm(catalog) 13 | 14 | # we are assuming there is only one key hash 15 | Puppet.debug("#{sname} instances: #{all.keys}") 16 | 17 | [from_hash(all)] 18 | end 19 | 20 | def flush 21 | @property_hash = resource.to_hash unless resource.nil? 22 | 23 | case self.ensure 24 | when :present 25 | set_jenkins_instance 26 | when :absent 27 | set_security_none 28 | else 29 | raise Puppet::Error, "invalid :ensure value: #{self.ensure}" 30 | end 31 | end 32 | 33 | private 34 | 35 | def self.from_hash(info) 36 | method_name = 'setSecurityRealm' 37 | class_name = info[method_name].keys.first 38 | ctor_args = info[method_name][class_name] 39 | 40 | args = { 41 | name: class_name, 42 | ensure: :present, 43 | arguments: ctor_args 44 | } 45 | 46 | # map nil -> :undef 47 | args = Puppet::X::Jenkins::Util.undefize(args) 48 | new(args) 49 | end 50 | private_class_method :from_hash 51 | 52 | def to_hash 53 | ctor = {} 54 | 55 | ctor[name] = if arguments == :absent 56 | [] 57 | else 58 | arguments 59 | end 60 | 61 | Puppet.debug("to_hash arguments #{arguments}") 62 | 63 | info = { 'setSecurityRealm' => ctor } 64 | # map :undef -> nil 65 | Puppet::X::Jenkins::Util.unundef(info) 66 | end 67 | 68 | # jenkins only supports a single configured security realm at a time 69 | def self.get_security_realm(catalog = nil) 70 | raw = clihelper(['get_security_realm'], catalog: catalog) 71 | 72 | begin 73 | JSON.parse(raw) 74 | rescue JSON::ParserError 75 | raise Puppet::Error, "unable to parse as JSON: #{raw}" 76 | end 77 | end 78 | private_class_method :get_security_realm 79 | 80 | def set_jenkins_instance(input = nil) 81 | input ||= to_hash 82 | 83 | clihelper(['set_jenkins_instance'], stdinjson: input) 84 | end 85 | 86 | def set_security_none 87 | input = { 88 | 'setSecurityRealm' => { 89 | 'hudson.security.SecurityRealm$None' => [] 90 | } 91 | } 92 | set_jenkins_instance(input) 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /lib/puppet/provider/jenkins_slaveagent_port/cli.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require File.join(File.dirname(__FILE__), '../../..', 'puppet/x/jenkins/util') 4 | require File.join(File.dirname(__FILE__), '../../..', 'puppet/x/jenkins/provider/cli') 5 | 6 | Puppet::Type.type(:jenkins_slaveagent_port).provide(:cli, parent: Puppet::X::Jenkins::Provider::Cli) do 7 | mk_resource_methods 8 | 9 | def self.instances(catalog = nil) 10 | n = get_slaveagent_port(catalog) 11 | 12 | # there can be only one value 13 | Puppet.debug("#{sname} instances: #{n}") 14 | 15 | [new(name: n, ensure: :present)] 16 | end 17 | 18 | def flush 19 | case self.ensure 20 | when :present 21 | set_slaveagent_port 22 | else 23 | raise Puppet::Error, "invalid :ensure value: #{self.ensure}" 24 | end 25 | end 26 | 27 | private 28 | 29 | def self.get_slaveagent_port(catalog = nil) 30 | clihelper(['get_slaveagent_port'], catalog: catalog).to_i 31 | end 32 | private_class_method :get_slaveagent_port 33 | 34 | def set_slaveagent_port 35 | clihelper(['set_slaveagent_port', name]) 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /lib/puppet/provider/jenkins_user/cli.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require File.join(File.dirname(__FILE__), '../../..', 'puppet/x/jenkins/util') 4 | require File.join(File.dirname(__FILE__), '../../..', 'puppet/x/jenkins/provider/cli') 5 | 6 | require 'json' 7 | 8 | Puppet::Type.type(:jenkins_user).provide(:cli, parent: Puppet::X::Jenkins::Provider::Cli) do 9 | mk_resource_methods 10 | 11 | def self.instances(catalog = nil) 12 | all = user_info_all(catalog) 13 | 14 | Puppet.debug("#{sname} instances: #{all.map { |i| i['id'] }}") 15 | 16 | all.map { |info| from_hash(info) } 17 | end 18 | 19 | def api_token_public=(_value) 20 | raise Puppet::Error, 'api_token_public is read-only' 21 | end 22 | 23 | def flush 24 | @property_hash = resource.to_hash unless resource.nil? 25 | 26 | case self.ensure 27 | when :present 28 | user_update 29 | when :absent 30 | delete_user 31 | else 32 | raise Puppet::Error, "invalid :ensure value: #{self.ensure}" 33 | end 34 | end 35 | 36 | private 37 | 38 | def self.from_hash(info) 39 | # map nil -> :undef 40 | info = Puppet::X::Jenkins::Util.undefize(info) 41 | 42 | new(name: info['id'], 43 | ensure: :present, 44 | full_name: info['full_name'], 45 | email_address: info['email_address'], 46 | api_token_plain: info['api_token_plain'], 47 | api_token_public: info['api_token_public'], 48 | public_keys: info['public_keys'], 49 | password: info['password']) 50 | end 51 | private_class_method :from_hash 52 | 53 | def to_hash 54 | info = { 'id' => name } 55 | 56 | properties = self.class.resource_type.validproperties 57 | properties.reject! { |x| x == :ensure } 58 | properties.reject! { |x| x == :api_token_public } 59 | 60 | properties.each do |prop| 61 | value = @property_hash[prop] 62 | info[prop.to_s] = value unless value.nil? 63 | end 64 | 65 | # map :undef -> nil 66 | Puppet::X::Jenkins::Util.unundef(info) 67 | end 68 | 69 | # array of hashes for multiple users 70 | def self.user_info_all(catalog = nil) 71 | raw = if catalog.nil? 72 | clihelper(['user_info_all']) 73 | else 74 | clihelper(['user_info_all'], catalog: catalog) 75 | end 76 | 77 | begin 78 | JSON.parse(raw) 79 | rescue JSON::ParserError 80 | raise Puppet::Error, "unable to parse as JSON: #{raw}" 81 | end 82 | end 83 | private_class_method :user_info_all 84 | 85 | def user_update 86 | input ||= to_hash 87 | 88 | clihelper(['user_update'], stdinjson: input) 89 | end 90 | 91 | def delete_user 92 | clihelper(['delete_user', name]) 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /lib/puppet/type/jenkins_authorization_strategy.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../../puppet/x/jenkins/type/cli' 4 | 5 | Puppet::X::Jenkins::Type::Cli.newtype(:jenkins_authorization_strategy) do 6 | @doc = "Manage Jenkins' authorization strategy" 7 | 8 | ensurable 9 | 10 | newparam(:name) do 11 | desc 'Name of the security realm class' 12 | isnamevar 13 | end 14 | 15 | newproperty(:arguments, array_matching: :all) do 16 | desc 'List of arguments to security realm class constructor' 17 | end 18 | 19 | # require all instances of jenkins_user as the authorization strategy being 20 | # converged might require one of those accounts for administrative control 21 | autorequire(:jenkins_user) do 22 | catalog.resources.select do |r| 23 | r.is_a?(Puppet::Type.type(:jenkins_user)) 24 | end 25 | end 26 | 27 | # the authorization strategy can potentially lockout all access if it is 28 | # configured but the security realm is none 29 | autorequire(:jenkins_security_realm) do 30 | if self[:ensure] == :present 31 | catalog.resources.select do |r| 32 | r.is_a?(Puppet::Type.type(:jenkins_security_realm)) 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/puppet/type/jenkins_credentials.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../../puppet/x/jenkins/type/cli' 4 | 5 | Puppet::X::Jenkins::Type::Cli.newtype(:jenkins_credentials) do 6 | @doc = <<-EOS 7 | Manage Jenkins' credentials 8 | 9 | XXX The properties specified are not validated against the specified 10 | jenkins class (`impl`) 11 | EOS 12 | 13 | ensurable 14 | 15 | newparam(:name) do 16 | desc 'Id for credentials entry' 17 | isnamevar 18 | end 19 | 20 | newproperty(:domain) do 21 | desc 'credentials domain within jenkins - :undef indicates the "global" domain' 22 | defaultto :undef 23 | newvalues(:undef) 24 | end 25 | 26 | newproperty(:scope) do 27 | desc 'credentials scope within jenkins' 28 | defaultto :GLOBAL 29 | newvalues(:GLOBAL, :SYSTEM) 30 | end 31 | 32 | newproperty(:impl) do 33 | desc 'name of the java class implimenting the credential' 34 | defaultto :UsernamePasswordCredentialsImpl 35 | newvalues(:UsernamePasswordCredentialsImpl, 36 | :BasicSSHUserPrivateKey, 37 | :ConduitCredentialsImpl, 38 | :StringCredentialsImpl, 39 | :FileCredentialsImpl, 40 | :AWSCredentialsImpl, 41 | :GitLabApiTokenImpl, 42 | :BrowserStackCredentials) 43 | end 44 | 45 | newproperty(:description) do 46 | desc 'description of credentials' 47 | defaultto 'Managed by Puppet' 48 | end 49 | 50 | newproperty(:username) do 51 | desc 'username for credentials - UsernamePasswordCredentialsImpl, CertificateCredentialsImpl, BrowserStackCredentials' 52 | end 53 | 54 | newproperty(:password) do 55 | desc 'password - UsernamePasswordCredentialsImpl, CertificateCredentialsImpl' 56 | end 57 | 58 | newproperty(:private_key) do 59 | desc 'ssh private key string - BasicSSHUserPrivateKey' 60 | end 61 | 62 | newproperty(:access_key) do 63 | desc 'AWS access key - AWSCredentialsImpl, BrowserStackCredentials' 64 | end 65 | 66 | newproperty(:secret_key) do 67 | desc 'AWS secret key - AWSCredentialsImpl' 68 | end 69 | 70 | newproperty(:passphrase) do 71 | desc 'passphrase to unlock ssh private key - BasicSSHUserPrivateKey' 72 | end 73 | 74 | newproperty(:secret) do 75 | desc 'secret string - StringCredentialsImpl' 76 | end 77 | 78 | newproperty(:file_name) do 79 | desc 'name of file - FileCredentialsImpl' 80 | end 81 | 82 | newproperty(:content) do 83 | desc 'content of file - FileCredentialsImpl, CertificateCredentialsImpl' 84 | end 85 | 86 | newproperty(:source) do 87 | desc 'content of file - CertificateCredentialsImpl' 88 | end 89 | 90 | newproperty(:key_store_impl) do 91 | desc 'name of the java class implimenting the key store - CertificateCredentialsImpl' 92 | end 93 | 94 | newproperty(:token) do 95 | desc 'conduit token - ConduitCredentialsImpl' 96 | end 97 | 98 | newproperty(:api_token) do 99 | desc 'API token - GitLabApiTokenImpl' 100 | end 101 | 102 | newproperty(:url) do 103 | desc 'URL of phabriactor installation - ConduitCredentialsImpl' 104 | end 105 | 106 | # require all authentication & authorization related types 107 | %i[ 108 | jenkins_user 109 | jenkins_security_realm 110 | jenkins_authorization_strategy 111 | ].each do |type| 112 | autorequire(type) do 113 | catalog.resources.select do |r| 114 | r.is_a?(Puppet::Type.type(type)) 115 | end 116 | end 117 | end 118 | end 119 | -------------------------------------------------------------------------------- /lib/puppet/type/jenkins_job.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'puppet/property/boolean' 4 | require 'puppet/parameter/boolean' 5 | require 'puppet/util/diff' 6 | require 'puppet/util/checksums' 7 | require 'pathname' 8 | require 'tempfile' 9 | 10 | require_relative '../../puppet/x/jenkins/type/cli' 11 | 12 | Puppet::X::Jenkins::Type::Cli.newtype(:jenkins_job) do 13 | @doc = "Manage Jenkins' jobs" 14 | 15 | ensurable 16 | 17 | newparam(:name) do 18 | desc 'job name' 19 | isnamevar 20 | end 21 | 22 | newproperty(:config) do 23 | include Puppet::Util::Diff 24 | include Puppet::Util::Checksums 25 | 26 | desc 'XML job configuration string' 27 | 28 | def change_to_s(currentvalue, newvalue) 29 | if currentvalue == :absent 30 | 'created' 31 | elsif newvalue == :absent 32 | 'removed' 33 | else 34 | return 'left unchanged' if @resource[:replace] == false 35 | 36 | if Puppet[:show_diff] && resource[:show_diff] 37 | # XXX this really should be turned into a helper method and submitted 38 | # to # core puppet 39 | Tempfile.open('puppet-file') do |d1| 40 | d1.write(currentvalue) 41 | d1.flush 42 | Tempfile.open('puppet-file') do |d2| 43 | d2.write(newvalue) 44 | d2.flush 45 | 46 | send @resource[:loglevel], "\n#{diff(d1.path, d2.path)}" 47 | 48 | d2.close 49 | d2.unlink 50 | end 51 | d1.close 52 | d1.unlink 53 | end 54 | 55 | end 56 | "content changed '{md5}#{md5(currentvalue)}' to '{md5}#{md5(newvalue)}'" 57 | end 58 | end 59 | end 60 | 61 | newparam(:show_diff, boolean: true, parent: Puppet::Parameter::Boolean) do 62 | desc 'enable/disable displaying configuration diff' 63 | defaultto true 64 | end 65 | 66 | newproperty(:enable, boolean: true, parent: Puppet::Property::Boolean) do 67 | desc 'enable/disable job' 68 | defaultto true 69 | end 70 | 71 | newparam(:replace, boolean: true, parent: Puppet::Parameter::Boolean) do 72 | desc 'replace existing job' 73 | defaultto true 74 | end 75 | 76 | # require all authentication & authorization related types 77 | %i[ 78 | jenkins_user 79 | jenkins_security_realm 80 | jenkins_authorization_strategy 81 | ].each do |type| 82 | autorequire(type) do 83 | catalog.resources.select do |r| 84 | r.is_a?(Puppet::Type.type(type)) 85 | end 86 | end 87 | end 88 | 89 | # if the job is contained in a `cloudbees-folder`, autorequire any parent 90 | # folder jobs 91 | # XXX we can't inspect @resource[:name] or self[:name] here outside of teh 92 | # autorequire block because of meta-programming funkiness 93 | autorequire(:jenkins_job) do 94 | if self[:ensure] == :present 95 | folders = [] 96 | Pathname(self[:name]).dirname.descend { |d| folders << d.to_path } 97 | folders 98 | end 99 | end 100 | 101 | # XXX completely punting on handling delete ordering for puppet < 4 102 | unless Puppet.version.to_f < 4.0 103 | autobefore(:jenkins_job) do 104 | if self[:ensure] == :absent 105 | folders = [] 106 | Pathname(self[:name]).dirname.descend { |d| folders << d.to_path } 107 | folders 108 | end 109 | end 110 | end 111 | end 112 | -------------------------------------------------------------------------------- /lib/puppet/type/jenkins_num_executors.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../../puppet/x/jenkins/type/cli' 4 | 5 | Puppet::X::Jenkins::Type::Cli.newtype(:jenkins_num_executors) do 6 | @doc = "Manage Jenkins' number of executor slots" 7 | 8 | # the cli jar does not have an interface for plugin removal so the only 9 | # allowed ensure value is :present 10 | ensurable do 11 | newvalue(:present) { provider.create } 12 | end 13 | 14 | newparam(:name) do 15 | desc 'Number of executors' 16 | isnamevar 17 | 18 | munge do |value| 19 | if value.is_a?(String) && value =~ %r{^[0-9]+$} 20 | Integer(value) 21 | else 22 | value 23 | end 24 | end 25 | end 26 | 27 | # require all authentication & authorization related types 28 | %i[ 29 | jenkins_user 30 | jenkins_security_realm 31 | jenkins_authorization_strategy 32 | ].each do |type| 33 | autorequire(type) do 34 | catalog.resources.select do |r| 35 | r.is_a?(Puppet::Type.type(type)) 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/puppet/type/jenkins_security_realm.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../../puppet/x/jenkins/type/cli' 4 | 5 | Puppet::X::Jenkins::Type::Cli.newtype(:jenkins_security_realm) do 6 | @doc = "Manage Jenkins' security realm" 7 | 8 | ensurable 9 | 10 | newparam(:name) do 11 | desc 'Name of the security realm class' 12 | isnamevar 13 | end 14 | 15 | newproperty(:arguments, array_matching: :all) do 16 | desc 'List of arguments to security realm class constructor' 17 | end 18 | 19 | # require all instances of jenkins_user, as does 20 | # jenkins_authorization_strategy, to ensure that the state of jenkins_user 21 | # resources is not attempted to be modify between jenkins_security_realm and 22 | # jenkins_authorization_strategy state changes. 23 | autorequire(:jenkins_user) do 24 | catalog.resources.select do |r| 25 | r.is_a?(Puppet::Type.type(:jenkins_user)) 26 | end 27 | end 28 | 29 | autorequire(:jenkins_authorization_strategy) do 30 | if self[:ensure] == :absent 31 | catalog.resources.select do |r| 32 | r.is_a?(Puppet::Type.type(:jenkins_authorization_strategy)) 33 | end 34 | end 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /lib/puppet/type/jenkins_slaveagent_port.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../../puppet/x/jenkins/type/cli' 4 | 5 | Puppet::X::Jenkins::Type::Cli.newtype(:jenkins_slaveagent_port) do 6 | @doc = "Manage Jenkins' slave agent listening port" 7 | 8 | # the cli jar does not have an interface for plugin removal so the only 9 | # allowed ensure value is :present 10 | ensurable do 11 | newvalue(:present) { provider.create } 12 | end 13 | 14 | newparam(:name) do 15 | desc 'port number' 16 | isnamevar 17 | 18 | munge do |value| 19 | if value.is_a?(String) && value =~ %r{^[0-9]+$} 20 | Integer(value) 21 | else 22 | value 23 | end 24 | end 25 | end 26 | 27 | # require all authentication & authorization related types 28 | %i[ 29 | jenkins_user 30 | jenkins_security_realm 31 | jenkins_authorization_strategy 32 | ].each do |type| 33 | autorequire(type) do 34 | catalog.resources.select do |r| 35 | r.is_a?(Puppet::Type.type(type)) 36 | end 37 | end 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /lib/puppet/type/jenkins_user.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../../puppet/x/jenkins/type/cli' 4 | 5 | Puppet::X::Jenkins::Type::Cli.newtype(:jenkins_user) do 6 | @doc = "Manage Jenkins' user account information" 7 | 8 | ensurable 9 | 10 | newparam(:name) do 11 | desc "Account login name. Jenkins calls this 'id'" 12 | isnamevar 13 | end 14 | 15 | newproperty(:full_name) do 16 | desc 'Optional longer account name.' 17 | end 18 | 19 | newproperty(:email_address) do 20 | desc 'email address' 21 | end 22 | 23 | newproperty(:api_token_plain) do 24 | desc "Unhashed or 'plain_text' API token that is digested to produce the public API token" 25 | validate do |value| 26 | # 32 char hex string 27 | unless value =~ %r{^\h{32}$} 28 | raise ArgumentError, "#{value} is not a 32char hex string" 29 | end 30 | end 31 | end 32 | 33 | newproperty(:api_token_public) do 34 | # XXX validate 35 | desc 'Literal public API token. read-only property.' 36 | end 37 | 38 | newproperty(:public_keys, array_matching: :all) do 39 | desc 'Array of ssh public key strings' 40 | end 41 | 42 | newproperty(:password) do 43 | desc 'Password for HudsonPrivateSecurityRealm' 44 | end 45 | end 46 | -------------------------------------------------------------------------------- /lib/puppet/x/jenkins.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | module Puppet::X; end 4 | 5 | module Puppet::X::Jenkins; end 6 | -------------------------------------------------------------------------------- /lib/puppet/x/jenkins/config.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'facter' 4 | 5 | require_relative '../jenkins' 6 | 7 | require 'puppet/util/warnings' 8 | 9 | # This class is used to lookup common configuration values by first looking for 10 | # the desired key as parameter to the config class in the catalog, then 11 | # checking for a prefixed fact, and falling back to hard coded defaults. 12 | class Puppet::X::Jenkins::Config 13 | class UnknownConfig < ArgumentError; end 14 | 15 | DEFAULTS = { 16 | cli_jar: '/usr/share/java/jenkins-cli.jar', 17 | url: 'http://localhost:8080', 18 | ssh_private_key: nil, 19 | puppet_helper: '/usr/share/java/puppet_helper.groovy', 20 | cli_tries: 30, 21 | cli_username: nil, 22 | cli_password: nil, 23 | cli_password_file: '/tmp/jenkins_credentials_for_puppet', 24 | cli_password_file_exists: false 25 | }.freeze 26 | CONFIG_CLASS = 'jenkins::cli::config' 27 | FACT_PREFIX = 'jenkins_' 28 | 29 | def initialize(catalog = nil) 30 | @catalog = catalog 31 | end 32 | 33 | def [](key) 34 | key = key.to_sym 35 | raise UnknownConfig unless DEFAULTS.key?(key) 36 | 37 | value = catalog_lookup(key) || fact_lookup(key) || default_lookup(key) 38 | return if value.nil? 39 | 40 | Puppet::Util::Warnings.debug_once "config: #{key} = #{value}" 41 | 42 | # handle puppet 3.x passing in all values as strings and convert back to 43 | # Integer/Fixnum 44 | if Puppet.version =~ %r{^3} 45 | default_type_integer?(key) ? value.to_i : value 46 | else 47 | value 48 | end 49 | end 50 | 51 | def catalog_lookup(key) 52 | return nil if @catalog.nil? 53 | 54 | config = @catalog.resource(:class, CONFIG_CLASS) 55 | return nil if config.nil? 56 | 57 | config[key.to_sym] 58 | end 59 | 60 | def fact_lookup(key) 61 | fact = FACT_PREFIX + key.to_s 62 | Facter.value(fact.to_sym) 63 | end 64 | 65 | def default_lookup(key) 66 | DEFAULTS[key] 67 | end 68 | 69 | def default_type_integer?(key) 70 | DEFAULTS[key].is_a?(Integer) 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /lib/puppet/x/jenkins/provider.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../jenkins' 4 | 5 | module Puppet::X::Jenkins::Provider; end 6 | -------------------------------------------------------------------------------- /lib/puppet/x/jenkins/type.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../jenkins' 4 | 5 | module Puppet::X::Jenkins::Type; end 6 | -------------------------------------------------------------------------------- /lib/puppet/x/jenkins/type/cli.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../../jenkins/type' 4 | require_relative '../../jenkins/config' 5 | 6 | module Puppet::X::Jenkins::Type::Cli 7 | def self.newtype(*args, &block) 8 | type = Puppet::Type.newtype(*args, &block) 9 | 10 | # The jenkins master needs to be avaiable in order to interact with it via 11 | # the cli jar. 12 | type.autorequire(:service) do 13 | ['jenkins'] 14 | end 15 | 16 | # If a file resource is declared for file path params, make sure that it's 17 | # converged so we can read it off disk. 18 | type.autorequire(:file) do 19 | config = Puppet::X::Jenkins::Config.new(catalog) 20 | 21 | autos = [] 22 | %w[ssh_private_key puppet_helper cli_jar].each do |param| 23 | value = config[param.to_sym] 24 | autos << value unless value.nil? 25 | end 26 | 27 | autos 28 | end 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /lib/puppet/x/jenkins/util.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require_relative '../jenkins' 4 | 5 | module Puppet::X::Jenkins::Util 6 | def unundef(data) 7 | iterate(data) { |x| x == :undef ? nil : x } 8 | end 9 | module_function :unundef 10 | 11 | def undefize(data) 12 | iterate(data) { |x| x.nil? ? :undef : x } 13 | end 14 | module_function :undefize 15 | 16 | # loosely based on 17 | # https://stackoverflow.com/questions/16412013/iterate-nested-hash-that-contains-hash-and-or-array 18 | def iterate(data, &block) 19 | return data unless block_given? 20 | 21 | case data 22 | when Hash 23 | data.transform_values do |v| 24 | iterate(v, &block) 25 | end 26 | when Array 27 | data.map { |v| iterate(v, &block) } 28 | else 29 | yield data 30 | end 31 | end 32 | module_function :iterate 33 | end 34 | -------------------------------------------------------------------------------- /manifests/augeas.pp: -------------------------------------------------------------------------------- 1 | # @summary Change config files using augeas 2 | # 3 | # @param config_filename 4 | # Filename of the configuration file to work on relative to the jenkins 5 | # localstatedir. 6 | # 7 | # @param changes 8 | # String or array with augeas changes to perform. 9 | # 10 | # @param onlyif 11 | # Optional augeas command and comparisons to control the execution of this type. 12 | # 13 | # @param plugin 14 | # Optionally jenkins::augeas can also install the plugin. If this is set to 15 | # true, we use the name of the resource as plugin name. If it's a string, 16 | # that is used as plugin name. 17 | # 18 | # @param plugin_version 19 | # Optional plugin version to pass through to jenkins::plugin 20 | # 21 | # @param context 22 | # Optional context to ease your change rules. 23 | # 24 | # @param restart 25 | # If set to true, will trigger a jenkins (safe-)restart in stead of reloading 26 | # the configuration. 27 | # 28 | # @param show_diff 29 | # Whether to display differences when the file changes, defaulting to true. 30 | # 31 | # @example Configure the git plugin 32 | # jenkins::augeas { 'git': 33 | # plugin => true, 34 | # config_filename => 'hudson.plugins.git.GitSCM.xml', 35 | # context => '/hudson.plugins.git.GitSCM_-DescriptorImpl', 36 | # changes => [ 37 | # 'set globalConfigName/#text "Bob the Builder"', 38 | # 'set globalConfigEmail/#text "bob@example.com", 39 | # ], 40 | # } 41 | define jenkins::augeas ( 42 | String $config_filename, 43 | Variant[Array[String], String] $changes, 44 | Optional[Variant[Array[String], String]] $onlyif = undef, 45 | Optional[String] $plugin_version = undef, 46 | String $context = '/', 47 | Variant[Boolean,String] $plugin = false, 48 | Boolean $restart = false, 49 | Boolean $show_diff = true, 50 | ) { 51 | include jenkins 52 | include jenkins::cli 53 | 54 | case $plugin { 55 | true: { 56 | jenkins::plugin { $name: 57 | version => $plugin_version, 58 | before => Augeas["jenkins::augeas: ${name}"], 59 | } 60 | } 61 | false: { 62 | # do nothing 63 | } 64 | default: { 65 | jenkins::plugin { $plugin: 66 | version => $plugin_version, 67 | before => Augeas["jenkins::augeas: ${name}"], 68 | } 69 | } 70 | } 71 | 72 | if $restart { 73 | $notify_exec = 'safe-restart-jenkins' 74 | } else { 75 | $notify_exec = 'reload-jenkins' 76 | } 77 | 78 | augeas { "jenkins::augeas: ${name}": 79 | incl => "${jenkins::localstatedir}/${config_filename}", 80 | lens => 'Xml.lns', 81 | context => regsubst("/files${jenkins::localstatedir}/${config_filename}/${context}", '\/{2,}', '/', 'G'), 82 | notify => Exec[$notify_exec], 83 | onlyif => $onlyif, 84 | changes => $changes, 85 | show_diff => $show_diff, 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /manifests/cli.pp: -------------------------------------------------------------------------------- 1 | # @summary Allow Jenkins commands to be issued from the command line 2 | # @api private 3 | class jenkins::cli { 4 | assert_private() 5 | 6 | include jenkins 7 | 8 | # XXX Classes/defines which include the jenkins::cli class assume that they 9 | # can use the cli even if $jenkins::cli == false. This breaks the top 10 | # level anchor pattern. The cli param should either be deprecated and 11 | # essentially hardwired to true or attempting to use cli functionality 12 | # without this param set should fail; either option is a backwards 13 | # incompatible change. 14 | # 15 | # As an attempt to preserve backwards compatibility, there are includes and 16 | # resource relationships being scattered throughout this module. 17 | if $jenkins::manage_service { 18 | Class['jenkins::service'] 19 | -> Class['jenkins::cli'] 20 | -> Anchor['jenkins::end'] 21 | } 22 | 23 | $jar = "${jenkins::libdir}/jenkins-cli.jar" 24 | $extract_jar = "jar -xf ${jenkins::libdir}/jenkins.war WEB-INF/lib/" 25 | $move_jar = "mv WEB-INF/lib/cli-*.jar ${jar}" 26 | $remove_dir = 'rm -rf WEB-INF' 27 | $cli_tries = $jenkins::cli_tries 28 | $cli_try_sleep = $jenkins::cli_try_sleep 29 | 30 | # make sure we always call Exec[jenlins-cli] in case 31 | # the binary does not exist 32 | exec { 'check-jenkins-cli': 33 | command => '/bin/true', 34 | creates => $jar, 35 | } 36 | ~> exec { 'jenkins-cli' : 37 | command => "${extract_jar} && ${move_jar} && ${remove_dir}", 38 | path => ['/bin', '/usr/bin'], 39 | cwd => '/tmp', 40 | refreshonly => true, 41 | } 42 | # Extract latest CLI in case package is updated / downgraded 43 | Package[$jenkins::package_name] ~> Exec['jenkins-cli'] 44 | 45 | file { $jar: 46 | ensure => file, 47 | mode => '0644', 48 | require => Exec['jenkins-cli'], 49 | } 50 | 51 | $port = jenkins_port() 52 | $prefix = jenkins_prefix() 53 | 54 | # The jenkins cli command with required parameter(s) 55 | $cmd = join( 56 | delete_undef_values([ 57 | 'java', 58 | "-jar ${jar}", 59 | "-s http://localhost:${port}${prefix}", 60 | $jenkins::_cli_auth_arg, 61 | ]), 62 | ' ' 63 | ) 64 | 65 | # Do a safe restart of Jenkins (only when notified) 66 | exec { 'safe-restart-jenkins': 67 | command => "${cmd} safe-restart && /bin/sleep 10", 68 | path => ['/bin', '/usr/bin'], 69 | tries => $cli_tries, 70 | try_sleep => $cli_try_sleep, 71 | refreshonly => true, 72 | require => File[$jar], 73 | } 74 | 75 | # jenkins::cli::reload should be included only after $jenkins::cli::cmd is 76 | # defined 77 | include jenkins::cli::reload 78 | } 79 | -------------------------------------------------------------------------------- /manifests/cli/config.pp: -------------------------------------------------------------------------------- 1 | # This class provides configuration values to override defaults and fact data 2 | # for PuppetX::Jenkins::Provider::Clihelper based providers. 3 | # 4 | # Default and fact data is managed internal to the 5 | # PuppetX::Jenkins::Provider::Clihelper class for compatiblity with the puppet 6 | # resource face. No defaults should be set in this classes definition. 7 | class jenkins::cli::config ( 8 | Optional[Stdlib::Absolutepath] $cli_jar = undef, 9 | Optional[String] $url = undef, 10 | Optional[Stdlib::Absolutepath] $ssh_private_key = undef, 11 | Optional[Stdlib::Absolutepath] $puppet_helper = undef, 12 | Optional[Integer] $cli_tries = undef, 13 | Optional[Numeric] $cli_try_sleep = undef, 14 | Optional[String] $cli_username = undef, 15 | Optional[String] $cli_password = undef, 16 | String $cli_password_file = '/tmp/jenkins_credentials_for_puppet', 17 | Boolean $cli_password_file_exists = false, 18 | Optional[String] $ssh_private_key_content = undef, 19 | ) { 20 | # lint:ignore:legacy_facts 21 | $is_root = $facts['id'] == 'root' 22 | # lint:endignore 23 | 24 | if $ssh_private_key and $ssh_private_key_content { 25 | file { $ssh_private_key: 26 | ensure => 'file', 27 | mode => '0400', 28 | backup => false, 29 | content => $ssh_private_key_content, 30 | } 31 | 32 | # allow this class to be included when not running as root 33 | if $is_root { 34 | File[$ssh_private_key] { 35 | # the owner/group should probably be set externally and retrieved if 36 | # present in the manfiest. At present, there is no authoritative place 37 | # to retrive this information from. 38 | owner => 'jenkins', 39 | group => 'jenkins', 40 | } 41 | } 42 | } 43 | 44 | # We manage the password file, to avoid printing username/password in the 45 | # ps ax output. 46 | # If file exists, we assume the user manages permissions and content 47 | if $cli_username and $cli_password and !$cli_password_file_exists { 48 | file { $cli_password_file: 49 | ensure => 'file', 50 | mode => '0400', 51 | backup => false, 52 | content => "${cli_username}:${cli_password}", 53 | } 54 | 55 | # allow this class to be included when not running as root 56 | if $is_root { 57 | File[$cli_password_file] { 58 | # the owner/group should probably be set externally and retrieved if 59 | # present in the manfiest. At present, there is no authoritative place 60 | # to retrive this information from. 61 | owner => 'jenkins', 62 | group => 'jenkins', 63 | } 64 | } 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /manifests/cli/exec.pp: -------------------------------------------------------------------------------- 1 | # @summary Executing custom helper script commands via the Jenkins CLI. 2 | # 3 | # @param unless 4 | # The unless parameter passed to the exec resource 5 | # @param command 6 | # The command or commands to run 7 | define jenkins::cli::exec ( 8 | Optional[String] $unless = undef, 9 | Variant[String, Array] $command = $title, 10 | ) { 11 | include jenkins 12 | include jenkins::cli_helper 13 | include jenkins::cli::reload 14 | 15 | Class['jenkins::cli_helper'] 16 | -> Jenkins::Cli::Exec[$title] 17 | -> Anchor['jenkins::end'] 18 | 19 | # $command may be either a string or an array due to the use of flatten() 20 | $run = join( 21 | delete_undef_values( 22 | flatten([ 23 | $jenkins::cli_helper::helper_cmd, 24 | $command, 25 | ]) 26 | ), 27 | ' ' 28 | ) 29 | 30 | if $unless { 31 | $environment_run = ["HELPER_CMD=eval ${jenkins::cli_helper::helper_cmd}"] 32 | } else { 33 | $environment_run = undef 34 | } 35 | 36 | exec { $title: 37 | provider => 'shell', 38 | command => $run, 39 | environment => $environment_run, 40 | unless => $unless, 41 | tries => $jenkins::cli_tries, 42 | try_sleep => $jenkins::cli_try_sleep, 43 | notify => Class['jenkins::cli::reload'], 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /manifests/cli/reload.pp: -------------------------------------------------------------------------------- 1 | # @summary Command Jenkins to reload config.xml via the CLI. 2 | # @api private 3 | class jenkins::cli::reload { 4 | assert_private() 5 | 6 | $cli_tries = $jenkins::cli_tries 7 | $cli_try_sleep = $jenkins::cli_try_sleep 8 | $jar_file = $jenkins::cli::jar 9 | 10 | # Reload all Jenkins config from disk (only when notified) 11 | exec { 'reload-jenkins': 12 | command => "${jenkins::cli::cmd} reload-configuration", 13 | path => ['/bin', '/usr/bin'], 14 | tries => $cli_tries, 15 | try_sleep => $cli_try_sleep, 16 | refreshonly => true, 17 | require => File[$jar_file], 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /manifests/cli_helper.pp: -------------------------------------------------------------------------------- 1 | # @summary A helper script for creating resources via the Jenkins cli 2 | class jenkins::cli_helper { 3 | include jenkins 4 | include jenkins::cli 5 | 6 | Class['jenkins::cli'] 7 | -> Class['jenkins::cli_helper'] 8 | -> Anchor['jenkins::end'] 9 | 10 | $libdir = $jenkins::libdir 11 | $cli_jar = $jenkins::cli::jar 12 | $port = jenkins_port() 13 | $prefix = jenkins_prefix() 14 | $helper_groovy = "${libdir}/puppet_helper.groovy" 15 | 16 | file { $helper_groovy: 17 | source => 'puppet:///modules/jenkins/puppet_helper.groovy', 18 | owner => $jenkins::user, 19 | group => $jenkins::group, 20 | mode => '0444', 21 | require => Class['jenkins::cli'], 22 | } 23 | 24 | $helper_cmd = join( 25 | delete_undef_values([ 26 | '/bin/cat', 27 | $helper_groovy, 28 | '|', 29 | '/usr/bin/java', 30 | "-jar ${cli_jar}", 31 | "-s http://127.0.0.1:${port}${prefix}", 32 | $jenkins::_cli_auth_arg, 33 | 'groovy =', 34 | ]), 35 | ' ' 36 | ) 37 | } 38 | -------------------------------------------------------------------------------- /manifests/config.pp: -------------------------------------------------------------------------------- 1 | # @summary Wire up the configuration 2 | # @api private 3 | class jenkins::config { 4 | assert_private() 5 | 6 | ensure_resource('jenkins::plugin', $jenkins::default_plugins) 7 | } 8 | -------------------------------------------------------------------------------- /manifests/credentials.pp: -------------------------------------------------------------------------------- 1 | # Copyright 2014 RetailMeNot, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # @summary Jenkins credentials via the CloudBees Credentials plugin 16 | define jenkins::credentials ( 17 | String $password, 18 | String $description = 'Managed by Puppet', 19 | String $private_key_or_path = '', # lint:ignore:params_empty_string_assignment 20 | Enum['present', 'absent'] $ensure = 'present', 21 | String $uuid = '', # lint:ignore:params_empty_string_assignment 22 | ) { 23 | include jenkins 24 | include jenkins::cli_helper 25 | 26 | Class['jenkins::cli_helper'] 27 | -> Jenkins::Credentials[$title] 28 | -> Anchor['jenkins::end'] 29 | 30 | case $ensure { 31 | 'present': { 32 | jenkins::cli::exec { "create-jenkins-credentials-${title}": 33 | command => [ 34 | 'create_or_update_credentials', 35 | $title, 36 | "'${password}'", 37 | "'${uuid}'", 38 | "'${description}'", 39 | "'${private_key_or_path}'", 40 | ], 41 | unless => "for i in \$(seq 1 ${jenkins::cli_tries}); do \$HELPER_CMD credential_info ${title} && break || sleep ${jenkins::cli_try_sleep}; done | grep ${title}", # lint:ignore:140chars 42 | } 43 | } 44 | 'absent': { 45 | # XXX not idempotent 46 | jenkins::cli::exec { "delete-jenkins-credentials-${title}": 47 | command => [ 48 | 'delete_credentials', 49 | $title, 50 | ], 51 | } 52 | } 53 | default: { 54 | fail "ensure must be 'present' or 'absent' but '${ensure}' was given" 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /manifests/direct_download.pp: -------------------------------------------------------------------------------- 1 | # @summary Support for directly downloading a package file - for when no 2 | # repository is available 3 | # @api private 4 | class jenkins::direct_download { 5 | assert_private() 6 | 7 | include jenkins::proxy 8 | 9 | # directory for temp files 10 | file { $jenkins::package_cache_dir: 11 | ensure => directory, 12 | owner => 'root', 13 | group => 'root', 14 | mode => '0644', 15 | } 16 | 17 | # equivalent to basename() - get the filename 18 | $package_file = regsubst($jenkins::direct_download, '(.*?)([^/]+)$', '\2') 19 | $local_file = "${jenkins::package_cache_dir}/${package_file}" 20 | 21 | if $jenkins::version != 'absent' { 22 | # make download optional if we are removing... 23 | archive { $package_file: 24 | source => $jenkins::direct_download, 25 | path => $local_file, 26 | proxy_server => $jenkins::proxy::url, 27 | cleanup => false, 28 | extract => false, 29 | before => Package[$jenkins::package_name], 30 | } 31 | } 32 | 33 | package { $jenkins::package_name: 34 | ensure => $jenkins::version, 35 | provider => $jenkins::package_provider, 36 | source => $local_file, 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /manifests/firewall.pp: -------------------------------------------------------------------------------- 1 | # @summary Integrate with the puppetlabs-firewall module for opening the port 2 | # to Jenkins automatically 3 | # @api private 4 | class jenkins::firewall { 5 | assert_private() 6 | 7 | firewall { '500 allow Jenkins inbound traffic': 8 | action => 'accept', 9 | state => 'NEW', 10 | dport => [jenkins_port()], 11 | proto => 'tcp', 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /manifests/job.pp: -------------------------------------------------------------------------------- 1 | # @summary Manage Jenkins jobs given a name and config xml 2 | # 3 | # @param config The content of the jenkins job config file (required) 4 | # @param source Path to a puppet file() resource containing the Jenkins XML job description. 5 | # Will override 'config' if set 6 | # @param template Path to a puppet template() resource containing the Jenkins XML job description. 7 | # Will override 'config' if set 8 | # @param jobname the name of the jenkins job 9 | # @param ensure choose 'absent' to ensure the job is removed 10 | # @param difftool Provide a command to execute to compare Jenkins job files 11 | # @param replace 12 | # Whether or not to replace the job if it already exists. 13 | define jenkins::job ( 14 | String $config, 15 | Optional[String] $source = undef, 16 | Optional[String[1]] $template = undef, 17 | String $jobname = $title, 18 | Enum['present', 'absent'] $ensure = 'present', 19 | String $difftool = '/usr/bin/diff -b -q', 20 | Boolean $replace = true 21 | ) { 22 | include jenkins::cli 23 | 24 | Class['jenkins::cli'] 25 | -> Jenkins::Job[$title] 26 | -> Anchor['jenkins::end'] 27 | 28 | if ($ensure == 'absent') { 29 | jenkins::job::absent { $title: 30 | jobname => $jobname, 31 | } 32 | } else { 33 | if $source { 34 | $realconfig = file($source) 35 | } 36 | elsif $template { 37 | $realconfig = template($template) 38 | } 39 | else { 40 | $realconfig = $config 41 | } 42 | 43 | jenkins::job::present { $title: 44 | config => $realconfig, 45 | jobname => $jobname, 46 | difftool => $difftool, 47 | replace => $replace, 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /manifests/job/absent.pp: -------------------------------------------------------------------------------- 1 | # @summary Removes a jenkins build job 2 | # @api private 3 | # 4 | # @param jobname 5 | # the name of the jenkins job 6 | # 7 | define jenkins::job::absent ( 8 | String $jobname = $title, 9 | ) { 10 | include jenkins::cli 11 | 12 | if $jenkins::service_ensure == 'stopped' or $jenkins::service_ensure == false { 13 | fail('Management of Jenkins jobs requires \$jenkins::service_ensure to be set to \'running\'') 14 | } 15 | 16 | $tmp_config_path = "/tmp/${jobname}-config.xml" 17 | $job_dir = "${jenkins::job_dir}/${jobname}" 18 | $config_path = "${job_dir}/config.xml" 19 | 20 | # Temp file to use as stdin for Jenkins CLI executable 21 | file { $tmp_config_path: 22 | ensure => absent, 23 | } 24 | 25 | # Delete the job 26 | exec { "jenkins delete-job ${jobname}": 27 | path => ['/usr/bin', '/usr/sbin', '/bin'], 28 | command => "${jenkins::cli::cmd} delete-job \"${jobname}\"", 29 | logoutput => false, 30 | onlyif => "test -f \"${config_path}\"", 31 | require => Exec['jenkins-cli'], 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /manifests/job/present.pp: -------------------------------------------------------------------------------- 1 | # @summary Creates or updates a jenkins build job 2 | # @api private 3 | # 4 | # @param config The content of the jenkins job config file 5 | # @param config_file Jenkins job config file (file on disk) 6 | # @param jobname The name of the jenkins job 7 | # @param replace Whether or not to replace the job if it already exists. 8 | # 9 | define jenkins::job::present ( 10 | Optional[String] $config = undef, 11 | Optional[String] $config_file = undef, 12 | String $jobname = $title, 13 | String $difftool = '/usr/bin/diff -b -q', 14 | Boolean $replace = true, 15 | ) { 16 | include jenkins::cli 17 | include jenkins::cli::reload 18 | 19 | if $config_file and $config { 20 | fail('You cannot set both $config_file AND $config param, only one is required') 21 | } 22 | 23 | if $config_file == undef and $config == undef { 24 | fail('Please set one of $config_file or $config to create a job') 25 | } 26 | 27 | if $jenkins::service_ensure == 'stopped' or $jenkins::service_ensure == false { 28 | fail('Management of Jenkins jobs requires \$jenkins::service_ensure to be set to \'running\'') 29 | } 30 | 31 | $jenkins_cli = $jenkins::cli::cmd 32 | if $config == undef { 33 | $tmp_config_path = $config_file 34 | } 35 | else { 36 | $tmp_config_path = "/tmp/${jobname}-config.xml" 37 | # 38 | # When a Jenkins job is imported via the cli, Jenkins will 39 | # re-format the xml file based on its own internal rules. 40 | # In order to make job management idempotent, we need to 41 | # apply that formatting before the import, so we can do a diff 42 | # on any pre-existing job to determine if an update is needed. 43 | # 44 | # Jenkins likes to change single quotes to double quotes 45 | $a = regsubst($config, 'version=\'1.0\' encoding=\'UTF-8\'', 46 | 'version="1.0" encoding="UTF-8"') 47 | # Change empty tags into self-closing tags 48 | $b = regsubst($a, '<([A-z]+)><\/\1>', '<\1/>', 'IG') 49 | # Change " to " since Jenkins is weird like that 50 | $c = regsubst($b, '"', '"', 'MG') 51 | # Change ' to ' since Jenkins is weird like that 52 | $d = regsubst($c, ''', '\'', 'MG') 53 | 54 | # Temp file to use as stdin for Jenkins CLI executable 55 | file { $tmp_config_path: 56 | content => $d, 57 | require => Exec['jenkins-cli'], 58 | before => Exec["jenkins create-job ${jobname}"], 59 | } 60 | } 61 | 62 | $job_dir = "${jenkins::job_dir}/${jobname}" 63 | $config_path = "${job_dir}/config.xml" 64 | 65 | # Bring variables from Class['jenkins'] into local scope. 66 | $cli_tries = $jenkins::cli_tries 67 | $cli_try_sleep = $jenkins::cli_try_sleep 68 | 69 | Exec { 70 | logoutput => false, 71 | path => '/bin:/usr/bin:/sbin:/usr/sbin', 72 | tries => $cli_tries, 73 | try_sleep => $cli_try_sleep, 74 | } 75 | 76 | # Use Jenkins CLI to create the job 77 | $cat_config = "cat \"${tmp_config_path}\"" 78 | $create_job = "${jenkins_cli} create-job \"${jobname}\"" 79 | exec { "jenkins create-job ${jobname}": 80 | command => "${cat_config} | ${create_job}", 81 | creates => [$config_path, "${job_dir}/builds"], 82 | } 83 | 84 | if $replace { 85 | # Use Jenkins CLI to update the job if it already exists 86 | $update_job = "${jenkins_cli} update-job ${jobname}" 87 | exec { "jenkins update-job ${jobname}": 88 | command => "${cat_config} | ${update_job}", 89 | onlyif => "test -e ${config_path}", 90 | unless => "${difftool} ${config_path} ${tmp_config_path}", 91 | notify => Exec['reload-jenkins'], 92 | } 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /manifests/jobs.pp: -------------------------------------------------------------------------------- 1 | # @summary Create Jenkins Jobs 2 | # @api private 3 | class jenkins::jobs { 4 | assert_private() 5 | 6 | create_resources('jenkins::job', $jenkins::job_hash) 7 | } 8 | -------------------------------------------------------------------------------- /manifests/master.pp: -------------------------------------------------------------------------------- 1 | # @summary Install a master 2 | # 3 | # @param version 4 | # Version of the swarm plugin 5 | class jenkins::master ( 6 | String $version = $jenkins::params::swarm_version 7 | ) inherits jenkins::params { 8 | jenkins::plugin { 'swarm': 9 | version => $version, 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /manifests/package.pp: -------------------------------------------------------------------------------- 1 | # @summary Installation of the Jenkins native package. 2 | # 3 | # The package might not specify a dependency on Java, so you may need to 4 | # specify that yourself 5 | # 6 | # @api private 7 | class jenkins::package { 8 | assert_private() 9 | 10 | package { $jenkins::package_name: 11 | ensure => $jenkins::version, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /manifests/params.pp: -------------------------------------------------------------------------------- 1 | # @summary Default parameters 2 | # @api private 3 | class jenkins::params { 4 | $swarm_version = '2.2' 5 | $config_hash_defaults = { 6 | 'JAVA_OPTS' => { value => '-Djava.awt.headless=true -Djenkins.install.runSetupWizard=false' }, 7 | } 8 | $default_plugins = [ 9 | 'bouncycastle-api', # required by instance-identity 10 | 'credentials', # required by puppet_helper.groovy 11 | 'instance-identity', # implied by structs 12 | 'javax-activation-api', # implied by all plugin 13 | 'javax-mail-api', # implied by all plugins 14 | 'structs', # required by credentials plugin 15 | ] 16 | 17 | case $facts['os']['family'] { 18 | 'Debian': { 19 | $repo = true 20 | $package_provider = 'dpkg' 21 | } 22 | 'RedHat': { 23 | $repo = true 24 | $package_provider = 'rpm' 25 | } 26 | 'Archlinux': { 27 | $repo = false 28 | $package_provider = 'pacman' 29 | } 30 | default: { 31 | $repo = true 32 | $package_provider = undef 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /manifests/plugins.pp: -------------------------------------------------------------------------------- 1 | # @summary Install Jenkins plugins 2 | # @api private 3 | class jenkins::plugins { 4 | assert_private() 5 | 6 | create_resources('jenkins::plugin',$jenkins::plugin_hash) 7 | } 8 | -------------------------------------------------------------------------------- /manifests/proxy.pp: -------------------------------------------------------------------------------- 1 | # @summary Configure the proxy part 2 | # @api private 3 | class jenkins::proxy { 4 | assert_private() 5 | 6 | # Bring variables from Class['jenkins'] into local scope. 7 | $proxy_host = $jenkins::proxy_host 8 | $proxy_port = $jenkins::proxy_port 9 | $no_proxy_list = $jenkins::no_proxy_list 10 | 11 | if $proxy_host and $proxy_port { 12 | # param format needed by puppet/archive 13 | $url = "http://${proxy_host}:${proxy_port}" 14 | $proxy_xml = "${jenkins::localstatedir}/proxy.xml" 15 | 16 | file { $proxy_xml: 17 | content => template('jenkins/proxy.xml.erb'), 18 | owner => $jenkins::user, 19 | group => $jenkins::group, 20 | mode => '0644', 21 | } 22 | 23 | Package['jenkins'] 24 | -> File[$proxy_xml] 25 | ~> Class['jenkins::service'] 26 | } else { 27 | $url = undef 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /manifests/repo.pp: -------------------------------------------------------------------------------- 1 | # @summary Pull in the platform specific repo classes 2 | # @api private 3 | class jenkins::repo ( 4 | Stdlib::Httpurl $base_url = 'https://pkg.jenkins.io', 5 | String $gpg_key_filename = 'jenkins.io-2023.key', 6 | Boolean $enabled = true, 7 | ) { 8 | assert_private() 9 | 10 | if $jenkins::repo { 11 | case $facts['os']['family'] { 12 | 'RedHat', 'Linux': { 13 | contain jenkins::repo::el 14 | } 15 | 16 | 'Debian': { 17 | contain jenkins::repo::debian 18 | } 19 | 20 | 'Suse' : { 21 | contain jenkins::repo::suse 22 | } 23 | 24 | default: { 25 | fail( "Unsupported OS family: ${facts['os']['family']}" ) 26 | } 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /manifests/repo/debian.pp: -------------------------------------------------------------------------------- 1 | # @summary Set up the apt repo on Debian-based distros 2 | # @api private 3 | class jenkins::repo::debian ( 4 | String $gpg_key_id = '63667EE74BBA1F0A08A698725BA31D57EF5975CA', 5 | ) { 6 | assert_private() 7 | 8 | include apt 9 | 10 | if $jenkins::lts { 11 | $location = "${jenkins::repo::base_url}/debian-stable" 12 | } else { 13 | $location = "${jenkins::repo::base_url}/debian" 14 | } 15 | 16 | apt::source { 'jenkins': 17 | location => $location, 18 | release => 'binary/', 19 | include => { 20 | 'src' => false, 21 | }, 22 | key => { 23 | 'id' => $gpg_key_id, 24 | 'source' => "${location}/${jenkins::repo::gpg_key_filename}", 25 | }, 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /manifests/repo/el.pp: -------------------------------------------------------------------------------- 1 | # @summary Set up the yum repo on Red Hat-based distros 2 | # @api private 3 | class jenkins::repo::el { 4 | assert_private() 5 | 6 | if $jenkins::lts { 7 | $baseurl = "${jenkins::repo::base_url}/redhat-stable/" 8 | } else { 9 | $baseurl = "${jenkins::repo::base_url}/redhat/" 10 | } 11 | 12 | yumrepo { 'jenkins': 13 | descr => 'Jenkins', 14 | baseurl => $baseurl, 15 | gpgcheck => 1, 16 | gpgkey => "${baseurl}${jenkins::repo::gpg_key_filename}", 17 | enabled => $jenkins::repo::enabled, 18 | proxy => $jenkins::repo_proxy, 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /manifests/repo/suse.pp: -------------------------------------------------------------------------------- 1 | # @summary Set up the Zypper repo on SUSE-based distros 2 | # @api private 3 | class jenkins::repo::suse { 4 | assert_private() 5 | 6 | if $jenkins::lts { 7 | $baseurl = "${jenkins::repo::base_url}/opensuse-stable/" 8 | } else { 9 | $baseurl = "${jenkins::repo::base_url}/opensuse/" 10 | } 11 | 12 | zypprepo { 'jenkins': 13 | descr => 'Jenkins', 14 | baseurl => $baseurl, 15 | gpgcheck => 1, 16 | gpgkey => "${baseurl}${jenkins::repo::gpg_key_filename}", 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /manifests/security.pp: -------------------------------------------------------------------------------- 1 | # Copyright 2014 RetailMeNot, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # @summary Jenkins security configuration 16 | # 17 | class jenkins::security ( 18 | String $security_model, 19 | ) { 20 | include jenkins::cli_helper 21 | 22 | Class['jenkins::cli_helper'] 23 | -> Class['jenkins::security'] 24 | -> Anchor['jenkins::end'] 25 | 26 | # XXX not idempotent 27 | jenkins::cli::exec { "jenkins-security-${security_model}": 28 | command => [ 29 | 'set_security', 30 | $security_model, 31 | ], 32 | unless => "\$HELPER_CMD get_authorization_strategyname | grep -q -e '^${security_model}\$'", 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /manifests/service.pp: -------------------------------------------------------------------------------- 1 | # @summary Manage the Jenkins service 2 | # @api private 3 | class jenkins::service { 4 | assert_private() 5 | 6 | service { 'jenkins': 7 | ensure => $jenkins::service_ensure, 8 | enable => $jenkins::service_enable, 9 | provider => $jenkins::service_provider, 10 | hasstatus => true, 11 | hasrestart => true, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /manifests/user.pp: -------------------------------------------------------------------------------- 1 | # Copyright 2014 RetailMeNot, Inc. 2 | # 3 | # Licensed under the Apache License, Version 2.0 (the "License"); 4 | # you may not use this file except in compliance with the License. 5 | # You may obtain a copy of the License at 6 | # 7 | # http://www.apache.org/licenses/LICENSE-2.0 8 | # 9 | # Unless required by applicable law or agreed to in writing, software 10 | # distributed under the License is distributed on an "AS IS" BASIS, 11 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | # See the License for the specific language governing permissions and 13 | # limitations under the License. 14 | # 15 | # @summary Manage Jenkins user accounts 16 | # 17 | define jenkins::user ( 18 | Pattern[/^[^@]+@[^@]+$/] $email, 19 | String $password, 20 | String $full_name = 'Managed by Puppet', 21 | String $public_key = '', # lint:ignore:params_empty_string_assignment 22 | Enum['present', 'absent'] $ensure = 'present', 23 | ) { 24 | include jenkins::cli_helper 25 | 26 | Class['jenkins::cli_helper'] 27 | -> Jenkins::User[$title] 28 | -> Anchor['jenkins::end'] 29 | 30 | case $ensure { 31 | 'present': { 32 | # XXX not idempotent 33 | jenkins::cli::exec { "create-jenkins-user-${title}": 34 | command => [ 35 | 'create_or_update_user', 36 | $title, 37 | $email, 38 | "'${password}'", 39 | "'${full_name}'", 40 | "'${public_key}'", 41 | ], 42 | } 43 | } 44 | 'absent': { 45 | # XXX not idempotent 46 | jenkins::cli::exec { "delete-jenkins-user-${title}": 47 | command => [ 48 | 'delete_user', 49 | $title, 50 | ], 51 | } 52 | } 53 | default: { 54 | fail "ensure must be 'present' or 'absent' but '${ensure}' was given" 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /manifests/user_setup.pp: -------------------------------------------------------------------------------- 1 | # @summary Optionally create the jenkins user and make sure all directories 2 | # have proper permissions setup. 3 | # 4 | # By having this in a separate class that is managed before 5 | # installing the package, we can effectivly override the 6 | # default local dir that is otherwise possibly created by 7 | # the package. 8 | # 9 | # @api private 10 | class jenkins::user_setup { 11 | assert_private() 12 | 13 | $dir_params = { 14 | ensure => directory, 15 | owner => $jenkins::user, 16 | group => $jenkins::group, 17 | mode => '0755', 18 | } 19 | 20 | # ensure_resource is used to try to maintain backwards compatiblity with 21 | # manifests that were able to external declare resources due to the 22 | # old conditional behavior of jenkins::plugin 23 | if $jenkins::manage_user { 24 | ensure_resource('user', $jenkins::user, { 25 | ensure => present, 26 | gid => $jenkins::group, 27 | home => $jenkins::localstatedir, 28 | managehome => false, 29 | system => true, 30 | }) 31 | } 32 | 33 | if $jenkins::manage_group { 34 | ensure_resource('group', $jenkins::group, { 35 | ensure => present, 36 | system => true, 37 | }) 38 | } 39 | 40 | $plugin_dir_params = $jenkins::purge_plugins ? { 41 | true => $dir_params + { 42 | 'purge' => true, 43 | 'recurse' => true, 44 | 'force' => true, 45 | 'notify' => Service['jenkins'], 46 | }, 47 | default => $dir_params, 48 | } 49 | 50 | if $jenkins::manage_datadirs { 51 | ensure_resource('file', $jenkins::localstatedir, $dir_params) 52 | ensure_resource('file', $jenkins::plugin_dir, $plugin_dir_params) 53 | ensure_resource('file', $jenkins::job_dir, $dir_params) 54 | } 55 | 56 | # On Debian the service is started by default so it must be configured prior 57 | # to installation which is why it's configured in this file rather than config.pp 58 | $config_hash = $jenkins::params::config_hash_defaults + $jenkins::config_hash 59 | 60 | systemd::dropin_file { 'puppet-overrides.conf': 61 | unit => 'jenkins.service', 62 | content => epp("${module_name}/jenkins-override.epp", { 'environment' => $config_hash, 'dropin_config' => $jenkins::service_override }), 63 | notify_service => true, 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /manifests/users.pp: -------------------------------------------------------------------------------- 1 | # @summary Create Jenkins users 2 | # @api private 3 | class jenkins::users { 4 | assert_private() 5 | 6 | create_resources('jenkins::user', $jenkins::user_hash) 7 | } 8 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "puppet-jenkins", 3 | "version": "6.0.0", 4 | "author": "Vox Pupuli", 5 | "license": "Apache-2.0", 6 | "summary": "Manage the Jenkins continuous integration service with Puppet", 7 | "source": "https://github.com/voxpupuli/puppet-jenkins", 8 | "project_page": "https://github.com/voxpupuli/puppet-jenkins", 9 | "issues_url": "https://github.com/voxpupuli/puppet-jenkins/issues", 10 | "tags": [ 11 | "jenkins", 12 | "jenkinsci", 13 | "voxpupuli" 14 | ], 15 | "operatingsystem_support": [ 16 | { 17 | "operatingsystem": "RedHat", 18 | "operatingsystemrelease": [ 19 | "8", 20 | "9" 21 | ] 22 | }, 23 | { 24 | "operatingsystem": "CentOS", 25 | "operatingsystemrelease": [ 26 | "9" 27 | ] 28 | }, 29 | { 30 | "operatingsystem": "OracleLinux", 31 | "operatingsystemrelease": [ 32 | "8", 33 | "9" 34 | ] 35 | }, 36 | { 37 | "operatingsystem": "Rocky", 38 | "operatingsystemrelease": [ 39 | "8", 40 | "9" 41 | ] 42 | }, 43 | { 44 | "operatingsystem": "AlmaLinux", 45 | "operatingsystemrelease": [ 46 | "8", 47 | "9" 48 | ] 49 | }, 50 | { 51 | "operatingsystem": "SLES", 52 | "operatingsystemrelease": [ 53 | "11", 54 | "12", 55 | "15" 56 | ] 57 | }, 58 | { 59 | "operatingsystem": "OpenSuSE", 60 | "operatingsystemrelease": [ 61 | "42", 62 | "15" 63 | ] 64 | }, 65 | { 66 | "operatingsystem": "Ubuntu", 67 | "operatingsystemrelease": [ 68 | "22.04", 69 | "24.04" 70 | ] 71 | }, 72 | { 73 | "operatingsystem": "Debian", 74 | "operatingsystemrelease": [ 75 | "12" 76 | ] 77 | } 78 | ], 79 | "dependencies": [ 80 | { 81 | "name": "puppetlabs/stdlib", 82 | "version_requirement": ">= 4.19.0 < 10.0.0" 83 | }, 84 | { 85 | "name": "puppetlabs/apt", 86 | "version_requirement": ">= 9.2.0 < 11.0.0" 87 | }, 88 | { 89 | "name": "puppetlabs/java", 90 | "version_requirement": ">= 1.0.1 < 12.0.0" 91 | }, 92 | { 93 | "name": "puppet/zypprepo", 94 | "version_requirement": ">= 2.0.0 < 6.0.0" 95 | }, 96 | { 97 | "name": "puppet/archive", 98 | "version_requirement": ">= 1.3.0 < 8.0.0" 99 | }, 100 | { 101 | "name": "puppet/systemd", 102 | "version_requirement": ">= 3.1.0 < 9.0.0" 103 | } 104 | ], 105 | "requirements": [ 106 | { 107 | "name": "puppet", 108 | "version_requirement": ">= 7.0.0 < 9.0.0" 109 | } 110 | ] 111 | } 112 | -------------------------------------------------------------------------------- /spec/acceptance/class_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'jenkins class' do 6 | context 'default parameters' do 7 | include_examples 'an idempotent resource' do 8 | let(:manifest) do 9 | <<~PUPPET 10 | class {'jenkins': 11 | cli => true, 12 | } 13 | PUPPET 14 | end 15 | end 16 | 17 | describe port(8080) do 18 | it { 19 | sleep(10) # Jenkins takes a while to start up 20 | is_expected.to be_listening 21 | } 22 | end 23 | 24 | describe file('/usr/share/java/jenkins-cli.jar') do 25 | it { is_expected.to be_file } 26 | it { is_expected.to be_readable.by('owner') } 27 | it { is_expected.to be_writable.by('owner') } 28 | it { is_expected.to be_readable.by('group') } 29 | it { is_expected.to be_readable.by('others') } 30 | end 31 | 32 | describe file('/etc/systemd/system/jenkins.service.d/puppet-overrides.conf') do 33 | it { is_expected.to be_file } 34 | it { is_expected.to contain 'Environment=' } 35 | end 36 | 37 | describe service('jenkins') do 38 | it { is_expected.to be_running } 39 | it { is_expected.to be_enabled } 40 | end 41 | 42 | describe process('java') do 43 | it { is_expected.to be_running } 44 | its(:args) { is_expected.to match(%r{-Djenkins\.install\.runSetupWizard=false}) } 45 | end 46 | end 47 | 48 | context 'executors' do 49 | include_examples 'an idempotent resource' do 50 | let(:manifest) do 51 | <<~PUPPET 52 | class {'jenkins': 53 | executors => 42, 54 | } 55 | PUPPET 56 | end 57 | end 58 | 59 | describe port(8080) do 60 | # jenkins should already have been running so we shouldn't have to 61 | # sleep 62 | it { is_expected.to be_listening } 63 | end 64 | 65 | describe service('jenkins') do 66 | it { is_expected.to be_running } 67 | it { is_expected.to be_enabled } 68 | end 69 | 70 | describe file('/var/lib/jenkins/config.xml') do 71 | it { is_expected.to contain ' 42' } 72 | end 73 | end 74 | 75 | context 'slaveagentport' do 76 | include_examples 'an idempotent resource' do 77 | let(:manifest) do 78 | <<~PUPPET 79 | class {'jenkins': 80 | slaveagentport => 7777, 81 | } 82 | PUPPET 83 | end 84 | end 85 | 86 | describe port(8080) do 87 | # jenkins should already have been running so we shouldn't have to 88 | # sleep 89 | it { is_expected.to be_listening } 90 | end 91 | 92 | describe service('jenkins') do 93 | it { is_expected.to be_running } 94 | it { is_expected.to be_enabled } 95 | end 96 | 97 | describe file('/var/lib/jenkins/config.xml') do 98 | it { is_expected.to contain ' 7777' } 99 | end 100 | end 101 | end 102 | -------------------------------------------------------------------------------- /spec/acceptance/hieradata/common.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | jenkins::service_override: 3 | StartLimitIntervalSec: '0' 4 | StartLimitBurst: '0' 5 | -------------------------------------------------------------------------------- /spec/acceptance/hieradata/family/Debian.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Jenkins 2.479.1 requires Java 17 or newer 3 | java::package: openjdk-17-jdk 4 | -------------------------------------------------------------------------------- /spec/acceptance/hieradata/family/RedHat.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | # Jenkins 2.479.1 requires Java 17 or newer 3 | java::package: java-17-openjdk-devel 4 | -------------------------------------------------------------------------------- /spec/acceptance/job_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'jenkins::job' do 6 | let(:test_build_job) do 7 | example = <<~EOS 8 | 9 | 10 | 11 | test job 12 | false 13 | 14 | 15 | true 16 | false 17 | false 18 | false 19 | 20 | false 21 | 22 | 23 | /usr/bin/true 24 | 25 | 26 | 27 | 28 | 29 | EOS 30 | # escape single quotes for puppet 31 | example.gsub("'", %q(\\\')) 32 | end 33 | 34 | context 'create' do 35 | it 'works with no errors' do 36 | pp = <<-EOS 37 | include jenkins 38 | 39 | # the historical assumption is that this will work without cli => true 40 | # set on the jenkins class 41 | jenkins::job { 'test-build-job': 42 | config => '#{test_build_job}', 43 | } 44 | EOS 45 | 46 | # Run it twice and test for idempotency 47 | apply(pp, catch_failures: true) 48 | # XXX idempotency is broken with at least jenkins 1.613 49 | # apply(pp, :catch_changes => true) 50 | end 51 | 52 | describe file('/var/lib/jenkins/jobs/test-build-job/config.xml') do 53 | it { is_expected.to be_file } 54 | it { is_expected.to be_owned_by 'jenkins' } 55 | it { is_expected.to be_grouped_into 'jenkins' } 56 | it { is_expected.to be_mode 644 } 57 | it { is_expected.to contain 'test job' } 58 | it { is_expected.to contain 'false' } 59 | it { is_expected.to contain '/usr/bin/true' } 60 | end 61 | end 62 | 63 | context 'no replace' do 64 | it 'does not replace an existing job' do 65 | pp_create = <<-EOS 66 | include jenkins 67 | jenkins::job {'test-noreplace-job': 68 | config => '#{test_build_job.gsub('test job', 'do not overwrite me')}', 69 | } 70 | EOS 71 | 72 | pp_update = <<-EOS 73 | include jenkins 74 | jenkins::job {'test-noreplace-job': 75 | config => '#{test_build_job}', 76 | replace => false, 77 | } 78 | EOS 79 | 80 | apply(pp_create, catch_failures: true) 81 | apply(pp_update, catch_failures: true) 82 | end 83 | 84 | describe file('/var/lib/jenkins/jobs/test-noreplace-job/config.xml') do 85 | it { is_expected.to be_file } 86 | it { is_expected.to be_owned_by 'jenkins' } 87 | it { is_expected.to be_grouped_into 'jenkins' } 88 | it { is_expected.to be_mode 644 } 89 | it { is_expected.to contain 'do not overwrite me' } 90 | end 91 | end 92 | 93 | context 'delete' do 94 | it 'works with no errors' do 95 | # create a test job so it can be deleted; job creation is not what 96 | # we're intending to be testing here 97 | pp = <<-EOS 98 | include jenkins 99 | jenkins::job { 'test-build-job': 100 | config => '#{test_build_job}', 101 | } 102 | EOS 103 | 104 | apply(pp) 105 | 106 | # test job deletion 107 | pp = <<-EOS 108 | include jenkins 109 | jenkins::job { 'test-build-job': 110 | ensure => 'absent', 111 | config => '#{test_build_job}', 112 | } 113 | EOS 114 | 115 | # Run it twice and test for idempotency 116 | apply(pp, catch_failures: true) 117 | # XXX idempotency is broken with at least jenkins 1.613 118 | # apply(pp, :catch_changes => true) 119 | end 120 | 121 | describe file('/var/lib/jenkins/jobs/test-build-job/config.xml') do 122 | # XXX Serverspec::Type::File doesn't support exists? 123 | it { is_expected.not_to be_file } 124 | end 125 | end 126 | end 127 | -------------------------------------------------------------------------------- /spec/acceptance/plugin_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | 5 | describe 'jenkins class', order: :defined do 6 | PDIR = '/var/lib/jenkins/plugins' 7 | 8 | # files/directories to test plugin purging removal of unmanaged files 9 | FILES = [ 10 | "#{PDIR}/a.hpi", 11 | "#{PDIR}/b.jpi", 12 | "#{PDIR}/c.txt", 13 | "#{PDIR}/a/foo", 14 | "#{PDIR}/b/bar", 15 | "#{PDIR}/c/baz" 16 | ].freeze 17 | DIRS = [ 18 | "#{PDIR}/a", 19 | "#{PDIR}/b", 20 | "#{PDIR}/c" 21 | ].freeze 22 | 23 | shared_examples 'has_plugin' do |plugin| 24 | describe file("#{PDIR}/#{plugin}.hpi") do 25 | it { is_expected.to be_file } 26 | end 27 | 28 | describe file("#{PDIR}/#{plugin}") do 29 | it { is_expected.to be_directory } 30 | end 31 | end 32 | 33 | shared_context 'plugin_test_files' do 34 | before(:context) do 35 | shell("mkdir -p #{DIRS.join(' ')}") 36 | shell("touch #{FILES.join(' ')}") 37 | end 38 | 39 | after(:context) do 40 | shell("rm -rf #{DIRS.join(' ')} #{FILES.join(' ')}") 41 | end 42 | end 43 | 44 | context 'default parameters' do 45 | include_examples 'an idempotent resource' do 46 | let(:manifest) do 47 | <<~PUPPET 48 | include jenkins 49 | jenkins::plugin {'git-plugin': 50 | name => 'git', 51 | version => '2.3.4', 52 | } 53 | PUPPET 54 | end 55 | end 56 | 57 | it_behaves_like 'has_plugin', 'git' 58 | end 59 | 60 | describe 'plugin downgrade' do 61 | describe 'jquery3-api plugin' do 62 | describe 'installs version 3.6.0-r3' do 63 | include_examples 'an idempotent resource' do 64 | let(:manifest) do 65 | <<~PUPPET 66 | class {'jenkins': 67 | purge_plugins => true, 68 | } 69 | 70 | # actual plugin 71 | jenkins::plugin { 'jquery3-api': 72 | version => '3.6.0-4', 73 | } 74 | PUPPET 75 | end 76 | end 77 | end 78 | 79 | describe 'downgrades to 3.6.0-3' do 80 | include_examples 'an idempotent resource' do 81 | let(:manifest) do 82 | <<~PUPPET 83 | package{'unzip': 84 | ensure => present 85 | } 86 | class {'jenkins': 87 | purge_plugins => true, 88 | } 89 | 90 | # actual plugin 91 | jenkins::plugin { 'jquery3-api': 92 | version => '3.6.0-3', 93 | } 94 | PUPPET 95 | end 96 | end 97 | end 98 | 99 | describe command("unzip -p #{PDIR}/jquery3-api.hpi META-INF/MANIFEST.MF | sed 's/Plugin-Version: \\(.*\\)/\\1/;tx;d;:x'") do 100 | its(:stdout) { is_expected.to eq("3.6.0-3\n") } 101 | end 102 | 103 | it_behaves_like 'has_plugin', 'jquery3-api' 104 | end 105 | end 106 | 107 | describe 'plugin purging' do 108 | context 'true' do 109 | include_context 'plugin_test_files' 110 | 111 | it 'works with no errors' do 112 | pp = <<-EOS 113 | class {'jenkins': 114 | purge_plugins => true, 115 | } 116 | 117 | # Actual plugin 118 | jenkins::plugin { 'jquery3-api': 119 | version => '3.6.0-4', 120 | } 121 | EOS 122 | 123 | apply(pp, catch_failures: true) 124 | apply(pp, catch_changes: true) 125 | end 126 | 127 | it_behaves_like 'has_plugin', 'jquery3-api' 128 | 129 | (DIRS + FILES).each do |f| 130 | describe file(f) do 131 | it { is_expected.not_to exist } 132 | end 133 | end 134 | end 135 | 136 | context 'false' do 137 | include_context 'plugin_test_files' 138 | 139 | it 'works with no errors' do 140 | pp = <<-EOS 141 | class {'jenkins': 142 | purge_plugins => false, 143 | } 144 | 145 | # Actual plugin 146 | jenkins::plugin { 'jquery3-api': 147 | version => '3.6.0-4', 148 | } 149 | EOS 150 | 151 | apply(pp, catch_failures: true) 152 | apply(pp, catch_changes: true) 153 | end 154 | 155 | it_behaves_like 'has_plugin', 'jquery3-api' 156 | 157 | DIRS.each do |f| 158 | describe file(f) do 159 | it { is_expected.to be_directory } 160 | end 161 | end 162 | 163 | FILES.each do |f| 164 | describe file(f) do 165 | it { is_expected.to be_file } 166 | end 167 | end 168 | end 169 | end 170 | end 171 | -------------------------------------------------------------------------------- /spec/classes/cli/config_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins::cli::config' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | 10 | shared_examples 'validate_absolute_path' do |param| 11 | context 'absolute path' do 12 | let(:params) { { param => '/dne' } } 13 | 14 | it { is_expected.to compile } 15 | end 16 | end 17 | 18 | shared_examples 'validate_integer' do |param| 19 | context 'integer' do 20 | let(:params) { { param => 42 } } 21 | 22 | it { is_expected.to compile } 23 | end 24 | end 25 | 26 | shared_examples 'validate_numeric' do |param| 27 | context 'integer' do 28 | let(:params) { { param => 42 } } 29 | 30 | it { is_expected.to compile } 31 | end 32 | 33 | context 'float' do 34 | let(:params) { { param => 42.12345 } } 35 | 36 | it { is_expected.to compile } 37 | end 38 | end 39 | 40 | shared_examples 'validate_string' do |param| 41 | context 'string' do 42 | let(:params) { { param => 'foo' } } 43 | 44 | it { is_expected.to compile } 45 | end 46 | end 47 | 48 | describe 'parameters' do 49 | context 'accept all params undef' do 50 | it { is_expected.to compile } 51 | end 52 | 53 | describe 'cli_jar' do 54 | it_behaves_like 'validate_absolute_path', :cli_jar 55 | end 56 | 57 | # context 'port' do 58 | # it_behaves_like 'validate_integer', :port 59 | # end 60 | context 'url' do 61 | it_behaves_like 'validate_string', :url 62 | end 63 | 64 | context 'ssh_private_key' do 65 | it_behaves_like 'validate_absolute_path', :ssh_private_key 66 | end 67 | 68 | context 'puppet_helper' do 69 | it_behaves_like 'validate_absolute_path', :puppet_helper 70 | end 71 | 72 | context 'cli_tries' do 73 | it_behaves_like 'validate_integer', :cli_tries 74 | end 75 | 76 | context 'cli_try_sleep' do 77 | it_behaves_like 'validate_numeric', :cli_try_sleep 78 | end 79 | 80 | context 'ssh_private_key_content' do 81 | it_behaves_like 'validate_string', :ssh_private_key_content 82 | 83 | context 'when ssh_private_key is also set' do 84 | let(:params) do 85 | { 86 | ssh_private_key: '/dne', 87 | ssh_private_key_content: 'foo' 88 | } 89 | end 90 | 91 | context 'as non-root user' do 92 | let :facts do 93 | super().merge(id: 'user') 94 | end 95 | 96 | it do 97 | is_expected.to contain_file('/dne').with( 98 | ensure: 'file', 99 | mode: '0400', 100 | backup: false, 101 | owner: nil, 102 | group: nil 103 | ) 104 | end 105 | 106 | it { is_expected.to contain_file('/dne').with_content('foo') } 107 | end 108 | 109 | context 'as root' do 110 | let :facts do 111 | super().merge(id: 'root') 112 | end 113 | 114 | it do 115 | is_expected.to contain_file('/dne').with( 116 | ensure: 'file', 117 | mode: '0400', 118 | backup: false, 119 | owner: 'jenkins', 120 | group: 'jenkins' 121 | ) 122 | end 123 | 124 | it { is_expected.to contain_file('/dne').with_content('foo') } 125 | end 126 | end 127 | end 128 | end 129 | end 130 | end 131 | end 132 | -------------------------------------------------------------------------------- /spec/classes/jenkins_cli_helper_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins::cli_helper' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | 10 | describe 'relationships' do 11 | it do 12 | is_expected.to contain_class('jenkins::cli') 13 | is_expected.to contain_class('jenkins::cli_helper'). 14 | that_requires('Class[jenkins::cli]') 15 | end 16 | 17 | it do 18 | is_expected.to contain_class('jenkins::cli_helper'). 19 | that_comes_before('Anchor[jenkins::end]') 20 | end 21 | end 22 | 23 | it do 24 | is_expected.to contain_file('/usr/share/java/puppet_helper.groovy').with( 25 | source: 'puppet:///modules/jenkins/puppet_helper.groovy', 26 | owner: 'jenkins', 27 | group: 'jenkins', 28 | mode: '0444' 29 | ) 30 | end 31 | end 32 | end 33 | end 34 | -------------------------------------------------------------------------------- /spec/classes/jenkins_cli_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | 10 | context 'cli' do 11 | context 'default' do 12 | it { is_expected.to contain_class('jenkins').with(cli: true) } 13 | it { is_expected.to contain_class('jenkins::cli') } 14 | it { is_expected.to contain_class('jenkins::cli_helper') } 15 | end 16 | 17 | context '$cli => true' do 18 | let(:params) do 19 | { cli: true, 20 | cli_ssh_keyfile: '/path/to/key', 21 | cli_username: 'myuser', 22 | libdir: '/path/to/libdir', 23 | config_hash: { 'JENKINS_PORT' => { 'value' => '9000' } } } 24 | end 25 | 26 | it { is_expected.to contain_class('jenkins::cli') } 27 | it { is_expected.to contain_exec('jenkins-cli') } 28 | it { is_expected.to contain_exec('reload-jenkins').with_command(%r{http://localhost:9000}) } 29 | it { is_expected.to contain_exec('reload-jenkins').with_command(%r{-i\s'/path/to/key'}) } 30 | it { is_expected.to contain_exec('reload-jenkins').that_requires('File[/path/to/libdir/jenkins-cli.jar]') } 31 | it { is_expected.to contain_exec('safe-restart-jenkins') } 32 | 33 | describe 'jenkins::cli' do 34 | describe 'relationships' do 35 | it do 36 | is_expected.to contain_class('jenkins::cli'). 37 | that_requires('Class[jenkins::service]') 38 | end 39 | 40 | it do 41 | is_expected.to contain_class('jenkins::cli'). 42 | that_comes_before('Anchor[jenkins::end]') 43 | end 44 | end 45 | end 46 | end 47 | 48 | context '$cli => false' do 49 | let(:params) { { cli: false } } 50 | 51 | it { is_expected.not_to contain_class('jenkins::cli') } 52 | it { is_expected.not_to contain_class('jenkins::cli_helper') } 53 | end 54 | end 55 | end 56 | end 57 | end 58 | -------------------------------------------------------------------------------- /spec/classes/jenkins_config_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | 10 | context 'config' do 11 | context 'default' do 12 | it { is_expected.to contain_class('jenkins::config') } 13 | it { is_expected.to contain_jenkins__plugin('credentials') } 14 | 15 | it do 16 | is_expected.to contain_file('/etc/systemd/system/jenkins.service.d/puppet-overrides.conf'). 17 | with_content <<~CONFIG 18 | [Service] 19 | Environment="JAVA_OPTS=-Djava.awt.headless=true -Djenkins.install.runSetupWizard=false" 20 | CONFIG 21 | end 22 | end 23 | 24 | context 'create config' do 25 | let(:params) { { config_hash: { 'AJP_PORT' => { 'value' => '1234' } }, service_override: { 'WorkingDirectory' => '/example/path' } } } 26 | 27 | it do 28 | is_expected.to contain_file('/etc/systemd/system/jenkins.service.d/puppet-overrides.conf'). 29 | with_content <<~CONFIG 30 | [Service] 31 | Environment="JAVA_OPTS=-Djava.awt.headless=true -Djenkins.install.runSetupWizard=false" 32 | Environment="AJP_PORT=1234" 33 | WorkingDirectory=/example/path 34 | CONFIG 35 | end 36 | end 37 | end 38 | end 39 | end 40 | end 41 | -------------------------------------------------------------------------------- /spec/classes/jenkins_direct_download_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | let(:params) { { direct_download: 'http://local.space/jenkins.rpm' } } 10 | 11 | describe 'direct_download' do 12 | context 'default' do 13 | it { is_expected.to contain_package('jenkins').with_installed } 14 | it { is_expected.not_to contain_class('jenkins::package') } 15 | it { is_expected.to contain_class('jenkins::direct_download') } 16 | end 17 | 18 | context 'with version' do 19 | let(:params) { { version: '1.2.3' } } 20 | 21 | it { is_expected.to contain_package('jenkins').with_ensure('1.2.3') } 22 | end 23 | 24 | context 'package dir created' do 25 | it { is_expected.to contain_file('/var/cache/jenkins_pkgs').with_ensure('directory') } 26 | end 27 | 28 | context 'staging resource created' do 29 | it do 30 | is_expected.to contain_archive('jenkins.rpm').with( 31 | source: 'http://local.space/jenkins.rpm', 32 | path: '/var/cache/jenkins_pkgs/jenkins.rpm', 33 | cleanup: false, 34 | extract: false 35 | ).that_comes_before('Package[jenkins]') 36 | end 37 | end 38 | 39 | context 'package removable' do 40 | let(:params) { { version: 'absent', direct_download: 'http://local.space/jenkins.rpm' } } 41 | 42 | it { is_expected.not_to contain_staging__file('jenkins.rpm') } 43 | it { is_expected.to contain_package('jenkins').with_ensure('absent') } 44 | end 45 | 46 | context 'unsupported provider fails' do 47 | let(:params) { { package_provider: false, direct_download: 'http://local.space/jenkins.rpm' } } 48 | 49 | it { is_expected.to compile.and_raise_error(%r{got Boolean}) } 50 | end 51 | end 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/classes/jenkins_firewall_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | let(:pre_condition) { ['define firewall($action, $state, $dport, $proto) {}'] } 10 | let(:params) { { configure_firewall: true } } 11 | 12 | context 'firewall' do 13 | it { is_expected.to contain_firewall('500 allow Jenkins inbound traffic') } 14 | end 15 | end 16 | end 17 | end 18 | -------------------------------------------------------------------------------- /spec/classes/jenkins_jobs_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | 10 | context 'jobs' do 11 | context 'default' do 12 | it { is_expected.to contain_class('jenkins::jobs') } 13 | end 14 | 15 | context 'with one job' do 16 | let(:params) { { job_hash: { 'build' => { 'config' => '' } } } } 17 | 18 | it { is_expected.to contain_jenkins__job('build').with_config('') } 19 | end 20 | 21 | context 'with cli disabled' do 22 | let(:params) do 23 | { 24 | service_ensure: 'stopped', 25 | cli: false, 26 | job_hash: { 'build' => { 'config' => '' } } 27 | } 28 | end 29 | 30 | it { is_expected.to compile.and_raise_error(%r{Management of Jenkins jobs requires \\\$jenkins::service_ensure to be set to 'running'}) } 31 | end 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/classes/jenkins_master_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins::master' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | let(:params) { { version: '1.2.3' } } 10 | let(:pre_condition) { 'include jenkins' } 11 | 12 | it { is_expected.to contain_jenkins__plugin('swarm').with_version('1.2.3') } 13 | end 14 | end 15 | end 16 | -------------------------------------------------------------------------------- /spec/classes/jenkins_package_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | 10 | describe 'package' do 11 | context 'default' do 12 | it { is_expected.to contain_package('jenkins').with_installed } 13 | end 14 | 15 | context 'with version' do 16 | let(:params) { { version: '1.2.3' } } 17 | 18 | it { is_expected.to contain_package('jenkins').with_ensure('1.2.3') } 19 | end 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/classes/jenkins_plugins_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | 10 | context 'plugins' do 11 | context 'default' do 12 | it { is_expected.to contain_class('jenkins::plugins') } 13 | end 14 | 15 | context 'install plugin' do 16 | let(:params) { { plugin_hash: { 'git' => { 'version' => '1.1.1' } } } } 17 | 18 | it { is_expected.to contain_jenkins__plugin('git').with_version('1.1.1') } 19 | end 20 | end 21 | end 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /spec/classes/jenkins_proxy_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | 10 | context 'proxy' do 11 | context 'default' do 12 | it { is_expected.to contain_class('jenkins::proxy') } 13 | end 14 | 15 | context 'with basic proxy config' do 16 | let(:params) { { proxy_host: 'myhost', proxy_port: 1234 } } 17 | 18 | it { is_expected.to create_class('jenkins::proxy') } 19 | 20 | it do 21 | is_expected.to contain_file('/var/lib/jenkins/proxy.xml').with( 22 | owner: 'jenkins', 23 | group: 'jenkins', 24 | mode: '0644' 25 | ) 26 | end 27 | 28 | it { is_expected.to contain_file('/var/lib/jenkins/proxy.xml').with(content: %r{myhost}) } 29 | it { is_expected.to contain_file('/var/lib/jenkins/proxy.xml').with(content: %r{1234}) } 30 | it { is_expected.to contain_file('/var/lib/jenkins/proxy.xml').without(content: %r{}) } 31 | end 32 | 33 | context 'with "no_proxy_list" proxy config' do 34 | let(:params) { { proxy_host: 'myhost', proxy_port: 1234, no_proxy_list: ['example.com', 'test.host.net'] } } 35 | 36 | it { is_expected.to create_class('jenkins::proxy') } 37 | 38 | it do 39 | is_expected.to contain_file('/var/lib/jenkins/proxy.xml').with( 40 | owner: 'jenkins', 41 | group: 'jenkins', 42 | mode: '0644' 43 | ) 44 | end 45 | 46 | it { is_expected.to contain_file('/var/lib/jenkins/proxy.xml').with(content: %r{myhost}) } 47 | it { is_expected.to contain_file('/var/lib/jenkins/proxy.xml').with(content: %r{1234}) } 48 | it { is_expected.to contain_file('/var/lib/jenkins/proxy.xml').with(content: %r{example\.com\ntest\.host\.net}) } 49 | end 50 | end 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /spec/classes/jenkins_repo_debian_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}", if: os_facts[:osfamily] == 'Debian' do 8 | let(:facts) { os_facts } 9 | 10 | context 'repo::debian' do 11 | shared_examples 'an apt catalog' do 12 | it { is_expected.to contain_class('apt') } 13 | it { is_expected.to contain_apt__source('jenkins') } 14 | end 15 | 16 | describe 'default' do 17 | it_behaves_like 'an apt catalog' 18 | it { is_expected.to contain_apt__source('jenkins').with_location('https://pkg.jenkins.io/debian-stable') } 19 | end 20 | 21 | describe 'lts = true' do 22 | let(:params) { { lts: true } } 23 | 24 | it_behaves_like 'an apt catalog' 25 | it { is_expected.to contain_apt__source('jenkins').with_location('https://pkg.jenkins.io/debian-stable') } 26 | end 27 | 28 | describe 'lts = false' do 29 | let(:params) { { lts: false } } 30 | 31 | it_behaves_like 'an apt catalog' 32 | it { is_expected.to contain_apt__source('jenkins').with_location('https://pkg.jenkins.io/debian') } 33 | end 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /spec/classes/jenkins_repo_el_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins' do 6 | on_supported_os(supported_os: [{ 'operatingsystem' => 'CentOS' }]).each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | 10 | context 'repo::el' do 11 | describe 'default' do 12 | it { is_expected.to contain_yumrepo('jenkins').with_baseurl('https://pkg.jenkins.io/redhat-stable/') } 13 | it { is_expected.to contain_yumrepo('jenkins').with_proxy(nil) } 14 | end 15 | 16 | describe 'lts = true' do 17 | let(:params) { { lts: true } } 18 | 19 | it { is_expected.to contain_yumrepo('jenkins').with_baseurl('https://pkg.jenkins.io/redhat-stable/') } 20 | it { is_expected.to contain_yumrepo('jenkins').with_proxy(nil) } 21 | end 22 | 23 | describe 'lts = false' do 24 | let(:params) { { lts: false } } 25 | 26 | it { is_expected.to contain_yumrepo('jenkins').with_proxy(nil) } 27 | it { is_expected.to contain_yumrepo('jenkins').with_baseurl('https://pkg.jenkins.io/redhat/') } 28 | end 29 | 30 | describe 'repo_proxy is set' do 31 | let(:params) { { repo_proxy: 'http://proxy:8080/' } } 32 | 33 | it { is_expected.to contain_yumrepo('jenkins').with_proxy('http://proxy:8080/') } 34 | end 35 | end 36 | end 37 | end 38 | end 39 | -------------------------------------------------------------------------------- /spec/classes/jenkins_repo_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | 10 | describe 'repo' do 11 | describe 'default' do 12 | case os_facts[:os]['family'] 13 | when 'RedHat' 14 | describe 'RedHat' do 15 | it { is_expected.to compile.with_all_deps } 16 | it { is_expected.to contain_class('jenkins::repo::el') } 17 | it { is_expected.not_to contain_class('jenkins::repo::suse') } 18 | it { is_expected.not_to contain_class('jenkins::repo::debian') } 19 | it { is_expected.to contain_package('jenkins').that_requires('Yumrepo[jenkins]') } 20 | end 21 | 22 | describe 'repo => false' do 23 | let(:params) { { repo: false } } 24 | 25 | it { is_expected.to compile.with_all_deps } 26 | it { is_expected.not_to contain_class('jenkins::repo') } 27 | it { is_expected.not_to contain_class('jenkins::repo::el') } 28 | it { is_expected.not_to contain_class('jenkins::repo::suse') } 29 | it { is_expected.not_to contain_class('jenkins::repo::debian') } 30 | end 31 | when 'Suse' 32 | describe 'Suse' do 33 | it { is_expected.to compile.with_all_deps } 34 | it { is_expected.to contain_class('jenkins::repo::suse') } 35 | it { is_expected.not_to contain_class('jenkins::repo::el') } 36 | it { is_expected.not_to contain_class('jenkins::repo::debian') } 37 | it { is_expected.to contain_package('jenkins').that_requires('Zypprepo[jenkins]') } 38 | end 39 | when 'Debian' 40 | describe 'Debian' do 41 | it { is_expected.to compile.with_all_deps } 42 | it { is_expected.to contain_class('jenkins::repo::debian') } 43 | it { is_expected.not_to contain_class('jenkins::repo::suse') } 44 | it { is_expected.not_to contain_class('jenkins::repo::el') } 45 | it { is_expected.to contain_package('jenkins').that_requires('Apt::Source[jenkins]') } 46 | end 47 | end 48 | end 49 | end 50 | end 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /spec/classes/jenkins_repo_suse_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}", if: os_facts[:osfamily] == 'Suse' do 8 | let(:facts) { os_facts } 9 | 10 | context 'repo::suse' do 11 | describe 'default' do 12 | it { is_expected.to contain_zypprepo('jenkins').with_baseurl('https://pkg.jenkins.io/opensuse-stable/') } 13 | end 14 | 15 | describe 'lts = true' do 16 | let(:params) { { lts: true } } 17 | 18 | it { is_expected.to contain_zypprepo('jenkins').with_baseurl('https://pkg.jenkins.io/opensuse-stable/') } 19 | end 20 | 21 | describe 'lts = false' do 22 | let(:params) { { lts: false } } 23 | 24 | it { is_expected.to contain_zypprepo('jenkins').with_baseurl('https://pkg.jenkins.io/opensuse/') } 25 | end 26 | end 27 | end 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/classes/jenkins_security_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins::security' do 6 | on_supported_os.each do |os, facts| 7 | context "on #{os}" do 8 | let(:facts) { facts } 9 | let(:params) { { security_model: 'test' } } 10 | 11 | describe 'relationships' do 12 | it do 13 | is_expected.to contain_class('jenkins::security'). 14 | that_requires('Class[jenkins::cli_helper]') 15 | end 16 | 17 | it do 18 | is_expected.to contain_class('jenkins::security'). 19 | that_comes_before('Anchor[jenkins::end]') 20 | end 21 | 22 | it { is_expected.to compile } 23 | end 24 | end 25 | end 26 | end 27 | -------------------------------------------------------------------------------- /spec/classes/jenkins_user_setup_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | 10 | context 'user_setup' do 11 | context 'default' do 12 | it { is_expected.to contain_user('jenkins') } 13 | it { is_expected.to contain_group('jenkins') } 14 | 15 | [ 16 | '/var/lib/jenkins', 17 | '/var/lib/jenkins/plugins', 18 | '/var/lib/jenkins/jobs' 19 | ].each do |datadir| 20 | it do 21 | is_expected.to contain_file(datadir).with( 22 | ensure: 'directory', 23 | mode: '0755', 24 | group: 'jenkins', 25 | owner: 'jenkins' 26 | ) 27 | end 28 | end 29 | end 30 | 31 | context 'unmanaged' do 32 | let(:params) do 33 | { 34 | manage_user: false, 35 | manage_group: false, 36 | manage_datadirs: false 37 | } 38 | end 39 | 40 | it { is_expected.not_to contain_user('jenkins') } 41 | it { is_expected.not_to contain_group('jenkins') } 42 | it { is_expected.not_to contain_file('/var/lib/jenkins') } 43 | it { is_expected.not_to contain_file('/var/lib/jenkins/jobs') } 44 | it { is_expected.not_to contain_file('/var/lib/jenkins/plugins') } 45 | end 46 | 47 | context 'custom home' do 48 | let(:params) do 49 | { 50 | localstatedir: '/custom/jenkins' 51 | } 52 | end 53 | 54 | it { is_expected.to contain_user('jenkins').with_home('/custom/jenkins') } 55 | it { is_expected.to contain_file('/custom/jenkins') } 56 | it { is_expected.to contain_file('/custom/jenkins/plugins') } 57 | it { is_expected.to contain_file('/custom/jenkins/jobs') } 58 | end 59 | end 60 | end 61 | end 62 | end 63 | -------------------------------------------------------------------------------- /spec/classes/jenkins_users_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | 10 | context 'users' do 11 | context 'default' do 12 | it { is_expected.to contain_class('jenkins::users') } 13 | end 14 | 15 | context 'with testuser' do 16 | let(:params) do 17 | { user_hash: { 'user' => { 18 | 'email' => 'user@example.com', 19 | 'password' => 'test' 20 | } } } 21 | end 22 | 23 | it { is_expected.to contain_jenkins__user('user').with_email('user@example.com').with_password('test') } 24 | end 25 | end 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/defines/jenkins_cli_exec_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins::cli::exec' do 6 | let(:title) { 'foo' } 7 | 8 | on_supported_os.each do |os, os_facts| 9 | context "on #{os}" do 10 | let(:facts) { os_facts } 11 | let(:helper_cmd) { '/bin/cat /usr/share/java/puppet_helper.groovy | /usr/bin/java -jar /usr/share/java/jenkins-cli.jar -s http://127.0.0.1:8080 groovy =' } 12 | 13 | describe 'relationships' do 14 | it do 15 | is_expected.to contain_jenkins__cli__exec('foo'). 16 | that_requires('Class[jenkins::cli_helper]') 17 | end 18 | 19 | it do 20 | is_expected.to contain_jenkins__cli__exec('foo'). 21 | that_comes_before('Anchor[jenkins::end]') 22 | end 23 | end 24 | 25 | describe 'title =>' do 26 | context 'foo' do 27 | # default title... 28 | 29 | it do 30 | is_expected.to contain_exec('foo').with( 31 | command: "#{helper_cmd} foo", 32 | tries: 10, 33 | try_sleep: 10, 34 | unless: nil 35 | ) 36 | end 37 | 38 | it { is_expected.to contain_exec('foo').that_notifies('Class[jenkins::cli::reload]') } 39 | end 40 | 41 | context 'bar' do 42 | let(:title) { 'bar' } 43 | 44 | it do 45 | is_expected.to contain_exec('bar').with( 46 | command: "#{helper_cmd} bar", 47 | tries: 10, 48 | try_sleep: 10, 49 | unless: nil 50 | ) 51 | end 52 | 53 | it { is_expected.to contain_exec('bar').that_notifies('Class[jenkins::cli::reload]') } 54 | end 55 | end 56 | 57 | describe 'command =>' do 58 | context 'bar' do 59 | let(:params) { { command: 'bar' } } 60 | 61 | it do 62 | is_expected.to contain_exec('foo').with( 63 | command: "#{helper_cmd} bar", 64 | tries: 10, 65 | try_sleep: 10, 66 | unless: nil 67 | ) 68 | end 69 | end 70 | 71 | context "['bar']" do 72 | let(:params) { { command: %w[bar] } } 73 | 74 | it do 75 | is_expected.to contain_exec('foo').with( 76 | command: "#{helper_cmd} bar", 77 | tries: 10, 78 | try_sleep: 10, 79 | unless: nil 80 | ) 81 | end 82 | end 83 | 84 | context "['bar', 'baz']" do 85 | let(:params) { { command: %w[bar baz] } } 86 | 87 | it do 88 | is_expected.to contain_exec('foo').with( 89 | command: "#{helper_cmd} bar baz", 90 | tries: 10, 91 | try_sleep: 10, 92 | unless: nil 93 | ) 94 | end 95 | end 96 | end 97 | 98 | describe 'unless =>' do 99 | context 'bar' do 100 | let(:params) { { unless: 'bar' } } 101 | 102 | it do 103 | is_expected.to contain_exec('foo').with( 104 | command: "#{helper_cmd} foo", 105 | environment: ["HELPER_CMD=eval #{helper_cmd}"], 106 | unless: 'bar', 107 | tries: 10, 108 | try_sleep: 10 109 | ) 110 | end 111 | end 112 | end 113 | end 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /spec/defines/jenkins_credentials_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins::credentials' do 6 | let(:title) { 'foo' } 7 | let(:helper_cmd) { '/usr/bin/java -jar cli.jar -s http://127.0.0.1:8080 groovy /var/lib/jenkins/puppet_helper.groovy' } 8 | let(:pre_condition) do 9 | "class jenkins::cli_helper { $helper_cmd = '#{helper_cmd}' }" 10 | end 11 | 12 | on_supported_os.each do |os, os_facts| 13 | context "on #{os}" do 14 | let(:facts) { os_facts } 15 | 16 | describe 'relationships' do 17 | let(:params) { { password: 'foo' } } 18 | 19 | it do 20 | is_expected.to contain_jenkins__credentials('foo'). 21 | that_requires('Class[jenkins::cli_helper]') 22 | end 23 | 24 | it do 25 | is_expected.to contain_jenkins__credentials('foo'). 26 | that_comes_before('Anchor[jenkins::end]') 27 | end 28 | end 29 | 30 | describe 'with ensure is present' do 31 | let(:params) do 32 | { 33 | ensure: 'present', 34 | password: 'mypass' 35 | } 36 | end 37 | 38 | it { 39 | is_expected.to contain_jenkins__cli__exec('create-jenkins-credentials-foo').with(command: ['create_or_update_credentials', title.to_s, "'mypass'", 40 | "''", "'Managed by Puppet'", "''"], 41 | unless: "for i in $(seq 1 10); do $HELPER_CMD credential_info #{title} && break || sleep 10; done | grep #{title}") 42 | } 43 | end 44 | 45 | describe 'with ensure is absent' do 46 | let(:params) do 47 | { 48 | ensure: 'absent', 49 | password: 'mypass' 50 | } 51 | end 52 | 53 | it { is_expected.to contain_jenkins__cli__exec('delete-jenkins-credentials-foo').with(command: ['delete_credentials', title.to_s]) } 54 | end 55 | 56 | describe 'with uuid set' do 57 | let(:params) do 58 | { 59 | ensure: 'present', 60 | password: 'mypass', 61 | uuid: 'e94d3b98-5ba4-43b9-89ed-79a08ea97f6f' 62 | } 63 | end 64 | 65 | it { 66 | is_expected.to contain_jenkins__cli__exec('create-jenkins-credentials-foo').with(command: ['create_or_update_credentials', title.to_s, "'mypass'", 67 | "'e94d3b98-5ba4-43b9-89ed-79a08ea97f6f'", "'Managed by Puppet'", "''"], 68 | unless: "for i in $(seq 1 10); do $HELPER_CMD credential_info #{title} && break || sleep 10; done | grep #{title}") 69 | } 70 | end 71 | end 72 | end 73 | end 74 | -------------------------------------------------------------------------------- /spec/defines/jenkins_job_present_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins::job::present' do 6 | let(:title) { 'myjob' } 7 | let(:pre_condition) do 8 | "class { 'jenkins': cli => true, }" 9 | end 10 | 11 | on_supported_os.each do |os, os_facts| 12 | context "on #{os}" do 13 | let(:facts) { os_facts } 14 | 15 | it { is_expected.to compile.and_raise_error(%r{Please set one of}) } 16 | 17 | describe 'with both_config_and_config_file_set' do 18 | quotes = "" 19 | let(:params) { { config: quotes, config_file: quotes } } 20 | 21 | it { is_expected.to compile.and_raise_error(%r{You cannot set both}) } 22 | end 23 | 24 | describe 'with config_file set' do 25 | let(:config_file) { File.expand_path(File.join(__dir__, '..', 'fixtures', 'testjob.xml')) } 26 | let(:params) { { config_file: config_file } } 27 | 28 | it { is_expected.to contain_exec('jenkins create-job myjob') } 29 | it { is_expected.to contain_exec('jenkins update-job myjob') } 30 | it { is_expected.not_to contain_exec('jenkins delete-job myjob') } 31 | end 32 | end 33 | end 34 | end 35 | -------------------------------------------------------------------------------- /spec/defines/jenkins_user_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins::user', type: :define do 6 | let(:title) { 'foo' } 7 | 8 | on_supported_os.each do |os, os_facts| 9 | context "on #{os}" do 10 | let(:facts) { os_facts } 11 | 12 | describe 'relationships' do 13 | let(:params) { { email: 'foo@example.org', password: 'foo' } } 14 | 15 | it do 16 | is_expected.to contain_jenkins__user('foo'). 17 | that_requires('Class[jenkins::cli_helper]') 18 | end 19 | 20 | it do 21 | is_expected.to contain_jenkins__user('foo'). 22 | that_comes_before('Anchor[jenkins::end]') 23 | end 24 | end 25 | end 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/fixtures/testjob.xml: -------------------------------------------------------------------------------- 1 | sourcedconfig 2 | -------------------------------------------------------------------------------- /spec/functions/jenkins_port_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins_port' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | 10 | context 'with default parameters' do 11 | let(:pre_condition) do 12 | 'include jenkins' 13 | end 14 | 15 | it 'defaults to 8080' do 16 | is_expected.to run.with_params.and_return(8080) 17 | end 18 | end 19 | 20 | context 'with overwritten configuration' do 21 | let(:pre_condition) do 22 | <<-ENDPUPPET 23 | class { 'jenkins': 24 | config_hash => {'JENKINS_PORT' => {'value' => '1337'}}, 25 | } 26 | ENDPUPPET 27 | end 28 | 29 | it 'is our overwritten port' do 30 | is_expected.to run.with_params.and_return('1337') 31 | end 32 | end 33 | end 34 | end 35 | end 36 | -------------------------------------------------------------------------------- /spec/functions/jenkins_prefix_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins_prefix' do 6 | on_supported_os.each do |os, os_facts| 7 | context "on #{os}" do 8 | let(:facts) { os_facts } 9 | 10 | context 'with default parameters' do 11 | let(:pre_condition) do 12 | 'include jenkins' 13 | end 14 | 15 | it { is_expected.to run.with_params.and_return('') } 16 | end 17 | 18 | context 'with overwritten configuration' do 19 | let(:pre_condition) do 20 | <<-ENDPUPPET 21 | class { 'jenkins': 22 | config_hash => {'PREFIX' => {'value' => '/test'}}, 23 | } 24 | ENDPUPPET 25 | end 26 | 27 | it { is_expected.to run.with_params.and_return('/test') } 28 | end 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /spec/setup_acceptance_node.pp: -------------------------------------------------------------------------------- 1 | # Facter < 4 needs lsb-release for os.distro.codename 2 | if versioncmp($facts['facterversion'], '4.0.0') < 0 and $facts['os']['family'] == 'Debian' { 3 | package { 'lsb-release': 4 | ensure => 'installed', 5 | } 6 | } 7 | 8 | # jenkins::job::present needs diff 9 | if $facts['os']['family'] == 'RedHat' { 10 | package { 'diffutils': 11 | ensure => present, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | # Managed by modulesync - DO NOT EDIT 4 | # https://voxpupuli.org/docs/updating-files-managed-with-modulesync/ 5 | 6 | # puppetlabs_spec_helper will set up coverage if the env variable is set. 7 | # We want to do this if lib exists and it hasn't been explicitly set. 8 | ENV['COVERAGE'] ||= 'yes' if Dir.exist?(File.expand_path('../lib', __dir__)) 9 | 10 | require 'voxpupuli/test/spec_helper' 11 | 12 | RSpec.configure do |c| 13 | c.facterdb_string_keys = false 14 | end 15 | 16 | add_mocked_facts! 17 | 18 | if File.exist?(File.join(__dir__, 'default_module_facts.yml')) 19 | facts = YAML.safe_load(File.read(File.join(__dir__, 'default_module_facts.yml'))) 20 | facts&.each do |name, value| 21 | add_custom_fact name.to_sym, value 22 | end 23 | end 24 | Dir['./spec/support/spec/**/*.rb'].sort.each { |f| require f } 25 | -------------------------------------------------------------------------------- /spec/spec_helper_acceptance.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'voxpupuli/acceptance/spec_helper_acceptance' 4 | 5 | configure_beaker 6 | 7 | def apply(pp, options = {}) 8 | options[:debug] = true if ENV.key?('PUPPET_DEBUG') 9 | 10 | apply_manifest(pp, options) 11 | end 12 | -------------------------------------------------------------------------------- /spec/unit/facter/plugins_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | describe 'jenkins_plugins', type: :fact do 6 | subject { Facter.value(:jenkins_plugins) } 7 | 8 | let(:plugins) { {} } 9 | 10 | before do 11 | Facter.clear 12 | Facter.loadfacts 13 | allow(Puppet::Jenkins::Plugins).to receive(:available).and_return(plugins) 14 | 15 | overridden_kernel = kernel 16 | Facter.add(:kernel, weight: 9999) do 17 | setcode { overridden_kernel } 18 | end 19 | end 20 | 21 | after { Facter.clear } 22 | 23 | context 'on Linux' do 24 | let(:kernel) { 'Linux' } 25 | 26 | after { expect(Puppet::Jenkins::Plugins).to have_received(:available) } 27 | 28 | context 'with no plugins' do 29 | it { is_expected.to be_instance_of String } 30 | it { is_expected.to be_empty } 31 | end 32 | 33 | context 'with one plugin' do 34 | let(:plugins) do 35 | { 36 | 'greenballs' => { plugin_version: '1.1', description: 'rspec' } 37 | } 38 | end 39 | 40 | it { is_expected.to be_instance_of String } 41 | it { is_expected.to eql 'greenballs 1.1' } 42 | end 43 | 44 | context 'with multiple plugins' do 45 | let(:plugins) do 46 | { 47 | 'greenballs' => { plugin_version: '1.1', description: 'rspec' }, 48 | 'git' => { plugin_version: '1.7', description: 'rspec' } 49 | } 50 | end 51 | 52 | it { is_expected.to be_instance_of String } 53 | it { is_expected.to eql 'git 1.7, greenballs 1.1' } 54 | end 55 | end 56 | 57 | context 'on FreeBSD' do 58 | let(:kernel) { 'FreeBSD' } 59 | 60 | after { expect(Puppet::Jenkins::Plugins).not_to have_received(:available) } 61 | 62 | it { is_expected.to be_nil } 63 | end 64 | end 65 | -------------------------------------------------------------------------------- /spec/unit/jenkins_plugins_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'puppet/jenkins/plugins' 5 | 6 | describe Puppet::Jenkins::Plugins do 7 | let(:git_plugin) do 8 | { 'buildDate' => 'Jan 08, 2014', 9 | 'dependencies' => 10 | [{ 'name' => 'promoted-builds', 'optional' => true, 'version' => '2.7' }, 11 | { 'name' => 'token-macro', 'optional' => true, 'version' => '1.5.1' }, 12 | { 'name' => 'ssh-credentials', 'optional' => false, 'version' => '1.5.1' }, 13 | { 'name' => 'scm-api', 'optional' => false, 'version' => '0.1' }, 14 | { 'name' => 'credentials', 'optional' => false, 'version' => '1.9.3' }, 15 | { 'name' => 'multiple-scms', 'optional' => true, 'version' => '0.2' }, 16 | { 'name' => 'parameterized-trigger', 'optional' => true, 'version' => '2.4' }, 17 | { 'name' => 'git-client', 'optional' => false, 'version' => '1.6.0' }], 18 | 'developers' => 19 | [{ 'developerId' => 'kohsuke', 'name' => 'Kohsuke Kawaguchi' }, 20 | { 'developerId' => 'ndeloof', 21 | 'email' => 'nicolas.deloof@gmail.com', 22 | 'name' => 'Nicolas De Loof' }], 23 | 'excerpt' => 24 | "This plugin allows use of Git as a build SCM. A recent Git runtime is required (1.7.9 minimum, 1.8.x recommended). Plugin is only tested on official git client. Use exotic installations at your own risks.", 25 | 'gav' => 'org.jenkins-ci.plugins:git:2.0.1', 26 | 'labels' => ['scm'], 27 | 'name' => 'git', 28 | 'previousTimestamp' => '2013-10-22T22:00:16.00Z', 29 | 'previousVersion' => '2.0', 30 | 'releaseTimestamp' => '2014-01-08T21:46:20.00Z', 31 | 'requiredCore' => '1.480', 32 | 'scm' => 'github.com', 33 | 'sha1' => 'r5bK/IP8soP08D55Xpcx5yWHzdY=', 34 | 'title' => 'Git Plugin', 35 | 'url' => 'http://updates.jenkins-ci.org/download/plugins/git/2.0.1/git.hpi', 36 | 'version' => '2.0.1', 37 | 'wiki' => 'https://wiki.jenkins-ci.org/display/JENKINS/Git+Plugin' } 38 | end 39 | 40 | describe '.exists?' do 41 | subject(:exists) { described_class.exists? } 42 | 43 | context 'if jenkins does not exist' do 44 | before do 45 | allow(Puppet::Jenkins).to receive(:home_dir).and_return(nil) 46 | end 47 | 48 | it { is_expected.to be false } 49 | end 50 | 51 | context 'if jenkins exists' do 52 | let(:home) { '/var/lib/jenkins' } 53 | let(:dir_exists) { false } 54 | 55 | before do 56 | allow(Puppet::Jenkins).to receive(:home_dir).and_return(home) 57 | expect(File).to receive(:directory?).with(File.join(home, 'plugins')).and_return(dir_exists) 58 | end 59 | 60 | context 'and the directory exists' do 61 | let(:dir_exists) { true } 62 | 63 | it { is_expected.to be true } 64 | end 65 | 66 | context 'and the directory does not exist' do 67 | it { is_expected.to be false } 68 | end 69 | end 70 | end 71 | 72 | describe '.available' do 73 | subject(:available) { described_class.available } 74 | 75 | context 'when plugins do not exist' do 76 | before do 77 | expect(described_class).to receive(:exists?).and_return(false) 78 | end 79 | 80 | it { is_expected.to be_empty } 81 | it { is_expected.to be_instance_of Hash } 82 | end 83 | 84 | context 'when plugins exist' do 85 | it 'generates a list of plugins' do 86 | pending 'This is too hard to unit test, feh.' 87 | raise 88 | end 89 | end 90 | end 91 | 92 | describe 'manifest_data' do 93 | subject(:data) { described_class.manifest_data(data_str) } 94 | 95 | context 'with a plugin version that is hyphenated' do 96 | let(:data_str) do 97 | ' 98 | Plugin-Version: 1.7.2-1 99 | Jenkins-Version: 1.456 100 | ' 101 | end 102 | 103 | it 'has the properly hyphenated plugin version' do 104 | expect(data[:plugin_version]).to eql('1.7.2-1') 105 | end 106 | end 107 | 108 | context 'with "standard" looking manifest data' do 109 | let(:data_str) do 110 | ' 111 | Manifest-Version: 1.0 112 | Archiver-Version: Plexus Archiver 113 | Created-By: Apache Maven 114 | Built-By: jglick 115 | Build-Jdk: 1.7.0_11 116 | Extension-Name: ant 117 | Implementation-Title: ant 118 | Implementation-Version: 1.2 119 | Group-Id: org.jenkins-ci.plugins 120 | Short-Name: ant 121 | Long-Name: Ant Plugin 122 | Url: http://wiki.jenkins-ci.org/display/JENKINS/Ant+Plugin 123 | Plugin-Version: 1.2 124 | Hudson-Version: 1.456 125 | Jenkins-Version: 1.456 126 | Plugin-Developers: 127 | 128 | ' 129 | end 130 | 131 | it { is_expected.to be_instance_of Hash } 132 | 133 | it 'parses the right plugin version' do 134 | expect(data[:plugin_version]).to eql('1.2') 135 | end 136 | end 137 | 138 | context 'with a more complex manifest' do 139 | let(:data_str) do 140 | ' 141 | Manifest-Version: 1.0 142 | Archiver-Version: Plexus Archiver 143 | Created-By: Apache Maven 144 | Built-By: nicolas 145 | Build-Jdk: 1.7.0_45 146 | Extension-Name: git 147 | Specification-Title: Integrates Jenkins with GIT SCM 148 | Implementation-Title: git 149 | Implementation-Version: 2.0.1 150 | Group-Id: org.jenkins-ci.plugins 151 | Short-Name: git 152 | Long-Name: Jenkins GIT plugin 153 | Url: http://wiki.jenkins-ci.org/display/JENKINS/Git+Plugin 154 | Plugin-Version: 2.0.1 155 | Hudson-Version: 1.480 156 | Jenkins-Version: 1.480 157 | Plugin-Dependencies: promoted-builds:2.7;resolution:=optional,token-ma 158 | cro:1.5.1;resolution:=optional,ssh-credentials:1.5.1,scm-api:0.1,cred 159 | entials:1.9.3,multiple-scms:0.2;resolution:=optional,parameterized-tr 160 | igger:2.4;resolution:=optional,git-client:1.6.0 161 | Plugin-Developers: Kohsuke Kawaguchi:kohsuke:,Nicolas De Loof:ndeloof: 162 | nicolas.deloof@gmail.com 163 | 164 | ' 165 | end 166 | 167 | it { is_expected.to be_instance_of Hash } 168 | 169 | it 'has the right number of keys' do 170 | expect(data.keys.size).to be(18) 171 | end 172 | end 173 | end 174 | end 175 | -------------------------------------------------------------------------------- /spec/unit/jenkins_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'puppet/jenkins' 5 | 6 | describe Puppet::Jenkins do 7 | describe '.home_dir' do 8 | subject(:home_dir) { described_class.home_dir } 9 | 10 | context "when a jenkins user doesn't exist" do 11 | before do 12 | expect(File).to receive(:expand_path).and_raise(ArgumentError) 13 | end 14 | 15 | it { is_expected.to be_nil } 16 | end 17 | 18 | context 'when a jenkins user does exist' do 19 | let(:home) { '/rspec/jenkins' } 20 | 21 | before do 22 | expect(File).to receive(:expand_path).and_return(home) 23 | end 24 | 25 | it { is_expected.to eql home } 26 | end 27 | end 28 | end 29 | -------------------------------------------------------------------------------- /spec/unit/puppet/provider/jenkins_authorization_strategy/cli_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'json' 5 | 6 | describe Puppet::Type.type(:jenkins_authorization_strategy).provider(:cli) do 7 | let(:strategy_oauth_json) do 8 | <<-EOS 9 | { 10 | "setAuthorizationStrategy": { 11 | "org.jenkinsci.plugins.GithubAuthorizationStrategy": [ 12 | "jhoblitt, dne", 13 | false, 14 | false, 15 | false, 16 | "lsst, sqre-test", 17 | false, 18 | false, 19 | false 20 | ] 21 | } 22 | } 23 | EOS 24 | end 25 | let(:strategy_oauth) { JSON.parse(strategy_oauth_json) } 26 | 27 | let(:strategy_unsecured_json) do 28 | <<-EOS 29 | { 30 | "setAuthorizationStrategy": { 31 | "hudson.security.AuthorizationStrategy$Unsecured": [ 32 | 33 | ] 34 | } 35 | } 36 | EOS 37 | end 38 | let(:strategy_unsecured) { JSON.parse(strategy_unsecured_json) } 39 | 40 | shared_examples 'a provider from example strategy' do 41 | it do 42 | method_name = 'setAuthorizationStrategy' 43 | class_name = info[method_name].keys.first 44 | ctor_args = info[method_name][class_name] 45 | 46 | expect(provider.name).to eq class_name 47 | expect(provider.ensure).to eq :present 48 | expect(provider.arguments).to eq ctor_args 49 | end 50 | end 51 | 52 | describe '::instances' do 53 | context 'without any params' do 54 | before do 55 | expect(described_class).to receive(:get_authorization_strategy). 56 | with(nil) { strategy_oauth } 57 | end 58 | 59 | it 'returns the correct number of instances' do 60 | expect(described_class.instances.size).to eq 1 61 | end 62 | 63 | context 'first instance returned' do 64 | it_behaves_like 'a provider from example strategy' do 65 | let(:info) { strategy_oauth } 66 | let(:provider) { described_class.instances[0] } 67 | end 68 | end 69 | end 70 | 71 | context 'when called with a catalog param' do 72 | it 'passes it on ::get_authorization_strategy' do 73 | catalog = Puppet::Resource::Catalog.new 74 | 75 | expect(described_class).to receive(:get_authorization_strategy). 76 | with(catalog) { strategy_oauth } 77 | 78 | described_class.instances(catalog) 79 | end 80 | end 81 | end 82 | 83 | describe '#flush' do 84 | it 'calls set_jenkins_instance' do 85 | provider = described_class.new 86 | provider.create 87 | 88 | expect(provider).to receive(:set_jenkins_instance) 89 | provider.flush 90 | end 91 | 92 | it 'calls set_strategy_unsecured' do 93 | provider = described_class.new 94 | provider.destroy 95 | 96 | expect(provider).to receive(:set_strategy_unsecured) 97 | provider.flush 98 | end 99 | 100 | it 'calls set_strategy_unsecured' do 101 | provider = described_class.new 102 | 103 | expect(provider).to receive(:set_strategy_unsecured) 104 | provider.flush 105 | end 106 | end 107 | 108 | # 109 | # private methods 110 | # 111 | 112 | describe '::from_hash' do 113 | it_behaves_like 'a provider from example strategy' do 114 | let(:info) { strategy_oauth } 115 | let(:provider) { described_class.send(:from_hash, info) } 116 | end 117 | 118 | it_behaves_like 'a provider from example strategy' do 119 | let(:info) { strategy_unsecured } 120 | let(:provider) { described_class.send(:from_hash, info) } 121 | end 122 | end 123 | 124 | describe '::to_hash' do 125 | # not isolated from ::from_hash in the interests of staying DRY 126 | it do 127 | provider = described_class.send :from_hash, strategy_oauth 128 | info = provider.send :to_hash 129 | 130 | expect(info).to eq strategy_oauth 131 | end 132 | end 133 | 134 | describe '::get_authorization_strategy' do 135 | it do 136 | expect(described_class).to receive(:clihelper).with( 137 | ['get_authorization_strategy'], 138 | catalog: nil 139 | ) { strategy_oauth_json } 140 | 141 | raw = described_class.send :get_authorization_strategy 142 | expect(raw).to eq strategy_oauth 143 | end 144 | end 145 | 146 | describe '#set_jenkins_instance' do 147 | it do 148 | provider = described_class.send :from_hash, strategy_oauth 149 | 150 | expect(described_class).to receive(:clihelper).with( 151 | ['set_jenkins_instance'], 152 | stdinjson: strategy_oauth 153 | ) 154 | 155 | provider.send :set_jenkins_instance 156 | end 157 | end 158 | 159 | describe '#set_strategy_unsecured' do 160 | it do 161 | provider = described_class.new(name: 'test') 162 | 163 | expect(described_class).to receive(:clihelper).with( 164 | ['set_jenkins_instance'], 165 | stdinjson: strategy_unsecured 166 | ) 167 | 168 | provider.send :set_strategy_unsecured 169 | end 170 | end 171 | end 172 | -------------------------------------------------------------------------------- /spec/unit/puppet/provider/jenkins_num_executors/cli_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'json' 5 | 6 | describe Puppet::Type.type(:jenkins_num_executors).provider(:cli) do 7 | describe '::instances' do 8 | context 'without any params' do 9 | before do 10 | expect(described_class).to receive(:get_num_executors). 11 | with(nil).and_return(42) 12 | end 13 | 14 | it 'returns the correct number of instances' do 15 | expect(described_class.instances.size).to eq 1 16 | end 17 | 18 | context 'first instance returned' do 19 | let(:provider) { described_class.instances[0] } 20 | 21 | it { expect(provider.name).to eq 42 } 22 | end 23 | end 24 | 25 | context 'when called with a catalog param' do 26 | it 'passes it on ::get_num_executors' do 27 | catalog = Puppet::Resource::Catalog.new 28 | 29 | expect(described_class).to receive(:get_num_executors). 30 | with(catalog).and_return(42) 31 | 32 | described_class.instances(catalog) 33 | end 34 | end 35 | end 36 | 37 | describe '#flush' do 38 | it 'calls set_num_executors' do 39 | provider = described_class.new 40 | provider.create 41 | 42 | expect(provider).to receive(:set_num_executors).with(no_args) 43 | provider.flush 44 | end 45 | 46 | it 'fails' do 47 | provider = described_class.new 48 | provider.destroy 49 | 50 | expect { provider.flush }. 51 | to raise_error(Puppet::Error, %r{invalid :ensure value: absent}) 52 | end 53 | end 54 | 55 | # 56 | # private methods 57 | # 58 | 59 | describe '::get_num_executors' do 60 | it do 61 | expect(described_class).to receive(:clihelper). 62 | with(['get_num_executors'], catalog: nil).and_return(42) 63 | 64 | n = described_class.send :get_num_executors 65 | expect(n).to eq 42 66 | end 67 | end 68 | 69 | describe '#set_jenkins_instance' do 70 | it do 71 | provider = described_class.new(name: 42) 72 | 73 | expect(described_class).to receive(:clihelper).with(['set_num_executors', 42]) 74 | 75 | provider.send :set_num_executors 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /spec/unit/puppet/provider/jenkins_security_realm/cli_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'json' 5 | 6 | describe Puppet::Type.type(:jenkins_security_realm).provider(:cli) do 7 | let(:realm_oauth_json) do 8 | <<-EOS 9 | { 10 | "setSecurityRealm": { 11 | "org.jenkinsci.plugins.GithubSecurityRealm": [ 12 | "https://github.com", 13 | "https://api.github.com", 14 | "42", 15 | "43", 16 | "read:org" 17 | ] 18 | } 19 | } 20 | EOS 21 | end 22 | let(:realm_oauth) { JSON.parse(realm_oauth_json) } 23 | 24 | let(:realm_none_json) do 25 | <<-EOS 26 | { 27 | "setSecurityRealm": { 28 | "hudson.security.SecurityRealm$None": [ 29 | 30 | ] 31 | } 32 | } 33 | EOS 34 | end 35 | let(:realm_none) { JSON.parse(realm_none_json) } 36 | 37 | shared_examples 'a provider from example realm' do 38 | it do 39 | method_name = 'setSecurityRealm' 40 | class_name = info[method_name].keys.first 41 | ctor_args = info[method_name][class_name] 42 | 43 | expect(provider.name).to eq class_name 44 | expect(provider.ensure).to eq :present 45 | expect(provider.arguments).to eq ctor_args 46 | end 47 | end 48 | 49 | describe '::instances' do 50 | context 'without any params' do 51 | before do 52 | expect(described_class).to receive(:get_security_realm). 53 | with(nil) { realm_oauth } 54 | end 55 | 56 | it 'returns the correct number of instances' do 57 | expect(described_class.instances.size).to eq 1 58 | end 59 | 60 | context 'first instance returned' do 61 | it_behaves_like 'a provider from example realm' do 62 | let(:info) { realm_oauth } 63 | let(:provider) { described_class.instances[0] } 64 | end 65 | end 66 | end 67 | 68 | context 'when called with a catalog param' do 69 | it 'passes it on ::get_security_realm' do 70 | catalog = Puppet::Resource::Catalog.new 71 | 72 | expect(described_class).to receive(:get_security_realm). 73 | with(catalog) { realm_oauth } 74 | 75 | described_class.instances(catalog) 76 | end 77 | end 78 | end 79 | 80 | describe '#flush' do 81 | it 'calls set_jenkins_instance' do 82 | provider = described_class.new 83 | provider.create 84 | 85 | expect(provider).to receive(:set_jenkins_instance) 86 | provider.flush 87 | end 88 | 89 | it 'calls set_security_none' do 90 | provider = described_class.new 91 | provider.destroy 92 | 93 | expect(provider).to receive(:set_security_none) 94 | provider.flush 95 | end 96 | 97 | it 'calls set_security_none' do 98 | provider = described_class.new 99 | 100 | expect(provider).to receive(:set_security_none) 101 | provider.flush 102 | end 103 | end 104 | 105 | # 106 | # private methods 107 | # 108 | 109 | describe '::from_hash' do 110 | it_behaves_like 'a provider from example realm' do 111 | let(:info) { realm_oauth } 112 | let(:provider) { described_class.send(:from_hash, info) } 113 | end 114 | 115 | it_behaves_like 'a provider from example realm' do 116 | let(:info) { realm_none } 117 | let(:provider) { described_class.send(:from_hash, info) } 118 | end 119 | end 120 | 121 | describe '::to_hash' do 122 | # not isolated from ::from_hash in the interests of staying DRY 123 | it do 124 | provider = described_class.send :from_hash, realm_oauth 125 | info = provider.send :to_hash 126 | 127 | expect(info).to eq realm_oauth 128 | end 129 | end 130 | 131 | describe '::get_security_realm' do 132 | # not isolated from ::from_hash in the interests of staying DRY 133 | it do 134 | expect(described_class).to receive(:clihelper).with( 135 | ['get_security_realm'], 136 | catalog: nil 137 | ) { realm_oauth_json } 138 | 139 | raw = described_class.send :get_security_realm 140 | expect(raw).to eq realm_oauth 141 | end 142 | end 143 | 144 | describe '#set_jenkins_instance' do 145 | it do 146 | provider = described_class.send :from_hash, realm_oauth 147 | 148 | expect(described_class).to receive(:clihelper).with( 149 | ['set_jenkins_instance'], 150 | stdinjson: realm_oauth 151 | ) 152 | 153 | provider.send :set_jenkins_instance 154 | end 155 | end 156 | 157 | describe '#set_security_none' do 158 | it do 159 | provider = described_class.new(name: 'test') 160 | 161 | expect(described_class).to receive(:clihelper).with( 162 | ['set_jenkins_instance'], 163 | stdinjson: realm_none 164 | ) 165 | 166 | provider.send :set_security_none 167 | end 168 | end 169 | end 170 | -------------------------------------------------------------------------------- /spec/unit/puppet/provider/jenkins_slaveagent_port/cli_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'json' 5 | 6 | describe Puppet::Type.type(:jenkins_slaveagent_port).provider(:cli) do 7 | describe '::instances' do 8 | context 'without any params' do 9 | before do 10 | expect(described_class).to receive(:get_slaveagent_port). 11 | with(nil).and_return(42) 12 | end 13 | 14 | it 'returns the correct number of instances' do 15 | expect(described_class.instances.size).to eq 1 16 | end 17 | 18 | context 'first instance returned' do 19 | let(:provider) { described_class.instances[0] } 20 | 21 | it { expect(provider.name).to eq 42 } 22 | end 23 | end 24 | 25 | context 'when called with a catalog param' do 26 | it 'passes it on ::get_slaveagent_port' do 27 | catalog = Puppet::Resource::Catalog.new 28 | 29 | expect(described_class).to receive(:get_slaveagent_port). 30 | with(catalog).and_return(42) 31 | 32 | described_class.instances(catalog) 33 | end 34 | end 35 | end 36 | 37 | describe '#flush' do 38 | it 'calls set_slaveagent_port' do 39 | provider = described_class.new 40 | provider.create 41 | 42 | expect(provider).to receive(:set_slaveagent_port).with(no_args) 43 | provider.flush 44 | end 45 | 46 | it 'fails' do 47 | provider = described_class.new 48 | provider.destroy 49 | 50 | expect { provider.flush }. 51 | to raise_error(Puppet::Error, %r{invalid :ensure value: absent}) 52 | end 53 | end 54 | 55 | # 56 | # private methods 57 | # 58 | 59 | describe '::get_slaveagent_port' do 60 | it do 61 | expect(described_class).to receive(:clihelper). 62 | with(['get_slaveagent_port'], catalog: nil).and_return(42) 63 | 64 | n = described_class.send :get_slaveagent_port 65 | expect(n).to eq 42 66 | end 67 | end 68 | 69 | describe '#set_jenkins_instance' do 70 | it do 71 | provider = described_class.new(name: 42) 72 | 73 | expect(described_class).to receive(:clihelper).with(['set_slaveagent_port', 42]) 74 | 75 | provider.send :set_slaveagent_port 76 | end 77 | end 78 | end 79 | -------------------------------------------------------------------------------- /spec/unit/puppet/provider/jenkins_user/cli_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'unit/puppet/x/spec_jenkins_providers' 5 | 6 | require 'json' 7 | 8 | describe Puppet::Type.type(:jenkins_user).provider(:cli) do 9 | let(:user_info_json) do 10 | <<-EOS 11 | [ 12 | { 13 | 14 | "id": "test", 15 | "full_name": "test", 16 | "email_address": "foo@foo.org", 17 | "public_keys": ["ssh-rsa foo com", "ssh-rsa bar com"], 18 | "api_token_public": "b0da1e0bf3f79ff02624c2f716913808", 19 | "api_token_plain": "51a8b1dd95bc76b1a2869356c043e8b9", 20 | "password": "#jbcrypt:$2a$10$dg5kqB/bNVgotE0alN.V5OQJ1BajkmM2ZOFAmtlSt29bB4xEDZOja" 21 | }, 22 | { 23 | "id": "guest" 24 | } 25 | ] 26 | EOS 27 | end 28 | let(:user_info) { JSON.parse(user_info_json) } 29 | let(:mutable_user_info) do 30 | # we should not be trying to flush the api_token_public value as it is 31 | # immutable 32 | info = user_info[0] 33 | info.delete('api_token_public') 34 | info 35 | end 36 | 37 | shared_examples 'a provider from example hash 1' do 38 | it do 39 | expect(provider.name).to eq user_info[0]['id'] 40 | expect(provider.ensure).to eq :present 41 | expect(provider.full_name).to eq user_info[0]['full_name'] 42 | expect(provider.email_address).to eq user_info[0]['email_address'] 43 | expect(provider.public_keys).to eq user_info[0]['public_keys'] 44 | expect(provider.api_token_public).to eq user_info[0]['api_token_public'] 45 | expect(provider.api_token_plain).to eq user_info[0]['api_token_plain'] 46 | expect(provider.password).to eq user_info[0]['password'] 47 | end 48 | end 49 | 50 | shared_examples 'a provider from example hash 2' do 51 | it do 52 | expect(provider.name).to eq user_info[1]['id'] 53 | expect(provider.ensure).to eq :present 54 | expect(provider.full_name).to eq :absent 55 | expect(provider.email_address).to eq :absent 56 | expect(provider.public_keys).to eq :absent 57 | expect(provider.api_token_public).to eq :absent 58 | expect(provider.api_token_plain).to eq :absent 59 | expect(provider.password).to eq :absent 60 | end 61 | end 62 | 63 | include_examples 'confines to cli dependencies' 64 | 65 | describe '::instances' do 66 | context 'without any params' do 67 | before do 68 | expect(described_class).to receive(:user_info_all). 69 | with(nil) { user_info } 70 | end 71 | 72 | it 'returns the correct number of instances' do 73 | expect(described_class.instances.size).to eq 2 74 | end 75 | 76 | context 'first instance returned' do 77 | it_behaves_like 'a provider from example hash 1' do 78 | let(:provider) do 79 | described_class.instances[0] 80 | end 81 | end 82 | end 83 | 84 | context 'second instance returned' do 85 | it_behaves_like 'a provider from example hash 2' do 86 | let(:provider) do 87 | described_class.instances[1] 88 | end 89 | end 90 | end 91 | end 92 | 93 | context 'when called with a catalog param' do 94 | it 'passes it on ::user_info_all' do 95 | catalog = Puppet::Resource::Catalog.new 96 | 97 | expect(described_class).to receive(:user_info_all). 98 | with(catalog) { user_info } 99 | 100 | described_class.instances(catalog) 101 | end 102 | end 103 | end 104 | 105 | describe '#api_token_public=' do 106 | it 'is read only (fail)' do 107 | provider = described_class.new 108 | 109 | expect { provider.api_token_public = 'foo' }.to raise_error(Puppet::Error, %r{api_token_public is read-only}) 110 | end 111 | end 112 | 113 | describe '#flush' do 114 | it 'calls user_update' do 115 | provider = described_class.new 116 | provider.create 117 | 118 | expect(provider).to receive(:user_update) 119 | provider.flush 120 | end 121 | 122 | it 'calls delete_user' do 123 | provider = described_class.new 124 | provider.destroy 125 | 126 | expect(provider).to receive(:delete_user) 127 | provider.flush 128 | end 129 | 130 | it 'calls delete_user' do 131 | provider = described_class.new 132 | 133 | expect(provider).to receive(:delete_user) 134 | provider.flush 135 | end 136 | end 137 | 138 | # 139 | # private methods 140 | # 141 | 142 | describe '::from_hash' do 143 | it_behaves_like 'a provider from example hash 1' do 144 | let(:provider) do 145 | described_class.send :from_hash, user_info[0] 146 | end 147 | end 148 | 149 | it_behaves_like 'a provider from example hash 2' do 150 | let(:provider) do 151 | described_class.send :from_hash, user_info[1] 152 | end 153 | end 154 | end 155 | 156 | describe '::to_hash' do 157 | # not isolated from ::from_hash in the interests of staying DRY 158 | it do 159 | provider = described_class.send :from_hash, user_info[0] 160 | info = provider.send :to_hash 161 | 162 | expect(info).to eq mutable_user_info 163 | end 164 | end 165 | 166 | describe '::user_info_all' do 167 | # not isolated from ::from_hash in the interests of staying DRY 168 | it do 169 | expect(described_class).to receive(:clihelper).with(['user_info_all']) { user_info_json } 170 | 171 | raw = described_class.send :user_info_all 172 | expect(raw).to eq user_info 173 | end 174 | end 175 | 176 | describe '#user_update' do 177 | RSpec::Matchers.define :a_json_doc do |x| 178 | match { |actual| JSON.parse(actual) == x } 179 | end 180 | 181 | it do 182 | provider = described_class.send :from_hash, user_info[0] 183 | 184 | expect(described_class).to receive(:clihelper).with( 185 | ['user_update'], 186 | stdinjson: mutable_user_info 187 | ) 188 | 189 | provider.send :user_update 190 | end 191 | end 192 | 193 | describe '#delete_user' do 194 | it do 195 | provider = described_class.send :from_hash, user_info[0] 196 | 197 | expect(described_class).to receive(:clihelper).with( 198 | %w[delete_user test] 199 | ) 200 | 201 | provider.send :delete_user 202 | end 203 | end 204 | end 205 | -------------------------------------------------------------------------------- /spec/unit/puppet/type/jenkins_authorization_strategy_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'unit/puppet/x/spec_jenkins_types' 5 | 6 | describe Puppet::Type.type(:jenkins_authorization_strategy) do 7 | before { Facter.clear } 8 | 9 | describe 'parameters' do 10 | describe 'name' do 11 | it_behaves_like 'generic namevar', :name 12 | end 13 | end 14 | 15 | describe 'properties' do 16 | describe 'ensure' do 17 | it_behaves_like 'generic ensurable' 18 | end 19 | 20 | describe 'arguments' do 21 | it_behaves_like 'array_matching property' 22 | end 23 | end 24 | 25 | describe 'autorequire' do 26 | it_behaves_like 'autorequires cli resources' 27 | it_behaves_like 'autorequires all jenkins_user resources' 28 | it_behaves_like 'autorequires jenkins_security_realm resource' 29 | end 30 | end 31 | -------------------------------------------------------------------------------- /spec/unit/puppet/type/jenkins_credentials_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'unit/puppet/x/spec_jenkins_types' 5 | 6 | describe Puppet::Type.type(:jenkins_credentials) do 7 | before { Facter.clear } 8 | 9 | describe 'parameters' do 10 | describe 'name' do 11 | it_behaves_like 'generic namevar', :name 12 | end 13 | end 14 | 15 | describe 'properties' do 16 | describe 'ensure' do 17 | it_behaves_like 'generic ensurable' 18 | end 19 | 20 | describe 'domain' do 21 | it_behaves_like 'validated property', :domain, :undef, [:undef] 22 | end 23 | 24 | describe 'scope' do 25 | it_behaves_like 'validated property', :scope, :GLOBAL, %i[GLOBAL SYSTEM] 26 | end 27 | 28 | describe 'impl' do 29 | it_behaves_like 'validated property', :impl, 30 | :UsernamePasswordCredentialsImpl, 31 | %i[ 32 | UsernamePasswordCredentialsImpl 33 | BasicSSHUserPrivateKey 34 | StringCredentialsImpl 35 | FileCredentialsImpl 36 | AWSCredentialsImpl 37 | GitLabApiTokenImpl 38 | BrowserStackCredentials 39 | ] 40 | end 41 | 42 | # unvalidated properties 43 | %i[ 44 | description 45 | username 46 | password 47 | private_key 48 | passphrase 49 | secret 50 | file_name 51 | content 52 | source 53 | key_store_impl 54 | secret_key 55 | access_key 56 | api_token 57 | ].each do |property| 58 | describe property.to_s do 59 | context 'attrtype' do 60 | it { expect(described_class.attrtype(property)).to eq :property } 61 | end 62 | end 63 | end 64 | end 65 | 66 | describe 'autorequire' do 67 | it_behaves_like 'autorequires cli resources' 68 | it_behaves_like 'autorequires all jenkins_user resources' 69 | it_behaves_like 'autorequires jenkins_security_realm resource' 70 | it_behaves_like 'autorequires jenkins_authorization_strategy resource' 71 | end 72 | end 73 | -------------------------------------------------------------------------------- /spec/unit/puppet/type/jenkins_job_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'unit/puppet/x/spec_jenkins_types' 5 | 6 | describe Puppet::Type.type(:jenkins_job) do 7 | before { Facter.clear } 8 | 9 | describe 'parameters' do 10 | describe 'name' do 11 | it_behaves_like 'generic namevar', :name 12 | end 13 | 14 | describe 'show_diff' do 15 | it_behaves_like 'boolean parameter', :show_diff, true 16 | end 17 | end 18 | 19 | describe 'properties' do 20 | describe 'ensure' do 21 | it_behaves_like 'generic ensurable' 22 | end 23 | 24 | describe 'enable' do 25 | it_behaves_like 'boolean property', :enable, true 26 | end 27 | 28 | describe 'replace' do 29 | it_behaves_like 'boolean parameter', :replace, true 30 | end 31 | 32 | describe 'config' do 33 | let(:resource) { described_class.new(name: 'foo', config: 'bar') } 34 | let(:property) { resource.property(:config) } 35 | 36 | it { expect(described_class.attrtype(:config)).to eq :property } 37 | 38 | [true, false].product([true, false]).each do |cfg, param| 39 | describe "and Puppet[:show_diff] is #{cfg} and show_diff => #{param}" do 40 | before do 41 | Puppet[:show_diff] = cfg 42 | resource[:show_diff] = param 43 | resource[:loglevel] = 'debug' 44 | end 45 | 46 | if cfg && param 47 | it 'displays a diff' do 48 | expect(property).to receive(:diff).once.and_return('foo') 49 | property.change_to_s('foo', 'bar') 50 | end 51 | else 52 | it 'does not display a diff' do 53 | expect(property).not_to receive :diff 54 | property.change_to_s('foo', 'bar') 55 | end 56 | end 57 | end 58 | end 59 | 60 | describe 'change_to_s change string' do 61 | context 'created' do 62 | it { expect(property.change_to_s(:absent, nil)).to eq 'created' } 63 | end 64 | 65 | context 'removed' do 66 | it { expect(property.change_to_s(nil, :absent)).to eq 'removed' } 67 | end 68 | 69 | context 'changed with replace' do 70 | it do 71 | expect(property.change_to_s('foo', 'bar')). 72 | to match(%r{content changed '{md5}\w+' to '{md5}\w+'}) 73 | end 74 | end 75 | 76 | context 'changed without replace' do 77 | let(:resource) { described_class.new(name: 'foo', config: 'bar', replace: false) } 78 | 79 | it { expect(property.change_to_s('foo', 'bar')).to eq 'left unchanged' } 80 | end 81 | end 82 | end 83 | end 84 | 85 | describe 'autorequire' do 86 | it_behaves_like 'autorequires cli resources' 87 | it_behaves_like 'autorequires all jenkins_user resources' 88 | it_behaves_like 'autorequires jenkins_security_realm resource' 89 | it_behaves_like 'autorequires jenkins_authorization_strategy resource' 90 | 91 | describe 'folders' do 92 | it 'autorequires parent folder resource' do 93 | folder = described_class.new( 94 | name: 'foo' 95 | ) 96 | 97 | job = described_class.new( 98 | name: 'foo/bar' 99 | ) 100 | 101 | folder[:ensure] = :present 102 | job[:ensure] = :present 103 | 104 | catalog = Puppet::Resource::Catalog.new 105 | catalog.add_resource folder 106 | catalog.add_resource job 107 | req = job.autorequire 108 | 109 | expect(req.size).to eq 1 110 | expect(req[0].source).to eq folder 111 | expect(req[0].target).to eq job 112 | end 113 | 114 | it 'autorequires multiple nested parent folder resources' do 115 | folder1 = described_class.new( 116 | name: 'foo' 117 | ) 118 | 119 | folder2 = described_class.new( 120 | name: 'foo/bar' 121 | ) 122 | 123 | job = described_class.new( 124 | name: 'foo/bar/baz' 125 | ) 126 | 127 | folder1[:ensure] = :present 128 | folder2[:ensure] = :present 129 | job[:ensure] = :present 130 | 131 | catalog = Puppet::Resource::Catalog.new 132 | catalog.add_resource folder1 133 | catalog.add_resource folder2 134 | catalog.add_resource job 135 | req = job.autorequire 136 | 137 | expect(req.size).to eq 2 138 | expect(req[0].source).to eq folder1 139 | expect(req[0].target).to eq job 140 | expect(req[1].source).to eq folder2 141 | expect(req[1].target).to eq job 142 | end 143 | 144 | it 'autobefores multiple nested parent folder resources', 145 | unless: Puppet.version.to_f < 4.0 do 146 | folder1 = described_class.new( 147 | name: 'foo' 148 | ) 149 | 150 | folder2 = described_class.new( 151 | name: 'foo/bar' 152 | ) 153 | 154 | job = described_class.new( 155 | name: 'foo/bar/baz' 156 | ) 157 | 158 | folder1[:ensure] = :absent 159 | folder2[:ensure] = :absent 160 | job[:ensure] = :absent 161 | 162 | catalog = Puppet::Resource::Catalog.new 163 | catalog.add_resource folder1 164 | catalog.add_resource folder2 165 | catalog.add_resource job 166 | req = job.autobefore 167 | 168 | expect(req.size).to eq 2 169 | expect(req[0].source).to eq job 170 | expect(req[0].target).to eq folder1 171 | expect(req[1].source).to eq job 172 | expect(req[1].target).to eq folder2 173 | end 174 | end 175 | end 176 | end 177 | -------------------------------------------------------------------------------- /spec/unit/puppet/type/jenkins_num_executors_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'unit/puppet/x/spec_jenkins_types' 5 | 6 | describe Puppet::Type.type(:jenkins_num_executors) do 7 | before { Facter.clear } 8 | 9 | describe 'parameters' do 10 | describe 'name' do 11 | it_behaves_like 'generic namevar', :name 12 | end 13 | end 14 | 15 | describe 'properties' do 16 | describe 'ensure' do 17 | it_behaves_like 'generic ensurable', :present 18 | end 19 | end 20 | 21 | describe 'autorequire' do 22 | it_behaves_like 'autorequires cli resources' 23 | it_behaves_like 'autorequires all jenkins_user resources' 24 | it_behaves_like 'autorequires jenkins_security_realm resource' 25 | it_behaves_like 'autorequires jenkins_authorization_strategy resource' 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/unit/puppet/type/jenkins_security_realm_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'unit/puppet/x/spec_jenkins_types' 5 | 6 | describe Puppet::Type.type(:jenkins_security_realm) do 7 | before { Facter.clear } 8 | 9 | describe 'parameters' do 10 | describe 'name' do 11 | it_behaves_like 'generic namevar', :name 12 | end 13 | end 14 | 15 | describe 'properties' do 16 | describe 'ensure' do 17 | it_behaves_like 'generic ensurable' 18 | end 19 | 20 | describe 'arguments' do 21 | it_behaves_like 'array_matching property' 22 | end 23 | end 24 | 25 | describe 'autorequire' do 26 | it_behaves_like 'autorequires cli resources' 27 | it_behaves_like 'autorequires all jenkins_user resources' 28 | end 29 | end 30 | -------------------------------------------------------------------------------- /spec/unit/puppet/type/jenkins_slaveagent_port_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'unit/puppet/x/spec_jenkins_types' 5 | 6 | describe Puppet::Type.type(:jenkins_slaveagent_port) do 7 | before { Facter.clear } 8 | 9 | describe 'parameters' do 10 | describe 'name' do 11 | it_behaves_like 'generic namevar', :name 12 | end 13 | end 14 | 15 | describe 'properties' do 16 | describe 'ensure' do 17 | it_behaves_like 'generic ensurable', :present 18 | end 19 | end 20 | 21 | describe 'autorequire' do 22 | it_behaves_like 'autorequires cli resources' 23 | it_behaves_like 'autorequires all jenkins_user resources' 24 | it_behaves_like 'autorequires jenkins_security_realm resource' 25 | it_behaves_like 'autorequires jenkins_authorization_strategy resource' 26 | end 27 | end 28 | -------------------------------------------------------------------------------- /spec/unit/puppet/type/jenkins_user_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'unit/puppet/x/spec_jenkins_types' 5 | 6 | describe Puppet::Type.type(:jenkins_user) do 7 | before { Facter.clear } 8 | 9 | describe 'parameters' do 10 | describe 'name' do 11 | it_behaves_like 'generic namevar', :name 12 | end 13 | end 14 | 15 | describe 'properties' do 16 | describe 'ensure' do 17 | it_behaves_like 'generic ensurable' 18 | end 19 | 20 | # unvalidated properties 21 | %i[full_name email_address 22 | api_token_public password].each do |property| 23 | describe property.to_s do 24 | it { expect(described_class.attrtype(property)).to eq :property } 25 | end 26 | end 27 | 28 | describe 'api_token_plain' do 29 | it { expect(described_class.attrtype(:api_token_plain)).to eq :property } 30 | 31 | it 'supports valid hexstrings' do 32 | value = '51a8b1dd95bc76b1a2869356c043e8b9' 33 | expect do 34 | described_class.new( 35 | name: 'nobody', 36 | api_token_plain: value 37 | ) 38 | end. 39 | not_to raise_error 40 | end 41 | 42 | %w[ 51a8b1dd95bc76b1a2869356c043e8b 43 | 51a8b1dd95bc76b1a2869356c043e8b99 ].each do |value| 44 | it 'rejects hexstrings of invalid length' do 45 | expect do 46 | described_class.new( 47 | name: 'nobody', 48 | api_token_plain: value 49 | ) 50 | end. 51 | to raise_error(Puppet::ResourceError, %r{is not a 32char hex string}) 52 | end 53 | end 54 | end 55 | 56 | describe 'public_keys' do 57 | it { expect(described_class.attrtype(:public_keys)).to eq :property } 58 | 59 | it 'supports single string' do 60 | value = 'ssh-rsa blah comment' 61 | user = described_class.new(name: 'nobody', public_keys: value) 62 | expect(user[:public_keys]).to eq [value] 63 | end 64 | 65 | it 'supports array of string' do 66 | value = ['ssh-rsa blah comment', 'ssh-rsa foo comment'] 67 | user = described_class.new(name: 'nobody', public_keys: value) 68 | expect(user[:public_keys]).to eq value 69 | end 70 | end 71 | end 72 | 73 | describe 'autorequire' do 74 | it_behaves_like 'autorequires cli resources' 75 | end 76 | end 77 | -------------------------------------------------------------------------------- /spec/unit/puppet/x/jenkins/config_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | require 'puppet/x/jenkins/config' 6 | 7 | describe Puppet::X::Jenkins::Config do 8 | DEFAULTS = { 9 | cli_jar: '/usr/share/java/jenkins-cli.jar', 10 | url: 'http://localhost:8080', 11 | ssh_private_key: nil, 12 | puppet_helper: '/usr/share/java/puppet_helper.groovy', 13 | cli_tries: 30 14 | }.freeze 15 | 16 | shared_context 'facts' do 17 | before do 18 | Facter.add(:jenkins_cli_jar) { setcode { 'fact.jar' } } 19 | Facter.add(:jenkins_url) { setcode { 'http://localhost:11' } } 20 | Facter.add(:jenkins_ssh_private_key) { setcode { 'fact.id_rsa' } } 21 | Facter.add(:jenkins_puppet_helper) { setcode { 'fact.groovy' } } 22 | Facter.add(:jenkins_cli_tries) { setcode { 22 } } 23 | end 24 | end 25 | 26 | shared_examples 'returns default values' do |_param| 27 | it 'returns default values' do 28 | DEFAULTS.each do |k, v| 29 | expect(config[k]).to eq v 30 | end 31 | end 32 | end 33 | 34 | shared_examples 'returns fact values' do |_param| 35 | it 'returns fact values' do 36 | DEFAULTS.each_key do |k| 37 | expect(config[k]).to eq Facter.value("jenkins_#{k}".to_sym) 38 | end 39 | end 40 | end 41 | 42 | shared_examples 'returns catalog values' do |_param| 43 | it 'returns catalog values' do 44 | config = catalog.resource(:class, 'jenkins::cli::config') 45 | 46 | DEFAULTS.each_key do |k| 47 | expect(config[k]).not_to be_nil 48 | end 49 | end 50 | end 51 | 52 | before { Facter.clear } 53 | 54 | # we are relying on a side effect of this method being to test features / 55 | # load libs 56 | describe '#initialize' do 57 | it { expect(described_class.new).to be_a described_class } 58 | end 59 | 60 | describe '#[]' do 61 | context 'unknown config key' do 62 | it do 63 | expect { described_class.new[:foo] }. 64 | to raise_error(Puppet::X::Jenkins::Config::UnknownConfig) 65 | end 66 | end 67 | 68 | context 'no catalog' do 69 | let(:config) { described_class.new } 70 | 71 | context 'no facts' do 72 | include_examples 'returns default values' 73 | end 74 | 75 | context 'with facts' do 76 | include_examples 'returns fact values' do 77 | include_context 'facts' 78 | end 79 | end 80 | end 81 | 82 | context 'with catalog' do 83 | let(:catalog) { Puppet::Resource::Catalog.new } 84 | let(:config) { described_class.new(catalog) } 85 | 86 | context 'no jenkins::cli::config class' do 87 | context 'no facts' do 88 | include_examples 'returns default values' 89 | end 90 | 91 | context 'with facts' do 92 | include_examples 'returns fact values' do 93 | include_context 'facts' 94 | end 95 | end 96 | end 97 | 98 | context 'with jenkins::cli::config class' do 99 | context 'with no params' do 100 | before do 101 | jenkins = Puppet::Type.type(:component).new( 102 | name: 'jenkins::cli::config' 103 | ) 104 | 105 | catalog.add_resource jenkins 106 | end 107 | 108 | context 'no facts' do 109 | include_examples 'returns default values' 110 | end 111 | 112 | context 'with facts' do 113 | include_examples 'returns fact values' do 114 | include_context 'facts' 115 | end 116 | end 117 | end 118 | 119 | context 'with all params' do 120 | before do 121 | jenkins = Puppet::Type.type(:component).new( 122 | name: 'jenkins::cli::config', 123 | cli_jar: 'cat.jar', 124 | url: 'http://localhost:111', 125 | ssh_private_key: 'cat.id_rsa', 126 | puppet_helper: 'cat.groovy', 127 | cli_tries: 222 128 | ) 129 | 130 | catalog.add_resource jenkins 131 | end 132 | 133 | context 'no facts' do 134 | include_examples 'returns catalog values' 135 | end 136 | 137 | context 'with facts' do 138 | include_examples 'returns catalog values' do 139 | include_context 'facts' 140 | end 141 | end 142 | end 143 | end 144 | end 145 | end 146 | end 147 | -------------------------------------------------------------------------------- /spec/unit/puppet/x/jenkins/type/cli_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | require 'unit/puppet/x/spec_jenkins_types' 5 | 6 | require 'puppet/x/jenkins/type/cli' 7 | 8 | Puppet::X::Jenkins::Type::Cli.newtype(:test) do 9 | newparam(:foo) { isnamevar } 10 | end 11 | 12 | describe Puppet::Type.type(:test) do 13 | describe 'autorequire' do 14 | it_behaves_like 'autorequires cli resources' 15 | end 16 | end 17 | -------------------------------------------------------------------------------- /spec/unit/puppet/x/jenkins/util_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | require 'puppet/x/jenkins/util' 6 | 7 | describe Puppet::X::Jenkins::Util do 8 | let(:data) do 9 | { 10 | a: :undef, 11 | b: [ 12 | nil, 13 | 1, 14 | { 15 | c: nil, 16 | d: [ 17 | :undef, 18 | 1 19 | ], 20 | e: 1 21 | } 22 | ], 23 | f: 1 24 | } 25 | end 26 | 27 | describe '::unundef' do 28 | it 'converts :undef values to nil' do 29 | expect(described_class.unundef(data)).to eq(a: nil, 30 | b: [ 31 | nil, 32 | 1, 33 | { 34 | c: nil, 35 | d: [ 36 | nil, 37 | 1 38 | ], 39 | e: 1 40 | } 41 | ], 42 | f: 1) 43 | end 44 | end 45 | 46 | describe '::undefize' do 47 | it 'converts nil values to :undef' do 48 | expect(described_class.undefize(data)).to eq(a: :undef, 49 | b: [ 50 | :undef, 51 | 1, 52 | { 53 | c: :undef, 54 | d: [ 55 | :undef, 56 | 1 57 | ], 58 | e: 1 59 | } 60 | ], 61 | f: 1) 62 | end 63 | end 64 | 65 | describe '::iterate' do 66 | it 'does not transform without block' do 67 | expect(described_class.iterate(data)).to eq(data) 68 | end 69 | 70 | it 'does not affect hash keys' do 71 | expect(described_class.iterate(data) { 5 }).to eq(a: 5, 72 | b: [ 73 | 5, 74 | 5, 75 | { 76 | c: 5, 77 | d: [ 78 | 5, 79 | 5 80 | ], 81 | e: 5 82 | } 83 | ], 84 | f: 5) 85 | end 86 | end 87 | end 88 | -------------------------------------------------------------------------------- /spec/unit/puppet/x/spec_jenkins_providers.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper' 4 | 5 | shared_examples 'confines to cli dependencies' do 6 | describe 'confine' do 7 | let(:confines) do 8 | described_class.confine_collection.instance_variable_get(:@confines) 9 | end 10 | 11 | context 'commands :java' do 12 | it do 13 | expect(confines).to include( 14 | be_a(Puppet::Confine::Exists). 15 | and(have_attributes(values: ['java'])) 16 | ) 17 | end 18 | end 19 | end 20 | 21 | describe 'commands' do 22 | before do 23 | allow(described_class).to receive(:command).with(:java).and_return('java') 24 | end 25 | 26 | context 'java' do 27 | it { expect(described_class.command(:java)).to eq('java') } 28 | it { expect(described_class).to respond_to(:java) } 29 | end 30 | end 31 | end 32 | -------------------------------------------------------------------------------- /templates/jenkins-override.epp: -------------------------------------------------------------------------------- 1 | <%- | Hash[String, Struct[{value => Any}]] $environment, 2 | Hash[String[1], String] $dropin_config = {} 3 | | -%> 4 | [Service] 5 | <% $environment.each |$key, $entry| { -%> 6 | Environment="<%= $key %>=<%= $entry['value'] %>" 7 | <% } -%> 8 | <% $dropin_config.each |$key, $value| { -%> 9 | <%= $key %>=<%= $value %> 10 | <% } -%> 11 | -------------------------------------------------------------------------------- /templates/jenkins-slave-defaults.erb: -------------------------------------------------------------------------------- 1 | # defaults for jenkins slave swarm clients 2 | 3 | # XXX due to the use of conditional statements and variable interpolation, 4 | # this file can not be used in its current state as a systemd EnvironmentFile. 5 | 6 | # location of java 7 | JAVA=<%= @java_cmd %> 8 | 9 | # arguments to pass to java 10 | #JAVA_ARGS="-Xmx256m" 11 | #JAVA_ARGS="-Djava.net.preferIPv4Stack=true" # make jenkins listen on IPv4 address 12 | <% if @java_args.any? -%> 13 | JAVA_ARGS="<%= @java_args.join(' ') -%>" 14 | <% end -%> 15 | 16 | # user id to be invoked as (otherwise will run as root; not wise!) 17 | JENKINS_SLAVE_USER=<%= @slave_user -%> 18 | 19 | # location of the jenkins war file 20 | JENKINS_SLAVE_JAR="<%= @slave_home -%>/<%= @client_jar -%>" 21 | 22 | # log location. this may be a syslog facility.priority 23 | JENKINS_SLAVE_LOG=/var/log/jenkins-slave/jenkins-slave.log 24 | 25 | # slave mode, can be either 'normal' (utilize this slave as much as possible) 26 | # or 'exclusive' (leave this machine for tied jobs only). 27 | JENKINS_SLAVE_MODE=<%= @slave_mode -%> 28 | 29 | # OS LIMITS SETUP 30 | # comment this out to observe /etc/security/limits.conf 31 | # this is on by default because http://github.com/jenkinsci/jenkins/commit/2fb288474e980d0e7ff9c4a3b768874835a3e92e 32 | # reported that Ubuntu's PAM configuration doesn't include pam_limits.so, and as a result the # of file 33 | # descriptors are forced to 1024 regardless of /etc/security/limits.conf 34 | MAXOPENFILES=8192 35 | 36 | MASTER_URL="<%= @masterurl -%>" 37 | AUTO_DISCOVERY_ADDRESS="<%= @autodiscoveryaddress -%>" 38 | LABELS="<%= @labels.join(' ') -%>" 39 | 40 | EXECUTORS=<%= @executors -%> 41 | 42 | CLIENT_NAME="<%= @slave_name -%>" 43 | 44 | FSROOT="<%= @slave_home -%>" 45 | 46 | DESCRIPTION="<%= @description -%>" 47 | 48 | TUNNEL="<%= @tunnel -%>" 49 | 50 | # credentials should be single quoted 51 | JENKINS_USERNAME=<%= @quoted_ui_user %> 52 | JENKINS_PASSWORD=<%= @quoted_ui_pass %> 53 | 54 | OTHER_ARGS="<%= @swarm_client_args.join(' ') %>" 55 | 56 | # boolean; any value but 'true' is considered false 57 | DISABLE_CLIENTS_UNIQUE_ID="<%= 'true' if @disable_clients_unique_id -%>" 58 | DISABLE_SSL_VERIFICATION="<%= 'true' if @disable_ssl_verification -%>" 59 | DELETE_EXISTING_CLIENTS="<%= 'true' if @delete_existing_clients -%>" 60 | 61 | # "= [= ...]" 62 | TOOL_LOCATIONS="<%= @_real_tool_locations %>" 63 | -------------------------------------------------------------------------------- /templates/jenkins-slave-run.erb: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | set -e 4 | 5 | fail() { 6 | echo -e "$@" 7 | exit 1 8 | } 9 | 10 | # mandatory input vars 11 | [[ -x "$JAVA" ]] || (fail "$JAVA is not executable") 12 | [[ -f "$JENKINS_SLAVE_JAR" ]] || (fail "$JENKINS_SLAVE_JAR not accessible") 13 | 14 | SLAVE_ARGS=() 15 | 16 | [[ -n "$JAVA_ARGS" ]] && 17 | SLAVE_ARGS+=("$JAVA_ARGS") 18 | 19 | SLAVE_ARGS+=(-jar "$JENKINS_SLAVE_JAR") 20 | 21 | [[ -n "$JENKINS_SLAVE_MODE" ]] && 22 | SLAVE_ARGS+=(-mode "$JENKINS_SLAVE_MODE") 23 | 24 | [[ -n "$EXECUTORS" ]] && 25 | SLAVE_ARGS+=(-executors "$EXECUTORS") 26 | 27 | [[ -n "$JENKINS_USERNAME" ]] && 28 | SLAVE_ARGS+=(-username "$JENKINS_USERNAME") 29 | 30 | [[ -n "$JENKINS_PASSWORD" ]] && 31 | SLAVE_ARGS+=(-passwordEnvVariable JENKINS_PASSWORD) 32 | 33 | [[ -n "$CLIENT_NAME" ]] && 34 | SLAVE_ARGS+=(-name "$CLIENT_NAME") 35 | 36 | [[ -n "$MASTER_URL" ]] && 37 | SLAVE_ARGS+=(-master "$MASTER_URL") 38 | 39 | [[ -n "$LABELS" ]] && 40 | SLAVE_ARGS+=(-labels "$LABELS") 41 | 42 | [[ -n "$FSROOT" ]] && 43 | SLAVE_ARGS+=(-fsroot "$FSROOT") 44 | 45 | [[ "$DISABLE_CLIENTS_UNIQUE_ID" == true ]] && 46 | SLAVE_ARGS+=(-disableClientsUniqueId) 47 | 48 | [[ "$DISABLE_SSL_VERIFICATION" == true ]] && 49 | SLAVE_ARGS+=(-disableSslVerification) 50 | 51 | [[ "$DELETE_EXISTING_CLIENTS" == true ]] && 52 | SLAVE_ARGS+=(-deleteExistingClients) 53 | 54 | [[ -n "$DESCRIPTION" ]] && 55 | SLAVE_ARGS+=(-description "$DESCRIPTION") 56 | 57 | [[ -n "$TUNNEL" ]] && 58 | SLAVE_ARGS+=(-tunnel "$TUNNEL") 59 | 60 | [[ -n "$AUTO_DISCOVERY_ADDRESS" ]] && 61 | SLAVE_ARGS+=(-autoDiscoveryAddress "$AUTO_DISCOVERY_ADDRESS") 62 | 63 | if [ -n "$TOOL_LOCATIONS" ]; then 64 | for t in $TOOL_LOCATIONS; do 65 | SLAVE_ARGS+=(--toolLocation "$t") 66 | done 67 | fi 68 | 69 | [[ -n "$OTHER_ARGS" ]] && 70 | SLAVE_ARGS+=($OTHER_ARGS) 71 | 72 | $JAVA "${SLAVE_ARGS[@]}" 73 | -------------------------------------------------------------------------------- /templates/jenkins-slave.service.erb: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Jenkins slave service 3 | Requires=network.target 4 | After=network.target 5 | 6 | [Service] 7 | Type=simple 8 | User=<%= scope['jenkins::slave::slave_user'] %> 9 | EnvironmentFile=<%= scope['jenkins::slave::defaults_location'] %>/jenkins-slave 10 | ExecStart=<%= scope['jenkins::slave::slave_home'] %>/jenkins-slave-run 11 | Restart=always 12 | RestartSec=60 13 | StartLimitInterval=0 14 | SuccessExitStatus=143 15 | 16 | [Install] 17 | WantedBy=multi-user.target 18 | -------------------------------------------------------------------------------- /templates/org.jenkins-ci.slave.jnlp.plist.epp: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | <% if $jenkins::slave::quoted_ui_pass { -%> 6 | EnvironmentVariables 7 | 8 | JENKINS_PASSWORD 9 | <%= $jenkins::slave::quoted_ui_pass -%> 10 | 11 | <% } -%> 12 | Label 13 | org.jenkins-ci.slave.jnlp 14 | ProgramArguments 15 | 16 | /usr/bin/java 17 | <% unless empty($jenkins::slave::java_args) { -%> 18 | <%= join($jenkins::slave::java_args, ' ') -%> 19 | <% } -%> 20 | -jar 21 | <%= $jenkins::slave::slave_home -%>/<%= $jenkins::slave::client_jar -%> 22 | <% if $jenkins::slave::slave_mode { -%> 23 | -mode 24 | <%= $jenkins::slave::slave_mode -%> 25 | <% } -%> 26 | <% if $jenkins::slave::executors { -%> 27 | -executors 28 | <%= $jenkins::slave::executors -%> 29 | <% } -%> 30 | <% if $jenkins::slave::quoted_ui_user { -%> 31 | -username 32 | <%= $jenkins::slave::quoted_ui_user -%> 33 | <% } -%> 34 | <% if $jenkins::slave::quoted_ui_pass { -%> 35 | -passwordEnvVariable 36 | JENKINS_PASSWORD 37 | <% } -%> 38 | <% if $jenkins::slave::slave_name { -%> 39 | -name 40 | <%= $jenkins::slave::slave_name -%> 41 | <% } -%> 42 | <% if $jenkins::slave::masterurl { -%> 43 | -master 44 | <%= $jenkins::slave::masterurl -%> 45 | <% } -%> 46 | <% if $jenkins::slave::slave_home { -%> 47 | -fsroot 48 | <%= $jenkins::slave::slave_home -%> 49 | <% } -%> 50 | <% if $jenkins::slave::disable_clients_unique_id { -%> 51 | -disableClientsUniqueId 52 | <% } -%> 53 | <% if $jenkins::slave::disable_ssl_verification { -%> 54 | -disableSslVerification 55 | <% } -%> 56 | <% if $jenkins::slave::delete_existing_clients { -%> 57 | -deleteExistingClients 58 | <% } -%> 59 | <% if $jenkins::slave::description { -%> 60 | -description 61 | <%= $jenkins::slave::description -%> 62 | <% } -%> 63 | <% if $jenkins::slave::autodiscoveryaddress { -%> 64 | -autoDiscoveryAddress 65 | <%= $jenkins::slave::autodiscoveryaddress -%> 66 | <% } -%> 67 | <% if $jenkins::slave::_real_tool_locations { -%> 68 | <% $jenkins::slave::_real_tool_locations.each |$location| { -%> 69 | -toolLocation 70 | <%= $location -%> 71 | <% } -%> 72 | <% } -%> 73 | <% unless empty($jenkins::slave::swarm_client_args) { -%> 74 | <%= join($jenkins::slave::swarm_client_args, ' ') -%> 75 | <% } -%> 76 | 77 | KeepAlive 78 | 79 | RunAtLoad 80 | 81 | UserName 82 | <%= $jenkins::slave::slave_user %> 83 | WorkingDirectory 84 | <%= $jenkins::slave::slave_home %> 85 | SessionCreate 86 | 87 | StandardInPath 88 | /dev/null 89 | StandardErrorPath 90 | /var/log/jenkins/org.jenkins-ci.slave.jnlp.log 91 | StandardOutPath 92 | /var/log/jenkins/org.jenkins-ci.slave.jnlp.log 93 | 94 | 95 | -------------------------------------------------------------------------------- /templates/proxy.xml.erb: -------------------------------------------------------------------------------- 1 | 2 | <%= @proxy_host %> 3 | <%= @proxy_port %> 4 | <% if @no_proxy_list -%> 5 | <%= @no_proxy_list.join("\n") %> 6 | <% end -%> 7 | 8 | -------------------------------------------------------------------------------- /types/tunnel.pp: -------------------------------------------------------------------------------- 1 | # A custom data type for a jenkins tunnel verification 2 | type Jenkins::Tunnel = Variant[Pattern[/.+:$/],Pattern[/.+:[0-9]+/], Pattern[/^:[0-9]+/]] 3 | --------------------------------------------------------------------------------