niso 2.0.3
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/.gitignore +5 -0
- data/.travis.yml +3 -0
- data/CHANGELOG.md +21 -0
- data/Gemfile +3 -0
- data/README.md +89 -0
- data/Rakefile +9 -0
- data/bin/niso +8 -0
- data/lib/niso/cli.rb +164 -0
- data/lib/niso/cloud/base.rb +103 -0
- data/lib/niso/cloud/droplet_kit.rb +92 -0
- data/lib/niso/cloud.rb +22 -0
- data/lib/niso/dependency.rb +38 -0
- data/lib/niso/logger.rb +17 -0
- data/lib/niso/utility.rb +17 -0
- data/lib/niso.rb +20 -0
- data/lib/templates/create/.gitignore +1 -0
- data/lib/templates/create/files/.gitkeep +0 -0
- data/lib/templates/create/install.sh +50 -0
- data/lib/templates/create/niso.yml +30 -0
- data/lib/templates/create/recipes/niso.sh +47 -0
- data/lib/templates/create/roles/db.sh +5 -0
- data/lib/templates/create/roles/web.sh +4 -0
- data/lib/templates/setup/droplet_kit.yml +15 -0
- data/niso.gemspec +23 -0
- data/test/niso_test_dir/files/nginx/nginx.conf +0 -0
- data/test/niso_test_dir/recipes/nginx.sh +0 -0
- data/test/test_cli.rb +52 -0
- metadata +145 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 44a5eba8f95a0e95645da843a3f3f17c342bdc42
|
4
|
+
data.tar.gz: aa7b1186b90247e42dfc3af1086b8b554333fc6d
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: f3190d8deba79bb938f78fca3580a840a04906b83420819b023b75b90654487619b6cecc7e7b7ebbe9fd6eb4319297a80d19c72cd685e526248bd882f4bdccf5
|
7
|
+
data.tar.gz: 36244fbc3c8b5be59fd58afd3f4d82272b6a2584494f14e2101f85ef770b17180a1a842d5343ffb3f2d1cf491ac3572f72d6bdc80417dd0520ecdc378394b14b
|
data/.travis.yml
ADDED
data/CHANGELOG.md
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
## 2.0, release 2017-03-28
|
2
|
+
* updated to explicitly use digital ocean for `niso setup` and `niso teardown`
|
3
|
+
* added `droplet_kit`
|
4
|
+
* removed `digital_ocean`
|
5
|
+
|
6
|
+
## 1.5, release 2013-09-27
|
7
|
+
* `niso deploy [linode|digital_ocean] [name]` will deploy to the instance with additional attributes.
|
8
|
+
|
9
|
+
## 1.4, release 2013-09-27
|
10
|
+
* `niso teardown` no longer requires instance names as an argument, it lets you choose from a list instead.
|
11
|
+
|
12
|
+
## Past Releases
|
13
|
+
* v1.3: SSH config support. Thanks to @toooooooby
|
14
|
+
* v1.2: Evaluate everything as ERB templates by default. Added "files" folder.
|
15
|
+
* v1.1: "set -e" by default. apt-get everywhere in place of aptitude. Linode DNS support for DigitalOcean instances.
|
16
|
+
* v1.0: System functions are refactored into niso.mute() and niso.install().
|
17
|
+
* v0.9: Support for [DigitalOcean](https://www.digitalocean.com) setup / teardown.
|
18
|
+
* v0.8: Added `--sudo` option to `niso deploy`.
|
19
|
+
* v0.7: Added `erase_remote_folder` and `cache_remote_recipes` preferences for customized behavior.
|
20
|
+
* v0.6: System function niso::silencer() added for succinct log messages.
|
21
|
+
* v0.5: Role-based configuration supported. Reworked directory structure. **Incompatible with previous versions**.
|
data/Gemfile
ADDED
data/README.md
ADDED
@@ -0,0 +1,89 @@
|
|
1
|
+
# nîso ᓃᓱ
|
2
|
+
|
3
|
+
niso is the number 2 (two) in Cree
|
4
|
+
|
5
|
+
## Quickstart
|
6
|
+
|
7
|
+
Install:
|
8
|
+
|
9
|
+
$ [sudo] gem install niso
|
10
|
+
|
11
|
+
Go into your project directory (if it's a Rails project, `config` would be a good place to start with), then:
|
12
|
+
|
13
|
+
$ niso create
|
14
|
+
|
15
|
+
It generates a `niso` folder along with subdirectories and templates. Inside `niso`, there are `niso.yml` and `install.sh`. Those two are the most important files that you mainly work on.
|
16
|
+
|
17
|
+
Go into the `niso` directory, then run `niso deploy`:
|
18
|
+
|
19
|
+
$ cd niso
|
20
|
+
$ niso deploy example.com
|
21
|
+
|
22
|
+
## Commands
|
23
|
+
|
24
|
+
$ niso # Show command help
|
25
|
+
$ niso compile # Compile niso project
|
26
|
+
$ niso create # Create a new niso project
|
27
|
+
$ niso deploy [user@host:port] [role] [--sudo] # Deploy niso project
|
28
|
+
|
29
|
+
$ niso setup # Setup a new digital ocean droplet
|
30
|
+
$ niso teardown # Teardown an existing digital ocean droplet
|
31
|
+
|
32
|
+
## Directory structure
|
33
|
+
|
34
|
+
Here's the directory structure that `niso create` automatically generates:
|
35
|
+
|
36
|
+
niso/
|
37
|
+
install.sh # main script
|
38
|
+
niso.yml # add custom attributes and remote recipes here
|
39
|
+
|
40
|
+
recipes/ # put commonly used scripts here, referred from install.sh
|
41
|
+
niso.sh
|
42
|
+
roles/ # when role is specified, scripts here will be concatenated
|
43
|
+
db.sh # to install.sh in the compile phase
|
44
|
+
web.sh
|
45
|
+
files/ # put any files to be transferred
|
46
|
+
compiled/ # everything under this folder will be transferred to the
|
47
|
+
# remote server (do not edit directly)
|
48
|
+
|
49
|
+
## Dynamic values?
|
50
|
+
|
51
|
+
There are two ways to pass dynamic values to the script - ruby and bash.
|
52
|
+
|
53
|
+
Make sure `eval_erb: true` is set in `niso.yml`. In the compile phase, attributes defined in `niso.yml` are accessible from any files in the form of `<%= @attributes.ruby_version %>`.
|
54
|
+
|
55
|
+
## Role-based configuration
|
56
|
+
|
57
|
+
You probably have different configurations between **web servers** and **database servers**.
|
58
|
+
|
59
|
+
No problem - how niso handles role-based configuration is refreshingly simple.
|
60
|
+
|
61
|
+
Shell scripts under the `roles` directory, such as `web.sh` or `db.sh`, are automatically recognized as a role. The role script will be appended to `install.sh` at deploy, so you should put common configurations in `install.sh` and role specific procedures in the role script.
|
62
|
+
|
63
|
+
For instance, when you set up a new web server, deploy with a role name:
|
64
|
+
|
65
|
+
niso deploy example.com web
|
66
|
+
|
67
|
+
It is equivalent to running `install.sh`, followed by `web.sh`.
|
68
|
+
|
69
|
+
### Requirements
|
70
|
+
|
71
|
+
For `niso deploy`
|
72
|
+
|
73
|
+
$ [sudo] gem install droplet_kit
|
74
|
+
$ [sudo] gem install highline
|
75
|
+
|
76
|
+
### DigitalOcean
|
77
|
+
You can setup a new VM, or teardown an existing VM interactively. Use `niso setup` and `niso teardown` for that.
|
78
|
+
|
79
|
+
[DigitalOcean](https://www.digitalocean.com) only.
|
80
|
+
|
81
|
+
v2.0 of the DigitalOcean API is supported — get your [access token](https://cloud.digitalocean.com/api_access).
|
82
|
+
|
83
|
+
### What's new:
|
84
|
+
|
85
|
+
Please see the [CHANGELOG](https://github.com/dakotalightning/niso/blob/master/CHANGELOG.md).
|
86
|
+
|
87
|
+
## Credits
|
88
|
+
|
89
|
+
- This project is a hard fork of the proejct [niso](https://github.com/kenn/niso) by [kenn](https://github.com/kenn).
|
data/Rakefile
ADDED
data/bin/niso
ADDED
data/lib/niso/cli.rb
ADDED
@@ -0,0 +1,164 @@
|
|
1
|
+
require 'open3'
|
2
|
+
require 'ostruct'
|
3
|
+
require 'net/ssh'
|
4
|
+
|
5
|
+
module Niso
|
6
|
+
class Cli < Thor
|
7
|
+
include Thor::Actions
|
8
|
+
|
9
|
+
desc 'create', 'Create niso project'
|
10
|
+
def create(project = 'niso')
|
11
|
+
do_create(project)
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'deploy [user@host:port] [role] [--sudo] or deploy droplet [name] [role] [--sudo]', 'Deploy niso project'
|
15
|
+
method_options :sudo => false
|
16
|
+
def deploy(first, *args)
|
17
|
+
do_deploy(first, *args)
|
18
|
+
end
|
19
|
+
|
20
|
+
desc 'compile', 'Compile niso project'
|
21
|
+
def compile(role = nil)
|
22
|
+
do_compile(role)
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'setup', 'Setup a new VM'
|
26
|
+
def setup(provider = "droplet_kit")
|
27
|
+
Niso::Cloud.new(self, provider).setup
|
28
|
+
end
|
29
|
+
|
30
|
+
desc 'teardown', 'Teardown an existing VM'
|
31
|
+
def teardown(provider = "droplet_kit")
|
32
|
+
Niso::Cloud.new(self, provider).teardown
|
33
|
+
end
|
34
|
+
|
35
|
+
desc 'version', 'Show version'
|
36
|
+
def version
|
37
|
+
puts Gem.loaded_specs['niso'].version.to_s
|
38
|
+
end
|
39
|
+
|
40
|
+
no_tasks do
|
41
|
+
include Niso::Utility
|
42
|
+
|
43
|
+
def self.source_root
|
44
|
+
File.expand_path('../../',__FILE__)
|
45
|
+
end
|
46
|
+
|
47
|
+
def do_create(project)
|
48
|
+
copy_file 'templates/create/.gitignore', "#{project}/.gitignore"
|
49
|
+
copy_file 'templates/create/niso.yml', "#{project}/niso.yml"
|
50
|
+
copy_file 'templates/create/install.sh', "#{project}/install.sh"
|
51
|
+
copy_file 'templates/create/recipes/niso.sh', "#{project}/recipes/niso.sh"
|
52
|
+
copy_file 'templates/create/roles/db.sh', "#{project}/roles/db.sh"
|
53
|
+
copy_file 'templates/create/roles/web.sh', "#{project}/roles/web.sh"
|
54
|
+
copy_file 'templates/create/files/.gitkeep', "#{project}/files/.gitkeep"
|
55
|
+
end
|
56
|
+
|
57
|
+
def do_deploy(first, *args)
|
58
|
+
if ['droplet'].include?(first)
|
59
|
+
@instance_attributes = YAML.load(File.read("#{first}/instances/#{args[0]}.yml"))
|
60
|
+
target = @instance_attributes[:fqdn]
|
61
|
+
role = args[1]
|
62
|
+
else
|
63
|
+
target = first
|
64
|
+
role = args[0]
|
65
|
+
end
|
66
|
+
|
67
|
+
sudo = 'sudo ' if options.sudo?
|
68
|
+
user, host, port = parse_target(target)
|
69
|
+
endpoint = "#{user}@#{host}"
|
70
|
+
|
71
|
+
# compile attributes and recipes
|
72
|
+
do_compile(role)
|
73
|
+
|
74
|
+
# The host key might change when we instantiate a new VM, so
|
75
|
+
# we remove (-R) the old host key from known_hosts.
|
76
|
+
`ssh-keygen -R #{host} 2> /dev/null`
|
77
|
+
|
78
|
+
remote_commands = <<-EOS
|
79
|
+
rm -rf ~/niso &&
|
80
|
+
mkdir ~/niso &&
|
81
|
+
cd ~/niso &&
|
82
|
+
tar xz &&
|
83
|
+
#{sudo}bash install.sh
|
84
|
+
EOS
|
85
|
+
|
86
|
+
remote_commands.strip! << ' && rm -rf ~/niso' if @config['preferences'] and @config['preferences']['erase_remote_folder']
|
87
|
+
|
88
|
+
local_commands = <<-EOS
|
89
|
+
cd compiled
|
90
|
+
tar cz . | ssh -o 'StrictHostKeyChecking no' #{endpoint} -p #{port} '#{remote_commands}'
|
91
|
+
EOS
|
92
|
+
|
93
|
+
Open3.popen3(local_commands) do |stdin, stdout, stderr|
|
94
|
+
stdin.close
|
95
|
+
t = Thread.new do
|
96
|
+
while (line = stderr.gets)
|
97
|
+
print line.color(:red)
|
98
|
+
end
|
99
|
+
end
|
100
|
+
while (line = stdout.gets)
|
101
|
+
print line.color(:green)
|
102
|
+
end
|
103
|
+
t.join
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def do_compile(role)
|
108
|
+
# Check if you're in the niso directory
|
109
|
+
abort_with 'You must be in the niso folder' unless File.exists?('niso.yml')
|
110
|
+
# Check if role exists
|
111
|
+
abort_with "#{role} doesn't exist!" if role and !File.exists?("roles/#{role}.sh")
|
112
|
+
|
113
|
+
# Load niso.yml
|
114
|
+
@config = YAML.load(File.read('niso.yml'))
|
115
|
+
|
116
|
+
# Merge instance attributes
|
117
|
+
@config['attributes'] ||= {}
|
118
|
+
@config['attributes'].update(Hash[@instance_attributes.map{|k,v| [k.to_s, v] }]) if @instance_attributes
|
119
|
+
|
120
|
+
# Break down attributes into individual files
|
121
|
+
(@config['attributes'] || {}).each {|key, value| create_file "compiled/attributes/#{key}", value }
|
122
|
+
|
123
|
+
# Retrieve remote recipes via HTTP
|
124
|
+
cache_remote_recipes = @config['preferences'] && @config['preferences']['cache_remote_recipes']
|
125
|
+
(@config['recipes'] || []).each do |key, value|
|
126
|
+
next if cache_remote_recipes and File.exists?("compiled/recipes/#{key}.sh")
|
127
|
+
get value, "compiled/recipes/#{key}.sh"
|
128
|
+
end
|
129
|
+
|
130
|
+
copy_or_template = (@config['preferences'] && @config['preferences']['eval_erb']) ? :template : :copy_file
|
131
|
+
copy_local_files(@config, copy_or_template)
|
132
|
+
|
133
|
+
# Build install.sh
|
134
|
+
if role
|
135
|
+
if copy_or_template == :template
|
136
|
+
template File.expand_path('install.sh'), 'compiled/_install.sh'
|
137
|
+
create_file 'compiled/install.sh', File.binread('compiled/_install.sh') << "\n" << File.binread("compiled/roles/#{role}.sh")
|
138
|
+
else
|
139
|
+
create_file 'compiled/install.sh', File.binread('install.sh') << "\n" << File.binread("roles/#{role}.sh")
|
140
|
+
end
|
141
|
+
else
|
142
|
+
send copy_or_template, File.expand_path('install.sh'), 'compiled/install.sh'
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def parse_target(target)
|
147
|
+
target.match(/(.*@)?(.*?)(:.*)?$/)
|
148
|
+
# Load ssh config if it exists
|
149
|
+
config = Net::SSH::Config.for($2)
|
150
|
+
[ ($1 && $1.delete('@') || config[:user] || 'root'),
|
151
|
+
config[:host_name] || $2,
|
152
|
+
($3 && $3.delete(':') || config[:port] && config[:port].to_s || '22') ]
|
153
|
+
end
|
154
|
+
|
155
|
+
def copy_local_files(config, copy_or_template)
|
156
|
+
@attributes = OpenStruct.new(config['attributes'])
|
157
|
+
files = Dir['{recipes,roles,files}/**/*'].select { |file| File.file?(file) }
|
158
|
+
files.each { |file| send copy_or_template, File.expand_path(file), File.expand_path("compiled/#{file}") }
|
159
|
+
|
160
|
+
(config['files'] || []).each {|file| send copy_or_template, File.expand_path(file), "compiled/files/#{File.basename(file)}" }
|
161
|
+
end
|
162
|
+
end
|
163
|
+
end
|
164
|
+
end
|
@@ -0,0 +1,103 @@
|
|
1
|
+
Niso::Dependency.load('highline')
|
2
|
+
|
3
|
+
module Niso
|
4
|
+
class Cloud
|
5
|
+
class Base
|
6
|
+
include Niso::Utility
|
7
|
+
|
8
|
+
def initialize(cli, provider)
|
9
|
+
@provider = provider
|
10
|
+
@cli = cli
|
11
|
+
@ui = HighLine.new
|
12
|
+
end
|
13
|
+
|
14
|
+
def setup
|
15
|
+
unless File.exist? provider_config_path
|
16
|
+
@cli.empty_directory "#{@provider}/instances"
|
17
|
+
@cli.template "templates/setup/#{@provider}.yml", provider_config_path
|
18
|
+
exit_with "Now go ahead and edit #{@provider}.yml, then run this command again!"
|
19
|
+
end
|
20
|
+
|
21
|
+
# get the config { provider }.yml
|
22
|
+
assign_config
|
23
|
+
|
24
|
+
if @config['name'] == 'example-droplet-01'
|
25
|
+
abort_with "You must have your own settings in #{@provider}.yml"
|
26
|
+
end
|
27
|
+
|
28
|
+
if @config['access_token'] == 'your_api_key'
|
29
|
+
abort_with "You must add a valid access_token in #{@provider}.yml"
|
30
|
+
end
|
31
|
+
|
32
|
+
# Ask environment and hostname
|
33
|
+
@env = ask_menu("environment?", @config['environments'])
|
34
|
+
|
35
|
+
@name = "#{@config['name']}-#{@env}"
|
36
|
+
abort_with "#{@name} already exists!" if instance_config_path.exist?
|
37
|
+
|
38
|
+
# get the client @client
|
39
|
+
setup_client
|
40
|
+
|
41
|
+
@attributes = {}
|
42
|
+
do_setup
|
43
|
+
|
44
|
+
# Save instance info
|
45
|
+
@cli.create_file instance_config_path, YAML.dump(@instance)
|
46
|
+
end
|
47
|
+
|
48
|
+
def teardown
|
49
|
+
names = Dir.glob("#{@provider}/instances/*.yml").map{|i| i.split('/').last.sub('.yml','') }
|
50
|
+
abort_with "No match found with #{@provider}/instances/*.yml" if names.empty?
|
51
|
+
|
52
|
+
@name = ask_menu("which instance?: ", names)
|
53
|
+
|
54
|
+
assign_config
|
55
|
+
|
56
|
+
@instance = YAML.load(instance_config_path.read)
|
57
|
+
|
58
|
+
# Are you sure?
|
59
|
+
moveon = ask("Are you sure about deleting #{@instance[:name]} permanently? (y/n) ", String) {|q| q.in = ['y','n']}
|
60
|
+
exit unless moveon == 'y'
|
61
|
+
|
62
|
+
# Run Linode / DigitalOcean specific tasks
|
63
|
+
setup_client
|
64
|
+
do_teardown
|
65
|
+
|
66
|
+
# Remove the instance config file
|
67
|
+
@cli.remove_file instance_config_path
|
68
|
+
|
69
|
+
say 'Done.'
|
70
|
+
end
|
71
|
+
|
72
|
+
def assign_config
|
73
|
+
@config = YAML.load(provider_config_path.read)
|
74
|
+
end
|
75
|
+
|
76
|
+
def provider_config_path
|
77
|
+
Pathname.new "#{@provider}/#{@provider}.yml"
|
78
|
+
end
|
79
|
+
|
80
|
+
def instance_config_path
|
81
|
+
Pathname.new "#{@provider}/instances/#{@name}.yml"
|
82
|
+
end
|
83
|
+
|
84
|
+
def ask(question, answer_type, &details)
|
85
|
+
@ui.ask(@ui.color(question, :green, :bold), answer_type, &details)
|
86
|
+
end
|
87
|
+
|
88
|
+
def ask_menu(question, choices)
|
89
|
+
answer = @ui.choose do |menu|
|
90
|
+
menu.prompt = "#{@ui.color(question, :green, :bold)} "
|
91
|
+
choices.each { |n| menu.choice(n) }
|
92
|
+
end
|
93
|
+
say("=> #{answer}")
|
94
|
+
answer
|
95
|
+
end
|
96
|
+
|
97
|
+
def proceed?
|
98
|
+
moveon = ask("Are you ready to go ahead and create #{@name}? (y/n) ", String) {|q| q.in = ['y','n']}
|
99
|
+
exit unless moveon == 'y'
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
Niso::Dependency.load('droplet_kit')
|
2
|
+
|
3
|
+
module Niso
|
4
|
+
class Cloud
|
5
|
+
class DropletKit < Base
|
6
|
+
def do_setup
|
7
|
+
|
8
|
+
if @config["size"]
|
9
|
+
# Choose size
|
10
|
+
@attributes[:size] = @config["size"]
|
11
|
+
say "using config size"
|
12
|
+
else
|
13
|
+
choose(:size, @client.sizes.all())
|
14
|
+
end
|
15
|
+
|
16
|
+
if @config["region"]
|
17
|
+
# Choose region
|
18
|
+
@attributes[:region] = @config["region"]
|
19
|
+
say "using config region"
|
20
|
+
else
|
21
|
+
choose(:region, @client.regions.all())
|
22
|
+
end
|
23
|
+
|
24
|
+
# Choose an image
|
25
|
+
if @config['images_filter']
|
26
|
+
result = @client.images.all(type: @config['images_filter'])
|
27
|
+
result = result.select{|i| i.distribution.match Regexp.new(@config['images_filter'], Regexp::IGNORECASE) }
|
28
|
+
end
|
29
|
+
choose(:image, result)
|
30
|
+
|
31
|
+
# Go ahead?
|
32
|
+
moveon = ask("Are you ready to go ahead and create #{@attributes}? (y/n) ", ['y','n'])
|
33
|
+
exit unless moveon == 'y'
|
34
|
+
|
35
|
+
ssh_keys = @client.ssh_keys.all.collect { |key| key.fingerprint }
|
36
|
+
|
37
|
+
# Create
|
38
|
+
say "creating a new droplet..."
|
39
|
+
droplet = ::DropletKit::Droplet.new(name: @config["name"], region: @attributes[:region_slug], image: @attributes[:image_slug], size: @attributes[:size_slug], ssh_keys: ssh_keys)
|
40
|
+
result = @client.droplets.create(droplet)
|
41
|
+
|
42
|
+
@droplet_id = result.id
|
43
|
+
say "Created a new droplet (id: #{@droplet_id}). Booting..."
|
44
|
+
|
45
|
+
# Boot - we need this before getting public IP
|
46
|
+
while @client.droplets.find(id: @droplet_id).status.downcase != 'active'
|
47
|
+
sleep 3
|
48
|
+
end
|
49
|
+
|
50
|
+
@networks = @client.droplets.find(id: @droplet_id).networks
|
51
|
+
say "Done. networks = #{@networks.ip_address}"
|
52
|
+
|
53
|
+
@instance = {
|
54
|
+
:droplet_id => @droplet_id,
|
55
|
+
:env => @env,
|
56
|
+
:host => @host,
|
57
|
+
:name => @name,
|
58
|
+
:networks => @networks,
|
59
|
+
:size_slug => @attributes[:size_slug],
|
60
|
+
:region_slug => @attributes[:region_slug],
|
61
|
+
:image_slug => @attributes[:image_slug],
|
62
|
+
}
|
63
|
+
end
|
64
|
+
|
65
|
+
def choose(key, result)
|
66
|
+
abort "no #{key} found!" if result.first.nil?
|
67
|
+
# result.each{|i| say "#{i.slug}" }
|
68
|
+
choices = result.map(&:slug).compact
|
69
|
+
@attributes[:"#{key}_slug"] = ask_menu("which #{key}?: ", choices)
|
70
|
+
end
|
71
|
+
|
72
|
+
def do_teardown
|
73
|
+
say 'deleting droplet...'
|
74
|
+
@client.droplets.delete(id: @instance[:droplet_id])
|
75
|
+
end
|
76
|
+
|
77
|
+
# Set the client for DropletKit
|
78
|
+
#
|
79
|
+
# DropletKit::DropletResource
|
80
|
+
def setup_client
|
81
|
+
begin
|
82
|
+
@client = ::DropletKit::Client.new(access_token: @config['access_token'])
|
83
|
+
info = @client.account.info()
|
84
|
+
moveon = ask(" ... continue with #{info.email} account? (y/n) ", String) {|q| q.in = ['y','n']}
|
85
|
+
exit unless moveon == 'y'
|
86
|
+
rescue ::DropletKit::Error => e
|
87
|
+
abort_with e.message
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
92
|
+
end
|
data/lib/niso/cloud.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Niso
|
2
|
+
class Cloud
|
3
|
+
include Niso::Utility
|
4
|
+
|
5
|
+
def initialize(cli, provider)
|
6
|
+
@subject = case provider
|
7
|
+
when 'droplet_kit'
|
8
|
+
Niso::Cloud::DropletKit.new(cli, provider)
|
9
|
+
else
|
10
|
+
abort_with "Provider #{provider} is not valid!"
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(sym, *args, &block)
|
15
|
+
@subject.send sym, *args, &block
|
16
|
+
end
|
17
|
+
|
18
|
+
def respond_to?(method)
|
19
|
+
@subject.respond_to?(sym) || super
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,38 @@
|
|
1
|
+
module Niso
|
2
|
+
class Dependency
|
3
|
+
def self.all
|
4
|
+
{
|
5
|
+
'highline' => { :require => 'highline', :version => '>= 1.7.8'},
|
6
|
+
'droplet_kit' => { :require => 'droplet_kit', :version => '>= 2.1.0' },
|
7
|
+
}
|
8
|
+
end
|
9
|
+
|
10
|
+
def self.load(name)
|
11
|
+
begin
|
12
|
+
gem(name, all[name][:version])
|
13
|
+
require(all[name][:require])
|
14
|
+
rescue LoadError
|
15
|
+
if $!.to_s =~ /Gemfile/
|
16
|
+
Logger.error <<-EOS
|
17
|
+
Dependency missing: #{name}
|
18
|
+
Add this line to your application's Gemfile.
|
19
|
+
|
20
|
+
gem '#{name}', '#{all[name][:version]}'
|
21
|
+
|
22
|
+
Please try again after running "bundle install".
|
23
|
+
EOS
|
24
|
+
else
|
25
|
+
Logger.error <<-EOS
|
26
|
+
Dependency missing: #{name}
|
27
|
+
To install the gem, issue the following command:
|
28
|
+
|
29
|
+
gem install #{name} -v '#{all[name][:version]}'
|
30
|
+
|
31
|
+
Please try again after installing the missing dependency.
|
32
|
+
EOS
|
33
|
+
end
|
34
|
+
abort
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
data/lib/niso/logger.rb
ADDED
data/lib/niso/utility.rb
ADDED
data/lib/niso.rb
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'rainbow'
|
3
|
+
require 'yaml'
|
4
|
+
|
5
|
+
# Starting 2.0.0, Rainbow no longer patches string with the color method by default.
|
6
|
+
require 'rainbow/version'
|
7
|
+
require 'rainbow/ext/string' unless Rainbow::VERSION < '2.0.0'
|
8
|
+
|
9
|
+
module Niso
|
10
|
+
autoload :Cli, 'niso/cli'
|
11
|
+
autoload :Cloud, 'niso/cloud'
|
12
|
+
autoload :Dependency, 'niso/dependency'
|
13
|
+
autoload :Logger, 'niso/logger'
|
14
|
+
autoload :Utility, 'niso/utility'
|
15
|
+
|
16
|
+
class Cloud
|
17
|
+
autoload :Base, 'niso/cloud/base'
|
18
|
+
autoload :DropletKit, 'niso/cloud/droplet_kit'
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
/compiled
|
File without changes
|
@@ -0,0 +1,50 @@
|
|
1
|
+
#!/bin/bash
|
2
|
+
set -e
|
3
|
+
|
4
|
+
# Load base utility functions like niso.mute() and niso.install()
|
5
|
+
source recipes/.sh
|
6
|
+
|
7
|
+
# This line is necessary for automated provisioning for Debian/Ubuntu.
|
8
|
+
# Remove if you're not on Debian/Ubuntu.
|
9
|
+
export DEBIAN_FRONTEND=noninteractive
|
10
|
+
|
11
|
+
# Add Dotdeb repository. Recommended if you're using Debian. See http://www.dotdeb.org/about/
|
12
|
+
# source recipes/dotdeb.sh
|
13
|
+
# source recipes/backports.sh
|
14
|
+
|
15
|
+
# Update apt catalog and upgrade installed packages
|
16
|
+
niso.mute "apt-get update"
|
17
|
+
niso.mute "apt-get -y upgrade"
|
18
|
+
|
19
|
+
# Install packages
|
20
|
+
apt-get -y install git-core ntp curl
|
21
|
+
|
22
|
+
# Install sysstat, then configure if this is a new install.
|
23
|
+
if niso.install "sysstat"; then
|
24
|
+
sed -i 's/ENABLED="false"/ENABLED="true"/' /etc/default/sysstat
|
25
|
+
/etc/init.d/sysstat restart
|
26
|
+
fi
|
27
|
+
|
28
|
+
# Set RAILS_ENV
|
29
|
+
environment=<%= @attributes.environment %>
|
30
|
+
|
31
|
+
if ! grep -Fq "RAILS_ENV" ~/.bash_profile; then
|
32
|
+
echo 'Setting up RAILS_ENV...'
|
33
|
+
echo "export RAILS_ENV=$environment" >> ~/.bash_profile
|
34
|
+
source ~/.bash_profile
|
35
|
+
fi
|
36
|
+
|
37
|
+
# Install Ruby using RVM
|
38
|
+
source recipes/rvm.sh
|
39
|
+
ruby_version=<%= @attributes.ruby_version %>
|
40
|
+
|
41
|
+
if [[ "$(which ruby)" != /usr/local/rvm/rubies/ruby-$ruby_version* ]]; then
|
42
|
+
echo "Installing ruby-$ruby_version"
|
43
|
+
# Install dependencies using RVM autolibs - see https://blog.engineyard.com/2013/rvm-ruby-2-0
|
44
|
+
rvm install --autolibs=3 $ruby_version
|
45
|
+
rvm $ruby_version --default
|
46
|
+
echo 'gem: --no-ri --no-rdoc' > ~/.gemrc
|
47
|
+
|
48
|
+
# Install Bundler
|
49
|
+
gem install bundler
|
50
|
+
fi
|
@@ -0,0 +1,30 @@
|
|
1
|
+
---
|
2
|
+
# Dynamic variables here will be compiled to individual files in compiled/attributes.
|
3
|
+
attributes:
|
4
|
+
environment: production
|
5
|
+
ruby_version: 2.0.0
|
6
|
+
|
7
|
+
# Remote recipes here will be downloaded to compiled/recipes.
|
8
|
+
recipes:
|
9
|
+
rvm: https://raw.githubusercontent.com/kenn/niso-recipes/master/ruby/rvm.sh
|
10
|
+
# dotdeb: https://raw.githubusercontent.com/kenn/niso-recipes/master/debian/dotdeb-wheezy.sh
|
11
|
+
# backports: https://raw.githubusercontent.com/kenn/niso-recipes/master/debian/backports-wheezy.sh
|
12
|
+
# mongodb-10gen: https://raw.githubusercontent.com/kenn/niso-recipes/master/debian/mongodb-10gen.sh
|
13
|
+
|
14
|
+
# Files specified here will be copied to compiled/files.
|
15
|
+
# files:
|
16
|
+
# - ~/.ssh/id_rsa.pub
|
17
|
+
|
18
|
+
# Fine tune how Niso should work.
|
19
|
+
preferences:
|
20
|
+
# Erase the generated folder on the server after deploy.
|
21
|
+
# Turn on when you are done with testing and ready for production use.
|
22
|
+
erase_remote_folder: true
|
23
|
+
|
24
|
+
# Skip retrieving remote recipes when local copies already exist. This setting helps
|
25
|
+
# iterative deploy testing considerably faster, when you have a lot of remote recipes.
|
26
|
+
cache_remote_recipes: false
|
27
|
+
|
28
|
+
# Evaluate files as ERB templates. When enabled, you can pass dynamic values in the form
|
29
|
+
# of <%= @attributes.environment %> in recipes, roles, files and install.sh.
|
30
|
+
eval_erb: true
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# This file is used to define functions under the niso.* namespace.
|
2
|
+
|
3
|
+
# Set $niso_pkg to "apt-get" or "yum", or abort.
|
4
|
+
#
|
5
|
+
if which apt-get >/dev/null 2>&1; then
|
6
|
+
export niso_pkg=apt-get
|
7
|
+
elif which yum >/dev/null 2>&1; then
|
8
|
+
export niso_pkg=yum
|
9
|
+
fi
|
10
|
+
|
11
|
+
if [ "$niso_pkg" = '' ]; then
|
12
|
+
echo 'niso only supports apt-get or yum!' >&2
|
13
|
+
exit 1
|
14
|
+
fi
|
15
|
+
|
16
|
+
# Mute STDOUT and STDERR
|
17
|
+
#
|
18
|
+
function niso.mute() {
|
19
|
+
echo "Running \"$@\""
|
20
|
+
`$@ >/dev/null 2>&1`
|
21
|
+
return $?
|
22
|
+
}
|
23
|
+
|
24
|
+
# Installer
|
25
|
+
#
|
26
|
+
function niso.installed() {
|
27
|
+
if [ "$niso_pkg" = 'apt-get' ]; then
|
28
|
+
dpkg -s $@ >/dev/null 2>&1
|
29
|
+
elif [ "$niso_pkg" = 'yum' ]; then
|
30
|
+
rpm -qa | grep $@ >/dev/null
|
31
|
+
fi
|
32
|
+
return $?
|
33
|
+
}
|
34
|
+
|
35
|
+
# When there's "set -e" in install.sh, niso.install should be used with if statement,
|
36
|
+
# otherwise the script may exit unexpectedly when the package is already installed.
|
37
|
+
#
|
38
|
+
function niso.install() {
|
39
|
+
if niso.installed "$@"; then
|
40
|
+
echo "$@ already installed"
|
41
|
+
return 1
|
42
|
+
else
|
43
|
+
echo "No packages found matching $@. Installing..."
|
44
|
+
niso.mute "$niso_pkg -y install $@"
|
45
|
+
return 0
|
46
|
+
fi
|
47
|
+
}
|
@@ -0,0 +1,5 @@
|
|
1
|
+
# Install Database Server
|
2
|
+
|
3
|
+
# source recipes/mongodb-10gen.sh # MongoDB
|
4
|
+
# apt-get -q -y install mysql-server-5.5 # MySQL 5.5 - You may need to enable Dotdeb in install.sh first
|
5
|
+
# apt-get -y install redis-server # Redis - You may need to enable Dotdeb in install.sh first
|
@@ -0,0 +1,15 @@
|
|
1
|
+
---
|
2
|
+
access_token: your_api_key
|
3
|
+
|
4
|
+
name: example-droplet-01
|
5
|
+
# region: sf01
|
6
|
+
# image: ubuntu-14-04-x64
|
7
|
+
# size: 512mb
|
8
|
+
|
9
|
+
# filter out large lists by keyword
|
10
|
+
images_filter: ubuntu
|
11
|
+
|
12
|
+
# add / remove environments
|
13
|
+
environments:
|
14
|
+
- production
|
15
|
+
- staging
|
data/niso.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# coding: utf-8
|
2
|
+
|
3
|
+
Gem::Specification.new do |spec|
|
4
|
+
spec.name = 'niso'
|
5
|
+
spec.version = '2.0.3'
|
6
|
+
spec.authors = ['Dakota Lightning']
|
7
|
+
spec.email = ['im@koda.io']
|
8
|
+
spec.homepage = 'http://github.com/dakotalightning/niso'
|
9
|
+
spec.summary = %q{Server provisioning utility}
|
10
|
+
spec.description = %q{Server provisioning utility}
|
11
|
+
spec.license = 'MIT'
|
12
|
+
|
13
|
+
spec.files = `git ls-files`.split($/)
|
14
|
+
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
15
|
+
spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
|
16
|
+
spec.require_paths = ['lib']
|
17
|
+
|
18
|
+
spec.add_runtime_dependency 'thor'
|
19
|
+
spec.add_runtime_dependency 'rainbow'
|
20
|
+
spec.add_runtime_dependency 'net-ssh'
|
21
|
+
spec.add_development_dependency 'rake'
|
22
|
+
spec.add_development_dependency 'minitest'
|
23
|
+
end
|
File without changes
|
File without changes
|
data/test/test_cli.rb
ADDED
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'niso'
|
2
|
+
require 'minitest/autorun'
|
3
|
+
|
4
|
+
class TestCli < Minitest::Test
|
5
|
+
def setup
|
6
|
+
@cli = Niso::Cli.new
|
7
|
+
end
|
8
|
+
|
9
|
+
def test_parse_target
|
10
|
+
assert_equal ['user', 'example.com', '2222'], @cli.parse_target('user@example.com:2222')
|
11
|
+
assert_equal ['root', 'example.com', '2222'], @cli.parse_target('example.com:2222')
|
12
|
+
assert_equal ['user', 'example.com', '22'], @cli.parse_target('user@example.com')
|
13
|
+
assert_equal ['root', 'example.com', '22'], @cli.parse_target('example.com')
|
14
|
+
assert_equal ['root', '192.168.0.1', '22'], @cli.parse_target('192.168.0.1')
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_parse_target_with_ssh_config
|
18
|
+
ssh_config = lambda do |host|
|
19
|
+
if host == 'example.com'
|
20
|
+
{ :host_name => "buzz.example.com", :user => "foobar", :port => 2222 }
|
21
|
+
else
|
22
|
+
{}
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
Net::SSH::Config.stub(:for, ssh_config) do
|
27
|
+
assert_equal ['foobar', 'buzz.example.com', '2222'], @cli.parse_target('example.com')
|
28
|
+
assert_equal ['foobar', 'buzz.example.com', '8080'], @cli.parse_target('example.com:8080')
|
29
|
+
assert_equal ['piyo', 'buzz.example.com', '2222'], @cli.parse_target('piyo@example.com')
|
30
|
+
assert_equal ['piyo', 'buzz.example.com', '8080'], @cli.parse_target('piyo@example.com:8080')
|
31
|
+
assert_equal ['root', '192.168.0.1', '22'], @cli.parse_target('192.168.0.1')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def test_create
|
36
|
+
@cli.create 'sandbox'
|
37
|
+
assert File.exist?('sandbox/niso.yml')
|
38
|
+
FileUtils.rm_rf 'sandbox'
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_copy_local_files
|
42
|
+
pwd = Dir.pwd
|
43
|
+
Dir.chdir('test/niso_test_dir')
|
44
|
+
|
45
|
+
@cli.copy_local_files({}, :copy_file)
|
46
|
+
assert File.exists?('compiled/files/nginx/nginx.conf')
|
47
|
+
assert File.exists?('compiled/recipes/nginx.sh')
|
48
|
+
FileUtils.rm_rf 'compiled'
|
49
|
+
|
50
|
+
Dir.chdir(pwd)
|
51
|
+
end
|
52
|
+
end
|
metadata
ADDED
@@ -0,0 +1,145 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: niso
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 2.0.3
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Dakota Lightning
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2017-03-30 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: rainbow
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: net-ssh
|
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: rake
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0'
|
62
|
+
type: :development
|
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: minitest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">="
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '0'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '0'
|
83
|
+
description: Server provisioning utility
|
84
|
+
email:
|
85
|
+
- im@koda.io
|
86
|
+
executables:
|
87
|
+
- niso
|
88
|
+
extensions: []
|
89
|
+
extra_rdoc_files: []
|
90
|
+
files:
|
91
|
+
- ".gitignore"
|
92
|
+
- ".travis.yml"
|
93
|
+
- CHANGELOG.md
|
94
|
+
- Gemfile
|
95
|
+
- README.md
|
96
|
+
- Rakefile
|
97
|
+
- bin/niso
|
98
|
+
- lib/niso.rb
|
99
|
+
- lib/niso/cli.rb
|
100
|
+
- lib/niso/cloud.rb
|
101
|
+
- lib/niso/cloud/base.rb
|
102
|
+
- lib/niso/cloud/droplet_kit.rb
|
103
|
+
- lib/niso/dependency.rb
|
104
|
+
- lib/niso/logger.rb
|
105
|
+
- lib/niso/utility.rb
|
106
|
+
- lib/templates/create/.gitignore
|
107
|
+
- lib/templates/create/files/.gitkeep
|
108
|
+
- lib/templates/create/install.sh
|
109
|
+
- lib/templates/create/niso.yml
|
110
|
+
- lib/templates/create/recipes/niso.sh
|
111
|
+
- lib/templates/create/roles/db.sh
|
112
|
+
- lib/templates/create/roles/web.sh
|
113
|
+
- lib/templates/setup/droplet_kit.yml
|
114
|
+
- niso.gemspec
|
115
|
+
- test/niso_test_dir/files/nginx/nginx.conf
|
116
|
+
- test/niso_test_dir/recipes/nginx.sh
|
117
|
+
- test/test_cli.rb
|
118
|
+
homepage: http://github.com/dakotalightning/niso
|
119
|
+
licenses:
|
120
|
+
- MIT
|
121
|
+
metadata: {}
|
122
|
+
post_install_message:
|
123
|
+
rdoc_options: []
|
124
|
+
require_paths:
|
125
|
+
- lib
|
126
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
127
|
+
requirements:
|
128
|
+
- - ">="
|
129
|
+
- !ruby/object:Gem::Version
|
130
|
+
version: '0'
|
131
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
132
|
+
requirements:
|
133
|
+
- - ">="
|
134
|
+
- !ruby/object:Gem::Version
|
135
|
+
version: '0'
|
136
|
+
requirements: []
|
137
|
+
rubyforge_project:
|
138
|
+
rubygems_version: 2.5.1
|
139
|
+
signing_key:
|
140
|
+
specification_version: 4
|
141
|
+
summary: Server provisioning utility
|
142
|
+
test_files:
|
143
|
+
- test/niso_test_dir/files/nginx/nginx.conf
|
144
|
+
- test/niso_test_dir/recipes/nginx.sh
|
145
|
+
- test/test_cli.rb
|