unsub 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +13 -0
- data/Readme.md +78 -0
- data/VERSION +1 -0
- data/bin/unsub +3 -0
- data/lib/unsub.rb +1 -0
- data/lib/unsub/event.rb +44 -0
- data/lib/unsub/main.rb +125 -0
- data/lib/unsub/metadata.rb +47 -0
- data/lib/unsub/mjolnir.rb +53 -0
- data/lib/unsub/service.rb +4 -0
- data/lib/unsub/service/base.rb +8 -0
- data/lib/unsub/service/chef.rb +59 -0
- data/lib/unsub/service/chef/config.rb +39 -0
- data/lib/unsub/service/icinga.rb +47 -0
- data/lib/unsub/service/sensu.rb +40 -0
- metadata +185 -0
    
        checksums.yaml
    ADDED
    
    | @@ -0,0 +1,7 @@ | |
| 1 | 
            +
            ---
         | 
| 2 | 
            +
            SHA1:
         | 
| 3 | 
            +
              metadata.gz: c7573632df7e513045df45faf213a4ceb43711a9
         | 
| 4 | 
            +
              data.tar.gz: cb65143b20481d3f8981248a2493dc577d23ce2f
         | 
| 5 | 
            +
            SHA512:
         | 
| 6 | 
            +
              metadata.gz: 4ed4f03b1f05827b24d18a0d0f967dbcbdc2c9b8e868b433323a2aaf0de093abe132be95f7722f0c28ebc37dbb0c5940414b54fe89b9655eec6ca26149c03b0c
         | 
| 7 | 
            +
              data.tar.gz: 2df0645c5915ac9a6227331d53de395fc9667a7981b0f509f3099e1498d0b795fdde425648c8e818fc3d9729e0eff4055a171548da101a7dcb1ceb094f017095
         | 
    
        data/LICENSE
    ADDED
    
    | @@ -0,0 +1,13 @@ | |
| 1 | 
            +
            Copyright (c) 2015 Sean Clemmer and Blue Jeans Network
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Permission to use, copy, modify, and/or distribute this software for any
         | 
| 4 | 
            +
            purpose with or without fee is hereby granted, provided that the above
         | 
| 5 | 
            +
            copyright notice and this permission notice appear in all copies.
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
         | 
| 8 | 
            +
            REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
         | 
| 9 | 
            +
            AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
         | 
| 10 | 
            +
            INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
         | 
| 11 | 
            +
            LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
         | 
| 12 | 
            +
            OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
         | 
| 13 | 
            +
            PERFORMANCE OF THIS SOFTWARE.
         | 
    
        data/Readme.md
    ADDED
    
    | @@ -0,0 +1,78 @@ | |
| 1 | 
            +
            # Unsub 
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            Proof-of-concept autoscaling "polyfill" for Sensu, Icinga & Chef
         | 
| 4 | 
            +
             | 
| 5 | 
            +
            On _launch_:
         | 
| 6 | 
            +
             | 
| 7 | 
            +
            - Icinga: Disable host downtime
         | 
| 8 | 
            +
            - Chef: Remove tag "termianted", add tag "launched"
         | 
| 9 | 
            +
             | 
| 10 | 
            +
            On _terminate_:
         | 
| 11 | 
            +
             | 
| 12 | 
            +
            - Sensu: Remove host's client
         | 
| 13 | 
            +
            - Icinga: Enable host downtime
         | 
| 14 | 
            +
            - Chef: Remove tag "launched", add tag "terminated"
         | 
| 15 | 
            +
             | 
| 16 | 
            +
             | 
| 17 | 
            +
             | 
| 18 | 
            +
            ## Installation, Usage &c.
         | 
| 19 | 
            +
             | 
| 20 | 
            +
            ### Installation
         | 
| 21 | 
            +
             | 
