cheftacular 2.0.0
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/bin/cft +4 -0
- data/bin/cftclr +4 -0
- data/bin/cheftacular +4 -0
- data/bin/client-list +4 -0
- data/lib/cheftacular/README.md +416 -0
- data/lib/cheftacular/actions/check.rb +32 -0
- data/lib/cheftacular/actions/console.rb +62 -0
- data/lib/cheftacular/actions/database.rb +13 -0
- data/lib/cheftacular/actions/db_console.rb +67 -0
- data/lib/cheftacular/actions/deploy.rb +40 -0
- data/lib/cheftacular/actions/log.rb +47 -0
- data/lib/cheftacular/actions/migrate.rb +57 -0
- data/lib/cheftacular/actions/run.rb +64 -0
- data/lib/cheftacular/actions/scale.rb +94 -0
- data/lib/cheftacular/actions/tail.rb +55 -0
- data/lib/cheftacular/actions.rb +14 -0
- data/lib/cheftacular/auditor.rb +46 -0
- data/lib/cheftacular/chef/data_bag.rb +104 -0
- data/lib/cheftacular/cheftacular.rb +55 -0
- data/lib/cheftacular/decryptors.rb +45 -0
- data/lib/cheftacular/encryptors.rb +48 -0
- data/lib/cheftacular/getters.rb +153 -0
- data/lib/cheftacular/helpers.rb +296 -0
- data/lib/cheftacular/initializers.rb +451 -0
- data/lib/cheftacular/parsers.rb +199 -0
- data/lib/cheftacular/remote_helpers.rb +30 -0
- data/lib/cheftacular/stateless_action.rb +16 -0
- data/lib/cheftacular/stateless_actions/add_ssh_key_to_bag.rb +44 -0
- data/lib/cheftacular/stateless_actions/arguments.rb +68 -0
- data/lib/cheftacular/stateless_actions/backups.rb +116 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/centos_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/coreos_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/fedora_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/redhat_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/ubuntu_bootstrap.rb +102 -0
- data/lib/cheftacular/stateless_actions/bootstrappers/vyatta_bootstrap.rb +7 -0
- data/lib/cheftacular/stateless_actions/chef_bootstrap.rb +40 -0
- data/lib/cheftacular/stateless_actions/chef_environment.rb +21 -0
- data/lib/cheftacular/stateless_actions/clean_cookbooks.rb +104 -0
- data/lib/cheftacular/stateless_actions/clean_sensu_plugins.rb +19 -0
- data/lib/cheftacular/stateless_actions/clean_server_passwords.rb +14 -0
- data/lib/cheftacular/stateless_actions/cleanup_log_files.rb +14 -0
- data/lib/cheftacular/stateless_actions/client_list.rb +89 -0
- data/lib/cheftacular/stateless_actions/cloud.rb +107 -0
- data/lib/cheftacular/stateless_actions/cloud_bootstrap.rb +109 -0
- data/lib/cheftacular/stateless_actions/compile_audit_log.rb +60 -0
- data/lib/cheftacular/stateless_actions/compile_readme.rb +41 -0
- data/lib/cheftacular/stateless_actions/create_git_key.rb +67 -0
- data/lib/cheftacular/stateless_actions/disk_report.rb +75 -0
- data/lib/cheftacular/stateless_actions/environment.rb +100 -0
- data/lib/cheftacular/stateless_actions/fetch_file.rb +24 -0
- data/lib/cheftacular/stateless_actions/fix_known_hosts.rb +70 -0
- data/lib/cheftacular/stateless_actions/full_bootstrap.rb +30 -0
- data/lib/cheftacular/stateless_actions/get_active_ssh_connections.rb +18 -0
- data/lib/cheftacular/stateless_actions/get_haproxy_log.rb +55 -0
- data/lib/cheftacular/stateless_actions/get_log_from_bag.rb +38 -0
- data/lib/cheftacular/stateless_actions/get_pg_pass.rb +61 -0
- data/lib/cheftacular/stateless_actions/help.rb +71 -0
- data/lib/cheftacular/stateless_actions/initialize_data_bag_contents.rb +220 -0
- data/lib/cheftacular/stateless_actions/knife_upload.rb +23 -0
- data/lib/cheftacular/stateless_actions/pass.rb +49 -0
- data/lib/cheftacular/stateless_actions/reinitialize.rb +46 -0
- data/lib/cheftacular/stateless_actions/remove_client.rb +81 -0
- data/lib/cheftacular/stateless_actions/replication_status.rb +103 -0
- data/lib/cheftacular/stateless_actions/restart_swap.rb +55 -0
- data/lib/cheftacular/stateless_actions/rvm.rb +14 -0
- data/lib/cheftacular/stateless_actions/server_update.rb +99 -0
- data/lib/cheftacular/stateless_actions/service.rb +14 -0
- data/lib/cheftacular/stateless_actions/test_env.rb +82 -0
- data/lib/cheftacular/stateless_actions/update_split_branches.rb +64 -0
- data/lib/cheftacular/stateless_actions/update_tld.rb +62 -0
- data/lib/cheftacular/stateless_actions/upload_nodes.rb +120 -0
- data/lib/cheftacular/stateless_actions/upload_roles.rb +24 -0
- data/lib/cheftacular/version.rb +5 -0
- data/lib/cheftacular.rb +4 -0
- data/lib/cloud_interactor/authentication.rb +56 -0
- data/lib/cloud_interactor/cloud_interactor.rb +23 -0
- data/lib/cloud_interactor/domain/create.rb +17 -0
- data/lib/cloud_interactor/domain/create_record.rb +27 -0
- data/lib/cloud_interactor/domain/destroy.rb +17 -0
- data/lib/cloud_interactor/domain/destroy_record.rb +23 -0
- data/lib/cloud_interactor/domain/list.rb +9 -0
- data/lib/cloud_interactor/domain/list_records.rb +22 -0
- data/lib/cloud_interactor/domain/read.rb +23 -0
- data/lib/cloud_interactor/domain/read_record.rb +27 -0
- data/lib/cloud_interactor/domain/update.rb +18 -0
- data/lib/cloud_interactor/domain/update_record.rb +42 -0
- data/lib/cloud_interactor/domain.rb +18 -0
- data/lib/cloud_interactor/flavor.rb +27 -0
- data/lib/cloud_interactor/helpers.rb +70 -0
- data/lib/cloud_interactor/image.rb +27 -0
- data/lib/cloud_interactor/parser.rb +37 -0
- data/lib/cloud_interactor/server/attach_volume.rb +33 -0
- data/lib/cloud_interactor/server/create.rb +39 -0
- data/lib/cloud_interactor/server/destroy.rb +11 -0
- data/lib/cloud_interactor/server/detach_volume.rb +21 -0
- data/lib/cloud_interactor/server/list.rb +7 -0
- data/lib/cloud_interactor/server/list_volumes.rb +25 -0
- data/lib/cloud_interactor/server/poll.rb +22 -0
- data/lib/cloud_interactor/server/read.rb +9 -0
- data/lib/cloud_interactor/server/read_volume.rb +24 -0
- data/lib/cloud_interactor/server.rb +17 -0
- data/lib/cloud_interactor/version.rb +4 -0
- data/lib/cloud_interactor/volume/create.rb +13 -0
- data/lib/cloud_interactor/volume/destroy.rb +11 -0
- data/lib/cloud_interactor/volume/list.rb +7 -0
- data/lib/cloud_interactor/volume/read.rb +9 -0
- data/lib/cloud_interactor/volume.rb +20 -0
- data/lib/ridley/monkeypatches.rb +11 -0
- data/lib/sshkit/actions/start_commit_check.rb +19 -0
- data/lib/sshkit/actions/start_deploy.rb +25 -0
- data/lib/sshkit/actions/start_log_fetch.rb +91 -0
- data/lib/sshkit/actions/start_task.rb +29 -0
- data/lib/sshkit/getters.rb +67 -0
- data/lib/sshkit/helpers.rb +13 -0
- data/lib/sshkit/monkeypatches.rb +19 -0
- metadata +375 -0
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
|
|
2
|
+
class Cheftacular
|
|
3
|
+
class StatelessActionDocumentation
|
|
4
|
+
def upload_nodes
|
|
5
|
+
@config['documentation']['stateless_action'] << [
|
|
6
|
+
"`cft upload_nodes` This command will resync the chef server's nodes with the data in our chef-repo/node_roles. ",
|
|
7
|
+
|
|
8
|
+
[
|
|
9
|
+
" 1. This command changes behavior depending on several factors about both your mode and the state of your environment",
|
|
10
|
+
|
|
11
|
+
" 2. In Devops mode, being run directly, this command will prompt you to update a data bag of node_role data that will help " +
|
|
12
|
+
"non-devops runs perform actions that involve setting roles on servers.",
|
|
13
|
+
|
|
14
|
+
" 1. In this setting, any time the chef server's data bag hash differs from the hash stored on disk for a role, you will be " +
|
|
15
|
+
"prompted to see if you really want to overwrite.",
|
|
16
|
+
|
|
17
|
+
" 3. When building new servers *in any mode*, this command will check the node_roles stored in the data bag only and update the " +
|
|
18
|
+
"run lists of the nodes from that data, NOT from the node_roles data stored on disk in the nodes_dir.",
|
|
19
|
+
|
|
20
|
+
" 1. Due to this, only users running this against their chef-repo need to worry about having a nodes_dir, the way it should be."
|
|
21
|
+
]
|
|
22
|
+
]
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
class StatelessAction
|
|
27
|
+
def upload_nodes invalidate_file_node_cache=false
|
|
28
|
+
raise "This action can only be performed if the mode is set to devops" if !@config['helper'].running_in_mode?('devops') && !@options['in_scaling']
|
|
29
|
+
|
|
30
|
+
@config['chef_environments'].each do |env|
|
|
31
|
+
@config['initializer'].initialize_data_bags_for_environment env, false, ['node_roles']
|
|
32
|
+
|
|
33
|
+
@config['initializer'].initialize_node_roles_bag_contents env
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
nodes = @options['multi-step'] ? @config['getter'].get_true_node_objects(true,true) : @config['getter'].get_true_node_objects(true)
|
|
37
|
+
|
|
38
|
+
node_roles_hash, bag_hash, allowed_changes_hash = {},{},{}
|
|
39
|
+
|
|
40
|
+
Dir.foreach(@config['locs']['nodes']) do |fr|
|
|
41
|
+
next if @config['helper'].is_junk_filename?(fr)
|
|
42
|
+
|
|
43
|
+
Dir.foreach("#{ @config['locs']['nodes'] }/#{ fr }") do |f|
|
|
44
|
+
next if @config['helper'].is_junk_filename?(f)
|
|
45
|
+
|
|
46
|
+
node_roles_hash[f.split('.json').first] = JSON.parse(File.read("#{ @config['locs']['nodes'] }/#{ fr }/#{ f }"))
|
|
47
|
+
end
|
|
48
|
+
end if @config['helper'].running_in_mode?('devops') #only devops modes should have a nodes_dir
|
|
49
|
+
|
|
50
|
+
@config['chef_environments'].each do |env|
|
|
51
|
+
@config[env]['node_roles_bag_hash']['node_roles'].each_pair do |role_name, role_hash|
|
|
52
|
+
bag_hash[role_hash['name']] = role_hash.to_hash #hashes from chef server are stored as hashie objects until forced into hashes
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
if !@options['force_yes'] && @config['helper'].running_in_mode?('devops')
|
|
57
|
+
node_roles_hash.each_pair do |role_name, role_hash|
|
|
58
|
+
overwrite = false
|
|
59
|
+
if bag_hash[role_name] != role_hash
|
|
60
|
+
puts "Detected difference between saved roles hash and updated node_roles json hash."
|
|
61
|
+
|
|
62
|
+
puts "Saved roles hash:"
|
|
63
|
+
ap(bag_hash[role_name])
|
|
64
|
+
|
|
65
|
+
puts "New roles hash:"
|
|
66
|
+
ap(role_hash)
|
|
67
|
+
|
|
68
|
+
puts "Preparing to overwrite the saved roles hash with the node_roles hash, enter Y/y to confirm."
|
|
69
|
+
|
|
70
|
+
input = STDIN.gets.chomp
|
|
71
|
+
|
|
72
|
+
overwrite = true if ( input =~ /y|Y|yes|Yes/ ) == 0
|
|
73
|
+
|
|
74
|
+
allowed_changes_hash[role_name] = role_hash if overwrite
|
|
75
|
+
else #bag_hash does not have a key for that role, populate it.
|
|
76
|
+
allowed_changes_hash[role_name] = role_hash
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
@config[role_hash['chef_environment']]['node_roles_bag_hash']['node_roles'][role_name] = role_hash
|
|
80
|
+
end
|
|
81
|
+
else
|
|
82
|
+
allowed_changes_hash = bag_hash
|
|
83
|
+
end
|
|
84
|
+
|
|
85
|
+
nodes.each do |node|
|
|
86
|
+
# if there is a node_roles file that completely matches the name of the file, use it
|
|
87
|
+
changes_for_current_node = false
|
|
88
|
+
|
|
89
|
+
if allowed_changes_hash[node.name]
|
|
90
|
+
allowed_changes_hash[node.name].each_pair do |node_key, node_val|
|
|
91
|
+
if (node_key =~ /name/) != 0 && node.send(node_key) != node_val
|
|
92
|
+
puts("Updating #{ node.name } with attribute #{ node_key } = #{ node_val } from #{ node.name }.json") unless @options['quiet']
|
|
93
|
+
node.send("#{ node_key }=", node_val)
|
|
94
|
+
|
|
95
|
+
changes_for_current_node, invalidate_file_node_cache = true, true
|
|
96
|
+
end
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
elsif allowed_changes_hash.keys.include?(node.name.gsub(/\d/,'')) #if there is a template file that matches the stripped down name, use it
|
|
100
|
+
allowed_changes_hash[node.name.gsub(/\d/,'')].each_pair do |node_key, node_val|
|
|
101
|
+
if (node_key =~ /name/) != 0 && node.send(node_key) != node_val
|
|
102
|
+
puts("Updating #{ node.name } with attribute #{ node_key } = #{ node_val } from template json file") unless @options['quiet']
|
|
103
|
+
node.send("#{ node_key }=", node_val)
|
|
104
|
+
|
|
105
|
+
changes_for_current_node, invalidate_file_node_cache = true, true
|
|
106
|
+
end
|
|
107
|
+
end
|
|
108
|
+
end
|
|
109
|
+
|
|
110
|
+
node.save if changes_for_current_node
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
@config['chef_environments'].each do |env|
|
|
114
|
+
@config['ChefDataBag'].save_node_roles_bag env
|
|
115
|
+
end if !@options['force_yes'] && @config['helper'].running_in_mode?('devops')
|
|
116
|
+
|
|
117
|
+
@config['helper'].cleanup_file_caches('current') if invalidate_file_node_cache
|
|
118
|
+
end
|
|
119
|
+
end
|
|
120
|
+
end
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
|
|
2
|
+
class Cheftacular
|
|
3
|
+
class StatelessActionDocumentation
|
|
4
|
+
def upload_roles
|
|
5
|
+
@config['documentation']['stateless_action'] << [
|
|
6
|
+
"`cft upload_roles` This command will resync the chef server's roles with the data in the chef-repo/roles."
|
|
7
|
+
]
|
|
8
|
+
end
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
class StatelessAction
|
|
12
|
+
def upload_roles
|
|
13
|
+
raise "This action can only be performed if the mode is set to devops" if !@config['helper'].running_in_mode?('devops') && !@options['in_scaling']
|
|
14
|
+
|
|
15
|
+
Dir.foreach(@config['locs']['roles']) do |rd|
|
|
16
|
+
next if @config['helper'].is_junk_filename?(rd)
|
|
17
|
+
|
|
18
|
+
puts("Loading in role from file #{ rd }") if @options['verbose']
|
|
19
|
+
|
|
20
|
+
puts `knife role from file "#{ @config['locs']['roles'] }/#{ rd }"`
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
end
|
data/lib/cheftacular.rb
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
|
|
2
|
+
class CloudInteractor
|
|
3
|
+
class Authentication
|
|
4
|
+
def initialize auth_hash, options={}
|
|
5
|
+
@auth_hash = auth_hash
|
|
6
|
+
@options = options
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def auth_service interaction_class
|
|
10
|
+
except_keys = []
|
|
11
|
+
|
|
12
|
+
interaction_class = case interaction_class.downcase
|
|
13
|
+
when 'volume'
|
|
14
|
+
case @options['preferred_cloud']
|
|
15
|
+
when 'rackspace'
|
|
16
|
+
except_keys = [:provider, :version]
|
|
17
|
+
|
|
18
|
+
'Rackspace::BlockStorage'
|
|
19
|
+
else
|
|
20
|
+
raise "CloudInteractor Does not currently support this #{ options['preferred_cloud'] }'s' volume creation at this time"
|
|
21
|
+
end
|
|
22
|
+
when 'dns'
|
|
23
|
+
case @options['preferred_cloud']
|
|
24
|
+
when 'rackspace'
|
|
25
|
+
except_keys = [:version, :rackspace_region]
|
|
26
|
+
|
|
27
|
+
interaction_class
|
|
28
|
+
else
|
|
29
|
+
except_keys = [:version]
|
|
30
|
+
|
|
31
|
+
interaction_class
|
|
32
|
+
end
|
|
33
|
+
else
|
|
34
|
+
interaction_class
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
classes_to_inject = interaction_class.split('::')
|
|
38
|
+
classes_to_inject = classes_to_inject.map { |str| str.camelcase }
|
|
39
|
+
|
|
40
|
+
cloud_hash = case @options['preferred_cloud']
|
|
41
|
+
when 'rackspace'
|
|
42
|
+
{
|
|
43
|
+
provider: @options['preferred_cloud'].capitalize,
|
|
44
|
+
rackspace_username: @auth_hash['cloud_auth'][@options['preferred_cloud']]['username'],
|
|
45
|
+
rackspace_api_key: @auth_hash['cloud_auth'][@options['preferred_cloud']]['api_key'],
|
|
46
|
+
version: :v2,
|
|
47
|
+
rackspace_region: @options['preferred_cloud_region'].to_sym,
|
|
48
|
+
connection_options: {}
|
|
49
|
+
}.delete_if { |key, value| except_keys.flatten.include?(key) }
|
|
50
|
+
else raise "CloudInteractor Does not currently support #{ @options['preferred_cloud'] } at this time"
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
Fog.class_eval(classes_to_inject.join('::')).new(cloud_hash)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class CloudInteractor
|
|
2
|
+
ALLOWED_HLMS = ['domain', 'flavor', 'image', 'server', 'volume']
|
|
3
|
+
|
|
4
|
+
def initialize auth_hash, options
|
|
5
|
+
@main_obj, @classes = {},{}
|
|
6
|
+
@options = options
|
|
7
|
+
@main_obj['output'] = {}
|
|
8
|
+
@auth_hash = auth_hash
|
|
9
|
+
@classes['helper'] = Helper.new(@main_obj, @classes, @options)
|
|
10
|
+
@classes['domain'] = Domain.new(@main_obj, @auth_hash, @classes, @options)
|
|
11
|
+
@classes['flavor'] = Flavor.new(@main_obj, @classes, @options)
|
|
12
|
+
@classes['image'] = Image.new(@main_obj, @classes, @options)
|
|
13
|
+
@classes['server'] = Server.new(@main_obj, @classes, @options)
|
|
14
|
+
@classes['volume'] = Volume.new(@main_obj, @classes, @options)
|
|
15
|
+
@classes['auth'] = Authentication.new(@auth_hash, @options)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def run args
|
|
19
|
+
parse_args(args) unless args.empty?
|
|
20
|
+
|
|
21
|
+
@main_obj['output']
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class CloudInteractor
|
|
2
|
+
class Domain
|
|
3
|
+
def create args, already_created=false
|
|
4
|
+
read args, false
|
|
5
|
+
|
|
6
|
+
unless @main_obj["specific_#{ IDENTITY }"].empty?
|
|
7
|
+
puts "#{ IDENTITY } #{ args[IDENTITY.singularize] } already exists... returning."
|
|
8
|
+
|
|
9
|
+
return false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
@classes['auth'].auth_service(RESOURCE).instance_eval('zones').create(domain: args[IDENTITY.singularize], email: @auth_hash['cloud_auth'][@options['preferred_cloud']]['email'])
|
|
13
|
+
|
|
14
|
+
puts "Created #{ IDENTITY } #{ args[IDENTITY.singularize] }..."
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class CloudInteractor
|
|
2
|
+
class Domain
|
|
3
|
+
def create_record args, already_created=false
|
|
4
|
+
args['type'] ||= 'A'
|
|
5
|
+
args['ttl'] ||= 300
|
|
6
|
+
|
|
7
|
+
read args, false
|
|
8
|
+
|
|
9
|
+
@main_obj['specific_records'][args[IDENTITY.singularize]].each do |record_hash|
|
|
10
|
+
already_created = true if record_hash['name'] == "#{ args['subdomain'] }.#{ args[IDENTITY.singularize] }"
|
|
11
|
+
|
|
12
|
+
break if already_created
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
if already_created
|
|
16
|
+
update_record args
|
|
17
|
+
|
|
18
|
+
else
|
|
19
|
+
specific_fog_object = @classes['auth'].auth_service(RESOURCE).instance_eval('zones').get @main_obj["specific_#{ IDENTITY }"].last['id']
|
|
20
|
+
|
|
21
|
+
specific_fog_object.records.create(name: "#{ args['subdomain'] }.#{ args[IDENTITY.singularize] }", value: args['target_ip'], type: args['type'], ttl: args['ttl'])
|
|
22
|
+
|
|
23
|
+
puts "Attached #{ args['subdomain'] } (#{ args['target_ip'] }) to #{ args[IDENTITY.singularize] }..."
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
class CloudInteractor
|
|
2
|
+
class Domain
|
|
3
|
+
def destroy args
|
|
4
|
+
read args, false
|
|
5
|
+
|
|
6
|
+
if @main_obj["specific_#{ IDENTITY }"].empty? && @main_obj["specific_#{ IDENTITY }"].last[IDENTITY.singularize] != args[IDENTITY.singularize]
|
|
7
|
+
puts "#{ IDENTITY } #{ args[IDENTITY.singularize] } doesn't exist... returning."
|
|
8
|
+
|
|
9
|
+
return false
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
@classes['auth'].auth_service(RESOURCE).instance_eval('zones').get(@main_obj["specific_#{ IDENTITY }"].last['id']).destroy
|
|
13
|
+
|
|
14
|
+
puts "Destroyed #{ IDENTITY.singularize } #{ args[IDENTITY.singularize] }..."
|
|
15
|
+
end
|
|
16
|
+
end
|
|
17
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class CloudInteractor
|
|
2
|
+
class Domain
|
|
3
|
+
def destroy_record args, already_exists=false
|
|
4
|
+
read args, false
|
|
5
|
+
|
|
6
|
+
@main_obj['specific_records'][args[IDENTITY.singularize]].each do |record_hash|
|
|
7
|
+
already_exists = true if record_hash['name'] == "#{ args['subdomain'] }.#{ args[IDENTITY.singularize] }"
|
|
8
|
+
|
|
9
|
+
args['id'] = record_hash['id']
|
|
10
|
+
|
|
11
|
+
break if already_exists
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
raise "Subdomain not found for #{ args[IDENTITY.singularize] }" unless already_exists
|
|
15
|
+
|
|
16
|
+
puts "Destroying #{ args['subdomain'] } from #{ args[IDENTITY.singularize] }..."
|
|
17
|
+
|
|
18
|
+
specific_fog_object = @classes['auth'].auth_service(RESOURCE).instance_eval('zones').get @main_obj["specific_#{ IDENTITY }"].last['id']
|
|
19
|
+
|
|
20
|
+
specific_fog_object.records.get(args['id']).destroy
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
class CloudInteractor
|
|
2
|
+
class Domain
|
|
3
|
+
def list_records args, output=false
|
|
4
|
+
puts "Querying #{ args["domain"] } for rackspace..."
|
|
5
|
+
|
|
6
|
+
specific_fog_object = @classes['auth'].auth_service(RESOURCE).instance_eval('zones').get @main_obj["specific_#{ IDENTITY }"].last['id']
|
|
7
|
+
|
|
8
|
+
@main_obj['specific_records'] ||= {}
|
|
9
|
+
@main_obj['specific_records'][args[IDENTITY.singularize]] ||= []
|
|
10
|
+
@main_obj["specific_#{ IDENTITY }"].last['records'] ||= []
|
|
11
|
+
|
|
12
|
+
specific_fog_object.records.each do |record|
|
|
13
|
+
record_obj = JSON.parse(record.to_json)
|
|
14
|
+
|
|
15
|
+
@main_obj["specific_#{ IDENTITY }"].last['records'] << record_obj
|
|
16
|
+
@main_obj['specific_records'][args[IDENTITY.singularize]] << record_obj
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
ap(@main_obj["specific_#{ IDENTITY }"].last['records']) if output
|
|
20
|
+
end
|
|
21
|
+
end
|
|
22
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class CloudInteractor
|
|
2
|
+
class Domain
|
|
3
|
+
def read args={}, output=true
|
|
4
|
+
list [], false
|
|
5
|
+
|
|
6
|
+
@classes['helper'].generic_read_parse args, IDENTITY, output, IDENTITY.singularize
|
|
7
|
+
|
|
8
|
+
@main_obj["specific_#{ IDENTITY }"] ||= {}
|
|
9
|
+
|
|
10
|
+
specific_identity = @classes['helper'].set_specific_identity args, IDENTITY.singularize
|
|
11
|
+
|
|
12
|
+
@main_obj[IDENTITY].each do |identity_hash|
|
|
13
|
+
next if !specific_identity.nil? && !identity_hash[IDENTITY.singularize].include?(specific_identity)
|
|
14
|
+
|
|
15
|
+
self.list_records identity_hash
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
ap(@main_obj["specific_#{ IDENTITY }"]) if output
|
|
19
|
+
|
|
20
|
+
puts("#{ specific_identity } not found in #{ IDENTITY }!") if @main_obj["specific_#{ IDENTITY }"].empty?
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
class CloudInteractor
|
|
2
|
+
class Domain
|
|
3
|
+
def read_record args, output=true, strict_match=false
|
|
4
|
+
|
|
5
|
+
specific_record = args['subdomain']
|
|
6
|
+
|
|
7
|
+
read args, false
|
|
8
|
+
|
|
9
|
+
@main_obj['specific_records'][args[IDENTITY.singularize]].each do |record_hash|
|
|
10
|
+
if strict_match
|
|
11
|
+
next unless record_hash['name'] == (specific_record)
|
|
12
|
+
else
|
|
13
|
+
next unless record_hash['name'].include?(specific_record)
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
@main_obj['specific_queried_domains'] ||= {}
|
|
17
|
+
|
|
18
|
+
@main_obj['specific_queried_domains'][args[IDENTITY.singularize]] ||= []
|
|
19
|
+
@main_obj['specific_queried_domains'][args[IDENTITY.singularize]] << record_hash
|
|
20
|
+
|
|
21
|
+
ap(record_hash) if output
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
puts("#{ args[IDENTITY.singularize] } does not have the subdomain #{ args['subdomain'] }!") if @main_obj["specific_queried_domains"].nil?
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
#TODO Grant more options to update?
|
|
2
|
+
class CloudInteractor
|
|
3
|
+
class Domain
|
|
4
|
+
def update args
|
|
5
|
+
read args, false
|
|
6
|
+
|
|
7
|
+
if (@main_obj["specific_#{ IDENTITY }"].nil? || @main_obj["specific_#{ IDENTITY }"].empty?) && @main_obj["specific_#{ IDENTITY }"].last[IDENTITY.singularize] != args[IDENTITY.singularize]
|
|
8
|
+
puts "#{ IDENTITY } #{ args[IDENTITY.singularize] } doesn't exist... returning."
|
|
9
|
+
|
|
10
|
+
return false
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
@classes['auth'].auth_service(resource).instance_eval('zones').get(@main_obj["specific_#{ IDENTITY }"].last['id']).update(ttl: 5, email: @auth_hash['cloud_auth'][@options['preferred_cloud']]['email'])
|
|
14
|
+
|
|
15
|
+
puts "Updated #{ IDENTITY } #{ args[IDENTITY.singularize] }..."
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
class CloudInteractor
|
|
2
|
+
class Domain
|
|
3
|
+
def update_record args, already_created=false
|
|
4
|
+
args['type'] ||= 'A'
|
|
5
|
+
args['ttl'] ||= 300
|
|
6
|
+
|
|
7
|
+
read args, false
|
|
8
|
+
|
|
9
|
+
puts "Updating #{ args['subdomain'] } for #{ args[IDENTITY.singularize] }..."
|
|
10
|
+
|
|
11
|
+
@main_obj['specific_records'][args[IDENTITY.singularize]].each do |record_hash|
|
|
12
|
+
already_created = true if record_hash['name'] == "#{ args['subdomain'] }.#{ args[IDENTITY.singularize] }"
|
|
13
|
+
|
|
14
|
+
args['id'] = record_hash['id']
|
|
15
|
+
|
|
16
|
+
break if already_created
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
if already_created
|
|
20
|
+
specific_fog_object = @classes['auth'].auth_service(RESOURCE).instance_eval('zones').get @main_obj["specific_#{ IDENTITY }"].last['id']
|
|
21
|
+
|
|
22
|
+
#the fact that there is no public update method is silly
|
|
23
|
+
specific_record = specific_fog_object.records.get(args['id'])
|
|
24
|
+
|
|
25
|
+
case @options['preferred_cloud']
|
|
26
|
+
when 'rackspace'
|
|
27
|
+
specific_record.type = args['type']
|
|
28
|
+
specific_record.value = args['target_ip']
|
|
29
|
+
specific_record.ttl = args['ttl']
|
|
30
|
+
|
|
31
|
+
specific_record.save
|
|
32
|
+
else
|
|
33
|
+
raise "Unsupported action #{ __method__ } for #{ @options['preferred_cloud'] }. Please create an issue on github or submit a PR to fix this issue."
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
puts "Updated #{ args['subdomain'] } (#{ args['target_ip'] }) to #{ args[IDENTITY.singularize] }..."
|
|
37
|
+
else
|
|
38
|
+
create_record [ args ]
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
42
|
+
end
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
|
|
2
|
+
class CloudInteractor
|
|
3
|
+
class Domain #http://docs.rackspace.com/cdns/api/v1.0/cdns-devguide/content/API_Operations_Wadl-d1e2648.html
|
|
4
|
+
IDENTITY = 'domains'
|
|
5
|
+
RESOURCE = 'DNS'
|
|
6
|
+
|
|
7
|
+
def initialize main_obj, auth_hash, classes, options={}
|
|
8
|
+
@main_obj = main_obj
|
|
9
|
+
@auth_hash = auth_hash
|
|
10
|
+
@options = options
|
|
11
|
+
@classes = classes
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def run method, args
|
|
15
|
+
self.send(method, args)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
class CloudInteractor
|
|
3
|
+
class Flavor #http://docs.rackspace.com/servers/api/v2/cs-devguide/content/List_Flavors-d1e4188.html
|
|
4
|
+
IDENTITY = 'flavors'
|
|
5
|
+
RESOURCE = 'compute'
|
|
6
|
+
|
|
7
|
+
def initialize main_obj, classes, options={}
|
|
8
|
+
@main_obj = main_obj
|
|
9
|
+
@options = options
|
|
10
|
+
@classes = classes
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def run method, args
|
|
14
|
+
self.send(method, args)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def list args={}, output=true
|
|
18
|
+
@classes['helper'].generic_list_call IDENTITY, RESOURCE, output
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def read args, output=true
|
|
22
|
+
list [], false
|
|
23
|
+
|
|
24
|
+
@classes['helper'].generic_read_parse args, IDENTITY, output
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
class CloudInteractor
|
|
2
|
+
class Helper
|
|
3
|
+
def initialize main_obj, classes, options={}
|
|
4
|
+
@main_obj = main_obj
|
|
5
|
+
@options = options
|
|
6
|
+
@classes = classes
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
def set_specific_identity args, key_to_extract
|
|
10
|
+
case args.class.to_s
|
|
11
|
+
when "Hash" then args[key_to_extract]
|
|
12
|
+
when "String" then args
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def generic_list_call identity, resource, output=true
|
|
17
|
+
puts "Returning list of #{ identity } for #{ @options['preferred_cloud'] }..."
|
|
18
|
+
|
|
19
|
+
@main_obj[identity] = JSON.parse(@classes['auth'].auth_service(resource).instance_eval(identity).to_json)
|
|
20
|
+
|
|
21
|
+
ap(@main_obj[identity]) if output && !@options['in_scaling']
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def generic_read_parse args, identity, output=true, mode='name', search_key='name'
|
|
25
|
+
search_key = mode if mode != 'name' && search_key == 'name'
|
|
26
|
+
|
|
27
|
+
specific_identity = set_specific_identity args, search_key
|
|
28
|
+
|
|
29
|
+
@main_obj["specific_#{ identity }"] ||= []
|
|
30
|
+
|
|
31
|
+
@main_obj[identity].each do |identity_hash|
|
|
32
|
+
if specific_identity.nil?
|
|
33
|
+
puts("Query arguments \"#{ args }\" are not being mapped correctly for #{ identity.singularize } reads from method #{ caller[3][/`.*'/][1..-2] }! This read will return no objects.")
|
|
34
|
+
|
|
35
|
+
break
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
next if identity_hash[mode] && !identity_hash[mode].include?(specific_identity)
|
|
39
|
+
next if identity == 'servers' && identity_hash['state'] == 'DELETED' #FOR SOME REASON you will get status 'DELETED' items on reads sometimes for rackspace servers
|
|
40
|
+
|
|
41
|
+
case identity
|
|
42
|
+
when 'image' then @main_obj["specific_#{ identity }"] << identity_hash unless identity_hash[mode].include?(@options['virtualization_mode'])
|
|
43
|
+
else @main_obj["specific_#{ identity }"] << identity_hash
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
ap(identity_hash) if output
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
puts("#{ specific_identity } not found in #{ identity }!") if @main_obj["specific_#{ identity }"].empty?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def generic_destroy_parse destroy_hash, identity, resource, mode='name'
|
|
53
|
+
puts("Queried #{ identity } #{ ap @main_obj["specific_#{ identity }"] }") if @options['verbose']
|
|
54
|
+
|
|
55
|
+
raise "#{ identity.singularize } not found for #{ destroy_hash[mode] }" unless @main_obj["specific_#{ identity }"]
|
|
56
|
+
|
|
57
|
+
if destroy_hash[mode].empty? || @main_obj["specific_#{ identity }"].last[mode] != destroy_hash[mode] #without this it will delete the first object in the list, this is obviously bad
|
|
58
|
+
raise "Name mismatch on destroy! Expected #{ destroy_hash[mode] } and was going to destroy #{ @main_obj["specific_#{ identity }"].last[mode] }"
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
puts "Destroying #{ destroy_hash[mode] }..."
|
|
62
|
+
|
|
63
|
+
specific_fog_object = @classes['auth'].auth_service(resource).instance_eval(identity).get @main_obj["specific_#{ identity }"].last['id']
|
|
64
|
+
|
|
65
|
+
@main_obj["#{ identity }_destroy_request"] = specific_fog_object.destroy
|
|
66
|
+
|
|
67
|
+
puts "REMINDER! This destroy is not instant! It can take up to a few minutes for a #{ identity.singularize } to actually be fully destroyed!"
|
|
68
|
+
end
|
|
69
|
+
end
|
|
70
|
+
end
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
|
|
2
|
+
class CloudInteractor
|
|
3
|
+
class Image #http://docs.rackspace.com/servers/api/v2/cs-devguide/content/List_Images-d1e4435.html
|
|
4
|
+
IDENTITY = 'images'
|
|
5
|
+
RESOURCE = 'compute'
|
|
6
|
+
|
|
7
|
+
def initialize main_obj, classes, options={}
|
|
8
|
+
@main_obj = main_obj
|
|
9
|
+
@options = options
|
|
10
|
+
@classes = classes
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def run method, args
|
|
14
|
+
self.send(method, args)
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def list args, output=true
|
|
18
|
+
@classes['helper'].generic_list_call IDENTITY, RESOURCE, output
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def read args, output=true
|
|
22
|
+
list [], false
|
|
23
|
+
|
|
24
|
+
@classes['helper'].generic_read_parse args, IDENTITY, output
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
class CloudInteractor
|
|
2
|
+
def parse_args args, final_args={}
|
|
3
|
+
#example args: domain "create:mydomain.us:23.253.44.192:my"
|
|
4
|
+
|
|
5
|
+
raise "This class does not support #{ args[0] } at this time" unless ALLOWED_HLMS.include?(args[0])
|
|
6
|
+
|
|
7
|
+
@classes[args[0]].send(:run, args[1].split(':').first, parse_args_hash(args))
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def parse_args_hash args, hash_args={}
|
|
11
|
+
prep_args = args[1].split(':')
|
|
12
|
+
|
|
13
|
+
hash_args[prep_args[0]] = {}
|
|
14
|
+
|
|
15
|
+
if prep_args.count > 1
|
|
16
|
+
prep_args[1..(prep_args.count-1)].each do |arg|
|
|
17
|
+
hash_args[prep_args[0]][prep_args.index(arg)] = arg
|
|
18
|
+
end
|
|
19
|
+
else
|
|
20
|
+
hash_args[prep_args[0]]['type'] = prep_args[0]
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
mappings = case
|
|
24
|
+
#this case should always be first or else "server" case overrides
|
|
25
|
+
when prep_args[0] =~ /attach_volume|detach_volume|list_volumes|read_volume/ then { 1 => "server_name", 2 => "volume_name", 3 => "size", 4 => "device_location", 5 => 'volume_type'}
|
|
26
|
+
#remap the args from generic args to ones specific for a case
|
|
27
|
+
when args[0] =~ /domain/ then { 1 => "domain", 2 => "subdomain", 3 => "target_ip", 4 => 'type' }
|
|
28
|
+
when args[0] =~ /server/ then { 1 => "name", 2 => "flavor" }
|
|
29
|
+
when args[0] =~ /volume/ then { 1 => "display_name", 2 => "size", 3 => 'volume_type' }
|
|
30
|
+
when args[0] =~ /flavor/ then { 1 => "name" }
|
|
31
|
+
when args[0] =~ /image/ then { 1 => "name" }
|
|
32
|
+
else raise "FATAL! Unsupported High Level Class #{ args[0] } for CloudInteractor! Please raise an issue on github with this stacktrace! args:#{ args }"
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
Hash[hash_args[hash_args.keys.first].map {|k,v| [mappings[k], v]}]
|
|
36
|
+
end
|
|
37
|
+
end
|