dtk-common-core 0.5.6
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 +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
|