├── spec ├── spec_helper.rb └── classes │ └── init_spec.rb ├── Gemfile ├── manifests ├── params.pp ├── master │ ├── service.pp │ └── config.pp ├── common │ ├── config.pp │ └── install.pp ├── slave │ ├── service.pp │ └── config.pp ├── access.pp ├── master.pp └── slave.pp ├── tests └── master.pp ├── Rakefile ├── templates ├── master_conf.erb ├── slave_conf.erb └── clusterrunner.conf.erb ├── metadata.json ├── files ├── clusterrunner_master └── clusterrunner_slave ├── CONTRIBUTING.md ├── README.md └── LICENSE /spec/spec_helper.rb: -------------------------------------------------------------------------------- 1 | require 'puppetlabs_spec_helper/module_spec_helper' 2 | -------------------------------------------------------------------------------- /spec/classes/init_spec.rb: -------------------------------------------------------------------------------- 1 | require 'spec_helper' 2 | describe 'clusterrunner' do 3 | 4 | context 'with defaults for all parameters' do 5 | it { should contain_class('clusterrunner') } 6 | end 7 | end 8 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | 3 | puppetversion = ENV.key?('PUPPET_VERSION') ? "= #{ENV['PUPPET_VERSION']}" : ['>= 3.3'] 4 | gem 'puppet', puppetversion 5 | gem 'puppetlabs_spec_helper', '>= 0.1.0' 6 | gem 'puppet-lint', '>= 0.3.2' 7 | gem 'facter', '>= 1.7.0' 8 | -------------------------------------------------------------------------------- /manifests/params.pp: -------------------------------------------------------------------------------- 1 | ## 2 | # Parameters used throughout the clusterrunner module. 3 | ## 4 | class clusterrunner::params { 5 | 6 | $home = '/usr/local/clusterrunner' 7 | $user = 'clusterrunner' 8 | $group = 'clusterrunner' 9 | $master_port = '43000' 10 | $slave_port = '43001' 11 | $package_url = 'https://cloud.box.com/shared/static/2pl4pi6ykvrbb9d06t4m.tgz' 12 | 13 | } 14 | 15 | -------------------------------------------------------------------------------- /tests/master.pp: -------------------------------------------------------------------------------- 1 | # The baseline for module testing used by Puppet Labs is that each manifest 2 | # should have a corresponding test manifest that declares that class or defined 3 | # type. 4 | # 5 | # Tests are then run by using puppet apply --noop (to check for compilation 6 | # errors and view a log of events) or by fully applying the test in a virtual 7 | # environment (to compare the resulting system state to the desired state). 8 | # 9 | # Learn more about module testing here: 10 | # http://docs.puppetlabs.com/guides/tests_smoke.html 11 | # 12 | include clusterrunner::master 13 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'rubygems' 2 | require 'puppetlabs_spec_helper/rake_tasks' 3 | require 'puppet-lint/tasks/puppet-lint' 4 | PuppetLint.configuration.send('disable_80chars') 5 | PuppetLint.configuration.ignore_paths = ["spec/**/*.pp", "pkg/**/*.pp"] 6 | 7 | desc "Validate manifests, templates, and ruby files" 8 | task :validate do 9 | Dir['manifests/**/*.pp'].each do |manifest| 10 | sh "puppet parser validate --noop #{manifest}" 11 | end 12 | Dir['spec/**/*.rb','lib/**/*.rb'].each do |ruby_file| 13 | sh "ruby -c #{ruby_file}" unless ruby_file =~ /spec\/fixtures/ 14 | end 15 | Dir['templates/**/*.erb'].each do |template| 16 | sh "erb -P -x -T '-' #{template} | ruby -c" 17 | end 18 | end 19 | -------------------------------------------------------------------------------- /templates/master_conf.erb: -------------------------------------------------------------------------------- 1 | ## Type: string 2 | ## ServiceRestart: clusterrunner_master 3 | # 4 | # ClusterRunner master hostname 5 | # 6 | MASTER_HOST="<%= @master %>" 7 | 8 | ## Type: integer(0:65535) 9 | ## ServiceRestart: clusterrunner_master 10 | # 11 | # ClusterRunner master port 12 | # 13 | MASTER_PORT="<%= @master_port %>" 14 | 15 | ## Type: string 16 | ## ServiceRestart: clusterrunner_master 17 | # 18 | # The name of the ClusterRunner service user 19 | # 20 | CLUSTERRUNNER_USER="<%= @user %>" 21 | 22 | ## Type: string 23 | ## ServiceRestart: clusterrunner_master 24 | # 25 | # The home directory of the ClusterRunner service user 26 | # (where ClusterRunner is installed) 27 | # 28 | CLUSTERRUNNER_HOME="<%= @home %>" -------------------------------------------------------------------------------- /manifests/master/service.pp: -------------------------------------------------------------------------------- 1 | # == Class: clusterrunner::master::service 2 | # 3 | # This class is responsible for starting the ClusterRunner master service 4 | # 5 | class clusterrunner::master::service( 6 | $user, 7 | $group, 8 | $ensure 9 | ) { 10 | 11 | $log_file = '/var/log/clusterrunner/master.log' 12 | file { $log_file: 13 | ensure => file, 14 | owner => $user, 15 | group => $group, 16 | mode => '0755', 17 | } 18 | 19 | $service_script = '/etc/init.d/clusterrunner_master' 20 | file { $service_script: 21 | ensure => file, 22 | owner => 'root', 23 | group => 'root', 24 | mode => '0755', 25 | source => 'puppet:///modules/clusterrunner/clusterrunner_master', 26 | } 27 | 28 | service { 'clusterrunner_master': 29 | ensure => $ensure, 30 | require => File[$service_script, $log_file], 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /manifests/common/config.pp: -------------------------------------------------------------------------------- 1 | # == Class: clusterrunner::config 2 | # 3 | # Sets up ClusterRunner configuration file (clusterrunner.conf) and common 4 | # directories. 5 | # 6 | class clusterrunner::common::config ( 7 | $home, 8 | $user, 9 | $group, 10 | $secret_key, 11 | ) { 12 | 13 | validate_absolute_path($home) 14 | file { "${home}/.clusterrunner/clusterrunner.conf" : 15 | ensure => present, 16 | owner => $user, 17 | group => $group, 18 | mode => '0600', 19 | content => template('clusterrunner/clusterrunner.conf.erb'), 20 | } 21 | 22 | $log_dir = '/var/log/clusterrunner' 23 | file { $log_dir: 24 | ensure => directory, 25 | owner => $user, 26 | group => $group, 27 | mode => '0755', 28 | } 29 | 30 | $conf_dir = '/etc/clusterrunner' 31 | file { $conf_dir: 32 | ensure => directory, 33 | owner => $user, 34 | group => $group, 35 | mode => '0755', 36 | } 37 | 38 | } 39 | -------------------------------------------------------------------------------- /manifests/slave/service.pp: -------------------------------------------------------------------------------- 1 | # == Class: clusterrunner::slave::service 2 | # 3 | # This class is responsible for starting the ClusterRunner slave service 4 | # NOTE: This should only be used after the ClusterRunner master has been setup 5 | # 6 | class clusterrunner::slave::service( 7 | $user, 8 | $group, 9 | $ensure 10 | ) { 11 | 12 | $log_file = '/var/log/clusterrunner/slave.log' 13 | file { $log_file: 14 | ensure => file, 15 | owner => $user, 16 | group => $group, 17 | mode => '0755', 18 | } 19 | 20 | $service_script = '/etc/init.d/clusterrunner_slave' 21 | file { $service_script: 22 | ensure => file, 23 | owner => 'root', 24 | group => 'root', 25 | mode => '0755', 26 | source => 'puppet:///modules/clusterrunner/clusterrunner_slave', 27 | } 28 | 29 | service { 'clusterrunner_slave': 30 | ensure => $ensure, 31 | require => File[$service_script, $log_file], 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /templates/slave_conf.erb: -------------------------------------------------------------------------------- 1 | ## Type: string 2 | ## ServiceRestart: clusterrunner_slave 3 | # 4 | # ClusterRunner master hostname 5 | # 6 | MASTER_HOST="<%= @master %>" 7 | 8 | ## Type: integer(0:65535) 9 | ## ServiceRestart: clusterrunner_slave 10 | # 11 | # ClusterRunner master port 12 | # 13 | MASTER_PORT="<%= @master_port %>" 14 | 15 | ## Type: string 16 | ## ServiceRestart: clusterrunner_slave 17 | # 18 | # ClusterRunner slave hostname 19 | # 20 | SLAVE_HOST="<%= @slave %>" 21 | 22 | ## Type: integer(0:65535) 23 | ## ServiceRestart: clusterrunner_slave 24 | # 25 | # ClusterRunner slave port 26 | # 27 | SLAVE_PORT="<%= @slave_port %>" 28 | 29 | ## Type: string 30 | ## ServiceRestart: clusterrunner_master 31 | # 32 | # The name of the ClusterRunner service user 33 | # 34 | CLUSTERRUNNER_USER="<%= @user %>" 35 | 36 | ## Type: string 37 | ## ServiceRestart: clusterrunner_master 38 | # 39 | # The home directory of the ClusterRunner service user 40 | # (where ClusterRunner is installed) 41 | # 42 | CLUSTERRUNNER_HOME="<%= @home %>" 43 | -------------------------------------------------------------------------------- /manifests/master/config.pp: -------------------------------------------------------------------------------- 1 | # == Class: clusterrunner::master::config 2 | # 3 | # Sets up main ClusterRunner configuration file (clusterrunner.conf) and the 4 | # system configuration file for the clusterrunner_master service 5 | # 6 | class clusterrunner::master::config ( 7 | $master, 8 | $master_port, 9 | $home, 10 | $user, 11 | $group, 12 | $secret_key, 13 | ) { 14 | 15 | # Validate the variables passed in as parameters. 16 | validate_string($master) 17 | validate_string("${master_port}") #lint:ignore:only_variable_string - This way, we can validate numbers or strings. 18 | 19 | class { 'clusterrunner::common::config': 20 | group => $group, 21 | user => $user, 22 | home => $home, 23 | secret_key => $secret_key, 24 | } 25 | 26 | $service_config_file = '/etc/clusterrunner/master_conf' 27 | file { $service_config_file: 28 | ensure => file, 29 | owner => 'root', 30 | group => 'root', 31 | mode => '0644', 32 | content => template('clusterrunner/master_conf.erb'), 33 | require => Class['clusterrunner::common::config'], 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /manifests/slave/config.pp: -------------------------------------------------------------------------------- 1 | # == Class: clusterrunner::slave::config 2 | # 3 | # Sets up main ClusterRunner configuration file (clusterrunner.conf) and the 4 | # system configuration file for the clusterrunner_slave service 5 | # 6 | class clusterrunner::slave::config ( 7 | $slave, 8 | $slave_port, 9 | $master, 10 | $master_port, 11 | $home, 12 | $user, 13 | $group, 14 | $secret_key, 15 | ) { 16 | 17 | # Validate the variables passed in as parameters. 18 | validate_string($master) 19 | validate_string($slave) 20 | validate_string("${master_port}") #lint:ignore:only_variable_string - This way, we can validate numbers or strings. 21 | validate_string("${slave_port}") #lint:ignore:only_variable_string - This way, we can validate numbers or strings. 22 | 23 | class { 'clusterrunner::common::config': 24 | group => $group, 25 | user => $user, 26 | home => $home, 27 | secret_key => $secret_key, 28 | } 29 | 30 | $service_config_file = '/etc/clusterrunner/slave_conf' 31 | file { $service_config_file: 32 | ensure => file, 33 | owner => 'root', 34 | group => 'root', 35 | mode => '0644', 36 | content => template('clusterrunner/slave_conf.erb'), 37 | require => Class['clusterrunner::common::config'], 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "box-clusterrunner", 3 | "version": "0.1.2", 4 | "source": "https://github.com/box/puppet-clusterrunner", 5 | "author": "Nadeem Ahmad ", 6 | "license": "Apache 2.0", 7 | "summary": "Installs ClusterRunner with Puppet", 8 | "project_page": "https://github.com/box/puppet-clusterrunner", 9 | "issues_url": "https://github.com/box/puppet-clusterrunner/issues", 10 | "operatingsystem_support": [ 11 | { 12 | "operatingsystem": "Scientific", 13 | "operatingsystemrelease": [ 14 | "6" 15 | ] 16 | }, 17 | { 18 | "operatingsystem": "Ubuntu", 19 | "operatingsystemrelease": [ 20 | "14" 21 | ] 22 | } 23 | ], 24 | "requirements": [ 25 | { 26 | "name": "pe", 27 | "version_requirement": "3.x" 28 | }, 29 | { 30 | "name": "puppet", 31 | "version_requirement": ">=2.7.0" 32 | } 33 | ], 34 | "dependencies": [ 35 | { 36 | "name": "puppetlabs-stdlib", 37 | "version_requirement": ">= 3.0.0" 38 | }, 39 | { 40 | "name": "puppet-archive", 41 | "version_requirement": ">= 0.1.0" 42 | } 43 | ] 44 | } 45 | 46 | -------------------------------------------------------------------------------- /manifests/common/install.pp: -------------------------------------------------------------------------------- 1 | # == Class: clusterrunner::install 2 | # 3 | # This class is meant to setup everything needed to install a 4 | # ClusterRunner server. 5 | # 6 | class clusterrunner::common::install ( 7 | $group, 8 | $user, 9 | $home, 10 | $package_url 11 | ) { 12 | 13 | group { $group: 14 | ensure => present, 15 | } 16 | 17 | user { $user: 18 | ensure => present, 19 | gid => $group, 20 | comment => 'ClusterRunner service user', 21 | home => $home, 22 | shell => '/bin/bash', 23 | password => '!!', 24 | managehome => true, 25 | } 26 | 27 | file { "${home}/.clusterrunner": 28 | ensure => directory, 29 | owner => $user, 30 | group => $group, 31 | mode => '0755', 32 | } 33 | 34 | file { "${home}/.clusterrunner/dist": 35 | ensure => directory, 36 | owner => $user, 37 | group => $group, 38 | mode => '0755', 39 | require => File["${home}/.clusterrunner"], 40 | } 41 | 42 | include archive 43 | archive { '/tmp/clusterrunner.tgz': 44 | ensure => present, 45 | source => $package_url, 46 | extract => true, 47 | extract_path => "${home}/.clusterrunner/dist", 48 | user => $user, 49 | group => $group, 50 | require => [ User[$user], File["${home}/.clusterrunner"] ], 51 | } 52 | 53 | } 54 | -------------------------------------------------------------------------------- /templates/clusterrunner.conf.erb: -------------------------------------------------------------------------------- 1 | ## This file contains the default values for common configuration settings. ClusterRunner expects a 2 | ## clusterrunner.conf file in the user's ~/.clusterrunner directory (or a location specified on the command line), 3 | ## and it will copy this file to ~/.clusterrunner/clusterrunner.conf if that doesn't exist. Uncomment settings to 4 | ## change their defaults. Settings which are specified via the command line (e.g. clusterrunner master --port 43777) 5 | ## will override these values. 6 | 7 | [general] 8 | secret = '<%= @secret_key %>' 9 | ## The root directory for files used during the build process. 10 | # base_directory = ~/.clusterrunner 11 | ## Symlinks to each build's project files are created here, to keep paths consistent across machines. 12 | # build_symlink_directory = /tmp/clusterrunner_build_symlinks 13 | 14 | ## The level to log at. Other options are DEBUG, INFO, NOTICE, WARNING, ERROR, and CRITIAL. 15 | # log_level = 'WARNING' 16 | ## A list of slaves, used for starting slaves with the "clusterrunner deploy" command 17 | # slaves = hostname01.example.com, hostname02.example.com, hostname03.example.com 18 | ## The hostname to refer to the local machine with 19 | # hostname = localhost 20 | 21 | [master] 22 | ## The port the master service will run on 23 | # port = 43000 24 | 25 | [slave] 26 | ## The port the slave service will run on 27 | # port = 43001 28 | ## The maximum number of parallel executions to run on this slave 29 | # num_executors = 1 30 | 31 | ## The master's hostname this slave will connect to 32 | # master_hostname = localhost 33 | ## The master's port this slave will connect to 34 | # master_port = 43000 35 | -------------------------------------------------------------------------------- /files/clusterrunner_master: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # clusterrunner_master - start, stop, and restart the ClusterRunner master service 3 | 4 | LOG_FILE=/var/log/clusterrunner/master.log 5 | CONF_PATH=/etc/clusterrunner/master_conf 6 | test -e "${CONF_PATH}" || { echo "${CONF_PATH} doesn't exist"; exit 1; } 7 | test -r "${CONF_PATH}" || { echo "${CONF_PATH} not readable. Perhaps you forgot 'sudo'?"; exit 1; } 8 | test -e "${LOG_FILE}" || { echo "${LOG_FILE} doesn't exist"; exit 1; } 9 | test -r "${CONF_PATH}" || { echo "${LOG_FILE} not readable. Perhaps you forgot 'sudo'?"; exit 1; } 10 | source "${CONF_PATH}" 11 | 12 | MASTER_API_ENDPOINT="${MASTER_HOST}:${MASTER_PORT}" 13 | CONNECTION_TIMEOUT='5' 14 | 15 | 16 | status() { 17 | echo "Checking status of ClusterRunner master..." 18 | curl -s -f -I -o /dev/null --connect-timeout "${CONNECTION_TIMEOUT}" -X GET "${MASTER_API_ENDPOINT}/v1" 19 | if [ "$?" -ne 0 ]; then 20 | echo "The ClusterRunner master is not responsive on ${MASTER_API_ENDPOINT}" 21 | exit 1 22 | else 23 | echo "The ClusterRunner master is responsive on ${MASTER_API_ENDPOINT}" 24 | exit 0 25 | fi 26 | } 27 | 28 | start() { 29 | echo "Attempting to start clusterrunner_master service" 30 | su "${CLUSTERRUNNER_USER}" -c "/usr/bin/nohup ${CLUSTERRUNNER_HOME}/.clusterrunner/dist/clusterrunner master --port ${MASTER_PORT} >> ${LOG_FILE} 2>&1 &" 31 | echo "Waiting 3 seconds to give the service time to start up" 32 | sleep 3 33 | status 34 | } 35 | 36 | 37 | stop() { 38 | echo "Killing the clusterrunner_master service" 39 | pkill -15 clusterrunner 40 | echo "Waiting 10 seconds to ensure the process is killed" 41 | sleep 10 42 | } 43 | 44 | case "$1" in 45 | start) 46 | start 47 | ;; 48 | stop) 49 | stop 50 | ;; 51 | restart) 52 | stop 53 | start 54 | ;; 55 | status) 56 | status 57 | ;; 58 | *) 59 | echo "Usage: $0 {start|stop|restart|status}" 60 | exit 1 61 | ;; 62 | esac 63 | 64 | -------------------------------------------------------------------------------- /manifests/access.pp: -------------------------------------------------------------------------------- 1 | # == Class: clusterrunner::access 2 | # 3 | # Ensures that the SSH public/private key pair is in the appropriate location 4 | # on the server where this class is declared. This class is useful as it 5 | # enables bi-directional SSH access between ClusterRunner master and slaves. 6 | # Therefore, it should be included on both the master and the slave. 7 | # 8 | # === Parameters 9 | # 10 | # [*public_key*] 11 | # SSH public key. 12 | # 13 | # [*private_key*] 14 | # SSH private key corresponding to the public key. 15 | # 16 | # [*user*] 17 | # ClusterRunner service user. Defaults to 'clusterrunner'. 18 | # 19 | # [*group*] 20 | # Group for the ClusterRunner service user. Defaults to 'clusterrunner'. 21 | # 22 | # [*home*] 23 | # The home directory of the ClusterRunner service user. This is where the 24 | # ClusterRunner package is installed. Defaults to '/usr/local/clusterrunner'. 25 | # 26 | # === Authors 27 | # 28 | # Nadeem Ahmad 29 | # 30 | # === Copyright 31 | # 32 | # Copyright 2016 Box, Inc. 33 | # 34 | class clusterrunner::access( 35 | $public_key, 36 | $private_key, 37 | $home = $::clusterrunner::params::home, 38 | $user = $::clusterrunner::params::user, 39 | $group = $::clusterrunner::params::group 40 | ) inherits clusterrunner::params { 41 | 42 | file { "${home}/.ssh": 43 | ensure => directory, 44 | owner => $user, 45 | group => $group, 46 | mode => '0700', 47 | } 48 | 49 | $key_type = 'ssh-rsa' 50 | $public_key_comment = 'key for using ClusterRunner' 51 | 52 | file { "${home}/.ssh/id_rsa.cr.pub": 53 | ensure => file, 54 | content => "${key_type} ${public_key} ${public_key_comment}", 55 | owner => $user, 56 | group => $group, 57 | mode => '0600', 58 | } 59 | 60 | ssh_authorized_key { $public_key_comment: 61 | user => $user, 62 | type => $key_type, 63 | key => $public_key, 64 | require => File["${home}/.ssh"], 65 | } 66 | 67 | file { "${home}/.ssh/id_rsa.cr": 68 | ensure => file, 69 | content => $private_key, 70 | owner => $user, 71 | group => $group, 72 | mode => '0600', 73 | } 74 | 75 | } 76 | -------------------------------------------------------------------------------- /files/clusterrunner_slave: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | # clusterrunner_slave - start, stop, and restart the ClusterRunner slave service 3 | 4 | LOG_FILE=/var/log/clusterrunner/slave.log 5 | CONF_PATH=/etc/clusterrunner/slave_conf 6 | test -e "${CONF_PATH}" || { echo "${CONF_PATH} doesn't exist"; exit 1; } 7 | test -r "${CONF_PATH}" || { echo "${CONF_PATH} not readable. Perhaps you forgot 'sudo'?"; exit 1; } 8 | test -e "${LOG_FILE}" || { echo "${LOG_FILE} doesn't exist"; exit 1; } 9 | test -r "${CONF_PATH}" || { echo "${LOG_FILE} not readable. Perhaps you forgot 'sudo'?"; exit 1; } 10 | source "${CONF_PATH}" 11 | 12 | MASTER_API_ENDPOINT="${MASTER_HOST}:${MASTER_PORT}" 13 | CONNECTION_TIMEOUT='5' 14 | 15 | status() { 16 | echo "First checking status of ClusterRunner master..." 17 | curl -s -f -I -o /dev/null --connect-timeout "${CONNECTION_TIMEOUT}" -X GET "${MASTER_API_ENDPOINT}/v1" 18 | if [ "$?" -ne 0 ]; then 19 | echo "The ClusterRunner master is not responsive on ${MASTER_API_ENDPOINT}" 20 | exit 1 21 | else 22 | echo "The ClusterRunner master is responsive on ${MASTER_API_ENDPOINT}" 23 | fi 24 | 25 | SLAVES_ENDPOINT_OUTPUT="$(curl -s --connect-timeout "${CONNECTION_TIMEOUT}" -X GET "${MASTER_API_ENDPOINT}/v1/slave")" 26 | echo "${SLAVES_ENDPOINT_OUTPUT}" | grep "${SLAVE_HOST}" > /dev/null 27 | if [ "$?" -ne 0 ]; then 28 | echo "The ClusterRunner slave ${SLAVE_HOST} is not responsive on ${MASTER_API_ENDPOINT}" 29 | exit 1 30 | else 31 | echo "The ClusterRunner slave ${SLAVE_HOST} is responsive on ${MASTER_API_ENDPOINT}" 32 | exit 0 33 | fi 34 | } 35 | 36 | start() { 37 | echo "Attempting to start clusterrunner_slave service" 38 | su "${ClUSTERRUNNER_USER}" -c "/usr/bin/nohup ${CLUSTERRUNNER_HOME}/.clusterrunner/dist/clusterrunner slave --master-url "${MASTER_API_ENDPOINT}" --port ${SLAVE_PORT} >> ${LOG_FILE} 2>&1 &" 39 | echo "Waiting 3 seconds to give the service time to start up" 40 | sleep 3 41 | status 42 | } 43 | 44 | 45 | stop() { 46 | echo "Killing the clusterrunner_slave service" 47 | pkill -15 clusterrunner 48 | echo "Waiting 10 seconds to ensure the process is killed" 49 | sleep 10 50 | } 51 | 52 | case "$1" in 53 | start) 54 | start 55 | ;; 56 | stop) 57 | stop 58 | ;; 59 | restart) 60 | stop 61 | start 62 | ;; 63 | status) 64 | status 65 | ;; 66 | *) 67 | echo "Usage: $0 {start|stop|restart|status}" 68 | exit 1 69 | ;; 70 | esac 71 | 72 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | 3 | All contributions are welcome to this project. 4 | 5 | ## Contributor License Agreement 6 | 7 | Before a contribution can be merged into this project, please fill out the Contributor License Agreement (CLA) located at: 8 | 9 | http://opensource.box.com/cla 10 | 11 | To learn more about CLAs and why they are important to open source projects, please see the [Wikipedia entry](http://en.wikipedia.org/wiki/Contributor_License_Agreement). 12 | 13 | ## Code of Conduct 14 | 15 | This project adheres to the [Box Open Code of Conduct](http://opensource.box.com/code-of-conduct/). By participating, you are expected to uphold this code. 16 | 17 | ## How to contribute 18 | 19 | * **File an issue** - if you found a bug, want to request an enhancement, or want to implement something (bug fix or feature). 20 | * **Send a pull request** - if you want to contribute code. Please be sure to file an issue first. 21 | 22 | ## Pull request best practices 23 | 24 | We want to accept your pull requests. Please follow these steps: 25 | 26 | ### Step 1: File an issue 27 | 28 | Before writing any code, please file an issue stating the problem you want to solve or the feature you want to implement. This allows us to give you feedback before you spend any time writing code. There may be a known limitation that can't be addressed, or a bug that has already been fixed in a different way. The issue allows us to communicate and figure out if it's worth your time to write a bunch of code for the project. 29 | 30 | ### Step 2: Fork this repository in GitHub 31 | 32 | This will create your own copy of our repository. 33 | 34 | ### Step 3: Add the upstream source 35 | 36 | The upstream source is the project under the Box organization on GitHub. To add an upstream source for this project, type: 37 | 38 | ``` 39 | git remote add upstream git@github.com:box/puppet-clusterrunner.git 40 | ``` 41 | 42 | This will come in useful later. 43 | 44 | ### Step 4: Create a feature branch 45 | 46 | Create a branch with a descriptive name, such as `add-search`. 47 | 48 | ### Step 5: Push your feature branch to your fork 49 | 50 | As you develop code, continue to push code to your remote feature branch. Please make sure to include the issue number you're addressing in your commit message, such as: 51 | 52 | ``` 53 | git commit -m "Adding search (fixes #123)" 54 | ``` 55 | 56 | This helps us out by allowing us to track which issue your commit relates to. 57 | 58 | Keep a separate feature branch for each issue you want to address. 59 | 60 | ### Step 6: Rebase 61 | 62 | Before sending a pull request, rebase against upstream, such as: 63 | 64 | ``` 65 | git fetch upstream 66 | git rebase upstream/master 67 | ``` 68 | 69 | This will add your changes on top of what's already in upstream, minimizing merge issues. 70 | 71 | ### Step 7: Send the pull request 72 | 73 | Send the pull request from your feature branch to us. Be sure to include a description that lets us know what work you did. 74 | 75 | Keep in mind that we like to see one issue addressed per pull request, as this helps keep our git history clean and we can more easily track down issues. 76 | -------------------------------------------------------------------------------- /manifests/master.pp: -------------------------------------------------------------------------------- 1 | # == Class: clusterrunner::master 2 | # 3 | # Installs the ClusterRunner package in an appropriate location and with 4 | # appropriate access rights on the ClusterRunner master server. It also ensures 5 | # that the clusterrunner_master service is in the specified state. 6 | # 7 | # === Parameters 8 | # 9 | # [*master*] 10 | # The hostname of the ClusterRunner master. 11 | # 12 | # [*master_port*] 13 | # The port that the ClusterRunner master service should run on. 14 | # Defaults to '43000'. 15 | # 16 | # [*secret_key*] 17 | # The key that the master will use to validate API requests and requests 18 | # from the slave. Should be a SHA-512 hash. 19 | # 20 | # [*user*] 21 | # ClusterRunner service user. Defaults to 'clusterrunner'. 22 | # 23 | # [*group*] 24 | # Group for the ClusterRunner service user. Defaults to 'clusterrunner'. 25 | # 26 | # [*home*] 27 | # The home directory of the ClusterRunner service user. This is where the 28 | # ClusterRunner package is installed. Defaults to '/usr/local/clusterrunner'. 29 | # 30 | # [*ensure*] 31 | # The state of the clusterrunner_master service. It can either be 'running' 32 | # or 'stopped'. Defaults to 'running'. 33 | # 34 | # [*package_url*] 35 | # This parameter allows you to specify an alternate url to the ClusterRunner 36 | # GZIP Compressed Tar Archive (.tgz) file. By default the ClusterRunner 37 | # package is pulled from the public internet hosted on Box. This parameter is 38 | # useful if access to the public internet is not available and/or internal 39 | # hosting is preferred. 40 | # 41 | # === Authors 42 | # 43 | # Nadeem Ahmad 44 | # 45 | # === Copyright 46 | # 47 | # Copyright 2016 Box, Inc. 48 | # 49 | class clusterrunner::master ( 50 | $master = undef, 51 | $secret_key = undef, 52 | $master_port = $clusterrunner::params::master_port, 53 | $group = $clusterrunner::params::group, 54 | $user = $clusterrunner::params::user, 55 | $home = $clusterrunner::params::home, 56 | $ensure = 'running', 57 | $package_url = $clusterrunner::params::package_url, 58 | 59 | ) inherits clusterrunner::params { 60 | 61 | unless ($secret_key and is_string($secret_key)) { 62 | fail('clusterrunner: A secret_key is required') 63 | } 64 | validate_absolute_path($home) 65 | 66 | # Declare the install/config classes with appropriate relationships. 67 | class { 'clusterrunner::common::install': 68 | group => $group, 69 | user => $user, 70 | home => $home, 71 | package_url => $package_url, 72 | before => Class['clusterrunner::master::config'], 73 | } 74 | 75 | class { 'clusterrunner::master::config': 76 | master => $master, 77 | master_port => $master_port, 78 | home => $home, 79 | user => $user, 80 | group => $group, 81 | secret_key => $secret_key, 82 | require => Class['clusterrunner::common::install'], 83 | } 84 | 85 | class { 'clusterrunner::master::service': 86 | ensure => $ensure, 87 | user => $user, 88 | group => $group, 89 | require => Class['clusterrunner::master::config'], 90 | } 91 | 92 | } 93 | -------------------------------------------------------------------------------- /manifests/slave.pp: -------------------------------------------------------------------------------- 1 | # == Class: clusterrunner::master 2 | # 3 | # Installs the ClusterRunner package in an appropriate location and with 4 | # appropriate access rights on a particular ClusterRunner slave server. It also 5 | # ensures that the ClusterRunner slave is connected to its specified master and 6 | # that clusterrunner_slave service is in the specified state. 7 | # 8 | # === Parameters 9 | # 10 | # [*slave*] 11 | # The hostname of the ClusterRunner slave. 12 | # 13 | # [*slave_port*] 14 | # The port that the ClusterRunner slave service should run on. Defaults to '43001'. 15 | # 16 | # [*master*] 17 | # The hostname of the ClusterRunner master that the slave intends to connect to. 18 | # 19 | # [*master_port*] 20 | # The port that the ClusterRunner master service is running on. Defaults to 21 | # '43000'. 22 | 23 | # [*secret_key*] 24 | # The key that the master will use to validate API requests and requests from 25 | # the slave. This MUST be the same as secret_key passed to the master. 26 | # 27 | # [*user*] 28 | # ClusterRunner service user. Defaults to 'clusterrunner'. 29 | # 30 | # [*group*] 31 | # Group for the ClusterRunner service user. Defaults to 'clusterrunner'. 32 | # 33 | # [*home*] 34 | # The home directory of the ClusterRunner service user. This is where the 35 | # ClusterRunner package is installed. Defaults to '/usr/local/clusterrunner'. 36 | # 37 | # [*ensure*] 38 | # The state of the clusterrunner_slave service. It can either be 'running' 39 | # or 'stopped'. Defaults to 'running'. 40 | # 41 | # [*package_url*] 42 | # This parameter allows you to specify an alternate url to the ClusterRunner 43 | # GZIP Compressed Tar Archive (.tgz) file. By default the ClusterRunner 44 | # package is pulled from the public internet hosted on Box. This parameter is 45 | # useful if access to the public internet is not available and/or internal 46 | # hosting is preferred. 47 | # 48 | # === Authors 49 | # 50 | # Nadeem Ahmad 51 | # 52 | # === Copyright 53 | # 54 | # Copyright 2016 Box, Inc. 55 | # 56 | class clusterrunner::slave ( 57 | $master = undef, 58 | $slave = undef, 59 | $secret_key = undef, 60 | $slave_port = $clusterrunner::params::slave_port, 61 | $master_port = $clusterrunner::params::master_port, 62 | $group = $clusterrunner::params::group, 63 | $user = $clusterrunner::params::user, 64 | $home = $clusterrunner::params::home, 65 | $ensure = 'running', 66 | $package_url = $clusterrunner::params::package_url, 67 | ) inherits clusterrunner::params { 68 | 69 | unless ($secret_key and is_string($secret_key)) { 70 | fail('clusterrunner: A secret_key is required') 71 | } 72 | validate_absolute_path($home) 73 | 74 | # Declare the install/config classes with appropriate relationships. 75 | class { 'clusterrunner::common::install': 76 | group => $group, 77 | user => $user, 78 | home => $home, 79 | package_url => $package_url, 80 | before => Class['clusterrunner::slave::config'], 81 | } 82 | 83 | class { 'clusterrunner::slave::config': 84 | master => $master, 85 | master_port => $master_port, 86 | slave => $slave, 87 | slave_port => $slave_port, 88 | home => $home, 89 | user => $user, 90 | group => $group, 91 | secret_key => $secret_key, 92 | require => Class['clusterrunner::common::install'], 93 | } 94 | 95 | class { 'clusterrunner::slave::service': 96 | ensure => $ensure, 97 | user => $user, 98 | group => $group, 99 | require => Class['clusterrunner::slave::config'], 100 | } 101 | 102 | } 103 | 104 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Puppet ClusterRunner 2 | 3 | [![Project Status](http://opensource.box.com/badges/active.svg)](http://opensource.box.com/badges) 4 | 5 | #### Table of Contents 6 | 7 | 1. [Overview](#overview) 8 | 2. [Module Description](#module-description) 9 | 3. [Setup](#setup) 10 | 4. [Usage](#usage) 11 | 5. [Reference](#reference) 12 | 6. [Limitations](#limitations) 13 | 7. [Contributors](#contributors) 14 | 8. [Support](#support) 15 | 9. [Copyright and License](#copyright-and-license) 16 | 17 | ## Overview 18 | 19 | This module installs and manages [ClusterRunner](http://www.clusterrunner.com/). ClusterRunner makes it easy to execute test suites across an infrastructure in the fastest and most efficient way possible. 20 | 21 | ## Module Description 22 | 23 | This module installs the ClusterRunner package for both the master and the slave. It also sets up the configuration and services required to get ClusterRunner up and running. 24 | 25 | ## Setup 26 | 27 | * This module will not alter any existing files or services. 28 | * There are only two Puppet module dependencies: 29 | * [`stdlib`](https://forge.puppetlabs.com/puppetlabs/stdlib) 30 | * [`archive`](https://forge.puppetlabs.com/puppet/archive) 31 | 32 | ## Usage 33 | 34 | To function, ClusterRunner requires a master host and one or more slaves. 35 | 36 | **NOTE**: The user must ensure that the ClusterRunner master service is started **BEFORE** the ClusterRunner slave service can be started. This means that the order shown below must be followed. 37 | 38 | Using this module, a ClusterRunner master is very easy to setup: 39 | 40 | ```puppet 41 | class { 'clusterrunner::master': 42 | master => 'clusterrunner-master.example.com', 43 | master_port => '3000', 44 | secret_key => $secret_key # Generated SHA-512 hash for secret_key 45 | } 46 | ``` 47 | 48 | After the master is up and running, the slave can be configured: 49 | ```puppet 50 | class { 'clusterrunner::slave': 51 | master => 'clusterrunner-master.example.com', 52 | master_port => '3000', 53 | slave => 'clusterrunner-slave.example.com' 54 | slave_port => '3001', 55 | secret_key => $secret_key #The secret_key must be the same as the one passed to the master 56 | } 57 | ``` 58 | This can be repeated for any number of slaves. 59 | 60 | ClusterRunner requires that the masters and the slaves have bi-directional SSH access. This modules provides a class to do just that. An SSH key pair can be generated and passed to `clusterrunner::access` class and included on **BOTH** the master and the slave. 61 | 62 | ```puppet 63 | class { 'clusterrunner::access': 64 | private_key => $private_key, 65 | public_key => $public_key, 66 | } 67 | ``` 68 | 69 | ## Reference 70 | 71 | ### Class `clusterrunner::master` 72 | 73 | Installs the ClusterRunner package in an appropriate location and with appropriate access rights on the ClusterRunner master server. It also ensures that the `clusterrunner_master` service is in the specified state. 74 | 75 | #### Parameters 76 | 77 | * `master`: The hostname of the ClusterRunner master. 78 | 79 | * `master_port`: The port that the ClusterRunner master service should run on. Defaults to '43000'. 80 | 81 | * `secret_key`: The key that the master will use to validate API requests and requests from the slave. Should be a SHA-512 hash. 82 | 83 | * `user`: ClusterRunner service user. Defaults to 'clusterrunner'. 84 | 85 | * `group`: Group for the ClusterRunner service user. Defaults to 'clusterrunner'. 86 | 87 | * `home`: The home directory of the ClusterRunner service user. This is where the ClusterRunner package is installed. Defaults to '/usr/local/clusterrunner'. 88 | 89 | * `ensure`: The state of the `clusterrunner_master` service. It can either be 'running' or 'stopped'. Defaults to 'running'. 90 | 91 | * `package_url`: This parameter allows you to specify an alternate URL to the ClusterRunner GZIP Compressed Tar Archive (.tgz) file. By default, the ClusterRunner package is pulled from the public internet hosted on Box. This parameter is useful if access to the public internet is not available and/or internal hosting is preferred. 92 | 93 | ### Class `clusterrunner::slave` 94 | 95 | Installs the ClusterRunner package in an appropriate location and with appropriate access rights on a particular ClusterRunner slave server. It also ensures that the ClusterRunner slave is connected to its specified master and that `clusterrunner_slave` service is in the specified state. 96 | 97 | * `slave`: The hostname of the ClusterRunner slave. 98 | 99 | * `slave_port`: The port that the ClusterRunner slave service should run on. Defaults to '43001'. 100 | 101 | * `master`: The hostname of the ClusterRunner master that the slave intends to connect to. 102 | 103 | * `master_port`: The port that the ClusterRunner master service is running on. Defaults to '43000'. 104 | 105 | * `secret_key`: The key that the master will use to validate API requests and requests from the slave. This MUST be the same as `secret_key` passed to the master. 106 | 107 | * `user`: ClusterRunner service user. Defaults to 'clusterrunner'. 108 | 109 | * `group`: Group for the ClusterRunner service user. Defaults to 'clusterrunner'. 110 | 111 | * `home`: The home directory of the ClusterRunner service user. This is where the ClusterRunner package is installed. Defaults to '/usr/local/clusterrunner'. 112 | 113 | * `ensure`: The state of the `clusterrunner_slave` service. It can either be 'running' or 'stopped'. Defaults to 'running'. 114 | 115 | * `package_url`: This parameter allows you to specify an alternate URL to the ClusterRunner GZIP Compressed Tar Archive (.tgz) file. By default, the ClusterRunner package is pulled from the public internet hosted on Box. This parameter is useful if access to the public internet is not available and/or internal hosting is preferred. 116 | 117 | ### Class `clusterrunner::access` 118 | 119 | Ensures that the SSH public/private key pair is in the appropriate location on the server where this class is declared. This class is useful as it enables bi-directional SSH access between ClusterRunner master and slaves. Therefore, it should be included on both the master and the slave. 120 | 121 | * `public_key`: SSH public key. 122 | 123 | * `private_key`: SSH private key corresponding to the public key. 124 | 125 | * `user`: ClusterRunner service user. Defaults to 'clusterrunner'. 126 | 127 | * `group`: Group for the ClusterRunner service user. Defaults to 'clusterrunner'. 128 | 129 | * `home`: The home directory of the ClusterRunner service user. This is where the ClusterRunner package is installed. Defaults to '/usr/local/clusterrunner'. 130 | 131 | ## Limitations 132 | Fully tested on Scientific Linux 6 and Ubuntu 14. Please open a pull request for problems encountered with other Linux systems. 133 | 134 | ## Contributors 135 | 136 | The list of contributors can be found at: https://github.com/box/puppet-clusterrunner/graphs/contributors 137 | 138 | ## Support 139 | 140 | Need to contact us directly? Email oss@box.com and be sure to include the name of this project in the subject. 141 | 142 | ## Copyright and License 143 | 144 | Copyright 2016 Box, Inc. All rights reserved. 145 | 146 | Licensed under the Apache License, Version 2.0 (the "License"); 147 | you may not use this file except in compliance with the License. 148 | You may obtain a copy of the License at 149 | 150 | http://www.apache.org/licenses/LICENSE-2.0 151 | 152 | Unless required by applicable law or agreed to in writing, software 153 | distributed under the License is distributed on an "AS IS" BASIS, 154 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 155 | See the License for the specific language governing permissions and 156 | limitations under the License. 157 | 158 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | "License" shall mean the terms and conditions for use, reproduction, 9 | and distribution as defined by Sections 1 through 9 of this document. 10 | "Licensor" shall mean the copyright owner or entity authorized by 11 | the copyright owner that is granting the License. 12 | "Legal Entity" shall mean the union of the acting entity and all 13 | other entities that control, are controlled by, or are under common 14 | control with that entity. For the purposes of this definition, 15 | "control" means (i) the power, direct or indirect, to cause the 16 | direction or management of such entity, whether by contract or 17 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 18 | outstanding shares, or (iii) beneficial ownership of such entity. 19 | "You" (or "Your") shall mean an individual or Legal Entity 20 | exercising permissions granted by this License. 21 | "Source" form shall mean the preferred form for making modifications, 22 | including but not limited to software source code, documentation 23 | source, and configuration files. 24 | "Object" form shall mean any form resulting from mechanical 25 | transformation or translation of a Source form, including but 26 | not limited to compiled object code, generated documentation, 27 | and conversions to other media types. 28 | "Work" shall mean the work of authorship, whether in Source or 29 | Object form, made available under the License, as indicated by a 30 | copyright notice that is included in or attached to the work 31 | (an example is provided in the Appendix below). 32 | "Derivative Works" shall mean any work, whether in Source or Object 33 | form, that is based on (or derived from) the Work and for which the 34 | editorial revisions, annotations, elaborations, or other modifications 35 | represent, as a whole, an original work of authorship. For the purposes 36 | of this License, Derivative Works shall not include works that remain 37 | separable from, or merely link (or bind by name) to the interfaces of, 38 | the Work and Derivative Works thereof. 39 | "Contribution" shall mean any work of authorship, including 40 | the original version of the Work and any modifications or additions 41 | to that Work or Derivative Works thereof, that is intentionally 42 | submitted to Licensor for inclusion in the Work by the copyright owner 43 | or by an individual or Legal Entity authorized to submit on behalf of 44 | the copyright owner. For the purposes of this definition, "submitted" 45 | means any form of electronic, verbal, or written communication sent 46 | to the Licensor or its representatives, including but not limited to 47 | communication on electronic mailing lists, source code control systems, 48 | and issue tracking systems that are managed by, or on behalf of, the 49 | Licensor for the purpose of discussing and improving the Work, but 50 | excluding communication that is conspicuously marked or otherwise 51 | designated in writing by the copyright owner as "Not a Contribution." 52 | "Contributor" shall mean Licensor and any individual or Legal Entity 53 | on behalf of whom a Contribution has been received by Licensor and 54 | subsequently incorporated within the Work. 55 | 56 | 2. Grant of Copyright License. Subject to the terms and conditions of 57 | this License, each Contributor hereby grants to You a perpetual, 58 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 59 | copyright license to reproduce, prepare Derivative Works of, 60 | publicly display, publicly perform, sublicense, and distribute the 61 | Work and such Derivative Works in Source or Object form. 62 | 63 | 3. Grant of Patent License. Subject to the terms and conditions of 64 | this License, each Contributor hereby grants to You a perpetual, 65 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 66 | (except as stated in this section) patent license to make, have made, 67 | use, offer to sell, sell, import, and otherwise transfer the Work, 68 | where such license applies only to those patent claims licensable 69 | by such Contributor that are necessarily infringed by their 70 | Contribution(s) alone or by combination of their Contribution(s) 71 | with the Work to which such Contribution(s) was submitted. If You 72 | institute patent litigation against any entity (including a 73 | cross-claim or counterclaim in a lawsuit) alleging that the Work 74 | or a Contribution incorporated within the Work constitutes direct 75 | or contributory patent infringement, then any patent licenses 76 | granted to You under this License for that Work shall terminate 77 | as of the date such litigation is filed. 78 | 79 | 4. Redistribution. You may reproduce and distribute copies of the 80 | Work or Derivative Works thereof in any medium, with or without 81 | modifications, and in Source or Object form, provided that You 82 | meet the following conditions: 83 | 84 | (a) You must give any other recipients of the Work or 85 | Derivative Works a copy of this License; and 86 | 87 | (b) You must cause any modified files to carry prominent notices 88 | stating that You changed the files; and 89 | 90 | (c) You must retain, in the Source form of any Derivative Works 91 | that You distribute, all copyright, patent, trademark, and 92 | attribution notices from the Source form of the Work, 93 | excluding those notices that do not pertain to any part of 94 | the Derivative Works; and 95 | 96 | (d) If the Work includes a "NOTICE" text file as part of its 97 | distribution, then any Derivative Works that You distribute must 98 | include a readable copy of the attribution notices contained 99 | within such NOTICE file, excluding those notices that do not 100 | pertain to any part of the Derivative Works, in at least one 101 | of the following places: within a NOTICE text file distributed 102 | as part of the Derivative Works; within the Source form or 103 | documentation, if provided along with the Derivative Works; or, 104 | within a display generated by the Derivative Works, if and 105 | wherever such third-party notices normally appear. The contents 106 | of the NOTICE file are for informational purposes only and 107 | do not modify the License. You may add Your own attribution 108 | notices within Derivative Works that You distribute, alongside 109 | or as an addendum to the NOTICE text from the Work, provided 110 | that such additional attribution notices cannot be construed 111 | as modifying the License. 112 | 113 | You may add Your own copyright statement to Your modifications and 114 | may provide additional or different license terms and conditions 115 | for use, reproduction, or distribution of Your modifications, or 116 | for any such Derivative Works as a whole, provided Your use, 117 | reproduction, and distribution of the Work otherwise complies with 118 | the conditions stated in this License. 119 | 120 | 5. Submission of Contributions. Unless You explicitly state otherwise, 121 | any Contribution intentionally submitted for inclusion in the Work 122 | by You to the Licensor shall be under the terms and conditions of 123 | this License, without any additional terms or conditions. 124 | Notwithstanding the above, nothing herein shall supersede or modify 125 | the terms of any separate license agreement you may have executed 126 | with Licensor regarding such Contributions. 127 | 128 | 6. Trademarks. This License does not grant permission to use the trade 129 | names, trademarks, service marks, or product names of the Licensor, 130 | except as required for reasonable and customary use in describing the 131 | origin of the Work and reproducing the content of the NOTICE file. 132 | 133 | 7. Disclaimer of Warranty. Unless required by applicable law or 134 | agreed to in writing, Licensor provides the Work (and each 135 | Contributor provides its Contributions) on an "AS IS" BASIS, 136 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 137 | implied, including, without limitation, any warranties or conditions 138 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 139 | PARTICULAR PURPOSE. You are solely responsible for determining the 140 | appropriateness of using or redistributing the Work and assume any 141 | risks associated with Your exercise of permissions under this License. 142 | 143 | 8. Limitation of Liability. In no event and under no legal theory, 144 | whether in tort (including negligence), contract, or otherwise, 145 | unless required by applicable law (such as deliberate and grossly 146 | negligent acts) or agreed to in writing, shall any Contributor be 147 | liable to You for damages, including any direct, indirect, special, 148 | incidental, or consequential damages of any character arising as a 149 | result of this License or out of the use or inability to use the 150 | Work (including but not limited to damages for loss of goodwill, 151 | work stoppage, computer failure or malfunction, or any and all 152 | other commercial damages or losses), even if such Contributor 153 | has been advised of the possibility of such damages. 154 | 155 | 9. Accepting Warranty or Additional Liability. While redistributing 156 | the Work or Derivative Works thereof, You may choose to offer, 157 | and charge a fee for, acceptance of support, warranty, indemnity, 158 | or other liability obligations and/or rights consistent with this 159 | License. However, in accepting such obligations, You may act only 160 | on Your own behalf and on Your sole responsibility, not on behalf 161 | of any other Contributor, and only if You agree to indemnify, 162 | defend, and hold each Contributor harmless for any liability 163 | incurred by, or claims asserted against, such Contributor by reason 164 | of your accepting any such warranty or additional liability. 165 | 166 | END OF TERMS AND CONDITIONS 167 | --------------------------------------------------------------------------------