hyrb 0.0.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 +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
|