simple_provision 0.99.2

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 ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 3b9054da4fdd261531b7c333a4e58768defe18e5
4
+ data.tar.gz: 4d15973d1f36b144cfa1be8df14b1fc447068e45
5
+ SHA512:
6
+ metadata.gz: aa8c47134f34cce7813461ac5c768abd708382b9717ef6e8c503f72727cf4ecdd59dfa4c13d06b0ca48a6198475ce975804ac66c54fdc6dd66acdf20df86d84f
7
+ data.tar.gz: bb5e2fe6f4428809a95a0a57c399a029af5851126375ebcd623ad474e5c59b317ae6315e392539cd8e947b3d1180e52dfad7fd43f0f31efce19f271d09ab4dfa
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in simple_provision.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Brandon Hilkert
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.
data/README.md ADDED
@@ -0,0 +1,116 @@
1
+ # Introduction
2
+ This is based on the work that brandonhilkert initally carried to automate
3
+ EC2 deployment with YAML and SHELL script. It tries to bring the good spirit: SIMPLE,
4
+ and JUST WORK to the world of Digital Ocean. So you own a Digital Ocean?
5
+
6
+ If you have suffer days of headache with Chef and Puppet, time for a new
7
+ simple way of provision. Let's rock.
8
+
9
+ ### Recipes repo
10
+
11
+ Don't forget to also checkout the recipes collection
12
+ https://github.com/phuongnd08/simple_privision_recipes
13
+
14
+ # How it works
15
+ This gem carries the provision by uploading a set of scripts (and
16
+ files) to the server and execute there.
17
+
18
+ It's up to you to choose the language you want. I often use a mix of
19
+ SHELL and RUBY scripts to accomplish the task. SHELL for some simple stuff
20
+ like install a package on the server and RUBY when I need to complete
21
+ some tricky part of the configuration.
22
+
23
+ Just remember that you need to use a shell script to install ruby/python first,
24
+ and then you can start use ruby/python. The install of ruby and python can be
25
+ as simple as create a bash contains "yum install ruby(python) -y" and include
26
+ it in the top of the `scripts` section in your server definition file.
27
+
28
+ # Project Structure
29
+ As simple as it could: One or few servers definition written in YAML + setup scripts written in
30
+ SHELL, RUBY and PYTHON + text files as resource.
31
+ It's up to you to use ERB, HAML or any kind of template processors.
32
+
33
+ ```
34
+ ./provisions
35
+ ├── files
36
+ │ ├── keys
37
+ │ │ └── deploy_key
38
+ │ └── rails_config
39
+ │ └── database.yml
40
+ ├── scripts
41
+ │ ├── apt.sh
42
+ │ ├── deploy_key.sh
43
+ │ ├── git.sh
44
+ │ ├── redis.rb
45
+ │ ├── ruby2.sh
46
+ │ ├── rubygems.sh
47
+ │ ├── install_tornado.py
48
+ │ ├── search_service_code.sh
49
+ │ └── search_service_env.sh
50
+ └── servers
51
+ ├── defaults.yml
52
+ └── search-service.yml
53
+ ```
54
+
55
+ So: You put definition of each type of server in `servers/type.yml`.
56
+ In `files` and `scripts` folder, you place files and scripts that will be
57
+ uploaded to the Digital Ocean droplet and executed.
58
+
59
+ ## Installation
60
+
61
+ Add this line to your application's Gemfile:
62
+
63
+ gem 'simple_provision'
64
+
65
+ And then execute:
66
+
67
+ $ bundle
68
+
69
+ Or install it yourself as:
70
+
71
+ $ gem install simple_provision
72
+
73
+ ## Servers Configuration
74
+
75
+ ### Server Definition
76
+ To define a server type, create a yaml file in the `./servers` directory with the following format:
77
+
78
+ `./servers/my-awesome-server.yml`
79
+
80
+ ```yaml
81
+ files:
82
+ - files/credentials.yml
83
+ scripts:
84
+ - scripts/git.sh
85
+ - scripts/ruby.sh
86
+ - scripts/rubygems.sh
87
+ - scripts/redis.sh
88
+ env:
89
+ DBNAME: my_db_name
90
+ WEBROOT: /var/www/app
91
+ ```
92
+
93
+ ### Shared Definitions
94
+
95
+ You can share definitions across server types with `./servers/defaults.yml`
96
+
97
+ ```yaml
98
+ private_key_path: /Users/bhilkert/.ssh/pd-app-server
99
+ ```
100
+
101
+
102
+ ### Passing variables to scripts
103
+ Variables defined in `env` will be exposed to scripts during execution.
104
+ That way you can use the same scripts for different type of server and
105
+ still be able to produce different outcomes.
106
+
107
+ ## Provision your Digital Ocean server:
108
+ `simpro y-awesome-server --droplet-name YOUR_DROPLET_NAME`
109
+
110
+ ## Contributing
111
+
112
+ 1. Fork it
113
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
114
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
115
+ 4. Push to the branch (`git push origin my-new-feature`)
116
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ task :default => [:console]
3
+
4
+ task :console do
5
+ exec "irb -r fss -I ./lib"
6
+ end
data/bin/simpro ADDED
@@ -0,0 +1,28 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'optparse'
4
+ require_relative '../lib/simple_provision'
5
+
6
+ options = {}
7
+
8
+ OptionParser.new do |opts|
9
+ opts.banner = "Usage: fss [options] type"
10
+
11
+ opts.on('--droplet-name NAME', 'An exiting Droplet Name' ) do |name|
12
+ options[:droplet_name] = name
13
+ end
14
+
15
+ opts.on('-h', '--help', 'Display this screen' ) do
16
+ puts opts
17
+ exit
18
+ end
19
+
20
+ opts.on('-v', '--version', 'Show version' ) do
21
+ puts SimpleProvision::Version
22
+ end
23
+ end.parse!
24
+
25
+ options.merge!(type: ARGV.first)
26
+
27
+ cli = SimpleProvision::CLI.new(options)
28
+ cli.configure
data/lib/ext/fog.rb ADDED
@@ -0,0 +1,61 @@
1
+ require 'fog'
2
+ require 'fog/core/ssh'
3
+
4
+ # Monkey-patch Fog 1.3.1 to stream SSH output
5
+ # (in real time) to stdout.
6
+ class Fog::SSH::Real
7
+ def run(commands)
8
+ commands = [*commands]
9
+ results = []
10
+ begin
11
+ Net::SSH.start(@address, @username, @options) do |ssh|
12
+ commands.each do |command|
13
+ result = Fog::SSH::Result.new(command)
14
+ ssh.open_channel do |ssh_channel|
15
+ ssh_channel.request_pty
16
+ ssh_channel.exec(command) do |channel, success|
17
+ unless success
18
+ raise "Could not execute command: #{command.inspect}"
19
+ end
20
+
21
+ channel.on_data do |ch, data|
22
+ result.stdout << data
23
+ puts data
24
+ end
25
+
26
+ channel.on_extended_data do |ch, type, data|
27
+ next unless type == 1
28
+ result.stderr << data
29
+ puts data
30
+ end
31
+
32
+ channel.on_request('exit-status') do |ch, data|
33
+ result.status = data.read_long
34
+ end
35
+
36
+ channel.on_request('exit-signal') do |ch, data|
37
+ result.status = 255
38
+ end
39
+ end
40
+ end
41
+ ssh.loop
42
+ results << result
43
+ end
44
+ end
45
+ rescue Net::SSH::HostKeyMismatch => exception
46
+ exception.remember_host!
47
+ sleep 0.2
48
+ retry
49
+ end
50
+ results
51
+ end
52
+ end
53
+
54
+ require "fog/digitalocean/models/compute/server"
55
+
56
+ # Monkey patch Digital Ocean to properly return public_ip_address to used in scp
57
+ Fog::Compute::DigitalOcean::Server.class_eval do
58
+ def public_ip_address
59
+ ip_address
60
+ end
61
+ end
@@ -0,0 +1,30 @@
1
+ module SimpleProvision
2
+ class CLI
3
+ def initialize(opts = {})
4
+ @opts = opts
5
+ @connection = SimpleProvision::Connection.new(options).connection
6
+ end
7
+
8
+ def bootstrap
9
+ server.bootstrap
10
+ end
11
+
12
+ def build
13
+ server.build
14
+ end
15
+
16
+ def configure
17
+ server.configure
18
+ end
19
+
20
+ private
21
+
22
+ def server
23
+ SimpleProvision::Server.new(@connection, options)
24
+ end
25
+
26
+ def options
27
+ @options ||= SimpleProvision::Configuration.new(@opts).options
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,49 @@
1
+ require 'yaml'
2
+
3
+ module SimpleProvision
4
+ class Configuration
5
+ MissingServerType = Class.new(StandardError)
6
+ MissingServerConfiguration = Class.new(StandardError)
7
+
8
+ attr_reader :options
9
+
10
+ def initialize(command_line_options = {})
11
+ @command_line_options = command_line_options
12
+
13
+ read_and_parse_server_options
14
+
15
+ raise MissingServerType, "Please specify a type of server you want to create using the --type option" unless options[:type]
16
+ end
17
+
18
+ private
19
+
20
+ def default_options
21
+ begin
22
+ YAML.load(File.read('servers/defaults.yml'))
23
+ rescue Errno::ENOENT
24
+ {}
25
+ end
26
+ end
27
+
28
+ def server_options
29
+ begin
30
+ YAML.load(File.read(server_file))
31
+ rescue Errno::ENOENT
32
+ raise MissingServerConfiguration, "Please create a configuration file './servers/#{type}.yml'"
33
+ end
34
+ end
35
+
36
+ def server_file
37
+ "servers/#{type}.yml"
38
+ end
39
+
40
+ def read_and_parse_server_options
41
+ options_string_hash = default_options.merge(server_options).merge(@command_line_options)
42
+ @options = Hash[options_string_hash.map{ |(k,v)| [k.to_sym, v] }]
43
+ end
44
+
45
+ def type
46
+ @command_line_options[:type]
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,25 @@
1
+ require 'fog'
2
+
3
+ module SimpleProvision
4
+ class Connection
5
+ MissingDigitalOceanCredentials = Class.new(StandardError)
6
+
7
+ def initialize(opts)
8
+ @opts = opts
9
+
10
+ if ENV["DIGITAL_OCEAN_API_KEY"].nil? || ENV["DIGITAL_OCEAN_CLIENT_ID"].nil?
11
+ raise SimpleProvision::Connection::MissingDigitalOceanCredentials, "Make sure DIGITAL_OCEAN_API_KEY and DIGITAL_OCEAN_CLIENT_ID are environmental variables with your credentials"
12
+ end
13
+ end
14
+
15
+ def connection
16
+ @connection ||= begin
17
+ Fog::Compute.new(
18
+ provider: "DigitalOcean",
19
+ digitalocean_client_id: ENV["DIGITAL_OCEAN_CLIENT_ID"],
20
+ digitalocean_api_key: ENV["DIGITAL_OCEAN_API_KEY"]
21
+ )
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,42 @@
1
+ module SimpleProvision
2
+ class SCP
3
+ FILENAME = "fss.tar.gz"
4
+
5
+ def initialize(server, opts)
6
+ @server, @opts = server, opts
7
+ end
8
+
9
+ def to_server
10
+ create_local_archive
11
+ scp_files_to_server
12
+ extract_remote_archive
13
+ remove_local_archive
14
+ end
15
+
16
+ private
17
+
18
+ def create_local_archive
19
+ files = @opts[:files] || []
20
+ scripts = @opts[:scripts] || []
21
+ includes = files + scripts
22
+
23
+ if includes.empty?
24
+ raise "Both files and scripts are empty. You should provide some"
25
+ end
26
+
27
+ `tar -czf #{FILENAME} #{includes.join(" ")}`
28
+ end
29
+
30
+ def scp_files_to_server
31
+ @server.scp(FILENAME, ".")
32
+ end
33
+
34
+ def extract_remote_archive
35
+ @server.ssh("tar -xzf #{FILENAME}")
36
+ end
37
+
38
+ def remove_local_archive
39
+ `rm -f #{FILENAME}`
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,55 @@
1
+ require 'shellwords'
2
+
3
+ module SimpleProvision
4
+ class Server
5
+ ServerNotFound = Class.new(StandardError)
6
+ MissingDropletName = Class.new(StandardError)
7
+
8
+ attr_reader :server
9
+
10
+ def initialize(connection, options)
11
+ @connection, @options = connection, options
12
+ end
13
+
14
+ def configure
15
+ get(options[:droplet_name]) if server.nil?
16
+ raise ServerNotFound, "Unable to find server with Droplet name #{options[:droplet_name]}." if server.nil?
17
+
18
+ SimpleProvision::SCP.new(server, options).to_server
19
+ scripts = options.fetch(:scripts).map do |script|
20
+ "#{environment_exports} bash -c '#{script}'"
21
+ end
22
+ server.ssh(scripts)
23
+ end
24
+
25
+ def environment_exports
26
+ @environment_exports ||= begin
27
+ if options[:env].nil?
28
+ ""
29
+ else
30
+ options[:env].map { |k, v| [k, Shellwords.escape(v)].join("=") }.join(" ")
31
+ end
32
+ end
33
+ end
34
+
35
+ private
36
+
37
+ attr_reader :options, :connection
38
+
39
+ def get(droplet_name)
40
+ if droplet_name.nil?
41
+ raise SimpleProvision::Server::MissingDropletName ,
42
+ "Please specify the Droplet Name using the --droplet-name option."
43
+ end
44
+ @server = connection.servers.detect { |server| server.name == droplet_name }
45
+ if options.has_key?(:private_key_path)
46
+ @server.private_key_path = options.fetch(:private_key_path)
47
+ end
48
+ @server
49
+ end
50
+
51
+ def name
52
+ "#{options.fetch(:name).downcase.sub(/ /, '-')}-#{Time.now.strftime("%y-%m-%d-%H-%M")}"
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,3 @@
1
+ module SimpleProvision
2
+ VERSION = "0.99.2"
3
+ end
@@ -0,0 +1,14 @@
1
+ require "simple_provision/version"
2
+
3
+ begin
4
+ require 'pry'
5
+ rescue LoadError
6
+ end
7
+
8
+ require_relative 'simple_provision/cli'
9
+ require_relative 'simple_provision/configuration'
10
+ require_relative 'simple_provision/connection'
11
+ require_relative 'simple_provision/scp'
12
+ require_relative 'simple_provision/server'
13
+
14
+ require_relative 'ext/fog'
@@ -0,0 +1,25 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'simple_provision/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "simple_provision"
8
+ spec.version = SimpleProvision::VERSION
9
+ spec.authors = ["Phuong Gia Su", "Brandon Hilkert"]
10
+ spec.email = ["phuongnd08@gmail.com", "brandonhilkert@gmail.com"]
11
+ spec.description = %q{The easiest, most common sense server provision tool.}
12
+ spec.summary = %q{The easiest, most common sense server provision tool.}
13
+ spec.homepage = "https://github.com/phuongnd08/simple_provision"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+
24
+ spec.add_dependency "fog", "~> 1.14.0"
25
+ end
metadata ADDED
@@ -0,0 +1,104 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: simple_provision
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.99.2
5
+ platform: ruby
6
+ authors:
7
+ - Phuong Gia Su
8
+ - Brandon Hilkert
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2014-05-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: bundler
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ~>
19
+ - !ruby/object:Gem::Version
20
+ version: '1.3'
21
+ type: :development
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ~>
26
+ - !ruby/object:Gem::Version
27
+ version: '1.3'
28
+ - !ruby/object:Gem::Dependency
29
+ name: rake
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :development
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: fog
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ~>
47
+ - !ruby/object:Gem::Version
48
+ version: 1.14.0
49
+ type: :runtime
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ~>
54
+ - !ruby/object:Gem::Version
55
+ version: 1.14.0
56
+ description: The easiest, most common sense server provision tool.
57
+ email:
58
+ - phuongnd08@gmail.com
59
+ - brandonhilkert@gmail.com
60
+ executables:
61
+ - simpro
62
+ extensions: []
63
+ extra_rdoc_files: []
64
+ files:
65
+ - .gitignore
66
+ - Gemfile
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - bin/simpro
71
+ - lib/ext/fog.rb
72
+ - lib/simple_provision.rb
73
+ - lib/simple_provision/cli.rb
74
+ - lib/simple_provision/configuration.rb
75
+ - lib/simple_provision/connection.rb
76
+ - lib/simple_provision/scp.rb
77
+ - lib/simple_provision/server.rb
78
+ - lib/simple_provision/version.rb
79
+ - simple_provision.gemspec
80
+ homepage: https://github.com/phuongnd08/simple_provision
81
+ licenses:
82
+ - MIT
83
+ metadata: {}
84
+ post_install_message:
85
+ rdoc_options: []
86
+ require_paths:
87
+ - lib
88
+ required_ruby_version: !ruby/object:Gem::Requirement
89
+ requirements:
90
+ - - '>='
91
+ - !ruby/object:Gem::Version
92
+ version: '0'
93
+ required_rubygems_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - '>='
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ requirements: []
99
+ rubyforge_project:
100
+ rubygems_version: 2.0.3
101
+ signing_key:
102
+ specification_version: 4
103
+ summary: The easiest, most common sense server provision tool.
104
+ test_files: []