├── Berksfile ├── 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 ├── .gitignore ├── .travis.yml ├── recipes ├── 10gen_repo.rb ├── user_management.rb ├── mongo_gem.rb ├── replicaset.rb ├── configserver.rb ├── shard.rb ├── mongodb_org_repo.rb ├── default.rb ├── mms_backup_agent.rb ├── mms_monitoring_agent.rb ├── mongos.rb └── install.rb ├── NOTICE ├── attributes ├── users.rb ├── ulimit.rb ├── sysconfig.rb ├── dbconfig.rb ├── mms_agent.rb └── default.rb ├── Gemfile ├── resources └── user.rb ├── test ├── integration │ ├── mms_backup_agent │ │ └── bats │ │ │ └── default.bats │ ├── mms_monitoring_agent │ │ └── bats │ │ │ └── default.bats │ ├── default │ │ └── bats │ │ │ └── default.bats │ ├── configserver │ │ └── bats │ │ │ └── default.bats │ ├── default_10gen │ │ └── bats │ │ │ └── default.bats │ ├── replicaset │ │ └── bats │ │ │ └── default.bats │ ├── user_management │ │ └── bats │ │ │ └── default.bats │ └── nodes │ │ └── mongodb-configserver-001.json └── unit │ ├── config_helper_spec.rb │ ├── mms_backup_agent_spec.rb │ ├── mms_monitoring_agent_spec.rb │ ├── install_spec.rb │ └── default_spec.rb ├── Makefile ├── libraries ├── mongodb_config_helpers.rb └── mongodb.rb ├── .rubocop.yml ├── Rakefile ├── providers └── user.rb ├── .kitchen.yml ├── CHANGELOG ├── metadata.rb ├── definitions └── mongodb.rb ├── LICENSE └── README.md /Berksfile: -------------------------------------------------------------------------------- 1 | source "http://api.berkshelf.com" 2 | 3 | metadata 4 | 5 | cookbook 'chef-solo-search' 6 | -------------------------------------------------------------------------------- /templates/default/sv-mms-agent-log-run.erb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | exec svlogd -tt <%= @options[:mms_agent_log_dir] %> 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .kitchen/ 3 | .kitchen.local.yml 4 | 5 | Berksfile.lock 6 | Gemfile.lock 7 | 8 | metadata.json 9 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 1.9.3 4 | - 2.0.0 5 | - 2.1.1 6 | bundler_args: --without integration 7 | -------------------------------------------------------------------------------- /recipes/10gen_repo.rb: -------------------------------------------------------------------------------- 1 | Chef::Log.warn('10gen_repo is deprecated, use mongodb_org_repo') 2 | include_recipe 'mongodb::mongodb_org_repo' 3 | -------------------------------------------------------------------------------- /templates/default/mongodb.conf.erb: -------------------------------------------------------------------------------- 1 | # 2 | # Automatically Generated by Chef, do not edit directly! 3 | # 4 | 5 | <%= to_boost_program_options @config %> 6 | -------------------------------------------------------------------------------- /NOTICE: -------------------------------------------------------------------------------- 1 | ====================== 2 | Chef Cookbooks Notices 3 | ====================== 4 | 5 | Developed at edelight GmbH (http://www.edelight-group.com/). 6 | 7 | Contributors: 8 | 9 | * Markus Korn 10 | -------------------------------------------------------------------------------- /attributes/users.rb: -------------------------------------------------------------------------------- 1 | default['mongodb']['admin'] = { 2 | 'username' => 'admin', 3 | 'password' => 'admin', 4 | 'roles' => %w(userAdminAnyDatabase dbAdminAnyDatabase), 5 | 'database' => 'admin' 6 | } 7 | 8 | default['mongodb']['users'] = [] 9 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | gem 'berkshelf', '~> 3.1.3' 4 | gem 'chefspec', '~> 4.0' 5 | # gem 'foodcritic', '~> 3.0' 6 | gem 'rake', '~> 10.1' 7 | gem 'rubocop', '~> 0.24.0' 8 | 9 | group :integration do 10 | gem 'test-kitchen', '~> 1.2' 11 | gem 'kitchen-vagrant', '~> 0.14' 12 | end 13 | -------------------------------------------------------------------------------- /resources/user.rb: -------------------------------------------------------------------------------- 1 | actions :add, :delete, :modify 2 | 3 | attribute :username, :kind_of => String, :name_attribute => true 4 | attribute :password, :kind_of => String 5 | attribute :roles, :kind_of => Array 6 | attribute :database, :kind_of => String 7 | attribute :connection, :kind_of => Hash 8 | 9 | def initialize(*args) 10 | super 11 | @action = :add 12 | end 13 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/integration/mms_backup_agent/bats/default.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | @test "starts mms backup agent" { 4 | # should return a 0 status code if backup agent is running 5 | run service mongodb-mms-backup-agent status 6 | [ "$status" -eq 0 ] 7 | } 8 | 9 | @test "sets sslRequireValidServerCertificates to false" { 10 | run grep "sslRequireValidServerCertificates=false" /etc/mongodb-mms/backup-agent.config 11 | [ "$status" -eq 0 ] 12 | } 13 | -------------------------------------------------------------------------------- /test/integration/mms_monitoring_agent/bats/default.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | @test "starts mms monitoring agent" { 4 | # should return a 0 status code if monitoring agent is running 5 | run service mongodb-mms-monitoring-agent status 6 | [ "$status" -eq 0 ] 7 | } 8 | 9 | @test "sets sslRequireValidServerCertificates to false" { 10 | run grep "sslRequireValidServerCertificates=false" /etc/mongodb-mms/monitoring-agent.config 11 | [ "$status" -eq 0 ] 12 | } 13 | -------------------------------------------------------------------------------- /test/integration/default/bats/default.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | @test "starts mongodb" { 4 | # should return a 0 status code if mongodb is running 5 | if [ -e /etc/init.d/mongodb ]; then 6 | run /etc/init.d/mongodb status 7 | [ "$status" -eq 0 ] 8 | fi 9 | if [ -e /etc/init.d/mongod ]; then 10 | run /etc/init.d/mongod status 11 | [ "$status" -eq 0 ] 12 | fi 13 | 14 | # this catches if neither init files are present 15 | [ "$status" -eq 0 ] 16 | } 17 | -------------------------------------------------------------------------------- /test/integration/configserver/bats/default.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | @test "starts configserver" { 4 | # should return a 0 status code if mongodb is running 5 | if [ -e /etc/init.d/mongodb ]; then 6 | run /etc/init.d/mongodb status 7 | [ "$status" -eq 0 ] 8 | fi 9 | if [ -e /etc/init.d/mongod ]; then 10 | run /etc/init.d/mongod status 11 | [ "$status" -eq 0 ] 12 | fi 13 | 14 | # this catches if neither init files are present 15 | [ "$status" -eq 0 ] 16 | } 17 | -------------------------------------------------------------------------------- /test/integration/default_10gen/bats/default.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | @test "starts mongodb" { 4 | # should return a 0 status code if mongodb is running 5 | if [ -e /etc/init.d/mongodb ]; then 6 | run /etc/init.d/mongodb status 7 | [ "$status" -eq 0 ] 8 | fi 9 | if [ -e /etc/init.d/mongod ]; then 10 | run /etc/init.d/mongod status 11 | [ "$status" -eq 0 ] 12 | fi 13 | 14 | # this catches if neither init files are present 15 | [ "$status" -eq 0 ] 16 | } 17 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | COOKBOOK=mongodb 3 | BRANCH=master 4 | 5 | BUILD_DIR=../build 6 | DIST_PREFIX=$(BUILD_DIR)/$(COOKBOOK) 7 | 8 | all: metadata.json 9 | 10 | clean: 11 | -rm metadata.json 12 | 13 | metadata.json: 14 | -rm $@ 15 | knife cookbook metadata -o .. $(COOKBOOK) 16 | 17 | dist: clean metadata.json 18 | mkdir -p $(BUILD_DIR) 19 | version=`python -c "import json;c = json.load(open('metadata.json')); print c.get('version', 'UNKNOWN')"`; \ 20 | tar --exclude-vcs --exclude=Makefile -cvzf $(DIST_PREFIX)-$$version.tar.gz ../$(COOKBOOK) 21 | -------------------------------------------------------------------------------- /recipes/user_management.rb: -------------------------------------------------------------------------------- 1 | chef_gem 'mongo' 2 | 3 | users = [] 4 | admin = node['mongodb']['admin'] 5 | 6 | # If authentication is required, 7 | # add the admin to the users array for adding/updating 8 | users << admin if node['mongodb']['config']['auth'] == true 9 | 10 | users.concat(node['mongodb']['users']) 11 | 12 | # Add each user specified in attributes 13 | users.each do |user| 14 | mongodb_user user['username'] do 15 | password user['password'] 16 | roles user['roles'] 17 | database user['database'] 18 | connection node['mongodb'] 19 | action :add 20 | end 21 | end 22 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /recipes/mongo_gem.rb: -------------------------------------------------------------------------------- 1 | # The build-essential cookbook was not running during the compile phase, install gcc explicitly for rhel so native 2 | # extensions can be installed 3 | gcc = package 'gcc' do 4 | action :nothing 5 | only_if { platform_family?('rhel') } 6 | end 7 | gcc.run_action(:install) 8 | 9 | if platform_family?('rhel') 10 | sasldev_pkg = 'cyrus-sasl-devel' 11 | else 12 | sasldev_pkg = 'libsasl2-dev' 13 | end 14 | 15 | package sasldev_pkg do 16 | action :nothing 17 | end.run_action(:install) 18 | 19 | node['mongodb']['ruby_gems'].each do |gem, version| 20 | chef_gem gem do 21 | version version 22 | end 23 | end 24 | -------------------------------------------------------------------------------- /test/integration/replicaset/bats/default.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | @test "starts mongodb" { 4 | # should return a 0 status code if mongodb is running 5 | if [ -e /etc/init.d/mongodb ]; then 6 | run /etc/init.d/mongodb status 7 | [ "$status" -eq 0 ] 8 | fi 9 | if [ -e /etc/init.d/mongod ]; then 10 | run /etc/init.d/mongod status 11 | [ "$status" -eq 0 ] 12 | fi 13 | 14 | # this catches if neither init files are present 15 | [ "$status" -eq 0 ] 16 | } 17 | 18 | @test "replicaset initialized" { 19 | run mongo --eval "rs.status().ok" 20 | [ "$status" -eq 0 ] 21 | [ "${lines[@]:(-1)}" -eq 1 ] 22 | } 23 | -------------------------------------------------------------------------------- /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 18 | .join("\n") 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /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 | if [ "x$ENABLE_MONGOD" = "xyes" ]; then 23 | exec start-stop-daemon --start --quiet --chuid $DAEMON_USER --exec $DAEMON -- $DAEMON_OPTS 24 | fi 25 | end script 26 | -------------------------------------------------------------------------------- /test/integration/user_management/bats/default.bats: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bats 2 | 3 | @test "starts mongodb" { 4 | # should return a 0 status code if mongodb is running 5 | if [ -e /etc/init.d/mongodb ]; then 6 | run /etc/init.d/mongodb status 7 | [ "$status" -eq 0 ] 8 | fi 9 | if [ -e /etc/init.d/mongod ]; then 10 | run /etc/init.d/mongod status 11 | [ "$status" -eq 0 ] 12 | fi 13 | 14 | # this catches if neither init files are present 15 | [ "$status" -eq 0 ] 16 | } 17 | 18 | @test "requires authentication" { 19 | mongo --eval "db.stats().ok" 20 | ! [ $? -eq 1 ] 21 | } 22 | 23 | @test "admin user created" { 24 | mongo admin -u admin -p admin --eval "db.stats().ok" 25 | [ $? -eq 0 ] 26 | } 27 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | HashSyntax: 2 | EnforcedStyle: hash_rockets 3 | 4 | LineLength: 5 | Max: 200 # TODO: reduce to 80 6 | 7 | MethodLength: 8 | Max: 155 # TODO: reduce to 10 9 | 10 | ClassLength: 11 | Max: 264 # TODO: reduce to 100 12 | 13 | BlockNesting: 14 | Max: 4 # TODO: reduce to 3 15 | 16 | CyclomaticComplexity: 17 | Enabled: false # TODO: enable 18 | 19 | Documentation: 20 | Enabled: false # TODO: enable 21 | 22 | NumericLiterals: 23 | MinDigits: 6 24 | 25 | Encoding: 26 | Enabled: false # TODO: enable (?) 27 | 28 | FileName: 29 | Exclude: 30 | - mms-agent.rb 31 | 32 | ClassAndModuleChildren: 33 | EnforcedStyle: compact 34 | 35 | DoubleNegation: 36 | Enabled: false # TODO: mms agent recipes could be improved to not use double negation -------------------------------------------------------------------------------- /attributes/sysconfig.rb: -------------------------------------------------------------------------------- 1 | include_attribute 'mongodb::default' 2 | 3 | default['mongodb']['sysconfig']['DAEMON'] = '/usr/bin/$NAME' 4 | default['mongodb']['sysconfig']['DAEMON_USER'] = node['mongodb']['user'] 5 | default['mongodb']['sysconfig']['DAEMON_OPTS'] = "--config #{node['mongodb']['dbconfig_file']}" 6 | default['mongodb']['sysconfig']['CONFIGFILE'] = node['mongodb']['dbconfig_file'] 7 | default['mongodb']['sysconfig']['ENABLE_MONGODB'] = 'yes' 8 | 9 | # these are backward compat purposes 10 | default['mongodb']['sysconfig']['DAEMONUSER'] = node['mongodb']['sysconfig']['DAEMON_USER'] 11 | default['mongodb']['sysconfig']['ENABLE_MONGOD'] = node['mongodb']['sysconfig']['ENABLE_MONGODB'] 12 | default['mongodb']['sysconfig']['ENABLE_MONGO'] = node['mongodb']['sysconfig']['ENABLE_MONGODB'] 13 | -------------------------------------------------------------------------------- /test/unit/mms_backup_agent_spec.rb: -------------------------------------------------------------------------------- 1 | require 'chefspec' 2 | require 'chefspec/berkshelf' 3 | 4 | describe 'mongodb::mms_backup_agent' do 5 | let(:chef_run) do 6 | ChefSpec::Runner.new(:platform => 'ubuntu', :version => '12.04') do |n| 7 | n.set.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 | 19 | end 20 | -------------------------------------------------------------------------------- /test/unit/mms_monitoring_agent_spec.rb: -------------------------------------------------------------------------------- 1 | require 'chefspec' 2 | require 'chefspec/berkshelf' 3 | 4 | describe 'mongodb::mms_monitoring_agent' do 5 | let(:chef_run) do 6 | ChefSpec::Runner.new(:platform => 'ubuntu', :version => '12.04') do |n| 7 | n.set.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 | 19 | end 20 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | 3 | require 'rake' 4 | require 'rake/tasklib' 5 | require 'rake/testtask' 6 | 7 | require 'rspec/core/rake_task' 8 | RSpec::Core::RakeTask.new do |t| 9 | t.rspec_opts = 'test/unit' 10 | end 11 | 12 | require 'rubocop/rake_task' 13 | desc 'Run RuboCop to check style' 14 | RuboCop::RakeTask.new(:rubocop) do |task| 15 | task.patterns = ['**/*.rb'] 16 | # only show the files with failures 17 | #task.formatters = ['files'] 18 | # don't abort rake on failure 19 | task.fail_on_error = false 20 | task.options = ['-D'] 21 | end 22 | 23 | begin 24 | require 'kitchen/rake_tasks' 25 | Kitchen::RakeTasks.new 26 | rescue LoadError 27 | warn 'Could not load kitchen rake tasks, skipping' 28 | end 29 | 30 | # aliases 31 | task :test => :spec 32 | task :lint => [:rubocop] 33 | task :default => [:lint, :test] 34 | task :all => [:test, 'kitchen:all'] 35 | -------------------------------------------------------------------------------- /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/unit/install_spec.rb: -------------------------------------------------------------------------------- 1 | require 'chefspec' 2 | require 'chefspec/berkshelf' 3 | require 'fauxhai' 4 | 5 | describe 'mongodb::default' do 6 | let(:chef_run) do 7 | ChefSpec::Runner.new( 8 | :platform => 'ubuntu', 9 | :version => '12.04' 10 | ) 11 | end 12 | 13 | it 'should include install recipe, and enable mongodb service' do 14 | chef_run.converge(described_recipe) 15 | expect(chef_run).to include_recipe('mongodb::install') 16 | expect(chef_run).to enable_service 'mongodb' 17 | end 18 | 19 | it 'package install mongodb-org via 10gen' do 20 | chef_run.node.set.mongodb.install_method = '10gen' 21 | chef_run.converge(described_recipe) 22 | expect(chef_run).to include_recipe('mongodb::10gen_repo') 23 | expect(chef_run).to include_recipe('mongodb::install') 24 | expect(chef_run).to install_package 'mongodb-org' 25 | expect(chef_run).to enable_service 'mongodb' 26 | end 27 | 28 | it 'package install mongodb-org via mongodb-org' do 29 | chef_run.node.set.mongodb.install_method = 'mongodb-org' 30 | chef_run.converge(described_recipe) 31 | expect(chef_run).to include_recipe('mongodb::10gen_repo') 32 | expect(chef_run).to include_recipe('mongodb::install') 33 | expect(chef_run).to install_package 'mongodb-org' 34 | expect(chef_run).to enable_service 'mongodb' 35 | end 36 | 37 | end 38 | -------------------------------------------------------------------------------- /test/unit/default_spec.rb: -------------------------------------------------------------------------------- 1 | require 'chefspec' 2 | require 'chefspec/berkshelf' 3 | require 'fauxhai' 4 | 5 | describe 'mongodb::default' do 6 | let(:chef_run) do 7 | ChefSpec::Runner.new( 8 | :platform => 'ubuntu', 9 | :version => '12.04' 10 | ) 11 | end 12 | 13 | it 'should install and enable mongodb' do 14 | chef_run.converge(described_recipe) 15 | expect(chef_run).to enable_service 'mongodb' 16 | expect(chef_run).to include_recipe('mongodb::install') 17 | end 18 | 19 | it 'should disable logpath when syslog is set' do 20 | chef_run.node.set.mongodb.config.syslog = true 21 | chef_run.converge(described_recipe) 22 | expect(chef_run).to_not create_directory('/var/log/mongodb') 23 | end 24 | 25 | it 'should be able to set logpath to nil, and wont create' do 26 | chef_run.node.set.mongodb.config.logpath = '/tmp/mongodb_config_logpath/logfile.log' 27 | chef_run.node.set.mongodb.config.logpath = nil 28 | chef_run.converge(described_recipe) 29 | expect(chef_run).to_not create_directory('/tmp/mongodb_config_logpath') 30 | end 31 | 32 | it 'should by default create a logpath' do 33 | chef_run.node.set.mongodb.config.logpath = '/tmp/mongodb_config_logpath/logfile.log' 34 | chef_run.converge(described_recipe) 35 | expect(chef_run).to create_directory('/tmp/mongodb_config_logpath') 36 | end 37 | end 38 | -------------------------------------------------------------------------------- /recipes/replicaset.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: mongodb 3 | # Recipe:: replicaset 4 | # 5 | # Copyright 2011, 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 | node.set['mongodb']['is_replicaset'] = true 21 | node.set['mongodb']['cluster_name'] = node['mongodb']['cluster_name'] 22 | 23 | include_recipe 'mongodb::install' 24 | include_recipe 'mongodb::mongo_gem' 25 | 26 | unless node['mongodb']['is_shard'] 27 | mongodb_instance node['mongodb']['instance_name'] do 28 | mongodb_type 'mongod' 29 | port node['mongodb']['config']['port'] 30 | logpath node['mongodb']['config']['logpath'] 31 | dbpath node['mongodb']['config']['dbpath'] 32 | replicaset node 33 | enable_rest node['mongodb']['config']['rest'] 34 | smallfiles node['mongodb']['config']['smallfiles'] 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /recipes/configserver.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: mongodb 3 | # Recipe:: configserver 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 | node.set['mongodb']['is_configserver'] = true 23 | node.set['mongodb']['cluster_name'] = node['mongodb']['cluster_name'] 24 | node.set['mongodb']['shard_name'] = node['mongodb']['shard_name'] 25 | 26 | include_recipe 'mongodb::install' 27 | 28 | # mongodb_instance will set configsvr = true in the config file. 29 | # http://docs.mongodb.org/manual/reference/configuration-options/#sharded-cluster-options 30 | # we still explicitly set the port and small files. 31 | mongodb_instance node['mongodb']['instance_name'] do 32 | mongodb_type 'configserver' 33 | port node['mongodb']['config']['port'] 34 | logpath node['mongodb']['config']['logpath'] 35 | dbpath node['mongodb']['config']['dbpath'] 36 | enable_rest node['mongodb']['config']['rest'] 37 | smallfiles node['mongodb']['config']['smallfiles'] 38 | end 39 | -------------------------------------------------------------------------------- /attributes/dbconfig.rb: -------------------------------------------------------------------------------- 1 | # All the configuration files that can be dumped 2 | # the attribute-based-configuration 3 | # dump anything into default['mongodb']['config'][] = 4 | # these options are in the order of mongodb docs 5 | 6 | include_attribute 'mongodb::default' 7 | 8 | default['mongodb']['config']['port'] = 27017 9 | default['mongodb']['config']['bind_ip'] = '0.0.0.0' 10 | # Workaround for opscode/chef#1507, which prevents users from 11 | # unsetting our default with a nil override. 12 | # So we make sure to unset logpath when syslog is set since the two 13 | # settings are incompatible. 14 | # For more information see: edelight/chef-mongodb#310 15 | unless node['mongodb']['config']['syslog'] 16 | default['mongodb']['config']['logpath'] = '/var/log/mongodb/mongodb.log' 17 | end 18 | default['mongodb']['config']['logappend'] = true 19 | # The platform_family? syntax in attributes files was added in Chef 11 20 | # if node.platform_family?("rhel", "fedora") then 21 | case node['platform_family'] 22 | when 'rhel', 'fedora' 23 | default['mongodb']['config']['fork'] = true 24 | default['mongodb']['config']['pidfilepath'] = '/var/run/mongodb/mongodb.pid' 25 | else 26 | default['mongodb']['config']['fork'] = false 27 | end 28 | default['mongodb']['config']['dbpath'] = '/var/lib/mongodb' 29 | default['mongodb']['config']['nojournal'] = false 30 | default['mongodb']['config']['rest'] = false 31 | default['mongodb']['config']['smallfiles'] = false 32 | default['mongodb']['config']['oplogSize'] = nil 33 | 34 | default['mongodb']['config']['replSet'] = nil 35 | default['mongodb']['config']['keyFile'] = '/etc/mongodb.key' if node['mongodb']['key_file_content'] 36 | -------------------------------------------------------------------------------- /recipes/shard.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: mongodb 3 | # Recipe:: shard 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 | node.set['mongodb']['is_shard'] = true 23 | node.set['mongodb']['shard_name'] = node['mongodb']['shard_name'] 24 | node.set['mongodb']['is_replicaset'] = node['mongodb']['is_replicaset'] 25 | node.set['mongodb']['cluster_name'] = node['mongodb']['cluster_name'] 26 | 27 | include_recipe 'mongodb::install' 28 | 29 | # we are not starting the shard service with the --shardsvr 30 | # commandline option because right now this only changes the port it's 31 | # running on, and we are overwriting this port anyway. 32 | mongodb_instance node['mongodb']['instance_name'] do 33 | mongodb_type 'shard' 34 | port node['mongodb']['config']['port'] 35 | logpath node['mongodb']['config']['logpath'] 36 | dbpath node['mongodb']['config']['dbpath'] 37 | replicaset node if node['mongodb']['is_replicaset'] 38 | enable_rest node['mongodb']['config']['rest'] 39 | smallfiles node['mongodb']['config']['smallfiles'] 40 | end 41 | -------------------------------------------------------------------------------- /recipes/mongodb_org_repo.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: mongodb 3 | # Recipe:: 10gen_repo 4 | # 5 | # Copyright 2011, edelight GmbH 6 | # Authors: 7 | # Miquel Torres 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 | # Sets up the repositories for stable mongodb-org packages found here: 23 | # http://www.mongodb.org/downloads#packages 24 | node.override['mongodb']['package_name'] = 'mongodb-org' 25 | 26 | case node['platform_family'] 27 | when 'debian' 28 | # Adds the repo: http://www.mongodb.org/display/DOCS/Ubuntu+and+Debian+packages 29 | apt_repository 'mongodb' do 30 | uri "http://downloads-distro.mongodb.org/repo/#{node[:mongodb][:apt_repo]}" 31 | distribution 'dist' 32 | components ['10gen'] 33 | keyserver 'hkp://keyserver.ubuntu.com:80' 34 | key '7F0CEB10' 35 | action :add 36 | end 37 | 38 | when 'rhel', 'fedora' 39 | yum_repository 'mongodb' do 40 | description 'mongodb RPM Repository' 41 | baseurl "http://downloads-distro.mongodb.org/repo/redhat/os/#{node['kernel']['machine'] =~ /x86_64/ ? 'x86_64' : 'i686'}" 42 | action :create 43 | gpgcheck false 44 | enabled true 45 | end 46 | 47 | else 48 | # pssst build from source 49 | Chef::Log.warn("Adding the #{node['platform_family']} 10gen repository is not yet not supported by this cookbook") 50 | end 51 | -------------------------------------------------------------------------------- /recipes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: mongodb 3 | # Recipe:: default 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 | include_recipe 'mongodb::install' 23 | 24 | # allow mongodb_instance to run if recipe isn't included 25 | allow_mongodb_instance_run = true 26 | conflicting_recipes = %w(mongodb::replicaset mongodb::shard mongodb::configserver mongodb::mongos mongodb::mms_agent) 27 | chef_major_version = Chef::VERSION.split('.').first.to_i 28 | if chef_major_version < 11 29 | conflicting_recipes.each do |recipe| 30 | allow_mongodb_instance_run &&= false if node.recipe?(recipe) 31 | end 32 | else 33 | conflicting_recipes.each do |recipe| 34 | allow_mongodb_instance_run &&= false if node.run_context.loaded_recipe?(recipe) 35 | end 36 | end 37 | 38 | if allow_mongodb_instance_run 39 | mongodb_instance node['mongodb']['instance_name'] do 40 | mongodb_type 'mongod' 41 | bind_ip node['mongodb']['config']['bind_ip'] 42 | port node['mongodb']['config']['port'] 43 | logpath node['mongodb']['config']['logpath'] 44 | dbpath node['mongodb']['config']['dbpath'] 45 | enable_rest node['mongodb']['config']['rest'] 46 | smallfiles node['mongodb']['config']['smallfiles'] 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /recipes/mms_backup_agent.rb: -------------------------------------------------------------------------------- 1 | Chef::Log.warn 'Found empty mms_agent.api_key attribute' if node['mongodb']['mms_agent']['api_key'].nil? 2 | 3 | arch = node[:kernel][:machine] 4 | agent_type = 'backup' 5 | package = node['mongodb']['mms_agent']['package_url'] % { :agent_type => agent_type } 6 | package_opts = '' 7 | 8 | case node.platform_family 9 | when 'debian' 10 | arch = 'amd64' if arch == 'x86_64' 11 | package = "#{package}_#{node[:mongodb][:mms_agent][:backup][:version]}_#{arch}.deb" 12 | provider = Chef::Provider::Package::Dpkg 13 | # Without this, if the package changes the config files that we rewrite install fails 14 | package_opts = '--force-confold' 15 | when 'rhel' 16 | package = "#{package}-#{node[:mongodb][:mms_agent][:backup][:version]}.#{arch}.rpm" 17 | provider = Chef::Provider::Package::Rpm 18 | else 19 | Chef::Log.warn('Unsupported platform family for MMS Agent.') 20 | return 21 | end 22 | 23 | remote_file "#{Chef::Config[:file_cache_path]}/mongodb-mms-backup-agent" do 24 | source package 25 | end 26 | 27 | package 'mongodb-mms-backup-agent' do 28 | source "#{Chef::Config[:file_cache_path]}/mongodb-mms-backup-agent" 29 | provider provider 30 | options package_opts 31 | end 32 | 33 | template '/etc/mongodb-mms/backup-agent.config' do 34 | source 'mms_agent_config.erb' 35 | owner node['mongodb']['mms_agent']['user'] 36 | group node['mongodb']['mms_agent']['group'] 37 | mode 0600 38 | variables( 39 | :config => node['mongodb']['mms_agent']['backup'] 40 | ) 41 | action :create 42 | notifies :restart, 'service[mongodb-mms-backup-agent]', :delayed 43 | end 44 | 45 | service 'mongodb-mms-backup-agent' do 46 | provider Chef::Provider::Service::Upstart if node['mongodb']['apt_repo'] == 'ubuntu-upstart' 47 | # restart is broken on rhel (MMS-1597) 48 | supports :start => true, :stop => true, :restart => true, :status => true 49 | action :nothing 50 | end 51 | -------------------------------------------------------------------------------- /recipes/mms_monitoring_agent.rb: -------------------------------------------------------------------------------- 1 | Chef::Log.warn 'Found empty mms_agent.api_key attribute' if node['mongodb']['mms_agent']['api_key'].nil? 2 | 3 | arch = node[:kernel][:machine] 4 | agent_type = 'monitoring' 5 | package = node['mongodb']['mms_agent']['package_url'] % { :agent_type => agent_type } 6 | package_opts = '' 7 | 8 | case node.platform_family 9 | when 'debian' 10 | arch = 'amd64' if arch == 'x86_64' 11 | package = "#{package}_#{node[:mongodb][:mms_agent][:monitoring][:version]}_#{arch}.deb" 12 | provider = Chef::Provider::Package::Dpkg 13 | # Without this, if the package changes the config files that we rewrite install fails 14 | package_opts = '--force-confold' 15 | when 'rhel' 16 | package = "#{package}-#{node[:mongodb][:mms_agent][:monitoring][:version]}.#{arch}.rpm" 17 | provider = Chef::Provider::Package::Rpm 18 | else 19 | Chef::Log.warn('Unsupported platform family for MMS Agent.') 20 | return 21 | end 22 | 23 | remote_file "#{Chef::Config[:file_cache_path]}/mongodb-mms-monitoring-agent" do 24 | source package 25 | end 26 | 27 | package 'mongodb-mms-monitoring-agent' do 28 | source "#{Chef::Config[:file_cache_path]}/mongodb-mms-monitoring-agent" 29 | provider provider 30 | options package_opts 31 | end 32 | 33 | template '/etc/mongodb-mms/monitoring-agent.config' do 34 | source 'mms_agent_config.erb' 35 | owner node['mongodb']['mms_agent']['user'] 36 | group node['mongodb']['mms_agent']['group'] 37 | mode 0600 38 | variables( 39 | :config => node['mongodb']['mms_agent']['monitoring'] 40 | ) 41 | action :create 42 | notifies :restart, 'service[mongodb-mms-monitoring-agent]', :delayed 43 | end 44 | 45 | service 'mongodb-mms-monitoring-agent' do 46 | provider Chef::Provider::Service::Upstart if node['mongodb']['apt_repo'] == 'ubuntu-upstart' 47 | # restart is broken on rhel (MMS-1597) 48 | supports :start => true, :stop => true, :restart => true, :status => true 49 | action :nothing 50 | end 51 | -------------------------------------------------------------------------------- /attributes/mms_agent.rb: -------------------------------------------------------------------------------- 1 | default['mongodb']['mms_agent']['api_key'] = nil 2 | default['mongodb']['mms_agent']['package_url'] = 'https://mms.mongodb.com/download/agent/%{agent_type}/mongodb-mms-%{agent_type}-agent' 3 | 4 | default['mongodb']['mms_agent']['monitoring']['version'] = '2.2.0.70-1' 5 | default['mongodb']['mms_agent']['monitoring']['mmsApiKey'] = node['mongodb']['mms_agent']['api_key'] 6 | default['mongodb']['mms_agent']['monitoring']['mmsBaseUrl'] = 'https://mms.mongodb.com' 7 | default['mongodb']['mms_agent']['monitoring']['configCollectionsEnabled'] = true 8 | default['mongodb']['mms_agent']['monitoring']['configDatabasesEnabled'] = true 9 | default['mongodb']['mms_agent']['monitoring']['throttlePassesShardChunkCounts'] = 10 10 | default['mongodb']['mms_agent']['monitoring']['throttlePassesDbstats'] = 20 11 | default['mongodb']['mms_agent']['monitoring']['throttlePassesOplog'] = 10 12 | default['mongodb']['mms_agent']['monitoring']['disableProfileDataCollection'] = false 13 | default['mongodb']['mms_agent']['monitoring']['disableGetLogsDataCollection'] = false 14 | default['mongodb']['mms_agent']['monitoring']['disableLocksAndRecordStatsDataCollection'] = false 15 | default['mongodb']['mms_agent']['monitoring']['enableMunin'] = true 16 | default['mongodb']['mms_agent']['monitoring']['useSslForAllConnections'] = false 17 | default['mongodb']['mms_agent']['monitoring']['sslRequireValidServerCertificates'] = false 18 | 19 | default['mongodb']['mms_agent']['backup']['version'] = '2.1.0.106-1' 20 | default['mongodb']['mms_agent']['backup']['mmsApiKey'] = node['mongodb']['mms_agent']['api_key'] 21 | default['mongodb']['mms_agent']['backup']['mothership'] = 'api-backup.mongodb.com' 22 | default['mongodb']['mms_agent']['backup']['https'] = true 23 | default['mongodb']['mms_agent']['backup']['sslRequireValidServerCertificates'] = false 24 | 25 | default['mongodb']['mms_agent']['user'] = 'mongodb-mms-agent' 26 | default['mongodb']['mms_agent']['group'] = 'mongodb-mms-agent' 27 | -------------------------------------------------------------------------------- /recipes/mongos.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: mongodb 3 | # Recipe:: mongos 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 | node.set['mongodb']['is_mongos'] = true 23 | node.set['mongodb']['shard_name'] = node['mongodb']['shard_name'] 24 | node.override['mongodb']['instance_name'] = 'mongos' 25 | 26 | include_recipe 'mongodb::install' 27 | include_recipe 'mongodb::mongo_gem' 28 | 29 | service node[:mongodb][:default_init_name] do 30 | action [:disable, :stop] 31 | end 32 | 33 | configsrvs = search( 34 | :node, 35 | "mongodb_cluster_name:#{node['mongodb']['cluster_name']} AND \ 36 | mongodb_is_configserver:true AND \ 37 | chef_environment:#{node.chef_environment}" 38 | ) 39 | 40 | if configsrvs.length != 1 && configsrvs.length != 3 41 | Chef::Log.error("Found #{configsrvs.length} configservers, need either one or three of them") 42 | fail 'Wrong number of configserver nodes' unless Chef::Config[:solo] 43 | end 44 | 45 | mongodb_instance node['mongodb']['instance_name'] do 46 | mongodb_type 'mongos' 47 | port node['mongodb']['config']['port'] 48 | logpath node['mongodb']['config']['logpath'] 49 | dbpath node['mongodb']['config']['dbpath'] 50 | configservers configsrvs 51 | enable_rest node['mongodb']['config']['rest'] 52 | smallfiles node['mongodb']['config']['smallfiles'] 53 | end 54 | -------------------------------------------------------------------------------- /providers/user.rb: -------------------------------------------------------------------------------- 1 | def user_exists?(username, connection) 2 | connection['admin']['system.users'].find(:user => username).count > 0 3 | end 4 | 5 | def add_user(username, password, roles = [], database) 6 | require 'rubygems' 7 | require 'mongo' 8 | 9 | connection = retrieve_db 10 | admin = connection.db('admin') 11 | db = connection.db(database) 12 | 13 | # Check if user is admin / admin, and warn that this should 14 | # be overridden to unique values 15 | if username == 'admin' && password == 'admin' 16 | Chef::Log.warn('Default username / password detected for admin user'); 17 | Chef::Log.warn('These should be overridden to different, unique values'); 18 | end 19 | 20 | # If authentication is required on database 21 | # must authenticate as a userAdmin after an admin user has been created 22 | # this will fail on the first attempt, but user will still be created 23 | # because of the localhost exception 24 | if node['mongodb']['config']['auth'] == true 25 | begin 26 | admin.authenticate(@new_resource.connection['admin']['username'], @new_resource.connection['admin']['password']) 27 | rescue Mongo::AuthenticationError => e 28 | Chef::Log.warn("Unable to authenticate as admin user. If this is a fresh install, ignore warning: #{e}") 29 | end 30 | end 31 | 32 | # Create the user if they don't exist 33 | # Update the user if they already exist 34 | db.add_user(username, password, false, :roles => roles) 35 | Chef::Log.info("Created or updated user #{username} on #{database}") 36 | end 37 | 38 | # Drop a user from the database specified 39 | def delete_user(username, database) 40 | require 'rubygems' 41 | require 'mongo' 42 | 43 | connection = retrieve_db 44 | admin = connection.db('admin') 45 | db = connection.db(database) 46 | 47 | admin.authenticate(@new_resource.connection['admin']['username'], @new_resource.connection['admin']['password']) 48 | 49 | if user_exists?(username, connection) 50 | db.remove_user(username) 51 | Chef::Log.info("Deleted user #{username} on #{database}") 52 | else 53 | Chef::Log.warn("Unable to delete non-existent user #{username} on #{database}") 54 | end 55 | end 56 | 57 | # Get the MongoClient connection 58 | def retrieve_db 59 | require 'rubygems' 60 | require 'mongo' 61 | 62 | Mongo::MongoClient.new( 63 | @new_resource.connection['host'], 64 | @new_resource.connection['port'], 65 | :connect_timeout => 15, 66 | :slave_ok => true 67 | ) 68 | end 69 | 70 | action :add do 71 | add_user(new_resource.username, new_resource.password, new_resource.roles, new_resource.database) 72 | end 73 | 74 | action :delete do 75 | delete_user(new_resource.username, new_resource.database) 76 | end 77 | 78 | action :modify do 79 | add_user(new_resource.username, new_resource.password, new_resource.roles, new_resource.database) 80 | end 81 | -------------------------------------------------------------------------------- /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=mongod 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 | echo -n $"Starting <%= @provides %>: " 42 | daemon --user "$DAEMON_USER" $NUMACTL $DAEMON $DAEMON_OPTS 43 | RETVAL=$? 44 | echo 45 | [ $RETVAL -eq 0 ] && touch $SUBSYS_LOCK_FILE 46 | } 47 | 48 | stop() 49 | { 50 | echo -n $"Stopping <%= @provides %>: " 51 | if test "x$PIDFILE" != "x"; then 52 | killproc -p $PIDFILE -d 300 $DAEMON 53 | else 54 | killproc -d 300 $DAEMON 55 | fi 56 | RETVAL=$? 57 | echo 58 | [ $RETVAL -eq 0 ] && rm -f $SUBSYS_LOCK_FILE 59 | } 60 | 61 | restart () { 62 | stop 63 | start 64 | } 65 | 66 | ulimit -f <%= @ulimit['fsize'] %> 67 | ulimit -t <%= @ulimit['cpu'] %> 68 | ulimit -v <%= @ulimit['as'] %> 69 | ulimit -n <%= @ulimit['nofile'] %> 70 | ulimit -m <%= @ulimit['rss'] %> 71 | ulimit -u <%= @ulimit['nproc'] %> 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/install.rb: -------------------------------------------------------------------------------- 1 | # install the 10gen repo if necessary 2 | include_recipe 'mongodb::10gen_repo' if %w(10gen mongodb-org).include?(node['mongodb']['install_method']) 3 | 4 | # prevent-install defaults, but don't overwrite 5 | file node['mongodb']['sysconfig_file'] do 6 | content 'ENABLE_MONGODB=no' 7 | group node['mongodb']['root_group'] 8 | owner 'root' 9 | mode 0644 10 | action :create_if_missing 11 | end 12 | 13 | # just-in-case config file drop 14 | template node['mongodb']['dbconfig_file'] do 15 | cookbook node['mongodb']['template_cookbook'] 16 | source node['mongodb']['dbconfig_file_template'] 17 | group node['mongodb']['root_group'] 18 | owner 'root' 19 | mode 0644 20 | variables( 21 | :config => node['mongodb']['config'] 22 | ) 23 | helpers MongoDBConfigHelpers 24 | action :create_if_missing 25 | end 26 | 27 | # and we install our own init file 28 | if node['mongodb']['apt_repo'] == 'ubuntu-upstart' 29 | init_file = File.join(node['mongodb']['init_dir'], "#{node['mongodb']['default_init_name']}.conf") 30 | mode = '0644' 31 | else 32 | init_file = File.join(node['mongodb']['init_dir'], "#{node['mongodb']['default_init_name']}") 33 | mode = '0755' 34 | end 35 | 36 | # Reload systemctl for RHEL 7+ after modifying the init file. 37 | execute 'mongodb-systemctl-daemon-reload' do 38 | command 'systemctl daemon-reload' 39 | action :nothing 40 | end 41 | 42 | template init_file do 43 | cookbook node['mongodb']['template_cookbook'] 44 | source node['mongodb']['init_script_template'] 45 | group node['mongodb']['root_group'] 46 | owner 'root' 47 | mode mode 48 | variables( 49 | :provides => 'mongod', 50 | :dbconfig_file => node['mongodb']['dbconfig_file'], 51 | :sysconfig_file => node['mongodb']['sysconfig_file'], 52 | :ulimit => node['mongodb']['ulimit'], 53 | :bind_ip => node['mongodb']['config']['bind_ip'], 54 | :port => node['mongodb']['config']['port'] 55 | ) 56 | action :create_if_missing 57 | 58 | if(platform_family?('rhel') && node['platform_version'].to_i >= 7) 59 | notifies :run, 'execute[mongodb-systemctl-daemon-reload]', :immediately 60 | end 61 | end 62 | 63 | if node['mongodb']['install_method'] != 'none' 64 | case node['platform_family'] 65 | when 'debian' 66 | # this options lets us bypass complaint of pre-existing init file 67 | # necessary until upstream fixes ENABLE_MONGOD/DB flag 68 | packager_opts = '-o Dpkg::Options::="--force-confold" --force-yes' 69 | when 'rhel' 70 | # Add --nogpgcheck option when package is signed 71 | # see: https://jira.mongodb.org/browse/SERVER-8770 72 | packager_opts = '--nogpgcheck' 73 | else 74 | packager_opts = '' 75 | end 76 | 77 | # install 78 | package node[:mongodb][:package_name] do 79 | options packager_opts 80 | action :install 81 | version node[:mongodb][:package_version] 82 | end 83 | end 84 | 85 | # Create keyFile if specified 86 | if node[:mongodb][:key_file_content] 87 | file node[:mongodb][:config][:keyFile] do 88 | owner node[:mongodb][:user] 89 | group node[:mongodb][:group] 90 | mode '0600' 91 | backup false 92 | content node[:mongodb][:key_file_content] 93 | end 94 | end 95 | -------------------------------------------------------------------------------- /.kitchen.yml: -------------------------------------------------------------------------------- 1 | --- 2 | driver: 3 | name: vagrant 4 | customize: 5 | memory: 512 6 | 7 | provisioner: 8 | name: chef_solo 9 | # require_chef_omnibus: latest # will install on each run 10 | require_chef_omnibus: 11.8.2 11 | 12 | platforms: 13 | 14 | - name: ubuntu-12.04 15 | run_list: 16 | - "recipe[apt]" 17 | 18 | - name: ubuntu-10.04 19 | run_list: 20 | - "recipe[apt]" 21 | 22 | - name: debian-7.2.0 23 | run_list: 24 | - "recipe[apt]" 25 | 26 | - name: centos-7.0 27 | run_list: 28 | - "recipe[yum]" 29 | - "recipe[yum-epel]" 30 | 31 | - name: centos-6.5 32 | run_list: 33 | - "recipe[yum]" 34 | - "recipe[yum-epel]" 35 | 36 | - name: centos-5.10 37 | run_list: 38 | - "recipe[yum]" 39 | - "recipe[yum-epel]" 40 | 41 | # not going to try until 42 | # https://jira.mongodb.org/browse/SERVER-7285 is closed 43 | #- name: fedora-19 44 | #run_list: 45 | #- "recipe[yum]" 46 | 47 | #- name: fedora-20 48 | #run_list: 49 | #- "recipe[yum]" 50 | 51 | # maybe again oneday 52 | #- name: freebsd-9.2 53 | #run_list: 54 | #- "recipe[yum]" 55 | 56 | suites: 57 | - name: default 58 | run_list: 59 | - "recipe[mongodb]" 60 | 61 | - name: default_mongodb_org_repo 62 | run_list: 63 | - "recipe[mongodb]" 64 | attributes: 65 | mongodb: 66 | install_method: mongodb-org 67 | 68 | - name: replicaset 69 | run_list: 70 | - "recipe[chef-solo-search]" 71 | - "recipe[mongodb::replicaset]" 72 | attributes: 73 | mongodb: 74 | cluster_name: kitchen 75 | config: 76 | replSet: kitchen 77 | excludes: 78 | # Upstart is not starting mongodb, status script returns 0 and there are no useful logs. wat? 79 | - ubuntu-10.04 80 | 81 | - name: configserver 82 | run_list: 83 | - "recipe[mongodb::configserver]" 84 | 85 | - name: mongos 86 | run_list: 87 | - "recipe[chef-solo-search]" 88 | - "recipe[mongodb::mongos]" 89 | attributes: 90 | mongodb: 91 | cluster_name: default 92 | config: 93 | configdb: my_configserver 94 | 95 | - name: mms_monitoring_agent 96 | run_list: 97 | - "recipe[mongodb::mms_monitoring_agent]" 98 | attributes: 99 | mongodb: 100 | mms_agent: 101 | api_key: "random key" 102 | monitoring: 103 | sslRequireValidServerCertificates: false 104 | excludes: 105 | # Upstart script uses setuid which is not present in this version 106 | - ubuntu-10.04 107 | # Upstart is not present 108 | - debian-7.2.0 109 | # Package does not create the user 110 | - centos-5.10 111 | 112 | - name: mms_backup_agent 113 | run_list: 114 | - "recipe[mongodb::mms_backup_agent]" 115 | attributes: 116 | mongodb: 117 | mms_agent: 118 | api_key: "random key" 119 | backup: 120 | sslRequireValidServerCertificates: false 121 | excludes: 122 | # Upstart script uses setuid which is not present in this version 123 | - ubuntu-10.04 124 | # Upstart is not present 125 | - debian-7.2.0 126 | # Package does not create the user 127 | - centos-5.10 128 | 129 | - name: user_management 130 | run_list: 131 | - "recipe[mongodb::default]" 132 | - "recipe[mongodb::user_management]" 133 | attributes: 134 | mongodb: 135 | install_method: mongodb-org 136 | # Needed to read the correct config file 137 | # since mongo 2.6 138 | default_init_name: mongod 139 | dbconfig_file: mongodb.conf 140 | config: 141 | auth: true 142 | excludes: 143 | # Can't connect to mongodb without it running from upstart 144 | - ubuntu-10.04 145 | -------------------------------------------------------------------------------- /attributes/default.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: 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] = '/etc/default/mongodb' 48 | default[:mongodb][:sysconfig_file_template] = 'mongodb.sysconfig.erb' 49 | default[:mongodb][:dbconfig_file_template] = 'mongodb.conf.erb' 50 | default[:mongodb][:dbconfig_file] = '/etc/mongodb.conf' 51 | 52 | default[:mongodb][:package_name] = 'mongodb' 53 | default[:mongodb][:package_version] = nil 54 | 55 | default[:mongodb][:default_init_name] = 'mongodb' 56 | default[:mongodb][:instance_name] = 'mongodb' 57 | 58 | # this option can be "distro", "mongodb-org" or "none" 59 | default[:mongodb][:install_method] = 'distro' 60 | 61 | default[:mongodb][:is_replicaset] = nil 62 | default[:mongodb][:is_shard] = nil 63 | default[:mongodb][:is_configserver] = nil 64 | 65 | default[:mongodb][:reload_action] = 'restart' # or "nothing" 66 | 67 | case node['platform_family'] 68 | when 'freebsd' 69 | default[:mongodb][:package_name] = 'mongo-10gen-server' 70 | default[:mongodb][:sysconfig_file] = '/etc/rc.conf.d/mongodb' 71 | default[:mongodb][:init_dir] = '/usr/local/etc/rc.d' 72 | default[:mongodb][:root_group] = 'wheel' 73 | when 'rhel', 'fedora' 74 | # determine the package name 75 | # from http://rpm.pbone.net/index.php3?stat=3&limit=1&srodzaj=3&dl=40&search=mongodb 76 | # verified for RHEL5,6 Fedora 18,19 77 | default[:mongodb][:package_name] = 'mongodb-server' 78 | default[:mongodb][:sysconfig_file] = '/etc/sysconfig/mongodb' 79 | default[:mongodb][:user] = 'mongod' 80 | default[:mongodb][:group] = 'mongod' 81 | default[:mongodb][:init_script_template] = 'redhat-mongodb.init.erb' 82 | default[:mongodb][:default_init_name] = 'mongod' 83 | default[:mongodb][:instance_name] = 'mongod' 84 | # then there is this guy 85 | if node['platform'] == 'centos' || node['platform'] == 'amazon' 86 | Chef::Log.warn("CentOS doesn't provide mongodb, forcing use of mongodb-org repo") 87 | default[:mongodb][:install_method] = 'mongodb-org' 88 | default[:mongodb][:package_name] = 'mongodb-org' 89 | end 90 | when 'debian' 91 | if node['platform'] == 'ubuntu' 92 | default[:mongodb][:apt_repo] = 'ubuntu-upstart' 93 | default[:mongodb][:init_dir] = '/etc/init/' 94 | default[:mongodb][:init_script_template] = 'debian-mongodb.upstart.erb' 95 | else 96 | default[:mongodb][:apt_repo] = 'debian-sysvinit' 97 | end 98 | else 99 | Chef::Log.error("Unsupported Platform Family: #{node['platform_family']}") 100 | end 101 | 102 | default[:mongodb][:template_cookbook] = 'mongodb' 103 | 104 | default[:mongodb][:key_file_content] = nil 105 | 106 | # install the mongo and bson_ext ruby gems at compile time to make them globally available 107 | # TODO: remove bson_ext once mongo gem supports bson >= 2 108 | default['mongodb']['ruby_gems'] = { 109 | :mongo => nil, 110 | :bson_ext => nil 111 | } 112 | -------------------------------------------------------------------------------- /CHANGELOG: -------------------------------------------------------------------------------- 1 | ======================== 2 | Chef-MongoDB Cookbook Changelog 3 | Last-Update: Tue Nov 4 11:48:54 PST 2014 4 | ======================== 5 | 6 | Pending 0.16.3 Changes 7 | -------------------------- 8 | * remove old runit dependency 9 | 10 | 11 | Version 0.16.2 Release Tue Nov 4 11:48:54 PST 2014 12 | -------------------------- 13 | * start doing even patches = release, odd = dev. 14 | * removed unmaintained freebsd support file 15 | * pass `--force-yes` to apt to bypass issue with package/config conflict #305 16 | * bumped mms_agent version for `monitoring` and `backup` #312 17 | * added user management/auth support, and patches #313, #317 18 | * fix patches on logpath #310 19 | * allow changing mongos service name #319 20 | * update upstart config for debian #323 21 | * added fixes for installation prereqs, sasl 22 | * fix systemd usecase for redhat (#352, #350) 23 | 24 | 25 | Version 0.16.1 26 | -------------------------- 27 | * remove old `mms_agent.rb` and `mms-agent.rb` in favor of new mms_*_agent.rb 28 | * Update mms_*_agent.rb to use template instead of ruby-block, nothing, but call restart 29 | * DEPRECATE '10gen_repo' for 'mongodb_org_repo' #287 30 | * node['mongodb']['install_method'] can now be '10gen' (DEPRECATE) or 'mongodb_org' 31 | * allow `node['mongodb']['config']['logpath'] to be nil for syslog #288 32 | 33 | Version 0.16.0 34 | -------------------------- 35 | * BREAKING CHANGE - drop support for Ruby < 1.9, Chef < 11 36 | * cookbook dependency change 37 | - yum >= 3.0 38 | - remove <= limit for all other dep 39 | * update to Berkshelf 3 40 | * #280 fix install for centos (missing build-essentials) 41 | 42 | Version 0.15.2 End of Ruby 1.8, Chef 10 support 43 | --------------------------- 44 | * update test-kitchen for mongos 45 | * update MMS version 46 | * update rubocop 47 | * minor typo fixes 48 | 49 | Version 0.15.1 50 | --------------------------- 51 | * 'potentially' BREAKING CHANGES, cookbook dependency pinned 52 | - yum < 3.0 53 | - runit < 1.5 54 | - python < 1.4.5 55 | * DEPRECATION: explicitly drop support for 'unsupported' platforms 56 | - must be freebsd, rhel, fedora, debian 57 | * DEPRECATION: recipe mms-agent.rb/mms_agent.rb 58 | - see #261 for new-recipes 59 | * use node.set to make sure is_* attributes are available for search 60 | * 'key_file' -> 'key_file_content' 61 | * allow pinning for gems, pip packages 62 | * #261 new mms agent recipe based on new packaging in upstream 63 | * #256 Allow mms_agent to be run as non-root user 64 | - replSet is not set automatically 65 | 66 | Version 0.15.0 67 | --------------------------- 68 | * DEPRECATION: backward compatability for dbconfig variables in node['mongodb'] 69 | - use node['mongodb']['config'][variable] = value 70 | 71 | Version 0.14.10 DEVELOPMENTAL RELEASE 72 | --------------------------- 73 | * Final 0.14 release 74 | * move node['mongodb']['config']['configsrv'] auto update to the top 75 | * Drop using Chef.Version as it is not rc/beta compatible 76 | * installs gem bson_ext 77 | 78 | Version 0.14.8 DEVELOPMENTAL RELEASE 79 | --------------------------- 80 | * Rubocop (cherry pick of #220) 81 | 82 | Version 0.14.7 DEVELOPMENTAL RELEASE 83 | --------------------------- 84 | * Automatically install bson_ext gem 85 | * Add check/protection for empty shard 86 | * Force node['mongodb']['config']['configsrv'] == true when set as configserver 87 | 88 | Version 0.14.6 DEVELOPMENTAL RELEASE 89 | --------------------------- 90 | * try to autoconfigure 'configsrv' from configserver_nodes 91 | * remove `include 'mongodb::default'` from definition 92 | * allow chef-run without restarting mongo 93 | * comment cleanup 94 | 95 | Version 0.14.X DEVELOPMENTAL RELEASES 96 | --------------------------- 97 | * Split out install into separate recipe 98 | * Adds more testing 99 | * Fixes mms-agent installation/runtime 100 | * Preliminary Work being done to convert completely to Resource style 101 | * patches to Replicaset 102 | * patches to fix upstart service 103 | * patches to configserver install 104 | 105 | Version 0.13.2, RELEASED 106 | --------------------------- 107 | add support for chef_gem on newer versions of chef 108 | 109 | Version 0.13.1, RELEASED 110 | --------------------------- 111 | Add keyfileSupport 112 | 113 | Version 0.13.0, RELEASED 114 | --------------------------- 115 | Bunch of stuff... 116 | 117 | Version 0.1.0 118 | ---------------------------- 119 | Initial release of the cookbooks for the chef configuration management system 120 | developed at edelight GmbH. 121 | With this first release we publish the mongodb cookbook we use for our systems. 122 | This cookbook helps you to configure single node mongodb instances as well as 123 | more complicated cluster setups (including sharding and replication). 124 | -------------------------------------------------------------------------------- /metadata.rb: -------------------------------------------------------------------------------- 1 | name 'mongodb' 2 | maintainer 'edelight GmbH' 3 | maintainer_email 'markus.korn@edelight.de' 4 | license 'Apache 2.0' 5 | description 'Installs and configures mongodb' 6 | version '0.16.3' 7 | 8 | recipe 'mongodb', 'Installs and configures a single node mongodb instance' 9 | recipe 'mongodb::10gen_repo', 'Adds the 10gen repo to get the latest packages' 10 | recipe 'mongodb::mongos', 'Installs and configures a mongos which can be used in a sharded setup' 11 | recipe 'mongodb::configserver', 'Installs and configures a configserver for mongodb sharding' 12 | recipe 'mongodb::shard', 'Installs and configures a single shard' 13 | recipe 'mongodb::replicaset', 'Installs and configures a mongodb replicaset' 14 | recipe 'mongodb::mms_monitoring_agent', 'Installs and configures a MongoDB MMS Monitoring Agent' 15 | recipe 'mongodb::mms_backup_agent', 'Installs and configures a MongoDB MMS Backup Agent' 16 | 17 | depends 'apt', '>= 1.8.2' 18 | depends 'yum', '>= 3.0' 19 | depends 'python' 20 | 21 | %w(ubuntu debian centos redhat amazon).each do |os| 22 | supports os 23 | end 24 | 25 | attribute 'mongodb/config/dbpath', 26 | :display_name => 'dbpath', 27 | :description => 'Path to store the mongodb data', 28 | :default => '/var/lib/mongodb' 29 | 30 | attribute 'mongodb/config/logpath', 31 | :display_name => 'logpath', 32 | :description => 'Path to store the logfiles of a mongodb instance', 33 | :default => '/var/log/mongodb/mongodb.log' 34 | 35 | attribute 'mongodb/config/port', 36 | :display_name => 'Port', 37 | :description => 'Port the mongodb instance is running on', 38 | :default => '27017' 39 | 40 | attribute 'mongodb/reload_action', 41 | :display_name => 'Reload', 42 | :description => 'Action to take when MongoDB config files are modified', 43 | :default => 'restart' 44 | 45 | attribute 'mongodb/client_roles', 46 | :display_name => 'Client Roles', 47 | :description => 'Roles of nodes who need access to the mongodb instance', 48 | :type => 'array', 49 | :default => [] 50 | 51 | attribute 'mongodb/cluster_name', 52 | :display_name => 'Cluster Name', 53 | :description => 'Name of the mongodb cluster, all nodes of a cluster must have the same name.', 54 | :default => '' 55 | 56 | attribute 'mongodb/shard_name', 57 | :display_name => 'Shard name', 58 | :description => 'Name of a mongodb shard', 59 | :default => 'default' 60 | 61 | attribute 'mongodb/sharded_collections', 62 | :display_name => 'Sharded Collections', 63 | :description => 'collections to shard', 64 | :type => 'array', 65 | :default => [] 66 | 67 | attribute 'mongodb/config/replSet', 68 | :display_name => 'Replicaset Name', 69 | :description => 'Name of a mongodb replicaset', 70 | :default => '' 71 | 72 | attribute 'mongodb/config/rest', 73 | :display_name => 'Enable Rest', 74 | :description => 'Enable the ReST interface of the webserver' 75 | 76 | attribute 'mongodb/config/smallfiles', 77 | :display_name => 'Use small files', 78 | :description => 'Modify MongoDB to use a smaller default data file size' 79 | 80 | attribute 'mongodb/config/bind_ip', 81 | :display_name => 'Bind address', 82 | :description => 'MongoDB instance bind address', 83 | :default => '' 84 | 85 | attribute 'mongodb/package_version', 86 | :display_name => 'MongoDB package version', 87 | :description => 'Version of the MongoDB package to install', 88 | :default => '' 89 | 90 | attribute 'mongodb/configfile', 91 | :display_name => 'Configuration File', 92 | :description => 'Name of configuration file to use with when starting mongod/mongos vs command line options', 93 | :default => '' 94 | 95 | attribute 'mongodb/config/nojournal', 96 | :display_name => 'Disable Journals', 97 | :description => 'Journals are enabled by default on 64bit after mongo 2.0, this can disable it', 98 | :default => 'false' 99 | 100 | attribute 'mongodb/mms_agent', 101 | :display_name => 'MMS Agent', 102 | :description => 'Hash of MMS Agent attributes', 103 | :type => 'hash' 104 | 105 | attribute 'mongodb/mms_agent/api_key', 106 | :display_name => 'MMS Agent API Key', 107 | :default => '' 108 | 109 | attribute 'mongodb/mms_agent/monitoring', 110 | :display_name => 'MMS Monitoring Agent', 111 | :description => 'Hash of MMS Monitoring Agent attributes', 112 | :type => 'hash' 113 | 114 | attribute 'mongodb/mms_agent/monitoring/version', 115 | :display_name => 'MMS Monitoring Agent version', 116 | :description => 'Version of MMS Monitoring Agent to install', 117 | :default => '2.0.0.17-1' 118 | 119 | attribute 'mongodb/mms_agent/backup', 120 | :display_name => 'MMS Backup Agent', 121 | :description => 'Hash of MMS Backup Agent attributes', 122 | :type => 'hash' 123 | 124 | attribute 'mongodb/mms_agent/backup/version', 125 | :display_name => 'MMS Backup Agent version', 126 | :description => 'Version of MMS Backup Agent to install', 127 | :default => '1.4.3.28-1' 128 | -------------------------------------------------------------------------------- /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/$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 --quiet --pidfile $PIDFILE \ 140 | --make-pidfile --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 --quiet --pidfile $PIDFILE \ 149 | --retry 300 \ 150 | --user $DAEMON_USER \ 151 | --exec $DAEMON 152 | errcode=$? 153 | return $errcode 154 | } 155 | 156 | force_stop() { 157 | # Force the process to die killing it manually 158 | [ ! -e "$PIDFILE" ] && return 159 | if running ; then 160 | kill -15 $pid 161 | # Is it really dead? 162 | sleep "$DIETIME"s 163 | if running ; then 164 | kill -9 $pid 165 | sleep "$DIETIME"s 166 | if running ; then 167 | echo "Cannot kill $NAME (pid=$pid)!" 168 | exit 1 169 | fi 170 | fi 171 | fi 172 | rm -f $PIDFILE 173 | } 174 | 175 | 176 | case "$1" in 177 | start) 178 | log_daemon_msg "Starting $DESC" "$NAME" 179 | # Check if it's running first 180 | if running ; then 181 | log_progress_msg "apparently already running" 182 | log_end_msg 0 183 | exit 0 184 | fi 185 | if start_server ; then 186 | # NOTE: Some servers might die some time after they start, 187 | # this code will detect this issue if STARTTIME is set 188 | # to a reasonable value 189 | [ -n "$STARTTIME" ] && sleep $STARTTIME # Wait some time 190 | if running ; then 191 | # It's ok, the server started and is running 192 | log_end_msg 0 193 | else 194 | # It is not running after we did start 195 | log_end_msg 1 196 | fi 197 | else 198 | # Either we could not start it 199 | log_end_msg 1 200 | fi 201 | ;; 202 | stop) 203 | log_daemon_msg "Stopping $DESC" "$NAME" 204 | if running ; then 205 | # Only stop the server if we see it running 206 | errcode=0 207 | stop_server || errcode=$? 208 | log_end_msg $errcode 209 | else 210 | # If it's not running don't do anything 211 | log_progress_msg "apparently not running" 212 | log_end_msg 0 213 | exit 0 214 | fi 215 | ;; 216 | force-stop) 217 | # First try to stop gracefully the program 218 | $0 stop 219 | if running; then 220 | # If it's still running try to kill it more forcefully 221 | log_daemon_msg "Stopping (force) $DESC" "$NAME" 222 | errcode=0 223 | force_stop || errcode=$? 224 | log_end_msg $errcode 225 | fi 226 | ;; 227 | restart|force-reload) 228 | log_daemon_msg "Restarting $DESC" "$NAME" 229 | errcode=0 230 | stop_server || errcode=$? 231 | # Wait some sensible amount, some server need this 232 | [ -n "$DIETIME" ] && sleep $DIETIME 233 | start_server || errcode=$? 234 | [ -n "$STARTTIME" ] && sleep $STARTTIME 235 | running || errcode=$? 236 | log_end_msg $errcode 237 | ;; 238 | status) 239 | 240 | log_daemon_msg "Checking status of $DESC" "$NAME" 241 | if running ; then 242 | log_progress_msg "running" 243 | log_end_msg 0 244 | else 245 | log_progress_msg "apparently not running" 246 | log_end_msg 1 247 | exit 1 248 | fi 249 | ;; 250 | # MongoDB can't reload its configuration. 251 | reload) 252 | log_warning_msg "Reloading $NAME daemon: not implemented, as the daemon" 253 | log_warning_msg "cannot re-read the config file (use restart)." 254 | ;; 255 | 256 | *) 257 | N=/etc/init.d/$NAME 258 | echo "Usage: $N {start|stop|force-stop|restart|force-reload|status}" >&2 259 | exit 1 260 | ;; 261 | esac 262 | 263 | exit 0 264 | 265 | -------------------------------------------------------------------------------- /definitions/mongodb.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: 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 | define :mongodb_instance, 23 | :mongodb_type => 'mongod', 24 | :action => [:enable, :start], 25 | :logpath => '/var/log/mongodb/mongodb.log', 26 | :dbpath => '/data', 27 | :configservers => [], 28 | :replicaset => nil, 29 | :notifies => [] do 30 | 31 | # TODO: this is the only remain use of params[:mongodb_type], is it still needed? 32 | unless %w(mongod shard configserver mongos).include?(params[:mongodb_type]) 33 | fail ArgumentError, ":mongodb_type must be 'mongod', 'shard', 'configserver' or 'mongos'; was #{params[:mongodb_type].inspect}" 34 | end 35 | 36 | # Make changes to node['mongodb']['config'] before copying to new_resource. Chef 11 appears to resolve the attributes 37 | # with precedence while Chef 10 copies to not (TBD: find documentation to support observed behavior). 38 | if node['mongodb']['is_mongos'] 39 | provider = 'mongos' 40 | # mongos will fail to start if dbpath is set 41 | node.default['mongodb']['config']['dbpath'] = nil 42 | unless node['mongodb']['config']['configdb'] 43 | node.default['mongodb']['config']['configdb'] = params[:configservers].map do |n| 44 | "#{(n['mongodb']['configserver_url'] || n['fqdn'])}:#{n['mongodb']['config']['port']}" 45 | end.sort.join(',') 46 | end 47 | else 48 | provider = 'mongod' 49 | end 50 | 51 | node.default['mongodb']['config']['configsvr'] = true if node['mongodb']['is_configserver'] 52 | 53 | require 'ostruct' 54 | 55 | new_resource = OpenStruct.new 56 | 57 | new_resource.name = params[:name] 58 | new_resource.dbpath = params[:dbpath] 59 | new_resource.logpath = params[:logpath] 60 | new_resource.replicaset = params[:replicaset] 61 | new_resource.service_action = params[:action] 62 | new_resource.service_notifies = params[:notifies] 63 | 64 | # TODO(jh): parameterize so we can make a resource provider 65 | new_resource.auto_configure_replicaset = node['mongodb']['auto_configure']['replicaset'] 66 | new_resource.auto_configure_sharding = node['mongodb']['auto_configure']['sharding'] 67 | new_resource.bind_ip = node['mongodb']['config']['bind_ip'] 68 | new_resource.cluster_name = node['mongodb']['cluster_name'] 69 | new_resource.config = node['mongodb']['config'] 70 | new_resource.dbconfig_file = node['mongodb']['dbconfig_file'] 71 | new_resource.dbconfig_file_template = node['mongodb']['dbconfig_file_template'] 72 | new_resource.init_dir = node['mongodb']['init_dir'] 73 | new_resource.init_script_template = node['mongodb']['init_script_template'] 74 | new_resource.is_replicaset = node['mongodb']['is_replicaset'] 75 | new_resource.is_shard = node['mongodb']['is_shard'] 76 | new_resource.is_configserver = node['mongodb']['is_configserver'] 77 | new_resource.is_mongos = node['mongodb']['is_mongos'] 78 | new_resource.mongodb_group = node['mongodb']['group'] 79 | new_resource.mongodb_user = node['mongodb']['user'] 80 | new_resource.replicaset_name = node['mongodb']['config']['replSet'] 81 | new_resource.port = node['mongodb']['config']['port'] 82 | new_resource.root_group = node['mongodb']['root_group'] 83 | new_resource.shard_name = node['mongodb']['shard_name'] 84 | new_resource.sharded_collections = node['mongodb']['sharded_collections'] 85 | new_resource.sysconfig_file = node['mongodb']['sysconfig_file'] 86 | new_resource.sysconfig_file_template = node['mongodb']['sysconfig_file_template'] 87 | new_resource.sysconfig_vars = node['mongodb']['sysconfig'] 88 | new_resource.template_cookbook = node['mongodb']['template_cookbook'] 89 | new_resource.ulimit = node['mongodb']['ulimit'] 90 | new_resource.reload_action = node['mongodb']['reload_action'] 91 | 92 | if node['mongodb']['apt_repo'] == 'ubuntu-upstart' 93 | new_resource.init_file = File.join(node['mongodb']['init_dir'], "#{new_resource.name}.conf") 94 | mode = '0644' 95 | else 96 | new_resource.init_file = File.join(node['mongodb']['init_dir'], new_resource.name) 97 | mode = '0755' 98 | end 99 | 100 | # TODO(jh): reimplement using polymorphism 101 | if new_resource.is_replicaset 102 | if new_resource.replicaset_name 103 | # trust a predefined replicaset name 104 | replicaset_name = new_resource.replicaset_name 105 | elsif new_resource.is_shard && new_resource.shard_name 106 | # for replicated shards we autogenerate 107 | # the replicaset name for each shard 108 | replicaset_name = "rs_#{new_resource.shard_name}" 109 | else 110 | # Well shoot, we don't have a predefined name and we aren't 111 | # really sharded. If we want backwards compatibility, this should be: 112 | # replicaset_name = "rs_#{new_resource.shard_name}" 113 | # which with default values defaults to: 114 | # replicaset_name = 'rs_default' 115 | # But using a non-default shard name when we're creating a default 116 | # replicaset name seems surprising to me and needlessly arbitrary. 117 | # So let's use the *default* default in this case: 118 | replicaset_name = 'rs_default' 119 | end 120 | else 121 | # not a replicaset, so no name 122 | replicaset_name = nil 123 | end 124 | 125 | # default file 126 | template new_resource.sysconfig_file do 127 | cookbook new_resource.template_cookbook 128 | source new_resource.sysconfig_file_template 129 | group new_resource.root_group 130 | owner 'root' 131 | mode '0644' 132 | variables( 133 | :sysconfig => new_resource.sysconfig_vars 134 | ) 135 | notifies new_resource.reload_action, "service[#{new_resource.name}]" 136 | end 137 | 138 | # config file 139 | template new_resource.dbconfig_file do 140 | cookbook new_resource.template_cookbook 141 | source new_resource.dbconfig_file_template 142 | group new_resource.root_group 143 | owner 'root' 144 | variables( 145 | :config => new_resource.config 146 | ) 147 | helpers MongoDBConfigHelpers 148 | mode '0644' 149 | notifies new_resource.reload_action, "service[#{new_resource.name}]" 150 | end 151 | 152 | # log dir [make sure it exists] 153 | if new_resource.logpath 154 | directory File.dirname(new_resource.logpath) do 155 | owner new_resource.mongodb_user 156 | group new_resource.mongodb_group 157 | mode '0755' 158 | action :create 159 | recursive true 160 | end 161 | end 162 | 163 | # dbpath dir [make sure it exists] 164 | directory new_resource.dbpath do 165 | owner new_resource.mongodb_user 166 | group new_resource.mongodb_group 167 | mode '0755' 168 | action :create 169 | recursive true 170 | not_if { new_resource.is_mongos } 171 | end 172 | 173 | # Reload systemctl for RHEL 7+ after modifying the init file. 174 | execute 'mongodb-systemctl-daemon-reload' do 175 | command 'systemctl daemon-reload' 176 | action :nothing 177 | end 178 | 179 | # init script 180 | template new_resource.init_file do 181 | cookbook new_resource.template_cookbook 182 | source new_resource.init_script_template 183 | group new_resource.root_group 184 | owner 'root' 185 | mode mode 186 | variables( 187 | :provides => provider, 188 | :dbconfig_file => new_resource.dbconfig_file, 189 | :sysconfig_file => new_resource.sysconfig_file, 190 | :ulimit => new_resource.ulimit, 191 | :bind_ip => new_resource.bind_ip, 192 | :port => new_resource.port 193 | ) 194 | notifies new_resource.reload_action, "service[#{new_resource.name}]" 195 | 196 | if(platform_family?('rhel') && node['platform_version'].to_i >= 7) 197 | notifies :run, 'execute[mongodb-systemctl-daemon-reload]', :immediately 198 | end 199 | end 200 | 201 | # service 202 | service new_resource.name do 203 | provider Chef::Provider::Service::Upstart if node['mongodb']['apt_repo'] == 'ubuntu-upstart' 204 | supports :status => true, :restart => true 205 | action new_resource.service_action 206 | new_resource.service_notifies.each do |service_notify| 207 | notifies :run, service_notify 208 | end 209 | notifies :create, 'ruby_block[config_replicaset]', :immediately if new_resource.is_replicaset && new_resource.auto_configure_replicaset 210 | notifies :create, 'ruby_block[config_sharding]', :immediately if new_resource.is_mongos && new_resource.auto_configure_sharding 211 | # we don't care about a running mongodb service in these cases, all we need is stopping it 212 | ignore_failure true if new_resource.name == 'mongodb' 213 | end 214 | 215 | # replicaset 216 | if new_resource.is_replicaset && new_resource.auto_configure_replicaset 217 | rs_nodes = search( 218 | :node, 219 | "mongodb_cluster_name:#{new_resource.replicaset['mongodb']['cluster_name']} AND \ 220 | mongodb_is_replicaset:true AND \ 221 | mongodb_shard_name:#{new_resource.replicaset['mongodb']['shard_name']} AND \ 222 | chef_environment:#{new_resource.replicaset.chef_environment}" 223 | ) 224 | 225 | ruby_block 'config_replicaset' do 226 | block do 227 | MongoDB.configure_replicaset(new_resource.replicaset, replicaset_name, rs_nodes) unless new_resource.replicaset.nil? 228 | end 229 | action :nothing 230 | end 231 | 232 | ruby_block 'run_config_replicaset' do 233 | block {} 234 | notifies :create, 'ruby_block[config_replicaset]' 235 | end 236 | end 237 | 238 | # sharding 239 | if new_resource.is_mongos && new_resource.auto_configure_sharding 240 | # add all shards 241 | # configure the sharded collections 242 | 243 | shard_nodes = search( 244 | :node, 245 | "mongodb_cluster_name:#{new_resource.cluster_name} AND \ 246 | mongodb_is_shard:true AND \ 247 | chef_environment:#{node.chef_environment}" 248 | ) 249 | 250 | ruby_block 'config_sharding' do 251 | block do 252 | MongoDB.configure_shards(node, shard_nodes) 253 | MongoDB.configure_sharded_collections(node, new_resource.sharded_collections) 254 | end 255 | action :nothing 256 | end 257 | end 258 | end 259 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MongoDB Cookbook 2 | 3 | Installs and configures MongoDB, supporting: 4 | 5 | * Single MongoDB instance 6 | * Replication 7 | * Sharding 8 | * Replication and Sharding 9 | * 10gen repository package installation 10 | * 10gen MongoDB Monitoring System 11 | 12 | [![Build Status](https://travis-ci.org/edelight/chef-mongodb.png?branch=master)](https://travis-ci.org/edelight/chef-mongodb) 13 | 14 | ## REQUIREMENTS: 15 | 16 | This cookbook depends on these external cookbooks 17 | 18 | - apt 19 | - python 20 | - yum 21 | 22 | As of 0.16 This Cookbook requires 23 | 24 | - Chef > 11 25 | - Ruby > 1.9 26 | 27 | ### Platform: 28 | 29 | Currently we 'actively' test using test-kitchen on Ubuntu, Debian, CentOS, Redhat 30 | 31 | ## DEFINITIONS: 32 | 33 | This cookbook contains a definition `mongodb_instance` which can be used to configure 34 | a certain type of mongodb instance, like the default mongodb or various components 35 | of a sharded setup. 36 | 37 | For examples see the USAGE section below. 38 | 39 | ## ATTRIBUTES: 40 | 41 | ### Mongodb Configuration 42 | 43 | Basically all settings defined in the Configuration File Options documentation page can be added to the `node['mongodb']['config'][]` attribute: http://docs.mongodb.org/manual/reference/configuration-options/ 44 | 45 | * `node['mongodb']['config']['dbpath']` - Location for mongodb data directory, defaults to "/var/lib/mongodb" 46 | * `node['mongodb']['config']['logpath']` - Path for the logfiles, default is "/var/log/mongodb/mongodb.log" 47 | * `node['mongodb']['config']['port']` - Port the mongod listens on, default is 27017 48 | * `node['mongodb']['config']['rest']` - Enable the ReST interface of the webserver 49 | * `node['mongodb']['config']['smallfiles']` - Modify MongoDB to use a smaller default data file size 50 | * `node['mongodb']['config']['oplogsize']` - Specifies a maximum size in megabytes for the replication operation log 51 | * `node['mongodb']['config']['bind_ip']` - Configure from which address to accept connections 52 | * `node['mongodb']['config'][]` - General MongoDB Configuration File option 53 | 54 | ### Cookbook specific attributes 55 | 56 | * `node[:mongodb][:reload_action]` - Action to take when MongoDB conf files are 57 | modified, default is `"restart"` 58 | * `node[:mongodb][:package_version]` - Version of the MongoDB package to install, default is nil 59 | * `node[:mongodb][:client_role]` - Role identifying all external clients which should have access to a mongod instance 60 | 61 | ### Sharding and replication attributes 62 | 63 | * `node['mongodb']['config']['replSet']` - Define name of replicaset 64 | * `node[:mongodb][:cluster_name]` - Name of the cluster, all members of the cluster must 65 | reference to the same name, as this name is used internally to identify all 66 | members of a cluster. 67 | * `node[:mongodb][:shard_name]` - Name of a shard, default is "default" 68 | * `node['mongodb']['sharded_collections']` - Define which collections are sharded 69 | * `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). 70 | * `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). 71 | * `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. 72 | * `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). 73 | * `node[:mongodb][:replica_priority]` - Node [priority](http://docs.mongodb.org/manual/reference/replica-configuration/#local.system.replset.members[n].priority). 74 | * `node[:mongodb][:replica_tags]` - Node [tags](http://docs.mongodb.org/manual/reference/replica-configuration/#local.system.replset.members[n].tags). 75 | * `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. 76 | 77 | 78 | ### shared MMS Agent attributes 79 | 80 | * `node['mongodb']['mms_agent']['api_key']` - MMS Agent API Key. No default, required. 81 | * `node['mongodb['mms_agent']['monitoring']['version']` - Version of the MongoDB MMS Monitoring Agent package to download and install. Default is '2.0.0.17-1', required. 82 | * `node['mongodb['mms_agent']['monitoring'][]` - General MongoDB MMS Monitoring Agent configuration file option. 83 | * `node['mongodb['mms_agent']['backup']['version']` - Version of the MongoDB MMS Backup Agent package to download and install. Default is '1.4.3.28-1', required. 84 | * `node['mongodb['mms_agent']['backup'][]` - General MongoDB MMS Monitoring Agent configuration file option. 85 | 86 | ### User management attributes 87 | 88 | * `node['mongodb']['config']['auth']` - Require authentication to access or modify the database 89 | * `node['mongodb']['admin']` - The admin user with userAdmin privileges that allows user management 90 | * `node['mongodb']['users']` - Array of users to add when running the user management recipe 91 | 92 | #### Monitoring Agent Settings 93 | 94 | The defaults values installed by the package are: 95 | 96 | ``` 97 | mmsBaseUrl=https://mms.mongodb.com 98 | configCollectionsEnabled=true 99 | configDatabasesEnabled=true 100 | throttlePassesShardChunkCounts = 10 101 | throttlePassesDbstats = 20 102 | throttlePassesOplog = 10 103 | disableProfileDataCollection=false 104 | disableGetLogsDataCollection=false 105 | disableLocksAndRecordStatsDataCollection=false 106 | enableMunin=true 107 | useSslForAllConnections=false 108 | sslRequireValidServerCertificates=false 109 | ``` 110 | 111 | #### Backup Agent Settings 112 | 113 | The defaults values installed by the package are: 114 | 115 | ``` 116 | mothership=api-backup.mongodb.com 117 | https=true 118 | sslRequireValidServerCertificates=false 119 | ``` 120 | 121 | ## USAGE: 122 | 123 | ### 10gen 124 | Adds the stable [10gen repo](http://www.mongodb.org/downloads#packages) for the 125 | corresponding platform. Currently only implemented for the Debian and Ubuntu repository. 126 | 127 | Usage: just add `recipe[mongodb::10gen_repo]` to the node run_list *before* any other 128 | MongoDB recipe, and the mongodb-10gen **stable** packages will be installed instead of the distribution default. 129 | 130 | ### Single mongodb instance 131 | 132 | Simply add 133 | 134 | ```ruby 135 | include_recipe "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 | dbpath "/data/" 155 | end 156 | ``` 157 | 158 | The result is a new system service with 159 | 160 | ```shell 161 | /etc/init.d/my_instance 162 | ``` 163 | 164 | ### Replicasets 165 | 166 | Add `mongodb::replicaset` (instead of `mongodb::default`) to the node's run_list. Also choose a name for your 167 | replicaset cluster and set the value of `node[:mongodb][:cluster_name]` for each 168 | member to this name. 169 | 170 | ### Sharding 171 | 172 | You need a few more components, but the idea is the same: identification of the 173 | members with their different internal roles (mongos, configserver, etc.) is done via 174 | the `node[:mongodb][:cluster_name]` and `node[:mongodb][:shard_name]` attributes. 175 | 176 | Let's have a look at a simple sharding setup, consisting of two shard servers, one 177 | config server and one mongos. 178 | 179 | First we would like to configure the two shards. For doing so, just use 180 | `mongodb::shard` in the node's run_list and define a unique `mongodb[:shard_name]` 181 | for each of these two nodes, say "shard1" and "shard2". 182 | 183 | Then configure a node to act as a config server - by using the `mongodb::configserver` 184 | recipe. 185 | 186 | And finally you need to configure the mongos. This can be done by using the 187 | `mongodb::mongos` recipe. The mongos needs some special configuration, as these 188 | mongos are actually doing the configuration of the whole sharded cluster. 189 | Most importantly you need to define what collections should be sharded by setting the 190 | attribute `mongodb[:sharded_collections]`: 191 | 192 | ```ruby 193 | { 194 | "mongodb": { 195 | "sharded_collections": { 196 | "test.addressbook": "name", 197 | "mydatabase.calendar": "date" 198 | } 199 | } 200 | } 201 | ``` 202 | 203 | Now mongos will automatically enable sharding for the "test" and the "mydatabase" 204 | database. Also the "addressbook" and the "calendar" collection will be sharded, 205 | with sharding key "name" resp. "date". 206 | In the context of a sharding cluster always keep in mind to use a single role 207 | which is added to all members of the cluster to identify all member nodes. 208 | Also shard names are important to distinguish the different shards. 209 | This is esp. important when you want to replicate shards. 210 | 211 | ### Sharding + Replication 212 | 213 | The setup is not much different to the one described above. All you have to do is add the 214 | `mongodb::replicaset` recipe to all shard nodes, and make sure that all shard 215 | nodes which should be in the same replicaset have the same shard name. 216 | 217 | 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. 218 | 219 | ### MMS Agent 220 | 221 | This cookbook also includes support for 222 | [MongoDB Monitoring System (MMS)](https://mms.mongodb.com/) 223 | agent. MMS is a hosted monitoring service, provided by 10gen, Inc. Once 224 | the small python agent program is installed on the MongoDB host, it 225 | automatically collects the metrics and uploads them to the MMS server. 226 | The graphs of these metrics are shown on the web page. It helps a lot 227 | for tackling MongoDB related problems, so MMS is the baseline for all 228 | production MongoDB deployments. 229 | 230 | 231 | To setup MMS, simply set your keys in 232 | `node['mongodb']['mms_agent']['api_key']` and then add the 233 | `mongodb::mms-agent` recipe to your run list. Your current keys should 234 | be available at your [MMS Settings page](https://mms.mongodb.com/settings). 235 | 236 | ### User Management 237 | 238 | An optional recipe is `mongodb::user_management` which will enable authentication in 239 | the configuration file by default and create any users in the `node['mongodb']['users']`. 240 | The users array expects a hash of username, password, roles, and database. Roles should be 241 | an array of roles the user should have on the database given. 242 | 243 | By default, authentication is not required on the database. This can be overridden by setting 244 | the `node['mongodb']['config']['auth']` attribute to true in the chef json. 245 | 246 | If the auth configuration is true, it will try to create the `node['mongodb']['admin']` user, or 247 | update them if they already exist. Before using on a new database, ensure you're overwriting 248 | the `node['mongodb']['admin']['username']` and `node['mongodb']['admin']['password']` to 249 | something besides their default values. 250 | 251 | There's also a user resource which has the actions `:add`, `:modify` and `:delete`. If modify is 252 | used on a user that doesn't exist, it will be added. If add is used on a user that exists, it 253 | will be modified. 254 | 255 | # LICENSE and AUTHOR: 256 | 257 | Author:: Markus Korn 258 | 259 | Copyright:: 2011-2014, edelight GmbH 260 | 261 | Licensed under the Apache License, Version 2.0 (the "License"); 262 | you may not use this file except in compliance with the License. 263 | You may obtain a copy of the License at 264 | 265 | http://www.apache.org/licenses/LICENSE-2.0 266 | 267 | Unless required by applicable law or agreed to in writing, software 268 | distributed under the License is distributed on an "AS IS" BASIS, 269 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 270 | See the License for the specific language governing permissions and 271 | limitations under the License. 272 | -------------------------------------------------------------------------------- /libraries/mongodb.rb: -------------------------------------------------------------------------------- 1 | # 2 | # Cookbook Name:: 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 | def self.configure_replicaset(node, name, members) 26 | # lazy require, to move loading this modules to runtime of the cookbook 27 | require 'rubygems' 28 | require 'mongo' 29 | 30 | if members.length == 0 31 | if Chef::Config[:solo] 32 | Chef::Log.warn('Cannot search for member nodes with chef-solo, defaulting to single node replica set') 33 | else 34 | Chef::Log.warn("Cannot configure replicaset '#{name}', no member nodes found") 35 | return 36 | end 37 | end 38 | 39 | begin 40 | connection = nil 41 | rescue_connection_failure do 42 | connection = Mongo::Connection.new('localhost', node['mongodb']['config']['port'], :op_timeout => 5, :slave_ok => true) 43 | connection.database_names # check connection 44 | end 45 | rescue => e 46 | Chef::Log.warn("Could not connect to database: 'localhost:#{node['mongodb']['config']['port']}', reason: #{e}") 47 | return 48 | end 49 | 50 | # Want the node originating the connection to be included in the replicaset 51 | members << node unless members.any? { |m| m.name == node.name } 52 | members.sort! { |x, y| x.name <=> y.name } 53 | rs_members = [] 54 | rs_options = {} 55 | members.each_index do |n| 56 | host = "#{members[n]['fqdn']}:#{members[n]['mongodb']['config']['port']}" 57 | rs_options[host] = {} 58 | rs_options[host]['arbiterOnly'] = true if members[n]['mongodb']['replica_arbiter_only'] 59 | rs_options[host]['buildIndexes'] = false unless members[n]['mongodb']['replica_build_indexes'] 60 | rs_options[host]['hidden'] = true if members[n]['mongodb']['replica_hidden'] 61 | slave_delay = members[n]['mongodb']['replica_slave_delay'] 62 | rs_options[host]['slaveDelay'] = slave_delay if slave_delay > 0 63 | if rs_options[host]['buildIndexes'] == false || rs_options[host]['hidden'] || rs_options[host]['slaveDelay'] 64 | priority = 0 65 | else 66 | priority = members[n]['mongodb']['replica_priority'] 67 | end 68 | rs_options[host]['priority'] = priority unless priority == 1 69 | tags = members[n]['mongodb']['replica_tags'].to_hash 70 | rs_options[host]['tags'] = tags unless tags.empty? 71 | votes = members[n]['mongodb']['replica_votes'] 72 | rs_options[host]['votes'] = votes unless votes == 1 73 | rs_members << { '_id' => n, 'host' => host }.merge(rs_options[host]) 74 | end 75 | 76 | Chef::Log.info( 77 | "Configuring replicaset with members #{members.map { |n| n['hostname'] }.join(', ')}" 78 | ) 79 | 80 | rs_member_ips = [] 81 | members.each_index do |n| 82 | port = members[n]['mongodb']['config']['port'] 83 | rs_member_ips << { '_id' => n, 'host' => "#{members[n]['ipaddress']}:#{port}" } 84 | end 85 | 86 | admin = connection['admin'] 87 | cmd = BSON::OrderedHash.new 88 | cmd['replSetInitiate'] = { 89 | '_id' => name, 90 | 'members' => rs_members 91 | } 92 | 93 | begin 94 | result = admin.command(cmd, :check_response => false) 95 | rescue Mongo::OperationTimeout 96 | Chef::Log.info('Started configuring the replicaset, this will take some time, another run should run smoothly') 97 | return 98 | end 99 | if result.fetch('ok', nil) == 1 100 | # everything is fine, do nothing 101 | elsif result.fetch('errmsg', nil) =~ /(\S+) is already initiated/ || (result.fetch('errmsg', nil) == 'already initialized') 102 | server, port = Regexp.last_match.nil? || Regexp.last_match.length < 2 ? ['localhost', node['mongodb']['config']['port']] : Regexp.last_match[1].split(':') 103 | begin 104 | connection = Mongo::Connection.new(server, port, :op_timeout => 5, :slave_ok => true) 105 | rescue 106 | abort("Could not connect to database: '#{server}:#{port}'") 107 | end 108 | 109 | # check if both configs are the same 110 | config = connection['local']['system']['replset'].find_one('_id' => name) 111 | 112 | if config['_id'] == name && config['members'] == rs_members 113 | # config is up-to-date, do nothing 114 | Chef::Log.info("Replicaset '#{name}' already configured") 115 | elsif config['_id'] == name && config['members'] == rs_member_ips 116 | # config is up-to-date, but ips are used instead of hostnames, change config to hostnames 117 | Chef::Log.info("Need to convert ips to hostnames for replicaset '#{name}'") 118 | old_members = config['members'].map { |m| m['host'] } 119 | mapping = {} 120 | rs_member_ips.each do |mem_h| 121 | members.each do |n| 122 | ip, prt = mem_h['host'].split(':') 123 | mapping["#{ip}:#{prt}"] = "#{n['fqdn']}:#{prt}" if ip == n['ipaddress'] 124 | end 125 | end 126 | config['members'].map! do |m| 127 | host = mapping[m['host']] 128 | { '_id' => m['_id'], 'host' => host }.merge(rs_options[host]) 129 | end 130 | config['version'] += 1 131 | 132 | rs_connection = nil 133 | rescue_connection_failure do 134 | rs_connection = Mongo::ReplSetConnection.new(old_members) 135 | rs_connection.database_names # check connection 136 | end 137 | 138 | admin = rs_connection['admin'] 139 | cmd = BSON::OrderedHash.new 140 | cmd['replSetReconfig'] = config 141 | result = nil 142 | begin 143 | result = admin.command(cmd, :check_response => false) 144 | rescue Mongo::ConnectionFailure 145 | # reconfiguring destroys existing connections, reconnect 146 | connection = Mongo::Connection.new('localhost', node['mongodb']['config']['port'], :op_timeout => 5, :slave_ok => true) 147 | config = connection['local']['system']['replset'].find_one('_id' => name) 148 | # Validate configuration change 149 | if config['members'] == rs_members 150 | Chef::Log.info("New config successfully applied: #{config.inspect}") 151 | else 152 | Chef::Log.error("Failed to apply new config. Current config: #{config.inspect} Target config #{rs_members}") 153 | return 154 | end 155 | end 156 | Chef::Log.error("configuring replicaset returned: #{result.inspect}") unless result.fetch('errmsg', nil).nil? 157 | else 158 | # remove removed members from the replicaset and add the new ones 159 | max_id = config['members'].map { |member| member['_id'] }.max 160 | rs_members.map! { |member| member['host'] } 161 | config['version'] += 1 162 | old_members = config['members'].map { |member| member['host'] } 163 | members_delete = old_members - rs_members 164 | config['members'] = config['members'].delete_if { |m| members_delete.include?(m['host']) } 165 | config['members'].map! do |m| 166 | host = m['host'] 167 | { '_id' => m['_id'], 'host' => host }.merge(rs_options[host]) 168 | end 169 | members_add = rs_members - old_members 170 | members_add.each do |m| 171 | max_id += 1 172 | config['members'] << { '_id' => max_id, 'host' => m }.merge(rs_options[m]) 173 | end 174 | 175 | rs_connection = nil 176 | rescue_connection_failure do 177 | rs_connection = Mongo::ReplSetConnection.new(old_members) 178 | rs_connection.database_names # check connection 179 | end 180 | 181 | admin = rs_connection['admin'] 182 | 183 | cmd = BSON::OrderedHash.new 184 | cmd['replSetReconfig'] = config 185 | 186 | result = nil 187 | begin 188 | result = admin.command(cmd, :check_response => false) 189 | rescue Mongo::ConnectionFailure 190 | # reconfiguring destroys existing connections, reconnect 191 | connection = Mongo::Connection.new('localhost', node['mongodb']['config']['port'], :op_timeout => 5, :slave_ok => true) 192 | config = connection['local']['system']['replset'].find_one('_id' => name) 193 | # Validate configuration change 194 | if config['members'] == rs_members 195 | Chef::Log.info("New config successfully applied: #{config.inspect}") 196 | else 197 | Chef::Log.error("Failed to apply new config. Current config: #{config.inspect} Target config #{rs_members}") 198 | return 199 | end 200 | end 201 | Chef::Log.error("configuring replicaset returned: #{result.inspect}") unless result.nil? || result.fetch('errmsg', nil).nil? 202 | end 203 | elsif !result.fetch('errmsg', nil).nil? 204 | Chef::Log.error("Failed to configure replicaset, reason: #{result.inspect}") 205 | end 206 | end 207 | 208 | def self.configure_shards(node, shard_nodes) 209 | # lazy require, to move loading this modules to runtime of the cookbook 210 | require 'rubygems' 211 | require 'mongo' 212 | 213 | shard_groups = Hash.new { |h, k| h[k] = [] } 214 | 215 | shard_nodes.each do |n| 216 | if n['recipes'].include?('mongodb::replicaset') 217 | # do not include hidden members when calling addShard 218 | # see https://jira.mongodb.org/browse/SERVER-9882 219 | next if n['mongodb']['replica_hidden'] 220 | key = "rs_#{n['mongodb']['shard_name']}" 221 | else 222 | key = '_single' 223 | end 224 | shard_groups[key] << "#{n['fqdn']}:#{n['mongodb']['config']['port']}" 225 | end 226 | Chef::Log.info(shard_groups.inspect) 227 | 228 | shard_members = [] 229 | shard_groups.each do |name, members| 230 | if name == '_single' 231 | shard_members += members 232 | else 233 | shard_members << "#{name}/#{members.join(',')}" 234 | end 235 | end 236 | Chef::Log.info(shard_members.inspect) 237 | 238 | begin 239 | connection = Mongo::Connection.new('localhost', node['mongodb']['config']['port'], :op_timeout => 5) 240 | rescue => e 241 | Chef::Log.warn("Could not connect to database: 'localhost:#{node['mongodb']['config']['port']}', reason #{e}") 242 | return 243 | end 244 | 245 | admin = connection['admin'] 246 | 247 | shard_members.each do |shard| 248 | cmd = BSON::OrderedHash.new 249 | cmd['addShard'] = shard 250 | begin 251 | result = admin.command(cmd, :check_response => false) 252 | rescue Mongo::OperationTimeout 253 | result = "Adding shard '#{shard}' timed out, run the recipe again to check the result" 254 | end 255 | Chef::Log.info(result.inspect) 256 | end 257 | end 258 | 259 | def self.configure_sharded_collections(node, sharded_collections) 260 | if sharded_collections.nil? || sharded_collections.empty? 261 | Chef::Log.warn('No sharded collections configured, doing nothing') 262 | return 263 | end 264 | 265 | # lazy require, to move loading this modules to runtime of the cookbook 266 | require 'rubygems' 267 | require 'mongo' 268 | 269 | begin 270 | connection = Mongo::Connection.new('localhost', node['mongodb']['config']['port'], :op_timeout => 5) 271 | rescue => e 272 | Chef::Log.warn("Could not connect to database: 'localhost:#{node['mongodb']['config']['port']}', reason #{e}") 273 | return 274 | end 275 | 276 | admin = connection['admin'] 277 | 278 | databases = sharded_collections.keys.map { |x| x.split('.').first }.uniq 279 | Chef::Log.info("enable sharding for these databases: '#{databases.inspect}'") 280 | 281 | databases.each do |db_name| 282 | cmd = BSON::OrderedHash.new 283 | cmd['enablesharding'] = db_name 284 | begin 285 | result = admin.command(cmd, :check_response => false) 286 | rescue Mongo::OperationTimeout 287 | result = "enable sharding for '#{db_name}' timed out, run the recipe again to check the result" 288 | end 289 | if result['ok'] == 0 290 | # some error 291 | errmsg = result.fetch('errmsg') 292 | if errmsg == 'already enabled' 293 | Chef::Log.info("Sharding is already enabled for database '#{db_name}', doing nothing") 294 | else 295 | Chef::Log.error("Failed to enable sharding for database #{db_name}, result was: #{result.inspect}") 296 | end 297 | else 298 | # success 299 | Chef::Log.info("Enabled sharding for database '#{db_name}'") 300 | end 301 | end 302 | 303 | sharded_collections.each do |name, key| 304 | cmd = BSON::OrderedHash.new 305 | cmd['shardcollection'] = name 306 | cmd['key'] = { key => 1 } 307 | begin 308 | result = admin.command(cmd, :check_response => false) 309 | rescue Mongo::OperationTimeout 310 | result = "sharding '#{name}' on key '#{key}' timed out, run the recipe again to check the result" 311 | end 312 | if result['ok'] == 0 313 | # some error 314 | errmsg = result.fetch('errmsg') 315 | if errmsg == 'already sharded' 316 | Chef::Log.info("Sharding is already configured for collection '#{name}', doing nothing") 317 | else 318 | Chef::Log.error("Failed to shard collection #{name}, result was: #{result.inspect}") 319 | end 320 | else 321 | # success 322 | Chef::Log.info("Sharding for collection '#{result['collectionsharded']}' enabled") 323 | end 324 | end 325 | end 326 | 327 | # Ensure retry upon failure 328 | def self.rescue_connection_failure(max_retries = 30) 329 | retries = 0 330 | begin 331 | yield 332 | rescue Mongo::ConnectionFailure => ex 333 | retries += 1 334 | raise ex if retries > max_retries 335 | sleep(0.5) 336 | retry 337 | end 338 | end 339 | end 340 | --------------------------------------------------------------------------------