├── LICENSE ├── README.rst └── reactor └── autoscale.sls /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Salt Stack Formulas 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | 15 | -------------------------------------------------------------------------------- /README.rst: -------------------------------------------------------------------------------- 1 | salt-cloud-reactor 2 | ================== 3 | 4 | This is a reactor formula, which allows supported providers in Salt Cloud to 5 | notify Salt when an instance is created, so that it may be automatically 6 | bootstrapped and accepted by the Salt Master, or when an instance is deleted, 7 | so that its key can be automatically removed from the Salt Master. 8 | 9 | 10 | Dependencies 11 | ------------ 12 | The following packages must be installed: 13 | 14 | .. code-block:: yaml 15 | 16 | - Salt (develop branch) 17 | 18 | 19 | Master Configuration 20 | -------------------- 21 | The following files need to be configured on the Salt Master: 22 | 23 | .. code-block:: yaml 24 | 25 | - /etc/salt/master 26 | - /etc/salt/cloud 27 | - /etc/salt/cloud.providers.d/* 28 | 29 | 30 | /etc/salt/master 31 | ~~~~~~~~~~~~~~~~ 32 | 33 | The master must be set up to point the reactor to the necessary Salt Cloud 34 | provider setting. Any additional settings to be used on the target minion, that 35 | are not configured in the provider configuration, can also be set here. 36 | 37 | .. code-block:: yaml 38 | 39 | reactor: 40 | - 'salt/cloud/*/cache_node_new': 41 | - '/srv/reactor/autoscale.sls' 42 | - 'salt/cloud/*/cache_node_missing': 43 | - '/srv/reactor/autoscale.sls' 44 | 45 | autoscale: 46 | provider: my-ec-config 47 | ssh_username: root 48 | 49 | 50 | /etc/salt/cloud 51 | ~~~~~~~~~~~~~~~ 52 | 53 | Salt Cloud must be configured to use the cloud cachedir, and to generate events 54 | based on the contents of it. The following two options need to be set: 55 | 56 | .. code-block:: yaml 57 | 58 | update_cachedir: True 59 | diff_cache_events: True 60 | 61 | This will cause Salt Cloud to fire events to Salt when changes are detected on 62 | the configured provider. 63 | 64 | Some of these events will contain data which describe a node. Because some of 65 | the fields returned may contain sensitive data, the ``cache_event_strip_fields`` 66 | configuration option exists to strip those fields from the event return. 67 | 68 | .. code-block:: yaml 69 | 70 | cache_event_strip_fields: 71 | - password 72 | - priv_key 73 | 74 | 75 | /etc/salt/cloud.providers.d/* 76 | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 77 | Existing Salt Cloud provider configuration can be used with this reactor. 78 | Profile configuration is not necessary on the master; the instance is assumed 79 | to already be created by the time it hits the reactor. 80 | 81 | .. code-block:: yaml 82 | 83 | my-ec2-config: 84 | id: 85 | key: 86 | keyname: 87 | securitygroup: 88 | private_key: 89 | location: us-east-1 90 | provider: ec2 91 | minion: 92 | master: saltmaster.example.com 93 | 94 | :Note: The openstack cloud provider is currently broken and autoscaling will not work with it until 95 | `this bug `_ is fixed. 96 | 97 | Basic Usage 98 | ----------- 99 | Once the Salt Master has been configured, the reactor will manage itself. When 100 | ``salt-cloud -F`` or ``salt-cloud --full-query`` is issued against a configured 101 | provider, the cloud cache will up reviewed and updated by Salt Cloud. When a 102 | new instance is detected, Salt Cloud will be notified to wait for it to become 103 | available, and bootstrap it with Salt. Its key will be automatically accepted, 104 | and if the minion configuration includes the appropriate startup state, then 105 | the minion will configure itself, and go to work. 106 | 107 | When the autoscaler spins down a machine, the Wheel system inside of Salt will 108 | be notified to delete its key from the master. This causes instances to be 109 | completely autonomous, both in setup and tear-down. 110 | 111 | In order to perform these queries on a regular basis, the above command needs 112 | to be issued via a scheduling system, such as cron or the Salt Scheduler. It is 113 | recommended in most configuration to use no less than a 5 minute delay between 114 | intervals, as a measure of respect to the cloud provider. 115 | 116 | Caveats 117 | ------- 118 | Because this data is polled for, rather than being triggered directly from the 119 | cloud provider, there will be a delay between the instance being created, and 120 | Salt Cloud being able to bootstrap it. 121 | -------------------------------------------------------------------------------- /reactor/autoscale.sls: -------------------------------------------------------------------------------- 1 | #!py 2 | 3 | def run(): 4 | ''' 5 | Run the reactor 6 | ''' 7 | if 'new_data' in data: 8 | vm_ = data['new_data'] 9 | 10 | vm_opts = __opts__.get('autoscale', {}) 11 | vm_['provider'] = vm_opts['provider'] 12 | for key, value in vm_opts.items(): 13 | vm_[key] = value 14 | vm_['instances'] = data['new_data']['name'] 15 | vm_['instance_id'] = data['new_data']['id'] 16 | vm_list = [] 17 | for key, value in vm_.items(): 18 | if not key.startswith('__') and key != 'state': 19 | vm_list.append({key: value}) 20 | 21 | # Fire off an event to wait for the machine 22 | ret = { 23 | 'autoscale_launch': { 24 | 'runner.cloud.create': vm_list 25 | } 26 | } 27 | elif 'missing node' in data: 28 | 29 | # Fire off an event to remove the minion key 30 | ret = { 31 | 'autoscale_termination': { 32 | 'wheel.key.delete': [ 33 | {'match': data['missing node']}, 34 | ] 35 | } 36 | } 37 | 38 | return ret 39 | --------------------------------------------------------------------------------