├── data └── common.yaml ├── .pmtignore ├── spec ├── acceptance │ ├── hieradata │ │ └── default.yaml │ ├── Dockerfile.centos7 │ ├── Dockerfile.centos8 │ ├── hiera.yaml │ ├── nodesets │ │ ├── debian9-5.5.yml │ │ ├── ubuntu1604-5.5.yml │ │ ├── centos7-5.5.yml │ │ └── centos8-5.5.yml │ ├── mesos_slave_spec.rb │ └── mesos_master_spec.rb ├── fixtures │ └── hiera │ │ ├── test.yaml │ │ ├── default.yaml │ │ └── hiera.yaml ├── unit │ └── facter │ │ └── mesos_version_spec.rb ├── spec_helper_system.rb ├── spec_helper.rb ├── classes │ ├── cli_spec.rb │ ├── install_spec.rb │ ├── config_spec.rb │ ├── init_spec.rb │ ├── repo_spec.rb │ ├── master_spec.rb │ └── slave_spec.rb ├── spec_helper_acceptance.rb ├── functions │ ├── zookeeper_servers_url_spec.rb │ └── mesos_hash_parser_spec.rb └── defines │ ├── service_spec.rb │ └── property_spec.rb ├── .fixtures.yml ├── .puppet-lint.rc ├── .vscode └── extensions.json ├── Puppetfile ├── .gitignore ├── .rspec ├── templates ├── systemd.master-service.erb ├── systemd.slave-service.erb ├── default.erb ├── mesos.json.erb ├── master.erb └── slave.erb ├── lib ├── facter │ └── mesos_version.rb └── puppet │ └── parser │ └── functions │ ├── mesos_hash_parser.rb │ └── zookeeper_servers_url.rb ├── TODO.md ├── Gemfile ├── Rakefile ├── metadata.json ├── manifests ├── config.pp ├── params.pp ├── install.pp ├── service.pp ├── cli.pp ├── property.pp ├── repo.pp ├── init.pp ├── master.pp └── slave.pp ├── .travis.yml ├── .rubocop.yml ├── CHANGELOG.md ├── LICENSE └── README.md /data/common.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | -------------------------------------------------------------------------------- /.pmtignore: -------------------------------------------------------------------------------- 1 | junit/ 2 | log/ 3 | spec/fixtures/modules/ 4 | pkg/ 5 | -------------------------------------------------------------------------------- /spec/acceptance/hieradata/default.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | classes: 3 | - '::mesos' -------------------------------------------------------------------------------- /.fixtures.yml: -------------------------------------------------------------------------------- 1 | fixtures: 2 | symlinks: 3 | mesos: "#{source_dir}" 4 | -------------------------------------------------------------------------------- /spec/fixtures/hiera/test.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | mesos::master::options: 3 | quorum: 2 -------------------------------------------------------------------------------- /.puppet-lint.rc: -------------------------------------------------------------------------------- 1 | --no-class_inherits_from_params_class-check 2 | --no-documentation-check 3 | -------------------------------------------------------------------------------- /spec/fixtures/hiera/default.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | mesos::master::options: 3 | advertise_ip: '10.0.0.1' -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | "recommendations": [ 3 | "jpogran.puppet-vscode", 4 | "rebornix.Ruby" 5 | ] 6 | } 7 | -------------------------------------------------------------------------------- /Puppetfile: -------------------------------------------------------------------------------- 1 | forge 'https://forgeapi.puppetlabs.com' 2 | 3 | mod 'puppetlabs-stdlib', '>= 4.2.0 < 7.0.0' 4 | mod 'puppetlabs-apt', '>= 3.0.0 < 8.0.0' 5 | -------------------------------------------------------------------------------- /spec/fixtures/hiera/hiera.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | :backends: 3 | - yaml 4 | :hierarchy: 5 | - test 6 | - default 7 | :yaml: 8 | :datadir: 'spec/fixtures/hiera' 9 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .librarian/* 2 | .tmp/* 3 | *.log 4 | spec/fixtures/modules/* 5 | pkg/* 6 | .bundle 7 | Gemfile.lock 8 | Puppetfile.lock 9 | vendor/ 10 | .idea 11 | -------------------------------------------------------------------------------- /.rspec: -------------------------------------------------------------------------------- 1 | --format documentation 2 | --color 3 | --pattern 'spec/{classes,defines,unit,functions,hosts,integration,types}/**/*_spec.rb' 4 | #--backtrace 5 | #--fail-fast 6 | -------------------------------------------------------------------------------- /spec/acceptance/Dockerfile.centos7: -------------------------------------------------------------------------------- 1 | FROM deric/centos7-puppet:5.5.17 2 | 3 | RUN mkdir -p /var/run/sshd && ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -q -N "" \ 4 | && ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -q -N "" 5 | 6 | EXPOSE 22 7 | CMD ["/sbin/init"] -------------------------------------------------------------------------------- /spec/acceptance/Dockerfile.centos8: -------------------------------------------------------------------------------- 1 | FROM deric/centos8-puppet:5.5.17 2 | 3 | RUN mkdir -p /var/run/sshd && ssh-keygen -t rsa -f /etc/ssh/ssh_host_rsa_key -q -N "" \ 4 | && ssh-keygen -t dsa -f /etc/ssh/ssh_host_dsa_key -q -N "" 5 | 6 | EXPOSE 22 7 | CMD ["/sbin/init"] -------------------------------------------------------------------------------- /spec/acceptance/hiera.yaml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 5 3 | defaults: # Used for any hierarchy level that omits these keys. 4 | datadir: hieradata # This path is relative to hiera.yaml's directory. 5 | data_hash: yaml_data # Use the built-in YAML backend. 6 | 7 | hierarchy: 8 | - name: "Defaults" 9 | path: "default.yaml" 10 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/debian9-5.5.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | debian-9-x64: 3 | platform: debian-9-amd64 4 | hypervisor : docker 5 | image: deric/stretch-puppet:5.5.16 6 | docker_preserve_image: false 7 | roles: 8 | - agent 9 | debug: false 10 | CONFIG: 11 | masterless: true 12 | log_level: trace 13 | type: foss -------------------------------------------------------------------------------- /spec/acceptance/nodesets/ubuntu1604-5.5.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | ubuntu1604-64: 3 | platform: ubuntu-16.04-amd64 4 | hypervisor : docker 5 | image: deric/xenial-puppet:5.5.16 6 | docker_preserve_image: false 7 | roles: 8 | - agent 9 | debug: false 10 | CONFIG: 11 | masterless: true 12 | log_level: debug 13 | type: foss -------------------------------------------------------------------------------- /templates/systemd.master-service.erb: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Mesos Master 3 | After=<%= scope.lookupvar("mesos::master::systemd_after") %> 4 | Wants=<%= scope.lookupvar("mesos::master::systemd_wants") %> 5 | 6 | [Service] 7 | ExecStart=/usr/bin/mesos-init-wrapper master 8 | Restart=always 9 | RestartSec=20 10 | LimitNOFILE=16384 11 | 12 | [Install] 13 | WantedBy=multi-user.target 14 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/centos7-5.5.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-7-x64: 3 | platform: centos-7-x86_64 4 | hypervisor: docker 5 | image: deric/centos7-puppet:5.5.17 6 | dockerfile: spec/acceptance/Dockerfile.centos7 7 | use_image_entry_point: true 8 | provision: true 9 | debug: false 10 | CONFIG: 11 | trace_limit: 200 12 | type: foss 13 | masterless: true 14 | log_level: debug -------------------------------------------------------------------------------- /templates/systemd.slave-service.erb: -------------------------------------------------------------------------------- 1 | [Unit] 2 | Description=Mesos Slave 3 | After=<%= scope.lookupvar("mesos::slave::systemd_after") %> 4 | Wants=<%= scope.lookupvar("mesos::slave::systemd_wants") %> 5 | 6 | [Service] 7 | ExecStart=/usr/bin/mesos-init-wrapper slave 8 | KillMode=process 9 | Restart=always 10 | RestartSec=20 11 | LimitNOFILE=16384 12 | CPUAccounting=true 13 | MemoryAccounting=true 14 | 15 | [Install] 16 | WantedBy=multi-user.target 17 | -------------------------------------------------------------------------------- /templates/default.erb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Managed by Puppet 3 | # 4 | # Configuration common for master and slave 5 | 6 | # enables mesos logging, otherwise stdout goes to LOG_DIR 7 | <%- if !@log_dir.nil? && !@log_dir.empty? -%> 8 | LOGS="<%= @log_dir %>" 9 | <%- else -%> 10 | #LOGS="/var/log/mesos" 11 | <%- end -%> 12 | ULIMIT="-n <%= @ulimit %>" 13 | 14 | # environment variables that apply to master and slave 15 | <% @env_var.sort.each do |key,val| -%> 16 | export <%= key %>="<%= val %>" 17 | <% end if @env_var -%> 18 | -------------------------------------------------------------------------------- /templates/mesos.json.erb: -------------------------------------------------------------------------------- 1 | { 2 | "default": { 3 | "response_timeout": <%= @response_timeout %>, 4 | "log_file": <%= @log_file %>, 5 | "log_level": "<%= @log_level %>", 6 | "max_workers": <%= @max_workers %>, 7 | <% if !@zookeeper.nil? && !@zookeeper.empty? -%> 8 | "master": "<%= @zookeeper %>", 9 | <% else -%> 10 | "master": "<%= @master %>:<%= @master_port %>", 11 | <% end -%> 12 | "debug": "<%= @debug %>", 13 | "scheme": "<%= @scheme %>" 14 | }, 15 | "profile": "default" 16 | } -------------------------------------------------------------------------------- /lib/facter/mesos_version.rb: -------------------------------------------------------------------------------- 1 | # Fact: mesos_version 2 | # 3 | # Purpose: get current Mesos version 4 | # 5 | # Resolution: 6 | # Tests for presence of mesos-master, returns nil if not present 7 | # returns output of `mesos-master --version` and splits on space. 8 | # 9 | # Caveats: 10 | # none 11 | # 12 | # Notes: 13 | # None 14 | Facter.add(:mesos_version) do 15 | setcode do 16 | if Facter::Util::Resolution.which('mesos-master') 17 | Facter::Util::Resolution.exec('mesos-master --version 2>&1').split(%r{ })[1].strip 18 | end 19 | end 20 | end 21 | -------------------------------------------------------------------------------- /spec/acceptance/nodesets/centos8-5.5.yml: -------------------------------------------------------------------------------- 1 | HOSTS: 2 | centos-8-x64: 3 | platform: el-8-x86_64 # needed to avoid installing ntpdate (BKR-1555) 4 | hypervisor: docker 5 | image: deric/centos8-puppet:5.5.17 6 | dockerfile: spec/acceptance/Dockerfile.centos8 7 | use_image_entry_point: true 8 | provision: true 9 | debug: true 10 | # install various tools required to get the image up to usable levels 11 | #docker_image_commands: 12 | # - 'yum install -y crontabs tar wget openssl sysvinit-tools iproute which initscripts' 13 | CONFIG: 14 | trace_limit: 200 15 | type: foss 16 | masterless: true 17 | log_level: trace -------------------------------------------------------------------------------- /spec/unit/facter/mesos_version_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe Facter::Util::Fact do 4 | before(:each) do 5 | Facter.clear 6 | end 7 | 8 | describe 'mesos_version' do 9 | context 'returns mesos version when mesos present' do 10 | it do 11 | mesos_version_output = 'mesos 0.27.1' 12 | Facter::Util::Resolution.expects(:which).with('mesos-master').returns('/usr/sbin/mesos-master') 13 | Facter::Util::Resolution.expects(:exec).with('mesos-master --version 2>&1').returns(mesos_version_output) 14 | Facter.value(:mesos_version).should == '0.27.1' 15 | end 16 | end 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /TODO.md: -------------------------------------------------------------------------------- 1 | = TODO 2 | 3 | * When the slave configuration is changed the file 4 | */tmp/mesos/meta/slaves/latest* should be removed. 5 | Or, even better, the slave should be started with *--recover=cleanup* 6 | and, when all executors have been cleaned, restarted back with 7 | *--recover=reconnect*. We should invent a way to do this to 8 | prevent a slave being unable to start after online reconfiguration. 9 | * Move all default values to the params class. Inherit all classes from 10 | the params class and pass custom values from the main class. 11 | * Rename all parameters to the recommended style *entity_property* 12 | i.e. *config_file_path*, *config_file_mode*. 13 | * Introduce some acceptance testing 14 | * Rewrite specs to the modern syntax 15 | -------------------------------------------------------------------------------- /spec/spec_helper_system.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'rspec-system/spec_helper' 4 | require 'rspec-system-puppet/helpers' 5 | require 'rspec-system-serverspec/helpers' 6 | include Serverspec::Helper::RSpecSystem 7 | include Serverspec::Helper::DetectOS 8 | include RSpecSystemPuppet::Helpers 9 | 10 | RSpec.configure do |c| 11 | # Project root 12 | proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) 13 | 14 | # Enable colour 15 | c.tty = true 16 | 17 | c.include RSpecSystemPuppet::Helpers 18 | 19 | # This is where we 'setup' the nodes before running our tests 20 | c.before :suite do 21 | # Install modules and dependencies 22 | puppet_module_install(source: proj_root, module_name: 'mesos') 23 | shell('puppet module install puppetlabs-stdlib') 24 | shell('puppet module install puppetlabs-apt') 25 | end 26 | end -------------------------------------------------------------------------------- /spec/acceptance/mesos_slave_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | require 'pry' 5 | 6 | describe 'mesos installation', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do 7 | context 'basic setup' do 8 | it 'install mesos-slave' do 9 | pp = <<-EOS 10 | class{'mesos::slave': } 11 | EOS 12 | 13 | expect(apply_manifest(pp, { 14 | :catch_failures => false, 15 | :debug => false, 16 | }).exit_code).to be_zero 17 | end 18 | 19 | describe package('mesos') do 20 | it { is_expected.to be_installed } 21 | end 22 | 23 | # systemd is broken in docker 24 | #describe service('mesos-slave') do 25 | # it { is_expected.to be_enabled } 26 | # #it { is_expected.to be_running } # might not work due to systemd bug 27 | # # /bin/sh -c systemctl\ is-active\ mesos-master 28 | # # Failed to connect to bus: No such file or directory 29 | #end 30 | end 31 | end -------------------------------------------------------------------------------- /spec/acceptance/mesos_master_spec.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'spec_helper_acceptance' 4 | require 'pry' 5 | 6 | describe 'mesos installation', :unless => UNSUPPORTED_PLATFORMS.include?(fact('osfamily')) do 7 | context 'basic setup' do 8 | it 'install mesos-master' do 9 | pp = <<-EOS 10 | class{'mesos::master': } 11 | EOS 12 | 13 | expect(apply_manifest(pp, { 14 | :catch_failures => false, 15 | :debug => false, 16 | }).exit_code).to be_zero 17 | end 18 | 19 | describe package('mesos') do 20 | it { is_expected.to be_installed } 21 | end 22 | 23 | # systemd is broken in docker 24 | #describe service('mesos-master') do 25 | # it { is_expected.to be_enabled } 26 | # #it { is_expected.to be_running } # might not work due to systemd bug 27 | # # /bin/sh -c systemctl\ is-active\ mesos-master 28 | # # Failed to connect to bus: No such file or directory 29 | #end 30 | end 31 | end -------------------------------------------------------------------------------- /lib/puppet/parser/functions/mesos_hash_parser.rb: -------------------------------------------------------------------------------- 1 | # 2 | # mesos_hash_parser.rb 3 | # 4 | 5 | module Puppet::Parser::Functions 6 | newfunction(:mesos_hash_parser, type: :rvalue, doc: <<-EOS 7 | This function converts simple key-value structure to a Hash 8 | that is required by create_resources function 9 | 10 | EOS 11 | ) do |args| 12 | 13 | # Arguments: hash key_prefix [file_prefix] 14 | 15 | if args.empty? || args.size > 3 16 | raise(Puppet::ParseError, "mesos_hash_parser(): Wrong number of args, given #{args.size}, accepts either 1, 2 or 3") 17 | end 18 | 19 | if args[0].class != Hash 20 | raise(Puppet::ParseError, 'mesos_hash_parser(): first argument must be a Hash, you passed a ' + args[0].class.to_s) 21 | end 22 | 23 | res = {} 24 | key_prefix = args[1] if args.size >= 2 25 | file_prefix = args[2] if args.size == 3 26 | args[0].each do |key, val| 27 | file = file_prefix ? "#{file_prefix}_#{key}" : key 28 | key = "#{key_prefix}_#{key}" if key_prefix 29 | res[key] = { 30 | 'value' => val, 31 | 'file' => file 32 | } 33 | end 34 | res 35 | end 36 | end 37 | -------------------------------------------------------------------------------- /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'puppet' 2 | require 'rspec' 3 | require 'puppetlabs_spec_helper/module_spec_helper' 4 | require 'rspec-puppet' 5 | require 'rspec-puppet/coverage' 6 | 7 | fixture_path = File.expand_path(File.join(__FILE__, '..', 'fixtures')) 8 | 9 | RSpec.configure do |c| 10 | c.treat_symbols_as_metadata_keys_with_true_values = true 11 | c.include PuppetlabsSpec::Files 12 | c.hiera_config = File.join fixture_path, 'hiera', 'hiera.yaml' 13 | 14 | c.before :each do 15 | # Ensure that we don't accidentally cache facts and environment 16 | # between test cases. 17 | Facter::Util::Loader.any_instance.stubs(:load_all) 18 | Facter.clear 19 | Facter.clear_messages 20 | 21 | # Store any environment variables away to be restored later 22 | @old_env = {} 23 | ENV.each_key { |k| @old_env[k] = ENV[k] } 24 | 25 | if ENV['STRICT_VARIABLES'] == 'yes' 26 | Puppet.settings[:strict_variables] = true 27 | end 28 | end 29 | c.after :each do 30 | PuppetlabsSpec::Files.cleanup 31 | end 32 | end 33 | 34 | def puppet_debug_override 35 | return unless ENV['SPEC_PUPPET_DEBUG'] 36 | Puppet::Util::Log.level = :debug 37 | Puppet::Util::Log.newdestination(:console) 38 | end 39 | 40 | at_exit { RSpec::Puppet::Coverage.report! } 41 | -------------------------------------------------------------------------------- /templates/master.erb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Managed by Puppet 3 | # 4 | # A list of the available options can be seen by running `mesos-master --help`. Each 5 | # option can be set in two ways: 6 | # 7 | # * By creating a file in which the value resides (e.g. /etc/mesos-master/ip). 8 | # * By setting the environment variable MESOS_OPTION_NAME (the option name with a 9 | # MESOS_ prefix added to it). 10 | # 11 | # Configuration values are searched for first in the environment, thus values set 12 | # in here precede those loaded from /etc/mesos-master (passed as command line arguments). 13 | 14 | # ZooKeeper URL (used for leader election amongst masters) 15 | <% if @zookeeper_url -%> 16 | export MESOS_ZK="<%= @zookeeper_url %>" 17 | <% end -%> 18 | 19 | # Human readable name for the cluster, displayed in the webui 20 | export MESOS_CLUSTER="<%= @cluster %>" 21 | 22 | # IP address to listen on (in case that you start master 23 | # on this node, it will bind to this address) 24 | <% if @listen_address -%> 25 | export MESOS_IP="<%= @listen_address %>" 26 | <% else -%> 27 | #export MESOS_IP="" 28 | <% end -%> 29 | 30 | # Port to listen on (default: 5050) 31 | export MESOS_PORT=<%= @master_port %> 32 | 33 | # master environment variables 34 | <% @env_var.sort.each do |key,val| -%> 35 | export <%= key %>="<%= val %>" 36 | <% end if @env_var -%> 37 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | group :rake do 4 | puppetversion = ENV.key?('PUPPET_GEM_VERSION') ? (ENV['PUPPET_GEM_VERSION']).to_s : ['>= 4.10.0', '< 7.0'] 5 | gem 'puppet', puppetversion 6 | gem 'puppet-lint' 7 | gem 'puppetlabs_spec_helper', '>=0.2.0' 8 | # removed method last_comment (requires rspec 3.5.0) 9 | gem 'highline' 10 | gem 'librarian-puppet', '>=2.0' 11 | gem 'metadata-json-lint', require: false 12 | gem 'nokogiri', '>= 1.10.4' 13 | gem 'rake' 14 | gem 'rspec-core', '>= 3.5.0' 15 | gem 'rspec-puppet' 16 | gem 'rspec-system-puppet', require: false 17 | gem 'safe_yaml' if RUBY_VERSION >= '2.2.0' 18 | gem 'semantic_puppet' 19 | gem 'xmlrpc' if RUBY_VERSION >= '2.3.0' 20 | gem 'parallel_tests' 21 | end 22 | 23 | group :development do 24 | gem 'puppet-blacksmith', git: 'https://github.com/deric/puppet-blacksmith', branch: 'tag-order' 25 | gem 'rubocop', '>= 0.49.0' 26 | gem 'rubocop-rspec' 27 | gem 'rubocop-i18n' 28 | gem 'pdk' 29 | end 30 | 31 | group :system_tests do 32 | gem 'pry' 33 | gem 'beaker', '>= 4.4.0' # fix for RHEL8 needed: https://github.com/puppetlabs/beaker/commit/287e84c4fb287f9fafdf1eda79e140cf6e59fd94 34 | gem 'beaker-rspec' 35 | gem 'beaker-docker' 36 | gem 'serverspec' 37 | gem 'beaker-hostgenerator' 38 | gem 'beaker-puppet_install_helper' 39 | gem 'master_manipulator' 40 | end 41 | -------------------------------------------------------------------------------- /templates/slave.erb: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | # Managed by Puppet 3 | # 4 | # A list of the available options can be seen by running `mesos-slave --help`. Each 5 | # option can be set in two ways: 6 | # 7 | # * By creating a file in which the value resides (e.g. /etc/mesos-slave/ip or 8 | # /etc/mesos-slave/resources/mem). 9 | # * By setting the environment variable MESOS_OPTION_NAME (the option name with a 10 | # MESOS_ prefix added to it). 11 | # 12 | # Configuration values are searched for first in the environment, thus values set 13 | # in here precede those loaded from /etc/mesos-slave (passed as command line arguments). 14 | # 15 | # If zookeeper URL is not specified, slaves will connect directly 16 | # to specified master (not a fault-tolerant mode) 17 | <% if !@zookeeper_url.nil? && !@zookeeper_url.empty? -%> 18 | export MESOS_MASTER="<%= @zookeeper_url %>" 19 | <% else -%> 20 | export MESOS_MASTER="<%= @master %>:<%= @master_port %>" 21 | <% end -%> 22 | 23 | # public slave's ip, must be unique in mesos cluster 24 | <% if @listen_address -%> 25 | export MESOS_IP="<%= @listen_address %>" 26 | <% else -%> 27 | #export MESOS_IP="" 28 | <% end -%> 29 | 30 | # Port to listen on (default: 5051) 31 | export MESOS_PORT=<%= @port %> 32 | 33 | <% @cgroups.sort.each do |key, val| -%> 34 | export MESOS_CGROUPS_<%= key.upcase %>="<%= val %>" 35 | <% end if @isolation == 'cgroups' -%> 36 | 37 | # slave environment variables 38 | <% @env_var.sort.each do |key,val| -%> 39 | export <%= key %>="<%= val %>" 40 | <% end if @env_var -%> 41 | -------------------------------------------------------------------------------- /lib/puppet/parser/functions/zookeeper_servers_url.rb: -------------------------------------------------------------------------------- 1 | module Puppet::Parser::Functions 2 | newfunction(:zookeeper_servers_url, type: :rvalue, doc: <<-EOS 3 | This function converts an array of ZooKeeper hostnames into a combined URL for 4 | ZooKeeper HA. Optionally you can pass custom path in ZooKeeper and default 5 | ZooKeeper port (applies only for servers without specified port) 6 | 7 | Usage: zookeeper_servers_url([10.0.0.1,10.0.0.2],'mesos', 2181) 8 | EOS 9 | ) do |args| 10 | 11 | # Only 1 argument should be passed 12 | if args.size > 3 13 | raise(Puppet::ParseError, 'zookeeper_servers_url(): Wrong number of args ' + "given (#{args.size} for 1)") 14 | end 15 | 16 | zk_path = args[1] if args.size > 1 17 | zk_path ||= 'mesos' 18 | zk_port = args[2] if args.size > 2 19 | zk_port ||= 2181 20 | 21 | # The argument should be an Array 22 | case args[0].class.name 23 | when 'Array' 24 | zookeeper_servers = args[0].clone 25 | when 'String' 26 | # backward compatibility, will be removed in 1.x 27 | return args[0] 28 | else 29 | raise(Puppet::ParseError, 'zookeeper_servers_url() accepts an Array, you passed a ' + args[0].class.name) 30 | end 31 | 32 | uri = 'zk://' 33 | zookeeper_servers.each_with_index do |server, i| 34 | uri << ',' if i > 0 35 | uri << if server.index(':') 36 | server 37 | else 38 | "#{server}:#{zk_port}" 39 | end 40 | end 41 | return "#{uri}/#{zk_path}" 42 | end 43 | end 44 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler' 2 | Bundler.require(:rake) 3 | require 'rake/clean' 4 | 5 | require 'puppetlabs_spec_helper/rake_tasks' 6 | require 'puppet-lint/tasks/puppet-lint' 7 | require 'rspec-system/rake_task' 8 | require 'puppetlabs_spec_helper/rake_tasks' 9 | # blacksmith does not support ruby 1.8.7 anymore 10 | require 'puppet_blacksmith/rake_tasks' if ENV['RAKE_ENV'] != 'ci' && RUBY_VERSION.split('.')[0, 3].join.to_i > 187 11 | 12 | desc 'Lint metadata.json file' 13 | task :meta do 14 | sh 'metadata-json-lint metadata.json' 15 | end 16 | 17 | exclude_paths = [ 18 | 'bundle/**/*', 19 | 'pkg/**/*', 20 | 'vendor/**/*', 21 | 'spec/**/*' 22 | ] 23 | Rake::Task[:lint].clear 24 | 25 | PuppetLint.configuration.relative = true 26 | PuppetLint.configuration.disable_80chars 27 | PuppetLint.configuration.disable_class_inherits_from_params_class 28 | PuppetLint.configuration.disable_class_parameter_defaults 29 | PuppetLint.configuration.fail_on_warnings = true 30 | 31 | PuppetLint::RakeTask.new :lint do |config| 32 | config.ignore_paths = exclude_paths 33 | end 34 | 35 | # use librarian-puppet to manage fixtures instead of .fixtures.yml 36 | # offers more possibilities like explicit version management, forge downloads,... 37 | task :librarian_spec_prep do 38 | sh 'librarian-puppet install --path=spec/fixtures/modules/' 39 | end 40 | task spec_prep: :librarian_spec_prep 41 | 42 | task default: %i[validate spec lint] 43 | 44 | desc "Run acceptance tests" 45 | RSpec::Core::RakeTask.new(:acceptance) do |t| 46 | # just `spec/acceptance` caused runnin all specs 47 | t.pattern = 'spec/acceptance/*_spec.rb' 48 | end 49 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "deric-mesos", 3 | "version": "1.1.1", 4 | "author": "Tomas Barton", 5 | "summary": "Module managing Mesos master and slave instalation", 6 | "license": "Apache-2.0", 7 | "source": "http://github.com/deric/puppet-mesos", 8 | "project_page": "http://github.com/deric/puppet-mesos", 9 | "issues_url": "https://github.com/deric/puppet-mesos/issues", 10 | "description": "Apache Mesos is a cluster manager that provides efficient resource isolation and sharing across distributed applications", 11 | "dependencies": [ 12 | { 13 | "name": "puppetlabs-stdlib", 14 | "version_requirement": ">= 2.4.0 < 7.0.0" 15 | }, 16 | { 17 | "name": "puppetlabs-apt", 18 | "version_requirement": ">= 3.0.0 < 8.0.0" 19 | } 20 | ], 21 | "operatingsystem_support": [ 22 | { 23 | "operatingsystem": "RedHat", 24 | "operatingsystemrelease": [ 25 | "6", 26 | "7" 27 | ] 28 | }, 29 | { 30 | "operatingsystem": "CentOS", 31 | "operatingsystemrelease": [ 32 | "6", 33 | "7" 34 | ] 35 | }, 36 | { 37 | "operatingsystem": "Debian", 38 | "operatingsystemrelease": [ 39 | "6", 40 | "7", 41 | "8", 42 | "9", 43 | "10" 44 | ] 45 | }, 46 | { 47 | "operatingsystem": "Ubuntu", 48 | "operatingsystemrelease": [ 49 | "10.04", 50 | "12.04", 51 | "14.04", 52 | "16.04", 53 | "18.04" 54 | ] 55 | } 56 | ], 57 | "requirements": [ 58 | { 59 | "name": "puppet", 60 | "version_requirement": ">= 4.0.0 < 7.0.0" 61 | } 62 | ] 63 | } 64 | -------------------------------------------------------------------------------- /spec/classes/cli_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'mesos::cli', type: :class do 4 | let(:owner) { 'mesos' } 5 | let(:group) { 'mesos' } 6 | 7 | let(:params) do 8 | { 9 | owner: owner, 10 | group: group, 11 | manage_pip: true 12 | } 13 | end 14 | 15 | let(:facts) do 16 | { 17 | # still old fact is needed due to this 18 | # https://github.com/puppetlabs/puppetlabs-apt/blob/master/manifests/params.pp#L3 19 | osfamily: 'Debian', 20 | os: { 21 | family: 'Debian', 22 | name: 'Debian', 23 | distro: { codename: 'stretch' }, 24 | release: { major: '9', minor: '1', full: '9.1' } 25 | }, 26 | puppetversion: Puppet.version 27 | } 28 | end 29 | 30 | before(:each) do 31 | puppet_debug_override 32 | end 33 | 34 | it { is_expected.to contain_package('python-pip') } 35 | it { is_expected.to contain_class('mesos::cli') } 36 | it { is_expected.to contain_package('mesos.cli').with('provider' => 'pip') } 37 | it { is_expected.to contain_package('mesos.interface').with('provider' => 'pip') } 38 | 39 | context 'set zookeeper url' do 40 | let(:params) do 41 | { 42 | zookeeper: 'zk://192.168.1.100:2181/mesos', 43 | owner: owner, 44 | group: group 45 | } 46 | end 47 | 48 | it do 49 | is_expected.to contain_file('/etc/.mesos.json').with( 50 | 'ensure' => 'present', 51 | 'owner' => owner, 52 | 'group' => group, 53 | 'mode' => '0644', 54 | ) 55 | end 56 | 57 | it do 58 | is_expected.to contain_file('/etc/.mesos.json').with_content( 59 | /zk:\/\/192.168.1.100:2181\/mesos/, 60 | ) 61 | end 62 | end 63 | end 64 | -------------------------------------------------------------------------------- /spec/spec_helper_acceptance.rb: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | 3 | require 'beaker-rspec/spec_helper' 4 | require 'beaker-rspec/helpers/serverspec' 5 | require 'beaker/puppet_install_helper' 6 | 7 | UNSUPPORTED_PLATFORMS = ['windows','AIX','Solaris'].freeze 8 | 9 | HIERA_PATH = '/etc/puppetlabs/code/environments/production' 10 | 11 | RSpec.configure do |c| 12 | proj_root = File.expand_path(File.join(File.dirname(__FILE__), '..')) 13 | 14 | c.formatter = :documentation 15 | hiera_config = '/etc/puppetlabs/puppet/hiera.yaml' 16 | 17 | # Configure all nodes in nodeset 18 | c.before :suite do 19 | #install_puppet 20 | hosts.each do |host| 21 | #on host, 'gem install bundler' 22 | #on host, 'cd /etc/puppet && bundle install --without development' 23 | on host, puppet('module','install','puppetlabs-stdlib'), { :acceptable_exit_codes => [0,1] } 24 | on host, puppet('module','install','puppetlabs-apt'), { :acceptable_exit_codes => [0,1] } 25 | #binding.pry 26 | on host, "mkdir -p /etc/puppetlabs/puppet /etc/puppet/modules", { :acceptable_exit_codes => [0] } 27 | on host, "mkdir -p #{HIERA_PATH}", { :acceptable_exit_codes => [0] } 28 | scp_to host, File.expand_path('./spec/acceptance/hiera.yaml'), hiera_config 29 | # compatibility with puppet 3.x 30 | on host, "ln -s #{hiera_config} /etc/puppet/hiera.yaml", { :acceptable_exit_codes => [0] } 31 | on host, "ln -s #{HIERA_PATH}/hieradata /etc/puppetlabs/puppet/hieradata", { :acceptable_exit_codes => [0] } 32 | scp_to host, File.expand_path('./spec/acceptance/hieradata'), HIERA_PATH 33 | # assume puppet is on $PATH 34 | on host, "puppet --version" 35 | on host, "puppet module list" 36 | end 37 | puppet_module_install(source: proj_root, module_name: 'mesos') 38 | end 39 | end 40 | -------------------------------------------------------------------------------- /manifests/config.pp: -------------------------------------------------------------------------------- 1 | # Class: mesos::config 2 | # 3 | # This class manages the mesos configuration directories 4 | # 5 | # Parameters: 6 | # [*log_dir*] - directory for logging, (default: '/var/log/mesos') 7 | # [*conf_dir*] - directory for configuration files (default: /etc/mesos) 8 | # [*manage_zk_file*] - flag whether module manages /etc/mesos/zk (default: true) 9 | # [*owner*] - owner of configuration files 10 | # [*group*] - group of configuration files 11 | # [*zookeeper_url*] - string of ZooKeeper servers e.g. `zk://10.0.0.1/mesos` 12 | # 13 | # This class should not be included directly, 14 | # always use 'mesos::slave' or 'mesos:master' 15 | # 16 | class mesos::config( 17 | Optional[String] $log_dir = undef, 18 | Integer $ulimit = 8192, 19 | String $conf_dir = '/etc/mesos', 20 | String $conf_file = '/etc/default/mesos', 21 | Boolean $manage_zk_file = true, 22 | String $owner = 'root', 23 | String $group = 'root', 24 | Hash $env_var = {}, 25 | Optional[String] $zookeeper_url = undef, 26 | ){ 27 | 28 | File { 29 | owner => $owner, 30 | group => $group, 31 | } 32 | 33 | if $log_dir { 34 | file { $log_dir: 35 | ensure => directory, 36 | } 37 | } 38 | 39 | file { $conf_dir: 40 | ensure => directory, 41 | } 42 | 43 | file { $conf_file: 44 | ensure => 'present', 45 | content => template('mesos/default.erb'), 46 | mode => '0644', 47 | require => Package['mesos'], 48 | } 49 | 50 | if $manage_zk_file { 51 | # file containing only zookeeper URL 52 | file { '/etc/mesos/zk': 53 | ensure => empty($zookeeper_url) ? { 54 | true => absent, 55 | false => present, 56 | }, 57 | content => $zookeeper_url, 58 | } 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /manifests/params.pp: -------------------------------------------------------------------------------- 1 | class mesos::params { 2 | $_defaults = { 3 | 'packages' => ['mesos'], 4 | 'service_provider' => undef, 5 | } 6 | 7 | case $facts['os']['family'] { 8 | 'Debian': { 9 | case $facts['os']['name'] { 10 | 'Debian': { 11 | case $facts['os']['release']['major'] { 12 | '7': { $initstyle = 'init' } 13 | '8': { $initstyle = 'systemd' } 14 | default: { $initstyle = undef } 15 | } 16 | } 17 | 'Ubuntu': { 18 | case $facts['os']['release']['major'] { 19 | '12.04': { $initstyle = 'upstart' } 20 | '14.04': { $initstyle = 'upstart' } 21 | '16.04': { $initstyle = 'systemd' } 22 | default: { $initstyle = undef } 23 | } 24 | } 25 | default: { $initstyle = undef } 26 | } 27 | 28 | $_os_overrides = { 29 | 'service_provider' => $initstyle, 30 | } 31 | } 32 | 'Redhat': { 33 | case $facts['os']['release']['major'] { 34 | #'6': { $initstyle = 'redhat' } # TODO: mesosphere packages works with upstart 35 | '6': { $initstyle = 'upstart' } # see issue #28 36 | '7': { $initstyle = 'systemd' } 37 | default: { $initstyle = undef } 38 | } 39 | $_os_overrides = { 40 | 'service_provider' => $initstyle, 41 | } 42 | } 43 | 44 | default: { 45 | $_os_overrides = {} 46 | } 47 | } 48 | $_params = merge($_defaults, $_os_overrides) 49 | 50 | 51 | $packages = $_params['packages'] 52 | $service_provider = $_params['service_provider'] 53 | 54 | $config_file_owner = 'root' 55 | $config_file_group = 'root' 56 | $config_file_mode = '0644' 57 | $manage_service_file = false 58 | $systemd_after = 'network.target' 59 | $systemd_wants = 'network.target' 60 | $systemd_path = '/etc/systemd/system' 61 | } 62 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | --- 2 | dist: xenial 3 | language: ruby 4 | cache: bundler 5 | before_install: 6 | - bundle -v 7 | - rm -f Gemfile.lock 8 | #- gem update --system $RUBYGEMS_VERSION 9 | - gem --version 10 | - bundle -v 11 | script: 12 | - 'bundle exec rake $CHECK' 13 | bundler_args: --without system_tests 14 | rvm: 15 | - 2.5.3 16 | stages: 17 | - static 18 | - spec 19 | - acceptance 20 | - 21 | if: tag =~ ^v\d 22 | name: deploy 23 | matrix: 24 | fast_finish: true 25 | include: 26 | - 27 | env: CHECK="check:symlinks check:git_ignore check:dot_underscore check:test_file rubocop syntax lint metadata_lint" 28 | stage: static 29 | - 30 | env: PUPPET_GEM_VERSION="~> 4.10" CHECK=parallel_spec 31 | rvm: 2.4.5 32 | stage: spec 33 | - 34 | env: PUPPET_GEM_VERSION="~> 5.0" CHECK=parallel_spec 35 | rvm: 2.4.5 36 | stage: spec 37 | - 38 | env: PUPPET_GEM_VERSION="~> 6.0" CHECK=parallel_spec 39 | rvm: 2.5.3 40 | stage: spec 41 | - 42 | rvm: 2.5.3 43 | dist: xenial 44 | env: RAKE_ENV=ci BEAKER_debug=true BEAKER_set=debian9-5.5 45 | script: bundle exec rake acceptance 46 | services: docker 47 | bundler_args: --without development 48 | - 49 | rvm: 2.5.3 50 | dist: xenial 51 | env: RAKE_ENV=ci BEAKER_debug=true BEAKER_set=ubuntu1604-5.5 52 | script: bundle exec rake acceptance 53 | services: docker 54 | bundler_args: --without development 55 | - 56 | rvm: 2.5.3 57 | dist: xenial 58 | env: RAKE_ENV=ci BEAKER_debug=true BEAKER_set=centos7-5.5 59 | script: bundle exec rake acceptance 60 | services: docker 61 | bundler_args: --without development 62 | - 63 | env: DEPLOY_TO_FORGE=yes 64 | stage: deploy 65 | branches: 66 | only: 67 | - master 68 | - /^v\d/ 69 | notifications: 70 | email: false 71 | -------------------------------------------------------------------------------- /manifests/install.pp: -------------------------------------------------------------------------------- 1 | # Class: mesos::install 2 | # 3 | # This class manages Mesos package installation. 4 | # 5 | # Parameters: 6 | # [*ensure*] - 'present' for installing any version of Mesos 7 | # 'latest' or e.g. '0.15' for specific version 8 | # 9 | # Sample Usage: is not meant for standalone usage, class is 10 | # required by 'mesos::master' and 'mesos::slave' 11 | # 12 | class mesos::install( 13 | String $ensure = 'present', 14 | Boolean $manage_repo = true, 15 | Variant[String,Hash] $repo_source = {}, 16 | Boolean $manage_python = false, 17 | String $python_package = 'python', 18 | Boolean $remove_package_services = false, 19 | ) { 20 | # 'ensure_packages' requires puppetlabs/stdlib 21 | # 22 | # linux containers are now implemented natively 23 | # with usage of cgroups, requires kernel >= 2.6.24 24 | # 25 | # Python is required for web GUI (mesos could be build without GUI) 26 | if $manage_python { 27 | ensure_resource('package', [$python_package], 28 | {'ensure' => 'present', 'require' => Package['mesos']} 29 | ) 30 | } 31 | 32 | if $manage_repo and !empty($repo_source) { 33 | class {'mesos::repo': 34 | source => $repo_source, 35 | } 36 | Package<| title == 'mesos' |> { 37 | require => Class['mesos::repo'] 38 | } 39 | } 40 | 41 | # a debian (or other binary package) must be available, 42 | # see https://github.com/deric/mesos-deb-packaging 43 | # for Debian packaging 44 | package { 'mesos': 45 | ensure => $ensure 46 | } 47 | 48 | if ($remove_package_services and $::osfamily == 'redhat' and $::operatingsystemmajrelease == '6') { 49 | file { [ 50 | '/etc/init/mesos-master.conf', '/etc/init/mesos-slave.conf' 51 | ]: 52 | ensure => absent, 53 | require => Package['mesos'], 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /spec/functions/zookeeper_servers_url_spec.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby -S rspec 2 | require 'spec_helper' 3 | require 'rspec-puppet' 4 | 5 | describe 'zookeeper_servers_url' do 6 | describe 'convert zookeeper servers list to a valid zookeeper url' do 7 | it 'convert zookeeper server list to zookeeper url only 1 server' do 8 | param = ['192.168.1.1:2181'] 9 | 10 | is_expected.to run.with_params(param).and_return( 11 | 'zk://192.168.1.1:2181/mesos', 12 | ) 13 | end 14 | 15 | it 'convert zookeeper server list to zookeeper url with more than 1 server' do 16 | param = ['192.168.1.1:2181', '192.168.1.2:2181'] 17 | 18 | is_expected.to run.with_params(param).and_return( 19 | 'zk://192.168.1.1:2181,192.168.1.2:2181/mesos', 20 | ) 21 | end 22 | 23 | it 'raises an error if run with extra arguments' do 24 | is_expected.to run.with_params(1, 2, 3).and_raise_error(Puppet::ParseError) 25 | end 26 | 27 | it 'raises an error if the argument is not an array' do 28 | param = { 'test' => 1 } 29 | is_expected.to run.with_params(param).and_raise_error(Puppet::ParseError) 30 | end 31 | 32 | it 'is backwards compatible' do 33 | param = 'zk://192.168.1.1:2181/mesos' 34 | 35 | is_expected.to run.with_params(param).and_return('zk://192.168.1.1:2181/mesos') 36 | end 37 | 38 | it 'allow changing zookeeper path' do 39 | param = ['192.168.1.1:2181', '192.168.1.2:2181'] 40 | 41 | is_expected.to run.with_params(param, 'foo').and_return( 42 | 'zk://192.168.1.1:2181,192.168.1.2:2181/foo', 43 | ) 44 | end 45 | 46 | it 'allows changing default port' do 47 | param = ['192.168.1.1:2180', '192.168.1.2', '192.168.1.3'] 48 | 49 | is_expected.to run.with_params(param, 'bar', 2222).and_return( 50 | 'zk://192.168.1.1:2180,192.168.1.2:2222,192.168.1.3:2222/bar', 51 | ) 52 | end 53 | end 54 | end 55 | -------------------------------------------------------------------------------- /spec/functions/mesos_hash_parser_spec.rb: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env ruby -S rspec 2 | require 'spec_helper' 3 | require 'rspec-puppet' 4 | 5 | describe 'mesos_hash_parser' do 6 | describe 'convert key-value to a puppet resource hash' do 7 | it 'convert simple hash' do 8 | param = { 9 | 'isolation' => 'cgroups' 10 | } 11 | 12 | is_expected.to run.with_params(param).and_return( 13 | 'isolation' => { 14 | 'value' => 'cgroups', 15 | 'file' => 'isolation' 16 | }, 17 | ) 18 | end 19 | 20 | it 'raises an error if run with extra arguments' do 21 | is_expected.to run.with_params(1, 2, 3, 4).and_raise_error(Puppet::ParseError) 22 | end 23 | 24 | it 'raises an error with incorrect type of arguments' do 25 | is_expected.to run.with_params(1, 2).and_raise_error(Puppet::ParseError) 26 | end 27 | 28 | it 'raises an error when running without arguments' do 29 | is_expected.to run.with_params(nil).and_raise_error(Puppet::ParseError) 30 | end 31 | 32 | it 'works with simple hash' do 33 | is_expected.to run.with_params('foo' => 'bar').and_return( 34 | 'foo' => { 35 | 'value' => 'bar', 36 | 'file' => 'foo' 37 | }, 38 | ) 39 | end 40 | end 41 | 42 | describe 'support prefixes' do 43 | it 'prefixes keys' do 44 | param = { 45 | 'root' => '/cgroups' 46 | } 47 | 48 | is_expected.to run.with_params(param, 'cg').and_return( 49 | 'cg_root' => { 50 | 'value' => '/cgroups', 51 | 'file' => 'root' 52 | }, 53 | ) 54 | end 55 | 56 | it 'prefixes files' do 57 | param = { 58 | 'root' => '/cgroups' 59 | } 60 | 61 | is_expected.to run.with_params(param, 'cg', 'cg').and_return( 62 | 'cg_root' => { 63 | 'value' => '/cgroups', 64 | 'file' => 'cg_root' 65 | }, 66 | ) 67 | end 68 | end 69 | end 70 | -------------------------------------------------------------------------------- /manifests/service.pp: -------------------------------------------------------------------------------- 1 | # Define: mesos::service 2 | # 3 | # This module manages mesos services 4 | # 5 | # Parameters: 6 | # [*enable*] - enable service autostart 7 | # [*manage*] - whether puppet should ensure running/stopping services 8 | # [*service_provider*] - choose a service provider; default = undef = system default; 'none' does not create a service resource at all. 9 | # 10 | # Should not be called directly 11 | # 12 | define mesos::service( 13 | Boolean $enable = false, 14 | Optional[String] $service_provider = undef, 15 | Boolean $manage = true, 16 | Boolean $manage_service_file = $::mesos::manage_service_file, 17 | String $systemd_after = $::mesos::params::systemd_after, 18 | String $systemd_wants = $::mesos::params::systemd_wants, 19 | ) { 20 | 21 | include ::mesos 22 | 23 | if $manage { 24 | if $enable { 25 | $ensure_service = 'running' 26 | } else { 27 | $ensure_service = 'stopped' 28 | } 29 | } else { 30 | $ensure_service = undef 31 | } 32 | 33 | if $manage_service_file == true { 34 | if $service_provider == 'systemd' { 35 | file { "${::mesos::systemd_path}/mesos-${name}.service": 36 | ensure => 'present', 37 | content => template("${module_name}/systemd.${name}-service.erb"), 38 | } 39 | ~> exec { "systemctl daemon-reload # for mesos-${name} service": 40 | refreshonly => true, 41 | path => $::path, 42 | notify => Service["mesos-${name}"] 43 | } 44 | } 45 | } 46 | 47 | if ($service_provider != 'none') { 48 | service { "mesos-${name}": 49 | ensure => $ensure_service, 50 | hasstatus => true, 51 | hasrestart => true, 52 | enable => $enable, 53 | provider => $service_provider, 54 | subscribe => [ 55 | File[$mesos::conf_file], 56 | Package['mesos'] 57 | ], 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /manifests/cli.pp: -------------------------------------------------------------------------------- 1 | # == Class mesos::cli 2 | # 3 | # Manages optional CLI packages providing e.g. command: `mesos ps`. 4 | # 5 | # Python 2.7 is required 6 | # === Parameters 7 | # 8 | # [*zookeeper*] 9 | # A zookeeper URL in format 'zk://server1:port[,server2:port]/mesos' 10 | # 11 | class mesos::cli( 12 | String $ensure = 'present', 13 | Array[String] $packages = ['mesos.cli', 'mesos.interface'], 14 | String $pip_package = 'python-pip', 15 | Boolean $manage_pip = true, 16 | Optional[String] $package_provider = undef, 17 | Integer $response_timeout = 5, 18 | Optional[String] $log_file = undef, 19 | String $log_level = 'warning', 20 | Integer $max_workers = 5, 21 | Boolean $debug = false, 22 | String $scheme = 'http', 23 | String $owner = $mesos::owner, 24 | String $group = $mesos::group, 25 | String $master = $mesos::master, 26 | Optional[String] $zookeeper = $mesos::zookeeper_url, 27 | ) inherits mesos { 28 | 29 | if $manage_pip { 30 | ensure_packages($pip_package) 31 | Package[$pip_package] -> Package[$packages] 32 | } 33 | 34 | if $package_provider { 35 | $package_provider_real = $package_provider 36 | } else { 37 | if $manage_pip { 38 | $package_provider_real = 'pip' 39 | } 40 | } 41 | 42 | $defaults = { 43 | 'provider' => $package_provider_real, 44 | 'ensure' => $ensure, 45 | } 46 | 47 | ensure_packages($packages, $defaults) 48 | 49 | file { '/etc/.mesos.json': 50 | ensure => 'present', 51 | content => template('mesos/mesos.json.erb'), 52 | owner => $owner, 53 | group => $group, 54 | mode => '0644', 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /spec/defines/service_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'mesos::service', type: :define do 4 | let(:title) { 'slave' } 5 | 6 | before(:each) do 7 | puppet_debug_override 8 | end 9 | 10 | let(:params) do 11 | { 12 | manage_service_file: false, 13 | systemd_after: '', 14 | systemd_wants: '' 15 | } 16 | end 17 | 18 | shared_examples 'mesos-service' do |family, os, codename, majdistrelease, _release| 19 | let(:facts) do 20 | { 21 | mesos_version: '1.2.0', 22 | osfamily: family, 23 | os: { 24 | family: family, 25 | name: os, 26 | distro: { codename: codename }, 27 | release: { major: majdistrelease, full: majdistrelease } 28 | }, 29 | path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 30 | puppetversion: Puppet.version 31 | } 32 | end 33 | 34 | it { 35 | is_expected.to contain_service('mesos-slave').with( 36 | ensure: 'stopped', 37 | enable: false, 38 | hasstatus: true, 39 | hasrestart: true, 40 | ) 41 | } 42 | context 'enable service' do 43 | let(:params) do 44 | { 45 | enable: true, 46 | manage_service_file: false, 47 | systemd_after: '', 48 | systemd_wants: '' 49 | } 50 | end 51 | 52 | it { 53 | is_expected.to contain_service('mesos-slave').with( 54 | enable: true, 55 | ensure: 'running', 56 | ) 57 | } 58 | end 59 | 60 | context 'do not manage service' do 61 | let(:params) do 62 | { 63 | enable: true, 64 | manage: false, # won't start service if it's not running 65 | manage_service_file: false, 66 | systemd_after: '', 67 | systemd_wants: '' 68 | } 69 | end 70 | 71 | it { 72 | is_expected.to contain_service('mesos-slave').with( 73 | enable: true, 74 | ensure: nil, 75 | ) 76 | } 77 | end 78 | end 79 | 80 | context 'on debian-like system' do 81 | # last argument should be service provider 82 | it_behaves_like 'mesos-service', 'Debian', 'Debian', '7', 'wheezy' 83 | it_behaves_like 'mesos-service', 'Debian', 'Ubuntu', '12.04', 'precise' 84 | end 85 | 86 | context 'on red-hat-like system' do 87 | it_behaves_like 'mesos-service', 'RedHat', 'RedHat', '6', '6' 88 | it_behaves_like 'mesos-service', 'RedHat', 'CentOS', '7', '7' 89 | end 90 | end 91 | -------------------------------------------------------------------------------- /spec/classes/install_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'mesos::install', type: :class do 4 | let(:facts) do 5 | { 6 | osfamily: 'Debian', 7 | os: { 8 | family: 'Debian', 9 | name: 'Debian', 10 | distro: { codename: 'stretch' }, 11 | release: { major: '9', minor: '1', full: '9.1' } 12 | }, 13 | path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 14 | puppetversion: Puppet.version 15 | } 16 | end 17 | 18 | context 'with given version' do 19 | let(:version) { '1.5' } 20 | let(:params) do 21 | { 22 | ensure: version, 23 | repo_source: 'mesosphere' 24 | } 25 | end 26 | 27 | before(:each) do 28 | puppet_debug_override 29 | end 30 | 31 | it { 32 | is_expected.to contain_package('mesos').with( 33 | 'ensure' => version, 34 | ) 35 | } 36 | 37 | # mesos dependencies (for web GUI) 38 | it { 39 | is_expected.not_to contain_package('python').with( 40 | 'ensure' => 'present', 41 | ) 42 | } 43 | 44 | it { is_expected.to contain_class('mesos::repo') } 45 | end 46 | 47 | context 'do not install repo' do 48 | let(:params) do 49 | { 50 | manage_repo: false 51 | } 52 | end 53 | 54 | it { is_expected.not_to contain_class('mesos::repo') } 55 | end 56 | 57 | context 'manage python installation' do 58 | let(:params) do 59 | { 60 | manage_python: true 61 | } 62 | end 63 | 64 | it { is_expected.to contain_package('python') } 65 | end 66 | 67 | context 'remove packaged services' do 68 | context 'keeps everything' do 69 | it { is_expected.not_to contain_file('/etc/init/mesos-master.conf') } 70 | it { is_expected.not_to contain_file('/etc/init/mesos-slave.conf') } 71 | end 72 | 73 | context 'keeps everything on RHEL 7' do 74 | let(:facts) do 75 | { 76 | osfamily: 'redhat', 77 | operatingsystemmajrelease: '7' 78 | } 79 | end 80 | let(:params) do 81 | { 82 | remove_package_services: true 83 | } 84 | end 85 | 86 | it { is_expected.not_to contain_file('/etc/init/mesos-master.conf') } 87 | it { is_expected.not_to contain_file('/etc/init/mesos-slave.conf') } 88 | end 89 | 90 | context 'removes packaged upstart config on RHEL 6' do 91 | let(:facts) do 92 | { 93 | osfamily: 'redhat', 94 | operatingsystemmajrelease: '6' 95 | } 96 | end 97 | 98 | let(:params) do 99 | { 100 | remove_package_services: true 101 | } 102 | end 103 | 104 | it { is_expected.to contain_file('/etc/init/mesos-master.conf').with_ensure('absent') } 105 | it { is_expected.to contain_file('/etc/init/mesos-slave.conf').with_ensure('absent') } 106 | end 107 | end 108 | end 109 | -------------------------------------------------------------------------------- /spec/classes/config_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'mesos::config', type: :class do 4 | let(:owner) { 'mesos' } 5 | let(:group) { 'mesos' } 6 | 7 | let(:params) do 8 | { 9 | conf_dir: '/etc/mesos', 10 | log_dir: '/var/log/mesos', 11 | owner: owner, 12 | group: group 13 | } 14 | end 15 | 16 | # puppet 5 compatibility: make sure all dependent classes are loaded 17 | let :pre_condition do 18 | 'include mesos::install' 19 | end 20 | 21 | before(:each) do 22 | puppet_debug_override 23 | end 24 | 25 | it { 26 | is_expected.to contain_file('/etc/default/mesos').with( 27 | 'ensure' => 'present', 28 | 'owner' => owner, 29 | 'group' => group, 30 | 'mode' => '0644', 31 | 'require' => 'Package[mesos]', 32 | ) 33 | } 34 | 35 | it 'has default log dir' do 36 | is_expected.to contain_file( 37 | '/etc/default/mesos', 38 | ).with_content(/LOGS="\/var\/log\/mesos"/) 39 | end 40 | 41 | it 'has default ulimit' do 42 | is_expected.to contain_file( 43 | '/etc/default/mesos', 44 | ).with_content(%r{ULIMIT="-n 8192"}) 45 | end 46 | 47 | context 'conf_file' do 48 | let(:conf_file) { '/etc/sysconfig/mesos' } 49 | let(:params) do 50 | { 51 | conf_file: conf_file, 52 | zookeeper_url: 'zk://10.0.0.1/mesos' 53 | } 54 | end 55 | 56 | it do 57 | is_expected.to contain_file(conf_file) 58 | end 59 | end 60 | 61 | context 'setting ulimit' do 62 | let(:params) do 63 | { 64 | ulimit: 16_384 65 | } 66 | end 67 | 68 | it { 69 | is_expected.to contain_file( 70 | '/etc/default/mesos', 71 | ).with_content(%r{ULIMIT="-n 16384"}) 72 | } 73 | end 74 | 75 | context 'setting log dir' do 76 | let(:params) do 77 | { 78 | log_dir: '/srv/mesos/log', 79 | zookeeper_url: 'zk://10.0.0.1/mesos' 80 | } 81 | end 82 | 83 | it { 84 | is_expected.to contain_file( 85 | '/etc/default/mesos', 86 | ).with_content(/LOGS="\/srv\/mesos\/log"/) 87 | } 88 | end 89 | 90 | context 'setting environment variables' do 91 | let(:params) do 92 | { 93 | env_var: { 94 | 'JAVA_HOME' => '/usr/bin/java', 95 | 'MESOS_HOME' => '/var/lib/mesos' 96 | } 97 | } 98 | end 99 | 100 | it { 101 | is_expected.to contain_file( 102 | '/etc/default/mesos', 103 | ).with_content(/export JAVA_HOME="\/usr\/bin\/java"/) 104 | } 105 | 106 | it { 107 | is_expected.to contain_file( 108 | '/etc/default/mesos', 109 | ).with_content(/export MESOS_HOME="\/var\/lib\/mesos"/) 110 | } 111 | end 112 | 113 | context 'set LOGS variable' do 114 | let(:file) { '/etc/default/mesos' } 115 | let(:params) do 116 | { 117 | log_dir: '/var/log/mesos' 118 | } 119 | end 120 | 121 | it { is_expected.to contain_file(file).with_content(/LOGS="\/var\/log\/mesos"/) } 122 | end 123 | end 124 | -------------------------------------------------------------------------------- /manifests/property.pp: -------------------------------------------------------------------------------- 1 | # == Define: mesos::property 2 | # 3 | # This definition can set one of the option files in the mesos configuration 4 | # directories. These files will be used as command line options by the 5 | # mesos services. 6 | # 7 | # === Examples 8 | # 9 | # mesos::property { 'my_option' : 10 | # value => 'my_value', 11 | # dir => '/etc/my_service/conf, 12 | # } 13 | # 14 | # === Parameters 15 | # 16 | # [*value*] 17 | # (required) The value of this option. If this parameter is boolean, the 18 | # predicate option file will be created. 19 | # 20 | # [*file*] 21 | # If this parameter is set, this value will be used instead of the definition 22 | # title as the option name. 23 | # 24 | # [*dir*] 25 | # (required) The root directory of this options. Should be set to the service 26 | # configuration directory. 27 | # 28 | # [*ensure*] 29 | # Create or remove the option. Can be one of present, absent, file. 30 | # Default: present 31 | # 32 | # [*owner*] 33 | # Which system yser should own this file? 34 | # Default: root 35 | # 36 | # [*group*] 37 | # What system groups should this file belong? 38 | # Default: root 39 | # 40 | # [*mode*] 41 | # What access mode should this file have? 42 | # Default: 0644 43 | # 44 | define mesos::property ( 45 | Stdlib::Absolutepath $dir, 46 | $value, 47 | Enum['present', 'absent', 'file'] $ensure = 'present', 48 | Optional[String] $file = undef, 49 | Optional[String] $service = undef, 50 | Optional[String] $owner = undef, 51 | Optional[String] $group = undef, 52 | Optional[String] $mode = undef, 53 | ) { 54 | include ::mesos::params 55 | 56 | if $service { 57 | fail("\$service is deprecated and will be removed in the next major release, please use \$notify => ${service} instead") 58 | } 59 | 60 | $file_name = pick($file, $name) 61 | $file_owner = pick($owner, $mesos::params::config_file_owner) 62 | $file_group = pick($group, $mesos::params::config_file_group) 63 | $file_mode = pick($mode, $mesos::params::config_file_mode) 64 | 65 | if $value =~ Boolean { 66 | if $value { 67 | $file_path = "${dir}/?${file_name}" 68 | } else { 69 | $file_path = "${dir}/?no-${file_name}" 70 | } 71 | $file_content = '' 72 | $file_ensure = $ensure 73 | } elsif $value =~ Numeric { 74 | # function empty() from puppetlabs-stdlib prior to 4.10.0 does not handle numeric values 75 | # thus we need to treat numeric values differently 76 | # TODO: this block can be safely removed after upgrading stdlib 77 | $file_path = "${dir}/${file_name}" 78 | if $ensure == undef { 79 | $file_ensure = present 80 | } else { 81 | $file_ensure = $ensure 82 | } 83 | $file_content = "${value}\n" 84 | } else { 85 | $file_path = "${dir}/${file_name}" 86 | if empty($value) { 87 | # TODO: remove in 0.9 88 | warning("Setting \$value to an empty value is deprecated and will be removed \ 89 | in the next major release, please use \$ensure => absent instead") 90 | if $ensure == 'file' { 91 | $file_ensure = 'present' 92 | } else { 93 | $file_ensure = 'absent' 94 | } 95 | } else { 96 | $file_ensure = $ensure 97 | } 98 | $file_content = "${value}\n" 99 | } 100 | 101 | file { $file_path : 102 | ensure => $file_ensure, 103 | content => $file_content, 104 | owner => $file_owner, 105 | group => $file_group, 106 | mode => $file_mode, 107 | notify => $service, 108 | } 109 | 110 | } 111 | -------------------------------------------------------------------------------- /manifests/repo.pp: -------------------------------------------------------------------------------- 1 | # == Class mesos::repo 2 | # 3 | # This class manages apt/yum repository for Mesos packages 4 | # 5 | # [source] - either a string (e.g.: 'mesoshpere') or a hash containing 6 | # repository configuration (currently only for Debian) 7 | # 8 | 9 | class mesos::repo( 10 | Optional[Variant[String,Hash]] $source = undef 11 | ) { 12 | 13 | if $source { 14 | case $facts['os']['family'] { 15 | 'Debian': { 16 | include ::apt 17 | 18 | $osname = downcase($facts['os']['name']) 19 | $mesosphere_apt = { 20 | location => "https://repos.mesosphere.io/${osname}", 21 | release => $facts['os']['distro']['codename'], 22 | repos => 'main', 23 | key => { 24 | 'id' => '81026D0004C44CF7EF55ADF8DF7D54CBE56151BF', 25 | 'server' => 'keyserver.ubuntu.com', 26 | }, 27 | include => { 28 | 'src' => false 29 | }, 30 | } 31 | 32 | # custom configuration 33 | if $source =~ Hash { 34 | # merge configuration with mesosphere's defaults 35 | $repo_config = deep_merge($mesosphere_apt, $source) 36 | ensure_resource('apt::source', 'mesos', $repo_config) 37 | anchor { 'mesos::repo::begin': } 38 | -> Apt::Source['mesos'] 39 | -> Class['apt::update'] 40 | -> anchor { 'mesos::repo::end': } 41 | } else { 42 | case $source { 43 | undef: { 44 | # make sure to cleanup, when no repository is defined 45 | file{'/etc/apt/sources.list.d/mesos.list': 46 | ensure => absent, 47 | } 48 | } 49 | 'mesosphere': { 50 | ensure_resource('apt::source', 'mesos', $mesosphere_apt) 51 | anchor { 'mesos::repo::begin': } 52 | -> Apt::Source['mesos'] 53 | -> Class['apt::update'] 54 | -> anchor { 'mesos::repo::end': } 55 | } 56 | default: { 57 | notify { "APT repository '${source}' is not supported for ${::osfamily}": } 58 | } 59 | } # case $source 60 | } 61 | } # case Debian 62 | 'RedHat': { 63 | case $source { 64 | undef: {} #nothing to do 65 | 'mesosphere': { 66 | $osrel = $facts['os']['release']['major'] 67 | case $osrel { 68 | '6', '7': { 69 | case $facts['os']['release']['minor'] { 70 | '1','2': { 71 | $mrel = $facts['os']['release']['minor'] 72 | } default: { 73 | # mesosphere no longer updates for new releases 74 | $mrel = '3' 75 | } 76 | } 77 | 78 | exec { 'yum-clean-expire-cache': 79 | user => 'root', 80 | path => '/usr/bin', 81 | refreshonly => true, 82 | command => 'yum clean expire-cache', 83 | } 84 | -> package { 'mesosphere-el-repo': 85 | ensure => present, 86 | provider => 'rpm', 87 | source => "https://repos.mesosphere.io/el/${osrel}/noarch/RPMS/mesosphere-el-repo-${osrel}-${mrel}.noarch.rpm" 88 | } 89 | } 90 | default: { 91 | notify { "Yum repository '${source}' is not supported for major version ${osrel}": } 92 | } 93 | } 94 | } 95 | default: { 96 | notify { "Repository \"${source}\" is not supported yet.": } 97 | } 98 | } 99 | } 100 | default: { 101 | fail("\"${module_name}\" provides no repository information for OSfamily \"${::osfamily}\"") 102 | } 103 | } 104 | } else { 105 | # make sure to cleanup, when no repository is defined 106 | file{'/etc/apt/sources.list.d/mesos.list': 107 | ensure => absent, 108 | } 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /.rubocop.yml: -------------------------------------------------------------------------------- 1 | --- 2 | require: 3 | - rubocop-rspec 4 | - rubocop-i18n 5 | AllCops: 6 | DisplayCopNames: true 7 | TargetRubyVersion: '2.5' 8 | Include: 9 | - "./**/*.rb" 10 | Exclude: 11 | - bin/* 12 | - ".vendor/**/*" 13 | - "**/Gemfile" 14 | - "**/Rakefile" 15 | - pkg/**/* 16 | - spec/fixtures/**/* 17 | - vendor/**/* 18 | - "**/Puppetfile" 19 | - "**/Vagrantfile" 20 | - "**/Guardfile" 21 | Metrics/LineLength: 22 | Description: People have wide screens, use them. 23 | Max: 200 24 | GetText: 25 | Enabled: false 26 | GetText/DecorateString: 27 | Description: We don't want to decorate test output. 28 | Exclude: 29 | - spec/**/* 30 | RSpec/BeforeAfterAll: 31 | Description: Beware of using after(:all) as it may cause state to leak between tests. 32 | A necessary evil in acceptance testing. 33 | Exclude: 34 | - spec/acceptance/**/*.rb 35 | RSpec/HookArgument: 36 | Description: Prefer explicit :each argument, matching existing module's style 37 | EnforcedStyle: each 38 | Style/BlockDelimiters: 39 | Description: Prefer braces for chaining. Mostly an aesthetical choice. Better to 40 | be consistent then. 41 | EnforcedStyle: braces_for_chaining 42 | Style/ClassAndModuleChildren: 43 | Description: Compact style reduces the required amount of indentation. 44 | EnforcedStyle: compact 45 | Style/EmptyElse: 46 | Description: Enforce against empty else clauses, but allow `nil` for clarity. 47 | EnforcedStyle: empty 48 | Style/FormatString: 49 | Description: Following the main puppet project's style, prefer the % format format. 50 | EnforcedStyle: percent 51 | Style/FormatStringToken: 52 | Description: Following the main puppet project's style, prefer the simpler template 53 | tokens over annotated ones. 54 | EnforcedStyle: template 55 | Style/Lambda: 56 | Description: Prefer the keyword for easier discoverability. 57 | EnforcedStyle: literal 58 | Style/RegexpLiteral: 59 | Description: Community preference. See https://github.com/voxpupuli/modulesync_config/issues/168 60 | EnforcedStyle: percent_r 61 | Style/TernaryParentheses: 62 | Description: Checks for use of parentheses around ternary conditions. Enforce parentheses 63 | on complex expressions for better readability, but seriously consider breaking 64 | it up. 65 | EnforcedStyle: require_parentheses_when_complex 66 | Style/TrailingCommaInArguments: 67 | Description: Prefer always trailing comma on multiline argument lists. This makes 68 | diffs, and re-ordering nicer. 69 | EnforcedStyleForMultiline: comma 70 | Style/TrailingCommaInArrayLiteral: 71 | Description: Prefer always trailing comma on multiline literals. This makes diffs, 72 | and re-ordering nicer. 73 | EnforcedStyleForMultiline: comma 74 | Style/SymbolArray: 75 | Description: Using percent style obscures symbolic intent of array's contents. 76 | EnforcedStyle: brackets 77 | RSpec/MessageSpies: 78 | EnforcedStyle: receive 79 | Style/Documentation: 80 | Exclude: 81 | - lib/puppet/parser/functions/**/* 82 | - spec/**/* 83 | Style/WordArray: 84 | EnforcedStyle: brackets 85 | Style/CollectionMethods: 86 | Enabled: true 87 | Style/MethodCalledOnDoEndBlock: 88 | Enabled: true 89 | Style/StringMethods: 90 | Enabled: true 91 | Layout/EndOfLine: 92 | Enabled: false 93 | Layout/IndentHeredoc: 94 | Enabled: false 95 | Metrics/AbcSize: 96 | Enabled: false 97 | Metrics/BlockLength: 98 | Enabled: false 99 | Metrics/ClassLength: 100 | Enabled: false 101 | Metrics/CyclomaticComplexity: 102 | Enabled: false 103 | Metrics/MethodLength: 104 | Enabled: false 105 | Metrics/ModuleLength: 106 | Enabled: false 107 | Metrics/ParameterLists: 108 | Enabled: false 109 | Metrics/PerceivedComplexity: 110 | Enabled: false 111 | RSpec/DescribeClass: 112 | Enabled: false 113 | RSpec/ExampleLength: 114 | Enabled: false 115 | RSpec/MessageExpectation: 116 | Enabled: false 117 | RSpec/MultipleExpectations: 118 | Enabled: false 119 | RSpec/NestedGroups: 120 | Enabled: false 121 | Style/AsciiComments: 122 | Enabled: false 123 | Style/IfUnlessModifier: 124 | Enabled: false 125 | Style/SymbolProc: 126 | Enabled: false 127 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # deric-mesos 2 | 3 | ## v1.1.0 4 | - Add parameter `mesos::manage_repo` that would disable repository management (#93) 5 | - Fix Ubuntu support (#89) 6 | - Workaround for CentOS, RHEL (#87, #90) 7 | - Basic acceptance tests 8 | - [Full diff](https://github.com/deric/puppet-mesos/compare/v1.0.3...v1.1.0) 9 | 10 | ## v1.0.3 11 | - [Full diff](https://github.com/deric/puppet-mesos/compare/v1.0.2...v1.0.3) 12 | - Support `puppetlabs-apt` 6.x 13 | 14 | ## v1.0.2 15 | - [Full diff](https://github.com/deric/puppet-mesos/compare/v1.0.1...v1.0.2) 16 | - Fixed the mesos_version variable check, which fails if the puppet server has "strict_variables = true" [#91](https://github.com/deric/puppet-mesos/pull/91) 17 | - Adds notifier from credentials and acls files to services [#92](https://github.com/deric/puppet-mesos/pull/92) 18 | - Mark Puppet 6 and new versions of `stdlib` and `apt` as compatible 19 | 20 | ## v1.0.1 21 | - [Full diff](https://github.com/deric/puppet-mesos/compare/v1.0.0...v1.0.1) 22 | - Fixed passing custom repository configuration 23 | 24 | ## v1.0.0 25 | - [Full diff](https://github.com/deric/puppet-mesos/compare/v0.9.1...v1.0.0) 26 | - Introduced Puppet 4 strong types 27 | 28 | - [**Breaking Changes**] 29 | - Dropped Puppet 3 support 30 | - Use Mesosphere repo by default 31 | 32 | ## v0.9.1 33 | - [Full diff](https://github.com/deric/puppet-mesos/compare/v0.9.0...v0.9.1) 34 | - Use https for package repositories (#88) 35 | - Fixed insecure dev dependency (`nokogiri`) 36 | 37 | ## v0.9.0 38 | - [Full diff](https://github.com/deric/puppet-mesos/compare/v0.8.4...v0.9.0) 39 | - New features: 40 | - [Allow passing after=, wants= parameters to systemd services](https://github.com/deric/puppet-mesos/issues/80) 41 | - [**Breaking Changes**] 42 | - `force_provider` replaced with `service_provider` 43 | - Agent's `work_dir` defaults to `/var/lib/mesos` 44 | 45 | ## v0.8.4 46 | - FIX: Install the package before managing the $conf_dir for slave [#79](https://github.com/deric/puppet-mesos/pull/79) [#72](https://github.com/deric/puppet-mesos/issues/72) 47 | - [full changelog](https://github.com/deric/puppet-mesos/compare/v0.8.2...v0.8.3) 48 | 49 | ## v0.8.3 50 | - incomplete release, fixed running tests on Ruby > 2.2.0 51 | 52 | ## v0.8.2 53 | 54 | - FIX: /etc/init.d scripts not found on installation #28 55 | - FEATURE: allow passing custom APT configuration #77 56 | - systemd disabled on older systems #74 57 | - removed code duplication #75 58 | - expire yum cache #76 59 | - [full changelog](https://github.com/deric/puppet-mesos/compare/v0.8.1...v0.8.2) 60 | 61 | 62 | ## v0.8.1 63 | 64 | - Removing configuration file should cause service reload #73 65 | - Refactor mesos::property and its spec #68 66 | - Undef property specs failing with new rspec #70 67 | - [full changelog](https://github.com/deric/puppet-mesos/compare/v0.8.0...v0.8.1) 68 | 69 | ## v0.8.0 70 | 71 | - [**BC**] `single_role`: by default node is either master xor slave #62 72 | - ZooKeeper servers are defined as an array (62ebc80498c35314af3b8ec56d2637c53d15ba8f) - old syntax is as well supported 73 | - support alternative pip providers #63 74 | - fix: restart service when `/etc/default/mesos-master` or `/etc/default/mesos-slave` changes #64 75 | - prefix credentials file with `file://` #65 76 | - full [changes diff](https://github.com/deric/puppet-mesos/compare/v0.7.1...v0.8.0) 77 | 78 | ## v0.7.1 79 | 80 | - Add ability to disable logging to syslog #60 81 | - Add `mesos_version` fact (312d441d163fe5115252a6e2034b4026d984521c) 82 | - test against Puppet 4 83 | - [diff to previous version](https://github.com/deric/puppet-mesos/compare/v0.7.0...v0.7.1) 84 | 85 | ## v0.7.0 86 | 87 | - Fix service restart when new version is installed #55 88 | - Support [Mesos CLI management](https://github.com/deric/puppet-mesos/commit/da5b2a784753b088571f523b4d4db97ada335d29) 89 | - Fix setting credential for mesos-slave #57 90 | - Allow changing path to `/etc/default/mesos` #58 91 | - Add ensure parameter to mesos::property #59 92 | - `mesos::property` require/notify fixes #56 93 | - [full diff to last patch version](https://github.com/deric/puppet-mesos/compare/v0.6.5...v0.7.0) 94 | - [diff to last minor version - 0.6.0](https://github.com/deric/puppet-mesos/compare/v0.6.0...v0.7.0) 95 | 96 | ## v0.6.5 97 | 98 | - Support ACLs #49 - Note: Mesos `<0.26` reads secrets as base64 encoded byte array, Mesos `>=0.26` reads secrets as plain strings 99 | - Fix running `apt-get update` #54 100 | - Add owner and group parameters to `mesos::property` #52 101 | - [full patch version diff](https://github.com/deric/puppet-mesos/compare/v0.6.4...v0.6.5) 102 | -------------------------------------------------------------------------------- /manifests/init.pp: -------------------------------------------------------------------------------- 1 | # == Class: mesos 2 | # 3 | # This module manages mesos installation 4 | # 5 | # === Examples 6 | # 7 | # class{ 'mesos': 8 | # zookeeper => ['192.168.1.1:2181', '192.168.1.1:2181'], 9 | # } 10 | # 11 | # === Parameters 12 | # [*ensure*] 13 | # Package ensure present|absent 14 | # 15 | # [*zookeeper*] 16 | # An array of ZooKeeper ip's (with port) (will be converted to a zk url) 17 | # 18 | # [*zookeeper_path*] 19 | # Mesos namespace in ZooKeeper (last part of the zk:// URL, e.g. `zk://192.168.1.1/mesos`) 20 | # 21 | # [*master*] 22 | # If `zookeeper` is empty, master value is used 23 | # 24 | # [*listen_address*] 25 | # Could be a fact like `$::ipaddress` or explicit ip address (String). 26 | # 27 | # [*single_role*] 28 | # When enabled each machine is expected to run either master or slave service. 29 | # 30 | # === Authors 31 | # 32 | # Tomas Barton 33 | # 34 | # === Copyright 35 | # 36 | # Copyright 2013-2018 Tomas Barton 37 | # 38 | class mesos( 39 | String $ensure = 'present', 40 | # if version is not defined, ensure will be used 41 | Optional[String] $version = undef, 42 | # master and slave creates separate logs automatically 43 | # TODO: currently not used 44 | Optional[String] $log_dir = undef, 45 | String $conf_dir = '/etc/mesos', 46 | String $conf_file = '/etc/default/mesos', 47 | Boolean $manage_zk_file = true, 48 | Boolean $manage_service = true, 49 | Optional[Variant[String,Array[String]]] $zookeeper = [], 50 | String $zk_path = 'mesos', 51 | Integer $zk_default_port = 2181, 52 | String $master = '127.0.0.1', 53 | Integer $master_port = 5050, 54 | String $owner = 'root', 55 | String $group = 'root', 56 | Optional[String] $listen_address = undef, 57 | Boolean $manage_repo = true, 58 | Variant[String,Hash] $repo = 'mesosphere', 59 | Hash $env_var = {}, 60 | Integer $ulimit = 8192, 61 | Boolean $manage_python = false, 62 | String $python_package = 'python', 63 | Optional[String] $force_provider = undef, #temporary workaround for starting services 64 | Boolean $use_hiera = false, 65 | Boolean $single_role = true, 66 | Optional[String] $service_provider = $::mesos::params::service_provider, 67 | Boolean $manage_service_file = $::mesos::params::manage_service_file, 68 | String $systemd_path = $::mesos::params::systemd_path, 69 | ) inherits ::mesos::params { 70 | if !empty($zookeeper) { 71 | unless $zookeeper =~ Array { 72 | warning('\$zookeeper parameter should be an array of IP addresses, please update your configuration.') 73 | } 74 | $zookeeper_url = zookeeper_servers_url($zookeeper, $zk_path, $zk_default_port) 75 | } else { 76 | $zookeeper_url = undef 77 | } 78 | 79 | $mesos_ensure = $version ? { 80 | undef => $ensure, 81 | default => $version, 82 | } 83 | 84 | class {'mesos::install': 85 | ensure => $mesos_ensure, 86 | repo_source => $repo, 87 | manage_repo => $manage_repo, 88 | manage_python => $manage_python, 89 | python_package => $python_package, 90 | remove_package_services => $force_provider == 'none', 91 | } 92 | 93 | class {'mesos::config': 94 | log_dir => $log_dir, 95 | conf_dir => $conf_dir, 96 | conf_file => $conf_file, 97 | manage_zk_file => $manage_zk_file, 98 | owner => $owner, 99 | group => $group, 100 | zookeeper_url => $zookeeper_url, 101 | env_var => $env_var, 102 | ulimit => $ulimit, 103 | require => Class['mesos::install'] 104 | } 105 | 106 | } 107 | -------------------------------------------------------------------------------- /spec/classes/init_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'mesos', type: :class do 4 | let(:facts) do 5 | { 6 | # still old fact is needed due to this 7 | # https://github.com/puppetlabs/puppetlabs-apt/blob/master/manifests/params.pp#L3 8 | osfamily: 'Debian', 9 | os: { 10 | family: 'Debian', 11 | name: 'Debian', 12 | distro: { codename: 'stretch' }, 13 | release: { major: '9', minor: '1', full: '9.1' } 14 | }, 15 | puppetversion: Puppet.version 16 | } 17 | end 18 | 19 | context 'with ensure' do 20 | let(:version) { '0.14' } 21 | let(:params) do 22 | { 23 | ensure: version 24 | } 25 | end 26 | 27 | before(:each) do 28 | puppet_debug_override 29 | end 30 | 31 | it { 32 | is_expected.to contain_package('mesos').with( 33 | 'ensure' => version, 34 | ) 35 | } 36 | 37 | it { 38 | is_expected.not_to contain_package('python').with( 39 | 'ensure' => 'present', 40 | ) 41 | } 42 | end 43 | 44 | context 'with given version' do 45 | let(:version) { '0.20' } 46 | let(:params) do 47 | { 48 | version: version 49 | } 50 | end 51 | 52 | it { 53 | is_expected.to contain_package('mesos').with( 54 | 'ensure' => version, 55 | ) 56 | } 57 | end 58 | 59 | context 'remove mesos' do 60 | let(:version) { 'absent' } 61 | let(:params) do 62 | { 63 | ensure: version 64 | } 65 | end 66 | 67 | it { 68 | is_expected.to contain_package('mesos').with( 69 | 'ensure' => version, 70 | ) 71 | } 72 | end 73 | 74 | context 'specify ulimit' do 75 | let(:ulimit) { 16_384 } 76 | let(:file) { '/etc/default/mesos' } 77 | let(:params) do 78 | { 79 | ulimit: ulimit 80 | } 81 | end 82 | 83 | it { is_expected.to contain_file(file).with_content(%r{ULIMIT="-n #{ulimit}"}) } 84 | end 85 | 86 | it { is_expected.to contain_class('mesos') } 87 | it { is_expected.to contain_class('mesos::repo') } 88 | it { is_expected.to contain_class('mesos::install') } 89 | it { is_expected.to contain_class('mesos::config') } 90 | it { is_expected.to contain_class('mesos::config').that_requires('Class[mesos::install]') } 91 | 92 | it { is_expected.to compile.with_all_deps } 93 | 94 | context 'change pyton packge name' do 95 | let(:python) { 'python3' } 96 | let(:params) do 97 | { 98 | manage_python: true, 99 | python_package: python 100 | } 101 | end 102 | 103 | it { 104 | is_expected.to contain_package(python).with( 105 | 'ensure' => 'present', 106 | ) 107 | } 108 | end 109 | 110 | context 'set LOGS variable' do 111 | let(:file) { '/etc/default/mesos' } 112 | let(:params) do 113 | { 114 | log_dir: '/var/log/mesos' 115 | } 116 | end 117 | 118 | it { is_expected.to contain_file(file).with_content(/LOGS="\/var\/log\/mesos"/) } 119 | end 120 | 121 | context 'remove packaged services' do 122 | let(:facts) do 123 | { 124 | # still old fact is needed due to this 125 | # https://github.com/puppetlabs/puppetlabs-apt/blob/master/manifests/params.pp#L3 126 | osfamily: 'Debian', 127 | os: { 128 | family: 'Debian', 129 | name: 'Debian', 130 | distro: { codename: 'stretch' }, 131 | release: { major: '9', minor: '1', full: '9.1' } 132 | }, 133 | puppetversion: Puppet.version 134 | } 135 | end 136 | 137 | context 'keeps everything' do 138 | it { 139 | is_expected.to contain_class('mesos::install').with( 140 | 'remove_package_services' => false, 141 | ) 142 | } 143 | end 144 | 145 | context 'remvoes packaged upstart config' do 146 | let(:params) do 147 | { 148 | force_provider: 'none' 149 | } 150 | end 151 | 152 | it { is_expected.to contain_class('mesos::install').with('remove_package_services' => true) } 153 | end 154 | end 155 | 156 | context 'with zookeeper' do 157 | let(:params) do 158 | { 159 | zookeeper: ['192.168.1.100:2181'] 160 | } 161 | end 162 | 163 | it { 164 | is_expected.to contain_file( 165 | '/etc/mesos/zk', 166 | ).with( 167 | ensure: 'present', 168 | ).with_content(/^zk:\/\/192.168.1.100:2181\/mesos/) 169 | } 170 | end 171 | 172 | context 'with manage_zk_file false' do 173 | let(:params) do 174 | { 175 | manage_zk_file: false, 176 | zookeeper: ['192.168.1.100:2181'] 177 | } 178 | end 179 | 180 | it { 181 | is_expected.not_to contain_file( 182 | '/etc/mesos/zk', 183 | ) 184 | } 185 | end 186 | 187 | context 'zookeeper URL - allow passing directly ZooKeeper\'s URI (backward compatibility 0.x)' do 188 | let(:params) do 189 | { 190 | zookeeper: 'zk://192.168.1.100:2181/mesos' 191 | } 192 | end 193 | 194 | it { 195 | is_expected.to contain_file( 196 | '/etc/mesos/zk', 197 | ).with( 198 | ensure: 'present', 199 | ).with_content(/^zk:\/\/192.168.1.100:2181\/mesos/) 200 | } 201 | end 202 | 203 | context 'allow changing zookeeper\'s namespace' do 204 | let(:params) do 205 | { 206 | zookeeper: ['192.168.1.100:2181', '192.168.1.105:2181'], 207 | zk_path: 'my_mesos' 208 | } 209 | end 210 | 211 | it { 212 | is_expected.to contain_file( 213 | '/etc/mesos/zk', 214 | ).with( 215 | ensure: 'present', 216 | ).with_content(/^zk:\/\/192.168.1.100:2181,192.168.1.105:2181\/my_mesos/) 217 | } 218 | end 219 | 220 | context 'allow changing zookeeper\'s default port' do 221 | let(:params) do 222 | { 223 | zookeeper: ['192.168.1.100', '192.168.1.105'], 224 | zk_default_port: 2828 225 | } 226 | end 227 | 228 | it { 229 | is_expected.to contain_file( 230 | '/etc/mesos/zk', 231 | ).with( 232 | ensure: 'present', 233 | ).with_content(/^zk:\/\/192.168.1.100:2828,192.168.1.105:2828\/mesos/) 234 | } 235 | end 236 | 237 | context 'pass custom repo configuration' do 238 | let(:params) do 239 | { 240 | repo: { 241 | location: 'http://repos.mesosphere.io/debian', 242 | release: 'jessie', 243 | repos: 'main', 244 | key: { 'id' => '00026D0004C44CF7EF55ADF8DF7D54CBE56151BB', 'server' => 'keyserver.example.com' } 245 | } 246 | } 247 | end 248 | 249 | it { 250 | is_expected.to contain_apt__source('mesos').with( 251 | 'location' => 'http://repos.mesosphere.io/debian', 252 | 'repos' => 'main', 253 | 'release' => 'jessie', 254 | 'key' => { 'id' => '00026D0004C44CF7EF55ADF8DF7D54CBE56151BB', 'server' => 'keyserver.example.com' }, 255 | 'include' => { 'src' => false }, 256 | ) 257 | } 258 | end 259 | end 260 | -------------------------------------------------------------------------------- /manifests/master.pp: -------------------------------------------------------------------------------- 1 | # Class: mesos::master 2 | # 3 | # This module manages Mesos master - installs Mesos package 4 | # and starts master service. 5 | # 6 | # Sample Usage: 7 | # 8 | # class{ 'mesos::master': } 9 | # 10 | # Parameters: 11 | # 12 | # [*single_role*] 13 | # Currently Mesos packages ships with both mesos-master and mesos-slave 14 | # enabled by default. `single_role` assumes that you use only either of 15 | # those on one machine. Default: true (mesos-slave service will be 16 | # disabled on master node) 17 | # [*manage_service_file*] 18 | # Whether override default service files (currently supported only for systemd) 19 | # default: false 20 | # 21 | # 22 | # mesos-master service stores configuration in /etc/default/mesos-master in file/directory 23 | # structure. Arguments passed via $options hash are converted to file/directories 24 | # 25 | class mesos::master( 26 | Boolean $enable = true, 27 | String $cluster = 'mesos', 28 | Stdlib::Absolutepath $conf_dir = '/etc/mesos-master', 29 | Stdlib::Absolutepath $work_dir = '/var/lib/mesos', # registrar directory, since 0.19 30 | Stdlib::Absolutepath $conf_file = '/etc/default/mesos-master', 31 | Stdlib::Absolutepath $acls_file = '/etc/mesos/acls', 32 | Stdlib::Absolutepath $credentials_file = '/etc/mesos/master-credentials', 33 | Integer $master_port = $mesos::master_port, 34 | Optional[Variant[String,Array[String]]] $zookeeper = $mesos::zookeeper, 35 | String $zk_path = $mesos::zk_path, 36 | Integer $zk_default_port = $mesos::zk_default_port, 37 | String $owner = $mesos::owner, 38 | String $group = $mesos::group, 39 | Optional[String] $listen_address = $mesos::listen_address, 40 | Boolean $manage_service = $mesos::manage_service, 41 | Hash $env_var = {}, 42 | Hash $options = {}, 43 | Hash $acls = {}, 44 | Array $credentials = [], 45 | Boolean $syslog_logger = true, 46 | Boolean $use_hiera = $mesos::use_hiera, 47 | Boolean $single_role = $mesos::single_role, 48 | Optional[String] $service_provider = $mesos::service_provider, 49 | Boolean $manage_service_file = $::mesos::manage_service_file, 50 | String $systemd_wants = $::mesos::params::systemd_wants, 51 | String $systemd_after = $::mesos::params::systemd_after, 52 | ) inherits ::mesos { 53 | 54 | if (!empty($acls)) { 55 | $acls_options = {'acls' => $acls_file} 56 | $acls_content = inline_template("<%= require 'json'; @acls.to_json %>") 57 | $acls_ensure = file 58 | } else { 59 | $acls_options = {} 60 | $acls_content = undef 61 | $acls_ensure = absent 62 | } 63 | 64 | if (!empty($credentials)) { 65 | $credentials_options = {'credentials' => "file://${credentials_file}"} 66 | $credentials_content = inline_template("<%= require 'json'; {:credentials => @credentials}.to_json %>") 67 | $credentials_ensure = file 68 | } else { 69 | $credentials_options = {} 70 | $credentials_content = undef 71 | $credentials_ensure = absent 72 | } 73 | 74 | if $use_hiera { 75 | # In Puppet 3 automatic lookup won't merge options across multiple config 76 | # files, see https://www.devco.net/archives/2016/02/03/puppet-4-data-lookup-strategies.php 77 | $opts = hiera_hash('mesos::master::options', $options) 78 | $merged_options = merge($opts, $acls_options, $credentials_options) 79 | } else { 80 | $merged_options = merge($options, $acls_options, $credentials_options) 81 | } 82 | 83 | if !empty($zookeeper) { 84 | unless $zookeeper =~ Array { 85 | warning('\$zookeeper parameter should be an array of IP addresses, please update your configuration.') 86 | } 87 | $zookeeper_url = zookeeper_servers_url($zookeeper, $zk_path, $zk_default_port) 88 | } 89 | 90 | File { 91 | owner => $owner, 92 | group => $group, 93 | } 94 | 95 | file { $conf_dir: 96 | ensure => directory, 97 | recurse => true, 98 | purge => true, 99 | force => true, 100 | require => Class['::mesos::install'], 101 | notify => Service['mesos-master'], # when key is removed we want to reload the service 102 | } 103 | 104 | file { $work_dir: 105 | ensure => directory, 106 | } 107 | 108 | file { $acls_file: 109 | ensure => $acls_ensure, 110 | content => $acls_content, 111 | mode => '0444', 112 | notify => Service['mesos-master'], 113 | } 114 | 115 | file { $credentials_file: 116 | ensure => $credentials_ensure, 117 | content => $credentials_content, 118 | mode => '0400', 119 | notify => Service['mesos-master'], 120 | } 121 | 122 | # work_dir can't be specified via options, 123 | # we would get a duplicate declaration error 124 | mesos::property {'master_work_dir': 125 | value => $work_dir, 126 | dir => $conf_dir, 127 | file => 'work_dir', 128 | owner => $owner, 129 | group => $group, 130 | notify => Service['mesos-master'], 131 | } 132 | 133 | create_resources(mesos::property, 134 | mesos_hash_parser($merged_options, 'master'), 135 | { 136 | dir => $conf_dir, 137 | owner => $owner, 138 | group => $group, 139 | notify => Service['mesos-master'], 140 | } 141 | ) 142 | 143 | file { $conf_file: 144 | ensure => present, 145 | content => template('mesos/master.erb'), 146 | mode => '0644', 147 | require => [File[$conf_dir], Package['mesos']], 148 | } 149 | 150 | # When launched by the "mesos-init-wrapper", the Mesos service's stdout/stderr 151 | # are logged to syslog using logger (http://linux.die.net/man/1/logger). This 152 | # is disabled using the "--no-logger" flag. There is no equivalent "--logger" 153 | # flag so the option must either be present or completely removed. 154 | $logger_ensure = $syslog_logger ? { 155 | true => absent, 156 | false => present, 157 | } 158 | mesos::property { 'master_logger': 159 | ensure => $logger_ensure, 160 | file => 'logger', 161 | value => false, 162 | dir => $conf_dir, 163 | owner => $owner, 164 | group => $group, 165 | } 166 | 167 | # Install mesos-master service 168 | mesos::service { 'master': 169 | enable => $enable, 170 | service_provider => $service_provider, 171 | manage => $manage_service, 172 | subscribe => File[$conf_file], 173 | manage_service_file => $manage_service_file, 174 | systemd_wants => $systemd_wants, 175 | systemd_after => $systemd_after, 176 | } 177 | 178 | if (!defined(Class['mesos::slave']) and $single_role) { 179 | mesos::service { 'slave': 180 | enable => false, 181 | manage => $manage_service, 182 | } 183 | } 184 | } 185 | -------------------------------------------------------------------------------- /spec/defines/property_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'mesos::property', type: :define do 4 | let(:title) { 'some-property' } 5 | let(:directory) { '/tmp/mesos-conf' } 6 | 7 | let(:facts) do 8 | { 9 | mesos_version: '1.2.0', 10 | osfamily: 'Debian', 11 | os: { 12 | family: 'Debian', 13 | name: 'Debian', 14 | distro: { codename: 'jessie' }, 15 | release: { major: '8', minor: '9', full: '8.9' } 16 | }, 17 | path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 18 | puppetversion: Puppet.version 19 | } 20 | end 21 | 22 | before(:each) do 23 | puppet_debug_override 24 | end 25 | 26 | context 'with a string value' do 27 | let(:params) do 28 | { 29 | value: 'foo', 30 | dir: directory 31 | } 32 | end 33 | 34 | it { is_expected.to compile.with_all_deps } 35 | 36 | it do 37 | parameters = { 38 | ensure: 'present', 39 | content: "foo\n" 40 | } 41 | is_expected.to contain_file("#{directory}/#{title}").with(parameters) 42 | end 43 | end 44 | 45 | context 'with an empty value' do 46 | let(:params) do 47 | { 48 | value: '', 49 | dir: directory 50 | } 51 | end 52 | 53 | it { is_expected.to compile.with_all_deps } 54 | 55 | it 'removes a property file' do 56 | is_expected.to contain_file("#{directory}/#{title}").with_ensure('absent') 57 | end 58 | end 59 | 60 | context 'with the :undef value' do 61 | let(:params) do 62 | { 63 | value: :undef, 64 | dir: directory 65 | } 66 | end 67 | 68 | it { is_expected.to compile.with_all_deps } 69 | 70 | it 'removes the property file' do 71 | is_expected.to contain_file("#{directory}/#{title}").with_ensure('absent') 72 | end 73 | end 74 | 75 | context 'without a defined value or dir' do 76 | let(:params) do 77 | {} 78 | end 79 | 80 | it 'fails with an error' do 81 | expect { 82 | is_expected.to compile.with_all_deps 83 | }.to raise_error %r{dir} 84 | end 85 | end 86 | 87 | context 'with a boolean (true) value' do 88 | let(:params) do 89 | { 90 | value: true, 91 | dir: directory 92 | } 93 | end 94 | 95 | it { is_expected.to compile.with_all_deps } 96 | 97 | it 'contains a positive "predicate" file' do 98 | parameters = { 99 | ensure: 'present', 100 | content: '' 101 | } 102 | is_expected.to contain_file("#{directory}/?#{title}").with(parameters) 103 | end 104 | end 105 | 106 | context 'with a boolean (false) value' do 107 | let(:params) do 108 | { 109 | value: false, 110 | dir: directory 111 | } 112 | end 113 | 114 | it { is_expected.to compile.with_all_deps } 115 | 116 | it 'contains a negative "predicate" file' do 117 | parameters = { 118 | ensure: 'present', 119 | content: '' 120 | } 121 | is_expected.to contain_file("#{directory}/?no-#{title}").with(parameters) 122 | end 123 | end 124 | 125 | context 'with an integer value' do 126 | let(:params) do 127 | { 128 | value: 123, 129 | dir: directory 130 | } 131 | end 132 | 133 | it { is_expected.to compile.with_all_deps } 134 | 135 | it 'creates a property file with the value' do 136 | parameters = { 137 | ensure: 'present', 138 | content: "123\n" 139 | } 140 | is_expected.to contain_file("#{directory}/#{title}").with(parameters) 141 | end 142 | end 143 | 144 | context 'with a float value' do 145 | let(:params) do 146 | { 147 | value: 3.14, 148 | dir: directory 149 | } 150 | end 151 | 152 | it { is_expected.to compile.with_all_deps } 153 | 154 | it 'creates a property file with the value' do 155 | parameters = { 156 | ensure: 'present', 157 | content: "3.14\n" 158 | } 159 | is_expected.to contain_file("#{directory}/#{title}").with(parameters) 160 | end 161 | end 162 | 163 | context 'ensure is set to absent' do 164 | let(:params) do 165 | { 166 | ensure: 'absent', 167 | value: 'foo', 168 | dir: directory 169 | } 170 | end 171 | 172 | it { is_expected.to compile.with_all_deps } 173 | 174 | it 'removes a property file' do 175 | is_expected.to contain_file("#{directory}/#{title}").with_ensure('absent') 176 | end 177 | end 178 | 179 | describe 'when ensure is file and the value is empty' do 180 | let(:params) do 181 | { 182 | ensure: 'file', 183 | value: '', 184 | dir: directory 185 | } 186 | end 187 | 188 | it { is_expected.to compile.with_all_deps } 189 | 190 | it 'creates the property file with an empty value' do 191 | parameters = { 192 | ensure: 'present', 193 | content: "\n" 194 | } 195 | is_expected.to contain_file("#{directory}/#{title}").with(parameters) 196 | end 197 | end 198 | 199 | describe 'when ensure is directory' do 200 | let(:params) do 201 | { 202 | ensure: 'directory', 203 | value: 'test', 204 | dir: directory 205 | } 206 | end 207 | 208 | it 'fails with an error' do 209 | expect { 210 | is_expected.to compile.with_all_deps 211 | }.to raise_error %r{expects a match for Enum} 212 | end 213 | end 214 | 215 | context 'file attributes' do 216 | context 'default' do 217 | let(:params) do 218 | { 219 | ensure: 'present', 220 | value: 'test', 221 | dir: directory 222 | } 223 | end 224 | 225 | it { is_expected.to compile.with_all_deps } 226 | 227 | it { is_expected.to contain_class('mesos::params') } 228 | 229 | parameters = { 230 | owner: 'root', 231 | group: 'root', 232 | mode: '0644' 233 | } 234 | 235 | it { is_expected.to contain_file("#{directory}/#{title}").with(parameters) } 236 | 237 | it { is_expected.not_to contain_mesos__property(title).with(parameters) } 238 | end 239 | 240 | context 'custom' do 241 | let(:params) do 242 | { 243 | ensure: 'present', 244 | value: 'test', 245 | owner: 'user', 246 | group: 'group', 247 | mode: '0640', 248 | dir: directory 249 | } 250 | end 251 | 252 | it { is_expected.to compile.with_all_deps } 253 | 254 | it { is_expected.to contain_class('mesos::params') } 255 | 256 | parameters = { 257 | owner: 'user', 258 | group: 'group', 259 | mode: '0640' 260 | } 261 | 262 | it { is_expected.to contain_file("#{directory}/#{title}").with(parameters) } 263 | 264 | it { is_expected.to contain_mesos__property(title).with(parameters) } 265 | end 266 | end 267 | 268 | context 'when the property file is overridden' do 269 | let(:params) do 270 | { 271 | value: 'foo', 272 | file: 'some-other-property', 273 | dir: directory 274 | } 275 | end 276 | 277 | it { is_expected.to compile.with_all_deps } 278 | 279 | it { is_expected.to contain_mesos__property(title) } 280 | 281 | it do 282 | parameters = { 283 | ensure: 'present', 284 | content: "foo\n" 285 | } 286 | is_expected.to contain_file("#{directory}/some-other-property").with(parameters) 287 | end 288 | end 289 | 290 | context 'service notification' do 291 | let(:params) do 292 | { 293 | ensure: 'present', 294 | value: 'test', 295 | service: 'Service[my-service]', 296 | dir: directory 297 | } 298 | end 299 | 300 | it 'fails with an error' do 301 | expect { 302 | is_expected.to compile.with_all_deps 303 | }.to raise_error %r{service is deprecated and will be removed in the next major release} 304 | end 305 | end 306 | end 307 | -------------------------------------------------------------------------------- /spec/classes/repo_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'mesos::repo', type: :class do 4 | shared_examples 'debian' do |family, operatingsystem, lsbdistcodename, puppet| 5 | let(:params) do 6 | { 7 | source: 'mesosphere' 8 | } 9 | end 10 | 11 | let(:facts) do 12 | { 13 | # still old fact is needed due to this 14 | # https://github.com/puppetlabs/puppetlabs-apt/blob/master/manifests/params.pp#L3 15 | osfamily: family, 16 | os: { 17 | family: family, 18 | name: operatingsystem, 19 | distro: { codename: lsbdistcodename }, 20 | release: { major: '8', minor: '9', full: '8.9' } 21 | }, 22 | puppetversion: puppet 23 | } 24 | end 25 | 26 | before(:each) do 27 | puppet_debug_override 28 | end 29 | 30 | it { 31 | is_expected.to contain_apt__source('mesos').with( 32 | 'location' => "https://repos.mesosphere.io/#{operatingsystem.downcase}", 33 | 'repos' => 'main', 34 | 'release' => lsbdistcodename.to_s, 35 | 'key' => { 'id' => '81026D0004C44CF7EF55ADF8DF7D54CBE56151BF', 'server' => 'keyserver.ubuntu.com' }, 36 | 'include' => { 'src' => false }, 37 | ) 38 | } 39 | 40 | it { is_expected.to contain_anchor('mesos::repo::begin').that_comes_before('Apt::Source[mesos]') } 41 | it { is_expected.to contain_apt__source('mesos').that_comes_before('Class[apt::update]') } 42 | it { is_expected.to contain_class('apt::update').that_comes_before('Anchor[mesos::repo::end]') } 43 | 44 | context 'undef source' do 45 | let(:params) do 46 | { 47 | source: nil 48 | } 49 | end 50 | 51 | it { is_expected.not_to contain_apt__source('mesos') } 52 | # it { is_expected.to contain_file('/etc/apt/sources.list.d/mesos.list').with({'ensure' => 'absent'}) } 53 | end 54 | end 55 | 56 | context 'on Debian based systems' do 57 | puppet = Puppet.version 58 | 59 | it_behaves_like 'debian', 'Debian', 'Debian', 'wheezy', puppet 60 | it_behaves_like 'debian', 'Debian', 'Ubuntu', 'precise', puppet 61 | end 62 | 63 | shared_examples 'redhat' do |family, operatingsystem, majrel, minrel| 64 | let(:params) do 65 | { 66 | source: 'mesosphere' 67 | } 68 | end 69 | 70 | let(:osrel) { majrel } 71 | let(:facts) do 72 | { 73 | # still old fact is needed due to this 74 | # https://github.com/puppetlabs/puppetlabs-apt/blob/master/manifests/params.pp#L3 75 | osfamily: family, 76 | os: { 77 | family: family, 78 | name: operatingsystem, 79 | release: { major: majrel, minor: minrel, full: "#{majrel}.#{minrel}" } 80 | }, 81 | puppetversion: Puppet.version 82 | } 83 | end 84 | 85 | it { 86 | is_expected.to contain_package('mesosphere-el-repo').with( 87 | 'ensure' => 'present', 88 | 'provider' => 'rpm', 89 | 'source' => "https://repos.mesosphere.io/el/#{majrel}/noarch/RPMS/mesosphere-el-repo-#{majrel}-#{minrel}.noarch.rpm", 90 | ) 91 | } 92 | 93 | it do 94 | is_expected.to contain_exec('yum-clean-expire-cache').with( 95 | command: 'yum clean expire-cache', 96 | ) 97 | end 98 | 99 | context 'undef source' do 100 | let(:params) do 101 | { 102 | source: nil 103 | } 104 | end 105 | 106 | it { is_expected.not_to contain_package('mesosphere-el-repo') } 107 | end 108 | end 109 | 110 | context 'on RedHat based systems' do 111 | it_behaves_like 'redhat', 'RedHat', 'CentOS', '6', '2' 112 | it_behaves_like 'redhat', 'RedHat', 'RedHat', '7', '1' 113 | end 114 | 115 | # see: https://github.com/deric/puppet-mesos/issues/77 116 | context 'custom repository' do 117 | let(:params) do 118 | { 119 | 'source' => { 120 | 'location' => 'http://myrepo.example.com/debian', 121 | 'release' => 'stretch', 122 | 'repos' => 'main', 123 | 'key' => { 124 | 'id' => '99926D0004C44CF7EF55ADF8DF7D54CBE56151BF', 125 | 'server' => 'keyserver.ubuntu.com' 126 | }, 127 | 'include' => { 128 | 'src' => false 129 | } 130 | } 131 | } 132 | end 133 | 134 | let(:facts) do 135 | { 136 | # still old fact is needed due to this 137 | # https://github.com/puppetlabs/puppetlabs-apt/blob/master/manifests/params.pp#L3 138 | osfamily: 'Debian', 139 | os: { 140 | family: 'Debian', 141 | name: 'Debian', 142 | distro: { codename: 'stretch' }, 143 | release: { major: '9', minor: '1', full: '9.1' } 144 | }, 145 | puppetversion: Puppet.version 146 | } 147 | end 148 | 149 | it { 150 | is_expected.to contain_apt__source('mesos').with( 151 | 'location' => 'http://myrepo.example.com/debian', 152 | 'repos' => 'main', 153 | 'release' => 'stretch', 154 | 'key' => { 'id' => '99926D0004C44CF7EF55ADF8DF7D54CBE56151BF', 'server' => 'keyserver.ubuntu.com' }, 155 | 'include' => { 'src' => false }, 156 | ) 157 | } 158 | end 159 | 160 | context 'allow passing only values different from the default' do 161 | let(:params) do 162 | { 163 | 'source' => { 164 | 'key' => { 165 | 'id' => '00026D0004C44CF7EF55ADF8DF7D54CBE56151BF', 166 | 'server' => 'keyserver.example.com' 167 | } 168 | } 169 | } 170 | end 171 | let(:facts) do 172 | { 173 | # still old fact is needed due to this 174 | # https://github.com/puppetlabs/puppetlabs-apt/blob/master/manifests/params.pp#L3 175 | osfamily: 'Debian', 176 | os: { 177 | family: 'Debian', 178 | name: 'Debian', 179 | distro: { codename: 'stretch' }, 180 | release: { major: '9', minor: '1', full: '9.1' } 181 | }, 182 | puppetversion: Puppet.version 183 | } 184 | end 185 | 186 | it { 187 | is_expected.to contain_apt__source('mesos').with( 188 | 'location' => 'https://repos.mesosphere.io/debian', 189 | 'repos' => 'main', 190 | 'release' => 'stretch', 191 | 'key' => { 'id' => '00026D0004C44CF7EF55ADF8DF7D54CBE56151BF', 'server' => 'keyserver.example.com' }, 192 | 'include' => { 'src' => false }, 193 | ) 194 | } 195 | end 196 | 197 | context 'puppet 4.x' do 198 | let(:params) do 199 | { 200 | source: 'mesosphere' 201 | } 202 | end 203 | 204 | let(:facts) do 205 | { 206 | # still old fact is needed due to this 207 | # https://github.com/puppetlabs/puppetlabs-apt/blob/master/manifests/params.pp#L3 208 | osfamily: 'Debian', 209 | os: { 210 | family: 'Debian', 211 | name: 'Debian', 212 | distro: { codename: 'stretch' }, 213 | release: { major: '9', minor: '1', full: '9.1' } 214 | }, 215 | puppetversion: Puppet.version 216 | } 217 | end 218 | 219 | before(:each) do 220 | puppet_debug_override 221 | end 222 | 223 | it { 224 | is_expected.to contain_apt__source('mesos').with( 225 | 'location' => 'https://repos.mesosphere.io/debian', 226 | 'repos' => 'main', 227 | 'release' => 'stretch', 228 | 'key' => { 'id' => '81026D0004C44CF7EF55ADF8DF7D54CBE56151BF', 'server' => 'keyserver.ubuntu.com' }, 229 | 'include' => { 'src' => false }, 230 | ) 231 | } 232 | end 233 | 234 | context 'Ubuntu' do 235 | let(:facts) do 236 | { 237 | # still old fact is needed due to this 238 | # https://github.com/puppetlabs/puppetlabs-apt/blob/master/manifests/params.pp#L3 239 | osfamily: 'Debian', 240 | os: { 241 | family: 'Debian', 242 | name: 'Ubuntu', 243 | distro: { codename: 'xenial' }, 244 | release: { major: '16.04', full: '16.04' } 245 | }, 246 | puppetversion: Puppet.version 247 | } 248 | end 249 | 250 | let(:params) do 251 | { 252 | source: 'mesosphere' 253 | } 254 | end 255 | 256 | it { 257 | is_expected.to contain_apt__source('mesos').with( 258 | 'location' => 'https://repos.mesosphere.io/ubuntu', 259 | 'repos' => 'main', 260 | 'release' => 'xenial', 261 | 'key' => { 'id' => '81026D0004C44CF7EF55ADF8DF7D54CBE56151BF', 'server' => 'keyserver.ubuntu.com' }, 262 | 'include' => { 'src' => false }, 263 | ) 264 | } 265 | 266 | end 267 | end 268 | -------------------------------------------------------------------------------- /manifests/slave.pp: -------------------------------------------------------------------------------- 1 | # Class: mesos::slave 2 | # 3 | # This module manages Mesos slave 4 | # 5 | # Parameters: 6 | # [*enable*] 7 | # Install Mesos slave service (default: true) 8 | # 9 | # [*master*] 10 | # IP address of Mesos master (default: localhost) 11 | # 12 | # [*master_port*] 13 | # Mesos master's port (default 5050) 14 | # 15 | # [*zookeeper*] 16 | # Zookeeper URL string (which keeps track of current Mesos master) 17 | # 18 | # [*work_dir*] 19 | # Directory for storing task's temporary files (default: /var/lib/mesos) 20 | # 21 | # [*isolation*] 22 | # Isolation mechanism - either 'process' or 'cgroups' newer versions 23 | # of Mesos > 0.18 support isolation mechanism 'cgroups/cpu,cgroups/mem' 24 | # or posix/cpu,posix/mem 25 | # 26 | # [*options*] 27 | # Any extra arguments that are not named here could be 28 | # stored in a hash: 29 | # 30 | # options => { "key" => "value" } 31 | # 32 | # (as value you can pass either string, boolean or numeric value) 33 | # which is serialized to disk and then passed to mesos-slave as: 34 | # 35 | # --key=value 36 | # 37 | # [*single_role*] 38 | # Currently Mesos packages ships with both mesos-master and mesos-slave 39 | # enabled by default. `single_role` assumes that you use only either of 40 | # those on one machine. Default: true (mesos-master service will be 41 | # disabled on slave node) 42 | # 43 | # Sample Usage: 44 | # 45 | # class{ 'mesos::slave': 46 | # master => '10.0.0.1', 47 | # master_port => 5050, 48 | # } 49 | # 50 | 51 | class mesos::slave ( 52 | Boolean $enable = true, 53 | Integer $port = 5051, 54 | String $work_dir = '/var/lib/mesos', 55 | Boolean $checkpoint = false, 56 | Optional[String] $isolation = undef, 57 | Stdlib::Absolutepath $conf_dir = '/etc/mesos-slave', 58 | Stdlib::Absolutepath $conf_file = '/etc/default/mesos-slave', 59 | Stdlib::Absolutepath $credentials_file = '/etc/mesos/slave-credentials', 60 | String $master = $mesos::master, 61 | Integer $master_port = $mesos::master_port, 62 | Optional[Variant[String,Array[String]]] $zookeeper = $mesos::zookeeper, 63 | String $zk_path = $mesos::zk_path, 64 | Integer $zk_default_port = $mesos::zk_default_port, 65 | String $owner = $mesos::owner, 66 | String $group = $mesos::group, 67 | Optional[String] $listen_address = $mesos::listen_address, 68 | Boolean $manage_service = $mesos::manage_service, 69 | Hash $env_var = {}, 70 | Hash $cgroups = {}, 71 | Hash $options = {}, 72 | Hash $resources = {}, 73 | Hash $attributes = {}, 74 | Optional[String] $principal = undef, 75 | Optional[String] $secret = undef, 76 | Boolean $syslog_logger = true, 77 | Boolean $use_hiera = $mesos::use_hiera, 78 | Boolean $single_role = $mesos::single_role, 79 | Optional[String] $service_provider = $mesos::service_provider, 80 | Boolean $manage_service_file = $::mesos::manage_service_file, 81 | String $systemd_wants = $::mesos::params::systemd_wants, 82 | String $systemd_after = $::mesos::params::systemd_after, 83 | ) inherits ::mesos { 84 | 85 | if !empty($zookeeper) { 86 | unless $zookeeper =~ Array { 87 | warning('\$zookeeper parameter should be an array of IP addresses, please update your configuration.') 88 | } 89 | $zookeeper_url = zookeeper_servers_url($zookeeper, $zk_path, $zk_default_port) 90 | } 91 | 92 | File { 93 | owner => $owner, 94 | group => $group, 95 | } 96 | 97 | file { $conf_dir: 98 | ensure => directory, 99 | recurse => true, 100 | purge => true, 101 | force => true, 102 | require => Class['::mesos::install'], 103 | notify => Service['mesos-slave'], # when key is removed we want to reload the service 104 | } 105 | 106 | file { "${conf_dir}/resources": 107 | ensure => directory, 108 | require => File[$conf_dir], 109 | recurse => true, 110 | purge => true, 111 | } 112 | 113 | file { "${conf_dir}/attributes": 114 | ensure => directory, 115 | require => File[$conf_dir], 116 | recurse => true, 117 | purge => true, 118 | } 119 | 120 | # stores properties in file structure 121 | create_resources(mesos::property, 122 | mesos_hash_parser($cgroups, 'slave', 'cgroups'), 123 | { 124 | owner => $owner, 125 | group => $group, 126 | dir => $conf_dir, 127 | notify => Service['mesos-slave'], 128 | } 129 | ) 130 | 131 | # for backwards compatibility, prefered way is specification via $options 132 | if !empty($isolation) { 133 | $isolator_options = {'isolation' => $isolation} 134 | } else { 135 | $isolator_options = {} 136 | } 137 | 138 | if (!empty($principal) and !empty($secret)) { 139 | $credentials_options = {'credential' => $credentials_file} 140 | $credentials_content = "{\"principal\": \"${principal}\", \"secret\": \"${secret}\"}" 141 | $credentials_ensure = file 142 | } else { 143 | $credentials_options = {} 144 | $credentials_content = undef 145 | $credentials_ensure = absent 146 | } 147 | 148 | if defined('$::mesos_version') { 149 | if ($::mesos_version != undef) and (versioncmp($::mesos_version, '0.28.0') >= 0) 150 | and $service_provider != 'systemd' { 151 | # otherwise rely on mesos-slave defaults 152 | $systemd_support = {'systemd_enable_support' => false} 153 | } else { 154 | $systemd_support = {} 155 | } 156 | } else { 157 | $systemd_support = {} 158 | } 159 | 160 | if $use_hiera { 161 | # In Puppet 3 automatic lookup won't merge options across multiple config 162 | # files, see https://www.devco.net/archives/2016/02/03/puppet-4-data-lookup-strategies.php 163 | $opts = hiera_hash('mesos::slave::options', $options) 164 | $merged_options = merge($systemd_support, $opts, $isolator_options, $credentials_options) 165 | } else { 166 | $merged_options = merge($systemd_support, $options, $isolator_options, $credentials_options) 167 | } 168 | 169 | # work_dir can't be specified via options, 170 | # we would get a duplicate declaration error 171 | mesos::property {'slave_work_dir': 172 | value => $work_dir, 173 | dir => $conf_dir, 174 | file => 'work_dir', 175 | owner => $owner, 176 | group => $group, 177 | notify => Service['mesos-slave'], 178 | } 179 | 180 | file { $work_dir: 181 | ensure => directory, 182 | } 183 | 184 | file { $credentials_file: 185 | ensure => $credentials_ensure, 186 | content => $credentials_content, 187 | mode => '0400', 188 | notify => Service['mesos-slave'], 189 | } 190 | 191 | create_resources(mesos::property, 192 | mesos_hash_parser($merged_options, 'slave'), 193 | { 194 | dir => $conf_dir, 195 | owner => $owner, 196 | group => $group, 197 | notify => Service['mesos-slave'], 198 | } 199 | ) 200 | 201 | create_resources(mesos::property, 202 | mesos_hash_parser($resources, 'resources'), 203 | { 204 | dir => "${conf_dir}/resources", 205 | owner => $owner, 206 | group => $group, 207 | notify => Service['mesos-slave'], 208 | } 209 | ) 210 | 211 | create_resources(mesos::property, 212 | mesos_hash_parser($attributes, 'attributes'), 213 | { 214 | dir => "${conf_dir}/attributes", 215 | owner => $owner, 216 | group => $group, 217 | notify => Service['mesos-slave'], 218 | } 219 | ) 220 | 221 | file { $conf_file: 222 | ensure => 'present', 223 | content => template('mesos/slave.erb'), 224 | mode => '0644', 225 | require => [Class['mesos::config'], File[$conf_dir], Package['mesos']], 226 | } 227 | 228 | $logger_ensure = $syslog_logger ? { 229 | true => absent, 230 | false => present, 231 | } 232 | mesos::property { 'slave_logger': 233 | ensure => $logger_ensure, 234 | file => 'logger', 235 | value => false, 236 | dir => $conf_dir, 237 | owner => $owner, 238 | group => $group, 239 | require => File[$conf_dir], 240 | } 241 | 242 | # Install mesos-slave service 243 | mesos::service { 'slave': 244 | enable => $enable, 245 | service_provider => $service_provider, 246 | manage => $manage_service, 247 | subscribe => File[$conf_file], 248 | manage_service_file => $manage_service_file, 249 | systemd_wants => $systemd_wants, 250 | systemd_after => $systemd_after, 251 | } 252 | 253 | if (!defined(Class['mesos::master']) and $single_role) { 254 | mesos::service { 'master': 255 | enable => false, 256 | manage => $manage_service, 257 | } 258 | } 259 | } 260 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | 2 | Apache License 3 | Version 2.0, January 2004 4 | http://www.apache.org/licenses/ 5 | 6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | "License" shall mean the terms and conditions for use, reproduction, 11 | and distribution as defined by Sections 1 through 9 of this document. 12 | 13 | "Licensor" shall mean the copyright owner or entity authorized by 14 | the copyright owner that is granting the License. 15 | 16 | "Legal Entity" shall mean the union of the acting entity and all 17 | other entities that control, are controlled by, or are under common 18 | control with that entity. For the purposes of this definition, 19 | "control" means (i) the power, direct or indirect, to cause the 20 | direction or management of such entity, whether by contract or 21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 22 | outstanding shares, or (iii) beneficial ownership of such entity. 23 | 24 | "You" (or "Your") shall mean an individual or Legal Entity 25 | exercising permissions granted by this License. 26 | 27 | "Source" form shall mean the preferred form for making modifications, 28 | including but not limited to software source code, documentation 29 | source, and configuration files. 30 | 31 | "Object" form shall mean any form resulting from mechanical 32 | transformation or translation of a Source form, including but 33 | not limited to compiled object code, generated documentation, 34 | and conversions to other media types. 35 | 36 | "Work" shall mean the work of authorship, whether in Source or 37 | Object form, made available under the License, as indicated by a 38 | copyright notice that is included in or attached to the work 39 | (an example is provided in the Appendix below). 40 | 41 | "Derivative Works" shall mean any work, whether in Source or Object 42 | form, that is based on (or derived from) the Work and for which the 43 | editorial revisions, annotations, elaborations, or other modifications 44 | represent, as a whole, an original work of authorship. For the purposes 45 | of this License, Derivative Works shall not include works that remain 46 | separable from, or merely link (or bind by name) to the interfaces of, 47 | the Work and Derivative Works thereof. 48 | 49 | "Contribution" shall mean any work of authorship, including 50 | the original version of the Work and any modifications or additions 51 | to that Work or Derivative Works thereof, that is intentionally 52 | submitted to Licensor for inclusion in the Work by the copyright owner 53 | or by an individual or Legal Entity authorized to submit on behalf of 54 | the copyright owner. For the purposes of this definition, "submitted" 55 | means any form of electronic, verbal, or written communication sent 56 | to the Licensor or its representatives, including but not limited to 57 | communication on electronic mailing lists, source code control systems, 58 | and issue tracking systems that are managed by, or on behalf of, the 59 | Licensor for the purpose of discussing and improving the Work, but 60 | excluding communication that is conspicuously marked or otherwise 61 | designated in writing by the copyright owner as "Not a Contribution." 62 | 63 | "Contributor" shall mean Licensor and any individual or Legal Entity 64 | on behalf of whom a Contribution has been received by Licensor and 65 | subsequently incorporated within the Work. 66 | 67 | 2. Grant of Copyright License. Subject to the terms and conditions of 68 | this License, each Contributor hereby grants to You a perpetual, 69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 70 | copyright license to reproduce, prepare Derivative Works of, 71 | publicly display, publicly perform, sublicense, and distribute the 72 | Work and such Derivative Works in Source or Object form. 73 | 74 | 3. Grant of Patent License. Subject to the terms and conditions of 75 | this License, each Contributor hereby grants to You a perpetual, 76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 77 | (except as stated in this section) patent license to make, have made, 78 | use, offer to sell, sell, import, and otherwise transfer the Work, 79 | where such license applies only to those patent claims licensable 80 | by such Contributor that are necessarily infringed by their 81 | Contribution(s) alone or by combination of their Contribution(s) 82 | with the Work to which such Contribution(s) was submitted. If You 83 | institute patent litigation against any entity (including a 84 | cross-claim or counterclaim in a lawsuit) alleging that the Work 85 | or a Contribution incorporated within the Work constitutes direct 86 | or contributory patent infringement, then any patent licenses 87 | granted to You under this License for that Work shall terminate 88 | as of the date such litigation is filed. 89 | 90 | 4. Redistribution. You may reproduce and distribute copies of the 91 | Work or Derivative Works thereof in any medium, with or without 92 | modifications, and in Source or Object form, provided that You 93 | meet the following conditions: 94 | 95 | (a) You must give any other recipients of the Work or 96 | Derivative Works a copy of this License; and 97 | 98 | (b) You must cause any modified files to carry prominent notices 99 | stating that You changed the files; and 100 | 101 | (c) You must retain, in the Source form of any Derivative Works 102 | that You distribute, all copyright, patent, trademark, and 103 | attribution notices from the Source form of the Work, 104 | excluding those notices that do not pertain to any part of 105 | the Derivative Works; and 106 | 107 | (d) If the Work includes a "NOTICE" text file as part of its 108 | distribution, then any Derivative Works that You distribute must 109 | include a readable copy of the attribution notices contained 110 | within such NOTICE file, excluding those notices that do not 111 | pertain to any part of the Derivative Works, in at least one 112 | of the following places: within a NOTICE text file distributed 113 | as part of the Derivative Works; within the Source form or 114 | documentation, if provided along with the Derivative Works; or, 115 | within a display generated by the Derivative Works, if and 116 | wherever such third-party notices normally appear. The contents 117 | of the NOTICE file are for informational purposes only and 118 | do not modify the License. You may add Your own attribution 119 | notices within Derivative Works that You distribute, alongside 120 | or as an addendum to the NOTICE text from the Work, provided 121 | that such additional attribution notices cannot be construed 122 | as modifying the License. 123 | 124 | You may add Your own copyright statement to Your modifications and 125 | may provide additional or different license terms and conditions 126 | for use, reproduction, or distribution of Your modifications, or 127 | for any such Derivative Works as a whole, provided Your use, 128 | reproduction, and distribution of the Work otherwise complies with 129 | the conditions stated in this License. 130 | 131 | 5. Submission of Contributions. Unless You explicitly state otherwise, 132 | any Contribution intentionally submitted for inclusion in the Work 133 | by You to the Licensor shall be under the terms and conditions of 134 | this License, without any additional terms or conditions. 135 | Notwithstanding the above, nothing herein shall supersede or modify 136 | the terms of any separate license agreement you may have executed 137 | with Licensor regarding such Contributions. 138 | 139 | 6. Trademarks. This License does not grant permission to use the trade 140 | names, trademarks, service marks, or product names of the Licensor, 141 | except as required for reasonable and customary use in describing the 142 | origin of the Work and reproducing the content of the NOTICE file. 143 | 144 | 7. Disclaimer of Warranty. Unless required by applicable law or 145 | agreed to in writing, Licensor provides the Work (and each 146 | Contributor provides its Contributions) on an "AS IS" BASIS, 147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 148 | implied, including, without limitation, any warranties or conditions 149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 150 | PARTICULAR PURPOSE. You are solely responsible for determining the 151 | appropriateness of using or redistributing the Work and assume any 152 | risks associated with Your exercise of permissions under this License. 153 | 154 | 8. Limitation of Liability. In no event and under no legal theory, 155 | whether in tort (including negligence), contract, or otherwise, 156 | unless required by applicable law (such as deliberate and grossly 157 | negligent acts) or agreed to in writing, shall any Contributor be 158 | liable to You for damages, including any direct, indirect, special, 159 | incidental, or consequential damages of any character arising as a 160 | result of this License or out of the use or inability to use the 161 | Work (including but not limited to damages for loss of goodwill, 162 | work stoppage, computer failure or malfunction, or any and all 163 | other commercial damages or losses), even if such Contributor 164 | has been advised of the possibility of such damages. 165 | 166 | 9. Accepting Warranty or Additional Liability. While redistributing 167 | the Work or Derivative Works thereof, You may choose to offer, 168 | and charge a fee for, acceptance of support, warranty, indemnity, 169 | or other liability obligations and/or rights consistent with this 170 | License. However, in accepting such obligations, You may act only 171 | on Your own behalf and on Your sole responsibility, not on behalf 172 | of any other Contributor, and only if You agree to indemnify, 173 | defend, and hold each Contributor harmless for any liability 174 | incurred by, or claims asserted against, such Contributor by reason 175 | of your accepting any such warranty or additional liability. 176 | 177 | END OF TERMS AND CONDITIONS 178 | 179 | APPENDIX: How to apply the Apache License to your work. 180 | 181 | To apply the Apache License to your work, attach the following 182 | boilerplate notice, with the fields enclosed by brackets "[]" 183 | replaced with your own identifying information. (Don't include 184 | the brackets!) The text should be enclosed in the appropriate 185 | comment syntax for the file format. We also recommend that a 186 | file or class name and description of purpose be included on the 187 | same "printed page" as the copyright notice for easier 188 | identification within third-party archives. 189 | 190 | Copyright 2013 Tomas Barton 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | you may not use this file except in compliance with the License. 194 | You may obtain a copy of the License at 195 | 196 | http://www.apache.org/licenses/LICENSE-2.0 197 | 198 | Unless required by applicable law or agreed to in writing, software 199 | distributed under the License is distributed on an "AS IS" BASIS, 200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 201 | See the License for the specific language governing permissions and 202 | limitations under the License. 203 | -------------------------------------------------------------------------------- /spec/classes/master_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'mesos::master', type: :class do 4 | let(:owner) { 'mesos' } 5 | let(:group) { 'mesos' } 6 | let(:conf) { '/etc/mesos-master' } 7 | let(:file) { '/etc/default/mesos-master' } 8 | 9 | let(:params) do 10 | { 11 | conf_dir: conf, 12 | owner: owner, 13 | group: group 14 | } 15 | end 16 | 17 | let(:facts) do 18 | { 19 | # still old fact is needed due to this 20 | # https://github.com/puppetlabs/puppetlabs-apt/blob/master/manifests/params.pp#L3 21 | osfamily: 'Debian', 22 | os: { 23 | family: 'Debian', 24 | name: 'Debian', 25 | distro: { codename: 'stretch' }, 26 | release: { major: '9', minor: '1', full: '9.1' } 27 | }, 28 | path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 29 | puppetversion: Puppet.version 30 | } 31 | end 32 | 33 | before(:each) do 34 | puppet_debug_override 35 | end 36 | 37 | it { is_expected.to contain_package('mesos') } 38 | it { is_expected.to contain_class('mesos::master') } 39 | it { 40 | is_expected.to contain_service('mesos-master').with( 41 | ensure: 'running', 42 | enable: true, 43 | ) 44 | } 45 | 46 | it { 47 | is_expected.to contain_file(file).with( 48 | 'ensure' => 'present', 49 | 'owner' => owner, 50 | 'group' => group, 51 | 'mode' => '0644', 52 | ) 53 | } 54 | 55 | it 'shoud not set any IP address by default' do 56 | is_expected.not_to contain_file( 57 | file, 58 | ).with_content(%r{^export MESOS_IP=}) 59 | end 60 | 61 | # no zookeeper set by default 62 | it { is_expected.not_to contain_file(file).with_content(%r{MESOS_ZK=""}) } 63 | 64 | it { is_expected.to contain_file(file).with_content(%r{MESOS_PORT=5050}) } 65 | 66 | context 'with zookeeper' do 67 | let(:params) do 68 | { 69 | zookeeper: ['192.168.1.100:2181'] 70 | } 71 | end 72 | 73 | it { 74 | is_expected.to contain_file( 75 | file, 76 | ).with_content(/^export MESOS_ZK="zk:\/\/192.168.1.100:2181\/mesos"/) 77 | } 78 | end 79 | 80 | context 'setting master port' do 81 | let(:params) do 82 | { 83 | master_port: 4040 84 | } 85 | end 86 | 87 | it { is_expected.to contain_file(file).with_content(%r{^export MESOS_PORT=4040}) } 88 | end 89 | 90 | it { is_expected.to contain_file(file).with_content(%r{CLUSTER="mesos"}) } 91 | 92 | context 'setting cluster name' do 93 | let(:params) do 94 | { 95 | cluster: 'cluster' 96 | } 97 | end 98 | 99 | it { is_expected.to contain_file(file).with_content(%r{^export MESOS_CLUSTER="cluster"}) } 100 | end 101 | 102 | context 'setting environment variables' do 103 | let(:params) do 104 | { 105 | env_var: { 106 | 'JAVA_HOME' => '/usr/bin/java', 107 | 'MESOS_HOME' => '/var/lib/mesos' 108 | } 109 | } 110 | end 111 | 112 | it { 113 | is_expected.to contain_file( 114 | file, 115 | ).with_content(/export JAVA_HOME="\/usr\/bin\/java"/) 116 | } 117 | 118 | it { 119 | is_expected.to contain_file( 120 | file, 121 | ).with_content(/export MESOS_HOME="\/var\/lib\/mesos"/) 122 | } 123 | end 124 | 125 | context 'disabling service' do 126 | let(:params) do 127 | { 128 | enable: false 129 | } 130 | end 131 | 132 | it { 133 | is_expected.to contain_service('mesos-master').with( 134 | enable: false, 135 | ) 136 | } 137 | end 138 | 139 | context 'changing master config file location' do 140 | let(:master_file) { '/etc/mesos/master' } 141 | let(:params) do 142 | { 143 | conf_file: master_file 144 | } 145 | end 146 | 147 | it { 148 | is_expected.to contain_file(master_file).with( 149 | 'ensure' => 'present', 150 | 'mode' => '0644', 151 | ) 152 | } 153 | end 154 | 155 | context 'set quorum via options' do 156 | let(:params) do 157 | { 158 | conf_dir: conf, 159 | options: { 'quorum' => 4 } 160 | } 161 | end 162 | 163 | it 'has quorum file in master config dir' do 164 | is_expected.to contain_file( 165 | "#{conf}/quorum", 166 | ).with_content(%r{^4$}).with( 167 | 'ensure' => 'present', 168 | ) 169 | end 170 | end 171 | 172 | context 'allow changing conf_dir' do 173 | let(:my_conf_dir) { '/var/mesos-master' } 174 | let(:params) do 175 | { 176 | conf_dir: my_conf_dir, 177 | options: { 'quorum' => 4 } 178 | } 179 | end 180 | 181 | it 'has quorum file in master config dir' do 182 | is_expected.to contain_file( 183 | "#{my_conf_dir}/quorum", 184 | ).with_content(%r{^4$}).with( 185 | 'ensure' => 'present', 186 | ) 187 | end 188 | end 189 | 190 | context 'work_dir' do 191 | let(:work_dir) { '/var/lib/mesos' } 192 | let(:params) do 193 | { 194 | conf_dir: conf, 195 | work_dir: work_dir, 196 | owner: owner, 197 | group: group 198 | } 199 | end 200 | 201 | it do 202 | is_expected.to contain_file(work_dir).with( 203 | 'ensure' => 'directory', 204 | 'owner' => owner, 205 | 'group' => group, 206 | ) 207 | end 208 | 209 | it do 210 | is_expected.to contain_mesos__property('master_work_dir').with( 211 | 'owner' => owner, 212 | 'group' => group, 213 | 'dir' => conf, 214 | 'value' => work_dir, 215 | ) 216 | end 217 | 218 | it do 219 | is_expected.to contain_file("#{conf}/work_dir") 220 | .with_content(work_dir + "\n") 221 | .that_requires("File[#{conf}]") 222 | end 223 | end 224 | 225 | context 'support boolean flags' do 226 | let(:my_conf_dir) { '/var/mesos-master' } 227 | let(:params) do 228 | { 229 | conf_dir: my_conf_dir, 230 | options: { 'authenticate' => true } 231 | } 232 | end 233 | 234 | it 'has authenticate file in config dir' do 235 | is_expected.to contain_file( 236 | "#{my_conf_dir}/?authenticate", 237 | ).with( 238 | 'ensure' => 'present', 239 | ) 240 | end 241 | end 242 | 243 | context 'nofify service after removing a key' do 244 | let(:my_conf_dir) { '/tmp/mesos-conf' } 245 | let(:params) do 246 | { 247 | conf_dir: my_conf_dir, 248 | options: { 'quorum' => 4 } 249 | } 250 | end 251 | 252 | before(:each) do 253 | system("mkdir -p #{my_conf_dir} && touch #{my_conf_dir}/foo") 254 | end 255 | 256 | after(:each) do 257 | system("rm -rf #{my_conf_dir}") 258 | end 259 | 260 | it { is_expected.to contain_service('mesos-master') } 261 | it { is_expected.to contain_file(my_conf_dir.to_s).that_notifies('Service[mesos-master]') } 262 | end 263 | 264 | context 'acls' do 265 | context 'default w/o acls' do 266 | let(:params) do 267 | { 268 | conf_dir: conf, 269 | owner: owner, 270 | group: group 271 | } 272 | end 273 | 274 | it 'has no acls property' do 275 | is_expected.not_to contain_mesos__property( 276 | 'master_acls', 277 | ) 278 | end 279 | 280 | it 'has not acls file' do 281 | is_expected.to contain_file( 282 | '/etc/mesos/acls', 283 | ) 284 | .with( 285 | 'ensure' => 'absent', 286 | ) 287 | end 288 | end 289 | 290 | context 'w/ acls' do 291 | let(:params) do 292 | { 293 | conf_dir: conf, 294 | owner: owner, 295 | group: group, 296 | acls: { 'some-key' => ['some-value', 'some-other-value'] } 297 | } 298 | end 299 | 300 | it 'has acls property' do 301 | is_expected.to contain_mesos__property( 302 | 'master_acls', 303 | ).with('value' => '/etc/mesos/acls') 304 | end 305 | 306 | it 'has acls file' do 307 | is_expected.to contain_file( 308 | '/etc/mesos/acls', 309 | ).with( 310 | 'ensure' => 'file', 311 | 'content' => %r{{"some-key":\s*\["some-value",\s*"some-other-value"\]}}, 312 | 'owner' => owner, 313 | 'group' => group, 314 | 'mode' => '0444', 315 | ) 316 | end 317 | end 318 | end 319 | 320 | context 'credentials' do 321 | context 'default w/o credentials' do 322 | let(:params) do 323 | { 324 | conf_dir: conf, 325 | owner: owner, 326 | group: group 327 | } 328 | end 329 | 330 | it 'has no credentials property' do 331 | is_expected.not_to contain_mesos__property( 332 | 'master_credentials', 333 | ) 334 | end 335 | 336 | it 'has not credentials file' do 337 | is_expected.to contain_file( 338 | '/etc/mesos/master-credentials', 339 | ) 340 | .with( 341 | 'ensure' => 'absent', 342 | ) 343 | end 344 | end 345 | 346 | context 'w/ credentials' do 347 | let(:params) do 348 | { 349 | conf_dir: conf, 350 | owner: owner, 351 | group: group, 352 | credentials: [{ 'principal' => 'some-mesos-principal', 'secret' => 'a-very-secret' }] 353 | } 354 | end 355 | 356 | it 'has credentials property' do 357 | is_expected.to contain_mesos__property( 358 | 'master_credentials', 359 | ).with( 360 | 'value' => 'file:///etc/mesos/master-credentials', 361 | ) 362 | end 363 | 364 | it 'has credentials file' do 365 | is_expected.to contain_file( 366 | '/etc/mesos/master-credentials', 367 | ).with( 368 | 'ensure' => 'file', 369 | 'content' => %r{{"credentials":\s*\[{"principal":\s*"some-mesos-principal",\s*"secret":\s*"a-very-secret"}\]}}, 370 | 'owner' => owner, 371 | 'group' => group, 372 | 'mode' => '0400', 373 | ) 374 | end 375 | end 376 | 377 | context 'syslog logger' do 378 | describe 'when syslog_logger is true' do 379 | let(:params) do 380 | { 381 | conf_dir: conf, 382 | owner: owner, 383 | group: group, 384 | syslog_logger: true 385 | } 386 | end 387 | 388 | it do 389 | is_expected.to contain_mesos__property('master_logger') 390 | .with( 391 | ensure: 'absent', 392 | file: 'logger', 393 | value: false, 394 | dir: conf, 395 | owner: owner, 396 | group: group, 397 | ) 398 | 399 | is_expected.to contain_file("#{conf}/?no-logger").with_ensure('absent') 400 | end 401 | end 402 | 403 | describe 'when syslog_logger is false' do 404 | let(:params) do 405 | { 406 | conf_dir: conf, 407 | owner: owner, 408 | group: group, 409 | syslog_logger: false 410 | } 411 | end 412 | 413 | it do 414 | is_expected.to contain_mesos__property('master_logger') 415 | .with( 416 | ensure: 'present', 417 | file: 'logger', 418 | value: false, 419 | dir: conf, 420 | owner: owner, 421 | group: group, 422 | ) 423 | 424 | is_expected.to contain_file("#{conf}/?no-logger").with_ensure('present') 425 | end 426 | end 427 | end 428 | end 429 | 430 | context 'test merging hashes from hiera' do 431 | let(:params) do 432 | { 433 | use_hiera: true 434 | } 435 | end 436 | 437 | # quorum defined in spec/fixtures/hiera/test.yaml 438 | it 'defines quorum' do 439 | is_expected.to contain_file("#{conf}/quorum").with_ensure('present') 440 | is_expected.to contain_mesos__property('master_quorum') 441 | .with(value: 2) 442 | end 443 | # advertise_ip defined in spec/fixtures/hiera/default.yaml 444 | it 'with advertised IP config' do 445 | is_expected.to contain_file("#{conf}/advertise_ip").with_ensure('present') 446 | is_expected.to contain_mesos__property('master_advertise_ip') 447 | .with(value: '10.0.0.1') 448 | end 449 | end 450 | 451 | context 'single role' do 452 | it { 453 | is_expected.to contain_service('mesos-master').with( 454 | ensure: 'running', 455 | enable: true, 456 | ) 457 | } 458 | 459 | it { 460 | is_expected.to contain_service('mesos-slave').with( 461 | enable: false, 462 | ) 463 | } 464 | 465 | it { 466 | is_expected.to contain_mesos__service('master').with(enable: true) 467 | is_expected.to contain_mesos__service('slave').with(enable: false) 468 | } 469 | 470 | context 'disable single role' do 471 | let(:params) do 472 | { 473 | single_role: false 474 | } 475 | end 476 | 477 | it { 478 | is_expected.not_to contain_service('mesos-slave').with( 479 | enable: false, 480 | ) 481 | } 482 | end 483 | end 484 | 485 | context 'custom systemd configuration' do 486 | let(:params) do 487 | { 488 | service_provider: 'systemd', 489 | manage_service_file: true, 490 | systemd_after: 'network-online.target openvpn-client@.service', 491 | systemd_wants: 'network-online.target openvpn-client@.service' 492 | } 493 | end 494 | 495 | it do 496 | is_expected.to contain_service('mesos-master').with( 497 | ensure: 'running', 498 | enable: true, 499 | ) 500 | end 501 | 502 | it do 503 | is_expected.to contain_mesos__service('master').with(enable: true) 504 | end 505 | 506 | it do 507 | is_expected.to contain_file( 508 | '/etc/systemd/system/mesos-master.service', 509 | ).with( 510 | 'ensure' => 'present', 511 | ) 512 | end 513 | 514 | it do 515 | is_expected.to contain_file( 516 | '/etc/systemd/system/mesos-master.service', 517 | ).with_content(%r{Wants=network-online.target openvpn-client@.service}) 518 | end 519 | 520 | it do 521 | is_expected.to contain_file( 522 | '/etc/systemd/system/mesos-master.service', 523 | ).with_content(%r{After=network-online.target openvpn-client@.service}) 524 | end 525 | end 526 | end 527 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Mesos Puppet Module 2 | [![Puppet 3 | Forge](http://img.shields.io/puppetforge/v/deric/mesos.svg)](https://forge.puppetlabs.com/deric/mesos) [![Build Status](https://travis-ci.org/deric/puppet-mesos.png)](https://travis-ci.org/deric/puppet-mesos) [![Puppet Forge 4 | Downloads](http://img.shields.io/puppetforge/dt/deric/mesos.svg)](https://forge.puppetlabs.com/deric/mesos/scores) 5 | 6 | *COMPATIBILITY NOTE:* current version (0.6.x) requires `puppetlabs-apt >= 2.1.0` which has significantly refactored API (doesn't matter if you don't wanna use Mesosphere APT repo). 7 | 8 | | `deric-mesos`| Puppet 3.x | Puppet 4.x | Puppet 5.x | 9 | | ------------ | ------------- |--------------| -----------| 10 | | `0.9.x` | :heavy_check_mark: | :heavy_check_mark: | :heavy_check_mark: | 11 | | `1.x.x` | :x: | :heavy_check_mark: | :heavy_check_mark: | 12 | 13 | For installing master 14 | 15 | ```puppet 16 | class{'mesos': 17 | repo => 'mesosphere', 18 | zookeeper => [ '192.168.1.1', '192.168.1.2', '192.168.1.3'], 19 | } 20 | 21 | class{'mesos::master': 22 | work_dir => '/var/lib/mesos', 23 | options => { 24 | quorum => 2 25 | } 26 | } 27 | ``` 28 | From ZooKeeper array an URI is created `zk://192.168.1.1:2181,192.168.1.2:2181,192.168.1.3:2181/mesos`. In order to customize this use either `server:port` or see `zk_path` and `zk_default_port` variable. 29 | 30 | Example slave configuration: 31 | 32 | ```puppet 33 | class{'mesos::slave': 34 | attributes => { 35 | 'env' => 'production', 36 | }, 37 | resources => { 38 | 'ports' => '[10000-65535]' 39 | }, 40 | options => { 41 | 'isolation' => 'cgroups/cpu,cgroups/mem', 42 | 'containerizers' => 'docker,mesos', 43 | 'hostname' => $::fqdn, 44 | } 45 | } 46 | ``` 47 | 48 | for using Hiera and other options see below. 49 | 50 | 51 | ## Shared parameters 52 | 53 | Parameters: 54 | 55 | - `zookeeper` - Array of ZooKeeper servers (with port) which is used for slaves connecting to the master and also for leader election, e.g.: 56 | - single ZooKeeper: `127.0.0.1:2181` (which isn't fault tolerant) 57 | - multiple ZooKeepers: `[ '192.168.1.1:2181', '192.168.1.2:2181', '192.168.1.3:2181']` (usually 3 or 5 ZooKeepers should be enough) 58 | - ZooKeeper URL will be stored in `/etc/mesos/zk`, `/etc/default/mesos-master` and/or `/etc/default/mesos-slave` 59 | - `conf_dir` - directory with simple configuration files containing master/slave parameters (name of the file is a key, contents its value) 60 | - this directory will be completely managed by Puppet 61 | - `env_var` - shared master/slave execution environment variables (see example under slave) 62 | - `version` - install specific version of Mesos 63 | - `manage_python` - Control whether mesos module should install python 64 | - `manage_zk_file` - Control whether module manages /etc/mesos/zk (default: true) 65 | - `manage_service` - Whether Puppet should ensure service state (applies to `mesos-master` and `mesos-slave`) (default: `true`) 66 | - `single_role` - When enabled each machine is expected to run either master or slave service (default: `true`) 67 | - `manage_service_file` Provide custom service definition - only `systemd` is supported right now (default: `false`) 68 | - `systemd_after` Ensures that the configured unit is started after the listed unit finished starting up (default: `network.target`) 69 | - `systemd_wants` If this unit gets activated, the units listed here will be activated as well (default: `network.target`) 70 | 71 | ### Master 72 | 73 | Should be as simple as this, on master node: 74 | 75 | ```puppet 76 | class{'mesos::master': } 77 | ``` 78 | optionally you can specify some parameters or it is possible to configure Mesos via Hiera (see below). 79 | 80 | ```puppet 81 | class{'mesos::master': 82 | master_port => 5050, 83 | work_dir => '/var/lib/mesos', 84 | options => { 85 | quorum => 4 86 | } 87 | } 88 | ``` 89 | 90 | For slave you have to specify either `master` 91 | 92 | ```puppet 93 | class{'mesos::slave': 94 | master => '192.168.1.1' 95 | } 96 | ``` 97 | or `zookeeper` node(s) to connect: 98 | ```puppet 99 | class{'mesos::slave': 100 | zookeeper => ['192.168.1.1:2181', '192.168.1.2:2181', '192.168.1.3:2181'] 101 | } 102 | ``` 103 | - `conf_dir` default value is `/etc/mesos-master` (this directory will be purged by Puppet!) 104 | - for list of supported options see `mesos-master --help` 105 | - `env_var` - master's execution environment variables (see example under slave) 106 | - `acls` - hash of mesos acls, `{"permissive" => true, "register_frameworks" => [..]}` (default: `{}`) 107 | - `acls_file` - path to file to store acls (default: `/etc/mesos/acls`) 108 | - `credentials` - array of mesos credentials, `[{'principal' => 'some-principal', 'secret' => 'some-secret'}]` (default: `[]`) 109 | - `credentials_file` - path to file to store credentials (default: `/etc/mesos/master-credentials`) 110 | - `syslog_logger` - whether to log the Mesos service's output to syslog - note that if this parameter is false then it is up to your init system to log the process's output and if Mesos fails to start properly it will not produce log files of its own (default: true) 111 | 112 | #### listen address 113 | 114 | By default Mesos will bind to `0.0.0.0`. If you want to change the IP address Mesos is binding to, you can either provide a Puppet Fact: 115 | 116 | ```puppet 117 | class{'mesos::master': 118 | listen_address => $::ipaddress_eth0 119 | } 120 | ``` 121 | or directly use some IP address: 122 | 123 | ```puppet 124 | class{'mesos::master': 125 | listen_address => '192.168.1.1' 126 | } 127 | ``` 128 | 129 | Note that Facter 2 will contain incorrect IP address in `$::ipaddress` fact when Docker is installed. See [FACT-380](https://tickets.puppetlabs.com/browse/FACT-380) for more information. 130 | 131 | By default no IP address is set, which means that Mesos will use IP to which translates `hostname -f` (you can influence bind address simply in `/etc/hosts`). 132 | 133 | 134 | ### Slave 135 | 136 | - `enable` - install Mesos slave service (default: `true`) 137 | - `port` - slave's port for incoming connections (default: `5051`) 138 | - `master`- ip address of Mesos master (default: `localhost`) 139 | - `master_port` - Mesos master's port (default: `5050`) 140 | - `work_dir` - directory for storing task's temporary files (default: `/var/lib/mesos`) 141 | - `env_var` - slave's execution environment variables - a Hash, if you are using 142 | Java, you might need e.g.: 143 | - `principal` - mesos principal used for auththentication 144 | - `secret` - secret used for auththentication 145 | - `credentials_file` - path to file to store credentials (default: `/etc/mesos/slave-credentials`) 146 | - `syslog_logger` - whether to log the Mesos service's output to syslog (default: true) 147 | 148 | ```puppet 149 | class{'mesos::slave': 150 | master => '192.168.1.1', 151 | env_var => { 152 | 'JAVA_HOME' => '/usr/bin/java' 153 | } 154 | } 155 | ``` 156 | 157 | in a similar manner you can specify cgroups isolation: 158 | 159 | ```puppet 160 | class{'mesos::slave': 161 | zookeeper => 'zk://192.168.1.1:2181/mesos', 162 | isolation => 'cgroups/cpu,cgroups/mem', 163 | cgroups => { 164 | 'hierarchy' => '/sys/fs/cgroup', 165 | 'root' => 'mesos', 166 | } 167 | } 168 | ``` 169 | - `conf_dir` default value is `/etc/mesos-slave` (this directory will be purged by Puppet!) 170 | - for list of supported options see `mesos-slave --help` 171 | 172 | ## File based configuration 173 | 174 | As Mesos configuration flags changes with each version we don't provide directly a named parameter for each flag. `mesos::property` allows to create a parameter file. e.g. configure value in `/etc/mesos-slave/hostname`: 175 | 176 | ```puppet 177 | mesos::property { 'hostname': 178 | value => 'mesos.hostname.com', 179 | dir => '/etc/mesos-slave', 180 | notify => Service['mesos-slave'] 181 | } 182 | ``` 183 | 184 | Remove this file simply use the `ensure` parameter: 185 | 186 | ```puppet 187 | mesos::property { 'hostname': 188 | ensure => absent, 189 | dir => '/etc/mesos-slave', 190 | notify => Service['mesos-slave'] 191 | } 192 | ``` 193 | 194 | This is equivalent approach to 195 | 196 | ```puppet 197 | class{'mesos::slave': 198 | options => { 199 | 'hostname' => 'mesos.hostname.com' 200 | } 201 | } 202 | ``` 203 | which will create a file `/etc/mesos-slave/hostname` with content `mesos.hostname.com` (where `/etc/mesos-slave` is a slave's `$conf_dir`). 204 | 205 | Yet another option would be to pass this value via Hiera (see the section below). 206 | 207 | 208 | ### Boolean flags 209 | 210 | Current Mesos packages recognizes boolean flags like `--[no-]quiet` via files named as `/etc/mesos-slave/?quiet` for `--quiet` (true) and `/etc/mesos-slave/?no-quiet` for false value. 211 | 212 | ```puppet 213 | class{'mesos::slave': 214 | options => { 215 | 'quiet' => true 216 | } 217 | } 218 | ``` 219 | 220 | *since 0.4.1* 221 | 222 | ## Mesos Standalone 223 | 224 | Standalone mode (non-HA) is useful for testing, it does not require ZooKeeper URI, nor `quorum` variable. If you are running both master and slave on the same node, make sure you disable `single_role` parameter: 225 | 226 | ```puppet 227 | class{'::mesos': 228 | single_role => false, 229 | } 230 | ``` 231 | 232 | ## Hiera support 233 | 234 | All configuration could be handled by Hiera. Simple parameter are looked up by default in Puppet 3, however hashes won't get merged. In order to merge options from multiple Hiera files set `mesos::use_hiera: true`. 235 | 236 | Either specify one master 237 | 238 | ```yaml 239 | mesos::master : '192.168.1.1' 240 | ``` 241 | 242 | or [Zookeeper](http://zookeeper.apache.org/) could be use for a fault-tolerant setup (multiple instances of zookeeper are separated by comma): 243 | 244 | ```yaml 245 | mesos::zookeeper: 246 | - '192.168.1.1:2181' 247 | - '192.168.1.2:2181' 248 | - '192.168.1.3:2181' 249 | ``` 250 | 251 | Some parameters are shared between master and slave nodes: 252 | 253 | ```yaml 254 | mesos::master_port : 5050 255 | mesos::log_dir : '/var/log/mesos' 256 | mesos::conf_dir : '/etc/mesos' 257 | mesos::owner : 'mesos' 258 | mesos::group : 'mesos' 259 | ``` 260 | 261 | Other are master specific: 262 | 263 | ```yaml 264 | mesos::master::cluster : 'my_mesos_cluster' 265 | mesos::master::whitelist : '*' 266 | ``` 267 | 268 | or slave specific: 269 | 270 | ```yaml 271 | mesos:slave::env_var: 272 | JAVA_HOME: '/usr/bin/java' 273 | ``` 274 | 275 | Mesos service reads configuration either from ENV variables or from configuration files wich are stored in `/etc/mesos-slave` resp. `/etc/mesos-master`. Hash passed via `options` will be converted to config files. Most of the options is possible to configure this way: 276 | 277 | ```yaml 278 | mesos::master::options: 279 | webui_dir: '/usr/local/share/mesos/webui' 280 | quorum: '4' 281 | ``` 282 | 283 | you can also use facts from Puppet: 284 | 285 | ``` 286 | mesos::master::options: 287 | hostname: "%{::fqdn}" 288 | ``` 289 | 290 | 291 | cgroups with Hiera: 292 | 293 | ```yaml 294 | mesos::slave::isolation: 'cgroups/cpu,cgroups/mem' 295 | mesos::slave::cgroups: 296 | hierarchy: '/sys/fs/cgroup' 297 | ``` 298 | 299 | Limit resources used by Mesos slave: 300 | 301 | ```yaml 302 | mesos::slave::resources: 303 | cpus: '10' 304 | ``` 305 | 306 | ### Mesos CLI 307 | 308 | Mesos command line interface is written in Python (currently supports 2.6 and 2.7). The CLI provides serveral useful commands like `mesos ps` ,`mesos ls`. For complete list see [CLI documentation](https://pypi.python.org/pypi/mesos.cli). 309 | 310 | By default CLI is not installed with master nor slave, you should enable this manually by including 311 | 312 | ```puppet 313 | class{'mesos::cli': 314 | debug => false, 315 | response_timeout => 5, 316 | } 317 | ``` 318 | 319 | on any machine that can connect to mesos-master and mesos-slaves. 320 | 321 | 322 | ### Python installation 323 | 324 | Python is required for Mesos Web UI and for CLI as well. Installing Python with Mesos should be responsibility of binary packages (Mesos could be build without UI), therefore this behaviour is not enabled by default. 325 | 326 | You can enable this feature with following: 327 | 328 | ```puppet 329 | class{'mesos': 330 | manage_python => true 331 | } 332 | ``` 333 | 334 | or change Python package name, to match your needs: 335 | 336 | ```puppet 337 | class{'mesos': 338 | manage_python => true, 339 | python_package => 'python-dev' 340 | } 341 | ``` 342 | 343 | 344 | ### Software repository 345 | 346 | Software repositories could be enabled by defining a source: 347 | 348 | ```yaml 349 | mesos::repo: 'mesosphere' 350 | mesos::manage_repo: true 351 | ``` 352 | 353 | or in Puppet code: 354 | 355 | ```puppet 356 | class{'mesos': 357 | repo => 'mesosphere', 358 | manage_repo => true, 359 | } 360 | ``` 361 | 362 | by default this feature is enabled and right we support [mesosphere.io](http://mesosphere.io) repositories for: 363 | 364 | * Debian/Ubuntu 365 | * RedHat/CentOS 366 | 367 | Feel free to send PR for other distributions/package sources. 368 | 369 | #### Custom APT repository 370 | 371 | Default APT repository is configured following way: 372 | 373 | ```puppet 374 | class{'mesos': 375 | repo => { 376 | location => "http://repos.mesosphere.io/${osfamily}", 377 | release => $::lsbdistcodename, 378 | repos => 'main', 379 | key => { 380 | 'id' => '81026D0004C44CF7EF55ADF8DF7D54CBE56151BF', 381 | 'server' => 'keyserver.ubuntu.com', 382 | }, 383 | include => { 384 | 'src' => false 385 | }, 386 | } 387 | } 388 | ``` 389 | where the `repo` hash is passsed to `apt::source` (from offcial [apt module](https://github.com/puppetlabs/puppetlabs-apt#add-an-apt-source-to-etcaptsourceslistd)). 390 | 391 | In case you want to override some settings, only the modified values needs to be passed e.g.: 392 | 393 | ```puppet 394 | class{'mesos': 395 | repo => { 396 | location => "http://myrepo.example.com", 397 | key => { 398 | 'id' => '{mykey}', 399 | 'server' => 'keyserver.ubuntu.com', 400 | }, 401 | } 402 | } 403 | ``` 404 | 405 | *since 0.8.2* 406 | 407 | ### Overriding service providers 408 | 409 | Some Mesos packages does not respect conventions on given OS for starting services. For both `mesos::master` and `mesos::slave` you can specify mechanism which will be used for starting services. 410 | 411 | ```puppet 412 | class{'mesos::master': 413 | service_provider => 'upstart' 414 | } 415 | ``` 416 | 417 | If you want to create the service resource yourself, set `service_provider` to `none`. 418 | 419 | Some reasonable values are: 420 | 421 | * `init` 422 | * `upstart` - e.g. Ubuntu 423 | * `systemd` 424 | * `runit` 425 | * `none` - service won't be installed 426 | * `undef` - (default) detected provider by Puppet 427 | 428 | A custom `systemd` configuration example: 429 | 430 | ```puppet 431 | class{'mesos::master': 432 | service_provider => 'systemd', 433 | manage_service_file => true, 434 | systemd_wants => 'network.target zookeeper.service', 435 | systemd_after => 'network.target zookeeper.service', 436 | } 437 | ``` 438 | 439 | ### Packages 440 | 441 | You can build package by yourself and upload package to your software repository. Or use packages from mesosphere.io: 442 | 443 | * Debian/Ubuntu 444 | * [mesosphere packages](http://mesosphere.io/downloads/) 445 | * RedHat/CentOS 446 | * [mesosphere packages](http://mesosphere.io/downloads/) 447 | 448 | ## Requirements 449 | 450 | * Puppet > 3.0 and < 5.0 451 | 452 | ## Dependencies 453 | 454 | * [stdlib](https://forge.puppetlabs.com/puppetlabs/stdlib) version `>= 4.2.0` - we need function `is_bool` 455 | * [apt](https://github.com/puppetlabs/puppetlabs-apt) version `>= 2.1.0` is required for Debian servers (since puppet-mesos 0.6) 456 | 457 | ## Installation 458 | 459 | Preferred installation is via [puppet-librarian](https://github.com/rodjek/librarian-puppet) just add to `Puppetfile`: 460 | 461 | ```ruby 462 | mod 'deric/mesos', '>= 0.6.0' 463 | ``` 464 | 465 | for latest version from git: 466 | ```ruby 467 | mod 'deric/mesos', :git => 'git://github.com/deric/puppet-mesos.git' 468 | ``` 469 | 470 | ## Acceptance testing 471 | 472 | Fastest way is to run tests on prepared Docker images: 473 | ``` 474 | BEAKER_set=debian9-5.5 bundle exec rake acceptance 475 | ``` 476 | For examining system state set Beaker's ENV variable `BEAKER_destroy=no`: 477 | 478 | ``` 479 | BEAKER_destroy=no BEAKER_set=debian9-6.3 bundle exec rake acceptance 480 | ``` 481 | and after finishing tests connect to container: 482 | 483 | ## Links 484 | 485 | For more information see [Mesos project](http://mesos.apache.org/) 486 | 487 | ## License 488 | 489 | Apache License 2.0 490 | 491 | 492 | ## Contributors 493 | 494 | Alphabetical list of contributors (not necessarily up-to-date), generated by command `git log --format='%aN' | sort -u | sed -e 's/^/\- /'`: 495 | 496 | - Andrew Teixeira 497 | - Chris Rebert 498 | - Felix Bechstein 499 | - Ian Burrell 500 | - Jamie Hewland 501 | - jfarrell 502 | - Jing Dong 503 | - Konrad Scherer 504 | - krall 505 | - Kyle Anderson 506 | - Maksym Melnychok 507 | - Oriol Fitó 508 | - Paul Otto 509 | - Rhommel Lamas 510 | - Sam Stoelinga 511 | - Sean McLaughlin 512 | - Sophie Haskins 513 | - Tadas Vilkeliskis 514 | - taik0 515 | - Tomas Barton 516 | - Tom Stockton 517 | - William Leese 518 | -------------------------------------------------------------------------------- /spec/classes/slave_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | 3 | describe 'mesos::slave', type: :class do 4 | let(:owner) { 'mesos' } 5 | let(:group) { 'mesos' } 6 | let(:conf) { '/etc/mesos-slave' } 7 | let(:slave_file) { '/etc/default/mesos-slave' } 8 | 9 | let(:params) do 10 | { 11 | conf_dir: conf, 12 | owner: owner, 13 | group: group 14 | } 15 | end 16 | 17 | let(:facts) do 18 | { 19 | mesos_version: '1.2.0', 20 | # still old fact is needed due to this 21 | # https://github.com/puppetlabs/puppetlabs-apt/blob/master/manifests/params.pp#L3 22 | osfamily: 'Debian', 23 | os: { 24 | family: 'Debian', 25 | name: 'Debian', 26 | distro: { codename: 'stretch' }, 27 | release: { major: '9', minor: '1', full: '9.1' } 28 | }, 29 | path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 30 | puppetversion: Puppet.version 31 | } 32 | end 33 | 34 | before(:each) do 35 | puppet_debug_override 36 | end 37 | 38 | it { is_expected.to contain_package('mesos') } 39 | it { 40 | is_expected.to contain_service('mesos-slave').with( 41 | ensure: 'running', 42 | enable: true, 43 | ) 44 | } 45 | 46 | it { 47 | is_expected.to contain_file(slave_file).with( 48 | 'ensure' => 'present', 49 | 'owner' => owner, 50 | 'group' => group, 51 | 'mode' => '0644', 52 | ) 53 | } 54 | 55 | it 'does not set IP address by default' do 56 | is_expected.not_to contain_file( 57 | slave_file, 58 | ).with_content(%r{^export MESOS_IP=}) 59 | end 60 | 61 | context 'with ip address set' do 62 | let(:params) do 63 | { 64 | listen_address: '192.168.1.1' 65 | } 66 | end 67 | 68 | it 'has ip address from param' do 69 | is_expected.to contain_file( 70 | slave_file, 71 | ).with_content(%r{^export MESOS_IP="192.168.1.1"$}) 72 | end 73 | end 74 | 75 | it 'has default port eq to 5051' do 76 | is_expected.to contain_file( 77 | slave_file, 78 | ).with_content(%r{^export MESOS_PORT=5051$}) 79 | end 80 | 81 | it 'checkpoint should be false' do 82 | is_expected.not_to contain_file( 83 | "#{conf}/?checkpoint", 84 | ).with( 85 | 'ensure' => 'present', 86 | ) 87 | end 88 | 89 | it 'has workdir in /var/lib/mesos' do 90 | is_expected.to contain_file( 91 | "#{conf}/work_dir", 92 | ).with_content(/^\/var\/lib\/mesos$/) 93 | end 94 | 95 | context 'one master node' do 96 | let(:params) do 97 | { 98 | master: '192.168.1.100' 99 | } 100 | end 101 | 102 | it { 103 | is_expected.to contain_file( 104 | slave_file, 105 | ).with_content(%r{^export MESOS_MASTER="192.168.1.100:5050"}) 106 | } 107 | it { 108 | is_expected.to contain_file( 109 | '/etc/mesos/zk', 110 | ).with(ensure: 'absent') 111 | } 112 | end 113 | 114 | context 'zookeeper should be preferred before single master' do 115 | let(:params) do 116 | { 117 | master: '172.16.0.1', 118 | zookeeper: ['192.168.1.100:2181'] 119 | } 120 | end 121 | 122 | it { 123 | is_expected.not_to contain_file( 124 | slave_file, 125 | ).with_content(%r{^export MESOS_MASTER="172.16.0.1"}) 126 | } 127 | # this would work only if we set mesos::zookeeper through hiera 128 | # it { should contain_file( 129 | # '/etc/mesos/zk' 130 | # ).with_content(/^zk:\/\/192.168.1.100:2181\/mesos/) 131 | # } 132 | end 133 | 134 | context 'disabling service' do 135 | let(:params) do 136 | { 137 | enable: false 138 | } 139 | end 140 | 141 | it { 142 | is_expected.to contain_service('mesos-slave').with( 143 | enable: false, 144 | ) 145 | } 146 | end 147 | 148 | context 'changing workdir' do 149 | let(:params) do 150 | { 151 | work_dir: '/home/mesos' 152 | } 153 | end 154 | 155 | it { 156 | is_expected.to contain_file( 157 | "#{conf}/work_dir", 158 | ).with_content(/^\/home\/mesos$/) 159 | } 160 | end 161 | 162 | context 'enabling checkpoint (enabled by default anyway)' do 163 | let(:params) do 164 | { 165 | options: { 166 | 'checkpoint' => true 167 | } 168 | } 169 | end 170 | 171 | it { 172 | is_expected.to contain_file( 173 | "#{conf}/?checkpoint", 174 | ).with( 175 | 'ensure' => 'present', 176 | ) 177 | } 178 | end 179 | 180 | context 'disabling checkpoint' do 181 | let(:params) do 182 | { 183 | options: { 184 | 'checkpoint' => false 185 | } 186 | } 187 | end 188 | 189 | it { 190 | is_expected.to contain_file( 191 | "#{conf}/?no-checkpoint", 192 | ).with( 193 | 'ensure' => 'present', 194 | ) 195 | } 196 | end 197 | 198 | context 'setting environment variables' do 199 | let(:params) do 200 | { 201 | env_var: { 202 | 'JAVA_HOME' => '/usr/bin/java', 203 | 'MESOS_HOME' => '/var/lib/mesos' 204 | } 205 | } 206 | end 207 | 208 | it { 209 | is_expected.to contain_file( 210 | slave_file, 211 | ).with_content(/export JAVA_HOME="\/usr\/bin\/java"/) 212 | } 213 | 214 | it { 215 | is_expected.to contain_file( 216 | slave_file, 217 | ).with_content(/export MESOS_HOME="\/var\/lib\/mesos"/) 218 | } 219 | end 220 | 221 | it 'does not set isolation by default (value depends on mesos version)' do 222 | is_expected.not_to contain_file( 223 | "#{conf}/isolation", 224 | ).with( 225 | 'ensure' => 'present', 226 | ) 227 | end 228 | 229 | context 'should set isolation to cgroups' do 230 | let(:params) do 231 | { 232 | isolation: 'cgroups/cpu,cgroups/mem' 233 | } 234 | end 235 | 236 | it { 237 | is_expected.to contain_file( 238 | "#{conf}/isolation", 239 | ).with( 240 | 'ensure' => 'present', 241 | ).with_content(/^cgroups\/cpu,cgroups\/mem$/) 242 | } 243 | end 244 | 245 | it 'does not contain cgroups settings' do 246 | is_expected.not_to contain_file( 247 | slave_file, 248 | ).with_content(%r{CGROUPS}) 249 | end 250 | 251 | context 'setting isolation mechanism' do 252 | let(:params) do 253 | { 254 | isolation: 'cgroups/cpu,cgroups/mem', 255 | cgroups: { 256 | 'hierarchy' => '/sys/fs/cgroup', 257 | 'root' => 'mesos' 258 | }, 259 | owner: owner, 260 | group: group 261 | } 262 | end 263 | 264 | it { 265 | is_expected.to contain_file( 266 | "#{conf}/cgroups_root", 267 | ).with_content(%r{^mesos$}) 268 | } 269 | 270 | it { 271 | is_expected.to contain_file( 272 | "#{conf}/cgroups_hierarchy", 273 | ).with_content(/^\/sys\/fs\/cgroup$/) 274 | } 275 | 276 | it { 277 | is_expected.to contain_file( 278 | "#{conf}/isolation", 279 | ).with( 280 | 'ensure' => 'present', 281 | ).with_content(/^cgroups\/cpu,cgroups\/mem$/) 282 | } 283 | 284 | it { 285 | is_expected.to contain_mesos__property('slave_hierarchy').with( 286 | 'owner' => owner, 287 | 'group' => group, 288 | 'dir' => conf, 289 | 'value' => '/sys/fs/cgroup', 290 | ) 291 | } 292 | end 293 | 294 | context 'changing slave config file location' do 295 | let(:slave_file) { '/etc/mesos/slave' } 296 | let(:params) do 297 | { 298 | conf_file: slave_file 299 | } 300 | end 301 | 302 | it { 303 | is_expected.to contain_file(slave_file).with( 304 | 'ensure' => 'present', 305 | 'mode' => '0644', 306 | ) 307 | } 308 | end 309 | 310 | context 'resources specification' do 311 | let(:resources_dir) { '/etc/mesos-slave/resources' } 312 | 313 | let(:params) do 314 | { 315 | resources: { 316 | 'cpu' => '4', 317 | 'mem' => '2048' 318 | } 319 | } 320 | end 321 | 322 | it { 323 | is_expected.to contain_file(resources_dir).with( 324 | 'ensure' => 'directory', 325 | ) 326 | } 327 | 328 | it { 329 | is_expected.to contain_file( 330 | "#{resources_dir}/cpu", 331 | ).with_content(%r{^4$}) 332 | } 333 | 334 | it { 335 | is_expected.to contain_file( 336 | "#{resources_dir}/mem", 337 | ).with_content(%r{^2048$}) 338 | } 339 | end 340 | 341 | context 'custom listen_address value' do 342 | let(:params) do 343 | { 344 | conf_dir: conf, 345 | owner: owner, 346 | group: group, 347 | listen_address: '192.168.1.2' 348 | } 349 | end 350 | 351 | # fact is not evaluated in test with newer puppet (or rspec) 352 | it 'has ip address from system fact' do 353 | is_expected.to contain_file( 354 | slave_file, 355 | ).with_content(%r{IP="192\.168\.1\.2"$}) 356 | end 357 | end 358 | 359 | context 'set isolation via options' do 360 | let(:params) do 361 | { 362 | conf_dir: conf, 363 | options: { 'isolation' => 'cgroups/cpu,cgroups/mem' } 364 | } 365 | end 366 | 367 | it 'contains isolation file in slave directory' do 368 | is_expected.to contain_file( 369 | "#{conf}/isolation", 370 | ).with_content(/^cgroups\/cpu,cgroups\/mem$/) 371 | end 372 | end 373 | 374 | context 'allow changing config directory' do 375 | let(:my_conf_dir) { '/var/mesos-slave' } 376 | let(:params) do 377 | { 378 | conf_dir: my_conf_dir, 379 | options: { 'isolation' => 'cgroups/cpu,cgroups/mem' } 380 | } 381 | end 382 | 383 | it 'contains isolation file in slave directory' do 384 | is_expected.to contain_file( 385 | "#{my_conf_dir}/isolation", 386 | ).with_content(/^cgroups\/cpu,cgroups\/mem$/) 387 | end 388 | end 389 | 390 | context 'work_dir' do 391 | let(:work_dir) { '/tmp/mesos' } 392 | let(:params) do 393 | { 394 | conf_dir: conf, 395 | work_dir: work_dir, 396 | owner: owner, 397 | group: group 398 | } 399 | end 400 | 401 | it do 402 | is_expected.to contain_file(work_dir).with( 403 | 'ensure' => 'directory', 404 | 'owner' => owner, 405 | 'group' => group, 406 | ) 407 | end 408 | 409 | it do 410 | is_expected.to contain_mesos__property('slave_work_dir').with( 411 | 'owner' => owner, 412 | 'group' => group, 413 | 'dir' => conf, 414 | 'value' => work_dir, 415 | ) 416 | end 417 | 418 | it do 419 | is_expected.to contain_file("#{conf}/work_dir") 420 | .with_content(work_dir + "\n") 421 | .that_requires("File[#{conf}]") 422 | end 423 | end 424 | 425 | context 'common slave config' do 426 | let(:params) do 427 | { 428 | zookeeper: 'zk://192.168.1.1:2181,192.168.1.2:2181,192.168.1.3:2181/mesos', 429 | listen_address: '192.168.1.1', 430 | attributes: { 431 | 'env' => 'production' 432 | }, 433 | resources: { 434 | 'ports' => '[10000-65535]' 435 | } 436 | } 437 | end 438 | 439 | it { is_expected.to compile.with_all_deps } 440 | it { is_expected.to contain_package('mesos') } 441 | it { 442 | is_expected.to contain_service('mesos-slave').with( 443 | ensure: 'running', 444 | enable: true, 445 | ) 446 | } 447 | 448 | it { 449 | is_expected.to contain_mesos__property('resources_ports').with( 450 | 'dir' => '/etc/mesos-slave/resources', 451 | 'file' => 'ports', 452 | 'value' => '[10000-65535]', 453 | ) 454 | } 455 | 456 | it { 457 | is_expected.to contain_mesos__property('attributes_env').with( 458 | 'dir' => '/etc/mesos-slave/attributes', 459 | 'file' => 'env', 460 | 'value' => 'production', 461 | ) 462 | } 463 | end 464 | 465 | context 'support boolean flags' do 466 | let(:my_conf_dir) { '/var/mesos-slave' } 467 | let(:params) do 468 | { 469 | conf_dir: my_conf_dir, 470 | options: { 'strict' => false } 471 | } 472 | end 473 | 474 | it 'has no-strict file in config dir' do 475 | is_expected.to contain_file( 476 | "#{my_conf_dir}/?no-strict", 477 | ).with( 478 | 'ensure' => 'present', 479 | ) 480 | end 481 | end 482 | 483 | context 'nofify service after removing a key' do 484 | let(:my_conf_dir) { '/tmp/mesos-conf' } 485 | let(:params) do 486 | { 487 | conf_dir: my_conf_dir 488 | } 489 | end 490 | 491 | before(:each) do 492 | system("mkdir -p #{my_conf_dir} && touch #{my_conf_dir}/foo") 493 | end 494 | 495 | after(:each) do 496 | system("rm -rf #{my_conf_dir}") 497 | end 498 | 499 | it { is_expected.to contain_service('mesos-slave') } 500 | it { is_expected.to contain_file(my_conf_dir.to_s).that_notifies('Service[mesos-slave]') } 501 | end 502 | 503 | context 'nofify service after removing a key' do 504 | let(:my_conf_dir) { '/tmp/mesos-conf' } 505 | let(:params) do 506 | { 507 | conf_dir: my_conf_dir 508 | } 509 | end 510 | 511 | before(:each) do 512 | system("mkdir -p #{my_conf_dir}/resources && echo 2 > #{my_conf_dir}/resources/cpus") 513 | end 514 | 515 | after(:each) do 516 | system("rm -rf #{my_conf_dir}") 517 | end 518 | 519 | it { is_expected.to contain_service('mesos-slave') } 520 | it { is_expected.to contain_file(my_conf_dir.to_s).that_notifies('Service[mesos-slave]') } 521 | end 522 | 523 | context 'credentials' do 524 | context 'default w/o principal/secret' do 525 | let(:params) do 526 | { 527 | conf_dir: conf, 528 | owner: owner, 529 | group: group 530 | } 531 | end 532 | 533 | it 'has no credentials property' do 534 | is_expected.not_to contain_mesos__property( 535 | 'slave_credential', 536 | ) 537 | end 538 | 539 | it 'has not credentials file' do 540 | is_expected.to contain_file( 541 | '/etc/mesos/slave-credentials', 542 | ) 543 | .with( 544 | 'ensure' => 'absent', 545 | ) 546 | end 547 | end 548 | 549 | context 'w/ principal/secret' do 550 | let(:params) do 551 | { 552 | conf_dir: conf, 553 | owner: owner, 554 | group: group, 555 | principal: 'some-mesos-principal', 556 | secret: 'a-very-secret' 557 | } 558 | end 559 | 560 | it 'has credentials property' do 561 | is_expected.to contain_mesos__property( 562 | 'slave_credential', 563 | ).with( 564 | 'value' => '/etc/mesos/slave-credentials', 565 | ) 566 | end 567 | 568 | it 'has credentials file' do 569 | is_expected.to contain_file( 570 | '/etc/mesos/slave-credentials', 571 | ).with( 572 | 'ensure' => 'file', 573 | 'content' => '{"principal": "some-mesos-principal", "secret": "a-very-secret"}', 574 | 'owner' => owner, 575 | 'group' => group, 576 | 'mode' => '0400', 577 | ) 578 | end 579 | end 580 | 581 | context 'syslog logger' do 582 | describe 'when syslog_logger is true' do 583 | let(:params) do 584 | { 585 | conf_dir: conf, 586 | owner: owner, 587 | group: group, 588 | syslog_logger: true 589 | } 590 | end 591 | 592 | it do 593 | is_expected.to contain_mesos__property('slave_logger') 594 | .with( 595 | ensure: 'absent', 596 | file: 'logger', 597 | value: false, 598 | dir: conf, 599 | owner: owner, 600 | group: group, 601 | ) 602 | 603 | is_expected.to contain_file("#{conf}/?no-logger").with_ensure('absent') 604 | end 605 | end 606 | 607 | describe 'when syslog_logger is false' do 608 | let(:params) do 609 | { 610 | conf_dir: conf, 611 | owner: owner, 612 | group: group, 613 | syslog_logger: false 614 | } 615 | end 616 | 617 | it do 618 | is_expected.to contain_mesos__property('slave_logger') 619 | .with( 620 | ensure: 'present', 621 | file: 'logger', 622 | value: false, 623 | dir: conf, 624 | owner: owner, 625 | group: group, 626 | ) 627 | 628 | is_expected.to contain_file("#{conf}/?no-logger").with_ensure('present') 629 | end 630 | end 631 | end 632 | end 633 | 634 | context 'single role' do 635 | it { 636 | is_expected.to contain_service('mesos-slave').with( 637 | ensure: 'running', 638 | enable: true, 639 | ) 640 | } 641 | 642 | it { 643 | is_expected.to contain_service('mesos-master').with( 644 | enable: false, 645 | ) 646 | } 647 | 648 | it { 649 | is_expected.to contain_mesos__service('master').with(enable: false) 650 | is_expected.to contain_mesos__service('slave').with(enable: true) 651 | } 652 | 653 | context 'disable single role' do 654 | let(:params) do 655 | { 656 | single_role: false 657 | } 658 | end 659 | 660 | it { 661 | is_expected.not_to contain_service('mesos-master').with( 662 | enable: false, 663 | ) 664 | } 665 | end 666 | end 667 | 668 | context 'systemd support' do 669 | context 'diable systemd support where systemd is not present' do 670 | let(:facts) do 671 | { 672 | mesos_version: '1.2.0', 673 | # still old fact is needed due to this 674 | # https://github.com/puppetlabs/puppetlabs-apt/blob/master/manifests/params.pp#L3 675 | osfamily: 'Debian', 676 | os: { 677 | family: 'Debian', 678 | name: 'Debian', 679 | distro: { codename: 'precise' }, 680 | release: { major: '12', minor: '04', full: '12.04' } 681 | }, 682 | path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 683 | puppetversion: Puppet.version 684 | } 685 | end 686 | 687 | it do 688 | is_expected.to contain_mesos__property('slave_systemd_enable_support') 689 | .with( 690 | ensure: 'present', 691 | file: 'systemd_enable_support', 692 | value: false, 693 | dir: conf, 694 | owner: owner, 695 | group: group, 696 | ) 697 | 698 | is_expected.to contain_file("#{conf}/?no-systemd_enable_support").with_ensure('present') 699 | end 700 | end 701 | 702 | context 'enable systemd support' do 703 | let(:facts) do 704 | { 705 | mesos_version: '1.2.0', 706 | osfamily: 'Debian', 707 | os: { 708 | family: 'Debian', 709 | name: 'Debian', 710 | distro: { codename: 'jessie' }, 711 | release: { major: '8', minor: '9', full: '8.9' } 712 | }, 713 | path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 714 | puppetversion: Puppet.version 715 | } 716 | end 717 | 718 | it do 719 | is_expected.not_to contain_mesos__property('slave_systemd_enable_support') 720 | .with( 721 | ensure: 'present', 722 | file: 'systemd_enable_support', 723 | value: true, 724 | dir: conf, 725 | owner: owner, 726 | group: group, 727 | ) 728 | 729 | is_expected.not_to contain_file("#{conf}/?systemd_enable_support").with_ensure('present') 730 | is_expected.not_to contain_file("#{conf}/?no-systemd_enable_support").with_ensure('present') 731 | end 732 | end 733 | 734 | context 'do not use systemd flag' do 735 | let(:facts) do 736 | { 737 | mesos_version: '1.2.0', 738 | osfamily: 'Debian', 739 | os: { 740 | family: 'Debian', 741 | name: 'Debian', 742 | distro: { codename: 'jessie' }, 743 | release: { major: '8', minor: '9', full: '8.9' } 744 | }, 745 | path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 746 | puppetversion: Puppet.version 747 | } 748 | end 749 | 750 | it do 751 | is_expected.not_to contain_mesos__property('slave_systemd_enable_support') 752 | .with( 753 | ensure: 'present', 754 | file: 'systemd_enable_support', 755 | value: true, 756 | dir: conf, 757 | owner: owner, 758 | group: group, 759 | ) 760 | 761 | is_expected.not_to contain_file("#{conf}/?systemd_enable_support").with_ensure('present') 762 | is_expected.not_to contain_file("#{conf}/?no-systemd_enable_support").with_ensure('present') 763 | end 764 | end 765 | 766 | context 'do not use systemd_enable_support flag for earlier versions than 0.28' do 767 | let(:facts) do 768 | { 769 | mesos_version: '0.27.0', 770 | osfamily: 'Debian', 771 | os: { 772 | family: 'Debian', 773 | name: 'Debian', 774 | distro: { codename: 'jessie' }, 775 | release: { major: '8', minor: '9', full: '8.9' } 776 | }, 777 | path: '/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin', 778 | puppetversion: Puppet.version 779 | } 780 | end 781 | 782 | it do 783 | is_expected.not_to contain_mesos__property('slave_systemd_enable_support') 784 | .with( 785 | ensure: 'present', 786 | file: 'systemd_enable_support', 787 | value: false, 788 | dir: conf, 789 | owner: owner, 790 | group: group, 791 | ) 792 | 793 | is_expected.not_to contain_file("#{conf}/?no-systemd_enable_support").with_ensure('present') 794 | is_expected.not_to contain_file("#{conf}/systemd_enable_support").with_ensure('present') 795 | end 796 | end 797 | end 798 | 799 | context 'auto-detect service provider' do 800 | let(:facts) do 801 | { 802 | mesos_version: '1.2.0', 803 | osfamily: 'RedHat', 804 | os: { 805 | family: 'RedHat', 806 | name: 'CentOS', 807 | release: { major: '6', minor: '7', full: '6.7' } 808 | } 809 | } 810 | end 811 | 812 | it { 813 | is_expected.to contain_service('mesos-slave').with( 814 | ensure: 'running', 815 | provider: 'upstart', 816 | enable: true, 817 | ) 818 | } 819 | 820 | context 'on CentOS 7' do 821 | let(:facts) do 822 | { 823 | mesos_version: '1.2.0', 824 | osfamily: 'RedHat', 825 | os: { 826 | family: 'RedHat', 827 | name: 'CentOS', 828 | release: { major: '7', minor: '7', full: '7.7' } 829 | } 830 | } 831 | end 832 | 833 | it { 834 | is_expected.to contain_service('mesos-slave').with( 835 | ensure: 'running', 836 | provider: 'systemd', 837 | enable: true, 838 | ) 839 | } 840 | end 841 | end 842 | 843 | context 'custom systemd configuration' do 844 | let(:params) do 845 | { 846 | service_provider: 'systemd', 847 | manage_service_file: true, 848 | systemd_after: 'network-online.target openvpn-client@.service', 849 | systemd_wants: 'network-online.target openvpn-client@.service' 850 | } 851 | end 852 | 853 | it do 854 | is_expected.to contain_service('mesos-slave').with( 855 | ensure: 'running', 856 | enable: true, 857 | ) 858 | end 859 | 860 | it do 861 | is_expected.to contain_mesos__service('slave').with(enable: true) 862 | end 863 | 864 | it do 865 | is_expected.to contain_file( 866 | '/etc/systemd/system/mesos-slave.service', 867 | ).with( 868 | 'ensure' => 'present', 869 | ) 870 | end 871 | 872 | it do 873 | is_expected.to contain_file( 874 | '/etc/systemd/system/mesos-slave.service', 875 | ).with_content(%r{Wants=network-online.target openvpn-client@.service}) 876 | end 877 | 878 | it do 879 | is_expected.to contain_file( 880 | '/etc/systemd/system/mesos-slave.service', 881 | ).with_content(%r{After=network-online.target openvpn-client@.service}) 882 | end 883 | end 884 | end 885 | --------------------------------------------------------------------------------