dtk-common-core 0.5.6
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +5 -0
- data/Gemfile +2 -0
- data/LICENSE +674 -0
- data/README.md +6 -0
- data/dtk-common-core.gemspec +19 -0
- data/lib/auxiliary.rb +131 -0
- data/lib/dtk-common-core/version.rb +3 -0
- data/lib/dtk_common_core.rb +5 -0
- data/lib/errors.rb +2 -0
- data/lib/errors/errors.rb +127 -0
- data/lib/errors/rest_error.rb +59 -0
- data/lib/hash_object.rb +49 -0
- data/lib/log.rb +37 -0
- data/lib/response.rb +195 -0
- metadata +73 -0
data/README.md
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require File.expand_path('../lib/dtk-common-core/version', __FILE__)
|
3
|
+
|
4
|
+
Gem::Specification.new do |gem|
|
5
|
+
gem.authors = ["Rich PELAVIN"]
|
6
|
+
gem.email = ["rich@reactor8.com"]
|
7
|
+
gem.description = %q{Dtk common repo is core common librabry of dtk, and mostly hold libraries used for http communication.}
|
8
|
+
gem.summary = %q{Common libraries used for DTK CLI client.}
|
9
|
+
gem.homepage = "https://github.com/rich-reactor8/dtk-common-repo"
|
10
|
+
gem.licenses = ["GPL-3.0"]
|
11
|
+
|
12
|
+
gem.files = `git ls-files`.split($\)
|
13
|
+
gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
|
14
|
+
gem.name = "dtk-common-core"
|
15
|
+
gem.require_paths = ["lib"]
|
16
|
+
gem.version = "#{DtkCommonCore::VERSION}.#{ARGV[3]}".chomp(".")
|
17
|
+
|
18
|
+
gem.add_dependency 'rest-client','~> 1.6.7'
|
19
|
+
end
|
data/lib/auxiliary.rb
ADDED
@@ -0,0 +1,131 @@
|
|
1
|
+
#TODO: will start moving over to using DtkCommon namespace; versions in DtkCommon namespace also in DTK::Common are teh upgraded versions
|
2
|
+
require 'etc'
|
3
|
+
|
4
|
+
module DtkCommon
|
5
|
+
module Aux
|
6
|
+
def self.dtk_instance_repo_username(tenant_id=nil)
|
7
|
+
instance_unique_id = get_ec2_instance_id() || get_macaddress().gsub(/:/,'-')
|
8
|
+
tenant_id ||= ::DTK::Common::Aux.running_process_user()
|
9
|
+
"dtk-#{instance_unique_id}--#{tenant_id}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module DTK
|
15
|
+
module Common
|
16
|
+
module AuxMixin
|
17
|
+
def get_ssh_rsa_pub_key()
|
18
|
+
path = "#{running_process_home_dir()}/.ssh/id_rsa.pub"
|
19
|
+
begin
|
20
|
+
File.open(path){|f|f.read}.chomp
|
21
|
+
rescue Errno::ENOENT
|
22
|
+
raise Error.new("user (#{ENV['USER']}) does not have a public key under #{path}")
|
23
|
+
rescue => e
|
24
|
+
raise e
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def hash_subset(hash,keys_subset,opts={})
|
29
|
+
keys_subset.inject(Hash.new) do |h,k|
|
30
|
+
index = k.kind_of?(Hash) ? k.keys.first : k
|
31
|
+
if opts[:no_non_nil] and hash[index].nil? then h
|
32
|
+
elsif not hash.has_key?(index) then h
|
33
|
+
else
|
34
|
+
key = k.kind_of?(Hash) ? k.values.first : k
|
35
|
+
val = hash[index]
|
36
|
+
h.merge(key => val)
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def convert_keys_to_symbols(hash)
|
42
|
+
hash.keys.inject(Hash.new){|h,k|h.merge(k.to_sym => hash[k])}
|
43
|
+
end
|
44
|
+
|
45
|
+
def dtk_instance_repo_username()
|
46
|
+
#on ec2 changing mac addresses; so selectively pick instance id on ec2
|
47
|
+
unique_id = get_ec2_instance_id() || get_macaddress().gsub(/:/,'-')
|
48
|
+
"dtk-#{unique_id}"
|
49
|
+
end
|
50
|
+
|
51
|
+
def update_ssh_known_hosts(remote_host)
|
52
|
+
fingerprint = `ssh-keyscan -H -t rsa #{remote_host}`
|
53
|
+
ssh_known_hosts = "#{running_process_home_dir()}/.ssh/known_hosts"
|
54
|
+
if File.file?(ssh_known_hosts)
|
55
|
+
`ssh-keygen -f "#{ssh_known_hosts}" -R #{remote_host}`
|
56
|
+
end
|
57
|
+
File.open(ssh_known_hosts,"a"){|f| f << "#{fingerprint}\n"}
|
58
|
+
end
|
59
|
+
|
60
|
+
def get_macaddress()
|
61
|
+
return @macaddress if @macaddress
|
62
|
+
#TODO: may just use underlying routines for facter - macaddress
|
63
|
+
require 'facter'
|
64
|
+
collection = ::Facter.collection
|
65
|
+
@macaddress = collection.fact('macaddress').value
|
66
|
+
end
|
67
|
+
|
68
|
+
def get_ec2_public_dns()
|
69
|
+
get_ec2_meta_data('public-hostname')
|
70
|
+
end
|
71
|
+
|
72
|
+
def get_ec2_instance_id()
|
73
|
+
# @ec2_instance_id_cached used because it could have tried to get this info and result was null
|
74
|
+
return @ec2_instance_id if @ec2_instance_id_cached
|
75
|
+
@ec2_instance_id_cached = true
|
76
|
+
@ec2_instance_id = get_ec2_meta_data('instance-id')
|
77
|
+
end
|
78
|
+
|
79
|
+
def snake_to_camel_case(snake_case)
|
80
|
+
snake_case.gsub(/(^|_)(.)/) { $2.upcase }
|
81
|
+
end
|
82
|
+
|
83
|
+
def platform_is_linux?()
|
84
|
+
RUBY_PLATFORM.downcase.include?("linux")
|
85
|
+
end
|
86
|
+
|
87
|
+
def platform_is_windows?()
|
88
|
+
RUBY_PLATFORM.downcase.include?("mswin") or RUBY_PLATFORM.downcase.include?("mingw")
|
89
|
+
end
|
90
|
+
|
91
|
+
def running_process_user()
|
92
|
+
if platform_is_windows?()
|
93
|
+
Etc.getlogin
|
94
|
+
else
|
95
|
+
Etc.getpwuid(Process.uid).name
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
def running_process_home_dir()
|
100
|
+
if platform_is_windows?()
|
101
|
+
File.expand_path('~')
|
102
|
+
else
|
103
|
+
Etc.getpwuid(Process.uid).dir
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
private
|
108
|
+
def get_ec2_meta_data(var)
|
109
|
+
#Fragments taken from Puppetlabs facter ec2
|
110
|
+
require 'open-uri'
|
111
|
+
require 'timeout'
|
112
|
+
ret = nil
|
113
|
+
begin
|
114
|
+
url = "http://169.254.169.254:80/"
|
115
|
+
Timeout::timeout(WaitSec) {open(url)}
|
116
|
+
ret = OpenURI.open_uri("http://169.254.169.254/2008-02-01/meta-data/#{var}").read
|
117
|
+
rescue Timeout::Error
|
118
|
+
rescue
|
119
|
+
#TODO: unexpected; write t log what error is
|
120
|
+
end
|
121
|
+
ret
|
122
|
+
end
|
123
|
+
WaitSec = 2
|
124
|
+
end
|
125
|
+
module Aux
|
126
|
+
class << self
|
127
|
+
include AuxMixin
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|
@@ -0,0 +1,5 @@
|
|
1
|
+
require File.expand_path('auxiliary.rb', File.dirname(__FILE__))
|
2
|
+
require File.expand_path('errors.rb', File.dirname(__FILE__))
|
3
|
+
require File.expand_path('hash_object.rb', File.dirname(__FILE__))
|
4
|
+
require File.expand_path('log.rb', File.dirname(__FILE__))
|
5
|
+
require File.expand_path('response.rb', File.dirname(__FILE__))
|
data/lib/errors.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
#TODO: should have a Common namespace put in after DTK
|
2
|
+
module DTK
|
3
|
+
class Error < NameError
|
4
|
+
def self.top_error_in_hash()
|
5
|
+
{:error => :Error}
|
6
|
+
end
|
7
|
+
def initialize(msg="",name_or_opts=nil)
|
8
|
+
name = nil
|
9
|
+
opts = Hash.new
|
10
|
+
if name_or_opts.kind_of?(Hash)
|
11
|
+
opts = name_or_opts
|
12
|
+
else
|
13
|
+
name = name_or_opts
|
14
|
+
end
|
15
|
+
super(msg,name)
|
16
|
+
#TODO: might make default to be :log_error => false
|
17
|
+
unless opts.has_key?(:log_error) and not opts[:log_error]
|
18
|
+
Log.info(to_s)
|
19
|
+
if caller_info = opts[:caller_info]
|
20
|
+
caller_depth = (caller_info.kind_of?(Hash) ? caller_info[:depth] : nil)||DefaultCallerDepth
|
21
|
+
Log.info_pp(caller[CallerOffset,caller_depth])
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
CallerOffset = 3
|
26
|
+
DefaultCallerDepth = 3
|
27
|
+
|
28
|
+
def to_hash()
|
29
|
+
if to_s == ""
|
30
|
+
Error.top_error_in_hash()
|
31
|
+
elsif name.nil?
|
32
|
+
{:error => {:Error => {:msg => to_s}}}
|
33
|
+
else
|
34
|
+
{:error => {name.to_sym => {:msg => to_s}}}
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
class R8ParseError < Error
|
40
|
+
def initialize(msg,calling_obj=nil)
|
41
|
+
msg = (calling_obj ? "#{msg} in class #{calling_obj.class.to_s}" : msg)
|
42
|
+
super(msg)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
class ErrorUsage < Error
|
46
|
+
end
|
47
|
+
|
48
|
+
class ErrorConstraintViolations < ErrorUsage
|
49
|
+
def initialize(violations)
|
50
|
+
super(msg(violations),:ConstraintViolations)
|
51
|
+
end
|
52
|
+
private
|
53
|
+
def msg(violations)
|
54
|
+
return ("constraint violation: " + violations) if violations.kind_of?(String)
|
55
|
+
v_with_text = violations.compact
|
56
|
+
if v_with_text.size < 2
|
57
|
+
return "constraint violations"
|
58
|
+
elsif v_with_text.size == 2
|
59
|
+
return "constraint violations: #{v_with_text[1]}"
|
60
|
+
end
|
61
|
+
ret = "constraint violations: "
|
62
|
+
ret << (v_with_text.first == :or ? "(atleast) one of " : "")
|
63
|
+
ret << "(#{v_with_text[1..v_with_text.size-1].join(", ")})"
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
class ErrorUserInputNeeded < ErrorUsage
|
68
|
+
def initialize(needed_inputs)
|
69
|
+
super()
|
70
|
+
@needed_inputs = needed_inputs
|
71
|
+
end
|
72
|
+
def to_s()
|
73
|
+
ret = "following inputs are needed:\n"
|
74
|
+
@needed_inputs.each do |k,v|
|
75
|
+
ret << " #{k}: type=#{v[:type]}; description=#{v[:description]}\n"
|
76
|
+
end
|
77
|
+
ret
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
class ErrorNotImplemented < Error
|
82
|
+
def initialize(msg="NotImplemented error")
|
83
|
+
super("in #{this_parent_parent_method}: #{msg}",:NotImplemented)
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class ErrorNotFound < Error
|
88
|
+
attr_reader :obj_type,:obj_value
|
89
|
+
def initialize(obj_type=nil,obj_value=nil)
|
90
|
+
@obj_type = obj_type
|
91
|
+
@obj_value = obj_value
|
92
|
+
end
|
93
|
+
def to_s()
|
94
|
+
if obj_type.nil?
|
95
|
+
"NotFound error:"
|
96
|
+
elsif obj_value.nil?
|
97
|
+
"NotFound error: type = #{@obj_type.to_s}"
|
98
|
+
else
|
99
|
+
"NotFound error: #{@obj_type.to_s} = #{@obj_value.to_s}"
|
100
|
+
end
|
101
|
+
end
|
102
|
+
def to_hash()
|
103
|
+
if obj_type.nil?
|
104
|
+
{:error => :NotFound}
|
105
|
+
elsif obj_value.nil?
|
106
|
+
{:error => {:NotFound => {:type => @obj_type}}}
|
107
|
+
else
|
108
|
+
{:error => {:NotFound => {:type => @obj_type, :value => @obj_value}}}
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
class ErrorAMQP < Error
|
114
|
+
def to_s()
|
115
|
+
"AMQP error"
|
116
|
+
end
|
117
|
+
end
|
118
|
+
class ErrorAMQPQueueDoesNotExist < ErrorAMQP
|
119
|
+
attr_reader :queue_name
|
120
|
+
def initialize(queue_name)
|
121
|
+
@queue_name = queue_name
|
122
|
+
end
|
123
|
+
def to_s()
|
124
|
+
"queue #{queue_name} does not exist"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
#TODO: should have a Common namespace put in after DTK
|
2
|
+
#When creating these objects, an internal errro class is passed to the creation functions
|
3
|
+
module DTK
|
4
|
+
class RestError
|
5
|
+
def self.create(err)
|
6
|
+
if RestUsageError.match?(err)
|
7
|
+
RestUsageError.new(err)
|
8
|
+
elsif NotFound.match?(err)
|
9
|
+
NotFound.new(err)
|
10
|
+
else
|
11
|
+
Internal.new(err)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
def initialize(err)
|
15
|
+
@code = nil
|
16
|
+
@message = nil
|
17
|
+
end
|
18
|
+
def hash_form()
|
19
|
+
{:code => code||:error, :message => message||''}
|
20
|
+
end
|
21
|
+
private
|
22
|
+
attr_reader :code, :message
|
23
|
+
public
|
24
|
+
#its either its a usage or and internal (application error) bug
|
25
|
+
class Internal < RestError
|
26
|
+
def hash_form()
|
27
|
+
super.merge(:internal => true)
|
28
|
+
end
|
29
|
+
private
|
30
|
+
def initialize(err)
|
31
|
+
super
|
32
|
+
@message = "#{err.to_s} (#{err.backtrace.first})"
|
33
|
+
end
|
34
|
+
end
|
35
|
+
class RestUsageError < RestError
|
36
|
+
def initialize(err)
|
37
|
+
super
|
38
|
+
@message = err.to_s
|
39
|
+
end
|
40
|
+
def self.match?(err)
|
41
|
+
err.kind_of?(ErrorUsage)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
class NotFound < RestUsageError
|
45
|
+
def self.match?(err)
|
46
|
+
err.kind_of?(::NoMethodError) and is_controller_method(err)
|
47
|
+
end
|
48
|
+
def initialize(err)
|
49
|
+
super
|
50
|
+
@code = :not_found
|
51
|
+
@message = "'#{err.name}' was not found"
|
52
|
+
end
|
53
|
+
private
|
54
|
+
def self.is_controller_method(err)
|
55
|
+
err.to_s =~ /#<XYZ::.+Controller:/
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
data/lib/hash_object.rb
ADDED
@@ -0,0 +1,49 @@
|
|
1
|
+
module DTK
|
2
|
+
module Common
|
3
|
+
class SimpleHashObject < Hash
|
4
|
+
def initialize(initial_val=nil,&block)
|
5
|
+
block ? super(&block) : super()
|
6
|
+
if initial_val
|
7
|
+
replace(initial_val)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
# require 'active_support/ordered_hash'
|
13
|
+
# class SimpleOrderedHash < ::ActiveSupport::OrderedHash
|
14
|
+
class SimpleOrderedHash < Hash
|
15
|
+
def initialize(elements=[])
|
16
|
+
super()
|
17
|
+
elements = [elements] unless elements.kind_of?(Array)
|
18
|
+
elements.each{|el|self[el.keys.first] = el.values.first}
|
19
|
+
end
|
20
|
+
|
21
|
+
#set unless value is nill
|
22
|
+
def set_unless_nil(k,v)
|
23
|
+
self[k] = v unless v.nil?
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
class PrettyPrintHash < SimpleOrderedHash
|
28
|
+
#field with '?' suffix means optioanlly add depending on whether name present and non-null in source
|
29
|
+
#if block is given then apply to source[name] rather than returning just source[name]
|
30
|
+
def add(model_object,*keys,&block)
|
31
|
+
keys.each do |key|
|
32
|
+
#if marked as optional skip if not present
|
33
|
+
if key.to_s =~ /(^.+)\?$/
|
34
|
+
key = $1.to_sym
|
35
|
+
next unless model_object[key]
|
36
|
+
end
|
37
|
+
#special treatment of :id
|
38
|
+
val = (key == :id ? model_object.id : model_object[key])
|
39
|
+
self[key] = (block ? block.call(val) : val)
|
40
|
+
end
|
41
|
+
self
|
42
|
+
end
|
43
|
+
|
44
|
+
def slice(*keys)
|
45
|
+
keys.inject(self.class.new){|h,k|h.merge(k => self[k])}
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
data/lib/log.rb
ADDED
@@ -0,0 +1,37 @@
|
|
1
|
+
#TODO: bring in a production quality ruby logging capability that gets wrapped here
|
2
|
+
#TODO: would put this in config
|
3
|
+
module DTK
|
4
|
+
module Log
|
5
|
+
Config = Hash.new
|
6
|
+
Config[:print_time] = false
|
7
|
+
Config[:print_method] = false
|
8
|
+
|
9
|
+
def self.info(msg, out = $stdout)
|
10
|
+
out << "info: "
|
11
|
+
out << format(msg)
|
12
|
+
end
|
13
|
+
def self.debug(msg, out = $stdout)
|
14
|
+
out << "debug: "
|
15
|
+
out << format(msg)
|
16
|
+
end
|
17
|
+
def self.error(msg, out = $stdout)
|
18
|
+
out << "error: "
|
19
|
+
out << format(msg)
|
20
|
+
end
|
21
|
+
def self.info_pp(obj, out = $stdout)
|
22
|
+
out << Aux::pp_form(obj)
|
23
|
+
end
|
24
|
+
def self.debug_pp(obj, out = $stdout)
|
25
|
+
out << Aux::pp_form(obj)
|
26
|
+
obj
|
27
|
+
end
|
28
|
+
private
|
29
|
+
def self.format(msg)
|
30
|
+
ret = String.new
|
31
|
+
ret << "#{Time.now}: " if Config[:print_time]
|
32
|
+
ret << "in fn: #{this_parent_method}: " if Config[:print_method]
|
33
|
+
ret << msg
|
34
|
+
ret << "\n"
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|