| 22 | 
            +
            Download the appropriate package for your system from the [GitHub releases page](https://github.com/sczizzo/unsub/releases)
         | 
| 23 | 
            +
             | 
| 24 | 
            +
            If you've got a sane Ruby environment around, you can use RubyGems:
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                $ gem install unsub
         | 
| 27 | 
            +
             | 
| 28 | 
            +
            ### Usage
         | 
| 29 | 
            +
             | 
| 30 | 
            +
            Just call for help!
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                $ unsub help
         | 
| 33 | 
            +
                Commands:
         | 
| 34 | 
            +
                  unsub art            # Show the application art
         | 
| 35 | 
            +
                  unsub go ...         # Start the main event loop
         | 
| 36 | 
            +
                  unsub help [COMMAND] # Describe available commands or one specific command
         | 
| 37 | 
            +
                  unsub version        # Show the application version
         | 
| 38 | 
            +
             | 
| 39 | 
            +
            Probably you're most interested in the default command (`go`):
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                $ unsub help go
         | 
| 42 | 
            +
                Usage:
         | 
| 43 | 
            +
                  unsub go \
         | 
| 44 | 
            +
                    -c, --icinga-cmd=ICINGA_CMD \
         | 
| 45 | 
            +
                    -k, --knife-file=KNIFE_FILE \
         | 
| 46 | 
            +
                    -q, --queue-url=QUEUE_URL   \
         | 
| 47 | 
            +
                    -s, --sensu-url=SENSU_URL
         | 
| 48 | 
            +
             | 
| 49 | 
            +
                Options:
         | 
| 50 | 
            +
                  -d, [--database=DATABASE]    # Local database path
         | 
| 51 | 
            +
                                               # Default: .ops1722.db
         | 
| 52 | 
            +
                  -q, --queue-url=QUEUE_URL    # SQS Queue URL
         | 
| 53 | 
            +
                  -c, --icinga-cmd=ICINGA_CMD  # Icinga Command URI
         | 
| 54 | 
            +
                  -s, --sensu-url=SENSU_URL    # Sensu API base URL
         | 
| 55 | 
            +
                  -k, --knife-file=KNIFE_FILE  # Knife file for Chef
         | 
| 56 | 
            +
                                               # Default: $HOME/.chef/knife.rb
         | 
| 57 | 
            +
                  -l, [--log=LOG]              # Log to file instead of STDOUT
         | 
| 58 | 
            +
                  -v, [--debug], [--no-debug]  # Enable DEBUG-level logging
         | 
| 59 | 
            +
                  -z, [--trace], [--no-trace]  # Enable TRACE-level logging
         | 
| 60 | 
            +
             | 
| 61 | 
            +
                Start the main event loop
         | 
| 62 | 
            +
             | 
| 63 | 
            +
            Here's an example:
         | 
| 64 | 
            +
             | 
| 65 | 
            +
                $ unsub \
         | 
| 66 | 
            +
                  -q 'https://sqs.my-region.amazonaws.com/123456789/my-queue' \
         | 
| 67 | 
            +
                  -c 'http://username:password@icinga/cgi-bin/icinga/cmd.cgi' \
         | 
| 68 | 
            +
                  -s 'http://sensu:1234' -d '/var/data/unsub.db'
         | 
| 69 | 
            +
             | 
| 70 | 
            +
             | 
| 71 | 
            +
             | 
| 72 | 
            +
            ## Changelog
         | 
| 73 | 
            +
             | 
| 74 | 
            +
            ### v1.0
         | 
| 75 | 
            +
             | 
| 76 | 
            +
            _In development_
         | 
| 77 | 
            +
             | 
| 78 | 
            +
            - Initial support for Sensu, Icinga & Chef
         | 
    
        data/VERSION
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            0.0.1
         | 
    
        data/bin/unsub
    ADDED
    
    
    
        data/lib/unsub.rb
    ADDED
    
    | @@ -0,0 +1 @@ | |
| 1 | 
            +
            require_relative 'unsub/main'
         | 
    
        data/lib/unsub/event.rb
    ADDED
    
    | @@ -0,0 +1,44 @@ | |
| 1 | 
            +
            module Unsub
         | 
| 2 | 
            +
              class Event
         | 
| 3 | 
            +
                attr_reader :kind, :host
         | 
| 4 | 
            +
             | 
| 5 | 
            +
                def initialize message, db, aws, services
         | 
| 6 | 
            +
                  discover_kind message
         | 
| 7 | 
            +
                  discover_host message, db, aws, services
         | 
| 8 | 
            +
                end
         | 
| 9 | 
            +
             | 
| 10 | 
            +
             | 
| 11 | 
            +
              private
         | 
| 12 | 
            +
             | 
| 13 | 
            +
                def discover_kind message
         | 
| 14 | 
            +
                  @kind = case message['Event']
         | 
| 15 | 
            +
                  when 'autoscaling:EC2_INSTANCE_LAUNCH'
         | 
| 16 | 
            +
                    :launch
         | 
| 17 | 
            +
                  when 'autoscaling:EC2_INSTANCE_TERMINATE'
         | 
| 18 | 
            +
                    :terminate
         | 
| 19 | 
            +
                  when 'autoscaling:EC2_INSTANCE_LAUNCH_ERROR'
         | 
| 20 | 
            +
                    :launch_error
         | 
| 21 | 
            +
                  when 'autoscaling:EC2_INSTANCE_TERMINATE_ERROR'
         | 
| 22 | 
            +
                    :terminate_error
         | 
| 23 | 
            +
                  end
         | 
| 24 | 
            +
                end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
             | 
| 27 | 
            +
                def discover_host message, db, aws, services
         | 
| 28 | 
            +
                  id = message['EC2InstanceId']
         | 
| 29 | 
            +
             | 
| 30 | 
            +
                  @host = unless id.nil? || id.empty?
         | 
| 31 | 
            +
                    is = aws[:ec2].describe_instances instance_ids: [ id ]
         | 
| 32 | 
            +
                    ip = is.reservations.first.instances.first.private_ip_address
         | 
| 33 | 
            +
             | 
| 34 | 
            +
                    host = { id: id, ip: ip }
         | 
| 35 | 
            +
                    cached_host = db[id] || {}
         | 
| 36 | 
            +
                    cached_host.each { |k,v| host[k] = v if host[k].nil? && v }
         | 
| 37 | 
            +
                    services.each { |_, service| service.extend_host host }
         | 
| 38 | 
            +
             | 
| 39 | 
            +
                    db.set! id, host
         | 
| 40 | 
            +
                    host
         | 
| 41 | 
            +
                  end
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
              end
         | 
| 44 | 
            +
            end
         | 
    
        data/lib/unsub/main.rb
    ADDED
    
    | @@ -0,0 +1,125 @@ | |
| 1 | 
            +
            require 'net/http'
         | 
| 2 | 
            +
            require 'socket'
         | 
| 3 | 
            +
            require 'json'
         | 
| 4 | 
            +
            require 'date'
         | 
| 5 | 
            +
            require 'time'
         | 
| 6 | 
            +
            require 'uri'
         | 
| 7 | 
            +
             | 
| 8 | 
            +
            require 'daybreak'
         | 
| 9 | 
            +
            require 'aws-sdk'
         | 
| 10 | 
            +
             | 
| 11 | 
            +
            require_relative 'mjolnir'
         | 
| 12 | 
            +
            require_relative 'metadata'
         | 
| 13 | 
            +
            require_relative 'service'
         | 
| 14 | 
            +
            require_relative 'event'
         | 
| 15 | 
            +
             | 
| 16 | 
            +
             | 
| 17 | 
            +
            Thread.abort_on_exception = false
         | 
| 18 | 
            +
             | 
| 19 | 
            +
             | 
| 20 | 
            +
             | 
| 21 | 
            +
            module Unsub
         | 
| 22 | 
            +
              class Main < Mjolnir
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                default_command :go
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                KNIFE = [
         | 
| 27 | 
            +
                  File.join(ENV['HOME'], '.chef', 'knife.rb'),
         | 
| 28 | 
            +
                  File.join('/etc', 'chef', 'knife.rb'),
         | 
| 29 | 
            +
                  ENV['KNIFE']
         | 
| 30 | 
            +
                ].select { |f| f && File.exists?(f) }.shift
         | 
| 31 | 
            +
             | 
| 32 | 
            +
             | 
| 33 | 
            +
                desc 'version', 'Show the application version'
         | 
| 34 | 
            +
                def version
         | 
| 35 | 
            +
                  puts VERSION
         | 
| 36 | 
            +
                end
         | 
| 37 | 
            +
             | 
| 38 | 
            +
             | 
| 39 | 
            +
                desc 'art', 'Show the application art'
         | 
| 40 | 
            +
                def art
         | 
| 41 | 
            +
                  puts "\n%s\n" % ART
         | 
| 42 | 
            +
                end
         | 
| 43 | 
            +
             | 
| 44 | 
            +
             | 
| 45 | 
            +
                desc 'go', 'Start the main event loop'
         | 
| 46 | 
            +
                option :database, \
         | 
| 47 | 
            +
                  type: :string,
         | 
| 48 | 
            +
                  aliases: %w[ -d ],
         | 
| 49 | 
            +
                  desc: 'Local database path',
         | 
| 50 | 
            +
                  default: '.ops1722.db'
         | 
| 51 | 
            +
                option :queue_url, \
         | 
| 52 | 
            +
                  type: :string,
         | 
| 53 | 
            +
                  aliases: %w[ -q ],
         | 
| 54 | 
            +
                  desc: 'SQS Queue URL',
         | 
| 55 | 
            +
                  required: true
         | 
| 56 | 
            +
                option :icinga_cmd, \
         | 
| 57 | 
            +
                  type: :string,
         | 
| 58 | 
            +
                  aliases: %w[ -c ],
         | 
| 59 | 
            +
                  desc: 'Icinga Command URI',
         | 
| 60 | 
            +
                  required: true
         | 
| 61 | 
            +
                option :sensu_url, \
         | 
| 62 | 
            +
                  type: :string,
         | 
| 63 | 
            +
                  aliases: %w[ -s ],
         | 
| 64 | 
            +
                  desc: 'Sensu API base URL',
         | 
| 65 | 
            +
                  required: true
         | 
| 66 | 
            +
                option :knife_file, \
         | 
| 67 | 
            +
                  type: :string,
         | 
| 68 | 
            +
                  aliases: %w[ -k ],
         | 
| 69 | 
            +
                  desc: 'Knife file for Chef',
         | 
| 70 | 
            +
                  default: KNIFE,
         | 
| 71 | 
            +
                  required: true
         | 
| 72 | 
            +
                include_common_options
         | 
| 73 | 
            +
                def go
         | 
| 74 | 
            +
                  db = Daybreak::DB.new options.database
         | 
| 75 | 
            +
             | 
| 76 | 
            +
                  aws = {
         | 
| 77 | 
            +
                    ec2: Aws::EC2::Client.new,
         | 
| 78 | 
            +
                    sqs: Aws::SQS::Client.new
         | 
| 79 | 
            +
                  }
         | 
| 80 | 
            +
             | 
| 81 | 
            +
                  services = {
         | 
| 82 | 
            +
                    icinga: Service::Icinga.new(options.icinga_cmd, log),
         | 
| 83 | 
            +
                    sensu: Service::Sensu.new(options.sensu_url, log),
         | 
| 84 | 
            +
                    chef: Service::Chef.new(options.knife_file, log)
         | 
| 85 | 
            +
                  }
         | 
| 86 | 
            +
             | 
| 87 | 
            +
                  log.info service: 'main', event: 'hello', \
         | 
| 88 | 
            +
                    db: db, aws: aws, services: services
         | 
| 89 | 
            +
             | 
| 90 | 
            +
                  loop do
         | 
| 91 | 
            +
                    response = aws[:sqs].receive_message queue_url: options.queue_url
         | 
| 92 | 
            +
             | 
| 93 | 
            +
                    response.messages.each do |raw|
         | 
| 94 | 
            +
                      receipt = raw.receipt_handle
         | 
| 95 | 
            +
                      body    = JSON.parse raw.body
         | 
| 96 | 
            +
                      message = JSON.parse body['Message']
         | 
| 97 | 
            +
                      event   = Event.new message, db, aws, services
         | 
| 98 | 
            +
             | 
| 99 | 
            +
                      success = case event.kind
         | 
| 100 | 
            +
                      when :launch
         | 
| 101 | 
            +
                        s1 = services[:icinga].disable_downtime event.host
         | 
| 102 | 
            +
                        s2 = services[:chef].remove_tag event.host, 'terminated'
         | 
| 103 | 
            +
                        s3 = services[:chef].add_tag event.host, 'launched'
         | 
| 104 | 
            +
                        s1 && s2 && s3
         | 
| 105 | 
            +
                      when :terminate
         | 
| 106 | 
            +
                        s1 = services[:sensu].delete_client event.host
         | 
| 107 | 
            +
                        s2 = services[:icinga].enable_downtime event.host
         | 
| 108 | 
            +
                        s3 = services[:chef].remove_tag event.host, 'launched'
         | 
| 109 | 
            +
                        s4 = services[:chef].add_tag event.host, 'terminated'
         | 
| 110 | 
            +
                        db.delete! event.host[:id]
         | 
| 111 | 
            +
                        s1 && s2 && s3 && s4
         | 
| 112 | 
            +
                      end
         | 
| 113 | 
            +
             | 
| 114 | 
            +
                      aws[:sqs].delete_message \
         | 
| 115 | 
            +
                        queue_url: options.queue_url,
         | 
| 116 | 
            +
                        receipt_handle: receipt
         | 
| 117 | 
            +
             | 
| 118 | 
            +
                      log.info service: 'main', event: 'processed', \
         | 
| 119 | 
            +
                        message: message, receipt: receipt, success: success
         | 
| 120 | 
            +
                    end
         | 
| 121 | 
            +
                  end
         | 
| 122 | 
            +
                end
         | 
| 123 | 
            +
             | 
| 124 | 
            +
              end
         | 
| 125 | 
            +
            end
         | 
| @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            module Unsub
         | 
| 2 | 
            +
              # Officially, it's "unsub"
         | 
| 3 | 
            +
              NAME     = 'unsub'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
              # A quick summary for use in the command-line interface
         | 
| 6 | 
            +
              SUMMARY  = %q.Autoscaling "polyfill" for Sensu, Icinga & Chef.
         | 
| 7 | 
            +
             | 
| 8 | 
            +
              # Take credit for your work
         | 
| 9 | 
            +
              AUTHOR   = 'Sean Clemmer'
         | 
| 10 | 
            +
             | 
| 11 | 
            +
              # Take responsibility for your work
         | 
| 12 | 
            +
              EMAIL    = 'sczizzo@gmail.com'
         | 
| 13 | 
            +
             | 
| 14 | 
            +
              # Like the MIT license, but even simpler
         | 
| 15 | 
            +
              LICENSE  = 'ISC'
         | 
| 16 | 
            +
             | 
| 17 | 
            +
              # Where you should look first
         | 
| 18 | 
            +
              HOMEPAGE = 'https://github.com/sczizzo/unsub'
         | 
| 19 | 
            +
             | 
| 20 | 
            +
              # Project root
         | 
| 21 | 
            +
              ROOT = File.join File.dirname(__FILE__), '..', '..'
         | 
| 22 | 
            +
             | 
| 23 | 
            +
              # Pull the project version out of the VERSION file
         | 
| 24 | 
            +
              VERSION = File.read(File.join(ROOT, 'VERSION')).strip
         | 
| 25 | 
            +
             | 
| 26 | 
            +
              # Bundled extensions
         | 
| 27 | 
            +
              TRAVELING_RUBY_VERSION = '20150517-2.2.2'
         | 
| 28 | 
            +
              THIN_VERSION = '1.6.3'
         | 
| 29 | 
            +
              FFI_VERSION = '1.9.6'
         | 
| 30 | 
            +
              EM_VERSION = '1.0.4'
         | 
| 31 | 
            +
             | 
| 32 | 
            +
              # Big money
         | 
| 33 | 
            +
              ART = <<-'EOART'
         | 
| 34 | 
            +
                                                                     ,---,
         | 
| 35 | 
            +
                           ,--,      ,---,                     ,--,,---.'|
         | 
| 36 | 
            +
                         ,'_ /|  ,-+-. /  | .--.--.          ,'_ /||   | :
         | 
| 37 | 
            +
                    .--. |  | : ,--.'|'   |/  /    '    .--. |  | ::   : :
         | 
| 38 | 
            +
                  ,'_ /| :  . ||   |  ,"' |  :  /`./  ,'_ /| :  . |:     |,-.
         | 
| 39 | 
            +
                  |  ' | |  . .|   | /  | |  :  ;_    |  ' | |  . .|   : '  |
         | 
| 40 | 
            +
                  |  | ' |  | ||   | |  | |\  \    `. |  | ' |  | ||   |  / :
         | 
| 41 | 
            +
                  :  | : ;  ; ||   | |  |/  `----.   \:  | : ;  ; |'   : |: |
         | 
| 42 | 
            +
                  '  :  `--'   \   | |--'  /  /`--'  /'  :  `--'   \   | '/ :
         | 
| 43 | 
            +
                  :  ,      .-./   |/     '--'.     / :  ,      .-./   :    |
         | 
| 44 | 
            +
                   `--`----'   '---'        `--'---'   `--`----'   /    \  /
         | 
| 45 | 
            +
                                                                   `-'----'
         | 
| 46 | 
            +
              EOART
         | 
| 47 | 
            +
            end
         | 
| @@ -0,0 +1,53 @@ | |
| 1 | 
            +
            require 'slog'
         | 
| 2 | 
            +
            require 'thor'
         | 
| 3 | 
            +
             | 
| 4 | 
            +
             | 
| 5 | 
            +
            module Unsub
         | 
| 6 | 
            +
             | 
| 7 | 
            +
              # Thor's hammer! Like Thor with better logging
         | 
| 8 | 
            +
              class Mjolnir < ::Thor
         | 
| 9 | 
            +
             | 
| 10 | 
            +
                # Common options for Thor commands
         | 
| 11 | 
            +
                COMMON_OPTIONS = {
         | 
| 12 | 
            +
                  log: {
         | 
| 13 | 
            +
                    type: :string,
         | 
| 14 | 
            +
                    aliases: %w[ -l ],
         | 
| 15 | 
            +
                    desc: 'Log to file instead of STDOUT',
         | 
| 16 | 
            +
                    default: ENV['UNSUB_LOG'] || nil
         | 
| 17 | 
            +
                  },
         | 
| 18 | 
            +
                  debug: {
         | 
| 19 | 
            +
                    type: :boolean,
         | 
| 20 | 
            +
                    aliases: %w[ -v ],
         | 
| 21 | 
            +
                    desc: 'Enable DEBUG-level logging',
         | 
| 22 | 
            +
                    default: ENV['UNSUB_DEBUG'] || false
         | 
| 23 | 
            +
                  },
         | 
| 24 | 
            +
                  trace: {
         | 
| 25 | 
            +
                    type: :boolean,
         | 
| 26 | 
            +
                    aliases: %w[ -z ],
         | 
| 27 | 
            +
                    desc: 'Enable TRACE-level logging',
         | 
| 28 | 
            +
                    default: ENV['UNSUB_TRACE'] || false
         | 
| 29 | 
            +
                  }
         | 
| 30 | 
            +
                }
         | 
| 31 | 
            +
             | 
| 32 | 
            +
                # Decorate Thor commands with the options above
         | 
| 33 | 
            +
                def self.include_common_options
         | 
| 34 | 
            +
                  COMMON_OPTIONS.each do |name, spec|
         | 
| 35 | 
            +
                    option name, spec
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
             | 
| 39 | 
            +
             | 
| 40 | 
            +
                no_commands do
         | 
| 41 | 
            +
             | 
| 42 | 
            +
                  # Construct a Logger given the command-line options
         | 
| 43 | 
            +
                  def log
         | 
| 44 | 
            +
                    return @logger if defined? @logger
         | 
| 45 | 
            +
                    @logger = Slog.new out: (options.log || $stdout), prettify: false
         | 
| 46 | 
            +
                    @logger.level = :debug if options.debug?
         | 
| 47 | 
            +
                    @logger.level = :trace if options.trace?
         | 
| 48 | 
            +
                    @logger
         | 
| 49 | 
            +
                  end
         | 
| 50 | 
            +
             | 
| 51 | 
            +
                end
         | 
| 52 | 
            +
              end
         | 
| 53 | 
            +
            end
         | 
| @@ -0,0 +1,59 @@ | |
| 1 | 
            +
            require 'chef-api'
         | 
| 2 | 
            +
             | 
| 3 | 
            +
            require_relative 'chef/config'
         | 
| 4 | 
            +
             | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Unsub
         | 
| 7 | 
            +
              module Service
         | 
| 8 | 
            +
                class Chef < Base
         | 
| 9 | 
            +
                  attr_reader :knife, :api
         | 
| 10 | 
            +
             | 
| 11 | 
            +
                  def initialize knife, log
         | 
| 12 | 
            +
                    @log   = log
         | 
| 13 | 
            +
                    @knife = Config.new knife
         | 
| 14 | 
            +
                    @api   = ChefAPI::Connection.new \
         | 
| 15 | 
            +
                      endpoint: @knife.chef_server_url,
         | 
| 16 | 
            +
                      client: @knife.node_name,
         | 
| 17 | 
            +
                      key: @knife.client_key
         | 
| 18 | 
            +
                  end
         | 
| 19 | 
            +
             | 
| 20 | 
            +
             | 
| 21 | 
            +
                  def extend_host host
         | 
| 22 | 
            +
                    name = if ip = host[:ip]
         | 
| 23 | 
            +
                      api.search.query(:node, 'ipaddress:"%s"' % ip).rows.shift.name rescue nil
         | 
| 24 | 
            +
                    end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    old_host = host.dup ; host.merge! chef_name: name if name
         | 
| 27 | 
            +
                    log.info service: 'chef', event: 'extend_host', old_host: old_host, host: host
         | 
| 28 | 
            +
                    host
         | 
| 29 | 
            +
                  end
         | 
| 30 | 
            +
             | 
| 31 | 
            +
             | 
| 32 | 
            +
                  def add_tag host, tag
         | 
| 33 | 
            +
                    success = if name = host[:chef_name]
         | 
| 34 | 
            +
                      node = api.nodes.fetch name
         | 
| 35 | 
            +
                      node.normal['tags'] ||= []
         | 
| 36 | 
            +
                      node.normal['tags'] << tag
         | 
| 37 | 
            +
                      node.normal['tags'].uniq!
         | 
| 38 | 
            +
                      !!node.save
         | 
| 39 | 
            +
                    end
         | 
| 40 | 
            +
             | 
| 41 | 
            +
                    log.info service: 'chef', event: 'add_tag', host: host, tag: tag, success: success
         | 
| 42 | 
            +
                    success
         | 
| 43 | 
            +
                  end
         | 
| 44 | 
            +
             | 
| 45 | 
            +
             | 
| 46 | 
            +
                  def remove_tag host, tag
         | 
| 47 | 
            +
                    success = if name = host[:chef_name]
         | 
| 48 | 
            +
                      node = api.nodes.fetch name
         | 
| 49 | 
            +
                      node.normal['tags'] ||= []
         | 
| 50 | 
            +
                      node.normal['tags'].delete tag
         | 
| 51 | 
            +
                      !!node.save
         | 
| 52 | 
            +
                    end
         | 
| 53 | 
            +
             | 
| 54 | 
            +
                    log.info service: 'chef', event: 'remove_tag', host: host, tag: tag, success: success
         | 
| 55 | 
            +
                    success
         | 
| 56 | 
            +
                  end
         | 
| 57 | 
            +
                end
         | 
| 58 | 
            +
              end
         | 
| 59 | 
            +
            end
         | 
| @@ -0,0 +1,39 @@ | |
| 1 | 
            +
            #
         | 
| 2 | 
            +
            # Cribbed from Ridley: https://github.com/reset/ridley/blob/master/lib/ridley/chef/config.rb
         | 
| 3 | 
            +
            #
         | 
| 4 | 
            +
            require 'buff/config/ruby'
         | 
| 5 | 
            +
             | 
| 6 | 
            +
            module Unsub
         | 
| 7 | 
            +
              module Service
         | 
| 8 | 
            +
                class Chef < Base
         | 
| 9 | 
            +
                  class Config < Buff::Config::Ruby
         | 
| 10 | 
            +
                    class << self
         | 
| 11 | 
            +
                      def location
         | 
| 12 | 
            +
                        possibles = []
         | 
| 13 | 
            +
                        possibles << ENV['CHEF_CONFIG'] if ENV['CHEF_CONFIG']
         | 
| 14 | 
            +
                        possibles << File.join(ENV['KNIFE_HOME'], 'knife.rb') if ENV['KNIFE_HOME']
         | 
| 15 | 
            +
                        possibles << File.join(Dir.pwd, 'knife.rb')
         | 
| 16 | 
            +
                        possibles << File.join(ENV['HOME'], '.chef', 'knife.rb') if ENV['HOME']
         | 
| 17 | 
            +
                        possibles.compact!
         | 
| 18 | 
            +
             | 
| 19 | 
            +
                        location = possibles.find { |loc| File.exists?(File.expand_path(loc)) }
         | 
| 20 | 
            +
                        File.expand_path(location) unless location.nil?
         | 
| 21 | 
            +
                      end
         | 
| 22 | 
            +
                    end
         | 
| 23 | 
            +
             | 
| 24 | 
            +
                    set_assignment_mode :carefree
         | 
| 25 | 
            +
             | 
| 26 | 
            +
                    attribute :node_name,
         | 
| 27 | 
            +
                      default: -> { Socket.gethostname }
         | 
| 28 | 
            +
                    attribute :chef_server_url,
         | 
| 29 | 
            +
                      default: 'http://localhost:4000'
         | 
| 30 | 
            +
                    attribute :client_key,
         | 
| 31 | 
            +
                      default: -> { '/etc/chef/client.pem' }
         | 
| 32 | 
            +
             | 
| 33 | 
            +
                    def initialize(path, options = {})
         | 
| 34 | 
            +
                      super(path || self.class.location, options)
         | 
| 35 | 
            +
                    end
         | 
| 36 | 
            +
                  end
         | 
| 37 | 
            +
                end
         | 
| 38 | 
            +
              end
         | 
| 39 | 
            +
            end
         | 
| @@ -0,0 +1,47 @@ | |
| 1 | 
            +
            module Unsub
         | 
| 2 | 
            +
              module Service
         | 
| 3 | 
            +
                class Icinga < Base
         | 
| 4 | 
            +
                  attr_reader :uri, :http
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def initialize cmd_url, log
         | 
| 7 | 
            +
                    @uri  = URI.parse cmd_url
         | 
| 8 | 
            +
                    @http = Net::HTTP.new uri.host, uri.port
         | 
| 9 | 
            +
                    @log  = log
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def enable_downtime host
         | 
| 14 | 
            +
                    success = cgi_command 28, host # CMD_ENABLE_HOST_SVC_NOTIFICATIONS
         | 
| 15 | 
            +
                    log.info service: 'icinga', event: 'enable_downtime', host: host, success: success
         | 
| 16 | 
            +
                    success
         | 
| 17 | 
            +
                  end
         | 
| 18 | 
            +
             | 
| 19 | 
            +
             | 
| 20 | 
            +
                  def disable_downtime host
         | 
| 21 | 
            +
                    success = cgi_command 29, host # CMD_DISABLE_HOST_SVC_NOTIFICATIONS
         | 
| 22 | 
            +
                    log.info service: 'icinga', event: 'disable_downtime', host: host, success: success
         | 
| 23 | 
            +
                    success
         | 
| 24 | 
            +
                  end
         | 
| 25 | 
            +
             | 
| 26 | 
            +
             | 
| 27 | 
            +
                private
         | 
| 28 | 
            +
             | 
| 29 | 
            +
                  def cgi_command command, host, opts={}
         | 
| 30 | 
            +
                    params = {
         | 
| 31 | 
            +
                      cmd_typ: command,
         | 
| 32 | 
            +
                      cmd_mod: 2,
         | 
| 33 | 
            +
                      host: (host[:sensu_name] || host[:chef_name]),
         | 
| 34 | 
            +
                      btnSubmit: 'Commit'
         | 
| 35 | 
            +
                    }.merge opts
         | 
| 36 | 
            +
             | 
| 37 | 
            +
                    if name = params[:host]
         | 
| 38 | 
            +
                      request  = Net::HTTP::Post.new uri.request_uri
         | 
| 39 | 
            +
                      request.basic_auth uri.user, uri.password
         | 
| 40 | 
            +
                      request.set_form_data params
         | 
| 41 | 
            +
                      response = http.request request
         | 
| 42 | 
            +
                      response.kind_of? Net::HTTPSuccess
         | 
| 43 | 
            +
                    end
         | 
| 44 | 
            +
                  end
         | 
| 45 | 
            +
                end
         | 
| 46 | 
            +
              end
         | 
| 47 | 
            +
            end
         | 
| @@ -0,0 +1,40 @@ | |
| 1 | 
            +
            module Unsub
         | 
| 2 | 
            +
              module Service
         | 
| 3 | 
            +
                class Sensu < Base
         | 
| 4 | 
            +
                  attr_reader :http
         | 
| 5 | 
            +
             | 
| 6 | 
            +
                  def initialize url, log
         | 
| 7 | 
            +
                    uri   = URI.parse url
         | 
| 8 | 
            +
                    @http = Net::HTTP.new uri.host, uri.port
         | 
| 9 | 
            +
                    @log  = log
         | 
| 10 | 
            +
                  end
         | 
| 11 | 
            +
             | 
| 12 | 
            +
             | 
| 13 | 
            +
                  def extend_host host
         | 
| 14 | 
            +
                    name = if ip = host[:ip]
         | 
| 15 | 
            +
                      get_clients = Net::HTTP::Get.new '/clients?limit=1000000'
         | 
| 16 | 
            +
                      response = http.request get_clients
         | 
| 17 | 
            +
                      clients  = JSON.parse response.body, symbolize_names: true
         | 
| 18 | 
            +
                      client   = clients.select { |c| c[:address] == ip }.shift
         | 
| 19 | 
            +
                      client.nil? ? nil : client[:name]
         | 
| 20 | 
            +
                    end
         | 
| 21 | 
            +
             | 
| 22 | 
            +
                    old_host = host.dup ; host.merge! sensu_name: name if name
         | 
| 23 | 
            +
                    log.info service: 'sensu', event: 'extend_host', old_host: old_host, host: host
         | 
| 24 | 
            +
                    host
         | 
| 25 | 
            +
                  end
         | 
| 26 | 
            +
             | 
| 27 | 
            +
             | 
| 28 | 
            +
                  def delete_client host
         | 
| 29 | 
            +
                    success = if name = host[:sensu_name]
         | 
| 30 | 
            +
                      delete_client = Net::HTTP::Delete.new '/clients/%s' % name
         | 
| 31 | 
            +
                      response = http.request delete_client
         | 
| 32 | 
            +
                      response.kind_of? Net::HTTPSuccess
         | 
| 33 | 
            +
                    end
         | 
| 34 | 
            +
             | 
| 35 | 
            +
                    log.info service: 'sensu', event: 'delete_client', host: host, success: success
         | 
| 36 | 
            +
                    success
         | 
| 37 | 
            +
                  end
         | 
| 38 | 
            +
                end
         | 
| 39 | 
            +
              end
         | 
| 40 | 
            +
            end
         | 
    
        metadata
    ADDED
    
    | @@ -0,0 +1,185 @@ | |
| 1 | 
            +
            --- !ruby/object:Gem::Specification
         | 
| 2 | 
            +
            name: unsub
         | 
| 3 | 
            +
            version: !ruby/object:Gem::Version
         | 
| 4 | 
            +
              version: 0.0.1
         | 
| 5 | 
            +
            platform: ruby
         | 
| 6 | 
            +
            authors:
         | 
| 7 | 
            +
            - Sean Clemmer
         | 
| 8 | 
            +
            autorequire: 
         | 
| 9 | 
            +
            bindir: bin
         | 
| 10 | 
            +
            cert_chain: []
         | 
| 11 | 
            +
            date: 2015-07-07 00:00:00.000000000 Z
         | 
| 12 | 
            +
            dependencies:
         | 
| 13 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 14 | 
            +
              name: thor
         | 
| 15 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 16 | 
            +
                requirements:
         | 
| 17 | 
            +
                - - "~>"
         | 
| 18 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 19 | 
            +
                    version: '0'
         | 
| 20 | 
            +
              type: :runtime
         | 
| 21 | 
            +
              prerelease: false
         | 
| 22 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 23 | 
            +
                requirements:
         | 
| 24 | 
            +
                - - "~>"
         | 
| 25 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 26 | 
            +
                    version: '0'
         | 
| 27 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 28 | 
            +
              name: slog
         | 
| 29 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 30 | 
            +
                requirements:
         | 
| 31 | 
            +
                - - "~>"
         | 
| 32 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 33 | 
            +
                    version: '1'
         | 
| 34 | 
            +
              type: :runtime
         | 
| 35 | 
            +
              prerelease: false
         | 
| 36 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 37 | 
            +
                requirements:
         | 
| 38 | 
            +
                - - "~>"
         | 
| 39 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 40 | 
            +
                    version: '1'
         | 
| 41 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 42 | 
            +
              name: aws-sdk
         | 
| 43 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 44 | 
            +
                requirements:
         | 
| 45 | 
            +
                - - ">="
         | 
| 46 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 47 | 
            +
                    version: '0'
         | 
| 48 | 
            +
              type: :runtime
         | 
| 49 | 
            +
              prerelease: false
         | 
| 50 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 51 | 
            +
                requirements:
         | 
| 52 | 
            +
                - - ">="
         | 
| 53 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 54 | 
            +
                    version: '0'
         | 
| 55 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 56 | 
            +
              name: daybreak
         | 
| 57 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 58 | 
            +
                requirements:
         | 
| 59 | 
            +
                - - ">="
         | 
| 60 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 61 | 
            +
                    version: '0'
         | 
| 62 | 
            +
              type: :runtime
         | 
| 63 | 
            +
              prerelease: false
         | 
| 64 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 65 | 
            +
                requirements:
         | 
| 66 | 
            +
                - - ">="
         | 
| 67 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 68 | 
            +
                    version: '0'
         | 
| 69 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 70 | 
            +
              name: chef-api
         | 
| 71 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 72 | 
            +
                requirements:
         | 
| 73 | 
            +
                - - ">="
         | 
| 74 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 75 | 
            +
                    version: '0'
         | 
| 76 | 
            +
              type: :runtime
         | 
| 77 | 
            +
              prerelease: false
         | 
| 78 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 79 | 
            +
                requirements:
         | 
| 80 | 
            +
                - - ">="
         | 
| 81 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 82 | 
            +
                    version: '0'
         | 
| 83 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 84 | 
            +
              name: buff-config
         | 
| 85 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 86 | 
            +
                requirements:
         | 
| 87 | 
            +
                - - ">="
         | 
| 88 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 89 | 
            +
                    version: '0'
         | 
| 90 | 
            +
              type: :runtime
         | 
| 91 | 
            +
              prerelease: false
         | 
| 92 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 93 | 
            +
                requirements:
         | 
| 94 | 
            +
                - - ">="
         | 
| 95 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 96 | 
            +
                    version: '0'
         | 
| 97 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 98 | 
            +
              name: eventmachine
         | 
| 99 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 100 | 
            +
                requirements:
         | 
| 101 | 
            +
                - - '='
         | 
| 102 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 103 | 
            +
                    version: 1.0.4
         | 
| 104 | 
            +
              type: :runtime
         | 
| 105 | 
            +
              prerelease: false
         | 
| 106 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 107 | 
            +
                requirements:
         | 
| 108 | 
            +
                - - '='
         | 
| 109 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 110 | 
            +
                    version: 1.0.4
         | 
| 111 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 112 | 
            +
              name: thin
         | 
| 113 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 114 | 
            +
                requirements:
         | 
| 115 | 
            +
                - - '='
         | 
| 116 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 117 | 
            +
                    version: 1.6.3
         | 
| 118 | 
            +
              type: :runtime
         | 
| 119 | 
            +
              prerelease: false
         | 
| 120 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 121 | 
            +
                requirements:
         | 
| 122 | 
            +
                - - '='
         | 
| 123 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 124 | 
            +
                    version: 1.6.3
         | 
| 125 | 
            +
            - !ruby/object:Gem::Dependency
         | 
| 126 | 
            +
              name: ffi
         | 
| 127 | 
            +
              requirement: !ruby/object:Gem::Requirement
         | 
| 128 | 
            +
                requirements:
         | 
| 129 | 
            +
                - - '='
         | 
| 130 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 131 | 
            +
                    version: 1.9.6
         | 
| 132 | 
            +
              type: :runtime
         | 
| 133 | 
            +
              prerelease: false
         | 
| 134 | 
            +
              version_requirements: !ruby/object:Gem::Requirement
         | 
| 135 | 
            +
                requirements:
         | 
| 136 | 
            +
                - - '='
         | 
| 137 | 
            +
                  - !ruby/object:Gem::Version
         | 
| 138 | 
            +
                    version: 1.9.6
         | 
| 139 | 
            +
            description: Autoscaling "polyfill" for Sensu, Icinga & Chef.
         | 
| 140 | 
            +
            email: sczizzo@gmail.com
         | 
| 141 | 
            +
            executables:
         | 
| 142 | 
            +
            - unsub
         | 
| 143 | 
            +
            extensions: []
         | 
| 144 | 
            +
            extra_rdoc_files: []
         | 
| 145 | 
            +
            files:
         | 
| 146 | 
            +
            - LICENSE
         | 
| 147 | 
            +
            - Readme.md
         | 
| 148 | 
            +
            - VERSION
         | 
| 149 | 
            +
            - bin/unsub
         | 
| 150 | 
            +
            - lib/unsub.rb
         | 
| 151 | 
            +
            - lib/unsub/event.rb
         | 
| 152 | 
            +
            - lib/unsub/main.rb
         | 
| 153 | 
            +
            - lib/unsub/metadata.rb
         | 
| 154 | 
            +
            - lib/unsub/mjolnir.rb
         | 
| 155 | 
            +
            - lib/unsub/service.rb
         | 
| 156 | 
            +
            - lib/unsub/service/base.rb
         | 
| 157 | 
            +
            - lib/unsub/service/chef.rb
         | 
| 158 | 
            +
            - lib/unsub/service/chef/config.rb
         | 
| 159 | 
            +
            - lib/unsub/service/icinga.rb
         | 
| 160 | 
            +
            - lib/unsub/service/sensu.rb
         | 
| 161 | 
            +
            homepage: https://github.com/sczizzo/unsub
         | 
| 162 | 
            +
            licenses:
         | 
| 163 | 
            +
            - ISC
         | 
| 164 | 
            +
            metadata: {}
         | 
| 165 | 
            +
            post_install_message: 
         | 
| 166 | 
            +
            rdoc_options: []
         | 
| 167 | 
            +
            require_paths:
         | 
| 168 | 
            +
            - lib
         | 
| 169 | 
            +
            required_ruby_version: !ruby/object:Gem::Requirement
         | 
| 170 | 
            +
              requirements:
         | 
| 171 | 
            +
              - - ">="
         | 
| 172 | 
            +
                - !ruby/object:Gem::Version
         | 
| 173 | 
            +
                  version: '0'
         | 
| 174 | 
            +
            required_rubygems_version: !ruby/object:Gem::Requirement
         | 
| 175 | 
            +
              requirements:
         | 
| 176 | 
            +
              - - ">="
         | 
| 177 | 
            +
                - !ruby/object:Gem::Version
         | 
| 178 | 
            +
                  version: '0'
         | 
| 179 | 
            +
            requirements: []
         | 
| 180 | 
            +
            rubyforge_project: 
         | 
| 181 | 
            +
            rubygems_version: 2.4.5
         | 
| 182 | 
            +
            signing_key: 
         | 
| 183 | 
            +
            specification_version: 4
         | 
| 184 | 
            +
            summary: Autoscaling "polyfill" for Sensu, Icinga & Chef
         | 
| 185 | 
            +
            test_files: []
         |