├── documentation └── .gitkeep ├── .mdlrc ├── .gitattributes ├── .github ├── CODEOWNERS ├── lock.yml ├── workflows │ ├── conventional-commits.yml │ ├── prevent-file-change.yml │ ├── release.yml │ ├── copilot-setup-steps.yml │ ├── stale.yml │ └── ci.yml └── copilot-instructions.md ├── .release-please-manifest.json ├── spec ├── spec_helper.rb └── recipes │ └── default_spec.rb ├── .envrc ├── templates └── default │ ├── sv-mms-agent-log-run.erb │ ├── mongodb.conf.erb │ ├── sv-mms-agent-run.erb │ ├── mongodb.sysconfig.erb │ ├── mms_agent_config.erb │ ├── debian-mongodb.upstart.erb │ ├── redhat-mongodb.init.erb │ └── debian-mongodb.init.erb ├── test ├── fixtures │ └── cookbooks │ │ └── mongodb_spec │ │ ├── metadata.rb │ │ └── recipes │ │ ├── user_delete.rb │ │ └── set_hostname.rb ├── integration │ ├── default │ │ └── inspec │ │ │ └── default_spec.rb │ ├── configserver │ │ └── inspec │ │ │ └── default_spec.rb │ ├── replicaset │ │ └── inspec │ │ │ └── replicaset_spec.rb │ ├── mms_agent │ │ └── inspec │ │ │ ├── backup_agent_spec.rb │ │ │ ├── monitoring_agent_spec.rb │ │ │ └── automation_agent_spec.rb │ ├── mongos │ │ └── inspec │ │ │ └── mongos_spec.rb │ ├── nodes │ │ ├── mongodb-replica-001.json │ │ ├── mongodb-configserver-001.json │ │ ├── mongo1.json │ │ ├── mongo2.json │ │ ├── mongo3.json │ │ ├── shard1-n1.json │ │ ├── shard1-n2.json │ │ └── shard1-n3.json │ ├── shard-mongos │ │ └── inspec │ │ │ └── shard_spec.rb │ ├── replicaset3 │ │ └── inspec │ │ │ └── replicaset_spec.rb │ ├── user_management │ │ └── inspec │ │ │ └── user_management_spec.rb │ ├── user_management_v2 │ │ └── inspec │ │ │ └── user_management_spec.rb │ └── user_management_v2_delete │ │ └── inspec │ │ └── user_management_spec.rb └── unit │ ├── config_helper_spec.rb │ ├── mms_backup_agent_spec.rb │ ├── mms_monitoring_agent_spec.rb │ ├── install_spec.rb │ └── default_spec.rb ├── Berksfile ├── CODE_OF_CONDUCT.md ├── kitchen.exec.yml ├── TESTING.md ├── .vscode └── extensions.json ├── .rubocop.yml ├── CONTRIBUTING.md ├── .markdownlint-cli2.yaml ├── .yamllint ├── release-please-config.json ├── .editorconfig ├── attributes ├── ulimit.rb ├── sysconfig.rb ├── users.rb ├── dbconfig.rb ├── mms_agent.rb └── default.rb ├── renovate.json ├── metadata.rb ├── .overcommit.yml ├── .gitignore ├── kitchen.global.yml ├── recipes ├── mms_automation_agent.rb ├── replicaset.rb ├── configserver.rb ├── default.rb ├── mongo_gem.rb ├── shard.rb ├── mms_backup_agent.rb ├── mms_monitoring_agent.rb ├── mongos.rb ├── user_management.rb ├── mongodb_org_repo.rb └── install.rb ├── libraries ├── mongodb_config_helpers.rb ├── user.rb └── mongodb.rb ├── Dangerfile ├── resources ├── user.rb └── agent.rb ├── chefignore ├── kitchen.dokken.yml ├── kitchen.yml ├── LICENSE ├── CHANGELOG.md ├── definitions └── mongodb.rb └── README.md /documentation/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /.mdlrc: -------------------------------------------------------------------------------- 1 | rules "~MD013" 2 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | * text=auto eol=lf 2 | -------------------------------------------------------------------------------- /.github/CODEOWNERS: -------------------------------------------------------------------------------- 1 | * @sous-chefs/maintainers 2 | -------------------------------------------------------------------------------- /.release-please-manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | ".": "5.1.26" 3 | } 4 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'chefspec' 2 | require 'chefspec/berkshelf' 3 | -------------------------------------------------------------------------------- /.envrc: -------------------------------------------------------------------------------- 1 | use chefworkstation 2 | export KITCHEN_GLOBAL_YAML=kitchen.global.yml 3 | -------------------------------------------------------------------------------- /templates/default/sv-mms-agent-log-run.erb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec svlogd -tt <%= @options[:mms_agent_log_dir] %> 4 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/mongodb_spec/metadata.rb: -------------------------------------------------------------------------------- 1 | name 'mongodb_spec' 2 | 3 | depends 'sc-mongodb' 4 | version '0.0.1' 5 | -------------------------------------------------------------------------------- /Berksfile: -------------------------------------------------------------------------------- 1 | source 'https://supermarket.chef.io' 2 | 3 | metadata 4 | 5 | cookbook 'mongodb_spec', path: 'test/fixtures/cookbooks/mongodb_spec' 6 | -------------------------------------------------------------------------------- /templates/default/mongodb.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically Generated by Chef, do not edit directly! 3 | # 4 | 5 | <%= to_yaml_options @config %> 6 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Community Guidelines 2 | 3 | This project follows the Chef Community Guidelines 4 | -------------------------------------------------------------------------------- /kitchen.exec.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: { name: exec } 3 | transport: { name: exec } 4 | 5 | platforms: 6 | - name: macos-latest 7 | - name: windows-latest 8 | -------------------------------------------------------------------------------- /test/integration/default/inspec/default_spec.rb: -------------------------------------------------------------------------------- 1 | describe service('mongod') do 2 | it { should be_installed } 3 | it { should be_enabled } 4 | it { should be_running } 5 | end 6 | -------------------------------------------------------------------------------- /TESTING.md: -------------------------------------------------------------------------------- 1 | # Testing 2 | 3 | Please refer to [the community cookbook documentation on testing](https://github.com/chef-cookbooks/community_cookbook_documentation/blob/main/TESTING.MD). 4 | -------------------------------------------------------------------------------- /test/integration/configserver/inspec/default_spec.rb: -------------------------------------------------------------------------------- 1 | describe service('mongod') do 2 | it { should be_installed } 3 | it { should be_enabled } 4 | it { should be_running } 5 | end 6 | -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "chef-software.chef", 4 | "Shopify.ruby-lsp", 5 | "editorconfig.editorconfig", 6 | "DavidAnson.vscode-markdownlint" 7 | ] 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/mongodb_spec/recipes/user_delete.rb: -------------------------------------------------------------------------------- 1 | mongodb_user '"kitchen" user delete' do 2 | username 'kitchen' 3 | database 'admin' 4 | connection node['mongodb'] 5 | action :delete 6 | end 7 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | AllCops: 3 | Exclude: 4 | - 'Dangerfile' 5 | 6 | # Cookstyle (rubocop) todo: 7 | BlockNesting: 8 | Max: 4 # TODO: reduce to 3 9 | Style/EachWithObject: 10 | Enabled: false 11 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | Please refer to 4 | [https://github.com/chef-cookbooks/community_cookbook_documentation/blob/main/CONTRIBUTING.MD](https://github.com/chef-cookbooks/community_cookbook_documentation/blob/main/CONTRIBUTING.MD) 5 | -------------------------------------------------------------------------------- /templates/default/sv-mms-agent-run.erb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | cd <%= @options[:mms_agent_dir] %> 4 | 5 | exec 2>&1 6 | exec chpst -u <%= @options[:mms_agent_user] %>:<%= @options[:mms_agent_group] %> python <%= @options[:mms_agent_dir] %>/agent.py 2>&1 7 | -------------------------------------------------------------------------------- /.github/lock.yml: -------------------------------------------------------------------------------- 1 | --- 2 | daysUntilLock: 365 3 | exemptLabels: [] 4 | lockLabel: false 5 | lockComment: > 6 | This thread has been automatically locked since there has not been 7 | any recent activity after it was closed. Please open a new issue for 8 | related bugs. 9 | -------------------------------------------------------------------------------- /.markdownlint-cli2.yaml: -------------------------------------------------------------------------------- 1 | config: 2 | ul-indent: false # MD007 3 | line-length: false # MD013 4 | no-duplicate-heading: false # MD024 5 | reference-links-images: false # MD052 6 | no-multiple-blanks: 7 | maximum: 2 8 | ignores: 9 | - .github/copilot-instructions.md 10 | -------------------------------------------------------------------------------- /templates/default/mongodb.sysconfig.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically Generated by Chef, do not edit directly! 3 | # 4 | 5 | <%- @sysconfig.keys.sort.each do |key| %> 6 | <%- unless @sysconfig[key].nil? %> 7 | <%= key %>="<%= @sysconfig[key] %>" 8 | <%- end %> 9 | <%- end %> 10 | -------------------------------------------------------------------------------- /templates/default/mms_agent_config.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically Generated by Chef, do not edit directly! 3 | # 4 | 5 | <%- @config.keys.sort.each do |key| %> 6 | <%- unless @config[key].nil? or key == 'version' %> 7 | <%= key %>=<%= @config[key] %> 8 | <%- end %> 9 | <%- end %> 10 | -------------------------------------------------------------------------------- /.github/workflows/conventional-commits.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: conventional-commits 3 | 4 | "on": 5 | pull_request: 6 | types: 7 | - opened 8 | - reopened 9 | - edited 10 | - synchronize 11 | 12 | jobs: 13 | conventional-commits: 14 | uses: sous-chefs/.github/.github/workflows/conventional-commits.yml@5.0.3 15 | -------------------------------------------------------------------------------- /test/integration/replicaset/inspec/replicaset_spec.rb: -------------------------------------------------------------------------------- 1 | # mongodb | mongod 2 | describe service('mongod') do 3 | it { should be_installed } 4 | it { should be_enabled } 5 | it { should be_running } 6 | end 7 | 8 | # replicaset initialized 9 | describe bash('mongo --eval "db.stats().ok"') do 10 | its('exit_status') { should eq 0 } 11 | end 12 | -------------------------------------------------------------------------------- /.yamllint: -------------------------------------------------------------------------------- 1 | --- 2 | extends: default 3 | rules: 4 | line-length: 5 | max: 256 6 | level: warning 7 | document-start: disable 8 | braces: 9 | forbid: false 10 | min-spaces-inside: 0 11 | max-spaces-inside: 1 12 | min-spaces-inside-empty: -1 13 | max-spaces-inside-empty: -1 14 | comments: 15 | min-spaces-from-content: 1 16 | -------------------------------------------------------------------------------- /release-please-config.json: -------------------------------------------------------------------------------- 1 | { 2 | "packages": { 3 | ".": { 4 | "package-name": "sc-mongodb", 5 | "changelog-path": "CHANGELOG.md", 6 | "release-type": "ruby", 7 | "include-component-in-tag": false, 8 | "version-file": "metadata.rb" 9 | } 10 | }, 11 | "$schema": "https://raw.githubusercontent.com/googleapis/release-please/main/schemas/config.json" 12 | } 13 | -------------------------------------------------------------------------------- /.github/workflows/prevent-file-change.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: prevent-file-change 3 | 4 | "on": 5 | pull_request: 6 | types: 7 | - opened 8 | - reopened 9 | - edited 10 | - synchronize 11 | 12 | jobs: 13 | prevent-file-change: 14 | uses: sous-chefs/.github/.github/workflows/prevent-file-change.yml@5.0.3 15 | secrets: 16 | token: ${{ secrets.GITHUB_TOKEN }} 17 | -------------------------------------------------------------------------------- /test/integration/mms_agent/inspec/backup_agent_spec.rb: -------------------------------------------------------------------------------- 1 | describe service('mongodb-mms-backup-agent') do 2 | it { should be_installed } 3 | it { should be_enabled } 4 | it { should be_running } 5 | end 6 | 7 | describe file('/etc/mongodb-mms/backup-agent.config') do 8 | its('content') { should match(/(mmsApiKey=randomkey)/) } 9 | its('content') { should match(/(mothership=api-backup.mongodb.com)/) } 10 | end 11 | -------------------------------------------------------------------------------- /test/integration/mms_agent/inspec/monitoring_agent_spec.rb: -------------------------------------------------------------------------------- 1 | describe service('mongodb-mms-monitoring-agent') do 2 | it { should be_installed } 3 | it { should be_enabled } 4 | it { should be_running } 5 | end 6 | 7 | describe file('/etc/mongodb-mms/monitoring-agent.config') do 8 | its('content') { should match(/(mmsApiKey=randomkey)/) } 9 | its('content') { should match(%r{(mmsBaseUrl=https://mms.mongodb.com)}) } 10 | end 11 | -------------------------------------------------------------------------------- /.editorconfig: -------------------------------------------------------------------------------- 1 | # https://EditorConfig.org 2 | 3 | # top-most EditorConfig file 4 | root=true 5 | 6 | # Unix-style newlines with a newline ending every file 7 | [*] 8 | end_of_line = lf 9 | insert_final_newline = true 10 | 11 | # 2 space indentation 12 | indent_style = space 13 | indent_size = 2 14 | 15 | # Avoid issues parsing cookbook files later 16 | charset = utf-8 17 | 18 | # Avoid cookstyle warnings 19 | trim_trailing_whitespace = true 20 | -------------------------------------------------------------------------------- /test/fixtures/cookbooks/mongodb_spec/recipes/set_hostname.rb: -------------------------------------------------------------------------------- 1 | ['192.168.10.10 mongo1', '192.168.10.20 mongo2', '192.168.10.30 mongo3', '192.168.10.99 mongos'].each do |host| 2 | execute "set_hostnames for #{host}" do 3 | command "echo #{host} >> /etc/hosts" 4 | end 5 | end 6 | 7 | if platform_family?('rhel') && node['platform_version'].to_i == 7 8 | execute 'fix_network' do 9 | command 'service NetworkManager stop && service network restart' 10 | end 11 | end 12 | -------------------------------------------------------------------------------- /attributes/ulimit.rb: -------------------------------------------------------------------------------- 1 | # http://docs.mongodb.org/manual/reference/ulimit/#recommended-settings 2 | default['mongodb']['ulimit']['fsize'] = 'unlimited' # file_size 3 | default['mongodb']['ulimit']['cpu'] = 'unlimited' # cpu_time 4 | default['mongodb']['ulimit']['as'] = 'unlimited' # virtual memory 5 | default['mongodb']['ulimit']['nofile'] = 64_000 # number_files 6 | default['mongodb']['ulimit']['rss'] = 'unlimited' # memory_size 7 | default['mongodb']['ulimit']['nproc'] = 32_000 # processes 8 | -------------------------------------------------------------------------------- /renovate.json: -------------------------------------------------------------------------------- 1 | { 2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json", 3 | "extends": ["config:base"], 4 | "packageRules": [ 5 | { 6 | "groupName": "Actions", 7 | "matchUpdateTypes": ["minor", "patch", "pin"], 8 | "automerge": true, 9 | "addLabels": ["Release: Patch", "Skip: Announcements"] 10 | }, 11 | { 12 | "groupName": "Actions", 13 | "matchUpdateTypes": ["major"], 14 | "automerge": false, 15 | "addLabels": ["Release: Patch", "Skip: Announcements"] 16 | } 17 | ] 18 | } 19 | -------------------------------------------------------------------------------- /metadata.rb: -------------------------------------------------------------------------------- 1 | name 'sc-mongodb' 2 | maintainer 'Sous Chefs' 3 | maintainer_email 'help@sous-chefs.org' 4 | license 'Apache-2.0' 5 | description 'Installs and configures mongodb' 6 | source_url 'https://github.com/sous-chefs/sc-mongodb' 7 | issues_url 'https://github.com/sous-chefs/sc-mongodb/issues' 8 | chef_version '>= 15.3' 9 | version '5.1.26' 10 | 11 | supports 'amazon' 12 | supports 'centos' 13 | supports 'debian' 14 | supports 'oracle' 15 | supports 'redhat' 16 | supports 'ubuntu' 17 | supports 'rocky' 18 | -------------------------------------------------------------------------------- /test/integration/mms_agent/inspec/automation_agent_spec.rb: -------------------------------------------------------------------------------- 1 | describe service('mongodb-mms-automation-agent') do 2 | it { should be_installed } 3 | it { should be_enabled } 4 | it { should be_running } 5 | end 6 | 7 | describe file('/etc/mongodb-mms/automation-agent.config') do 8 | its('content') { should match(/(mmsApiKey=randomkey)/) } 9 | its('content') { should match(%r{(logFile=/var/log/mongodb-mms-automation/automation-agent.log)}) } 10 | its('content') { should match(%r{(mmsConfigBackup=/var/lib/mongodb-mms-automation/mms-cluster-config-backup.json)}) } 11 | end 12 | -------------------------------------------------------------------------------- /.github/workflows/release.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: release 3 | 4 | "on": 5 | push: 6 | branches: 7 | - main 8 | 9 | permissions: 10 | contents: write 11 | issues: write 12 | pull-requests: write 13 | packages: write 14 | attestations: write 15 | id-token: write 16 | 17 | jobs: 18 | release: 19 | uses: sous-chefs/.github/.github/workflows/release-cookbook.yml@5.0.3 20 | secrets: 21 | token: ${{ secrets.PORTER_GITHUB_TOKEN }} 22 | supermarket_user: ${{ secrets.CHEF_SUPERMARKET_USER }} 23 | supermarket_key: ${{ secrets.CHEF_SUPERMARKET_KEY }} 24 | -------------------------------------------------------------------------------- /.overcommit.yml: -------------------------------------------------------------------------------- 1 | --- 2 | PreCommit: 3 | TrailingWhitespace: 4 | enabled: true 5 | YamlLint: 6 | enabled: true 7 | required_executable: "yamllint" 8 | ChefSpec: 9 | enabled: true 10 | required_executable: "chef" 11 | command: ["chef", "exec", "rspec"] 12 | Cookstyle: 13 | enabled: true 14 | required_executable: "cookstyle" 15 | command: ["cookstyle"] 16 | MarkdownLint: 17 | enabled: false 18 | required_executable: "npx" 19 | command: ["npx", "markdownlint-cli2", "'**/*.md'"] 20 | include: ["**/*.md"] 21 | 22 | CommitMsg: 23 | HardTabs: 24 | enabled: true 25 | -------------------------------------------------------------------------------- /test/unit/config_helper_spec.rb: -------------------------------------------------------------------------------- 1 | require_relative '../../libraries/mongodb_config_helpers' 2 | 3 | describe 'MongoDBConfigHelpers' do 4 | it 'convert to boost::program_options format' do 5 | extend ::MongoDBConfigHelpers 6 | input = { 7 | 'string' => 'foo', 8 | 'boolean' => true, 9 | 'numeric' => 216, 10 | 'absent' => nil, 11 | 'empty-string' => '', 12 | } 13 | actual = to_boost_program_options input 14 | expected = "boolean = true\n" \ 15 | "numeric = 216\n" \ 16 | 'string = foo' 17 | expect(actual).to eq(expected) 18 | end 19 | end 20 | -------------------------------------------------------------------------------- /.github/workflows/copilot-setup-steps.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: 'Copilot Setup Steps' 3 | 4 | "on": 5 | workflow_dispatch: 6 | push: 7 | paths: 8 | - .github/workflows/copilot-setup-steps.yml 9 | pull_request: 10 | paths: 11 | - .github/workflows/copilot-setup-steps.yml 12 | 13 | jobs: 14 | copilot-setup-steps: 15 | runs-on: ubuntu-latest 16 | permissions: 17 | contents: read 18 | steps: 19 | - name: Check out code 20 | uses: actions/checkout@v5 21 | - name: Install Chef 22 | uses: actionshub/chef-install@main 23 | - name: Install cookbooks 24 | run: berks install 25 | -------------------------------------------------------------------------------- /test/integration/mongos/inspec/mongos_spec.rb: -------------------------------------------------------------------------------- 1 | # Mongos service 2 | describe service('mongos') do 3 | it { should be_installed } 4 | it { should be_running } 5 | end 6 | 7 | # SystemD on Debian 8 doesn't detect enabled sysvinit services correctly 8 | unless os[:family] == 'debian' && os[:release] =~ /^8\./ 9 | describe service('mongos') do 10 | it { should be_enabled } 11 | end 12 | end 13 | 14 | # Mongos process 15 | describe port(27017) do 16 | it { should be_listening } 17 | its('protocols') { should eq ['tcp'] } 18 | end 19 | 20 | # Config process 21 | describe port(27019) do 22 | it { should be_listening } 23 | its('protocols') { should eq ['tcp'] } 24 | end 25 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.rbc 2 | .config 3 | InstalledFiles 4 | pkg 5 | test/tmp 6 | test/version_tmp 7 | tmp 8 | _Store 9 | *~ 10 | *# 11 | .#* 12 | \#*# 13 | *.un~ 14 | *.tmp 15 | *.bk 16 | *.bkup 17 | 18 | # editor files 19 | .idea 20 | .*.sw[a-z] 21 | 22 | # ruby/bundler/rspec files 23 | .ruby-version 24 | .ruby-gemset 25 | .rvmrc 26 | Gemfile.lock 27 | .bundle 28 | *.gem 29 | coverage 30 | spec/reports 31 | 32 | # YARD / rdoc artifacts 33 | .yardoc 34 | _yardoc 35 | doc/ 36 | rdoc 37 | 38 | # chef infra stuff 39 | Berksfile.lock 40 | .kitchen 41 | kitchen.local.yml 42 | vendor/ 43 | .coverage/ 44 | .zero-knife.rb 45 | Policyfile.lock.json 46 | 47 | # vagrant stuff 48 | .vagrant/ 49 | .vagrant.d/ 50 | -------------------------------------------------------------------------------- /test/unit/mms_backup_agent_spec.rb: -------------------------------------------------------------------------------- 1 | require 'chefspec' 2 | require 'chefspec/berkshelf' 3 | 4 | describe 'sc-mongodb::mms_backup_agent' do 5 | let(:chef_run) do 6 | ChefSpec::Runner.new(platform: 'ubuntu') do |n| 7 | n['mongodb']['mms_agent']['api_key'] = 'strange key' 8 | end 9 | end 10 | 11 | it 'package install the mms_backup_agent' do 12 | chef_run.converge(described_recipe) 13 | expect(chef_run).to install_package('mongodb-mms-backup-agent') 14 | expect(chef_run).to render_file('/etc/mongodb-mms/backup-agent.config').with_content(/.*=strange key/) 15 | resource = chef_run.template('/etc/mongodb-mms/backup-agent.config') 16 | expect(resource).to notify('service[mongodb-mms-backup-agent]').to(:restart).delayed 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/unit/mms_monitoring_agent_spec.rb: -------------------------------------------------------------------------------- 1 | require 'chefspec' 2 | require 'chefspec/berkshelf' 3 | 4 | describe 'sc-mongodb::mms_monitoring_agent' do 5 | let(:chef_run) do 6 | ChefSpec::Runner.new(platform: 'ubuntu') do |n| 7 | n['mongodb']['mms_agent']['api_key'] = 'strange key' 8 | end 9 | end 10 | 11 | it 'package install the mms_monitoring_agent' do 12 | chef_run.converge(described_recipe) 13 | expect(chef_run).to install_package('mongodb-mms-monitoring-agent') 14 | expect(chef_run).to render_file('/etc/mongodb-mms/monitoring-agent.config').with_content(/.*=strange key/) 15 | resource = chef_run.template('/etc/mongodb-mms/monitoring-agent.config') 16 | expect(resource).to notify('service[mongodb-mms-monitoring-agent]').to(:restart).delayed 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /test/unit/install_spec.rb: -------------------------------------------------------------------------------- 1 | require 'chefspec' 2 | require 'chefspec/berkshelf' 3 | require 'fauxhai' 4 | 5 | describe 'sc-mongodb::default' do 6 | let(:chef_run) { ChefSpec::Runner.new(platform: 'ubuntu') } 7 | 8 | it 'should include install recipe, and enable mongodb service' do 9 | chef_run.converge(described_recipe) 10 | expect(chef_run).to include_recipe('sc-mongodb::install') 11 | expect(chef_run).to enable_service('mongodb') 12 | end 13 | 14 | it 'package install mongodb-org via mongodb-org' do 15 | chef_run.node['mongodb']['install_method'] = 'mongodb-org' 16 | chef_run.converge(described_recipe) 17 | expect(chef_run).to include_recipe('sc-mongodb::mongodb_org_repo') 18 | expect(chef_run).to include_recipe('sc-mongodb::install') 19 | expect(chef_run).to install_package('mongodb-org') 20 | expect(chef_run).to enable_service('mongodb') 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /kitchen.global.yml: -------------------------------------------------------------------------------- 1 | --- 2 | provisioner: 3 | name: chef_infra 4 | product_name: chef 5 | product_version: <%= ENV['CHEF_VERSION'] || 'latest' %> 6 | channel: stable 7 | install_strategy: once 8 | chef_license: accept 9 | enforce_idempotency: <%= ENV['ENFORCE_IDEMPOTENCY'] || true %> 10 | multiple_converge: <%= ENV['MULTIPLE_CONVERGE'] || 2 %> 11 | deprecations_as_errors: true 12 | log_level: <%= ENV['CHEF_LOG_LEVEL'] || 'auto' %> 13 | 14 | verifier: 15 | name: inspec 16 | 17 | platforms: 18 | - name: almalinux-8 19 | - name: almalinux-9 20 | - name: amazonlinux-2023 21 | - name: centos-stream-9 22 | - name: debian-11 23 | - name: debian-12 24 | - name: fedora-latest 25 | - name: opensuse-leap-15 26 | - name: oraclelinux-8 27 | - name: oraclelinux-9 28 | - name: rockylinux-8 29 | - name: rockylinux-9 30 | - name: ubuntu-20.04 31 | - name: ubuntu-22.04 32 | - name: ubuntu-24.04 33 | -------------------------------------------------------------------------------- /test/integration/nodes/mongodb-replica-001.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "mongodb-replica-001", 3 | "name": "mongodb-replica-001", 4 | "chef_environment": "_default", 5 | "json_class": "Chef::Node", 6 | "automatic": { 7 | "fqdn": "mongodb-replica-001.example.net", 8 | "hostname": "mongodb-replica-001", 9 | "os": "centos" 10 | }, 11 | "normal": { 12 | "mongodb":{ 13 | "cluster_name": "kitchen", 14 | "is_replicaset": "true", 15 | "shard_name": "default", 16 | "config": { 17 | "port": "1234" 18 | }, 19 | "replica_arbiter_only": false, 20 | "replica_build_indexes": true, 21 | "replica_hidden": false, 22 | "replica_slave_delay": 0, 23 | "replica_priority": 1, 24 | "replica_tags": {"foo":"bar"}, 25 | "replica_votes": 1 26 | } 27 | }, 28 | "chef_type": "node", 29 | "default": {}, 30 | "override": {}, 31 | "run_list": [] 32 | } 33 | 34 | -------------------------------------------------------------------------------- /test/integration/nodes/mongodb-configserver-001.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "mongodb-configserver-001", 3 | "name": "mongodb-configserver-001", 4 | "chef_environment": "_default", 5 | "json_class": "Chef::Node", 6 | "automatic": { 7 | "hostname": "vagrant.vm", 8 | "os": "centos" 9 | }, 10 | "normal": { 11 | "mongodb":{ 12 | "cluster_name": "default", 13 | "is_configserver": "true" 14 | }, 15 | "mongodb_cluster_name": "default", 16 | "mongodb_is_configserver": "true" 17 | }, 18 | "chef_type": "node", 19 | "default": { 20 | "mongodb":{ 21 | "cluster_name": "default", 22 | "is_configserver": "true" 23 | }, 24 | "mongodb_cluster_name": "default", 25 | "mongodb_is_configserver": "true" 26 | }, 27 | "mongodb_cluster_name": "default", 28 | "mongodb_is_configserver": "true", 29 | "override": { 30 | }, 31 | "run_list": [ 32 | "role[mongodb-configserver]" 33 | ] 34 | } 35 | -------------------------------------------------------------------------------- /test/integration/shard-mongos/inspec/shard_spec.rb: -------------------------------------------------------------------------------- 1 | # mongod service is running 2 | describe service('mongod') do 3 | it { should be_installed } 4 | it { should be_enabled } 5 | it { should be_running } 6 | end 7 | 8 | # only 1 shard initialized 9 | describe bash('mongo --eval "sh.status()" | grep \'{ "_id"\' | grep host | wc -l') do 10 | its('exit_status') { should eq 0 } 11 | its(:stdout) { should match '1' } 12 | end 13 | 14 | # shard initialized with the correct node names 15 | describe bash('mongo --eval "sh.status()"') do 16 | its('exit_status') { should eq 0 } 17 | 18 | # Sharding 19 | its(:stdout) { should match %r(.*^\s{2}shards:\n\t{ "_id" : "kitchen1", "host" : "kitchen1/mongo1:27017,mongo2:27017,mongo3:27017" }$.*) } 20 | 21 | # Sharded collection 22 | its(:stdout) { should match(/.*^\t{ "_id" : "test", "primary" : "kitchen1", "partitioned" : true }\n\t{2}test.testing\n\t{3}shard key: { "_id" : 1 }$.*/) } 23 | end 24 | -------------------------------------------------------------------------------- /test/integration/replicaset3/inspec/replicaset_spec.rb: -------------------------------------------------------------------------------- 1 | # mongod service is running 2 | describe service('mongod') do 3 | it { should be_installed } 4 | it { should be_enabled } 5 | it { should be_running } 6 | end 7 | 8 | # replicaset initialized with 3 nodes 9 | describe bash('mongo --eval "rs.status()" | grep name | wc -l') do 10 | its('exit_status') { should eq 0 } 11 | its(:stdout) { should match '3' } 12 | end 13 | 14 | # replicaset initialized with the correct node names 15 | describe bash('mongo --eval "rs.status()"') do 16 | its('exit_status') { should eq 0 } 17 | its(:stdout) { should match(/.*^connecting to: test\n\{\n\t{1}"set" : "kitchen"\,$.*/) } 18 | its(:stdout) { should match(/.*^\t{3}"_id" : 0\,\n\t{3}"name" : "mongo1:27017"\,$.*/) } 19 | its(:stdout) { should match(/.*^\t{3}"_id" : 1\,\n\t{3}"name" : "mongo2:27017"\,$.*/) } 20 | its(:stdout) { should match(/.*^\t{3}"_id" : 2\,\n\t{3}"name" : "mongo3:27017"\,$.*/) } 21 | end 22 | -------------------------------------------------------------------------------- /test/integration/user_management/inspec/user_management_spec.rb: -------------------------------------------------------------------------------- 1 | # @test "requires authentication" { 2 | # mongo --eval "db.stats().ok" 3 | # ! [ $? -eq 1 ] 4 | # } 5 | # 6 | # @test "admin user created" { 7 | # mongo admin -u admin -p admin --eval "db.stats().ok" 8 | # [ $? -eq 0 ] 9 | # } 10 | 11 | describe service('mongod') do 12 | it { should be_installed } 13 | it { should be_enabled } 14 | it { should be_running } 15 | end 16 | 17 | # admin user created 18 | describe bash('mongo admin -u admin -p admin --eval "db.stats().ok"') do 19 | its('exit_status') { should eq 0 } 20 | end 21 | 22 | describe bash('mongo --eval "db.stats().ok"') do 23 | its('exit_status') { should_not eq 1 } 24 | end 25 | 26 | # kitchen read user created 27 | describe bash(%(mongo admin -u admin -p admin --eval "db.system.users.find({'_id' : 'admin.kitchen', 'user' : 'kitchen', 'db' : 'admin', 'roles' : [ { 'role' : 'read', 'db' : 'admin' } ]})" | grep _id)) do 28 | its('exit_status') { should eq 0 } 29 | end 30 | -------------------------------------------------------------------------------- /test/integration/user_management_v2/inspec/user_management_spec.rb: -------------------------------------------------------------------------------- 1 | # @test "requires authentication" { 2 | # mongo --eval "db.stats().ok" 3 | # ! [ $? -eq 1 ] 4 | # } 5 | # 6 | # @test "admin user created" { 7 | # mongo admin -u admin -p admin --eval "db.stats().ok" 8 | # [ $? -eq 0 ] 9 | # } 10 | 11 | describe service('mongod') do 12 | it { should be_installed } 13 | it { should be_enabled } 14 | it { should be_running } 15 | end 16 | 17 | # admin user created 18 | describe bash('mongo admin -u admin -p admin --eval "db.stats().ok"') do 19 | its('exit_status') { should eq 0 } 20 | end 21 | 22 | describe bash('mongo --eval "db.stats().ok"') do 23 | its('exit_status') { should_not eq 1 } 24 | end 25 | 26 | # kitchen read user created 27 | describe bash(%(mongo admin -u admin -p admin --eval "db.system.users.find({'_id' : 'admin.kitchen', 'user' : 'kitchen', 'db' : 'admin', 'roles' : [ { 'role' : 'read', 'db' : 'admin' } ]})" | grep _id)) do 28 | its('exit_status') { should eq 0 } 29 | end 30 | -------------------------------------------------------------------------------- /attributes/sysconfig.rb: -------------------------------------------------------------------------------- 1 | include_attribute 'sc-mongodb::default' 2 | 3 | # mongod defaults 4 | default['mongodb']['sysconfig']['mongod']['DAEMON'] = '/usr/bin/$NAME' 5 | default['mongodb']['sysconfig']['mongod']['DAEMON_USER'] = node['mongodb']['user'] 6 | default['mongodb']['sysconfig']['mongod']['DAEMON_OPTS'] = "--config #{node['mongodb']['dbconfig_file']['mongod']}" 7 | default['mongodb']['sysconfig']['mongod']['CONFIGFILE'] = node['mongodb']['dbconfig_file']['mongod'] 8 | default['mongodb']['sysconfig']['mongod']['ENABLE_MONGODB'] = 'yes' 9 | 10 | # mongos defaults 11 | default['mongodb']['sysconfig']['mongos']['DAEMON'] = '/usr/bin/$NAME' 12 | default['mongodb']['sysconfig']['mongos']['DAEMON_USER'] = node['mongodb']['user'] 13 | default['mongodb']['sysconfig']['mongos']['DAEMON_OPTS'] = "--config #{node['mongodb']['dbconfig_file']['mongos']}" 14 | default['mongodb']['sysconfig']['mongos']['CONFIGFILE'] = node['mongodb']['dbconfig_file']['mongos'] 15 | default['mongodb']['sysconfig']['mongos']['ENABLE_MONGODB'] = 'yes' 16 | -------------------------------------------------------------------------------- /test/integration/user_management_v2_delete/inspec/user_management_spec.rb: -------------------------------------------------------------------------------- 1 | # @test "requires authentication" { 2 | # mongo --eval "db.stats().ok" 3 | # ! [ $? -eq 1 ] 4 | # } 5 | # 6 | # @test "admin user created" { 7 | # mongo admin -u admin -p admin --eval "db.stats().ok" 8 | # [ $? -eq 0 ] 9 | # } 10 | 11 | describe service('mongod') do 12 | it { should be_installed } 13 | it { should be_enabled } 14 | it { should be_running } 15 | end 16 | 17 | # admin user created 18 | describe bash('mongo admin -u admin -p admin --eval "db.stats().ok"') do 19 | its('exit_status') { should eq 0 } 20 | end 21 | 22 | describe bash('mongo --eval "db.stats().ok"') do 23 | its('exit_status') { should_not eq 1 } 24 | end 25 | 26 | # kitchen read user created but then deleted 27 | describe bash(%(mongo admin -u admin -p admin --eval "db.system.users.find({'_id' : 'admin.kitchen', 'user' : 'kitchen', 'db' : 'admin', 'roles' : [ { 'role' : 'read', 'db' : 'admin' } ]})" | grep _id)) do 28 | its('exit_status') { should eq 1 } 29 | end 30 | -------------------------------------------------------------------------------- /.github/workflows/stale.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: Mark stale issues and pull requests 3 | 4 | "on": 5 | schedule: [cron: "0 0 * * *"] 6 | 7 | jobs: 8 | stale: 9 | runs-on: ubuntu-latest 10 | steps: 11 | - uses: actions/stale@v10 12 | with: 13 | repo-token: ${{ secrets.GITHUB_TOKEN }} 14 | close-issue-message: > 15 | Closing due to inactivity. 16 | If this is still an issue please reopen or open another issue. 17 | Alternatively drop by the #sous-chefs channel on the [Chef Community Slack](http://community-slack.chef.io/) and we'll be happy to help! 18 | Thanks, Sous-Chefs. 19 | days-before-close: 7 20 | days-before-stale: 365 21 | stale-issue-message: > 22 | Marking stale due to inactivity. 23 | Remove stale label or comment or this will be closed in 7 days. 24 | Alternatively drop by the #sous-chefs channel on the [Chef Community Slack](http://community-slack.chef.io/) and we'll be happy to help! 25 | Thanks, Sous-Chefs. 26 | -------------------------------------------------------------------------------- /test/integration/nodes/mongo1.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "mongo1", 3 | "name": "mongo1", 4 | "chef_environment": "_default", 5 | "json_class": "Chef::Node", 6 | "automatic": { 7 | "hostname": "vagrant.vm", 8 | "os": "centos" 9 | }, 10 | "normal": { 11 | "fqdn": "mongo1", 12 | "mongodb":{ 13 | "cluster_name": "kitchen", 14 | "is_replicaset": "true" 15 | }, 16 | "mongodb_cluster_name": "default" 17 | }, 18 | "chef_type": "node", 19 | "default": { 20 | "mongodb":{ 21 | "cluster_name": "kitchen", 22 | "config": { 23 | "mongod": { 24 | "net": { 25 | "port": 27017 26 | }, 27 | "replication": { 28 | "replSetName": "kitchen" 29 | } 30 | } 31 | }, 32 | "is_replicaset": "true", 33 | "replica_build_indexes": true, 34 | "replica_priority": 1, 35 | "replica_tags": {}, 36 | "replica_slave_delay": 0, 37 | "replica_votes": 1 38 | }, 39 | "mongodb_cluster_name": "default" 40 | }, 41 | "override": { 42 | }, 43 | "run_list": [ 44 | "role[mongodb-configserver]" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /test/integration/nodes/mongo2.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "mongo2", 3 | "name": "mongo2", 4 | "chef_environment": "_default", 5 | "json_class": "Chef::Node", 6 | "automatic": { 7 | "hostname": "vagrant.vm", 8 | "os": "centos" 9 | }, 10 | "normal": { 11 | "fqdn": "mongo2", 12 | "mongodb":{ 13 | "cluster_name": "kitchen", 14 | "is_replicaset": "true" 15 | }, 16 | "mongodb_cluster_name": "default" 17 | }, 18 | "chef_type": "node", 19 | "default": { 20 | "mongodb":{ 21 | "cluster_name": "kitchen", 22 | "config": { 23 | "mongod": { 24 | "net": { 25 | "port": 27017 26 | }, 27 | "replication": { 28 | "replSetName": "kitchen" 29 | } 30 | } 31 | }, 32 | "is_replicaset": "true", 33 | "replica_build_indexes": true, 34 | "replica_priority": 1, 35 | "replica_tags": {}, 36 | "replica_slave_delay": 0, 37 | "replica_votes": 1 38 | }, 39 | "mongodb_cluster_name": "default" 40 | }, 41 | "override": { 42 | }, 43 | "run_list": [ 44 | "role[mongodb-configserver]" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /test/integration/nodes/mongo3.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "mongo3", 3 | "name": "mongo3", 4 | "chef_environment": "_default", 5 | "json_class": "Chef::Node", 6 | "automatic": { 7 | "hostname": "vagrant.vm", 8 | "os": "centos" 9 | }, 10 | "normal": { 11 | "fqdn": "mongo3", 12 | "mongodb":{ 13 | "cluster_name": "kitchen", 14 | "is_replicaset": "true" 15 | }, 16 | "mongodb_cluster_name": "default" 17 | }, 18 | "chef_type": "node", 19 | "default": { 20 | "mongodb":{ 21 | "cluster_name": "kitchen", 22 | "config": { 23 | "mongod": { 24 | "net": { 25 | "port": 27017 26 | }, 27 | "replication": { 28 | "replSetName": "kitchen" 29 | } 30 | } 31 | }, 32 | "is_replicaset": "true", 33 | "replica_build_indexes": true, 34 | "replica_priority": 1, 35 | "replica_tags": {}, 36 | "replica_slave_delay": 0, 37 | "replica_votes": 1 38 | }, 39 | "mongodb_cluster_name": "default" 40 | }, 41 | "override": { 42 | }, 43 | "run_list": [ 44 | "role[mongodb-configserver]" 45 | ] 46 | } 47 | -------------------------------------------------------------------------------- /recipes/mms_automation_agent.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: mongodb 3 | # Recipe:: mms_automation_agent 4 | # 5 | # Copyright:: 2017, Grant Ridder 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | Chef::Log.warn 'Found empty mms_agent.api_key attribute' if node['mongodb']['mms_agent']['api_key'].nil? 21 | 22 | mongodb_agent 'automation' do 23 | config node['mongodb']['mms_agent']['automation']['config'] 24 | group node['mongodb']['mms_agent']['automation']['group'] 25 | package_url node['mongodb']['mms_agent']['automation']['package_url'] 26 | user node['mongodb']['mms_agent']['automation']['user'] 27 | end 28 | -------------------------------------------------------------------------------- /recipes/replicaset.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: sc-mongodb 3 | # Recipe:: replicaset 4 | # 5 | # Copyright:: 2011, edelight GmbH 6 | # 7 | # Copyright:: 2016-2017, Grant Ridder 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | # 21 | 22 | node.default['mongodb']['is_replicaset'] = true 23 | node.default['mongodb']['cluster_name'] = node['mongodb']['cluster_name'] 24 | 25 | include_recipe 'sc-mongodb::install' 26 | include_recipe 'sc-mongodb::mongo_gem' 27 | 28 | mongodb_instance node['mongodb']['instance_name']['mongod'] do 29 | mongodb_type 'mongod' 30 | replicaset true 31 | not_if { node['mongodb']['is_shard'] } 32 | end 33 | -------------------------------------------------------------------------------- /libraries/mongodb_config_helpers.rb: -------------------------------------------------------------------------------- 1 | # MongoDBConfigHelpers provides helpers to remove rendering logic 2 | # from templates 3 | module MongoDBConfigHelpers 4 | # to_boost_program_options takes a config Hash (with string keys and 5 | # scalar values) and converts to the boost::program_options format 6 | # used by mongodb 2.4. 7 | # 8 | # Notably it: 9 | # - ensures consistent ordering by key name 10 | # - does not render entries with a value of nil or '' 11 | def to_boost_program_options(config) 12 | config.sort \ 13 | .map do |key, value| 14 | next if value.nil? || value == '' 15 | "#{key} = #{value}" 16 | end \ 17 | .compact.join("\n") 18 | end 19 | 20 | def to_yaml_options(config) 21 | YAML.dump(config.to_hash.compact) 22 | end 23 | end 24 | 25 | # Monkey patches Hash to allow us to throw away keys that have empty or nil values 26 | class Hash 27 | def compact 28 | each_with_object({}) do |(k, v), new_hash| 29 | if v.is_a?(Hash) 30 | v = v.compact 31 | new_hash[k] = v unless v.empty? 32 | else 33 | new_hash[k] = v unless v.nil? 34 | end 35 | end 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /templates/default/debian-mongodb.upstart.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically Generated by Chef, do not edit directly! 3 | # 4 | 5 | <%- @ulimit.to_hash.keys.sort.each do |key| %> 6 | <%- if not @ulimit[key].nil? %> 7 | limit <%= key %> <%= @ulimit[key] %> <%= @ulimit[key] %> 8 | <% end %> 9 | <%- end %> 10 | 11 | kill timeout 300 # wait 300s between SIGTERM and SIGKILL. 12 | 13 | start on runlevel [2345] 14 | stop on runlevel [06] 15 | 16 | script 17 | NAME=<%= @provides %> 18 | ENABLE_MONGOD="yes" 19 | if [ -f <%= @sysconfig_file %> ]; then 20 | . <%= @sysconfig_file %>; 21 | fi 22 | 23 | # Handle NUMA access to CPUs (SERVER-3574) 24 | # This verifies the existence of numactl as well as testing that the command works 25 | NUMACTL_ARGS="--interleave=all" 26 | if which numactl >/dev/null 2>/dev/null && numactl $NUMACTL_ARGS ls / >/dev/null 2>/dev/null 27 | then 28 | NUMACTL="`which numactl` -- $NUMACTL_ARGS" 29 | DAEMON_OPTS="$DAEMON_OPTS" 30 | else 31 | NUMACTL="" 32 | DAEMON_OPTS="-- $DAEMON_OPTS" 33 | fi 34 | 35 | if [ "x$ENABLE_MONGOD" = "xyes" ]; then 36 | exec start-stop-daemon --start --quiet --chuid $DAEMON_USER --exec $NUMACTL $DAEMON $DAEMON_OPTS 37 | fi 38 | end script 39 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | --- 2 | name: ci 3 | 4 | "on": 5 | pull_request: 6 | push: 7 | branches: 8 | - main 9 | 10 | jobs: 11 | lint-unit: 12 | uses: sous-chefs/.github/.github/workflows/lint-unit.yml@5.0.3 13 | permissions: 14 | actions: write 15 | checks: write 16 | pull-requests: write 17 | statuses: write 18 | issues: write 19 | 20 | integration: 21 | needs: "lint-unit" 22 | 23 | runs-on: ubuntu-latest 24 | strategy: 25 | matrix: 26 | os: 27 | - "debian-10" 28 | - "centos-stream-9" 29 | - "ubuntu-2204" 30 | suite: 31 | - "default" 32 | # - 'replicaset' 33 | - "configserver" 34 | # - 'mongos' 35 | # - 'mms-agent' 36 | fail-fast: false 37 | 38 | steps: 39 | - name: Check out code 40 | uses: actions/checkout@v5 41 | - name: Install Chef 42 | uses: actionshub/chef-install@3.0.1 43 | - name: Dokken 44 | uses: actionshub/test-kitchen@3.0.0 45 | env: 46 | CHEF_LICENSE: accept-no-persist 47 | KITCHEN_LOCAL_YAML: kitchen.dokken.yml 48 | with: 49 | suite: ${{ matrix.suite }} 50 | os: ${{ matrix.os }} 51 | -------------------------------------------------------------------------------- /test/integration/nodes/shard1-n1.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "mongo1", 3 | "name": "mongo1", 4 | "chef_environment": "_default", 5 | "json_class": "Chef::Node", 6 | "automatic": { 7 | "hostname": "vagrant.vm", 8 | "os": "centos", 9 | "recipes": ["sc-mongodb::replicaset", "sc-mongodb::install", "sc-mongodb::mongodb_org_repo"] 10 | }, 11 | "normal": { 12 | "fqdn": "mongo1", 13 | "mongodb":{ 14 | "cluster_name": "kitchen1", 15 | "is_replicaset": "true" 16 | }, 17 | "mongodb_cluster_name": "default" 18 | }, 19 | "chef_type": "node", 20 | "default": { 21 | "mongodb":{ 22 | "cluster_name": "kitchen1", 23 | "config": { 24 | "mongod": { 25 | "net": { 26 | "port": 27017 27 | }, 28 | "replication": { 29 | "replSetName": "kitchen1" 30 | } 31 | } 32 | }, 33 | "is_replicaset": "true", 34 | "is_shard": "true", 35 | "replica_build_indexes": true, 36 | "replica_priority": 1, 37 | "replica_tags": {}, 38 | "replica_slave_delay": 0, 39 | "replica_votes": 1, 40 | "shard_name": "default" 41 | }, 42 | "mongodb_cluster_name": "default" 43 | }, 44 | "override": { 45 | }, 46 | "run_list": [ 47 | "role[mongodb-configserver]" 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /test/integration/nodes/shard1-n2.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "mongo2", 3 | "name": "mongo2", 4 | "chef_environment": "_default", 5 | "json_class": "Chef::Node", 6 | "automatic": { 7 | "hostname": "vagrant.vm", 8 | "os": "centos", 9 | "recipes": ["sc-mongodb::replicaset", "sc-mongodb::install", "sc-mongodb::mongodb_org_repo"] 10 | }, 11 | "normal": { 12 | "fqdn": "mongo2", 13 | "mongodb":{ 14 | "cluster_name": "kitchen1", 15 | "is_replicaset": "true" 16 | }, 17 | "mongodb_cluster_name": "default" 18 | }, 19 | "chef_type": "node", 20 | "default": { 21 | "mongodb":{ 22 | "cluster_name": "kitchen1", 23 | "config": { 24 | "mongod": { 25 | "net": { 26 | "port": 27017 27 | }, 28 | "replication": { 29 | "replSetName": "kitchen1" 30 | } 31 | } 32 | }, 33 | "is_replicaset": "true", 34 | "is_shard": "true", 35 | "replica_build_indexes": true, 36 | "replica_priority": 1, 37 | "replica_tags": {}, 38 | "replica_slave_delay": 0, 39 | "replica_votes": 1, 40 | "shard_name": "default" 41 | }, 42 | "mongodb_cluster_name": "default" 43 | }, 44 | "override": { 45 | }, 46 | "run_list": [ 47 | "role[mongodb-configserver]" 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /test/integration/nodes/shard1-n3.json: -------------------------------------------------------------------------------- 1 | { 2 | "id": "mongo3", 3 | "name": "mongo3", 4 | "chef_environment": "_default", 5 | "json_class": "Chef::Node", 6 | "automatic": { 7 | "hostname": "vagrant.vm", 8 | "os": "centos", 9 | "recipes": ["sc-mongodb::replicaset", "sc-mongodb::install", "sc-mongodb::mongodb_org_repo"] 10 | }, 11 | "normal": { 12 | "fqdn": "mongo3", 13 | "mongodb":{ 14 | "cluster_name": "kitchen1", 15 | "is_replicaset": "true" 16 | }, 17 | "mongodb_cluster_name": "default" 18 | }, 19 | "chef_type": "node", 20 | "default": { 21 | "mongodb":{ 22 | "cluster_name": "kitchen1", 23 | "config": { 24 | "mongod": { 25 | "net": { 26 | "port": 27017 27 | }, 28 | "replication": { 29 | "replSetName": "kitchen1" 30 | } 31 | } 32 | }, 33 | "is_replicaset": "true", 34 | "is_shard": "true", 35 | "replica_build_indexes": true, 36 | "replica_priority": 1, 37 | "replica_tags": {}, 38 | "replica_slave_delay": 0, 39 | "replica_votes": 1, 40 | "shard_name": "default" 41 | }, 42 | "mongodb_cluster_name": "default" 43 | }, 44 | "override": { 45 | }, 46 | "run_list": [ 47 | "role[mongodb-configserver]" 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /attributes/users.rb: -------------------------------------------------------------------------------- 1 | # The username / password combination that is used 2 | # to authenticate with the mongo database 3 | default['mongodb']['authentication']['username'] = 'admin' 4 | default['mongodb']['authentication']['password'] = 'admin' 5 | 6 | default['mongodb']['admin'] = { 7 | 'username' => default['mongodb']['authentication']['username'], 8 | 'password' => default['mongodb']['authentication']['password'], 9 | 'roles' => %w(userAdminAnyDatabase dbAdminAnyDatabase clusterAdmin), 10 | 'database' => 'admin', 11 | } 12 | 13 | default['mongodb']['users'] = [] 14 | 15 | # Force creation of admin user. auth=true is an invalid 16 | # setting for mongos so this is needed to ensure the admin 17 | # user is created 18 | default['mongodb']['mongos_create_admin'] = false 19 | 20 | # For connecting to mongo on localhost, retries to make after 21 | # connection failures and delay in seconds to retry 22 | default['mongodb']['user_management']['connection']['retries'] = 2 23 | default['mongodb']['user_management']['connection']['delay'] = 2 24 | 25 | # For mongod replicasets, the delay in seconds and number 26 | # of times to retry adding a user. Used to handle election 27 | # of primary not being completed immediately 28 | default['mongodb']['mongod_create_user']['retries'] = 2 29 | default['mongodb']['mongod_create_user']['delay'] = 10 30 | -------------------------------------------------------------------------------- /recipes/configserver.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: sc-mongodb 3 | # Recipe:: configserver 4 | # 5 | # Copyright:: 2011, edelight GmbH 6 | # Authors: 7 | # Markus Korn 8 | # 9 | # Copyright:: 2016-2017, Grant Ridder 10 | # 11 | # Licensed under the Apache License, Version 2.0 (the "License"); 12 | # you may not use this file except in compliance with the License. 13 | # You may obtain a copy of the License at 14 | # 15 | # http://www.apache.org/licenses/LICENSE-2.0 16 | # 17 | # Unless required by applicable law or agreed to in writing, software 18 | # distributed under the License is distributed on an "AS IS" BASIS, 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | # See the License for the specific language governing permissions and 21 | # limitations under the License. 22 | # 23 | 24 | node.override['mongodb']['is_configserver'] = true 25 | node.override['mongodb']['cluster_name'] = node['mongodb']['cluster_name'] 26 | 27 | include_recipe 'sc-mongodb::install' 28 | 29 | # mongodb_instance will set configsvr = true in the config file. 30 | # http://docs.mongodb.org/manual/reference/configuration-options/#sharded-cluster-options 31 | # we still explicitly set the port and small files. 32 | mongodb_instance node['mongodb']['instance_name']['mongod'] do 33 | mongodb_type 'configserver' 34 | end 35 | -------------------------------------------------------------------------------- /test/unit/default_spec.rb: -------------------------------------------------------------------------------- 1 | require 'chefspec' 2 | require 'chefspec/berkshelf' 3 | require 'fauxhai' 4 | 5 | describe 'sc-mongodb::default' do 6 | let(:chef_run) do 7 | ChefSpec::Runner.new( 8 | platform: 'ubuntu' 9 | ) 10 | end 11 | 12 | it 'should install and enable mongodb' do 13 | chef_run.converge(described_recipe) 14 | expect(chef_run).to enable_service('mongodb') 15 | expect(chef_run).to include_recipe('sc-mongodb::install') 16 | end 17 | 18 | it 'should disable logpath when syslog is set' do 19 | chef_run.node['mongodb']['config']['syslog'] = true 20 | chef_run.converge(described_recipe) 21 | expect(chef_run).to_not create_directory('/var/log/mongodb') 22 | end 23 | 24 | it 'should be able to set logpath to nil, and wont create' do 25 | chef_run.node['mongodb']['config']['logpath'] = '/tmp/mongodb_config_logpath/logfile.log' 26 | chef_run.node['mongodb']['config']['logpath'] = nil 27 | chef_run.converge(described_recipe) 28 | expect(chef_run).to_not create_directory('/tmp/mongodb_config_logpath') 29 | end 30 | 31 | it 'should by default create a logpath' do 32 | chef_run.node['mongodb']['config']['logpath'] = '/tmp/mongodb_config_logpath/logfile.log' 33 | chef_run.converge(described_recipe) 34 | expect(chef_run).to create_directory('/tmp/mongodb_config_logpath') 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /recipes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: sc-mongodb 3 | # Recipe:: default 4 | # 5 | # Copyright:: 2011, edelight GmbH 6 | # Authors: 7 | # Markus Korn 8 | # 9 | # Copyright:: 2016-2017, Grant Ridder 10 | # 11 | # Licensed under the Apache License, Version 2.0 (the "License"); 12 | # you may not use this file except in compliance with the License. 13 | # You may obtain a copy of the License at 14 | # 15 | # http://www.apache.org/licenses/LICENSE-2.0 16 | # 17 | # Unless required by applicable law or agreed to in writing, software 18 | # distributed under the License is distributed on an "AS IS" BASIS, 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | # See the License for the specific language governing permissions and 21 | # limitations under the License. 22 | # 23 | 24 | include_recipe 'sc-mongodb::install' 25 | 26 | # allow mongodb_instance to run if recipe isn't included 27 | allow_mongodb_instance_run = true 28 | conflicting_recipes = %w(sc-mongodb::replicaset sc-mongodb::shard sc-mongodb::configserver sc-mongodb::mongos sc-mongodb::mms_agent) 29 | conflicting_recipes.each do |recipe| 30 | allow_mongodb_instance_run &&= false if node.run_context.loaded_recipe?(recipe) 31 | end 32 | 33 | mongodb_instance node['mongodb']['instance_name']['mongod'] do 34 | mongodb_type 'mongod' 35 | only_if { allow_mongodb_instance_run } 36 | end 37 | -------------------------------------------------------------------------------- /recipes/mongo_gem.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: mongodb 3 | # Recipe:: mongo_gem 4 | # 5 | # Copyright:: 2011, edelight GmbH 6 | # Authors: 7 | # Markus Korn 8 | # 9 | # Copyright:: 2016-2017, Grant Ridder 10 | # 11 | # Licensed under the Apache License, Version 2.0 (the "License"); 12 | # you may not use this file except in compliance with the License. 13 | # You may obtain a copy of the License at 14 | # 15 | # http://www.apache.org/licenses/LICENSE-2.0 16 | # 17 | # Unless required by applicable law or agreed to in writing, software 18 | # distributed under the License is distributed on an "AS IS" BASIS, 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | # See the License for the specific language governing permissions and 21 | # limitations under the License. 22 | # 23 | 24 | # The build-essential cookbook was not running during the compile phase, install gcc explicitly for rhel so native 25 | # extensions can be installed 26 | gcc = package 'gcc' do 27 | action :nothing 28 | only_if { platform_family?('rhel') } 29 | end 30 | gcc.run_action(:install) 31 | 32 | sasldev_pkg = if platform_family?('rhel', 'amazon') 33 | 'cyrus-sasl-devel' 34 | else 35 | 'libsasl2-dev' 36 | end 37 | 38 | package sasldev_pkg do 39 | action :nothing 40 | end.run_action(:install) 41 | 42 | node['mongodb']['ruby_gems'].each do |gem, version| 43 | chef_gem gem do 44 | version version 45 | compile_time false 46 | end 47 | end 48 | -------------------------------------------------------------------------------- /recipes/shard.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: sc-mongodb 3 | # Recipe:: shard 4 | # 5 | # Copyright:: 2011, edelight GmbH 6 | # Authors: 7 | # Markus Korn 8 | # 9 | # Copyright:: 2016-2017, Grant Ridder 10 | # 11 | # Licensed under the Apache License, Version 2.0 (the "License"); 12 | # you may not use this file except in compliance with the License. 13 | # You may obtain a copy of the License at 14 | # 15 | # http://www.apache.org/licenses/LICENSE-2.0 16 | # 17 | # Unless required by applicable law or agreed to in writing, software 18 | # distributed under the License is distributed on an "AS IS" BASIS, 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | # See the License for the specific language governing permissions and 21 | # limitations under the License. 22 | # 23 | 24 | node.default['mongodb']['is_shard'] = true 25 | node.default['mongodb']['shard_name'] = node['mongodb']['shard_name'] 26 | node.default['mongodb']['is_replicaset'] = node['mongodb']['is_replicaset'] 27 | node.default['mongodb']['cluster_name'] = node['mongodb']['cluster_name'] 28 | 29 | include_recipe 'sc-mongodb::install' 30 | 31 | ruby_block 'chef_gem_at_converge_time' do 32 | block do 33 | node['mongodb']['ruby_gems'].each do |gem, version| 34 | version = Gem::Dependency.new(gem, version) 35 | Chef::Provider::Package::Rubygems::GemEnvironment.new.install(version) 36 | end 37 | end 38 | end 39 | 40 | mongodb_instance node['mongodb']['instance_name']['mongod'] do 41 | mongodb_type 'mongod' 42 | replicaset true 43 | end 44 | -------------------------------------------------------------------------------- /recipes/mms_backup_agent.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: mongodb 3 | # Recipe:: mms_backup_agent 4 | # 5 | # Copyright:: 2011, edelight GmbH 6 | # Authors: 7 | # Markus Korn 8 | # 9 | # Copyright:: 2016-2017, Grant Ridder 10 | # 11 | # Licensed under the Apache License, Version 2.0 (the "License"); 12 | # you may not use this file except in compliance with the License. 13 | # You may obtain a copy of the License at 14 | # 15 | # http://www.apache.org/licenses/LICENSE-2.0 16 | # 17 | # Unless required by applicable law or agreed to in writing, software 18 | # distributed under the License is distributed on an "AS IS" BASIS, 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | # See the License for the specific language governing permissions and 21 | # limitations under the License. 22 | # 23 | 24 | Chef::Log.warn 'Found empty mms_agent.api_key attribute' if node['mongodb']['mms_agent']['api_key'].nil? 25 | 26 | # The MMS agent is hard coded for a specific version of libsasl that is newer 27 | # on RHEL 7 28 | # See http://stackoverflow.com/a/26242879 29 | link '/usr/lib64/libsasl2.so.2 mms_backup_agent' do 30 | to '/usr/lib64/libsasl2.so.3' 31 | target_file '/usr/lib64/libsasl2.so.2' 32 | not_if { ::File.exist?('/usr/lib64/libsasl2.so.2') } 33 | only_if { ::File.exist?('/usr/lib64/libsasl2.so.3') } 34 | only_if { platform_family?('rhel') && node['platform_version'].to_i == 7 } 35 | end 36 | 37 | mongodb_agent 'backup' do 38 | config node['mongodb']['mms_agent']['backup']['config'] 39 | group node['mongodb']['mms_agent']['backup']['group'] 40 | package_url node['mongodb']['mms_agent']['backup']['package_url'] 41 | user node['mongodb']['mms_agent']['backup']['user'] 42 | end 43 | -------------------------------------------------------------------------------- /Dangerfile: -------------------------------------------------------------------------------- 1 | # Reference: http://danger.systems/reference.html 2 | 3 | # A pull request summary is required. Add a description of the pull request purpose. 4 | # Changelog must be updated for each pull request that changes code. 5 | # Warnings will be issued for: 6 | # Pull request with more than 400 lines of code changed 7 | # Pull reqest that change more than 5 lines without test changes 8 | # Failures will be issued for: 9 | # Pull request without summary 10 | # Pull requests with code changes without changelog entry 11 | 12 | def code_changes? 13 | code = %w(libraries attributes recipes resources files templates) 14 | code.each do |location| 15 | return true unless git.modified_files.grep(/#{location}/).empty? 16 | end 17 | false 18 | end 19 | 20 | def test_changes? 21 | tests = %w(spec test kitchen.yml kitchen.dokken.yml) 22 | tests.each do |location| 23 | return true unless git.modified_files.grep(/#{location}/).empty? 24 | end 25 | false 26 | end 27 | 28 | failure 'Please provide a summary of your Pull Request.' if github.pr_body.length < 10 29 | 30 | warn 'This is a big Pull Request.' if git.lines_of_code > 400 31 | 32 | warn 'This is a Table Flip.' if git.lines_of_code > 2000 33 | 34 | # Require a CHANGELOG entry for non-test changes. 35 | if !git.modified_files.include?('CHANGELOG.md') && code_changes? 36 | failure 'Please include a CHANGELOG entry.' 37 | end 38 | 39 | # Require Major Minor Patch version labels 40 | unless github.pr_labels.grep /minor|major|patch/i 41 | warn 'Please add a release label to this pull request' 42 | end 43 | 44 | # A sanity check for tests. 45 | if git.lines_of_code > 5 && code_changes? && !test_changes? 46 | warn 'This Pull Request is probably missing tests.' 47 | end 48 | -------------------------------------------------------------------------------- /recipes/mms_monitoring_agent.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: mongodb 3 | # Recipe:: mms_monitoring_agent 4 | # 5 | # Copyright:: 2011, edelight GmbH 6 | # Authors: 7 | # Markus Korn 8 | # 9 | # Copyright:: 2016-2017, Grant Ridder 10 | # 11 | # Licensed under the Apache License, Version 2.0 (the "License"); 12 | # you may not use this file except in compliance with the License. 13 | # You may obtain a copy of the License at 14 | # 15 | # http://www.apache.org/licenses/LICENSE-2.0 16 | # 17 | # Unless required by applicable law or agreed to in writing, software 18 | # distributed under the License is distributed on an "AS IS" BASIS, 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | # See the License for the specific language governing permissions and 21 | # limitations under the License. 22 | # 23 | 24 | Chef::Log.warn 'Found empty mms_agent.api_key attribute' if node['mongodb']['mms_agent']['api_key'].nil? 25 | 26 | # The MMS agent is hard coded for a specific version of libsasl that is newer 27 | # on RHEL 7 28 | # See http://stackoverflow.com/a/26242879 29 | link '/usr/lib64/libsasl2.so.2 mms_monitoring_agent' do 30 | to '/usr/lib64/libsasl2.so.3' 31 | target_file '/usr/lib64/libsasl2.so.2' 32 | not_if { ::File.exist?('/usr/lib64/libsasl2.so.2') } 33 | only_if { ::File.exist?('/usr/lib64/libsasl2.so.3') } 34 | only_if { platform_family?('rhel') && node['platform_version'].to_i == 7 } 35 | end 36 | 37 | mongodb_agent 'monitoring' do 38 | config node['mongodb']['mms_agent']['monitoring']['config'] 39 | group node['mongodb']['mms_agent']['monitoring']['group'] 40 | package_url node['mongodb']['mms_agent']['monitoring']['package_url'] 41 | user node['mongodb']['mms_agent']['monitoring']['user'] 42 | end 43 | -------------------------------------------------------------------------------- /resources/user.rb: -------------------------------------------------------------------------------- 1 | provides :mongodb_user 2 | unified_mode true 3 | 4 | property :username, String, name_property: true 5 | property :password, String 6 | property :roles, Array 7 | property :database, String 8 | property :connection, Hash 9 | 10 | action :add do 11 | require 'mongo' 12 | if defined?(Mongo::VERSION) && Gem::Version.new(Mongo::VERSION) >= Gem::Version.new('2.0.0') 13 | # The gem displays a lot of debug messages by default so set to INFO 14 | Mongo::Logger.logger.level = ::Logger::INFO 15 | add_user_v2(new_resource.username, new_resource.password, new_resource.database, new_resource.roles) 16 | else # mongo gem version 1.x 17 | add_user(new_resource.username, new_resource.password, new_resource.database, new_resource.roles) 18 | end 19 | end 20 | 21 | action :delete do 22 | require 'mongo' 23 | if defined?(Mongo::VERSION) && Gem::Version.new(Mongo::VERSION) >= Gem::Version.new('2.0.0') 24 | # The gem displays a lot of debug messages by default so set to INFO 25 | Mongo::Logger.logger.level = ::Logger::INFO 26 | delete_user_v2(new_resource.username, new_resource.database) 27 | else # mongo gem version 1.x 28 | delete_user(new_resource.username, new_resource.database) 29 | end 30 | end 31 | 32 | action :modify do 33 | require 'mongo' 34 | if defined?(Mongo::VERSION) && Gem::Version.new(Mongo::VERSION) >= Gem::Version.new('2.0.0') 35 | # The gem displays a lot of debug messages by default so set to INFO 36 | Mongo::Logger.logger.level = ::Logger::INFO 37 | # TODO: implement modify for 2.x gem 38 | else # mongo gem version 1.x 39 | add_user(new_resource.username, new_resource.password, new_resource.database, new_resource.roles) 40 | end 41 | end 42 | 43 | action_class do 44 | include MongoDB::Helpers::User 45 | end 46 | -------------------------------------------------------------------------------- /recipes/mongos.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: sc-mongodb 3 | # Recipe:: mongos 4 | # 5 | # Copyright:: 2011, edelight GmbH 6 | # Authors: 7 | # Markus Korn 8 | # 9 | # Copyright:: 2016-2017, Grant Ridder 10 | # 11 | # Licensed under the Apache License, Version 2.0 (the "License"); 12 | # you may not use this file except in compliance with the License. 13 | # You may obtain a copy of the License at 14 | # 15 | # http://www.apache.org/licenses/LICENSE-2.0 16 | # 17 | # Unless required by applicable law or agreed to in writing, software 18 | # distributed under the License is distributed on an "AS IS" BASIS, 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | # See the License for the specific language governing permissions and 21 | # limitations under the License. 22 | # 23 | 24 | node.override['mongodb']['is_mongos'] = true 25 | 26 | include_recipe 'sc-mongodb::install' 27 | 28 | ruby_block 'chef_gem_at_converge_time' do 29 | block do 30 | node['mongodb']['ruby_gems'].each do |gem, version| 31 | version = Gem::Dependency.new(gem, version) 32 | Chef::Provider::Package::Rubygems::GemEnvironment.new.install(version) 33 | end 34 | end 35 | end 36 | 37 | configsrvs = search( 38 | :node, 39 | "mongodb_cluster_name:#{node['mongodb']['cluster_name']} AND \ 40 | mongodb_is_configserver:true AND \ 41 | chef_environment:#{node.chef_environment}" 42 | ) 43 | 44 | if configsrvs.length != 1 && configsrvs.length != 3 45 | Chef::Log.error("Found #{configsrvs.length} configservers, need either one or three of them") 46 | raise 'Wrong number of configserver nodes' unless Chef::Config[:solo] 47 | end 48 | 49 | mongodb_instance node['mongodb']['instance_name']['mongos'] do 50 | mongodb_type 'mongos' 51 | configservers configsrvs 52 | end 53 | -------------------------------------------------------------------------------- /recipes/user_management.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: mongodb 3 | # Recipe:: user_management 4 | # 5 | # Copyright:: 2011, edelight GmbH 6 | # Authors: 7 | # Markus Korn 8 | # 9 | # Copyright:: 2016-2017, Grant Ridder 10 | # 11 | # Licensed under the Apache License, Version 2.0 (the "License"); 12 | # you may not use this file except in compliance with the License. 13 | # You may obtain a copy of the License at 14 | # 15 | # http://www.apache.org/licenses/LICENSE-2.0 16 | # 17 | # Unless required by applicable law or agreed to in writing, software 18 | # distributed under the License is distributed on an "AS IS" BASIS, 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | # See the License for the specific language governing permissions and 21 | # limitations under the License. 22 | # 23 | 24 | include_recipe 'sc-mongodb::mongo_gem' 25 | 26 | users = [] 27 | admin = node['mongodb']['admin'] 28 | 29 | # If authentication is required, 30 | # add the admin to the users array for adding/updating 31 | users << admin if (node['mongodb']['config']['auth'] == true) || (node['mongodb']['mongos_create_admin'] == true) 32 | 33 | users.concat(node['mongodb']['users']) 34 | 35 | # Add each user specified in attributes 36 | users.each do |user| 37 | mongodb_user user['username'] do 38 | password user['password'] 39 | roles user['roles'] 40 | database user['database'] 41 | connection node['mongodb'] 42 | if node.recipe?('sc-mongodb::mongos') || node.recipe?('sc-mongodb::replicaset') 43 | # If it's a replicaset or mongos, don't make any users until the end 44 | action :nothing 45 | subscribes :add, 'ruby_block[config_replicaset]', :delayed 46 | subscribes :add, 'ruby_block[config_sharding]', :delayed 47 | else 48 | action user['action'] || :add 49 | end 50 | end 51 | end 52 | -------------------------------------------------------------------------------- /chefignore: -------------------------------------------------------------------------------- 1 | # Put files/directories that should be ignored in this file when uploading 2 | # to a Chef Infra Server or Supermarket. 3 | # Lines that start with '# ' are comments. 4 | 5 | # OS generated files # 6 | ###################### 7 | .DS_Store 8 | ehthumbs.db 9 | Icon? 10 | nohup.out 11 | Thumbs.db 12 | .envrc 13 | 14 | # EDITORS # 15 | ########### 16 | .#* 17 | .project 18 | .settings 19 | *_flymake 20 | *_flymake.* 21 | *.bak 22 | *.sw[a-z] 23 | *.tmproj 24 | *~ 25 | \#* 26 | REVISION 27 | TAGS* 28 | tmtags 29 | .vscode 30 | .editorconfig 31 | 32 | ## COMPILED ## 33 | ############## 34 | *.class 35 | *.com 36 | *.dll 37 | *.exe 38 | *.o 39 | *.pyc 40 | *.so 41 | */rdoc/ 42 | a.out 43 | mkmf.log 44 | 45 | # Testing # 46 | ########### 47 | .circleci/* 48 | .codeclimate.yml 49 | .delivery/* 50 | .foodcritic 51 | .kitchen* 52 | .mdlrc 53 | .overcommit.yml 54 | .rspec 55 | .rubocop.yml 56 | .travis.yml 57 | .watchr 58 | .yamllint 59 | azure-pipelines.yml 60 | Dangerfile 61 | examples/* 62 | features/* 63 | Guardfile 64 | kitchen*.yml 65 | mlc_config.json 66 | Procfile 67 | Rakefile 68 | spec/* 69 | test/* 70 | 71 | # SCM # 72 | ####### 73 | .git 74 | .gitattributes 75 | .gitconfig 76 | .github/* 77 | .gitignore 78 | .gitkeep 79 | .gitmodules 80 | .svn 81 | */.bzr/* 82 | */.git 83 | */.hg/* 84 | */.svn/* 85 | 86 | # Berkshelf # 87 | ############# 88 | Berksfile 89 | Berksfile.lock 90 | cookbooks/* 91 | tmp 92 | 93 | # Bundler # 94 | ########### 95 | vendor/* 96 | Gemfile 97 | Gemfile.lock 98 | 99 | # Policyfile # 100 | ############## 101 | Policyfile.rb 102 | Policyfile.lock.json 103 | 104 | # Documentation # 105 | ############# 106 | CODE_OF_CONDUCT* 107 | CONTRIBUTING* 108 | documentation/* 109 | TESTING* 110 | UPGRADING* 111 | 112 | # Vagrant # 113 | ########### 114 | .vagrant 115 | Vagrantfile 116 | -------------------------------------------------------------------------------- /resources/agent.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: sc-mongodb 3 | # Resource:: agent 4 | # 5 | # Copyright:: 2017, Grant Ridder 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | provides :mongodb_agent 21 | unified_mode true 22 | 23 | property :type, String, name_property: true, equal_to: %w(automation backup monitoring) 24 | property :config, Hash 25 | property :group, String 26 | property :package_url, String 27 | property :user, String 28 | 29 | action :create do 30 | filename = new_resource.package_url.split('/').last 31 | full_file_path = "#{Chef::Config[:file_cache_path]}/#{filename}" 32 | 33 | remote_file full_file_path do 34 | source new_resource.package_url 35 | end 36 | 37 | if filename.split('.').last == 'deb' 38 | dpkg_package "mongodb-mms-#{new_resource.type}-agent" do 39 | source full_file_path 40 | end 41 | else 42 | package "mongodb-mms-#{new_resource.type}-agent" do 43 | source full_file_path 44 | end 45 | end 46 | 47 | template "/etc/mongodb-mms/#{new_resource.type}-agent.config" do 48 | source 'mms_agent_config.erb' 49 | owner new_resource.user 50 | group new_resource.group 51 | mode '600' 52 | variables( 53 | config: new_resource.config 54 | ) 55 | action :create 56 | notifies :restart, "service[mongodb-mms-#{new_resource.type}-agent]", :delayed 57 | end 58 | 59 | service "mongodb-mms-#{new_resource.type}-agent" do 60 | supports start: true, stop: true, restart: true, status: true 61 | action [:enable, :start] 62 | end 63 | end 64 | 65 | action :delete do 66 | filename = new_resource.package_url.split('/').last 67 | full_file_path = "#{Chef::Config[:file_cache_path]}/#{filename}" 68 | 69 | file full_file_path do 70 | action :delete 71 | end 72 | 73 | service "mongodb-mms-#{new_resource.type}-agent" do 74 | action [:disable, :stop] 75 | end 76 | 77 | package "mongodb-mms-#{new_resource.type}-agent" do 78 | action :remove 79 | end 80 | 81 | file "/etc/mongodb-mms/#{new_resource.type}-agent.config" do 82 | action :delete 83 | end 84 | end 85 | -------------------------------------------------------------------------------- /kitchen.dokken.yml: -------------------------------------------------------------------------------- 1 | driver: 2 | name: dokken 3 | privileged: true 4 | chef_version: <%= ENV['CHEF_VERSION'] || 'current' %> 5 | 6 | transport: { name: dokken } 7 | provisioner: { name: dokken } 8 | 9 | platforms: 10 | - name: almalinux-8 11 | driver: 12 | image: dokken/almalinux-8 13 | pid_one_command: /usr/lib/systemd/systemd 14 | 15 | - name: almalinux-9 16 | driver: 17 | image: dokken/almalinux-9 18 | pid_one_command: /usr/lib/systemd/systemd 19 | 20 | - name: almalinux-10 21 | driver: 22 | image: dokken/almalinux-10 23 | pid_one_command: /usr/lib/systemd/systemd 24 | 25 | - name: amazonlinux-2023 26 | driver: 27 | image: dokken/amazonlinux-2023 28 | pid_one_command: /usr/lib/systemd/systemd 29 | 30 | - name: centos-stream-9 31 | driver: 32 | image: dokken/centos-stream-9 33 | pid_one_command: /usr/lib/systemd/systemd 34 | 35 | - name: centos-stream-10 36 | driver: 37 | image: dokken/centos-stream-10 38 | pid_one_command: /usr/lib/systemd/systemd 39 | 40 | - name: debian-12 41 | driver: 42 | image: dokken/debian-12 43 | pid_one_command: /bin/systemd 44 | 45 | - name: debian-13 46 | driver: 47 | image: dokken/debian-13 48 | pid_one_command: /usr/lib/systemd/systemd 49 | 50 | - name: fedora-latest 51 | driver: 52 | image: dokken/fedora-latest 53 | pid_one_command: /usr/lib/systemd/systemd 54 | 55 | - name: opensuse-leap-15 56 | driver: 57 | image: dokken/opensuse-leap-15 58 | pid_one_command: /usr/lib/systemd/systemd 59 | 60 | - name: oraclelinux-8 61 | driver: 62 | image: dokken/oraclelinux-8 63 | pid_one_command: /usr/lib/systemd/systemd 64 | 65 | - name: oraclelinux-9 66 | driver: 67 | image: dokken/oraclelinux-9 68 | pid_one_command: /usr/lib/systemd/systemd 69 | 70 | - name: rockylinux-8 71 | driver: 72 | image: dokken/rockylinux-8 73 | pid_one_command: /usr/lib/systemd/systemd 74 | 75 | - name: rockylinux-9 76 | driver: 77 | image: dokken/rockylinux-9 78 | pid_one_command: /usr/lib/systemd/systemd 79 | 80 | - name: rockylinux-10 81 | driver: 82 | image: dokken/rockylinux-10 83 | pid_one_command: /usr/lib/systemd/systemd 84 | 85 | - name: ubuntu-20.04 86 | driver: 87 | image: dokken/ubuntu-20.04 88 | pid_one_command: /bin/systemd 89 | 90 | - name: ubuntu-22.04 91 | driver: 92 | image: dokken/ubuntu-22.04 93 | pid_one_command: /bin/systemd 94 | 95 | - name: ubuntu-24.04 96 | driver: 97 | image: dokken/ubuntu-24.04 98 | pid_one_command: /bin/systemd 99 | -------------------------------------------------------------------------------- /attributes/dbconfig.rb: -------------------------------------------------------------------------------- 1 | # All the configuration files that can be dumped 2 | # the attribute-based-configuration 3 | # dump anything into default['mongodb']['config']['mongod'][] = 4 | # these options are in the order of mongodb docs 5 | 6 | include_attribute 'sc-mongodb::default' 7 | 8 | # mongod defaults 9 | default['mongodb']['config']['mongod']['net']['port'] = 27017 10 | default['mongodb']['config']['mongod']['net']['bindIp'] = '0.0.0.0' 11 | 12 | default['mongodb']['config']['mongod']['systemLog']['destination'] = 'file' 13 | default['mongodb']['config']['mongod']['systemLog']['logAppend'] = true 14 | default['mongodb']['config']['mongod']['systemLog']['path'] = '/var/log/mongodb/mongod.log' 15 | 16 | if platform_family?('rhel', 'fedora', 'debian') 17 | default['mongodb']['config']['mongod']['processManagement']['fork'] = true 18 | default['mongodb']['config']['mongod']['processManagement']['pidFilePath'] = '/var/run/mongodb/mongod.pid' 19 | end 20 | 21 | default['mongodb']['config']['mongod']['storage']['journal']['enabled'] = true 22 | default['mongodb']['config']['mongod']['storage']['dbPath'] = if platform_family?('rhel', 'fedora') 23 | '/var/lib/mongo' 24 | else 25 | '/var/lib/mongodb' 26 | end 27 | 28 | default['mongodb']['config']['mongod']['storage']['engine'] = 'wiredTiger' 29 | 30 | default['mongodb']['config']['mongod']['replication']['oplogSizeMB'] = nil 31 | default['mongodb']['config']['mongod']['replication']['replSetName'] = nil 32 | default['mongodb']['config']['mongod']['replication']['secondaryIndexPrefetch'] = nil 33 | default['mongodb']['config']['mongod']['replication']['enableMajorityReadConcern'] = nil 34 | 35 | default['mongodb']['config']['mongod']['security']['keyFile'] = nil 36 | 37 | # mongos defaults 38 | default['mongodb']['config']['mongos']['net']['port'] = 27017 39 | default['mongodb']['config']['mongos']['net']['bindIp'] = '0.0.0.0' 40 | 41 | default['mongodb']['config']['mongos']['systemLog']['destination'] = 'file' 42 | default['mongodb']['config']['mongos']['systemLog']['logAppend'] = true 43 | default['mongodb']['config']['mongos']['systemLog']['path'] = '/var/log/mongodb/mongos.log' 44 | 45 | if platform_family?('rhel', 'fedora') 46 | default['mongodb']['config']['mongos']['processManagement']['fork'] = true 47 | default['mongodb']['config']['mongos']['processManagement']['pidFilePath'] = '/var/run/mongodb/mongos.pid' 48 | end 49 | 50 | default['mongodb']['config']['mongos']['sharding']['configDB'] = nil 51 | 52 | default['mongodb']['config']['mongos']['security']['keyFile'] = nil 53 | -------------------------------------------------------------------------------- /templates/default/redhat-mongodb.init.erb: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # <%= @provides %> - Startup script for mongod 4 | 5 | # chkconfig: 35 85 15 6 | # description: Mongo is a scalable, document-oriented database. 7 | # processname: <%= @provides %> 8 | # config: <%= @dbconfig_file %> 9 | 10 | . /etc/rc.d/init.d/functions 11 | 12 | NAME=<%= @provides %> 13 | SYSCONFIG=<%= @sysconfig_file %> 14 | DAEMON_USER=<%= @user %> 15 | ENABLE_MONGODB=yes 16 | 17 | SUBSYS_LOCK_FILE=/var/lock/subsys/<%= @provides %> 18 | 19 | if [ -f "$SYSCONFIG" ]; then 20 | . "$SYSCONFIG" 21 | fi 22 | 23 | # FIXME: 1.9.x has a --shutdown flag that parses the config file and 24 | # shuts down the correct running pid, but that's unavailable in 1.8 25 | # for now. This can go away when this script stops supporting 1.8. 26 | DBPATH=`awk -F= '/^dbpath[[:blank:]]*=[[:blank:]]*/{print $2}' "$CONFIGFILE"` 27 | PIDFILE=`awk -F= '/^pidfilepath[[:blank:]]*=[[:blank:]]*/{print $2}' "$CONFIGFILE"` 28 | 29 | # Handle NUMA access to CPUs (SERVER-3574) 30 | # This verifies the existence of numactl as well as testing that the command works 31 | NUMACTL_ARGS="--interleave=all" 32 | if which numactl >/dev/null 2>/dev/null && numactl $NUMACTL_ARGS ls / >/dev/null 2>/dev/null 33 | then 34 | NUMACTL="numactl $NUMACTL_ARGS" 35 | else 36 | NUMACTL="" 37 | fi 38 | 39 | start() 40 | { 41 | ulimit -f <%= @ulimit['fsize'] %> 42 | ulimit -t <%= @ulimit['cpu'] %> 43 | ulimit -v <%= @ulimit['as'] %> 44 | ulimit -n <%= @ulimit['nofile'] %> 45 | ulimit -m <%= @ulimit['rss'] %> 46 | ulimit -u <%= @ulimit['nproc'] %> 47 | 48 | echo -n $"Starting <%= @provides %>: " 49 | daemon --user "$DAEMON_USER" $NUMACTL $DAEMON $DAEMON_OPTS 50 | RETVAL=$? 51 | echo 52 | [ $RETVAL -eq 0 ] && touch $SUBSYS_LOCK_FILE 53 | } 54 | 55 | stop() 56 | { 57 | echo -n $"Stopping <%= @provides %>: " 58 | if test "x$PIDFILE" != "x"; then 59 | killproc -p $PIDFILE -d 300 $DAEMON 60 | else 61 | killproc -d 300 $DAEMON 62 | fi 63 | RETVAL=$? 64 | echo 65 | [ $RETVAL -eq 0 ] && rm -f $SUBSYS_LOCK_FILE 66 | } 67 | 68 | restart () { 69 | stop 70 | start 71 | } 72 | 73 | RETVAL=0 74 | 75 | if test "x$ENABLE_MONGODB" != "xyes"; then 76 | exit $RETVAL 77 | fi 78 | 79 | case "$1" in 80 | start) 81 | start 82 | ;; 83 | stop) 84 | stop 85 | ;; 86 | restart|reload|force-reload) 87 | restart 88 | ;; 89 | condrestart) 90 | [ -f $SUBSYS_LOCK_FILE ] && restart || : 91 | ;; 92 | status) 93 | if test "x$PIDFILE" != "x"; then 94 | status -p $PIDFILE $DAEMON 95 | else 96 | status $DAEMON 97 | fi 98 | RETVAL=$? 99 | ;; 100 | *) 101 | echo "Usage: $0 {start|stop|status|restart|reload|force-reload|condrestart}" 102 | RETVAL=1 103 | esac 104 | 105 | exit $RETVAL 106 | 107 | -------------------------------------------------------------------------------- /recipes/mongodb_org_repo.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: sc-mongodb 3 | # Recipe:: mongodb_org_repo 4 | # 5 | # Copyright:: 2011, edelight GmbH 6 | # Authors: 7 | # Miquel Torres 8 | # 9 | # Copyright:: 2016-2017, Grant Ridder 10 | # 11 | # Licensed under the Apache License, Version 2.0 (the "License"); 12 | # you may not use this file except in compliance with the License. 13 | # You may obtain a copy of the License at 14 | # 15 | # http://www.apache.org/licenses/LICENSE-2.0 16 | # 17 | # Unless required by applicable law or agreed to in writing, software 18 | # distributed under the License is distributed on an "AS IS" BASIS, 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | # See the License for the specific language governing permissions and 21 | # limitations under the License. 22 | # 23 | 24 | # Sets up the repositories for stable mongodb-org packages found here: 25 | # http://www.mongodb.org/downloads#packages 26 | node.override['mongodb']['package_name'] = 'mongodb-org' 27 | 28 | package_version_major = node['mongodb']['package_version'].to_f 29 | 30 | package_repo_url = case node['platform'] 31 | when 'redhat', 'oracle', 'centos', 'rocky' 32 | "https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/#{package_version_major}/#{node['kernel']['machine'] =~ /x86_64/ ? 'x86_64' : 'i686'}" 33 | when 'fedora' 34 | "https://repo.mongodb.org/yum/redhat/7/mongodb-org/#{package_version_major}/#{node['kernel']['machine'] =~ /x86_64/ ? 'x86_64' : 'i686'}" 35 | when 'amazon' 36 | "https://repo.mongodb.org/yum/amazon/2013.03/mongodb-org/#{package_version_major}/x86_64/" 37 | end 38 | 39 | case node['platform_family'] 40 | when 'debian' 41 | # Ubuntu: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-ubuntu/ 42 | # Debian: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-debian/ 43 | apt_repository 'mongodb' do 44 | uri node['mongodb']['repo'] 45 | distribution "#{node['lsb']['codename']}/mongodb-org/#{package_version_major}" 46 | components platform?('ubuntu') ? ['multiverse'] : ['main'] 47 | key "https://www.mongodb.org/static/pgp/server-#{package_version_major}.asc" 48 | end 49 | when 'amazon', 'fedora', 'rhel' 50 | # RHEL: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-red-hat/ 51 | # Amazon: https://docs.mongodb.com/manual/tutorial/install-mongodb-on-amazon/ 52 | yum_repository 'mongodb' do 53 | description 'mongodb RPM Repository' 54 | baseurl package_repo_url 55 | gpgkey "https://www.mongodb.org/static/pgp/server-#{package_version_major}.asc" 56 | gpgcheck true 57 | sslverify true 58 | enabled true 59 | end 60 | else 61 | # pssst build from source 62 | Chef::Log.warn("Adding the #{node['platform_family']} mongodb-org repository is not yet not supported by this cookbook") 63 | end 64 | -------------------------------------------------------------------------------- /attributes/mms_agent.rb: -------------------------------------------------------------------------------- 1 | default['mongodb']['mms_agent']['api_key'] = nil 2 | default['mongodb']['mms_agent']['mmsGroupId'] = nil 3 | 4 | default['mongodb']['mms_agent']['user'] = 'mongodb-mms-agent' 5 | default['mongodb']['mms_agent']['group'] = 'mongodb-mms-agent' 6 | 7 | # See https://docs.cloud.mongodb.com/reference/automation-agent/ for more settings 8 | default['mongodb']['mms_agent']['automation']['config']['mmsGroupId'] = nil 9 | default['mongodb']['mms_agent']['automation']['config']['mmsApiKey'] = node['mongodb']['mms_agent']['api_key'] 10 | default['mongodb']['mms_agent']['automation']['config']['mmsBaseUrl'] = 'https://mms.mongodb.com' 11 | default['mongodb']['mms_agent']['automation']['config']['logFile'] = '/var/log/mongodb-mms-automation/automation-agent.log' 12 | default['mongodb']['mms_agent']['automation']['config']['mmsConfigBackup'] = '/var/lib/mongodb-mms-automation/mms-cluster-config-backup.json' 13 | default['mongodb']['mms_agent']['automation']['config']['logLevel'] = 'INFO' 14 | default['mongodb']['mms_agent']['automation']['config']['maxLogFiles'] = 10 15 | default['mongodb']['mms_agent']['automation']['config']['maxLogFileSize'] = 268435456 16 | if platform_family?('rhel', 'fedora') 17 | default['mongodb']['mms_agent']['automation']['user'] = 'mongod' 18 | default['mongodb']['mms_agent']['automation']['group'] = 'mongod' 19 | else 20 | default['mongodb']['mms_agent']['automation']['user'] = 'mongodb' 21 | default['mongodb']['mms_agent']['automation']['group'] = 'mongodb' 22 | end 23 | 24 | # See https://docs.cloud.mongodb.com/reference/backup-agent/ for more settings 25 | default['mongodb']['mms_agent']['backup']['config']['mmsApiKey'] = node['mongodb']['mms_agent']['api_key'] 26 | default['mongodb']['mms_agent']['backup']['config']['mothership'] = 'api-backup.mongodb.com' 27 | default['mongodb']['mms_agent']['backup']['config']['https'] = true 28 | default['mongodb']['mms_agent']['backup']['user'] = node['mongodb']['mms_agent']['user'] 29 | default['mongodb']['mms_agent']['backup']['group'] = node['mongodb']['mms_agent']['user'] 30 | 31 | # See https://docs.cloud.mongodb.com/reference/monitoring-agent/ for more settings 32 | default['mongodb']['mms_agent']['monitoring']['config']['mmsApiKey'] = node['mongodb']['mms_agent']['api_key'] 33 | default['mongodb']['mms_agent']['monitoring']['config']['mmsBaseUrl'] = 'https://mms.mongodb.com' 34 | default['mongodb']['mms_agent']['monitoring']['user'] = node['mongodb']['mms_agent']['user'] 35 | default['mongodb']['mms_agent']['monitoring']['group'] = node['mongodb']['mms_agent']['user'] 36 | 37 | mms_agent_download_base = 'https://cloud.mongodb.com/download/agent' 38 | 39 | case node['platform_family'] 40 | when 'amazon', 'fedora', 'rhel' 41 | extention = if node['platform_version'].to_i == 6 42 | 'rpm' 43 | else 44 | 'rhel7.rpm' 45 | end 46 | default['mongodb']['mms_agent']['automation']['package_url'] = "#{mms_agent_download_base}/automation/mongodb-mms-automation-agent-manager-latest.x86_64.#{extention}" 47 | default['mongodb']['mms_agent']['backup']['package_url'] = "#{mms_agent_download_base}/backup/mongodb-mms-backup-agent-latest.x86_64.rpm" 48 | default['mongodb']['mms_agent']['monitoring']['package_url'] = "#{mms_agent_download_base}/monitoring/mongodb-mms-monitoring-agent-latest.x86_64.rpm" 49 | when 'debian' 50 | if platform?('ubuntu') && node['platform_version'].to_f >= 15.04 51 | default['mongodb']['mms_agent']['automation']['package_url'] = "#{mms_agent_download_base}/automation/mongodb-mms-automation-agent-manager_latest_amd64.ubuntu1604.deb" 52 | default['mongodb']['mms_agent']['backup']['package_url'] = "#{mms_agent_download_base}/backup/mongodb-mms-backup-agent_latest_amd64.ubuntu1604.deb" 53 | default['mongodb']['mms_agent']['monitoring']['package_url'] = "#{mms_agent_download_base}/monitoring/mongodb-mms-monitoring-agent_latest_amd64.ubuntu1604.deb" 54 | else 55 | default['mongodb']['mms_agent']['automation']['package_url'] = "#{mms_agent_download_base}/automation/mongodb-mms-automation-agent-manager_latest_amd64.deb" 56 | default['mongodb']['mms_agent']['backup']['package_url'] = "#{mms_agent_download_base}/backup/mongodb-mms-backup-agent_latest_amd64.deb" 57 | default['mongodb']['mms_agent']['monitoring']['package_url'] = "#{mms_agent_download_base}/monitoring/mongodb-mms-monitoring-agent_latest_amd64.deb" 58 | end 59 | end 60 | -------------------------------------------------------------------------------- /.github/copilot-instructions.md: -------------------------------------------------------------------------------- 1 | # Copilot Instructions for Sous Chefs Cookbooks 2 | 3 | ## Repository Overview 4 | 5 | **Chef cookbook** for managing software installation and configuration. Part of the Sous Chefs cookbook ecosystem. 6 | 7 | **Key Facts:** Ruby-based, Chef >= 16 required, supports various OS platforms (check metadata.rb, kitchen.yml and .github/workflows/ci.yml for which platforms to specifically test) 8 | 9 | ## Project Structure 10 | 11 | **Critical Paths:** 12 | - `recipes/` - Chef recipes for cookbook functionality (if this is a recipe-driven cookbook) 13 | - `resources/` - Custom Chef resources with properties and actions (if this is a resource-driven cookbook) 14 | - `spec/` - ChefSpec unit tests 15 | - `test/integration/` - InSpec integration tests (tests all platforms supported) 16 | - `test/cookbooks/` or `test/fixtures/` - Example cookbooks used during testing that show good examples of custom resource usage 17 | - `attributes/` - Configuration for recipe driven cookbooks (not applicable to resource cookbooks) 18 | - `libraries/` - Library helpers to assist with the cookbook. May contain multiple files depending on complexity of the cookbook. 19 | - `templates/` - ERB templates that may be used in the cookbook 20 | - `files/` - files that may be used in the cookbook 21 | - `metadata.rb`, `Berksfile` - Cookbook metadata and dependencies 22 | 23 | ## Build and Test System 24 | 25 | ### Environment Setup 26 | **MANDATORY:** Install Chef Workstation first - provides chef, berks, cookstyle, kitchen tools. 27 | 28 | ### Essential Commands (strict order) 29 | ```bash 30 | berks install # Install dependencies (always first) 31 | cookstyle # Ruby/Chef linting 32 | yamllint . # YAML linting 33 | markdownlint-cli2 '**/*.md' # Markdown linting 34 | chef exec rspec # Unit tests (ChefSpec) 35 | # Integration tests will be done via the ci.yml action. Do not run these. Only check the action logs for issues after CI is done running. 36 | ``` 37 | 38 | ### Critical Testing Details 39 | - **Kitchen Matrix:** Multiple OS platforms × software versions (check kitchen.yml for specific combinations) 40 | - **Docker Required:** Integration tests use Dokken driver 41 | - **CI Environment:** Set `CHEF_LICENSE=accept-no-persist` 42 | - **Full CI Runtime:** 30+ minutes for complete matrix 43 | 44 | ### Common Issues and Solutions 45 | - **Always run `berks install` first** - most failures are dependency-related 46 | - **Docker must be running** for kitchen tests 47 | - **Chef Workstation required** - no workarounds, no alternatives 48 | - **Test data bags needed** (optional for some cookbooks) in `test/integration/data_bags/` for convergence 49 | 50 | ## Development Workflow 51 | 52 | ### Making Changes 53 | 1. Edit recipes/resources/attributes/templates/libraries 54 | 2. Update corresponding ChefSpec tests in `spec/` 55 | 3. Also update any InSpec tests under test/integration 56 | 4. Ensure cookstyle and rspec passes at least. You may run `cookstyle -a` to automatically fix issues if needed. 57 | 5. Also always update all documentation found in README.md and any files under documentation/* 58 | 6. **Always update CHANGELOG.md** (required by Dangerfile) - Make sure this conforms with the Sous Chefs changelog standards. 59 | 60 | ### Pull Request Requirements 61 | - **PR description >10 chars** (Danger enforced) 62 | - **CHANGELOG.md entry** for all code changes 63 | - **Version labels** (major/minor/patch) required 64 | - **All linters must pass** (cookstyle, yamllint, markdownlint) 65 | - **Test updates** needed for code changes >5 lines and parameter changes that affect the code logic 66 | 67 | ## Chef Cookbook Patterns 68 | 69 | ### Resource Development 70 | - Custom resources in `resources/` with properties and actions 71 | - Include comprehensive ChefSpec tests for all actions 72 | - Follow Chef resource DSL patterns 73 | 74 | ### Recipe Conventions 75 | - Use `include_recipe` for modularity 76 | - Handle platforms with `platform_family?` conditionals 77 | - Use encrypted data bags for secrets (passwords, SSL certs) 78 | - Leverage attributes for configuration with defaults 79 | 80 | ### Testing Approach 81 | - **ChefSpec (Unit):** Mock dependencies, test recipe logic in `spec/` 82 | - **InSpec (Integration):** Verify actual system state in `test/integration/inspec/` - InSpec files should contain proper inspec.yml and controls directories so that it could be used by other suites more easily. 83 | - One test file per recipe, use standard Chef testing patterns 84 | 85 | ## Trust These Instructions 86 | 87 | These instructions are validated for Sous Chefs cookbooks. **Do not search for build instructions** unless information here fails. 88 | 89 | **Error Resolution Checklist:** 90 | 1. Verify Chef Workstation installation 91 | 2. Confirm `berks install` completed successfully 92 | 3. Ensure Docker is running for integration tests 93 | 4. Check for missing test data dependencies 94 | 95 | The CI system uses these exact commands - following them matches CI behavior precisely. 96 | -------------------------------------------------------------------------------- /attributes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: sc-mongodb 3 | # Attributes:: default 4 | # 5 | # Copyright:: 2010, edelight GmbH 6 | # 7 | # Licensed under the Apache License, Version 2.0 (the "License"); 8 | # you may not use this file except in compliance with the License. 9 | # You may obtain a copy of the License at 10 | # 11 | # http://www.apache.org/licenses/LICENSE-2.0 12 | # 13 | # Unless required by applicable law or agreed to in writing, software 14 | # distributed under the License is distributed on an "AS IS" BASIS, 15 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 | # See the License for the specific language governing permissions and 17 | # limitations under the License. 18 | # 19 | 20 | # cluster identifier 21 | default['mongodb']['client_roles'] = [] 22 | default['mongodb']['cluster_name'] = nil 23 | default['mongodb']['shard_name'] = 'default' 24 | 25 | # replica options 26 | default['mongodb']['replica_arbiter_only'] = false 27 | default['mongodb']['replica_build_indexes'] = true 28 | default['mongodb']['replica_hidden'] = false 29 | default['mongodb']['replica_slave_delay'] = 0 30 | default['mongodb']['replica_priority'] = 1 31 | default['mongodb']['replica_tags'] = {} 32 | default['mongodb']['replica_votes'] = 1 33 | 34 | default['mongodb']['auto_configure']['replicaset'] = true 35 | default['mongodb']['auto_configure']['sharding'] = true 36 | 37 | # don't use the node's fqdn, but this url instead; something like 'ec2-x-y-z-z.aws.com' or 'cs1.domain.com' (no port) 38 | # if not provided, will fall back to the FQDN 39 | default['mongodb']['configserver_url'] = nil 40 | 41 | default['mongodb']['root_group'] = 'root' 42 | default['mongodb']['user'] = 'mongodb' 43 | default['mongodb']['group'] = 'mongodb' 44 | 45 | default['mongodb']['init_dir'] = '/etc/init.d' 46 | default['mongodb']['init_script_template'] = 'debian-mongodb.init.erb' 47 | default['mongodb']['sysconfig_file']['mongod'] = '/etc/default/mongodb' 48 | default['mongodb']['sysconfig_file']['mongos'] = '/etc/default/mongos' 49 | default['mongodb']['sysconfig_file']['template'] = 'mongodb.sysconfig.erb' 50 | 51 | default['mongodb']['dbconfig_file']['template'] = 'mongodb.conf.erb' 52 | default['mongodb']['dbconfig_file']['mongod'] = '/etc/mongod.conf' 53 | default['mongodb']['dbconfig_file']['mongos'] = '/etc/mongos.conf' 54 | 55 | default['mongodb']['package_name'] = 'mongodb' 56 | default['mongodb']['package_version'] = '7.0.2' 57 | 58 | default['mongodb']['default_init_name'] = 'mongod' 59 | default['mongodb']['instance_name']['mongod'] = 'mongod' 60 | default['mongodb']['instance_name']['mongos'] = 'mongos' 61 | 62 | case node['platform_family'] # rubocop:disable Style/ConditionalAssignment 63 | when 'debian' 64 | # this options lets us bypass complaint of pre-existing init file 65 | # necessary until upstream fixes ENABLE_MONGOD/DB flag 66 | default['mongodb']['packager_options'] = '-o Dpkg::Options::="--force-confold" --force-yes' 67 | when 'rhel' 68 | # Add --nogpgcheck option when package is signed 69 | # see: https://jira.mongodb.org/browse/SERVER-8770 70 | default['mongodb']['packager_options'] = '--nogpgcheck' 71 | else 72 | default['mongodb']['packager_options'] = '' 73 | end 74 | 75 | # this option can be "mongodb-org" or "none" 76 | default['mongodb']['install_method'] = 'mongodb-org' 77 | 78 | default['mongodb']['is_replicaset'] = nil 79 | default['mongodb']['is_shard'] = nil 80 | default['mongodb']['is_configserver'] = nil 81 | 82 | default['mongodb']['reload_action'] = 'restart' # or "nothing" 83 | 84 | case node['platform_family'] 85 | when 'rhel', 'fedora' 86 | # determine the package name 87 | # from http://rpm.pbone.net/index.php3?stat=3&limit=1&srodzaj=3&dl=40&search=mongodb 88 | # verified for RHEL5,6 Fedora 18,19 89 | default['mongodb']['package_name'] = 'mongodb-org' 90 | default['mongodb']['sysconfig_file']['mongod'] = '/etc/sysconfig/mongodb' 91 | default['mongodb']['user'] = 'mongod' 92 | default['mongodb']['group'] = 'mongod' 93 | default['mongodb']['init_script_template'] = 'redhat-mongodb.init.erb' 94 | when 'debian' 95 | if platform?('ubuntu') 96 | default['mongodb']['repo'] = 'http://repo.mongodb.org/apt/ubuntu' 97 | 98 | # Upstart 99 | if node['platform_version'].to_f < 15.04 100 | default['mongodb']['init_dir'] = '/etc/init/' 101 | default['mongodb']['init_script_template'] = 'debian-mongodb.upstart.erb' 102 | end 103 | elsif platform?('debian') 104 | default['mongodb']['repo'] = 'http://repo.mongodb.org/apt/debian' 105 | end 106 | else 107 | Chef::Log.error("Unsupported Platform Family: #{node['platform_family']}") 108 | end 109 | 110 | default['mongodb']['template_cookbook'] = 'sc-mongodb' 111 | 112 | default['mongodb']['key_file_content'] = nil 113 | 114 | # install the mongo and bson_ext ruby gems at compile time to make them globally available 115 | # TODO: remove bson_ext once mongo gem supports bson >= 2 116 | default['mongodb']['ruby_gems'] = { 117 | mongo: '~> 1.12', 118 | bson_ext: nil, 119 | } 120 | -------------------------------------------------------------------------------- /recipes/install.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: mongodb 3 | # Recipe:: install 4 | # 5 | # Copyright:: 2011, edelight GmbH 6 | # Authors: 7 | # Markus Korn 8 | # 9 | # Copyright:: 2016-2017, Grant Ridder 10 | # 11 | # Licensed under the Apache License, Version 2.0 (the "License"); 12 | # you may not use this file except in compliance with the License. 13 | # You may obtain a copy of the License at 14 | # 15 | # http://www.apache.org/licenses/LICENSE-2.0 16 | # 17 | # Unless required by applicable law or agreed to in writing, software 18 | # distributed under the License is distributed on an "AS IS" BASIS, 19 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 20 | # See the License for the specific language governing permissions and 21 | # limitations under the License. 22 | # 23 | 24 | # install the mongodb org repo if necessary 25 | include_recipe 'sc-mongodb::mongodb_org_repo' if node['mongodb']['install_method'] == 'mongodb-org' 26 | 27 | # TODO: still need all of the tools? 28 | # yum_package[autoconf, bison, flex, gcc, gcc-c++, gettext, kernel-devel, make, m4, ncurses-devel, patch] 29 | build_essential 'build-tools' 30 | 31 | config_type = node['mongodb']['is_mongos'] ? 'mongos' : 'mongod' 32 | 33 | config = node['mongodb']['config'][config_type] 34 | dbconfig_file = node['mongodb']['dbconfig_file'][config_type] 35 | sysconfig_file = node['mongodb']['sysconfig_file'][config_type] 36 | 37 | # prevent-install defaults, but don't overwrite 38 | file "#{sysconfig_file} install" do 39 | path sysconfig_file 40 | content 'ENABLE_MONGODB=no' 41 | group node['mongodb']['root_group'] 42 | owner 'root' 43 | mode '0644' 44 | action :create_if_missing 45 | end 46 | 47 | # just-in-case config file drop 48 | template "#{dbconfig_file} install" do 49 | path dbconfig_file 50 | cookbook node['mongodb']['template_cookbook'] 51 | source node['mongodb']['dbconfig_file']['template'] 52 | group node['mongodb']['root_group'] 53 | owner 'root' 54 | mode '0644' 55 | variables( 56 | config: config 57 | ) 58 | helpers MongoDBConfigHelpers 59 | action :create_if_missing 60 | end 61 | 62 | # this package required by init script 63 | package 'netcat' do 64 | only_if { platform_family?('debian') } 65 | end 66 | 67 | # and we install our own init file 68 | if platform?('ubuntu') && node['platform_version'].to_f < 15.04 69 | init_file = File.join(node['mongodb']['init_dir'], "#{node['mongodb']['default_init_name']}.conf") 70 | mode = '0644' 71 | else 72 | init_file = File.join(node['mongodb']['init_dir'], node['mongodb']['default_init_name']) 73 | mode = '0755' 74 | end 75 | 76 | template "#{init_file} install" do 77 | path init_file 78 | cookbook node['mongodb']['template_cookbook'] 79 | source node['mongodb']['init_script_template'] 80 | group node['mongodb']['root_group'] 81 | owner 'root' 82 | mode mode 83 | variables( 84 | provides: 'mongod', 85 | dbconfig_file: dbconfig_file, 86 | sysconfig_file: sysconfig_file, 87 | ulimit: node['mongodb']['ulimit'], 88 | bind_ip: config['net']['bindIp'], 89 | port: config['net']['port'] 90 | ) 91 | action :create_if_missing 92 | end 93 | 94 | service 'mongod' do 95 | supports start: true, stop: true, restart: true, status: true 96 | action :enable 97 | end 98 | 99 | # Adjust the version number for RHEL style if needed 100 | package_version = case node['platform_family'] 101 | when 'rhel' 102 | if platform?('amazon') 103 | "#{node['mongodb']['package_version']}-1.amzn1" 104 | else 105 | "#{node['mongodb']['package_version']}-1.el#{node['platform_version'].to_i}" 106 | end 107 | when 'fedora' 108 | "#{node['mongodb']['package_version']}-1.el7" 109 | else 110 | node['mongodb']['package_version'] 111 | end 112 | 113 | # Install 114 | package node['mongodb']['package_name'] do 115 | options node['mongodb']['packager_options'] 116 | action :install 117 | version package_version 118 | not_if { node['mongodb']['install_method'] == 'none' } 119 | end 120 | 121 | # Change needed so that updates work properly on debian based systems 122 | if platform_family?('debian') 123 | deb_pkg_suffixes = %w( 124 | server 125 | shell 126 | tools 127 | mongos 128 | ) 129 | deb_pkg_suffixes.each do |suffix| 130 | package "#{node['mongodb']['package_name']}-#{suffix}" do 131 | options node['mongodb']['packager_options'] 132 | action :install 133 | version package_version 134 | not_if { node['mongodb']['install_method'] == 'none' } 135 | end 136 | end 137 | end 138 | 139 | # Create keyFile if specified 140 | key_file_content = node['mongodb']['key_file_content'] 141 | 142 | if key_file_content 143 | file config['security']['keyFile'] do 144 | owner node['mongodb']['user'] 145 | group node['mongodb']['group'] 146 | mode '0600' 147 | backup false 148 | content key_file_content 149 | end 150 | end 151 | 152 | node.default['mongodb']['config'][config_type]['security']['keyFile'] = nil if key_file_content.nil? 153 | 154 | # create directory for pidfile 155 | directory File.dirname(node['mongodb']['config']['mongod']['processManagement']['pidFilePath']) do 156 | owner node['mongodb']['user'] 157 | group node['mongodb']['group'] 158 | mode '0755' 159 | end 160 | -------------------------------------------------------------------------------- /kitchen.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: vagrant 4 | 5 | provisioner: 6 | name: chef_zero 7 | 8 | verifier: 9 | name: inspec 10 | 11 | client_rb: 12 | treat_deprecation_warnings_as_errors: true 13 | 14 | platforms: 15 | - name: centos-7 16 | # - name: centos-8 17 | - name: debian-9 18 | # - name: debian-10 19 | # - name: fedora-latest 20 | - name: ubuntu-16.04 21 | # - name: ubuntu-18.04 22 | # - name: ubuntu-20.04 23 | 24 | suites: 25 | - name: default 26 | run_list: 27 | - "recipe[sc-mongodb]" 28 | 29 | - name: replicaset 30 | run_list: 31 | - "recipe[sc-mongodb::replicaset]" 32 | attributes: 33 | mongodb: 34 | cluster_name: kitchen 35 | config: 36 | mongod: 37 | replication: 38 | replSetName: kitchen 39 | 40 | - name: configserver 41 | run_list: 42 | - "recipe[sc-mongodb::configserver]" 43 | 44 | - name: mongos 45 | run_list: 46 | - "recipe[sc-mongodb::configserver]" 47 | - "recipe[sc-mongodb::mongos]" 48 | attributes: 49 | mongodb: 50 | cluster_name: default 51 | config: 52 | mongod: 53 | net: 54 | # use this port since mongos will use 27017 instead 55 | port: 27019 56 | mongos: 57 | sharding: 58 | configDB: 127.0.0.1:27019 59 | 60 | - name: mms_agent 61 | run_list: 62 | - recipe[sc-mongodb::mms_automation_agent] 63 | - recipe[sc-mongodb::mms_backup_agent] 64 | - recipe[sc-mongodb::mms_monitoring_agent] 65 | attributes: 66 | mongodb: 67 | mms_agent: 68 | api_key: randomkey 69 | backup: 70 | config: 71 | mmsGroupId: randomgroup 72 | automation: 73 | config: 74 | mmsGroupId: randomgroup 75 | monitoring: 76 | config: 77 | mmsGroupId: randomgroup 78 | excludes: 79 | - debian-7.11 80 | - debian-8.6 81 | 82 | - name: user_management 83 | run_list: 84 | - "recipe[sc-mongodb::default]" 85 | - "recipe[sc-mongodb::user_management]" 86 | attributes: 87 | mongodb: 88 | config: 89 | auth: true 90 | users: 91 | - username: kitchen 92 | password: blah123 93 | roles: 94 | - read 95 | database: admin 96 | includes: 97 | # Only need to test this on one OS since this is 98 | # purely to test mongo ruby driver code 99 | - centos-7 100 | 101 | - name: user_management_v2 102 | run_list: 103 | - recipe[sc-mongodb::default] 104 | - recipe[sc-mongodb::user_management] 105 | attributes: 106 | mongodb: 107 | config: 108 | auth: true 109 | ruby_gems: 110 | mongo: ~> 2.0 111 | users: 112 | - username: kitchen 113 | password: blah123 114 | roles: 115 | - read 116 | database: admin 117 | includes: 118 | # Only need to test this on one OS since this is 119 | # purely to test mongo ruby driver code 120 | - centos-7 121 | 122 | - name: user_management_v2_delete 123 | run_list: 124 | - recipe[sc-mongodb::default] 125 | - recipe[sc-mongodb::user_management] 126 | - recipe[mongodb_spec::user_delete] 127 | attributes: 128 | mongodb: 129 | config: 130 | auth: true 131 | ruby_gems: 132 | mongo: ~> 2.0 133 | users: 134 | - username: kitchen 135 | password: blah123 136 | roles: 137 | - read 138 | database: admin 139 | includes: 140 | # Only need to test this on one OS since this is 141 | # purely to test mongo ruby driver code 142 | - centos-7 143 | 144 | - name: replicaset1 145 | driver: 146 | network: 147 | private_network: 148 | ip: 192.168.10.10 149 | run_list: 150 | - recipe[mongodb_spec::set_hostname] 151 | - recipe[sc-mongodb::replicaset] 152 | attributes: 153 | mongodb: 154 | auto_configure: 155 | replicaset: false 156 | cluster_name: kitchen 157 | config: 158 | mongod: 159 | replication: 160 | replSetName: kitchen 161 | includes: 162 | # Only need to test this on one OS since this is 163 | # purely to test mongo ruby driver code 164 | - centos-7 165 | 166 | - name: replicaset2 167 | driver: 168 | network: 169 | private_network: 170 | ip: 192.168.10.20 171 | run_list: 172 | - recipe[mongodb_spec::set_hostname] 173 | - recipe[sc-mongodb::replicaset] 174 | attributes: 175 | mongodb: 176 | auto_configure: 177 | replicaset: false 178 | cluster_name: kitchen 179 | config: 180 | mongod: 181 | replication: 182 | replSetName: kitchen 183 | includes: 184 | # Only need to test this on one OS since this is 185 | # purely to test mongo ruby driver code 186 | - centos-7 187 | 188 | - name: replicaset3 189 | driver: 190 | network: 191 | private_network: 192 | ip: 192.168.10.30 193 | run_list: 194 | - recipe[mongodb_spec::set_hostname] 195 | - recipe[sc-mongodb::replicaset] 196 | attributes: 197 | mongodb: 198 | auto_configure: 199 | replicaset: true 200 | cluster_name: kitchen 201 | config: 202 | mongod: 203 | replication: 204 | replSetName: kitchen 205 | includes: 206 | # Only need to test this on one OS since this is 207 | # purely to test mongo ruby driver code 208 | - centos-7 209 | 210 | - name: shard1-n1 211 | driver: 212 | network: 213 | private_network: 214 | ip: 192.168.10.10 215 | run_list: 216 | - recipe[mongodb_spec::set_hostname] 217 | - recipe[sc-mongodb::shard] 218 | attributes: 219 | mongodb: 220 | auto_configure: 221 | replicaset: false 222 | cluster_name: kitchen1 223 | config: 224 | mongod: 225 | replication: 226 | replSetName: kitchen1 227 | is_replicaset: true 228 | includes: 229 | # Only need to test this on one OS since this is 230 | # purely to test mongo ruby driver code 231 | - centos-7 232 | 233 | - name: shard1-n2 234 | driver: 235 | network: 236 | private_network: 237 | ip: 192.168.10.20 238 | run_list: 239 | - recipe[mongodb_spec::set_hostname] 240 | - recipe[sc-mongodb::shard] 241 | attributes: 242 | mongodb: 243 | auto_configure: 244 | replicaset: false 245 | cluster_name: kitchen1 246 | config: 247 | mongod: 248 | replication: 249 | replSetName: kitchen1 250 | is_replicaset: true 251 | includes: 252 | # Only need to test this on one OS since this is 253 | # purely to test mongo ruby driver code 254 | - centos-7 255 | 256 | - name: shard1-n3 257 | driver: 258 | network: 259 | private_network: 260 | ip: 192.168.10.30 261 | run_list: 262 | - recipe[mongodb_spec::set_hostname] 263 | - recipe[sc-mongodb::shard] 264 | attributes: 265 | mongodb: 266 | auto_configure: 267 | replicaset: true 268 | cluster_name: kitchen1 269 | config: 270 | mongod: 271 | replication: 272 | replSetName: kitchen1 273 | is_replicaset: true 274 | includes: 275 | # Only need to test this on one OS since this is 276 | # purely to test mongo ruby driver code 277 | - centos-7 278 | 279 | - name: shard-mongos 280 | driver: 281 | network: 282 | private_network: 283 | ip: 192.168.10.99 284 | run_list: 285 | - recipe[mongodb_spec::set_hostname] 286 | - recipe[sc-mongodb::configserver] 287 | - recipe[sc-mongodb::mongos] 288 | attributes: 289 | mongodb: 290 | cluster_name: kitchen1 291 | config: 292 | mongod: 293 | net: 294 | # use this port since mongos will use 27017 instead 295 | port: 27019 296 | sharding: 297 | clusterRole: configsvr 298 | mongos: 299 | sharding: 300 | configDB: mongos:27019 301 | sharded_collections: 302 | test.testing: _id 303 | includes: 304 | # Only need to test this on one OS since this is 305 | # purely to test mongo ruby driver code 306 | - centos-7 307 | -------------------------------------------------------------------------------- /templates/default/debian-mongodb.init.erb: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # 3 | # init.d script with LSB support. 4 | # 5 | # Copyright (c) 2007 Javier Fernandez-Sanguino 6 | # Copyright (c) 2011 edelight GmbH 7 | # Author: Markus Korn 8 | # 9 | # This is free software; you may redistribute it and/or modify 10 | # it under the terms of the GNU General Public License as 11 | # published by the Free Software Foundation; either version 2, 12 | # or (at your option) any later version. 13 | # 14 | # This is distributed in the hope that it will be useful, but 15 | # WITHOUT ANY WARRANTY; without even the implied warranty of 16 | # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | # GNU General Public License for more details. 18 | # 19 | # You should have received a copy of the GNU General Public License with 20 | # the Debian operating system, in /usr/share/common-licenses/GPL; if 21 | # not, write to the Free Software Foundation, Inc., 59 Temple Place, 22 | # Suite 330, Boston, MA 02111-1307 USA 23 | # 24 | ### BEGIN INIT INFO 25 | # Provides: <%= @provides %> 26 | # Required-Start: $network $local_fs $remote_fs 27 | # Required-Stop: $network $local_fs $remote_fs 28 | # Should-Start: $named 29 | # Should-Stop: 30 | # Default-Start: 2 3 4 5 31 | # Default-Stop: 0 1 6 32 | # Short-Description: Start/stop <%= @provides %> An object/document-origented database 33 | # Description: MongoDB is a high-performance, open source, schema-free 34 | # document-oriented data store that's easy to deploy, manage 35 | # and use. It's network accessible, written in C++ and offers 36 | # the following features: 37 | # 38 | # * Collection oriented storage - easy storage of object- 39 | # style data 40 | # * Full index support, including on inner objects 41 | # * Query profiling 42 | # * Replication and fail-over support 43 | # * Efficient storage of binary data including large 44 | # objects (e.g. videos) 45 | # * Auto-sharding for cloud-level scalability (Q209) 46 | # 47 | # High performance, scalability, and reasonable depth of 48 | # functionality are the goals for the project. 49 | ### END INIT INFO 50 | # 51 | NAME=<%= @provides %> 52 | 53 | PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin 54 | DESC=database 55 | PIDFILE=/var/run/mongodb/$NAME.pid 56 | ENABLE_MONGODB=yes 57 | SYSCONFIG=<%= @sysconfig_file %> 58 | 59 | # this should get removed at some point for more shell agnostic init script 60 | ulimit -f <%= @ulimit['fsize'] %> 61 | ulimit -t <%= @ulimit['cpu'] %> 62 | ulimit -v <%= @ulimit['as'] %> 63 | ulimit -n <%= @ulimit['nofile'] %> 64 | ulimit -m <%= @ulimit['rss'] %> 65 | ulimit -u <%= @ulimit['nproc'] %> 66 | 67 | # Include mongodb defaults if available 68 | if [ -f $SYSCONFIG ] ; then 69 | . $SYSCONFIG 70 | fi 71 | 72 | # Handle NUMA access to CPUs (SERVER-3574) 73 | # This verifies the existence of numactl as well as testing that the command works 74 | NUMACTL_ARGS="--interleave=all" 75 | if which numactl >/dev/null 2>/dev/null && numactl $NUMACTL_ARGS ls / >/dev/null 2>/dev/null 76 | then 77 | NUMACTL="`which numactl` -- $NUMACTL_ARGS" 78 | DAEMON_OPTS="$DAEMON_OPTS" 79 | else 80 | NUMACTL="" 81 | DAEMON_OPTS="-- $DAEMON_OPTS" 82 | fi 83 | 84 | if test ! -x $DAEMON; then 85 | echo "Could not find $DAEMON" 86 | exit 0 87 | fi 88 | 89 | if test "x$ENABLE_MONGODB" != "xyes"; then 90 | exit 0 91 | fi 92 | 93 | . /lib/lsb/init-functions 94 | 95 | STARTTIME=1 96 | DIETIME=10 # Time to wait for the server to die, in seconds 97 | # If this value is set too low you might not 98 | # let some servers to die gracefully and 99 | # 'restart' will not work 100 | 101 | DAEMON_USER=${DAEMON_USER:-mongodb} 102 | 103 | # debugging 104 | echo "** Running $NAME ($DAEMON $DAEMON_OPTS)" 105 | 106 | set -e 107 | 108 | 109 | running_pid() { 110 | # Check if a given process pid's cmdline matches a given name 111 | pid=$1 112 | name=$2 113 | [ -z "$pid" ] && return 1 114 | [ ! -d /proc/$pid ] && return 1 115 | cmd=`cat /proc/$pid/cmdline | tr "\000" "\n"|head -n 1 |cut -d : -f 1` 116 | # Is this the expected server 117 | [ "$cmd" != "$name" ] && return 1 118 | return 0 119 | } 120 | 121 | running() { 122 | # Check if the process is running looking at /proc 123 | # (works for all users) 124 | 125 | # No pidfile, probably no daemon present 126 | [ ! -f "$PIDFILE" ] && return 1 127 | pid=`cat $PIDFILE` 128 | running_pid $pid $DAEMON || return 1 129 | for i in `seq 1 20`; do 130 | nc -z <%= @bind_ip %> <%= @port %> && return 0 131 | echo -n "." 132 | sleep 15 133 | done 134 | return 1 135 | } 136 | 137 | start_server() { 138 | # Start the process using the wrapper 139 | start-stop-daemon --background --start --pidfile $PIDFILE \ 140 | --chuid $DAEMON_USER \ 141 | --exec $NUMACTL $DAEMON $DAEMON_OPTS 142 | errcode=$? 143 | return $errcode 144 | } 145 | 146 | stop_server() { 147 | # Stop the process using the wrapper 148 | start-stop-daemon --stop --oknodo --pidfile $PIDFILE \ 149 | --retry 300 --remove-pidfile 150 | errcode=$? 151 | return $errcode 152 | } 153 | 154 | force_stop() { 155 | # Force the process to die killing it manually 156 | [ ! -e "$PIDFILE" ] && return 157 | if running ; then 158 | kill -15 $pid 159 | # Is it really dead? 160 | sleep "$DIETIME"s 161 | if running ; then 162 | kill -9 $pid 163 | sleep "$DIETIME"s 164 | if running ; then 165 | echo "Cannot kill $NAME (pid=$pid)!" 166 | exit 1 167 | fi 168 | fi 169 | fi 170 | rm -f $PIDFILE 171 | } 172 | 173 | 174 | case "$1" in 175 | start) 176 | log_daemon_msg "Starting $DESC" "$NAME" 177 | # Check if it's running first 178 | if running ; then 179 | log_progress_msg "apparently already running" 180 | log_end_msg 0 181 | exit 0 182 | fi 183 | if start_server ; then 184 | # NOTE: Some servers might die some time after they start, 185 | # this code will detect this issue if STARTTIME is set 186 | # to a reasonable value 187 | [ -n "$STARTTIME" ] && sleep $STARTTIME # Wait some time 188 | if running ; then 189 | # It's ok, the server started and is running 190 | log_end_msg 0 191 | else 192 | # It is not running after we did start 193 | log_end_msg 1 194 | fi 195 | else 196 | # Either we could not start it 197 | log_end_msg 1 198 | fi 199 | ;; 200 | stop) 201 | log_daemon_msg "Stopping $DESC" "$NAME" 202 | if running ; then 203 | # Only stop the server if we see it running 204 | errcode=0 205 | stop_server || errcode=$? 206 | log_end_msg $errcode 207 | else 208 | # If it's not running don't do anything 209 | log_progress_msg "apparently not running" 210 | log_end_msg 0 211 | exit 0 212 | fi 213 | ;; 214 | force-stop) 215 | # First try to stop gracefully the program 216 | $0 stop 217 | if running; then 218 | # If it's still running try to kill it more forcefully 219 | log_daemon_msg "Stopping (force) $DESC" "$NAME" 220 | errcode=0 221 | force_stop || errcode=$? 222 | log_end_msg $errcode 223 | fi 224 | ;; 225 | restart|force-reload) 226 | log_daemon_msg "Restarting $DESC" "$NAME" 227 | errcode=0 228 | stop_server || errcode=$? 229 | # Wait some sensible amount, some server need this 230 | [ -n "$DIETIME" ] && sleep $DIETIME 231 | start_server || errcode=$? 232 | [ -n "$STARTTIME" ] && sleep $STARTTIME 233 | running || errcode=$? 234 | log_end_msg $errcode 235 | ;; 236 | status) 237 | 238 | log_daemon_msg "Checking status of $DESC" "$NAME" 239 | if running ; then 240 | log_progress_msg "running" 241 | log_end_msg 0 242 | else 243 | log_progress_msg "apparently not running" 244 | log_end_msg 1 245 | exit 1 246 | fi 247 | ;; 248 | # MongoDB can't reload its configuration. 249 | reload) 250 | log_warning_msg "Reloading $NAME daemon: not implemented, as the daemon" 251 | log_warning_msg "cannot re-read the config file (use restart)." 252 | ;; 253 | 254 | *) 255 | N=/etc/init.d/$NAME 256 | echo "Usage: $N {start|stop|force-stop|restart|force-reload|status}" >&2 257 | exit 1 258 | ;; 259 | esac 260 | 261 | exit 0 262 | 263 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "[]" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright [yyyy] [name of copyright owner] 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # sc-mongodb Cookbook CHANGELOG 2 | 3 | Standardise files with files in sous-chefs/repo-management 4 | Standardise files with files in sous-chefs/repo-management 5 | 6 | ## [5.1.26](https://github.com/sous-chefs/sc-mongodb/compare/5.1.25...v5.1.26) (2025-10-16) 7 | 8 | 9 | ### Bug Fixes 10 | 11 | * **ci:** Update workflows to use release pipeline ([#330](https://github.com/sous-chefs/sc-mongodb/issues/330)) ([8f524b1](https://github.com/sous-chefs/sc-mongodb/commit/8f524b1ae5f12038e6a68b6bfb6e3c39c724e238)) 12 | 13 | ## 5.1.24 - *2024-11-18* 14 | 15 | Standardise files with files in sous-chefs/repo-management 16 | 17 | Standardise files with files in sous-chefs/repo-management 18 | 19 | Standardise files with files in sous-chefs/repo-management 20 | 21 | Standardise files with files in sous-chefs/repo-management 22 | 23 | Standardise files with files in sous-chefs/repo-management 24 | 25 | ## 5.1.22 - *2024-05-02* 26 | 27 | Update default version to 7.0.2 28 | 29 | ## 5.1.20 - *2023-10-29* 30 | 31 | Update platforms in Test Kitchen 32 | 33 | ## 5.1.15 - *2023-06-01* 34 | 35 | * Add support for Rocky Linux 36 | 37 | ## 5.1.12 - *2023-04-07* 38 | 39 | Standardise files with files in sous-chefs/repo-management 40 | 41 | ## 5.1.9 - *2023-04-01* 42 | 43 | Standardise files with files in sous-chefs/repo-management 44 | 45 | ## 5.1.8 - *2023-03-20* 46 | 47 | Standardise files with files in sous-chefs/repo-management 48 | 49 | ## 5.1.7 - *2023-03-15* 50 | 51 | Standardise files with files in sous-chefs/repo-management 52 | 53 | ## 5.1.6 - *2023-02-23* 54 | 55 | Standardise files with files in sous-chefs/repo-management 56 | 57 | ## 5.1.4 - *2023-02-16* 58 | 59 | Standardise files with files in sous-chefs/repo-management 60 | 61 | Standardise files with files in sous-chefs/repo-management 62 | 63 | ## 5.1.2 - *2022-12-11* 64 | 65 | Standardise files with files in sous-chefs/repo-management 66 | 67 | Standardise files with files in sous-chefs/repo-management 68 | 69 | ## 5.1.1 - *2022-02-17* 70 | 71 | * Standardise files with files in sous-chefs/repo-management 72 | 73 | ## 5.1.0 - *2022-02-10* 74 | 75 | * Use chef_gem for replicaset recipe 76 | 77 | ## 5.0.1 - *2022-02-08* 78 | 79 | * Remove delivery folder 80 | 81 | ## 5.0.0 - *2022-01-07* 82 | 83 | * Require Chef >= 15.3 for unified_mode 84 | * resolved cookstyle error: resources/agent.rb:1:1 refactor: `Chef/Deprecations/ResourceWithoutUnifiedTrue` 85 | * resolved cookstyle error: resources/user.rb:1:1 refactor: `Chef/Deprecations/ResourceWithoutUnifiedTrue` 86 | 87 | ## 4.1.3 - *2022-01-06* 88 | 89 | * resolved regression in custom resource names (#272) 90 | 91 | ## 4.1.2 - *2021-08-30* 92 | 93 | * Standardise files with files in sous-chefs/repo-management 94 | 95 | ## 4.1.1 - *2021-06-01* 96 | 97 | * resolved cookstyle error: definitions/mongodb.rb:53:12 convention: `Style/RedundantParentheses` 98 | * resolved cookstyle error: libraries/mongodb_config_helpers.rb:21:5 warning: `ChefDeprecations/UseYamlDump` 99 | * resolved cookstyle error: libraries/user.rb:144:1 convention: `Layout/TrailingWhitespace` 100 | * resolved cookstyle error: libraries/user.rb:144:15 convention: `Style/RedundantBegin` 101 | * resolved cookstyle error: libraries/user.rb:145:13 convention: `Layout/IndentationWidth` 102 | * resolved cookstyle error: libraries/user.rb:146:17 convention: `Layout/IndentationConsistency` 103 | * resolved cookstyle error: libraries/user.rb:147:17 convention: `Layout/IndentationConsistency` 104 | * resolved cookstyle error: libraries/user.rb:149:17 convention: `Layout/IndentationConsistency` 105 | * resolved cookstyle error: libraries/user.rb:166:1 convention: `Layout/TrailingWhitespace` 106 | * resolved cookstyle error: libraries/user.rb:167:1 convention: `Layout/EmptyLines` 107 | * resolved cookstyle error: libraries/user.rb:168:15 convention: `Layout/IndentationConsistency` 108 | * resolved cookstyle error: libraries/user.rb:168:17 convention: `Layout/IndentationConsistency` 109 | * resolved cookstyle error: libraries/user.rb:169:15 convention: `Layout/IndentationConsistency` 110 | * resolved cookstyle error: libraries/user.rb:169:17 convention: `Layout/IndentationConsistency` 111 | * Resolve cookstyle issues 112 | * Comment out ChefSpec until defintions are converted to custom resources 113 | * Disable platforms that do not currently work 114 | * Remove testing on EL6 115 | 116 | ## 4.0.1 117 | 118 | * resolved cookstyle error: recipes/mongodb_org_repo.rb:31:53 convention: `Layout/TrailingWhitespace` 119 | * resolved cookstyle error: recipes/mongodb_org_repo.rb:31:54 refactor: `ChefModernize/FoodcriticComments` 120 | 121 | ## 4.0.0 122 | 123 | * Minimum Chef version 14 124 | * resolved cookstyle error: libraries/user.rb:116:15 refactor: `ChefCorrectness/ChefApplicationFatal` 125 | * resolved cookstyle error: libraries/user.rb:138:11 refactor: `ChefCorrectness/ChefApplicationFatal` 126 | * resolved cookstyle error: libraries/user.rb:172:13 refactor: `ChefCorrectness/ChefApplicationFatal` 127 | * resolved cookstyle error: libraries/user.rb:212:13 refactor: `ChefCorrectness/ChefApplicationFatal` 128 | * resolved cookstyle error: libraries/user.rb:222:15 refactor: `ChefCorrectness/ChefApplicationFatal` 129 | * resolved cookstyle error: metadata.rb:11:1 refactor: `ChefModernize/UnnecessaryDependsChef14` 130 | 131 | ## v3.0.0 132 | 133 | * Update CI tooling to actions 134 | * mongodb.rb (cluster_up_to_date?): optimize 135 | * Fix packages version pin for Debian 136 | * Fix user creation error with MongoDB 4.x 137 | * Simplify the platform checks 138 | * Remove specs for EOL platforms 139 | * Remove deprecated long_description metadata 140 | * Remove if respond_to? usage in metadata.rb 141 | * Remove the ChefSpec converge report 142 | 143 | ## v2.0.0 144 | 145 | * custom resource user resource 146 | * update CI tooling 147 | 148 | ## v1.2.0 149 | 150 | * Fixes issue where on debain based systems mongo is not updated if version is increased 151 | * Correct attribute usage (#186) 152 | * Fix Invalid Keyfile Option (#191) 153 | * Fix for issue where mongo would not upgrade on debian based systems (#181) 154 | * Use pgp keyfiles for debian packages (#193) 155 | 156 | ## v1.0.1 Changes 157 | 158 | * Refactor replicaset configuration 159 | 160 | ## v1.0.0 Changes (Released 2017-05-23) 161 | 162 | * WARNING:* This is a rewrite that contains many backwards incompatable changes. Many attributes have changed defaults and/or the attribute key itself. 163 | 164 | * Drop support for 10gen repo and default to mongo-org repo install 165 | * Remove Chef 10 and 11 support 166 | * Update Mongo default version to 3.2.10 167 | * Drop support for Mongo < 3.0 168 | * Convert all test-kitchen verification to inspec 169 | * Change `node['mongodb']['config']` to have `mongos` and `mongod` keys before actual config setting 170 | * Update apt and yum repo 171 | * Update MMS agent install to deb/rpm packages and add `mongodb_agent` resource 172 | * Fixup relicaset and shard helpers 173 | 174 | ## v0.18.1 Changes 175 | 176 | * Fix cookbook name in attributes file 177 | 178 | ## v0.18.0 Changes 179 | 180 | * Rename cookbook from mongodb to sc-mongodb (no attribute changes, should be 100% compatable with mongodb cookbook on supermarket) 181 | 182 | **NOTE** v0.18.0 is the same as v0.17.0 with the exception of the cookbook name. All attributes have keped the `mongodb` namespace while the cookbook has been renamed `sc-mongodb` 183 | 184 | ## v0.17.0 Changes 185 | 186 | * Add ability to use ipaddress instead of fqdn in replicaset 187 | * fix creating user bug for custom host and port 188 | * Add a new install method 'none' that doesn't install. 189 | * Add NUMA support to debian-mongodb upstart script 190 | * Moved the ulimit commands to the start function 191 | * fix for bug #348 - broken sharding 192 | * Get rid of chef_gem 193 | * Excluded Amazon Linux from using systemctl 194 | * pessimistically allow 1.X mongo gem 195 | * add custom repo support 196 | * Moved the running of the sysconfig_file above the NUMA support 197 | * replace deprecated recipe 198 | * bug fix in member_config for having_id > 255 199 | * User management in replicasets / sharding 200 | * Force rs.reconfig when there are no old members 201 | * Packager Options Attribute 202 | 203 | ## v0.16.3 Changes (release never tagged, rolled into `v0.17.0` release) 204 | 205 | * remove old runit dependency 206 | * fix user/group attribute variables for newer versions of EL and Fedora 207 | 208 | ## v0.16.2 (Release Tue Nov 4 11:48:54 PST 2014) 209 | 210 | * start doing even patches = release, odd = dev. 211 | * removed unmaintained freebsd support file 212 | * pass `--force-yes` to apt to bypass issue with package/config conflict #305 213 | * bumped mms_agent version for `monitoring` and `backup` #312 214 | * added user management/auth support, and patches #313, #317 215 | * fix patches on logpath #310 216 | * allow changing mongos service name #319 217 | * update upstart config for debian #323 218 | * added fixes for installation prereqs, sasl 219 | * fix systemd usecase for redhat (#352, #350) 220 | 221 | ## v0.16.1 222 | 223 | * remove old `mms_agent.rb` and `mms-agent.rb` in favor of new mms_*_agent.rb 224 | * Update mms_*_agent.rb to use template instead of ruby-block, nothing, but call restart 225 | * DEPRECATE '10gen_repo' for 'mongodb_org_repo' #287 226 | * node['mongodb']['install_method'] can now be '10gen' (DEPRECATE) or 'mongodb_org' 227 | * allow `node['mongodb']['config']['logpath'] to be nil for syslog #288 228 | 229 | ## v0.16.0 230 | 231 | * BREAKING CHANGE - drop support for Ruby < 1.9, Chef < 11 232 | * cookbook dependency change 233 | 234 | * yum >= 3.0 235 | * remove <= limit for all other dep 236 | 237 | * update to Berkshelf 3 238 | 239 | * 280 fix install for centos (missing build-essentials) 240 | 241 | ## v0.15.2 End of Ruby 1.8, Chef 10 support 242 | 243 | * update test-kitchen for mongos 244 | * update MMS version 245 | * update rubocop 246 | * minor typo fixes 247 | 248 | ## v0.15.1 249 | 250 | * 'potentially' BREAKING CHANGES, cookbook dependency pinned 251 | 252 | * yum < 3.0 253 | * runit < 1.5 254 | * python < 1.4.5 255 | 256 | * DEPRECATION: explicitly drop support for 'unsupported' platforms 257 | * must be freebsd, rhel, fedora, debian 258 | 259 | * DEPRECATION: recipe mms-agent.rb/mms_agent.rb 260 | * see #261 for new-recipes 261 | 262 | * use node.set to make sure is_* attributes are available for search 263 | * 'key_file' -> 'key_file_content' 264 | * allow pinning for gems, pip packages 265 | * 261 new mms agent recipe based on new packaging in upstream 266 | * 256 Allow mms_agent to be run as non-root user 267 | * replSet is not set automatically 268 | 269 | ## v0.15.0 270 | 271 | * DEPRECATION: backward compatability for dbconfig variables in node['mongodb'] 272 | 273 | * use node['mongodb']['config'][variable] = value 274 | 275 | ## v0.14.10 DEVELOPMENTAL RELEASE 276 | 277 | * Final 0.14 release 278 | * move node['mongodb']['config']['configsrv'] auto update to the top 279 | * Drop using Chef.Version as it is not rc/beta compatible 280 | * installs gem bson_ext 281 | 282 | ## v0.14.8 DEVELOPMENTAL RELEASE 283 | 284 | * Rubocop (cherry pick of #220) 285 | 286 | ## v0.14.7 DEVELOPMENTAL RELEASE 287 | 288 | * Automatically install bson_ext gem 289 | * Add check/protection for empty shard 290 | * Force node['mongodb']['config']['configsrv'] == true when set as configserver 291 | 292 | ## v0.14.6 DEVELOPMENTAL RELEASE 293 | 294 | * try to autoconfigure 'configsrv' from configserver_nodes 295 | * remove `include 'mongodb::default'` from definition 296 | * allow chef-run without restarting mongo 297 | * comment cleanup 298 | 299 | ## v0.14.X DEVELOPMENTAL RELEASES 300 | 301 | * Split out install into separate recipe 302 | * Adds more testing 303 | * Fixes mms-agent installation/runtime 304 | * Preliminary Work being done to convert completely to Resource style 305 | * patches to Replicaset 306 | * patches to fix upstart service 307 | * patches to configserver install 308 | 309 | ## v0.13.2, RELEASED 310 | 311 | * add support for chef_gem on newer versions of chef 312 | 313 | ## v0.13.1, RELEASED 314 | 315 | * Add keyfileSupport 316 | 317 | ## v0.13.0, RELEASED 318 | 319 | * Bunch of stuff... 320 | 321 | ## v0.1.0 322 | 323 | Initial release of the cookbooks for the chef configuration management system developed at edelight GmbH. With this first release we publish the mongodb cookbook we use for our systems. This cookbook helps you to configure single node mongodb instances as well as more complicated cluster setups (including sharding and replication). 324 | -------------------------------------------------------------------------------- /definitions/mongodb.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: sc-mongodb 3 | # Definition:: mongodb 4 | # 5 | # Copyright:: 2011, edelight GmbH 6 | # Authors: 7 | # Markus Korn 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | # 21 | 22 | # TODO: This needs to be converted to a custom resource 23 | define :mongodb_instance, # rubocop:disable ChefModernize/Definitions 24 | mongodb_type: 'mongod', 25 | action: [:enable, :start], 26 | logpath: '/var/log/mongodb/mongod.log', 27 | configservers: [], 28 | replicaset: nil, 29 | notifies: [] do 30 | # TODO: this is the only remain use of params[:mongodb_type], is it still needed? 31 | unless %w(mongod configserver mongos).include?(params[:mongodb_type]) 32 | raise ArgumentError, ":mongodb_type must be 'mongod', 'configserver' or 'mongos'; was #{params[:mongodb_type].inspect}" 33 | end 34 | 35 | require 'ostruct' 36 | 37 | new_resource = OpenStruct.new 38 | 39 | # Determine type right away so we know if we need mongos or mognod installation 40 | new_resource.is_mongos = params[:mongodb_type] == 'mongos' 41 | 42 | # Determine if this will be part of a shard 43 | new_resource.is_shard = node['mongodb']['is_shard'] 44 | 45 | # Make changes to node['mongodb']['config'] before copying to new_resource. 46 | if new_resource.is_mongos 47 | provider = 'mongos' 48 | # mongos will fail to start if dbpath is set 49 | node.default['mongodb']['config']['mongos']['storage']['dbPath'] = nil 50 | 51 | # Search for config servers 52 | unless node['mongodb']['config']['mongos']['sharding']['configDB'] 53 | node.default['mongodb']['config']['mongos']['sharding']['configDB'] = params[:configservers].map do |n| 54 | "#{n['mongodb']['configserver_url'] || n['fqdn']}:#{n['mongodb']['config']['mongod']['net']['port']}" 55 | end.sort.join(',') 56 | 57 | # TODO: handle 3.2 config server replicasets 58 | # if node['mongodb']['package_version'].to_f >= 3.2 59 | # node.default['mongodb']['config']['sharding']['configDB'] 60 | # end 61 | end 62 | new_resource.config = node['mongodb']['config']['mongos'].to_hash 63 | new_resource.dbconfig_file = node['mongodb']['dbconfig_file']['mongos'] 64 | new_resource.dbpath = nil 65 | new_resource.sysconfig_file = node['mongodb']['sysconfig_file']['mongos'] 66 | new_resource.sysconfig_vars = node['mongodb']['sysconfig']['mongos'] 67 | else 68 | provider = 'mongod' 69 | new_resource.config = node['mongodb']['config']['mongod'].to_hash 70 | new_resource.replicaset_name = new_resource.config['replication']['replSetName'] 71 | new_resource.dbconfig_file = node['mongodb']['dbconfig_file']['mongod'] 72 | new_resource.sysconfig_file = node['mongodb']['sysconfig_file']['mongod'] 73 | new_resource.sysconfig_vars = node['mongodb']['sysconfig']['mongod'] 74 | 75 | if new_resource.is_shard 76 | new_resource.config['sharding'] ||= {} 77 | new_resource.config['sharding']['clusterRole'] = 'shardsvr' 78 | end 79 | 80 | if node['mongodb']['config']['mongod']['storage']['dbPath'].nil? 81 | node.default['mongodb']['config']['mongod']['storage']['dbPath'] = '/data' 82 | end 83 | new_resource.dbpath = node['mongodb']['config']['mongod']['storage']['dbPath'] 84 | end 85 | 86 | node.default['mongodb']['config']['configsvr'] = true if node['mongodb']['is_configserver'] 87 | 88 | new_resource.name = params[:name] 89 | new_resource.logpath = params[:logpath] 90 | new_resource.replicaset = params[:replicaset] 91 | new_resource.service_action = params[:action] 92 | new_resource.service_notifies = params[:notifies] 93 | 94 | # TODO(jh): parameterize so we can make a resource provider 95 | new_resource.auto_configure_replicaset = node['mongodb']['auto_configure']['replicaset'] 96 | new_resource.auto_configure_sharding = node['mongodb']['auto_configure']['sharding'] 97 | new_resource.bind_ip = new_resource.config['net']['bindIp'] 98 | new_resource.cluster_name = node['mongodb']['cluster_name'] 99 | new_resource.dbconfig_file_template = node['mongodb']['dbconfig_file']['template'] 100 | new_resource.init_dir = node['mongodb']['init_dir'] 101 | new_resource.init_script_template = node['mongodb']['init_script_template'] 102 | new_resource.is_replicaset = node['mongodb']['is_replicaset'] 103 | new_resource.is_configserver = node['mongodb']['is_configserver'] 104 | new_resource.mongodb_group = node['mongodb']['group'] 105 | new_resource.mongodb_user = node['mongodb']['user'] 106 | new_resource.port = new_resource.config['net']['port'] 107 | new_resource.root_group = node['mongodb']['root_group'] 108 | new_resource.shard_name = node['mongodb']['shard_name'] 109 | new_resource.sharded_collections = node['mongodb']['sharded_collections'] 110 | new_resource.sysconfig_file_template = node['mongodb']['sysconfig_file']['template'] 111 | new_resource.template_cookbook = node['mongodb']['template_cookbook'] 112 | new_resource.ulimit = node['mongodb']['ulimit'] 113 | new_resource.reload_action = node['mongodb']['reload_action'] 114 | 115 | # Upstart or sysvinit 116 | if platform?('ubuntu') && node['platform_version'].to_f < 15.04 117 | new_resource.init_file = File.join(node['mongodb']['init_dir'], "#{new_resource.name}.conf") 118 | mode = '0644' 119 | else 120 | new_resource.init_file = File.join(node['mongodb']['init_dir'], new_resource.name) 121 | mode = '0755' 122 | end 123 | 124 | # TODO(jh): reimplement using polymorphism 125 | replicaset_name = if new_resource.is_replicaset 126 | if new_resource.replicaset_name 127 | # trust a predefined replicaset name 128 | new_resource.replicaset_name 129 | elsif new_resource.is_shard && new_resource.shard_name 130 | # for replicated shards we autogenerate 131 | # the replicaset name for each shard 132 | "rs_#{new_resource.shard_name}" 133 | else 134 | # Well shoot, we don't have a predefined name and we aren't 135 | # really sharded. If we want backwards compatibility, this should be: 136 | # replicaset_name = "rs_#{new_resource.shard_name}" 137 | # which with default values defaults to: 138 | # replicaset_name = 'rs_default' 139 | # But using a non-default shard name when we're creating a default 140 | # replicaset name seems surprising to me and needlessly arbitrary. 141 | # So let's use the *default* default in this case: 142 | 'rs_default' 143 | end 144 | end 145 | 146 | # default file 147 | template new_resource.sysconfig_file do 148 | cookbook new_resource.template_cookbook 149 | source new_resource.sysconfig_file_template 150 | group new_resource.root_group 151 | owner 'root' 152 | mode '0644' 153 | variables( 154 | sysconfig: new_resource.sysconfig_vars 155 | ) 156 | notifies new_resource.reload_action, "service[#{new_resource.name}]" 157 | end 158 | 159 | # config file 160 | template new_resource.dbconfig_file do 161 | cookbook new_resource.template_cookbook 162 | source new_resource.dbconfig_file_template 163 | group new_resource.root_group 164 | owner 'root' 165 | variables( 166 | config: new_resource.config 167 | ) 168 | helpers MongoDBConfigHelpers 169 | mode '0644' 170 | notifies new_resource.reload_action, "service[#{new_resource.name}]" 171 | end 172 | 173 | # log dir [make sure it exists] 174 | directory File.dirname(new_resource.logpath) do 175 | owner new_resource.mongodb_user 176 | group new_resource.mongodb_group 177 | mode '0755' 178 | action :create 179 | recursive true 180 | not_if { new_resource.logpath.nil? || new_resource.logpath.empty? } 181 | end 182 | 183 | # dbpath dir [make sure it exists] unless it is a mongos 184 | unless new_resource.is_mongos 185 | directory new_resource.dbpath do 186 | owner new_resource.mongodb_user 187 | group new_resource.mongodb_group 188 | mode '0755' 189 | action :create 190 | recursive true 191 | end 192 | end 193 | 194 | # Reload systemctl for RHEL 7+ after modifying the init file. 195 | execute "mongodb-systemctl-daemon-reload-#{new_resource.name}" do 196 | command 'systemctl daemon-reload' 197 | action :nothing 198 | end 199 | 200 | # init script 201 | template new_resource.init_file do 202 | cookbook new_resource.template_cookbook 203 | source new_resource.init_script_template 204 | group new_resource.root_group 205 | owner 'root' 206 | mode mode 207 | variables( 208 | provides: provider, 209 | dbconfig_file: new_resource.dbconfig_file, 210 | sysconfig_file: new_resource.sysconfig_file, 211 | ulimit: new_resource.ulimit, 212 | bind_ip: new_resource.bind_ip, 213 | port: new_resource.port 214 | ) 215 | notifies new_resource.reload_action, "service[#{new_resource.name}]" 216 | 217 | if (platform_family?('rhel') && !platform?('amazon') && node['platform_version'].to_i >= 7) || (platform?('debian') && node['platform_version'].to_i >= 8) 218 | notifies :run, "execute[mongodb-systemctl-daemon-reload-#{new_resource.name}]", :immediately 219 | end 220 | end 221 | 222 | # service 223 | service new_resource.name do 224 | supports status: true, restart: true 225 | action new_resource.service_action 226 | new_resource.service_notifies.each do |service_notify| 227 | notifies :run, service_notify 228 | end 229 | notifies :run, 'ruby_block[config_replicaset]', :immediately if new_resource.is_replicaset && new_resource.auto_configure_replicaset 230 | notifies :run, 'ruby_block[config_sharding]', :immediately if new_resource.is_mongos && new_resource.auto_configure_sharding 231 | # we don't care about a running mongodb service in these cases, all we need is stopping it 232 | ignore_failure true if new_resource.name == 'mongodb' 233 | end 234 | 235 | # replicaset 236 | if new_resource.is_replicaset && new_resource.auto_configure_replicaset 237 | rs_nodes = search( 238 | :node, 239 | "mongodb_cluster_name:#{new_resource.cluster_name} AND " \ 240 | 'mongodb_is_replicaset:true AND ' \ 241 | "mongodb_config_mongod_replication_replSetName:#{new_resource.replicaset_name} AND " \ 242 | "chef_environment:#{node.chef_environment}" 243 | ) 244 | 245 | ruby_block 'config_replicaset' do 246 | block do 247 | MongoDB.configure_replicaset(node, replicaset_name, rs_nodes) unless new_resource.replicaset.nil? 248 | end 249 | action :nothing 250 | end 251 | 252 | ruby_block 'run_config_replicaset' do 253 | block {} 254 | notifies :run, 'ruby_block[config_replicaset]' 255 | end 256 | end 257 | 258 | # sharding 259 | if new_resource.is_mongos && new_resource.auto_configure_sharding 260 | # add all shards 261 | # configure the sharded collections 262 | 263 | shard_nodes = search( 264 | :node, 265 | "mongodb_cluster_name:#{new_resource.cluster_name} AND " \ 266 | "mongodb_shard_name:#{new_resource.shard_name} AND " \ 267 | 'mongodb_is_shard:true AND ' \ 268 | "chef_environment:#{node.chef_environment}" 269 | ) 270 | 271 | ruby_block 'config_sharding' do 272 | block do 273 | MongoDB.configure_shards(node, shard_nodes) 274 | MongoDB.configure_sharded_collections(node, new_resource.sharded_collections) 275 | end 276 | action :nothing 277 | end 278 | 279 | ruby_block 'run_config_sharding' do 280 | block {} 281 | notifies :run, 'ruby_block[config_sharding]' 282 | end 283 | end 284 | end 285 | -------------------------------------------------------------------------------- /spec/recipes/default_spec.rb: -------------------------------------------------------------------------------- 1 | # require 'spec_helper' 2 | # 3 | # describe 'sc-mongodb::default' do 4 | # context 'with default node attributes' do 5 | # # mongod_conf_XXXX: the variables are a copy of what is in the default 6 | # # attributes file so that the OS's can be tested for correct attribute 7 | # # assignments 8 | # let(:mongod_conf_rhel) do 9 | # { 10 | # 'net' => { 11 | # 'port' => 27017, 12 | # 'bindIp' => '0.0.0.0', 13 | # }, 14 | # 'systemLog' => { 15 | # 'destination' => 'file', 16 | # 'logAppend' => true, 17 | # 'path' => '/var/log/mongodb/mongod.log', 18 | # }, 19 | # 'processManagement' => { 20 | # 'fork' => true, 21 | # 'pidFilePath' => '/var/run/mongodb/mongod.pid', 22 | # }, 23 | # 'storage' => { 24 | # 'journal' => { 25 | # 'enabled' => true, 26 | # }, 27 | # 'dbPath' => '/var/lib/mongo', 28 | # 'engine' => 'wiredTiger', 29 | # }, 30 | # 'replication' => { 31 | # 'oplogSizeMB' => nil, 32 | # 'replSetName' => nil, 33 | # 'secondaryIndexPrefetch' => nil, 34 | # 'enableMajorityReadConcern' => nil, 35 | # }, 36 | # 'security' => { 37 | # 'keyFile' => nil, 38 | # }, 39 | # } 40 | # end 41 | # 42 | # let(:mongod_conf_debian) do 43 | # { 44 | # 'net' => { 45 | # 'port' => 27017, 46 | # 'bindIp' => '0.0.0.0', 47 | # }, 48 | # 'systemLog' => { 49 | # 'destination' => 'file', 50 | # 'logAppend' => true, 51 | # 'path' => '/var/log/mongodb/mongod.log', 52 | # }, 53 | # 'storage' => { 54 | # 'journal' => { 55 | # 'enabled' => true, 56 | # }, 57 | # 'dbPath' => '/var/lib/mongodb', 58 | # 'engine' => 'wiredTiger', 59 | # }, 60 | # 'replication' => { 61 | # 'oplogSizeMB' => nil, 62 | # 'replSetName' => nil, 63 | # 'secondaryIndexPrefetch' => nil, 64 | # 'enableMajorityReadConcern' => nil, 65 | # }, 66 | # 'security' => { 67 | # 'keyFile' => nil, 68 | # }, 69 | # } 70 | # end 71 | # 72 | # # mongod_init: Cookbook supports 2 different init systems right now 73 | # let(:mongod_init_sysvinit) do 74 | # '/etc/init.d/mongod' 75 | # end 76 | # 77 | # let(:mongod_init_upstart) do 78 | # '/etc/init/mongod.conf' 79 | # end 80 | # 81 | # # mongod_packager_options: pkg install options for mongo per OS 82 | # let(:mongod_packager_options_rhel) do 83 | # '--nogpgcheck' 84 | # end 85 | # 86 | # let(:mongod_packager_options_debian) do 87 | # '-o Dpkg::Options::="--force-confold" --force-yes' 88 | # end 89 | # 90 | # # mongod_sysconfig_file: sysconfig file location for mongo per OS 91 | # let(:mongod_sysconfig_file_debian) do 92 | # '/etc/default/mongodb' 93 | # end 94 | # 95 | # let(:mongod_sysconfig_file_rhel) do 96 | # '/etc/sysconfig/mongodb' 97 | # end 98 | # 99 | # # mongod_version: RHEL appends a release and OS versions to their versions 100 | # let(:mongod_version_rhel) do 101 | # '3.2.18-1.el7' 102 | # end 103 | # 104 | # let(:mongod_version_debian) do 105 | # '3.2.18' 106 | # end 107 | # 108 | # # All tests in this section run for all OS's 109 | # shared_examples_for 'default recipe' do 110 | # it 'should include "sc-mongodb::install" recipe' do 111 | # expect_any_instance_of(Chef::Recipe).to receive(:include_recipe).with('sc-mongodb::install') 112 | # chef_run 113 | # end 114 | # 115 | # it 'should install "build-tools" build_essential' do 116 | # expect(chef_run).to install_build_essential('build-tools') 117 | # end 118 | # 119 | # it 'should create sysconfig install template' do 120 | # expect(chef_run).to create_file_if_missing("#{mongod_sysconfig_file} install").with( 121 | # path: mongod_sysconfig_file, 122 | # content: 'ENABLE_MONGODB=no', 123 | # owner: 'root', 124 | # group: 'root', 125 | # mode: '0644' 126 | # ) 127 | # end 128 | # 129 | # it 'should create "/etc/mongod.conf install" template' do 130 | # expect(chef_run).to create_template_if_missing('/etc/mongod.conf install').with( 131 | # path: '/etc/mongod.conf', 132 | # cookbook: 'sc-mongodb', 133 | # source: 'mongodb.conf.erb', 134 | # owner: 'root', 135 | # group: 'root', 136 | # mode: '0644', 137 | # variables: { 138 | # config: mongod_conf, 139 | # } 140 | # ) 141 | # end 142 | # 143 | # it 'should not run "mongodb-systemctl-daemon-reload-mongod" execute' do 144 | # expect(chef_run).to_not run_execute('mongodb-systemctl-daemon-reload-mongod') 145 | # end 146 | # 147 | # it 'should create init install template' do 148 | # mode = mongod_init_source == 'debian-mongodb.upstart.erb' ? '0644' : '0755' 149 | # expect(chef_run).to create_template_if_missing("#{mongod_init_file} install").with( 150 | # path: mongod_init_file, 151 | # cookbook: 'sc-mongodb', 152 | # source: mongod_init_source, 153 | # owner: 'root', 154 | # group: 'root', 155 | # mode: mode, 156 | # variables: { 157 | # provides: 'mongod', 158 | # dbconfig_file: '/etc/mongod.conf', 159 | # sysconfig_file: mongod_sysconfig_file, 160 | # ulimit: { 161 | # 'fsize' => 'unlimited', 162 | # 'cpu' => 'unlimited', 163 | # 'as' => 'unlimited', 164 | # 'nofile' => 64000, 165 | # 'rss' => 'unlimited', 166 | # 'nproc' => 32000, 167 | # }, 168 | # bind_ip: '0.0.0.0', 169 | # port: 27017, 170 | # } 171 | # ) 172 | # end 173 | # 174 | # it 'should install "mongodb-org" package' do 175 | # expect(chef_run).to install_package('mongodb-org').with( 176 | # options: mongod_packager_options, 177 | # version: mongod_version 178 | # ) 179 | # end 180 | # 181 | # it 'should create sysconfig template' do 182 | # expect(chef_run).to create_template(mongod_sysconfig_file).with( 183 | # path: mongod_sysconfig_file, 184 | # cookbook: 'sc-mongodb', 185 | # source: 'mongodb.sysconfig.erb', 186 | # owner: 'root', 187 | # group: 'root', 188 | # mode: '0644', 189 | # variables: { 190 | # sysconfig: { 191 | # 'DAEMON' => '/usr/bin/$NAME', 192 | # 'DAEMON_USER' => file_owner, 193 | # 'DAEMON_OPTS' => '--config /etc/mongod.conf', 194 | # 'CONFIGFILE' => '/etc/mongod.conf', 195 | # 'ENABLE_MONGODB' => 'yes', 196 | # }, 197 | # } 198 | # ) 199 | # end 200 | # 201 | # it 'should create "/etc/mongod.conf" template' do 202 | # expect(chef_run).to create_template('/etc/mongod.conf').with( 203 | # path: '/etc/mongod.conf', 204 | # cookbook: 'sc-mongodb', 205 | # source: 'mongodb.conf.erb', 206 | # owner: 'root', 207 | # group: 'root', 208 | # mode: '0644', 209 | # variables: { 210 | # config: mongod_conf, 211 | # } 212 | # ) 213 | # end 214 | # 215 | # it 'should create "/var/log/mongodb" directory' do 216 | # expect(chef_run).to create_directory('/var/log/mongodb').with( 217 | # owner: file_owner, 218 | # group: file_owner, 219 | # mode: '0755' 220 | # ) 221 | # end 222 | # 223 | # it 'should create dbPath directory' do 224 | # expect(chef_run).to create_directory(mongod_conf['storage']['dbPath']).with( 225 | # owner: file_owner, 226 | # group: file_owner, 227 | # mode: '0755' 228 | # ) 229 | # end 230 | # 231 | # it 'should not run "mongodb-systemctl-daemon-reload" execute' do 232 | # expect(chef_run).to_not run_execute('mongodb-systemctl-daemon-reload') 233 | # end 234 | # 235 | # it 'should create init template' do 236 | # mode = mongod_init_source == 'debian-mongodb.upstart.erb' ? '0644' : '0755' 237 | # expect(chef_run).to create_template(mongod_init_file).with( 238 | # path: mongod_init_file, 239 | # cookbook: 'sc-mongodb', 240 | # source: mongod_init_source, 241 | # owner: 'root', 242 | # group: 'root', 243 | # mode: mode, 244 | # variables: { 245 | # provides: 'mongod', 246 | # dbconfig_file: '/etc/mongod.conf', 247 | # sysconfig_file: mongod_sysconfig_file, 248 | # ulimit: { 249 | # 'fsize' => 'unlimited', 250 | # 'cpu' => 'unlimited', 251 | # 'as' => 'unlimited', 252 | # 'nofile' => 64000, 253 | # 'rss' => 'unlimited', 254 | # 'nproc' => 32000, 255 | # }, 256 | # bind_ip: '0.0.0.0', 257 | # port: 27017, 258 | # } 259 | # ) 260 | # end 261 | # 262 | # it 'should enable "mongod" service' do 263 | # expect(chef_run).to enable_service('mongod') 264 | # end 265 | # 266 | # it 'should start "mongod" service' do 267 | # expect(chef_run).to start_service('mongod') 268 | # end 269 | # end 270 | # 271 | # shared_examples_for 'debian based install' do 272 | # it 'should install "mongodb-org-server" package' do 273 | # expect(chef_run).to install_package('mongodb-org-server').with( 274 | # options: mongod_packager_options, 275 | # version: mongod_version 276 | # ) 277 | # end 278 | # 279 | # it 'should install "mongodb-org-shell" package' do 280 | # expect(chef_run).to install_package('mongodb-org-shell').with( 281 | # options: mongod_packager_options, 282 | # version: mongod_version 283 | # ) 284 | # end 285 | # 286 | # it 'should install "mongodb-org-tools" package' do 287 | # expect(chef_run).to install_package('mongodb-org-tools').with( 288 | # options: mongod_packager_options, 289 | # version: mongod_version 290 | # ) 291 | # end 292 | # 293 | # it 'should install "mongodb-org-mongos" package' do 294 | # expect(chef_run).to install_package('mongodb-org-mongos').with( 295 | # options: mongod_packager_options, 296 | # version: mongod_version 297 | # ) 298 | # end 299 | # end 300 | # 301 | # context 'CentOS 7' do 302 | # let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'centos', version: '7').converge(described_recipe) } 303 | # 304 | # it_behaves_like 'default recipe' do 305 | # let(:file_owner) { 'mongod' } 306 | # let(:mongod_conf) { mongod_conf_rhel } 307 | # let(:mongod_init_file) { mongod_init_sysvinit } 308 | # let(:mongod_init_source) { 'redhat-mongodb.init.erb' } 309 | # let(:mongod_packager_options) { mongod_packager_options_rhel } 310 | # let(:mongod_sysconfig_file) { mongod_sysconfig_file_rhel } 311 | # let(:mongod_version) { mongod_version_rhel } 312 | # end 313 | # 314 | # it 'should create "mongodb" yum_repository' do 315 | # expect(chef_run).to create_yum_repository('mongodb').with( 316 | # description: 'mongodb RPM Repository', 317 | # baseurl: 'https://repo.mongodb.org/yum/redhat/$releasever/mongodb-org/3.2/x86_64', 318 | # gpgkey: 'https://www.mongodb.org/static/pgp/server-3.2.asc', 319 | # gpgcheck: true, 320 | # sslverify: true, 321 | # enabled: true 322 | # ) 323 | # end 324 | # end 325 | # 326 | # context 'Debian 8' do 327 | # let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'debian', version: '8').converge(described_recipe) } 328 | # 329 | # it_behaves_like 'default recipe' do 330 | # let(:file_owner) { 'mongodb' } 331 | # let(:mongod_conf) { mongod_conf_debian } 332 | # let(:mongod_init_file) { mongod_init_sysvinit } 333 | # let(:mongod_init_source) { 'debian-mongodb.init.erb' } 334 | # let(:mongod_packager_options) { mongod_packager_options_debian } 335 | # let(:mongod_sysconfig_file) { mongod_sysconfig_file_debian } 336 | # let(:mongod_version) { mongod_version_debian } 337 | # end 338 | # 339 | # it_behaves_like 'debian based install' do 340 | # let(:mongod_packager_options) { mongod_packager_options_debian } 341 | # let(:mongod_version) { mongod_version_debian } 342 | # end 343 | # 344 | # it 'should create "mongodb" yum_repository' do 345 | # expect(chef_run).to add_apt_repository('mongodb').with( 346 | # uri: 'http://repo.mongodb.org/apt/debian', 347 | # distribution: 'jessie/mongodb-org/3.2', 348 | # components: ['main'], 349 | # key: ['https://www.mongodb.org/static/pgp/server-3.2.asc'] 350 | # ) 351 | # end 352 | # end 353 | # 354 | # context 'Ubuntu 16.04' do 355 | # let(:chef_run) { ChefSpec::SoloRunner.new(platform: 'ubuntu', version: '16.04').converge(described_recipe) } 356 | # 357 | # it_behaves_like 'default recipe' do 358 | # let(:file_owner) { 'mongodb' } 359 | # let(:mongod_conf) { mongod_conf_debian } 360 | # let(:mongod_init_file) { mongod_init_sysvinit } 361 | # let(:mongod_init_source) { 'debian-mongodb.init.erb' } 362 | # let(:mongod_packager_options) { mongod_packager_options_debian } 363 | # let(:mongod_sysconfig_file) { mongod_sysconfig_file_debian } 364 | # let(:mongod_version) { mongod_version_debian } 365 | # end 366 | # 367 | # it_behaves_like 'debian based install' do 368 | # let(:mongod_packager_options) { mongod_packager_options_debian } 369 | # let(:mongod_version) { mongod_version_debian } 370 | # end 371 | # 372 | # it 'should create "mongodb" yum_repository' do 373 | # expect(chef_run).to add_apt_repository('mongodb').with( 374 | # uri: 'http://repo.mongodb.org/apt/ubuntu', 375 | # distribution: 'xenial/mongodb-org/3.2', 376 | # components: ['multiverse'], 377 | # key: ['https://www.mongodb.org/static/pgp/server-3.2.asc'] 378 | # ) 379 | # end 380 | # end 381 | # end 382 | # end 383 | -------------------------------------------------------------------------------- /libraries/user.rb: -------------------------------------------------------------------------------- 1 | module MongoDB 2 | module Helpers 3 | module User 4 | def user_exists?(username, connection) 5 | connection['admin']['system.users'].find(user: username).any? 6 | end 7 | 8 | def user_exists_v2?(username, connection) 9 | connection['system.users'].find(user: username).any? 10 | end 11 | 12 | def add_user(username, password, database, roles = []) 13 | require 'rubygems' 14 | require 'mongo' 15 | 16 | connection = retrieve_db 17 | admin = connection.db('admin') 18 | db = connection.db(database) 19 | 20 | # Check if user is admin / admin, and warn that this should 21 | # be overridden to unique values 22 | if username == 'admin' && password == 'admin' 23 | Chef::Log.warn('Default username / password detected for admin user') 24 | Chef::Log.warn('These should be overridden to different, unique values') 25 | end 26 | 27 | # If authentication is required on database 28 | # must authenticate as a userAdmin after an admin user has been created 29 | # this will fail on the first attempt, but user will still be created 30 | # because of the localhost exception 31 | if (@new_resource.connection['config']['auth'] == true) || (@new_resource.connection['mongos_create_admin'] == true) 32 | begin 33 | admin.authenticate(@new_resource.connection['authentication']['username'], @new_resource.connection['authentication']['password']) 34 | rescue Mongo::AuthenticationError => e 35 | Chef::Log.warn("Unable to authenticate as admin user. If this is a fresh install, ignore warning: #{e}") 36 | end 37 | end 38 | 39 | # Create the user if they don't exist 40 | # Update the user if they already exist 41 | begin 42 | db.add_user(username, password, false, roles: roles, mechanisms: ['SCRAM-SHA-1']) 43 | Chef::Log.info("Created or updated user #{username} on #{database}") 44 | rescue Mongo::ConnectionFailure => e 45 | if @new_resource.connection['is_replicaset'] 46 | # Node is part of a replicaset and may not be initialized yet, going to retry if set to 47 | i = 0 48 | while i < @new_resource.connection['mongod_create_user']['retries'] 49 | begin 50 | # See if we can get the current replicaset status back from the node 51 | cmd = BSON::OrderedHash.new 52 | cmd['replSetGetStatus'] = 1 53 | result = admin.command(cmd) 54 | # Check if the current node in the replicaset status has an info message set (at this point, most likely 55 | # a message about the election) 56 | has_info_message = result['members'].any? { |a| a['self'] && a.key?('infoMessage') } 57 | if result['myState'] == 1 58 | # This node is a primary node, try to add the user 59 | db.add_user(username, password, false, roles: roles, mechanisms: ['SCRAM-SHA-1']) 60 | Chef::Log.info("Created or updated user #{username} on #{database} of primary replicaset node") 61 | break 62 | elsif result['myState'] == 2 && has_info_message == true 63 | # This node is secondary but may be in the process of an election, retry 64 | Chef::Log.info("Unable to add user to secondary, election may be in progress, retrying in #{@new_resource.connection['mongod_create_user']['delay']} seconds...") 65 | elsif result['myState'] == 2 && has_info_message == false 66 | # This node is secondary and not in the process of an election, bail out 67 | Chef::Log.info('Current node appears to be a secondary node in replicaset, could not detect election in progress, not adding user') 68 | break 69 | end 70 | rescue Mongo::ConnectionFailure => e 71 | # Unable to connect to the node, may not be initialized yet 72 | Chef::Log.warn("Unable to add user, retrying in #{@new_resource.connection['mongod_create_user']['delay']} second(s)... #{e}") 73 | rescue Mongo::OperationFailure => e 74 | # Unable to make either add call or replicaset call on node, should retry in case it was in the middle of being initialized 75 | Chef::Log.warn("Unable to add user, retrying in #{@new_resource.connection['mongod_create_user']['delay']} second(s)... #{e}") 76 | end 77 | i += 1 78 | sleep(@new_resource.connection['mongod_create_user']['delay']) 79 | end 80 | else 81 | Chef::Log.fatal("Unable to add user: #{e}") 82 | end 83 | end 84 | end 85 | 86 | def add_user_v2(username, password, database, roles = []) 87 | # Check if user is admin / admin, and warn that this should 88 | # be overridden to unique values 89 | if username == 'admin' && password == 'admin' 90 | Chef::Log.warn('Default username / password detected for admin user') 91 | Chef::Log.warn('These should be overridden to different, unique values') 92 | end 93 | 94 | # If authentication is required on database 95 | # must authenticate as a userAdmin after an admin user has been created 96 | # this will fail on the first attempt, but user will still be created 97 | # because of the localhost exception 98 | if (@new_resource.connection['config']['auth'] == true) || (@new_resource.connection['mongos_create_admin'] == true) 99 | begin 100 | connection = retrieve_db_v2( 101 | @new_resource.connection['authentication']['username'], 102 | @new_resource.connection['authentication']['password'] 103 | ) 104 | rescue Mongo::Auth::Unauthorized => e 105 | # invalid creds 106 | Chef::Log.warn("Unable to authenticate as admin user. If this is a fresh install, ignore warning: #{e}") 107 | connection = retrieve_db_v2 108 | rescue Mongo::Error::NoServerAvailable => e 109 | # Replicaset not initialized 110 | Chef::Log.warn("Server appears to be part of an uninitialized or initializing replicaset: #{e}") 111 | Chef::Log.warn('Retrying 1 time') 112 | sleep(@new_resource.connection['mongod_create_user']['delay']) 113 | begin 114 | connection = retrieve_db_v2 115 | rescue Mongo::Error::NoServerAvailable => e 116 | raise("Unable to connect to mongo: #{e}") 117 | end 118 | end 119 | end 120 | 121 | admin = connection.use('admin') 122 | db = connection.use(database) 123 | 124 | begin 125 | if user_exists_v2?(username, connection) 126 | Chef::Log.warn("#{username} already exists on #{database}") 127 | else 128 | # Create the user 129 | db.database.users.create( 130 | username, 131 | password: password, 132 | roles: roles 133 | ) 134 | Chef::Log.info("Created user #{username} on #{database}") 135 | end 136 | rescue Mongo::Error::OperationFailure => e 137 | # User probably already exists 138 | raise("Unable to add user on initial try: #{e}") 139 | rescue Mongo::Error::NoServerAvailable => e 140 | if @new_resource.connection['is_replicaset'] 141 | # Node is part of a replicaset and may not be initialized yet, going to retry if set to 142 | i = 0 143 | while i < @new_resource.connection['mongod_create_user']['retries'] 144 | 145 | rs_info = admin.command(replSetGetStatus: 1) 146 | rs_info_self = rs_info.documents[0]['members'].select { |a| a['self'] }.first 147 | has_info_message = rs_info_self.key?('infoMessage') 148 | 149 | if rs_info_self['state'] == 1 150 | # This node is a primary node, try to add the user 151 | db.database.users.create( 152 | username, 153 | password: password, 154 | roles: roles 155 | ) 156 | Chef::Log.info("Created or updated user #{username} on #{database} of primary replicaset node") 157 | break 158 | elsif rs_info_self['state'] == 2 && has_info_message 159 | # This node is secondary but may be in the process of an election, retry 160 | Chef::Log.info("Unable to add user to secondary, election may be in progress, retrying in #{@new_resource.connection['mongod_create_user']['delay']} seconds...") 161 | elsif rs_info_self['state'] == 2 && !has_info_message 162 | # This node is secondary and not in the process of an election, bail out 163 | Chef::Log.info('Current node appears to be a secondary node in replicaset, could not detect election in progress, not adding user') 164 | break 165 | end 166 | 167 | i += 1 168 | sleep(@new_resource.connection['mongod_create_user']['delay']) 169 | end 170 | else 171 | raise("Unable to add user: #{e}") 172 | end 173 | end 174 | end 175 | 176 | # Drop a user from the database specified 177 | def delete_user(username, database) 178 | require 'rubygems' 179 | require 'mongo' 180 | 181 | connection = retrieve_db 182 | admin = connection.db('admin') 183 | db = connection.db(database) 184 | 185 | # Only try to authenticate with db if required 186 | if (@new_resource.connection['config']['auth'] == true) || (@new_resource.connection['mongos_create_admin'] == true) 187 | begin 188 | admin.authenticate(@new_resource.connection['authentication']['username'], @new_resource.connection['authentication']['password']) 189 | rescue Mongo::AuthenticationError => e 190 | Chef::Log.warn("Unable to authenticate as admin user: #{e}") 191 | end 192 | end 193 | 194 | if user_exists?(username, connection) 195 | db.remove_user(username) 196 | Chef::Log.info("Deleted user #{username} on #{database}") 197 | else 198 | Chef::Log.warn("Unable to delete non-existent user #{username} on #{database}") 199 | end 200 | end 201 | 202 | def delete_user_v2(username, database) 203 | if (@new_resource.connection['config']['auth'] == true) || (@new_resource.connection['mongos_create_admin'] == true) 204 | begin 205 | connection = retrieve_db_v2( 206 | @new_resource.connection['authentication']['username'], 207 | @new_resource.connection['authentication']['password'] 208 | ) 209 | rescue Mongo::Auth::Unauthorized => e 210 | # invalid creds 211 | raise("Unable to authenticate as admin user: #{e}") 212 | rescue Mongo::Error::NoServerAvailable => e 213 | # Replicaset not initialized 214 | Chef::Log.warn("Server appears to be part of an uninitialized or initializing replicaset: #{e}") 215 | Chef::Log.warn('Retrying 1 time') 216 | sleep(@new_resource.connection['mongod_create_user']['delay']) 217 | begin 218 | connection = retrieve_db_v2 219 | rescue Mongo::Error::NoServerAvailable => e 220 | raise("Unable to connect to mongo: #{e}") 221 | end 222 | end 223 | end 224 | 225 | db = connection.use(database) 226 | 227 | if user_exists_v2?(username, connection) 228 | db.database.users.remove(username) 229 | Chef::Log.info("Deleted user #{username} on #{database}") 230 | else 231 | Chef::Log.warn("Unable to delete non-existent user #{username} on #{database}") 232 | end 233 | end 234 | 235 | # Get the MongoClient connection 236 | def retrieve_db(attempt = 0) 237 | require 'rubygems' 238 | require 'mongo' 239 | 240 | begin 241 | Mongo::MongoClient.new( 242 | @new_resource.connection['host'], 243 | @new_resource.connection['port'], 244 | connect_timeout: 15, 245 | slave_ok: true 246 | ) 247 | rescue Mongo::ConnectionFailure 248 | if attempt < @new_resource.connection['user_management']['connection']['retries'] 249 | Chef::Log.warn("Unable to connect to MongoDB instance, retrying in #{@new_resource.connection['user_management']['connection']['delay']} second(s)...") 250 | sleep(@new_resource.connection['user_management']['connection']['delay']) 251 | retrieve_db(attempt + 1) 252 | end 253 | end 254 | end 255 | 256 | def retrieve_db_v2(username = nil, password = nil, attempt = 0) 257 | require 'mongo' 258 | 259 | host = @new_resource.connection['host'] || 'localhost' 260 | port = @new_resource.connection['port'] || 27017 261 | 262 | begin 263 | Chef::Log.info("Connecting to #{host}:#{port} with #{username}") 264 | client = Mongo::Client.new( 265 | ["#{host}:#{port}"], 266 | user: username, 267 | password: password, 268 | connect_timeout: 5, 269 | socket_timeout: 5, 270 | max_read_retries: 5, 271 | server_selection_timeout: 3 272 | ) 273 | 274 | # Query the server for all database names to verify server connection 275 | client.database_names 276 | rescue Mongo::Error::NoServerAvailable, Mongo::Error::OperationFailure => e 277 | if attempt < @new_resource.connection['user_management']['connection']['retries'] 278 | Chef::Log.warn("Unable to connect to MongoDB instance: #{e}, retrying in #{@new_resource.connection['user_management']['connection']['delay']} second(s)...") 279 | sleep(@new_resource.connection['user_management']['connection']['delay']) 280 | retrieve_db_v2(username, password, attempt + 1) 281 | end 282 | end 283 | 284 | client 285 | end 286 | end 287 | end 288 | end 289 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # sc-MongoDB Cookbook 2 | 3 | [![Cookbook Version](https://img.shields.io/cookbook/v/sc-mongodb.svg)](https://supermarket.chef.io/cookbooks/sc-mongodb) 4 | [![CI State](https://github.com/sous-chefs/mongodb/workflows/ci/badge.svg)](https://github.com/sous-chefs/mongodb/actions?query=workflow%3Aci) 5 | [![OpenCollective](https://opencollective.com/sous-chefs/backers/badge.svg)](#backers) 6 | [![OpenCollective](https://opencollective.com/sous-chefs/sponsors/badge.svg)](#sponsors) 7 | [![License](https://img.shields.io/badge/License-Apache%202.0-green.svg)](https://opensource.org/licenses/Apache-2.0) 8 | 9 | Installs and configures MongoDB 10 | 11 | ## Maintainers 12 | 13 | This cookbook is maintained by the Sous Chefs. The Sous Chefs are a community of Chef cookbook maintainers working together to maintain important cookbooks. If you’d like to know more please visit [sous-chefs.org](https://sous-chefs.org/) or come chat with us on the Chef Community Slack in [#sous-chefs](https://chefcommunity.slack.com/messages/C2V7B88SF). 14 | 15 | ## Supports 16 | 17 | - Single MongoDB instance 18 | - Replication 19 | - Sharding 20 | - Replication and Sharding 21 | - MongoDB Monitoring System 22 | 23 | ## Requirements 24 | 25 | ### Cookbooks 26 | 27 | - none 28 | 29 | ### Chef Infra Client 30 | 31 | - Chef Infra Client 14+ 32 | 33 | ### Platform 34 | 35 | Currently we 'actively' test using test-kitchen on Ubuntu, Debian, CentOS, Redhat 36 | 37 | ## Definitions 38 | 39 | This cookbook contains a definition `mongodb_instance` which can be used to configure 40 | a certain type of mongodb instance, like the default mongodb or various components 41 | of a sharded setup. 42 | 43 | For examples see the USAGE section below. 44 | 45 | ## Attributes 46 | 47 | ### MongoDB setup 48 | 49 | - `default['mongodb']['install_method']` - This option can be "mongodb-org" or "none" - Default ("mongodb-org") 50 | 51 | ### MongoDB Configuration 52 | 53 | The `node['mongodb']['config']` is split into 2 keys, `mongod` and `mongos` (i.e. node['mongodb']['config']['mongod']). They attributes are rendered out as a yaml config file. All settings defined in the Configuration File Options documentation page can be added to the `node['mongodb']['config'][]` attribute: [https://docs.mongodb.com/manual/reference/configuration-options/](https://docs.mongodb.com/manual/reference/configuration-options/) 54 | 55 | Several important attributes to note: 56 | 57 | - `node['mongodb']['config']['mongod']['net']['bindIp']` - Configure from which address to accept connections 58 | - `node['mongodb']['config']['mongod']['net']['port']` - Port the mongod listens on, default is 27017 59 | - `node['mongodb']['config']['mongod']['replication']['oplogSizeMB']` - Specifies a maximum size in megabytes for the replication operation log 60 | - `node['mongodb']['config']['mongod']['storage']['dbPath']` - Location for mongodb data directory, defaults to "/var/lib/mongodb" 61 | - `node['mongodb']['config']['mongod']['storage']['engine']` - Storage engine to use, default is `"wiredTiger"` 62 | - `node['mongodb']['config']['mongod']['systemLog']['path']` - Path for the logfiles, default is `"/var/lib/mongo"` for `rhel` and `fedora` and `"/var/log/mongodb/mongod.log"` for all others 63 | - `node['mongodb']['config']['mongod'][]` - General MongoDB Configuration File option 64 | 65 | ### Cookbook specific attributes 66 | 67 | - `node['mongodb']['reload_action']` - Action to take when MongoDB conf files are modified, default is `"restart"` 68 | - `node['mongodb']['package_version']` - Version of the MongoDB package to install, default is nil 69 | - `node['mongodb']['client_roles']` - Role identifying all external clients which should have access to a mongod instance 70 | 71 | ### Sharding and replication attributes 72 | 73 | - `node['mongodb']['config']['mongod']['replication']['replSetName']` - Define name of replicaset 74 | - `node['mongodb']['cluster_name']` - Name of the cluster, all members of the cluster must reference to the same name, as this name is used internally to identify all members of a cluster. 75 | - `node['mongodb']['shard_name']` - Name of a shard, default is "default" 76 | - `node['mongodb']['sharded_collections']` - Define which collections are sharded 77 | - `node['mongodb']['replica_arbiter_only']` - Set to true to make node an [arbiter](http://docs.mongodb.org/manual/reference/replica-configuration/#local.system.replset.members[n].arbiterOnly). 78 | - `node['mongodb']['replica_build_indexes']` - Set to false to omit [index creation](http://docs.mongodb.org/manual/reference/replica-configuration/#local.system.replset.members[n].buildIndexes). 79 | - `node['mongodb']['replica_hidden']` - Set to true to [hide](http://docs.mongodb.org/manual/reference/replica-configuration/#local.system.replset.members[n].hidden) node from replicaset. 80 | - `node['mongodb']['replica_slave_delay']` - Number of seconds to [delay slave replication](http://docs.mongodb.org/manual/reference/replica-configuration/#local.system.replset.members[n].slaveDelay). 81 | - `node['mongodb']['replica_priority']` - Node [priority](http://docs.mongodb.org/manual/reference/replica-configuration/#local.system.replset.members[n].priority). 82 | - `node['mongodb']['replica_tags']` - Node [tags](http://docs.mongodb.org/manual/reference/replica-configuration/#local.system.replset.members[n].tags). 83 | - `node['mongodb']['replica_votes']` - Number of [votes](http://docs.mongodb.org/manual/reference/replica-configuration/#local.system.replset.members[n].votes) node will cast in an election. 84 | 85 | ### shared MMS Agent attributes 86 | 87 | - `node['mongodb']['mms_agent']['api_key']` - MMS Agent API Key. No default, required. 88 | - `node['mongodb']['mms_agent']['automation']['config'][]` - General MongoDB MMS Automation Agent configuration file option. 89 | - `node['mongodb']['mms_agent']['backup']['config'][]` - General MongoDB MMS Monitoring Agent configuration file option. 90 | - `node['mongodb']['mms_agent']['monitoring']['config'][]` - General MongoDB MMS Monitoring Agent configuration file option. 91 | 92 | #### Automation Agent Settings 93 | 94 | The defaults values installed by the package are: 95 | 96 | ```toml 97 | mmsBaseUrl=https://mms.mongodb.com 98 | logFile=/var/log/mongodb-mms-automation/automation-agent.log 99 | mmsConfigBackup=/var/lib/mongodb-mms-automation/mms-cluster-config-backup.json 100 | logLevel=INFO 101 | maxLogFiles=10 102 | maxLogFileSize=268435456 103 | ``` 104 | 105 | #### Backup Agent Settings 106 | 107 | The defaults values installed by the package are: 108 | 109 | ```toml 110 | mothership=api-backup.mongodb.com 111 | https=true 112 | ``` 113 | 114 | #### Monitoring Agent Settings 115 | 116 | The defaults values installed by the package are: 117 | 118 | ```toml 119 | mmsBaseUrl=https://mms.mongodb.com 120 | ``` 121 | 122 | ### User management attributes 123 | 124 | - `node['mongodb']['config']['auth']` - Require authentication to access or modify the database (`True` or `False`), Default is `nil` 125 | - `node['mongodb']['admin']` - The admin user with userAdmin privileges that allows user management 126 | - `node['mongodb']['users']` - Array of users to add when running the user management recipe 127 | 128 | ## Usage 129 | 130 | ### Single mongodb instance 131 | 132 | Simply add 133 | 134 | ```ruby 135 | include_recipe "sc-mongodb::default" 136 | ``` 137 | 138 | to your recipe. This will run the mongodb instance as configured by your distribution. 139 | You can change the dbpath, logpath and port settings (see ATTRIBUTES) for this node by 140 | using the `mongodb_instance` definition: 141 | 142 | ```ruby 143 | mongodb_instance "mongodb" do 144 | port node['application']['port'] 145 | end 146 | ``` 147 | 148 | This definition also allows you to run another mongod instance with a different 149 | name on the same node 150 | 151 | ```ruby 152 | mongodb_instance "my_instance" do 153 | port node['mongodb']['port'] + 100 154 | end 155 | ``` 156 | 157 | The result is a new system service with 158 | 159 | ```shell 160 | /etc/init.d/my_instance 161 | ``` 162 | 163 | ### Replicasets 164 | 165 | Add `sc-mongodb::replicaset` (instead of `sc-mongodb::default`) to the node's run_list. Also choose a name for your 166 | replicaset cluster and set the value of `node['mongodb']['cluster_name']` for each 167 | member to this name. 168 | 169 | The recipe will try to configure the replicaset with the instances already registered in your chef-server with the same 170 | `node['mongodb']['cluster_name']`, to configure various machines with the replicaset you'll need to deactivate the 171 | automatic configuration with `node['mongodb']['auto_configure']['replicaset'] = false` and enable that flag only on the last 172 | instance of the replicaset. 173 | 174 | ### Sharding 175 | 176 | You need a few more components, but the idea is the same: identification of the 177 | members with their different internal roles (mongos, configserver, etc.) is done via 178 | the `node['mongodb']['cluster_name']` and `node['mongodb']['shard_name']` attributes. 179 | 180 | Let's have a look at a simple sharding setup, consisting of two shard servers, one 181 | config server and one mongos. 182 | 183 | First, we would like to configure the two shards. For doing so, just use 184 | `sc-mongodb::shard` in the node's run_list and define a unique `mongodb['shard_name']` 185 | for each of these two nodes, say "shard1" and "shard2". 186 | 187 | Then configure a node to act as a config server - by using the `sc-mongodb::configserver` 188 | recipe. 189 | 190 | And finally, you need to configure the mongos. This can be done by using the 191 | `sc-mongodb::mongos` recipe. The mongos needs some special configuration, as these 192 | mongos are actually doing the configuration of the whole sharded cluster. 193 | Most importantly you need to define what collections should be sharded by setting the 194 | attribute `mongodb['sharded_collections']`: 195 | 196 | ```ruby 197 | { 198 | "mongodb": { 199 | "sharded_collections": { 200 | "test.addressbook": "name", 201 | "mydatabase.calendar": "date" 202 | } 203 | } 204 | } 205 | ``` 206 | 207 | Now mongos will automatically enable sharding for the "test" and the "mydatabase" 208 | database. Also, the "addressbook" and the "calendar" collection will be sharded, 209 | with sharding key "name" resp. "date". 210 | In the context of a sharding cluster always keep in mind to use a single role 211 | which is added to all members of the cluster to identify all member nodes. 212 | Also, shard names are important to distinguish the different shards. 213 | This is esp. important when you want to replicate shards. 214 | 215 | ### Sharding + Replication 216 | 217 | The setup is not much different from the one described above. All you have to do is add the 218 | `sc-mongodb::replicaset` recipe to all shard nodes, and make sure that all shard 219 | nodes which should be in the same replicaset have the same shard name. 220 | 221 | For more details, you can find a [tutorial for Sharding + Replication](https://github.com/edelight/chef-mongodb/wiki/MongoDB%3A-Replication%2BSharding) in the wiki. 222 | 223 | ### MMS Agent 224 | 225 | This cookbook also includes support for 226 | [MongoDB Monitoring System (MMS)](https://mms.mongodb.com/) 227 | agent. MMS is a hosted monitoring service, provided by MongoDB, Inc. Once 228 | the small agent program is installed on the MongoDB host, it 229 | automatically collects the metrics and uploads them to the MMS server. 230 | The graphs of these metrics are shown on the web page. It helps a lot 231 | for tackling MongoDB related problems, so MMS is the baseline for all 232 | production MongoDB deployments. 233 | 234 | To setup MMS, simply set your keys in 235 | `node['mongodb']['mms_agent']['api_key']` and then add the 236 | `sc-mongodb::mms_monitoring_agent` recipe to your run list. Your current keys 237 | should be available at your [MMS Settings page](https://mms.mongodb.com/settings). 238 | 239 | The agent install and configurations is also available via a custom resource for 240 | wrapper cookbooks. This allows for further customization outside of this 241 | cookbook 242 | 243 | ```ruby 244 | mongodb_agent 'monitoring' do 245 | config {} # Key and value pairs that will be in the config file 246 | group 'group' # Group to own the config file 247 | package_url 'package_url' # Download URL of the agent package 248 | user 'user' # User to own the config file 249 | end 250 | ``` 251 | 252 | ### User Management 253 | 254 | --NOTE:-- Using the `sc-mongodb::user_management` is not secure since passwords are stored plain 255 | text in your node attributes. Please concider using a wrapper recipe with encrypted data bags 256 | when using this cookbook in production. 257 | 258 | An optional recipe is `sc-mongodb::user_management` which will enable authentication in 259 | the configuration file by default and create any users in the `node['mongodb']['users']`. 260 | The users array expects a hash of username, password, roles, and database. Roles should be 261 | an array of roles the user should have on the database given. 262 | 263 | By default, authentication is not required on the database. This can be overridden by setting 264 | the `node['mongodb']['config']['auth']` attribute to true in the chef json. 265 | 266 | If the auth configuration is true, it will try to create the `node['mongodb']['admin']` user, or 267 | update them if they already exist. Before using on a new database, ensure you're overwriting 268 | the `node['mongodb']['authentication']['username']` and `node['mongodb']['authentication']['password']` to 269 | something besides their default values. 270 | 271 | To update the admin username or password after already having deployed the recipe with authentication 272 | as required, simply change `node['mongodb']['admin']['password']` to the new password while keeping the 273 | value of `node['mongodb']['authentication']['password']` the old value. After the recipe runs successfully, 274 | be sure to change the latter variable to the new password so that subsequent attempts to authenticate will 275 | work. 276 | 277 | There's also a user resource which has the actions `:add`, `:modify` and `:delete`. If modify is 278 | used on a user that doesn't exist, it will be added. If add is used on a user that exists, it 279 | will be modified. 280 | 281 | If using this recipe with replication and sharding, ensure that the `node['mongodb']['key_file_content']` 282 | is set. All nodes must have the same key file in order for the replica set to initialize successfully 283 | when authentication is required. For mongos instances, set `node['mongodb']['mongos_create_admin']` to 284 | `true` to force the creation of the admin user on mongos instances. 285 | 286 | ## Contributors 287 | 288 | This project exists thanks to all the people who [contribute.](https://opencollective.com/sous-chefs/contributors.svg?width=890&button=false) 289 | 290 | ### Backers 291 | 292 | Thank you to all our backers! 293 | 294 | ![https://opencollective.com/sous-chefs#backers](https://opencollective.com/sous-chefs/backers.svg?width=600&avatarHeight=40) 295 | 296 | ### Sponsors 297 | 298 | Support this project by becoming a sponsor. Your logo will show up here with a link to your website. 299 | 300 | ![https://opencollective.com/sous-chefs/sponsor/0/website](https://opencollective.com/sous-chefs/sponsor/0/avatar.svg?avatarHeight=100) 301 | ![https://opencollective.com/sous-chefs/sponsor/1/website](https://opencollective.com/sous-chefs/sponsor/1/avatar.svg?avatarHeight=100) 302 | ![https://opencollective.com/sous-chefs/sponsor/2/website](https://opencollective.com/sous-chefs/sponsor/2/avatar.svg?avatarHeight=100) 303 | ![https://opencollective.com/sous-chefs/sponsor/3/website](https://opencollective.com/sous-chefs/sponsor/3/avatar.svg?avatarHeight=100) 304 | ![https://opencollective.com/sous-chefs/sponsor/4/website](https://opencollective.com/sous-chefs/sponsor/4/avatar.svg?avatarHeight=100) 305 | ![https://opencollective.com/sous-chefs/sponsor/5/website](https://opencollective.com/sous-chefs/sponsor/5/avatar.svg?avatarHeight=100) 306 | ![https://opencollective.com/sous-chefs/sponsor/6/website](https://opencollective.com/sous-chefs/sponsor/6/avatar.svg?avatarHeight=100) 307 | ![https://opencollective.com/sous-chefs/sponsor/7/website](https://opencollective.com/sous-chefs/sponsor/7/avatar.svg?avatarHeight=100) 308 | ![https://opencollective.com/sous-chefs/sponsor/8/website](https://opencollective.com/sous-chefs/sponsor/8/avatar.svg?avatarHeight=100) 309 | ![https://opencollective.com/sous-chefs/sponsor/9/website](https://opencollective.com/sous-chefs/sponsor/9/avatar.svg?avatarHeight=100) 310 | -------------------------------------------------------------------------------- /libraries/mongodb.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook:: sc-mongodb 3 | # Definition:: mongodb 4 | # 5 | # Copyright:: 2011, edelight GmbH 6 | # Authors: 7 | # Markus Korn 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | # 21 | 22 | require 'json' 23 | 24 | class Chef::ResourceDefinitionList::MongoDB 25 | # node['mongodb']['config']['mongod']['net']['port'] required for port 26 | # if node['mongodb']['use_ip_address'] 27 | # node['ipaddress'] is required 28 | # else 29 | # node['fqdn'] is required 30 | # end 31 | # 32 | # if node['fqnd'] is a vagrant host, ignore it 33 | # node['mongodb']['replica_priority'] is required 34 | # 35 | def self.cluster_up_to_date?(from_server, expected) 36 | cut_down = from_server.map do |s| 37 | other = expected.select { |e| s['_id'] == e['_id'] }.first 38 | s.select { |k, _v| other.key?(k) } 39 | end 40 | 41 | cut_down == expected 42 | end 43 | 44 | def self.create_replicaset_member(node) 45 | return {} if node['fqdn'] =~ /\.vagrantup\.com$/ 46 | 47 | port = node['mongodb']['config']['mongod']['net']['port'] 48 | host = node['mongodb']['use_ip_address'] ? node['ipaddress'] : node['fqdn'] 49 | address = "#{host}:#{port}" 50 | 51 | member = {} 52 | member['host'] = address 53 | member['arbiterOnly'] = true if node['mongodb']['replica_arbiter_only'] 54 | member['buildIndexes'] = false unless node['mongodb']['replica_build_indexes'] 55 | member['hidden'] = true if node['mongodb']['replica_hidden'] 56 | slave_delay = node['mongodb']['replica_slave_delay'] 57 | member['slaveDelay'] = slave_delay if slave_delay > 0 58 | 59 | priority = if member['buildIndexes'] == false || member['hidden'] || member['slaveDelay'] 60 | 0 61 | else 62 | node['mongodb']['replica_priority'] 63 | end 64 | member['priority'] = priority unless priority == 1 65 | tags = node['mongodb']['replica_tags'].to_hash 66 | member['tags'] = tags unless tags.empty? 67 | votes = node['mongodb']['replica_votes'] 68 | member['votes'] = votes unless votes == 1 69 | 70 | member.freeze 71 | end 72 | 73 | def self.configure_replicaset(node, name, members) 74 | # lazy require, to move loading this modules to runtime of the cookbook 75 | require 'rubygems' 76 | require 'mongo' 77 | 78 | if members.empty? && Chef::Config[:solo] 79 | Chef::Log.warn('Cannot search for member nodes with chef-solo, defaulting to single node replica set') 80 | end 81 | 82 | mongo_host = 'localhost' 83 | mongo_port = node['mongodb']['config']['mongod']['net']['port'] 84 | 85 | begin 86 | connection = nil 87 | rescue_connection_failure do 88 | connection = Mongo::Connection.new(mongo_host, mongo_port, op_timeout: 5, slave_ok: true) 89 | connection.database_names # check connection 90 | end 91 | rescue => e 92 | Chef::Log.warn("Could not connect to database: '#{mongo_host}:#{mongo_port}', reason: #{e}") 93 | return 94 | end 95 | 96 | # Want the node originating the connection to be included in the replicaset 97 | members << node unless members.any? { |m| m.name == node.name } 98 | members.sort! { |x, y| x.name <=> y.name } 99 | 100 | rs_members = members.each_with_index.map { |member, n| create_replicaset_member(member).merge('_id' => n) }.select { |m| m.key? 'host' } 101 | 102 | Chef::Log.info( 103 | "Configuring replicaset with members #{members.map { |n| n['hostname'] }.join(', ')}" 104 | ) 105 | 106 | Chef::Log.debug( 107 | "Configuring replicaset with config: #{rs_members}" 108 | ) 109 | 110 | admin = connection['admin'] 111 | cmd = BSON::OrderedHash.new 112 | cmd['replSetInitiate'] = { 113 | '_id' => name, 114 | 'members' => rs_members, 115 | } 116 | 117 | begin 118 | result = admin.command(cmd, check_response: false) 119 | rescue Mongo::OperationTimeout 120 | Chef::Log.info('Started configuring the replicaset, this will take some time, another run should run smoothly') 121 | return 122 | end 123 | if result.fetch('ok', nil) == 1 124 | # everything is fine, do nothing 125 | elsif result.fetch('errmsg', nil) =~ /(\S+) is already initiated/ || \ 126 | result.fetch('errmsg', nil) == 'already initialized' || \ 127 | result.fetch('errmsg', nil) =~ /is not empty on the initiating member/ 128 | mongo_configured_host, mongo_configured_port = \ 129 | Regexp.last_match.nil? || Regexp.last_match.length < 2 ? [mongo_host, mongo_port] : Regexp.last_match[1].split(':') 130 | begin 131 | connection = Mongo::Connection.new(mongo_configured_host, mongo_configured_port, op_timeout: 5, slave_ok: true) 132 | rescue 133 | abort("Could not connect to database: '#{mongo_host}:#{mongo_port}'") 134 | end 135 | 136 | rs_member_ips = members.each_with_index.map do |member, n| 137 | port = member['mongodb']['config']['mongod']['net']['port'] 138 | { '_id' => n, 'host' => "#{member['ipaddress']}:#{port}" } 139 | end 140 | 141 | # check if both configs are the same 142 | config = connection['local']['system']['replset'].find_one('_id' => name) 143 | Chef::Log.debug "Current members are #{config['members']} and we expect #{rs_members}" 144 | if config && cluster_up_to_date?(config['members'], rs_members) 145 | # config is up-to-date, do nothing 146 | Chef::Log.info("Replicaset '#{name}' already configured") 147 | elsif config['_id'] == name && config['members'] == rs_member_ips 148 | # config is up-to-date, but ips are used instead of hostnames, change config to hostnames 149 | Chef::Log.info("Need to convert ips to hostnames for replicaset '#{name}'") 150 | old_members = config['members'].map { |m| m['host'] } 151 | mapping = {} 152 | rs_member_ips.each do |mem_h| 153 | members.each do |n| 154 | ip, prt = mem_h['host'].split(':') 155 | mapping["#{ip}:#{prt}"] = "#{n['fqdn']}:#{prt}" if ip == n['ipaddress'] 156 | end 157 | end 158 | config['members'].map! do |m| 159 | host = mapping[m['host']] 160 | { '_id' => m['_id'], 'host' => host }.merge(rs_options[host]) 161 | end 162 | config['version'] += 1 163 | 164 | rs_connection = nil 165 | rescue_connection_failure do 166 | rs_connection = Mongo::ReplSetConnection.new(old_members) 167 | rs_connection.database_names # check connection 168 | end 169 | 170 | admin = rs_connection['admin'] 171 | cmd = BSON::OrderedHash.new 172 | cmd['replSetReconfig'] = config 173 | result = nil 174 | begin 175 | result = admin.command(cmd, check_response: false) 176 | rescue Mongo::ConnectionFailure 177 | # reconfiguring destroys existing connections, reconnect 178 | connection = Mongo::Connection.new('localhost', node['mongodb']['config']['port'], op_timeout: 5, slave_ok: true) 179 | config = connection['local']['system']['replset'].find_one('_id' => name) 180 | # Validate configuration change 181 | if config['members'] == rs_members 182 | Chef::Log.info("New config successfully applied: #{config.inspect}") 183 | else 184 | Chef::Log.error("Failed to apply new config. Current config: #{config.inspect} Target config #{rs_members}") 185 | return 186 | end 187 | end 188 | Chef::Log.error("configuring replicaset returned: #{result.inspect}") unless result.fetch('errmsg', nil).nil? 189 | else 190 | Chef::Log.info 'going to update the members of the replicaset' 191 | old_members = config['members'].dup 192 | new_members = rs_members.dup 193 | old_ids = old_members.map { |m| m['_id'] } 194 | 195 | old_members_by_host = old_members.each_with_object({}) { |m, hash| hash[m['host']] = m } 196 | new_members_by_host = new_members.each_with_object({}) { |m, hash| hash[m['host']] = m } 197 | 198 | ids = (0...256).to_a - old_ids 199 | 200 | # use the _id value when present, use a generated one from ids otherwise 201 | new_members = new_members_by_host.map { |h, m| old_members_by_host.fetch(h, {}).merge(m) } 202 | 203 | new_members.map! { |member| member.merge('_id' => member['_id'] || ids.shift) } 204 | 205 | new_config = config.dup 206 | new_config['members'] = new_members 207 | new_config['version'] += 1 208 | 209 | Chef::Log.info "after updating the members, config = #{new_config}" 210 | 211 | rs_connection = nil 212 | force = false 213 | rescue_connection_failure do 214 | case new_members.count 215 | when 0 216 | # deletes the replicaset 217 | force = true 218 | rs_connection = Mongo::Connection.new(mongo_host, mongo_port, op_timeout: 5, slave_ok: true) 219 | else 220 | rs_connection = Mongo::ReplSetConnection.new(old_members.map { |m| m['host'] }) 221 | end 222 | rs_connection.database_names # check connection 223 | end 224 | 225 | admin = rs_connection['admin'] 226 | 227 | cmd = BSON::OrderedHash.new 228 | cmd['replSetReconfig'] = new_config 229 | 230 | result = nil 231 | begin 232 | result = admin.command(cmd, force: force, check_response: false) 233 | rescue Mongo::ConnectionFailure 234 | # reconfiguring destroys existing connections, reconnect 235 | connection = Mongo::Connection.new(mongo_host, mongo_port, op_timeout: 5, slave_ok: true) 236 | config = connection['local']['system']['replset'].find_one('_id' => name) 237 | # Validate configuration change 238 | if config['members'] == rs_members 239 | Chef::Log.info("New config successfully applied: #{config.inspect}") 240 | else 241 | Chef::Log.error("Failed to apply new config. Current config: #{config.inspect} Target config #{rs_members}") 242 | return 243 | end 244 | end 245 | Chef::Log.error("configuring replicaset returned: #{result.inspect}") unless result.nil? || result.fetch('errmsg', nil).nil? 246 | end 247 | elsif !result.fetch('errmsg', nil).nil? 248 | Chef::Log.error("Failed to configure replicaset, reason: #{result.inspect}") 249 | end 250 | end 251 | 252 | def self.configure_shards(node, shard_nodes) 253 | # lazy require, to move loading this modules to runtime of the cookbook 254 | require 'rubygems' 255 | require 'mongo' 256 | 257 | shard_groups = Hash.new { |h, k| h[k] = [] } 258 | 259 | shard_nodes.each do |n| 260 | if n['recipes'].include?('sc-mongodb::replicaset') 261 | # do not include hidden members when calling addShard 262 | # see https://jira.mongodb.org/browse/SERVER-9882 263 | next if n['mongodb']['replica_hidden'] 264 | key = n['mongodb']['config']['mongod']['replication']['replSetName'] || "rs_#{n['mongodb']['shard_name']}" 265 | else 266 | key = '_single' 267 | end 268 | shard_groups[key] << "#{n['fqdn']}:#{n['mongodb']['config']['mongod']['net']['port']}" 269 | end 270 | Chef::Log.info(shard_groups.inspect) 271 | 272 | shard_members = [] 273 | shard_groups.each do |name, members| 274 | if name == '_single' 275 | shard_members += members 276 | else 277 | shard_members << "#{name}/#{members.join(',')}" 278 | end 279 | end 280 | Chef::Log.info(shard_members.inspect) 281 | 282 | mongo_port = node['mongodb']['config']['mongos']['net']['port'] 283 | 284 | begin 285 | connection = nil 286 | rescue_connection_failure do 287 | connection = Mongo::Connection.new('localhost', mongo_port, op_timeout: 5) 288 | end 289 | rescue => e 290 | Chef::Log.warn("Could not connect to database: 'localhost:#{mongo_port}', reason #{e}") 291 | return 292 | end 293 | 294 | admin = connection['admin'] 295 | 296 | # If we require authentication on mongos / mongod, need to authenticate to run these commands 297 | if node.recipe?('sc-mongodb::user_management') 298 | begin 299 | admin.authenticate(node['mongodb']['authentication']['username'], node['mongodb']['authentication']['password']) 300 | rescue Mongo::AuthenticationError => e 301 | Chef::Log.warn("Unable to authenticate with database to add shards to mongos node: #{e}") 302 | end 303 | end 304 | 305 | shard_members.each do |shard| 306 | cmd = BSON::OrderedHash.new 307 | cmd['addShard'] = shard 308 | require 'pry' 309 | # binding.pry 310 | begin 311 | result = admin.command(cmd, check_response: false) 312 | rescue Mongo::OperationTimeout 313 | result = "Adding shard '#{shard}' timed out, run the recipe again to check the result" 314 | end 315 | 316 | if result['ok'] == 0.0 317 | Chef::Log.error(result.inspect) 318 | else 319 | Chef::Log.info(result.inspect) 320 | end 321 | end 322 | end 323 | 324 | def self.configure_sharded_collections(node, sharded_collections) 325 | if sharded_collections.nil? || sharded_collections.empty? 326 | Chef::Log.warn('No sharded collections configured, doing nothing') 327 | return 328 | end 329 | 330 | # lazy require, to move loading this modules to runtime of the cookbook 331 | require 'rubygems' 332 | require 'mongo' 333 | 334 | mongo_port = node['mongodb']['config']['mongos']['net']['port'] 335 | 336 | begin 337 | connection = nil 338 | rescue_connection_failure do 339 | connection = Mongo::Connection.new('localhost', mongo_port, op_timeout: 5) 340 | end 341 | rescue => e 342 | Chef::Log.warn("Could not connect to database: 'localhost:#{mongo_port}', reason #{e}") 343 | return 344 | end 345 | 346 | admin = connection['admin'] 347 | 348 | # If we require authentication on mongos / mongod, need to authenticate to run these commands 349 | if node.recipe?('sc-mongodb::user_management') 350 | begin 351 | admin.authenticate(node['mongodb']['authentication']['username'], node['mongodb']['authentication']['password']) 352 | rescue Mongo::AuthenticationError => e 353 | Chef::Log.warn("Unable to authenticate with database to configure databased on mongos node: #{e}") 354 | end 355 | end 356 | 357 | databases = sharded_collections.keys.map { |x| x.split('.').first }.uniq 358 | Chef::Log.info("enable sharding for these databases: '#{databases.inspect}'") 359 | 360 | databases.each do |db_name| 361 | cmd = BSON::OrderedHash.new 362 | cmd['enablesharding'] = db_name 363 | begin 364 | result = admin.command(cmd, check_response: false) 365 | rescue Mongo::OperationTimeout 366 | result = "enable sharding for '#{db_name}' timed out, run the recipe again to check the result" 367 | end 368 | if result['ok'] == 0 369 | # some error 370 | errmsg = result.fetch('errmsg') 371 | if errmsg == 'already enabled' 372 | Chef::Log.info("Sharding is already enabled for database '#{db_name}', doing nothing") 373 | else 374 | Chef::Log.error("Failed to enable sharding for database #{db_name}, result was: #{result.inspect}") 375 | end 376 | else 377 | # success 378 | Chef::Log.info("Enabled sharding for database '#{db_name}'") 379 | end 380 | end 381 | 382 | sharded_collections.each do |name, key| 383 | cmd = BSON::OrderedHash.new 384 | cmd['shardcollection'] = name 385 | cmd['key'] = { key => 1 } 386 | begin 387 | result = admin.command(cmd, check_response: false) 388 | rescue Mongo::OperationTimeout 389 | result = "sharding '#{name}' on key '#{key}' timed out, run the recipe again to check the result" 390 | end 391 | if result['ok'] == 0 392 | # some error 393 | errmsg = result.fetch('errmsg') 394 | if errmsg == 'already sharded' 395 | Chef::Log.info("Sharding is already configured for collection '#{name}', doing nothing") 396 | else 397 | Chef::Log.error("Failed to shard collection #{name}, result was: #{result.inspect}") 398 | end 399 | else 400 | # success 401 | Chef::Log.info("Sharding for collection '#{result['collectionsharded']}' enabled") 402 | end 403 | end 404 | end 405 | 406 | # Ensure retry upon failure 407 | def self.rescue_connection_failure(max_retries = 30) 408 | retries = 0 409 | begin 410 | yield 411 | rescue Mongo::ConnectionFailure => ex 412 | retries += 1 413 | raise ex if retries > max_retries 414 | sleep(0.5) 415 | retry 416 | end 417 | end 418 | end 419 | --------------------------------------------------------------------------------