hyrb 0.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +61 -0
- data/Rakefile +8 -0
- data/bin/hyrb +9 -0
- data/hyrb.gemspec +35 -0
- data/lib/hyrb/cli.rb +27 -0
- data/lib/hyrb/command.rb +74 -0
- data/lib/hyrb/commands/ansible.rb +13 -0
- data/lib/hyrb/commands/creds.rb +20 -0
- data/lib/hyrb/commands/defaults.rb +20 -0
- data/lib/hyrb/commands/developers.rb +15 -0
- data/lib/hyrb/commands/digital_ocean.rb +15 -0
- data/lib/hyrb/commands/environment.rb +11 -0
- data/lib/hyrb/commands/github.rb +15 -0
- data/lib/hyrb/commands/hipchat.rb +20 -0
- data/lib/hyrb/commands/project.rb +36 -0
- data/lib/hyrb/commands/provision.rb +17 -0
- data/lib/hyrb/commands/rackspace.rb +11 -0
- data/lib/hyrb/model.rb +96 -0
- data/lib/hyrb/models/ansible_host.rb +28 -0
- data/lib/hyrb/models/ansible_site.rb +46 -0
- data/lib/hyrb/models/cache.rb +35 -0
- data/lib/hyrb/models/creds.rb +24 -0
- data/lib/hyrb/models/defaults.rb +11 -0
- data/lib/hyrb/models/developer.rb +28 -0
- data/lib/hyrb/models/environment.rb +41 -0
- data/lib/hyrb/models/project.rb +35 -0
- data/lib/hyrb/pipeline.rb +63 -0
- data/lib/hyrb/task.rb +46 -0
- data/lib/hyrb/tasks/ansible.rb +96 -0
- data/lib/hyrb/tasks/creds.rb +39 -0
- data/lib/hyrb/tasks/defaults.rb +43 -0
- data/lib/hyrb/tasks/developers.rb +120 -0
- data/lib/hyrb/tasks/digital_ocean.rb +84 -0
- data/lib/hyrb/tasks/environment.rb +56 -0
- data/lib/hyrb/tasks/github.rb +120 -0
- data/lib/hyrb/tasks/google.rb +15 -0
- data/lib/hyrb/tasks/hipchat.rb +76 -0
- data/lib/hyrb/tasks/project/bootstrap.rb +88 -0
- data/lib/hyrb/tasks/project.rb +48 -0
- data/lib/hyrb/tasks/provision.rb +91 -0
- data/lib/hyrb/tasks/rackspace.rb +95 -0
- data/lib/hyrb/version.rb +3 -0
- data/lib/hyrb.rb +18 -0
- data/lib/templates/ansible/Vagrantfile.erb +21 -0
- data/lib/templates/ansible/roles/db/handlers/main.yml +2 -0
- data/lib/templates/ansible/roles/db/tasks/main.yml +24 -0
- data/lib/templates/ansible/roles/lamp/tasks/main.yml +53 -0
- data/lib/templates/ansible/roles/lamp.yml +6 -0
- data/lib/templates/ansible/roles/site/handlers/main.yml +3 -0
- data/lib/templates/ansible/roles/site/tasks/db.yml +23 -0
- data/lib/templates/ansible/roles/site/tasks/main.yml +16 -0
- data/lib/templates/ansible/roles/site/templates/vhost.conf.j2 +24 -0
- data/lib/templates/ansible/site.yml +10 -0
- data/lib/templates/roles/db/handlers/main.yml +2 -0
- data/lib/templates/roles/db/tasks/main.yml +24 -0
- data/lib/templates/roles/lamp/tasks/main.yml +36 -0
- data/lib/templates/roles/lamp.yml +6 -0
- data/lib/templates/roles/site/handlers/main.yml +3 -0
- data/lib/templates/roles/site/tasks/db.yml +23 -0
- data/lib/templates/roles/site/tasks/main.yml +16 -0
- data/lib/templates/roles/site/templates/vhost.conf.j2 +24 -0
- data/spec/hyrb/pipeline_spec.rb +91 -0
- data/spec/spec_helper.rb +5 -0
- metadata +295 -0
@@ -0,0 +1,35 @@
|
|
1
|
+
module Hyrb
|
2
|
+
module Models
|
3
|
+
module Cache
|
4
|
+
class Developers < Model
|
5
|
+
def initialize(data = nil)
|
6
|
+
super(File.join("cache", "developers"), data)
|
7
|
+
end
|
8
|
+
|
9
|
+
def serialize(data)
|
10
|
+
data.map(&:to_hash)
|
11
|
+
end
|
12
|
+
|
13
|
+
def deserialize(data)
|
14
|
+
data && data.map { |d| Developer.new(d) }
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
class DigitalOcean < Struct
|
19
|
+
define_keys %w( flavors images regions )
|
20
|
+
|
21
|
+
def initialize
|
22
|
+
super(File.join("cache", "digital_ocean"))
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class Rackspace < Struct
|
27
|
+
define_keys %w( flavors images )
|
28
|
+
|
29
|
+
def initialize
|
30
|
+
super(File.join("cache", "rackspace"))
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Hyrb
|
2
|
+
module Models
|
3
|
+
class Creds < Struct
|
4
|
+
define_keys %w( rackspace_api_key
|
5
|
+
rackspace_username
|
6
|
+
digital_ocean_api_key
|
7
|
+
digital_ocean_client_id
|
8
|
+
aws_access_key_id
|
9
|
+
aws_secret_access_key
|
10
|
+
github_org
|
11
|
+
github_api_key
|
12
|
+
hipchat_api_key
|
13
|
+
hipchat_user_id
|
14
|
+
google_client_id
|
15
|
+
google_client_secret
|
16
|
+
google_refresh_token
|
17
|
+
google_spreadsheet_key )
|
18
|
+
|
19
|
+
def initialize
|
20
|
+
super("creds")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Hyrb
|
2
|
+
module Models
|
3
|
+
class Developer
|
4
|
+
ROLE_MAP = {
|
5
|
+
1 => :admin,
|
6
|
+
2 => :developer,
|
7
|
+
3 => :contractor,
|
8
|
+
4 => :banned,
|
9
|
+
}
|
10
|
+
|
11
|
+
attr_accessor :name, :email, :role, :github_username, :keys, :digital_ocean_id
|
12
|
+
|
13
|
+
def initialize(hash = {})
|
14
|
+
hash.each do |k,v|
|
15
|
+
send("#{k}=", v)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def employee?
|
20
|
+
[:admin, :developer].include?(role)
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_hash
|
24
|
+
Hash[%w(name email role github_username keys).map { |a| [a, send(a)] }]
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Hyrb
|
2
|
+
module Models
|
3
|
+
class Environment < Struct
|
4
|
+
PROVIDERS = %w( digital_ocean rackspace )
|
5
|
+
|
6
|
+
attr_accessor :project
|
7
|
+
|
8
|
+
define_keys %w( name
|
9
|
+
host
|
10
|
+
domain
|
11
|
+
label
|
12
|
+
server_name
|
13
|
+
provider
|
14
|
+
server_id
|
15
|
+
deploy_user
|
16
|
+
base_path
|
17
|
+
relative_web_root
|
18
|
+
database_host
|
19
|
+
database_name
|
20
|
+
database_user
|
21
|
+
database_password
|
22
|
+
has_dns_record )
|
23
|
+
|
24
|
+
def initialize(project, name)
|
25
|
+
@name = name
|
26
|
+
super(File.join("projects", project.name, "project"))
|
27
|
+
self.project = project
|
28
|
+
self.name = name
|
29
|
+
end
|
30
|
+
|
31
|
+
def serialize(data)
|
32
|
+
project.environments[@name] = data
|
33
|
+
project.serialize(project.data)
|
34
|
+
end
|
35
|
+
|
36
|
+
def deserialize(data)
|
37
|
+
super(data["environments"][@name])
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Hyrb
|
2
|
+
module Models
|
3
|
+
class Project < Struct
|
4
|
+
define_keys %w( name
|
5
|
+
title
|
6
|
+
description
|
7
|
+
github_org
|
8
|
+
repo_name
|
9
|
+
github_team
|
10
|
+
room_name
|
11
|
+
users
|
12
|
+
environments
|
13
|
+
jira_project
|
14
|
+
has_hipchat_hook )
|
15
|
+
|
16
|
+
def initialize(name)
|
17
|
+
super(File.join("projects", name, "project"))
|
18
|
+
self.name = name
|
19
|
+
data.environments ||= { }
|
20
|
+
end
|
21
|
+
|
22
|
+
def developers(all_developers)
|
23
|
+
users ? all_developers.select {|dev| users.include? dev.email } : []
|
24
|
+
end
|
25
|
+
|
26
|
+
def repo_path
|
27
|
+
"#{github_org}/#{repo_name}"
|
28
|
+
end
|
29
|
+
|
30
|
+
def ansible_path
|
31
|
+
File.join(File.dirname(path), "ansible")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
require 'tsort'
|
2
|
+
|
3
|
+
module Hyrb
|
4
|
+
class Pipeline
|
5
|
+
include TSort
|
6
|
+
|
7
|
+
attr_accessor :prev_tasks, :next_tasks, :current_task, :env
|
8
|
+
|
9
|
+
def self.rules
|
10
|
+
@@rules ||= {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(task_class)
|
14
|
+
@task_class = task_class
|
15
|
+
@prev_tasks = []
|
16
|
+
@next_tasks = build_task_list
|
17
|
+
end
|
18
|
+
|
19
|
+
def run(env = {})
|
20
|
+
@env = env
|
21
|
+
while @current_task = @next_tasks.shift
|
22
|
+
task = @current_task.new(self)
|
23
|
+
info "--> Running #{@current_task}" if @env[:verbose]
|
24
|
+
task.run_before(@env)
|
25
|
+
task.run(@env)
|
26
|
+
@prev_tasks << @current_task
|
27
|
+
end
|
28
|
+
@env
|
29
|
+
end
|
30
|
+
|
31
|
+
def invoke(task_class)
|
32
|
+
pipeline = self.class.new(task_class)
|
33
|
+
pipeline.prev_tasks += self.prev_tasks
|
34
|
+
pipeline.next_tasks -= self.prev_tasks
|
35
|
+
pipeline.run(@env)
|
36
|
+
self.next_tasks -= pipeline.prev_tasks
|
37
|
+
end
|
38
|
+
|
39
|
+
def info(message)
|
40
|
+
puts message
|
41
|
+
end
|
42
|
+
|
43
|
+
def tsort_each_child(node, &block)
|
44
|
+
(self.class.rules[node] || []).each(&block)
|
45
|
+
end
|
46
|
+
|
47
|
+
def tsort_each_node(&block)
|
48
|
+
(self.class.rules || {}).each_key(&block)
|
49
|
+
end
|
50
|
+
|
51
|
+
def build_task_list(id_map={}, stack=[])
|
52
|
+
arr = []
|
53
|
+
each_strongly_connected_component_from(@task_class, id_map, stack) do |t|
|
54
|
+
if t.length == 1
|
55
|
+
arr << t.first
|
56
|
+
else
|
57
|
+
raise TSort::Cyclic.new("cyclic dependencies: #{t.inspect}")
|
58
|
+
end
|
59
|
+
end
|
60
|
+
arr
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
data/lib/hyrb/task.rb
ADDED
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'hyrb/pipeline'
|
2
|
+
|
3
|
+
module Hyrb
|
4
|
+
class Task
|
5
|
+
extend Forwardable
|
6
|
+
|
7
|
+
attr_reader :env, :pipeline
|
8
|
+
|
9
|
+
def_delegators :pipeline, :invoke, :say, :ask, :prompt, :edit, :option_list, :beep, :yes?, :no?
|
10
|
+
|
11
|
+
def self.depends(*args)
|
12
|
+
Commands::Pipeline.rules.merge!({self => args})
|
13
|
+
end
|
14
|
+
|
15
|
+
def self.prompt(hash_name, key, options = {})
|
16
|
+
self.prompts << [hash_name, key, options]
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.prompts
|
20
|
+
@prompts ||= []
|
21
|
+
end
|
22
|
+
|
23
|
+
def initialize(pipeline)
|
24
|
+
@pipeline = pipeline
|
25
|
+
end
|
26
|
+
|
27
|
+
# TODO: run task after
|
28
|
+
|
29
|
+
def run(env)
|
30
|
+
end
|
31
|
+
|
32
|
+
def run_before(env)
|
33
|
+
self.class.prompts.each do |(hash_name, key, options)|
|
34
|
+
if options.is_a?(Hash) && options[:default].respond_to?(:call)
|
35
|
+
options[:default] = options[:default].call(env)
|
36
|
+
end
|
37
|
+
|
38
|
+
prompt "Please enter #{key}", env[hash_name], key, options
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
module Tasks
|
44
|
+
Hyrb.autoload_each self, "hyrb/tasks", %w( ansible creds defaults developers digital_ocean environment github google hipchat project provision rackspace )
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
module Hyrb
|
5
|
+
module Tasks
|
6
|
+
module Ansible
|
7
|
+
class Inject < Task
|
8
|
+
depends Environment::SetupExisting
|
9
|
+
|
10
|
+
def run(env)
|
11
|
+
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
class Init < Task
|
16
|
+
depends Environment::SetupExisting
|
17
|
+
|
18
|
+
def run(env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class CreatePlaybookDir < Task
|
23
|
+
depends Init
|
24
|
+
|
25
|
+
def run(env)
|
26
|
+
# How to deal with enviornment vs project for ansible
|
27
|
+
if File.directory? env.project.ansible_path
|
28
|
+
say "Ansible dir exists in #{env.project.ansible_path}", :yellow
|
29
|
+
else
|
30
|
+
FileUtils.mkdir_p(env.project.ansible_path)
|
31
|
+
FileUtils.cp_r("#{TEMPLATE_PATH}/ansible/roles", "#{env.project.ansible_path}/roles")
|
32
|
+
say "Created ansible dir in #{env.project.ansible_path}", :green
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
class DestroyPlaybookDir < Task
|
38
|
+
depends Init
|
39
|
+
|
40
|
+
def run(env)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
class CreatePlaybook < Task
|
45
|
+
depends CreatePlaybookDir, Environment::Deployment
|
46
|
+
|
47
|
+
def run(env)
|
48
|
+
host = Models::AnsibleHost.new(env.project, env.environment)
|
49
|
+
host.save!
|
50
|
+
say "Saved inventory file to #{host.filepath}", :green
|
51
|
+
|
52
|
+
site_playbook = Models::AnsibleSite.new(env.project, env.environment)
|
53
|
+
site_playbook.data.merge!({
|
54
|
+
project_name: env.project.name,
|
55
|
+
host: env.environment.host,
|
56
|
+
deploy_user: env.environment.deploy_user,
|
57
|
+
base_path: env.environment.base_path,
|
58
|
+
domain: env.environment.domain,
|
59
|
+
relative_web_root: env.environment.relative_web_root,
|
60
|
+
})
|
61
|
+
|
62
|
+
site_playbook.hosts = env.environment.label
|
63
|
+
|
64
|
+
if ! site_playbook.mysql_host && yes?("Does the project use a SQL database?")
|
65
|
+
invoke Environment::Database
|
66
|
+
|
67
|
+
site_playbook.data.merge!({
|
68
|
+
mysql_host: env.environment.database_host,
|
69
|
+
mysql_db: env.environment.database_name,
|
70
|
+
mysql_user: env.environment.database_user,
|
71
|
+
mysql_password: env.environment.database_password,
|
72
|
+
})
|
73
|
+
|
74
|
+
# TODO: add mysql role to playbook
|
75
|
+
|
76
|
+
site_playbook.vars_prompt = [{
|
77
|
+
"name" => "mysql_root_user",
|
78
|
+
"prompt" => "MySQL Root User",
|
79
|
+
"private" => false
|
80
|
+
},{
|
81
|
+
"name" => "mysql_root_password",
|
82
|
+
"prompt" => "MySQL Root Password"
|
83
|
+
}]
|
84
|
+
end
|
85
|
+
|
86
|
+
site_playbook.save!
|
87
|
+
|
88
|
+
say "Saved site playbook to #{site_playbook.filepath}", :green
|
89
|
+
say "Run it with", :green
|
90
|
+
say "\tcd #{env.project.ansible_path}", :green
|
91
|
+
say "\tansible-playbook -i $(which yaminv) #{env.environment.label}.yml", :green
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Hyrb
|
2
|
+
module Tasks
|
3
|
+
module Creds
|
4
|
+
class Inject < Task
|
5
|
+
depends Defaults::Init
|
6
|
+
|
7
|
+
def run(env)
|
8
|
+
env.creds = Hyrb::Models::Creds.new
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
class Init < Task
|
13
|
+
depends Inject
|
14
|
+
end
|
15
|
+
|
16
|
+
class Show < Task
|
17
|
+
depends Init
|
18
|
+
|
19
|
+
def run(env)
|
20
|
+
say env.creds.data.to_hash.to_yaml
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Build < Task
|
25
|
+
depends Init
|
26
|
+
|
27
|
+
Hyrb::Models::Creds.keys.each { |key| prompt :creds, key, always: true }
|
28
|
+
end
|
29
|
+
|
30
|
+
class Edit < Task
|
31
|
+
depends Init
|
32
|
+
|
33
|
+
def run(env)
|
34
|
+
edit(env.creds.filepath)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
module Hyrb
|
2
|
+
module Tasks
|
3
|
+
module Defaults
|
4
|
+
class Inject < Task
|
5
|
+
def run(env)
|
6
|
+
env.defaults = Hyrb::Models::Defaults.new
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
class Init < Task
|
11
|
+
depends Inject
|
12
|
+
|
13
|
+
prompt :defaults, :domain, "hyfnrsx1.com"
|
14
|
+
end
|
15
|
+
|
16
|
+
class Show < Task
|
17
|
+
depends Defaults::Init
|
18
|
+
|
19
|
+
def run(env)
|
20
|
+
say env.defaults.data.to_hash.to_yaml
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Build < Task
|
25
|
+
depends Defaults::Init
|
26
|
+
|
27
|
+
Hyrb::Models::Defaults.keys.each { |key| prompt :defaults, key, always: true }
|
28
|
+
|
29
|
+
def run(env)
|
30
|
+
say "Credentials updated", :green
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
class Edit < Task
|
35
|
+
depends Defaults::Init
|
36
|
+
|
37
|
+
def run(env)
|
38
|
+
edit(env.defaults.filepath)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require 'faraday'
|
2
|
+
require 'uri'
|
3
|
+
require 'google_doc_seed'
|
4
|
+
require 'json'
|
5
|
+
require 'csv'
|
6
|
+
|
7
|
+
module Hyrb
|
8
|
+
module Tasks
|
9
|
+
module Developers
|
10
|
+
class Inject < Task
|
11
|
+
def run(env)
|
12
|
+
env.developers = Hyrb::Models::Cache::Developers.new.data
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
class Init < Task
|
17
|
+
depends Inject
|
18
|
+
|
19
|
+
def run(env)
|
20
|
+
invoke(Download) if env.developers.blank?
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
class Show < Task
|
25
|
+
depends Init
|
26
|
+
|
27
|
+
def run(env)
|
28
|
+
say env.developers.map(&:to_hash).to_yaml
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
class Download < Task
|
33
|
+
depends Google::Init
|
34
|
+
|
35
|
+
def run(env)
|
36
|
+
response = Faraday.post "https://accounts.google.com/o/oauth2/token",
|
37
|
+
refresh_token: env.creds.google_refresh_token,
|
38
|
+
client_id: env.creds.google_client_id,
|
39
|
+
client_secret: env.creds.google_client_secret,
|
40
|
+
grant_type: "refresh_token"
|
41
|
+
|
42
|
+
access_token = JSON.parse(response.body)['access_token']
|
43
|
+
seeder = GoogleDocSeed.new(access_token)
|
44
|
+
csv_string = seeder.to_csv_string(env.creds.google_spreadsheet_key)
|
45
|
+
|
46
|
+
csv = CSV.parse(csv_string, GoogleCSVConverters::CSV_SETTINGS)
|
47
|
+
|
48
|
+
env.developers = Hyrb::Models::Cache::Developers.new
|
49
|
+
env.developers.data = csv.map do |row|
|
50
|
+
pks = []
|
51
|
+
while true
|
52
|
+
field = row.delete(:public_key)
|
53
|
+
if field.length > 1
|
54
|
+
pks << field[1] if field[1]
|
55
|
+
else
|
56
|
+
break
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
Hyrb::Models::Developer.new({
|
61
|
+
name: row[:name],
|
62
|
+
email: row[:email],
|
63
|
+
role: Hyrb::Models::Developer::ROLE_MAP[row[:role].to_i],
|
64
|
+
github_username: row[:github],
|
65
|
+
keys: pks,
|
66
|
+
}) if row[:role].to_i < 4 && row[:email]
|
67
|
+
end.compact
|
68
|
+
|
69
|
+
env.developers.save!
|
70
|
+
say "Developer data downloaded", :green
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class AddToProject < Task
|
75
|
+
depends Init, Project::Init
|
76
|
+
|
77
|
+
def run(env)
|
78
|
+
added_devs = if env.project.users.try(:any?)
|
79
|
+
env.developers.select {|dev| env.project.users.include? dev.email }
|
80
|
+
else
|
81
|
+
env.developers.select {|dev| dev.role == :admin }
|
82
|
+
end
|
83
|
+
|
84
|
+
loop do
|
85
|
+
say "Devs with access"
|
86
|
+
list_devs(added_devs)
|
87
|
+
|
88
|
+
break unless yes? "Add more devs?"
|
89
|
+
|
90
|
+
dev_list = env.developers - added_devs
|
91
|
+
devs = *prompt_for_dev(dev_list)
|
92
|
+
added_devs += devs
|
93
|
+
end
|
94
|
+
|
95
|
+
env.project.users = added_devs.map(&:email)
|
96
|
+
env.project.save!
|
97
|
+
end
|
98
|
+
|
99
|
+
def list_devs(devs)
|
100
|
+
devs.each {|d| say "#{d.name} <#{d.email}>"}
|
101
|
+
end
|
102
|
+
|
103
|
+
def prompt_for_dev(devs)
|
104
|
+
option_list(devs) { |d, i| "#{i+1}: #{d.name} <#{d.email}>" }
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
module GoogleCSVConverters
|
109
|
+
BLANK_TO_NIL = ->(f) { f.empty? ? nil : f }
|
110
|
+
TRIM_WHITESPACE = ->(f) { f.respond_to?(:gsub) ? f.gsub(/\s+$/, '') : f }
|
111
|
+
|
112
|
+
CSV_SETTINGS = {
|
113
|
+
headers: true,
|
114
|
+
header_converters: :symbol,
|
115
|
+
converters: [BLANK_TO_NIL, TRIM_WHITESPACE],
|
116
|
+
}
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
require 'fog/digitalocean'
|
2
|
+
require 'parallel'
|
3
|
+
|
4
|
+
module Hyrb
|
5
|
+
module Tasks
|
6
|
+
module DigitalOcean
|
7
|
+
class Inject < Task
|
8
|
+
depends Creds::Init
|
9
|
+
|
10
|
+
prompt :creds, :digital_ocean_api_key
|
11
|
+
prompt :creds, :digital_ocean_client_id
|
12
|
+
|
13
|
+
def run(env)
|
14
|
+
env.digital_ocean_client = Fog::Compute.new({
|
15
|
+
provider: 'DigitalOcean',
|
16
|
+
digitalocean_api_key: env.creds.digital_ocean_api_key,
|
17
|
+
digitalocean_client_id: env.creds.digital_ocean_client_id,
|
18
|
+
})
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class Init < Task
|
23
|
+
depends Inject
|
24
|
+
|
25
|
+
def run(env)
|
26
|
+
# TODO: allow cache referesh to be forced
|
27
|
+
env.digital_ocean_cache = Hyrb::Models::Cache::DigitalOcean.new
|
28
|
+
env.digital_ocean_cache.class.keys.each do |key|
|
29
|
+
env.digital_ocean_cache[key] ||= env.digital_ocean_client.send(key).map(&:attributes)
|
30
|
+
end
|
31
|
+
env.digital_ocean_cache.save!
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
class SSHKeys < Task
|
36
|
+
depends Init, Developers::Init
|
37
|
+
|
38
|
+
def run(env)
|
39
|
+
env.digital_ocean_ssh_keys = Parallel.map(env.digital_ocean_client.ssh_keys.all, in_threads: 12) do |dokey|
|
40
|
+
env.digital_ocean_client.ssh_keys.get(dokey.id)
|
41
|
+
end
|
42
|
+
|
43
|
+
env.digital_ocean_ssh_keys.each do |k|
|
44
|
+
dev = env.developers.find { |dev| dev.keys.include? k.ssh_pub_key }
|
45
|
+
dev.digital_ocean_id = k.id if dev
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class ShowSSHKeys < Task
|
51
|
+
depends SSHKeys
|
52
|
+
|
53
|
+
def run(env)
|
54
|
+
say env.digital_ocean_ssh_keys.map { |k| {name: k.name, id: k.id, ssh_key: k.ssh_pub_key} }.to_yaml
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class SyncSSHKeys < Task
|
59
|
+
depends SSHKeys
|
60
|
+
|
61
|
+
def run(env)
|
62
|
+
env.digital_ocean_ssh_keys.each do |dokey|
|
63
|
+
unless key = env.developers.any? { |dev| dev.keys.include? dokey.ssh_pub_key }
|
64
|
+
say "Removing #{dokey.name} SSH key from Digital Ocean", :green
|
65
|
+
env.digital_ocean_client.ssh_keys.destroy(dokey.id)
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
env.developers.select { |d| d.keys.any? }.each do |dev|
|
70
|
+
if key = env.digital_ocean_ssh_keys.any? { |k| dev.keys.include? k.ssh_pub_key }
|
71
|
+
say "#{dev.name}'s keys already added to Digital Ocean", :yellow
|
72
|
+
else
|
73
|
+
dev.keys.each do |pubkey, i|
|
74
|
+
say "Adding pubkey for #{dev.name} to Digital Ocean", :green
|
75
|
+
env.digital_ocean_client.ssh_keys.create(name: dev.name, ssh_pub_key: pubkey)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|