├── Rakefile ├── lib ├── zeployment │ └── version.rb └── zeployment.rb ├── .gitignore ├── bin ├── setup └── console ├── Gemfile ├── Gemfile.lock ├── LICENSE.txt ├── zeployment.gemspec ├── CODE_OF_CONDUCT.md └── README.md /Rakefile: -------------------------------------------------------------------------------- 1 | require "bundler/gem_tasks" 2 | task :default => :spec 3 | -------------------------------------------------------------------------------- /lib/zeployment/version.rb: -------------------------------------------------------------------------------- 1 | module Zeployment 2 | VERSION = "0.3.1" 3 | end 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.bundle/ 2 | /.yardoc 3 | /_yardoc/ 4 | /coverage/ 5 | /doc/ 6 | /pkg/ 7 | /spec/reports/ 8 | /tmp/ 9 | .idea 10 | lib/.byebug_history -------------------------------------------------------------------------------- /bin/setup: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | set -euo pipefail 3 | IFS=$'\n\t' 4 | set -vx 5 | 6 | bundle install 7 | 8 | # Do any other automated setup that you need to do here 9 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | git_source(:github) {|repo_name| "https://github.com/#{repo_name}" } 4 | 5 | # Specify your gem's dependencies in zeployment.gemspec 6 | gemspec 7 | 8 | gem 'json' 9 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: . 3 | specs: 4 | zeployment (0.3.1) 5 | 6 | GEM 7 | remote: https://rubygems.org/ 8 | specs: 9 | json (2.0.4) 10 | rake (10.5.0) 11 | 12 | PLATFORMS 13 | ruby 14 | 15 | DEPENDENCIES 16 | bundler (~> 1.16) 17 | json 18 | rake (~> 10.0) 19 | zeployment! 20 | 21 | BUNDLED WITH 22 | 1.16.2 23 | -------------------------------------------------------------------------------- /bin/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler/setup" 4 | require "zeployment" 5 | 6 | # You can add fixtures and/or initialization code here to make experimenting 7 | # with your gem easier. You can also use a different console, if you like. 8 | 9 | # (If you use this, don't forget to add pry to your Gemfile!) 10 | # require "pry" 11 | # Pry.start 12 | 13 | require "irb" 14 | IRB.start(__FILE__) 15 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Naman Gupta 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /zeployment.gemspec: -------------------------------------------------------------------------------- 1 | 2 | lib = File.expand_path("../lib", __FILE__) 3 | $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib) 4 | require "zeployment/version" 5 | 6 | Gem::Specification.new do |spec| 7 | spec.name = "zeployment" 8 | spec.version = Zeployment::VERSION 9 | spec.authors = ["Naman Gupta"] 10 | spec.email = ["01namangupta@gmail.com"] 11 | 12 | spec.summary = %q{A gem for using common aws-cli functionality and also for zero-downtime deployment} 13 | spec.description = %q{This gem is used for accessing aws-cli commands related to zero-downtime deployment through ruby functions} 14 | spec.homepage = "https://github.com/namangupta01/zeployment" 15 | spec.license = "MIT" 16 | 17 | # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host' 18 | # to allow pushing to a single host or delete this section to allow pushing to any host. 19 | if spec.respond_to?(:metadata) 20 | spec.metadata["allowed_push_host"] = 'https://rubygems.org' 21 | else 22 | raise "RubyGems 2.0 or newer is required to protect against " \ 23 | "public gem pushes." 24 | end 25 | 26 | # Specify which files should be added to the gem when it is released. 27 | # The `git ls-files -z` loads the files in the RubyGem that have been added into git. 28 | spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do 29 | `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) } 30 | end 31 | spec.bindir = "exe" 32 | spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) } 33 | spec.require_paths = ["lib"] 34 | 35 | spec.add_development_dependency "bundler", "~> 1.16" 36 | spec.add_development_dependency "rake", "~> 10.0" 37 | end 38 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | In the interest of fostering an open and welcoming environment, we as 6 | contributors and maintainers pledge to making participation in our project and 7 | our community a harassment-free experience for everyone, regardless of age, body 8 | size, disability, ethnicity, gender identity and expression, level of experience, 9 | nationality, personal appearance, race, religion, or sexual identity and 10 | orientation. 11 | 12 | ## Our Standards 13 | 14 | Examples of behavior that contributes to creating a positive environment 15 | include: 16 | 17 | * Using welcoming and inclusive language 18 | * Being respectful of differing viewpoints and experiences 19 | * Gracefully accepting constructive criticism 20 | * Focusing on what is best for the community 21 | * Showing empathy towards other community members 22 | 23 | Examples of unacceptable behavior by participants include: 24 | 25 | * The use of sexualized language or imagery and unwelcome sexual attention or 26 | advances 27 | * Trolling, insulting/derogatory comments, and personal or political attacks 28 | * Public or private harassment 29 | * Publishing others' private information, such as a physical or electronic 30 | address, without explicit permission 31 | * Other conduct which could reasonably be considered inappropriate in a 32 | professional setting 33 | 34 | ## Our Responsibilities 35 | 36 | Project maintainers are responsible for clarifying the standards of acceptable 37 | behavior and are expected to take appropriate and fair corrective action in 38 | response to any instances of unacceptable behavior. 39 | 40 | Project maintainers have the right and responsibility to remove, edit, or 41 | reject comments, commits, code, wiki edits, issues, and other contributions 42 | that are not aligned to this Code of Conduct, or to ban temporarily or 43 | permanently any contributor for other behaviors that they deem inappropriate, 44 | threatening, offensive, or harmful. 45 | 46 | ## Scope 47 | 48 | This Code of Conduct applies both within project spaces and in public spaces 49 | when an individual is representing the project or its community. Examples of 50 | representing a project or community include using an official project e-mail 51 | address, posting via an official social media account, or acting as an appointed 52 | representative at an online or offline event. Representation of a project may be 53 | further defined and clarified by project maintainers. 54 | 55 | ## Enforcement 56 | 57 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 58 | reported by contacting the project team at 01namangupta@gmail.com. All 59 | complaints will be reviewed and investigated and will result in a response that 60 | is deemed necessary and appropriate to the circumstances. The project team is 61 | obligated to maintain confidentiality with regard to the reporter of an incident. 62 | Further details of specific enforcement policies may be posted separately. 63 | 64 | Project maintainers who do not follow or enforce the Code of Conduct in good 65 | faith may face temporary or permanent repercussions as determined by other 66 | members of the project's leadership. 67 | 68 | ## Attribution 69 | 70 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, 71 | available at [http://contributor-covenant.org/version/1/4][version] 72 | 73 | [homepage]: http://contributor-covenant.org 74 | [version]: http://contributor-covenant.org/version/1/4/ 75 | -------------------------------------------------------------------------------- /lib/zeployment.rb: -------------------------------------------------------------------------------- 1 | require "zeployment/version" 2 | require "json" 3 | 4 | module Zeployment 5 | class Aws 6 | 7 | def self.number_of_registered_instances_to_loadbalancer (name_of_load_balancer) 8 | load_balancer_instances_description_hash = get_load_balancer_instances_description(name_of_load_balancer) 9 | return load_balancer_instances_description_hash["InstanceStates"].length 10 | end 11 | 12 | def self.get_load_balancer_instances_description (name_of_load_balancer) 13 | load_balancers_instance_description_command = get_load_balancers_instances_description_command (name_of_load_balancer) 14 | load_balancer_instances_description = `#{load_balancers_instance_description_command}` 15 | return JSON.parse(load_balancer_instances_description) 16 | end 17 | 18 | def self.get_load_balancers_instances_description_command (name_of_load_balancer) 19 | return "aws elb describe-instance-health --load-balancer-name #{name_of_load_balancer}" 20 | end 21 | 22 | def self.number_of_instances_in_service (name_of_load_balancer) 23 | instances_in_service = 0 24 | load_balancer_instances_description_hash = get_load_balancer_instances_description(name_of_load_balancer) 25 | load_balancer_instances_description_hash["InstanceStates"].each do |instance_state| 26 | if instance_state["State"] == "InService" 27 | instances_in_service = instances_in_service + 1 28 | end 29 | end 30 | return instances_in_service 31 | end 32 | 33 | def self.number_of_instances_not_in_service (name_of_load_balancer) 34 | instances_not_in_service = 0 35 | load_balancer_instances_description_hash = get_load_balancer_instances_description(name_of_load_balancer) 36 | load_balancer_instances_description_hash["InstanceStates"].each do |instance_state| 37 | if instance_state["State"] != "InService" 38 | instances_not_in_service = instances_not_in_service + 1 39 | end 40 | end 41 | return instances_not_in_service 42 | end 43 | 44 | def self.get_ip_address_of_ec2_from_id (instance_id) 45 | instance_description_response = `#{get_ip_address_of_ec2_from_id_command(instance_id)}` 46 | instance_description_hash = JSON.parse(instance_description_response) 47 | ip_address = instance_description_hash["Reservations"][0]["Instances"][0]["PublicIpAddress"] 48 | return ip_address 49 | end 50 | 51 | def self.get_ip_address_of_ec2_from_id_command (instance_id) 52 | return "aws ec2 describe-instances --instance-id #{instance_id}" 53 | end 54 | 55 | def self.deregister_instance_from_load_balancer (name_of_load_balancer, instance_id) 56 | JSON.parse(`aws elb deregister-instances-from-load-balancer --load-balancer-name #{name_of_load_balancer} --instances #{instance_id}`) 57 | end 58 | 59 | def self.register_instance_with_load_balancer (name_of_load_balancer, instance_id) 60 | JSON.parse(`aws elb register-instances-with-load-balancer --load-balancer-name #{name_of_load_balancer} --instances #{instance_id}`) 61 | end 62 | 63 | def self.get_load_balancer_particular_instance_data (name_of_load_balancer, instance_id) 64 | load_balancers_instance_description_command = "aws elb describe-instance-health --load-balancer-name #{name_of_load_balancer} --instances #{instance_id}" 65 | load_balancer_instances_description = `#{load_balancers_instance_description_command}` 66 | return JSON.parse(load_balancer_instances_description) 67 | end 68 | 69 | def self.instance_is_in_service? (name_of_load_balancer, instance_id) 70 | load_balancer_insrance_data_hash = get_load_balancer_particular_instance_data name_of_load_balancer, instance_id 71 | return load_balancer_insrance_data_hash["InstanceStates"][0]["State"] == "InService" 72 | end 73 | 74 | def self.deploy (name_of_load_balancer, login_command_without_ip, commands_to_run) 75 | instances_details = get_load_balancer_instances_description(name_of_load_balancer) 76 | instances_details["InstanceStates"].each do |instance| 77 | instance_id = instance["InstanceId"] 78 | instance_ip = get_ip_address_of_ec2_from_id(instance_id) 79 | puts instance_ip 80 | deregister_instance_from_load_balancer(name_of_load_balancer, instance_id) 81 | login_and_run_commands("#{login_command_without_ip}@#{instance_ip}", commands_to_run) 82 | register_instance_with_load_balancer(name_of_load_balancer, instance_id) 83 | wait_till_the_instance_in_service(name_of_load_balancer, instance_id) 84 | puts ">>>>>>> Instance is in Service now <<<<<<<<<<<<<" 85 | end 86 | end 87 | 88 | def self.wait_till_the_instance_in_service(name_of_load_balancer, instance_id) 89 | while !instance_is_in_service?(name_of_load_balancer, instance_id) do 90 | puts ">>>>>>> Instance Not in Service <<<<<<<<<<<<<" 91 | end 92 | end 93 | 94 | def self.login_and_run_commands(login_command, commands_to_run) 95 | system("#{login_command} << #{commands_to_run} ") 96 | end 97 | 98 | end 99 | end 100 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Gem Version](https://badge.fury.io/rb/zeployment.svg)](https://badge.fury.io/rb/zeployment) 2 | 3 | # Zeployment 4 | 5 | Zeployment is a gem that provides functions as a wrapper to aws-cli and also to perform certain deployment functionality like zero downtime deployment which includes deregistering-deploying-registering of instances from load balancer. 6 | 7 | ## Installation 8 | 9 | Add this line to your application's Gemfile: 10 | 11 | ```ruby 12 | gem 'zeployment' 13 | ``` 14 | 15 | And then execute: 16 | 17 | $ bundle 18 | 19 | Or install it yourself as: 20 | 21 | $ gem install zeployment 22 | 23 | ## Getting Started 24 | 1. Install aws-cli on the system [Click here for Docs](https://docs.aws.amazon.com/cli/latest/userguide/installing.html "Click here for Docs") 25 | 2. Set aws configuration [Docs](https://docs.aws.amazon.com/cli/latest/userguide/cli-chap-getting-started.html "Docs") 26 | (Now you are good to go) 27 | 28 | ## Usage 29 | - To get the number of registered instance to the loadbalancer just use the method `number_of_registered_instances_to_loadbalancer` and pass the load balancer name as argument name and output will be the number of instances registered to the loadbalancer. 30 | ```ruby 31 | Zeployment::Aws.number_of_registered_instances_to_loadbalancer (name_of_load_balancer) 32 | ``` 33 | 34 | - To Get the load balancer instances description i.e the description of each instances registered with the load balancer use the following method. 35 | ```ruby 36 | Zeployment::Aws.get_load_balancer_instances_description (name_of_load_balancer) 37 | ``` 38 | 39 | - To get the number of instances regsitered with loadbalancer that is in service use the method name `number_of_instances_in_service` with the `load balancer name` as argument 40 | ```ruby 41 | Zeployment::Aws.number_of_instances_in_service (name_of_load_balancer) 42 | ``` 43 | - To get the number of instances regsitered with loadbalancer that is not in service use the method name `number_of_instances_not_in_service` with the `load balancer name` as argument 44 | ```ruby 45 | Zeployment::Aws.number_of_instances_not_in_service (name_of_load_balancer) 46 | ``` 47 | - To get the public ip address of the aws ec2 machine using the `instance id` use the method name `get_ip_address_of_ec2_from_id` and pass instance id as argument. 48 | ```ruby 49 | Zeployment::Aws.get_ip_address_of_ec2_from_id (instance_id) 50 | ``` 51 | 52 | - To deregister ec2 instance from load balancer use the method `deregister_instance_from_load_balancer` and pass `name of load balancer` as first argument and `instance id` as second argument. 53 | ```ruby 54 | Zeployment::Aws.deregister_instance_from_load_balancer (name_of_load_balancer, instance_id) 55 | ``` 56 | - To register ec2 instance with the load balancer use the method `register_instance_with_load_balancer` and pass `name of load balancer` as first argument and `instance id` as second argument. 57 | ```ruby 58 | Zeployment::Aws.register_instance_with_load_balancer (name_of_load_balancer, instance_id) 59 | ``` 60 | - To get the particular instance status related to loadbalancer use the method `get_load_balancer_particular_instance_data` and pass `name of load balancer` as first argument and `instance id` as second argument. 61 | ```ruby 62 | Zeployment::Aws.get_load_balancer_particular_instance_data (name_of_load_balancer, instance_id) 63 | ``` 64 | - To check if the instance is in service use the method `instance_is_in_service?` and pass `name of load balancer` as first argument and `instane id` as second argument. 65 | ```ruby 66 | Zeployment::Aws.instance_is_in_service? (name_of_load_balancer, instance_id) 67 | ``` 68 | - To perform zero downtime deployment use the method `deploy` and pass the `name of load balancer` as first argument and `login command before ip address` as second argument and also the commands to run for performing deployment after logging in into the machine. 69 | ```ruby 70 | Zeployment::Aws.deploy (name_of_load_balancer, login_command_without_ip, commands_to_run) 71 | ``` 72 | 73 | ## Development 74 | 75 | After checking out the repo, run `bin/setup` to install dependencies. You can also run `bin/console` for an interactive prompt that will allow you to experiment. 76 | 77 | To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org). 78 | 79 | ## Contributing 80 | 81 | Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/zeployment. This project is intended to be a safe, welcoming space for collaboration, and contributors are expected to adhere to the [Contributor Covenant](http://contributor-covenant.org) code of conduct. 82 | 83 | ## License 84 | 85 | The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT). 86 | 87 | ## Code of Conduct 88 | 89 | Everyone interacting in the Zeployment project’s codebases, issue trackers, chat rooms and mailing lists is expected to follow the [code of conduct](https://github.com/[USERNAME]/zeployment/blob/master/CODE_OF_CONDUCT.md). 90 | 91 | --------------------------------------------------------------------------------