├── .gitignore ├── .travis.yml ├── CHANGELOG.md ├── LICENSE ├── README.md ├── bin ├── ec2dns ├── ec2dnshelper ├── ec2host ├── ec2scp ├── ec2ssh └── ec2updatehostsfile ├── composer.json └── lib ├── ec2.php ├── ec2dns.php └── ec2host.php /.gitignore: -------------------------------------------------------------------------------- 1 | vendor 2 | composer.lock 3 | composer.phar 4 | 5 | bin/hoa 6 | bin/php-cs-fixer 7 | bin/sabre-cs-fixer 8 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: php 2 | php: 3 | - 5.5 4 | - 5.6 5 | - 7 6 | - hhvm 7 | 8 | matrix: 9 | fast_finish: true 10 | 11 | cache: vendor 12 | sudo: false 13 | 14 | script: 15 | - ./bin/sabre-cs-fixer fix . --dry-run --diff 16 | 17 | before_script: composer install --dev 18 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | Changelog 2 | ========= 3 | 4 | 2.0.2 (2016-04-28) 5 | ------------------ 6 | 7 | * Fixed: Instances that had no tags at all were not processed correctly, #18. 8 | 9 | 2.0.1 (2016-03-18) 10 | ------------------ 11 | 12 | * Updated to latest v2 aws/aws-sdk-php, #17. 13 | * Fixed: Default timezone to suppress PHP warnings, #16. 14 | 15 | 2.0.0 (2015-04-29) 16 | ------------------ 17 | 18 | * Added: Built-in DNS server, #13. 19 | * Fixed: Coding standards using sabre/cs. 20 | * Migrated to aws/aws-sdk-php v2, #12. 21 | 22 | 1.4.1 (2014-10-14) 23 | ------------------ 24 | 25 | * Fixed: Better exception on auth failure, #10. 26 | 27 | 1.4.0 (2013-07-08) 28 | ------------------ 29 | 30 | * Added: Support for EC2_URL env var, #3. 31 | * Added: Window name support for tmux, #5 (Thanks, @evert). 32 | * Added: Updated AWS regions. 33 | * Fixed: Instance tag parsing, #4 (Thanks, @sleets). 34 | * Fixed: Failing hostname lookup in ec2ssh, #6 (Thanks, @evert). 35 | 36 | 1.3.0 (2012-11-28) 37 | ------------------ 38 | 39 | * Added: Interactive shell support for ec2ssh. 40 | 41 | 1.2.0 (2012-09-12) 42 | ------------------ 43 | 44 | * Added: Updater tool for `/etc/hosts`. 45 | * Improved error and exception handling. 46 | 47 | 1.1.0 (2012-08-31) 48 | ------------------ 49 | 50 | * Added: ec2scp tool. 51 | 52 | 1.0.0 (2012-08-30) 53 | ------------------ 54 | 55 | * First release. 56 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) fruux GmbH. 2 | 3 | All rights reserved. 4 | 5 | Redistribution and use in source and binary forms, with or without modification, 6 | are permitted provided that the following conditions are met: 7 | 8 | * Redistributions of source code must retain the above copyright notice, 9 | this list of conditions and the following disclaimer. 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | * Neither the name of the software nor the names of its contributors 14 | may be used to endorse or promote products derived from this software 15 | without specific prior written permission. 16 | 17 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 18 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 20 | ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE 21 | LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 24 | INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 25 | CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 26 | ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 27 | POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # ec2dns 2 | 3 | ec2dns is a set of command line tools that makes it easy to resolve public hostnames of [EC2](http://aws.amazon.com/ec2/) instances and ssh into them via their tag name. 4 | 5 | ## Usage of ec2dns 6 | 7 | ec2dns provides DNS resolution of ec2 tag names under the ".ec2" TLD. If a servers tag name is `appserver-1`, it can be resolved as `appserver-1.ec2`. 8 | 9 | ec2dns allows working with ec2 tag names like with normal domain names as shown in the following examples. 10 | 11 | ### ssh 12 | 13 | ``` 14 | ssh ubuntu@appserver-2.ec2 15 | ubuntu@ip-1-11-11-11:~$ 16 | ``` 17 | 18 | ### scp 19 | 20 | ``` 21 | $ scp ubuntu@appserver-1.ec2:/etc/nginx/nginx.conf . 22 | nginx.conf 100% 221 0.2KB/s 00:00 23 | ``` 24 | 25 | ### mysql 26 | 27 | ``` 28 | $ mysql --host=appserver-1.ec2 --user=someUser --password=somePassword someDatabase 29 | Welcome to the MySQL monitor. Commands end with ; or \g. 30 | Your MySQL connection id is 348 31 | Server version: 5.5.25a Source distribution 32 | 33 | Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 34 | 35 | Oracle is a registered trademark of Oracle Corporation and/or its 36 | affiliates. Other names may be trademarks of their respective 37 | owners. 38 | 39 | Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. 40 | 41 | mysql> 42 | ``` 43 | 44 | ## ec2dns tools 45 | 46 | ec2dns also includes wrappers around ssh and scp (if you can't or don't want to set up the DNS feature on your machine), as well as a tool to lookup hostnames and a tool to update your /etc/hosts file. 47 | 48 | You won't need these wrapper tools if you have set up the DNS feature as shown in the installation instructions below. 49 | 50 | ### ec2host 51 | 52 | #### Get a list of ec2 instances 53 | 54 | ``` 55 | $ ec2host 56 | i-12345678: appserver-1 ec9-99-99-99-99.compute-1.amazonaws.com 57 | i-87654321: appserver-2 ec1-11-11-11-11.compute-1.amazonaws.com 58 | ``` 59 | 60 | #### Get the hostname of an ec2 instance by its name tag 61 | 62 | ``` 63 | $ ec2host appserver-1 64 | ec9-99-99-99-99.compute-1.amazonaws.com 65 | ``` 66 | 67 | #### combine ec2host with other commands by using backticks 68 | 69 | ``` 70 | $ mysql --host=`ec2host appserver-1` --user=someUser --password=somePassword someDatabase 71 | Welcome to the MySQL monitor. Commands end with ; or \g. 72 | Your MySQL connection id is 348 73 | Server version: 5.5.25a Source distribution 74 | 75 | Copyright (c) 2000, 2011, Oracle and/or its affiliates. All rights reserved. 76 | 77 | Oracle is a registered trademark of Oracle Corporation and/or its 78 | affiliates. Other names may be trademarks of their respective 79 | owners. 80 | 81 | Type 'help;' or '\h' for help. Type '\c' to clear the current input statement. 82 | 83 | mysql> 84 | ``` 85 | 86 | ### ec2ssh 87 | 88 | #### ssh into an instance via its name tag 89 | 90 | ``` 91 | $ ec2ssh ubuntu@appserver-2 92 | ubuntu@ip-1-11-11-11:~$ 93 | ``` 94 | 95 | #### ssh into an instance via its name tag and execute commands 96 | 97 | ``` 98 | $ ec2ssh appserver-1 uptime 99 | 13:09:10 up 1 day, 14:23, 0 users, load average: 0.35, 0.36, 0.35 100 | 101 | $ ec2ssh appserver-1 'uname -a' 102 | Linux ip-10-140-78-75 3.2.0-23-virtual #36-Ubuntu SMP Tue Apr 10 22:29:03 UTC 2012 x86_64 x86_64 x86_64 GNU/Linux 103 | ``` 104 | 105 | ### ec2scp 106 | 107 | #### copy a file from an ec2 instance onto your machine 108 | 109 | ``` 110 | $ ec2scp ubuntu@appserver-1:/etc/nginx/nginx.conf . 111 | nginx.conf 100% 221 0.2KB/s 00:00 112 | ``` 113 | 114 | ### ec2updatehostsfile 115 | 116 | #### update /etc/hosts with your ec2 instances 117 | 118 | ``` 119 | $ sudo -E ec2updatehostsfile 120 | Updated/Added 2 hosts. 121 | ``` 122 | 123 | Your machine is now able to resolve your ec2 instances by tags directly, so for example the following will just work 124 | 125 | ``` 126 | $ ping appserver-1 127 | ``` 128 | 129 | ## Prerequisites 130 | 131 | * Obviously an [AWS](http://aws.amazon.com) account and at least one running EC2 instance. 132 | * Correctly set `AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` environment variables. 133 | * You need [Composer](http://getcomposer.org) to install the dependencies - you should install it globally, as described [here](http://getcomposer.org/doc/00-intro.md#globally). 134 | * You should have a `~/bin` directory and it should be included in your `PATH` environment variable. 135 | 136 | ## Installation 137 | 138 | ### Basic 139 | 140 | * The following command installs ec2dns via Composer. 141 | 142 | ``` 143 | composer global require "fruux/ec2dns=~2.0.0" 144 | ``` 145 | 146 | * Make sure you have `~/.composer/vendor/bin/` in your path. 147 | 148 | ### DNS feature 149 | 150 | These instructions are for OS X. 151 | 152 | * Add the configuration for the DNS resolver by creating the file `/etc/resolver/ec2` and pasting the following content. 153 | 154 | ``` 155 | nameserver 127.0.0.1 156 | port 57005 157 | ``` 158 | 159 | * Create the LaunchAgent configuration that starts the DNS server by creating the file `~/Library/LaunchAgents/com.fruux.ec2dns.plist` with the following command. If you are using ZSH, you'll have to change `/bin/bash` to `/bin/zsh`. 160 | 161 | ``` 162 | tee ~/Library/LaunchAgents/com.fruux.ec2dns.plist < 164 | 165 | 166 | 167 | Label 168 | com.fruux.ec2dns 169 | ProgramArguments 170 | 171 | /bin/bash 172 | -i 173 | -l 174 | -c 175 | $HOME/.composer/vendor/bin/ec2dns 176 | 177 | RunAtLoad 178 | 179 | StandardErrorPath 180 | /usr/local/var/log/ec2dns.log 181 | StandardOutPath 182 | /usr/local/var/log/ec2dns.log 183 | 184 | 185 | EOF 186 | ``` 187 | 188 | * Finally activate the LaunchAgent config by pasting the following into the terminal. 189 | 190 | ``` 191 | launchctl load ~/Library/LaunchAgents/com.fruux.ec2dns.plist 192 | ``` 193 | 194 | ## Updating 195 | 196 | ``` 197 | composer global update fruux/ec2dns 198 | 199 | launchctl unload ~/Library/LaunchAgents/com.fruux.ec2dns.plist 200 | launchctl load ~/Library/LaunchAgents/com.fruux.ec2dns.plist 201 | ``` 202 | 203 | ## Contributing 204 | 205 | Please submit all pull requests against the master branch. Code accompanied with phpunit tests is highly appreciated. Thanks! 206 | 207 | ## Acknowledgements 208 | 209 | ec2dns is inspired by [ec2-ssh](http://github.com/Instagram/ec2-ssh) and powered by [aws/aws-sdk-php](https://github.com/aws/aws-sdk-php). 210 | 211 | ## Copyright and license 212 | 213 | Copyright (c) [fruux GmbH](http://fruux.com). All rights reserved. 214 | 215 | *fruux is a free service that takes care of your contacts, calendars and more so you don't have to (powered by CardDAV and CalDAV).* 216 | 217 | Check the [license](https://github.com/fruux/ec2dns/blob/master/LICENSE). 218 | -------------------------------------------------------------------------------- /bin/ec2dns: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getMessage() . "\n"); 44 | die(); 45 | } 46 | 47 | /* 48 | * Instantiate ec2dns 49 | */ 50 | try { 51 | $ec2dns = new ec2dns($ec2); 52 | $ec2dns->run(); 53 | } catch (Exception $e) { 54 | fwrite(STDERR, $e->getMessage() . "\n"); 55 | die(); 56 | } 57 | -------------------------------------------------------------------------------- /bin/ec2dnshelper: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getMessage() . "\n"); 52 | die(); 53 | } 54 | 55 | $matchedHost = null; 56 | 57 | /* 58 | * Replace known tags with hostnames 59 | * (This needs optimization!) 60 | */ 61 | $ec2host = new ec2host($ec2); 62 | if ($ec2host->instances) { 63 | 64 | $arguments = array_slice($argv, 1); 65 | foreach ($arguments as $argumentKey => $argumentValue) { 66 | 67 | if (strpos($argumentValue, '-', 0) === 0 || strpos($argumentValue, '--', 0) === 0) { 68 | continue; 69 | } 70 | 71 | foreach ($ec2host->instances as $instance) { 72 | 73 | if ($instance['tag'] == $ec2host->emptyTag) { 74 | continue; 75 | } 76 | 77 | $newArgumentValue = preg_replace('/(^.*@?)(' . preg_quote($instance['tag'], '/') . ')(:?.*$)/i', '$1' . $instance['dnsName'] . '$3', $argumentValue); 78 | if ($newArgumentValue != $argumentValue) { 79 | $arguments[$argumentKey] = $newArgumentValue; 80 | $matchedHost = $instance['tag']; 81 | break; 82 | } 83 | 84 | } 85 | 86 | } 87 | 88 | } 89 | 90 | /* 91 | * Return processed arguments 92 | */ 93 | echo "export ec2args=" . escapeshellarg(implode(' ', $arguments)) . ";\n"; 94 | echo "export ec2host=" . $matchedHost . ";\n"; 95 | -------------------------------------------------------------------------------- /bin/ec2host: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getMessage() . "\n"); 45 | die(); 46 | } 47 | 48 | /* 49 | * Run ec2host 50 | */ 51 | if (isset($argv[1])) { 52 | $ec2host = new ec2host($ec2, $argv[1]); 53 | } else { 54 | $ec2host = new ec2host($ec2); 55 | } 56 | 57 | /* 58 | * Output result of ec2host 59 | */ 60 | foreach ($ec2host->instances as $instance) { 61 | if (isset($argv[1])) { 62 | echo $instance['dnsName'] . "\n"; 63 | } else { 64 | echo $instance['instanceId'] . ": " . $instance['tag'] . "\t" . $instance['dnsName'] . "\n"; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /bin/ec2scp: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ec2scp 4 | # 5 | # @copyright Copyright (c) fruux GmbH. All rights reserved. 6 | # @author Dominik Tobschall (http://fruux.com/) 7 | 8 | # 9 | # Find script path 10 | # 11 | SOURCE="${BASH_SOURCE[0]}" 12 | DIR="$( dirname "$SOURCE" )" 13 | while [ -h "$SOURCE" ] 14 | do 15 | SOURCE="$(readlink "$SOURCE")" 16 | [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" 17 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 18 | done 19 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 20 | 21 | # 22 | # Hand arguments to ec2dnshelper if we have any 23 | # 24 | if [ "$#" -ge 1 ]; then 25 | eval $($DIR/ec2dnshelper $@) 26 | else 27 | ec2args="" 28 | fi 29 | 30 | # 31 | # run scp 32 | # 33 | scp $ec2args 34 | -------------------------------------------------------------------------------- /bin/ec2ssh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # ec2ssh 4 | # 5 | # @copyright Copyright (c) fruux GmbH. All rights reserved. 6 | # @author Dominik Tobschall (http://fruux.com/) 7 | 8 | 9 | # Changing the window title 10 | setwindowtitle() { 11 | printf "\033k$1\033\\" 12 | } 13 | 14 | # 15 | # Find script path 16 | # 17 | SOURCE="${BASH_SOURCE[0]}" 18 | DIR="$( dirname "$SOURCE" )" 19 | while [ -h "$SOURCE" ] 20 | do 21 | SOURCE="$(readlink "$SOURCE")" 22 | [[ $SOURCE != /* ]] && SOURCE="$DIR/$SOURCE" 23 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 24 | done 25 | DIR="$( cd -P "$( dirname "$SOURCE" )" && pwd )" 26 | 27 | # 28 | # Hand arguments to ec2dnshelper if we have any 29 | # 30 | if [ "$#" -ge 1 ]; then 31 | eval $($DIR/ec2dnshelper $@) 32 | setwindowtitle "ec2:$ec2host" 33 | else 34 | ec2args="" 35 | fi 36 | 37 | # 38 | # run ssh 39 | # 40 | ssh $ec2args 41 | 42 | # back to some default.. could be wrong I suppose, but I'm not fully sure how 43 | # to capture the old name, and set that instead. 44 | setwindowtitle bash 45 | -------------------------------------------------------------------------------- /bin/ec2updatehostsfile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | getMessage() . "\n"); 44 | die(); 45 | } 46 | 47 | /* 48 | * read /etc/hosts 49 | */ 50 | if (!$hosts = file("/etc/hosts")) { 51 | fwrite(STDERR, "Could not read /etc/hosts.\n"); 52 | die(); 53 | } 54 | 55 | /* 56 | * Run ec2host 57 | */ 58 | $ec2host = new ec2host($ec2); 59 | 60 | /* 61 | * check for existing entries 62 | */ 63 | $existingEntries = 0; 64 | foreach ($hosts as $hostsLine => $hostsValue) { 65 | if ($hostsValue == "## ec2dns\n") { 66 | if (!preg_match_all("/(#)(\\s+)(\\d+)(\\s+)(hosts)(\n)/is", $hosts[$hostsLine + 1], $matches)) { 67 | 68 | fwrite(STDERR, "Found ec2dns marker in /etc/hosts, but something seems to be wrong.\nProbably you manually changed the records.\nPlease remove them manually before running this tool again.\n"); 69 | die(); 70 | 71 | } else { 72 | 73 | /* 74 | * remove existing entries 75 | */ 76 | $existingEntries = $matches[3][0]; 77 | for ($i = 0; $i <= $existingEntries + 1; $i++) { 78 | unset($hosts[$hostsLine + $i]); 79 | } 80 | $hosts = array_values($hosts); 81 | break; 82 | 83 | } 84 | } 85 | } 86 | 87 | /* 88 | * get data for new entries 89 | */ 90 | $newHosts = []; 91 | $entries = 0; 92 | 93 | foreach ($ec2host->instances as $instance) { 94 | if ($instance['tag'] != $ec2host->emptyTag) { 95 | $newHosts[] = gethostbyname($instance['dnsName']) . " " . $instance['tag'] . "\n"; 96 | $entries++; 97 | } 98 | } 99 | 100 | $header = [ 101 | "## ec2dns\n", 102 | "# " . $entries . " hosts\n" 103 | ]; 104 | 105 | array_splice($newHosts, 0, 0, $header); 106 | 107 | /* 108 | * add new entries to /etc/hosts 109 | */ 110 | if ($existingEntries > 0) { 111 | 112 | array_splice($hosts, $hostsLine, 0, $newHosts); 113 | 114 | } else { 115 | 116 | foreach ($newHosts as $newHost) { 117 | $hosts[] = $newHost; 118 | } 119 | 120 | } 121 | $hosts = implode("", $hosts); 122 | 123 | /* 124 | * Check if we have write access to /etc/hosts 125 | */ 126 | $hostsHandle = @fopen("/etc/hosts", "w"); 127 | 128 | if (!$hostsHandle) { 129 | fwrite(STDERR, "No write permissions for /etc/hosts. You have to run this tool like this: 'sudo -E " . $argv[0] . "'.\n"); 130 | die(); 131 | } 132 | 133 | /* 134 | * save /etc/hosts 135 | */ 136 | fwrite($hostsHandle, $hosts); 137 | fclose($hostsHandle); 138 | 139 | /* 140 | * Provide feedback 141 | */ 142 | if ($existingEntries > 0) { 143 | echo "Updated " . $entries . " hosts.\n"; 144 | } else { 145 | echo "Added " . $entries . " hosts.\n"; 146 | } 147 | -------------------------------------------------------------------------------- /composer.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fruux/ec2dns", 3 | "replace": { "dominikto/ec2dns": "self.version" }, 4 | "description": "ec2dns is a set of command line tools that makes it easy to display public hostnames of EC2 instances and ssh into them via their tag name.", 5 | "keywords" : [ "aws", "ec2", "host", "dns", "server", "hostname", "tags", "ssh", "scp" ], 6 | "homepage" : "https://github.com/fruux/ec2dns", 7 | "license" : "BSD-3-Clause", 8 | "require": { 9 | "php":">=5.5.0", 10 | "aws/aws-sdk-php": "~2.8.0", 11 | "hoa/dns": "~3.0", 12 | "hoa/socket": "~1.0" 13 | }, 14 | "require-dev": { 15 | "sabre/cs" : "dev-master" 16 | }, 17 | "autoload" : { 18 | "psr-4" : { 19 | "ec2dns\\" : "lib/" 20 | } 21 | }, 22 | "config" : { 23 | "bin-dir" : "bin" 24 | }, 25 | "bin": [ 26 | "bin/ec2dns", 27 | "bin/ec2updatehostsfile", 28 | "bin/ec2host", 29 | "bin/ec2ssh", 30 | "bin/ec2scp", 31 | "bin/ec2dnshelper" 32 | ], 33 | "authors": [ 34 | { 35 | "name": "Dominik Tobschall" 36 | } 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /lib/ec2.php: -------------------------------------------------------------------------------- 1 | initEC2($awsKey, $awsSecret, $awsRegion); 31 | 32 | } 33 | 34 | /** 35 | * Sets up the \Aws\Ec2\Ec2Client instance. 36 | * 37 | * @param string $awsKey 38 | * @param string $awsSecret 39 | * @param string \Aws\Common\Enum\Region 40 | * @return void 41 | */ 42 | private function initEC2($awsKey, $awsSecret, $awsRegion = false) { 43 | 44 | if (!empty($awsKey) && !empty($awsSecret)) { 45 | $this->awsEC2 = \Aws\Ec2\Ec2Client::factory([ 46 | 'credentials' => new \Aws\Common\Credentials\Credentials($awsKey, $awsSecret), 47 | 'region' => empty($awsRegion) ? $this->defaultRegion : $this->getRegionByUrl($awsRegion) 48 | ]); 49 | } else { 50 | throw new \LogicException('AWS Key or Secret are not set.'); 51 | } 52 | 53 | } 54 | 55 | /** 56 | * Returns the region constants defined in the \Aws\Common\Enum\Region class 57 | * for endpoint urls 58 | * 59 | * @param string $url 60 | * @return constant 61 | */ 62 | private function getRegionByUrl($url) { 63 | 64 | $url = strtolower(parse_url($url, \PHP_URL_HOST)); 65 | 66 | $regions = [ 67 | 'ec2.us-east-1.amazonaws.com' => 'us-east-1', 68 | 'ec2.us-west-1.amazonaws.com' => 'us-west-1', 69 | 'ec2.us-west-2.amazonaws.com' => 'us-west-2', 70 | 'ec2.eu-west-1.amazonaws.com' => 'eu-west-1', 71 | 'ec2.eu-central-1.amazonaws.com' => 'eu-central-1', 72 | 'ec2.ap-northeast-1.amazonaws.com' => 'ap-northeast-1', 73 | 'ec2.ap-southeast-1.amazonaws.com' => 'ap-southeast-1', 74 | 'ec2.ap-southeast-2.amazonaws.com' => 'ap-southeast-2', 75 | 'ec2.sa-east-1.amazonaws.com' => 'sa-east-1', 76 | 'ec2.cn-north-1.amazonaws.com.cn' => 'cn-north-1', 77 | 'ec2.us-gov-west-1.amazonaws.com' => 'us-gov-west-1' 78 | ]; 79 | 80 | if (!isset($regions[$url])) { 81 | throw new \InvalidArgumentException('The supplied region is unknown. Check your EC2_URL environment variable.'); 82 | } 83 | 84 | if (!in_array($regions[$url], \Aws\Common\Enum\Region::values())) { 85 | throw new \InvalidArgumentException('The supplied region is known, but not supported by your version of aws/aws-sdk-php.'); 86 | } 87 | 88 | return $regions[strtolower($url)]; 89 | 90 | } 91 | 92 | /** 93 | * Adds a filter rule. 94 | * 95 | * @param string $name 96 | * @param array $values 97 | * @return void 98 | */ 99 | function addFilter($name, $values) { 100 | 101 | $this->filters[] = ['Name' => $name, 'Values' => $values]; 102 | 103 | } 104 | 105 | /** 106 | * Makes the actual API request to AWS and stores the returned EC2 instances in the class. 107 | * 108 | * @return void 109 | */ 110 | private function getInstances() { 111 | 112 | try { 113 | $this->instances = $this->awsEC2->getIterator('describeInstances', 114 | [ 115 | 'Filters' => $this->filters 116 | ] 117 | ); 118 | } catch (\Aws\Ec2\Exception\Ec2Exception $e) { 119 | if ($e->getStatusCode() === 401) { 120 | throw new \RuntimeException('AWS was not able to validate the provided access credentials'); 121 | } else { 122 | throw new \RuntimeException('Request failed!'); 123 | } 124 | } 125 | 126 | } 127 | 128 | /** 129 | * This method returns the found instances. 130 | * 131 | * @return array $instance 132 | */ 133 | function getNext() { 134 | 135 | if (!$this->instances) { 136 | $this->getInstances(); 137 | } 138 | 139 | $this->instances->next(); 140 | return $this->instances->current(); 141 | 142 | } 143 | 144 | } 145 | -------------------------------------------------------------------------------- /lib/ec2dns.php: -------------------------------------------------------------------------------- 1 | ec2 = $ec2; 32 | $this->dns = new \Hoa\Dns\Resolver(new \Hoa\Socket\Server($this->listener)); 33 | 34 | } 35 | 36 | /** 37 | * dnsCache Setter 38 | * 39 | * @param string $type 40 | * @param string $tag 41 | * @param string $ip 42 | * @return void 43 | */ 44 | protected function setCache($type, $tag, $ip) { 45 | 46 | $this->dnsCache[md5($type . $tag)] = ['ip' => $ip, 'created' => time()]; 47 | 48 | } 49 | 50 | /** 51 | * dnsCache Getter 52 | * 53 | * @param string $type 54 | * @param string $tag 55 | * @return false|string 56 | */ 57 | protected function getCache($type, $tag) 58 | { 59 | if (isset($this->dnsCache[md5($type . $tag)])) { 60 | if (time() - $this->ttl < $this->dnsCache[md5($type . $tag)]['created']) { 61 | return $this->dnsCache[md5($type . $tag)]; 62 | } else { 63 | unset($this->dnsCache[md5($type . $tag)]); 64 | return false; 65 | } 66 | } else { 67 | return false; 68 | } 69 | 70 | } 71 | 72 | /** 73 | * Returns the tag. 74 | * 75 | * @param string $domain 76 | * @return string 77 | */ 78 | protected function stripTld($domain) { 79 | 80 | return preg_replace('/.' . $this->tld . '$/', '', $domain); 81 | 82 | } 83 | 84 | /** 85 | * This method resolves tags to IPs via ec2host. 86 | * 87 | * @param string $type 88 | * @param string $tag 89 | * @return false|string 90 | */ 91 | protected function resolve($type, $tag) { 92 | 93 | if ($dnsCache = $this->getCache($type, $tag)) { 94 | return $dnsCache['ip']; 95 | } else { 96 | $ec2host = new ec2host(clone $this->ec2, $tag); 97 | 98 | if ($ec2host->instances) { 99 | $ip = gethostbyname($ec2host->instances[0]['dnsName']); 100 | $this->setCache($type, $tag, $ip); 101 | 102 | return $ip; 103 | } else { 104 | return false; 105 | } 106 | 107 | } 108 | 109 | } 110 | 111 | /** 112 | * onQuery callback 113 | * 114 | * @param \Hoa\Event\Bucket $bucket 115 | * @return false|string 116 | */ 117 | function onQueryCallback(\Hoa\Event\Bucket $bucket) { 118 | 119 | $data = $bucket->getData(); 120 | return $this->resolve($data['type'], $this->stripTld($data['domain'])); 121 | 122 | } 123 | 124 | /** 125 | * This method starts the ec2dns eventloop 126 | * 127 | * @return void 128 | */ 129 | function run() { 130 | 131 | try { 132 | $this->dns->on('query', xcallable($this, 'onQueryCallback')); 133 | $this->dns->run(); 134 | } catch (\Hoa\Socket\Exception $e) { 135 | throw new \InvalidArgumentException($e->getMessage()); 136 | } 137 | 138 | } 139 | 140 | } 141 | -------------------------------------------------------------------------------- /lib/ec2host.php: -------------------------------------------------------------------------------- 1 | ec2 = $ec2; 30 | $this->instanceTag = $instanceTag; 31 | 32 | $this->initFilters(); 33 | $this->run(); 34 | 35 | } 36 | 37 | /** 38 | * This method initializes the filters in the instance of ec2dns\ec2. 39 | * 40 | * @return void 41 | */ 42 | protected function initFilters() { 43 | 44 | $this->ec2->addFilter('instance-state-name', ['running']); 45 | 46 | if ($this->instanceTag) { 47 | $this->ec2->addFilter('tag:Name', [$this->instanceTag]); 48 | } 49 | 50 | } 51 | 52 | /** 53 | * This method executes the request via the instance of ec2dns\ec2 54 | * and stores the result in the class. 55 | * 56 | * @return void 57 | */ 58 | protected function run() { 59 | 60 | while ($instance = $this->ec2->getNext()) { 61 | 62 | $tag = false; 63 | $instanceId = false; 64 | $dnsName = false; 65 | 66 | if (isset($instance['Tags'])) { 67 | 68 | foreach ($instance['Tags'] as $tag) { 69 | if ($tag['Key'] == 'Name' && !empty($tag['Value'])) { 70 | $tag = $tag['Value']; 71 | break; 72 | } else { 73 | $tag = false; 74 | } 75 | } 76 | 77 | } 78 | 79 | $instanceId = $instance['InstanceId']; 80 | $dnsName = $instance['PublicDnsName']; 81 | $tag = ($tag) ? $tag : $this->emptyTag; 82 | 83 | $this->instances[] = ["instanceId" => $instanceId, "dnsName" => $dnsName, "tag" => $tag]; 84 | 85 | } 86 | 87 | } 88 | 89 | } 90 | --------------------------------------------------------------------------------