├── Gemfile ├── lib └── aws │ ├── instmd │ └── version.rb │ └── instmd.rb ├── bin └── awsinstmd ├── Rakefile ├── aws-instmd.gemspec ├── LICENSE.txt └── README.md /Gemfile: -------------------------------------------------------------------------------- 1 | source 'https://rubygems.org' 2 | gemspec 3 | -------------------------------------------------------------------------------- /lib/aws/instmd/version.rb: -------------------------------------------------------------------------------- 1 | module AWS 2 | class InstMD 3 | VERSION = '0.1.1' 4 | end 5 | end 6 | -------------------------------------------------------------------------------- /bin/awsinstmd: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require 'aws/instmd' 4 | require 'json' 5 | 6 | puts JSON::pretty_generate AWS::InstMD.to_hash 7 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | require 'bundler/gem_tasks' 2 | require 'rake/testtask' 3 | 4 | Rake::TestTask.new do |t| 5 | t.libs = %w[lib test] 6 | t.test_files = FileList['test/**/test*.rb'] 7 | end 8 | 9 | desc 'Run tests' 10 | task :default => :test 11 | -------------------------------------------------------------------------------- /aws-instmd.gemspec: -------------------------------------------------------------------------------- 1 | # coding: utf-8 2 | lib = File.expand_path '../lib', __FILE__ 3 | $LOAD_PATH.unshift lib unless $LOAD_PATH.include? lib 4 | 5 | require 'aws/instmd/version' 6 | 7 | Gem::Specification.new do |spec| 8 | spec.name = 'aws-instmd' 9 | spec.version = AWS::InstMD::VERSION 10 | spec.authors = ['Pierre Carrier'] 11 | spec.email = ['pierre@gcarrier.fr'] 12 | spec.description = 'AWS instance metadata client' 13 | spec.summary = 'Query the entire 169.254.169.254 tree in seconds' 14 | spec.homepage = 'https://github.com/airbnb/gem-aws-instmd' 15 | spec.license = 'MIT' 16 | 17 | spec.files = `git ls-files`.split $/ 18 | spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename f } 19 | spec.test_files = spec.files.grep %r{^(test|spec|features)/} 20 | spec.require_paths = %w[lib] 21 | 22 | spec.add_dependency 'json' 23 | 24 | spec.add_development_dependency 'bundler', '~> 1.3' 25 | spec.add_development_dependency 'rake' 26 | end 27 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2014 Airbnb, Inc. 2 | 3 | MIT License 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining 6 | a copy of this software and associated documentation files (the 7 | "Software"), to deal in the Software without restriction, including 8 | without limitation the rights to use, copy, modify, merge, publish, 9 | distribute, sublicense, and/or sell copies of the Software, and to 10 | permit persons to whom the Software is furnished to do so, subject to 11 | the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be 14 | included in all copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 17 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 18 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 19 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 20 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 21 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 22 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # AWS::InstMD 2 | 3 | AWS::InstMD exposes the instance metadata information through a Ruby API. 4 | 5 | **As-is:** This project is not actively maintained or supported. 6 | While updates may still be made and we welcome feedback, keep in mind we may not respond to pull requests or issues quickly. 7 | 8 | **Let us know!** If you fork this, or if you use it, or if it helps in anyway, we'd love to hear from you! opensource@airbnb.com 9 | 10 | ## Why? 11 | 12 | 169.254.169.254 does not offer a simple view of the whole instance metadata, 13 | and querying the whole tree can be a bit quirky. 14 | 15 | We wanted to solve that problem once and for all, expose it in a straightforward 16 | and maintainable way. 17 | 18 | ## Installation 19 | 20 | Add this line to your application's Gemfile: 21 | 22 | gem 'aws-instmd' 23 | 24 | And then execute: 25 | 26 | $ bundle 27 | 28 | Or install it yourself as: 29 | 30 | $ gem install aws-instmd 31 | 32 | ## Usage 33 | 34 | ### Command-line utility 35 | 36 | `awsinstmd` will dump the metadata in JSON. 37 | 38 | ### Ruby API 39 | 40 | The Ruby gem should be easy to use (feedback is obviously welcome). 41 | 42 | Documentation should be improved; in the meantime, here is a simple example: 43 | 44 | require 'aws/instmd' 45 | puts AWS::InstMD.meta_data.instance_id 46 | puts AWS::InstMD.meta_data.instance_type 47 | 48 | ## Contributing 49 | 50 | 1. Fork it 51 | 2. Create your feature branch (`git checkout -b my-new-feature`) 52 | 3. Commit your changes (`git commit -am 'Add some feature'`) 53 | 4. Push to the branch (`git push origin my-new-feature`) 54 | 5. Create new Pull Request 55 | -------------------------------------------------------------------------------- /lib/aws/instmd.rb: -------------------------------------------------------------------------------- 1 | require 'net/http' 2 | 3 | module AWS 4 | class InstMD 5 | def self.method_missing name, *args, &block 6 | @@root ||= InstMD.new 7 | @@root.method(name).call(*args, &block) 8 | end 9 | 10 | # Can't be the first one to make that pun. 11 | # Still proud. 12 | class Hashish < Hash 13 | def method_missing name 14 | self[name.to_s.gsub('_', '-')] 15 | end 16 | end 17 | 18 | class Treeish < Hashish 19 | private 20 | def initialize http, prefix 21 | entries = InstMD.query http, prefix 22 | entries.lines.each do |l| 23 | l.chomp! 24 | if l.end_with? '/' 25 | self[l[0..-2]] = Treeish.new http, "#{prefix}#{l}" 26 | # meta-data/public-keys/ entries have a '0=foo' format 27 | elsif l =~ /(\d+)=(.*)/ 28 | number, name = $1, $2 29 | self[name] = Treeish.new http, "#{prefix}#{number}/" 30 | else 31 | self[l] = InstMD.query http, "#{prefix}#{l}" 32 | end 33 | end 34 | end 35 | end 36 | 37 | attr_accessor :user_data, :meta_data, :dynamic 38 | 39 | # Amazon, Y U NO trailing slash entries 40 | # in /, /$version and /$version/dynamic/?? 41 | # There is waaay too much code here. 42 | def initialize version='latest', host='169.254.169.254', port='80' 43 | http = Net::HTTP.new host, port 44 | @meta_data = Treeish.new http, "/#{version}/meta-data/" 45 | @user_data = InstMD.query http, "/#{version}/user-data" 46 | @dynamic = Hashish.new 47 | 48 | begin 49 | dynamic_stuff = InstMD.query(http, "/#{version}/dynamic/").lines 50 | rescue 51 | dynamic_stuff = [] 52 | end 53 | dynamic_stuff.each do |e| 54 | e = e.chomp.chomp '/' 55 | @dynamic[e] = Treeish.new http, "/#{version}/dynamic/#{e}/" 56 | end 57 | end 58 | 59 | def self.query http, path 60 | rep = http.request Net::HTTP::Get.new path 61 | unless Net::HTTPOK === rep 62 | raise Net::HTTPBadResponse, "#{rep.code} #{path}" 63 | end 64 | rep.body 65 | end 66 | 67 | def to_hash 68 | {:meta_data => @meta_data, :user_data => @user_data, :dynamic => @dynamic} 69 | end 70 | end 71 | end 72 | --------------------------------------------------------------------------------