dtk-shell 0.10.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.
Files changed (191) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +5 -0
  3. data/Gemfile_dev +13 -0
  4. data/README.md +121 -0
  5. data/bin/dtk-execute +32 -0
  6. data/bin/dtk-run +92 -0
  7. data/bin/dtk-shell +31 -0
  8. data/dtk-shell.gemspec +50 -0
  9. data/lib/auxiliary.rb +61 -0
  10. data/lib/bundler_monkey_patch.rb +26 -0
  11. data/lib/client.rb +58 -0
  12. data/lib/command_helper.rb +33 -0
  13. data/lib/command_helpers/git_repo.rb +589 -0
  14. data/lib/command_helpers/git_repo/merge.rb +153 -0
  15. data/lib/command_helpers/jenkins_client.rb +106 -0
  16. data/lib/command_helpers/jenkins_client/config_xml.rb +288 -0
  17. data/lib/command_helpers/service_importer.rb +251 -0
  18. data/lib/command_helpers/service_link.rb +33 -0
  19. data/lib/command_helpers/test_module_creator.rb +69 -0
  20. data/lib/command_helpers/test_module_templates/dtk.model.yaml.eruby +10 -0
  21. data/lib/command_helpers/test_module_templates/spec_helper.rb.eruby +10 -0
  22. data/lib/command_helpers/test_module_templates/temp_component_spec.rb.eruby +5 -0
  23. data/lib/commands.rb +57 -0
  24. data/lib/commands/common/thor/access_control.rb +133 -0
  25. data/lib/commands/common/thor/action_result_handler.rb +74 -0
  26. data/lib/commands/common/thor/assembly_template.rb +92 -0
  27. data/lib/commands/common/thor/assembly_workspace.rb +1801 -0
  28. data/lib/commands/common/thor/base_command_helper.rb +59 -0
  29. data/lib/commands/common/thor/clone.rb +82 -0
  30. data/lib/commands/common/thor/common.rb +88 -0
  31. data/lib/commands/common/thor/common_base.rb +49 -0
  32. data/lib/commands/common/thor/create_target.rb +70 -0
  33. data/lib/commands/common/thor/edit.rb +255 -0
  34. data/lib/commands/common/thor/inventory_parser.rb +98 -0
  35. data/lib/commands/common/thor/list_diffs.rb +128 -0
  36. data/lib/commands/common/thor/module.rb +1011 -0
  37. data/lib/commands/common/thor/module/import.rb +210 -0
  38. data/lib/commands/common/thor/node.rb +53 -0
  39. data/lib/commands/common/thor/poller.rb +65 -0
  40. data/lib/commands/common/thor/pull_clone_changes.rb +28 -0
  41. data/lib/commands/common/thor/pull_from_remote.rb +152 -0
  42. data/lib/commands/common/thor/puppet_forge.rb +72 -0
  43. data/lib/commands/common/thor/purge_clone.rb +101 -0
  44. data/lib/commands/common/thor/push_clone_changes.rb +162 -0
  45. data/lib/commands/common/thor/push_to_remote.rb +94 -0
  46. data/lib/commands/common/thor/remotes.rb +71 -0
  47. data/lib/commands/common/thor/reparse.rb +40 -0
  48. data/lib/commands/common/thor/set_required_attributes.rb +46 -0
  49. data/lib/commands/thor/account.rb +239 -0
  50. data/lib/commands/thor/assembly.rb +356 -0
  51. data/lib/commands/thor/attribute.rb +79 -0
  52. data/lib/commands/thor/component.rb +70 -0
  53. data/lib/commands/thor/component_module.rb +501 -0
  54. data/lib/commands/thor/component_template.rb +174 -0
  55. data/lib/commands/thor/dependency.rb +34 -0
  56. data/lib/commands/thor/developer.rb +144 -0
  57. data/lib/commands/thor/dtk.rb +152 -0
  58. data/lib/commands/thor/library.rb +125 -0
  59. data/lib/commands/thor/node.rb +504 -0
  60. data/lib/commands/thor/node_template.rb +94 -0
  61. data/lib/commands/thor/project.rb +34 -0
  62. data/lib/commands/thor/provider.rb +233 -0
  63. data/lib/commands/thor/remotes.rb +49 -0
  64. data/lib/commands/thor/service.rb +941 -0
  65. data/lib/commands/thor/service_module.rb +914 -0
  66. data/lib/commands/thor/state_change.rb +25 -0
  67. data/lib/commands/thor/target.rb +250 -0
  68. data/lib/commands/thor/task.rb +116 -0
  69. data/lib/commands/thor/test_module.rb +310 -0
  70. data/lib/commands/thor/utils.rb +21 -0
  71. data/lib/commands/thor/workspace.rb +685 -0
  72. data/lib/config/cacert.pem +3785 -0
  73. data/lib/config/client.conf.header +20 -0
  74. data/lib/config/configuration.rb +99 -0
  75. data/lib/config/default.conf +16 -0
  76. data/lib/config/disk_cacher.rb +80 -0
  77. data/lib/configurator.rb +176 -0
  78. data/lib/context_router.rb +44 -0
  79. data/lib/core.rb +497 -0
  80. data/lib/domain/git_adapter.rb +412 -0
  81. data/lib/domain/git_error_handler.rb +64 -0
  82. data/lib/domain/response.rb +285 -0
  83. data/lib/domain/response/error_handler.rb +86 -0
  84. data/lib/dtk-shell/version.rb +20 -0
  85. data/lib/dtk_constants.rb +40 -0
  86. data/lib/dtk_error.rb +114 -0
  87. data/lib/dtk_logger.rb +126 -0
  88. data/lib/dtk_shell.rb +31 -0
  89. data/lib/error.rb +85 -0
  90. data/lib/execute.rb +29 -0
  91. data/lib/execute/cli_pure/cli_rerouter.rb +102 -0
  92. data/lib/execute/command.rb +40 -0
  93. data/lib/execute/command/api_call.rb +60 -0
  94. data/lib/execute/command/api_call/map.rb +60 -0
  95. data/lib/execute/command/api_call/service.rb +91 -0
  96. data/lib/execute/command/api_call/translation_term.rb +119 -0
  97. data/lib/execute/command/rest_call.rb +37 -0
  98. data/lib/execute/command_processor.rb +30 -0
  99. data/lib/execute/command_processor/rest_call.rb +59 -0
  100. data/lib/execute/error_usage.rb +21 -0
  101. data/lib/execute/execute_context.rb +86 -0
  102. data/lib/execute/execute_context/result_store.rb +37 -0
  103. data/lib/execute/script.rb +64 -0
  104. data/lib/execute/script/add_tenant.rb +121 -0
  105. data/lib/git-logs/git.log +0 -0
  106. data/lib/parser/adapters/option_parser.rb +70 -0
  107. data/lib/parser/adapters/thor.rb +555 -0
  108. data/lib/parser/adapters/thor/common_option_defs.rb +40 -0
  109. data/lib/require_first.rb +104 -0
  110. data/lib/search_hash.rb +44 -0
  111. data/lib/shell.rb +261 -0
  112. data/lib/shell/context.rb +1065 -0
  113. data/lib/shell/context_aux.rb +46 -0
  114. data/lib/shell/domain/active_context.rb +186 -0
  115. data/lib/shell/domain/context_entity.rb +89 -0
  116. data/lib/shell/domain/context_params.rb +223 -0
  117. data/lib/shell/domain/override_tasks.rb +88 -0
  118. data/lib/shell/domain/shadow_entity.rb +76 -0
  119. data/lib/shell/header_shell.rb +44 -0
  120. data/lib/shell/help_monkey_patch.rb +283 -0
  121. data/lib/shell/interactive_wizard.rb +225 -0
  122. data/lib/shell/message_queue.rb +63 -0
  123. data/lib/shell/parse_monkey_patch.rb +39 -0
  124. data/lib/shell/status_monitor.rb +124 -0
  125. data/lib/task_status.rb +83 -0
  126. data/lib/task_status/refresh_mode.rb +77 -0
  127. data/lib/task_status/snapshot_mode.rb +28 -0
  128. data/lib/task_status/stream_mode.rb +48 -0
  129. data/lib/task_status/stream_mode/element.rb +101 -0
  130. data/lib/task_status/stream_mode/element/format.rb +101 -0
  131. data/lib/task_status/stream_mode/element/hierarchical_task.rb +100 -0
  132. data/lib/task_status/stream_mode/element/hierarchical_task/result.rb +72 -0
  133. data/lib/task_status/stream_mode/element/hierarchical_task/result/action.rb +93 -0
  134. data/lib/task_status/stream_mode/element/hierarchical_task/result/components.rb +26 -0
  135. data/lib/task_status/stream_mode/element/hierarchical_task/result/node_level.rb +26 -0
  136. data/lib/task_status/stream_mode/element/hierarchical_task/steps.rb +34 -0
  137. data/lib/task_status/stream_mode/element/hierarchical_task/steps/action.rb +53 -0
  138. data/lib/task_status/stream_mode/element/hierarchical_task/steps/components.rb +53 -0
  139. data/lib/task_status/stream_mode/element/hierarchical_task/steps/node_level.rb +42 -0
  140. data/lib/task_status/stream_mode/element/no_results.rb +26 -0
  141. data/lib/task_status/stream_mode/element/render.rb +59 -0
  142. data/lib/task_status/stream_mode/element/stage.rb +84 -0
  143. data/lib/task_status/stream_mode/element/stage/render.rb +76 -0
  144. data/lib/task_status/stream_mode/element/task_end.rb +35 -0
  145. data/lib/task_status/stream_mode/element/task_start.rb +37 -0
  146. data/lib/util/common_util.rb +37 -0
  147. data/lib/util/console.rb +235 -0
  148. data/lib/util/dtk_puppet.rb +65 -0
  149. data/lib/util/module_util.rb +66 -0
  150. data/lib/util/os_util.rb +385 -0
  151. data/lib/util/permission_util.rb +31 -0
  152. data/lib/util/remote_dependency_util.rb +84 -0
  153. data/lib/util/ssh_util.rb +94 -0
  154. data/lib/view_processor.rb +129 -0
  155. data/lib/view_processor/augmented_simple_list.rb +44 -0
  156. data/lib/view_processor/hash_pretty_print.rb +123 -0
  157. data/lib/view_processor/simple_list.rb +156 -0
  158. data/lib/view_processor/table_print.rb +309 -0
  159. data/lib/violation.rb +86 -0
  160. data/lib/violation/attribute.rb +76 -0
  161. data/lib/violation/fix.rb +26 -0
  162. data/lib/violation/fix/result.rb +73 -0
  163. data/lib/violation/fix/result/error.rb +34 -0
  164. data/lib/violation/fix/set_attribute.rb +41 -0
  165. data/lib/violation/sub_classes.rb +60 -0
  166. data/puppet/manifests/init.pp +72 -0
  167. data/puppet/manifests/params.pp +16 -0
  168. data/puppet/r8meta.puppet.yml +35 -0
  169. data/puppet/templates/bash_profile.erb +2 -0
  170. data/puppet/templates/client.conf.erb +1 -0
  171. data/puppet/templates/dtkclient.erb +2 -0
  172. data/spec/component_module_spec.rb +34 -0
  173. data/spec/dependency_spec.rb +6 -0
  174. data/spec/dtk_shell_spec.rb +13 -0
  175. data/spec/dtk_spec.rb +33 -0
  176. data/spec/lib/spec_helper.rb +10 -0
  177. data/spec/lib/spec_thor.rb +108 -0
  178. data/spec/node_template_spec.rb +24 -0
  179. data/spec/project_spec.rb +6 -0
  180. data/spec/repo_spec.rb +7 -0
  181. data/spec/response_spec.rb +52 -0
  182. data/spec/service_module_spec.rb +38 -0
  183. data/spec/service_spec.rb +50 -0
  184. data/spec/state_change_spec.rb +7 -0
  185. data/spec/table_print_spec.rb +48 -0
  186. data/spec/target_spec.rb +57 -0
  187. data/spec/task_spec.rb +28 -0
  188. data/views/assembly/augmented_simple_list.rb +12 -0
  189. data/views/assembly_template/augmented_simple_list.rb +12 -0
  190. data/views/list_task/augmented_simple_list.rb +12 -0
  191. metadata +421 -0
@@ -0,0 +1,37 @@
1
+ #
2
+ # Copyright (C) 2010-2016 dtk contributors
3
+ #
4
+ # This file is part of the dtk project.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ class DTK::Client::Execute
19
+ class ExecuteContext
20
+ class ResultStore < Hash
21
+ def store(result,result_index=nil)
22
+ self[result_index||Index.last] = result
23
+ end
24
+
25
+ def get_last_result?()
26
+ self[Index.last]
27
+ end
28
+
29
+ module Index
30
+ def self.last()
31
+ Last
32
+ end
33
+ Last = :last
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,64 @@
1
+ #
2
+ # Copyright (C) 2010-2016 dtk contributors
3
+ #
4
+ # This file is part of the dtk project.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ class DTK::Client::Execute
19
+ class Script < self
20
+ dtk_require('script/add_tenant')
21
+ def self.execute()
22
+ script_class().execute_script()
23
+ end
24
+
25
+ private
26
+ Scripts = {
27
+ 'add-tenant' => AddTenant,
28
+ }
29
+
30
+ def self.script_class()
31
+ script_name = ARGV.size > 0 && ARGV[0]
32
+ unless script_class = script_name && Scripts[script_name]
33
+ legal_scripts = Scripts.keys
34
+ err_msg = (script_name ? "Unsupported script '#{script_name}'" : "Missing script name")
35
+ err_msg << "; supported scripts are (#{legal_scripts.join(',')})"
36
+ raise ErrorUsage.new(err_msg)
37
+ end
38
+ script_class
39
+ end
40
+
41
+ def self.execute_script()
42
+ params = ret_params_from_argv()
43
+ execute_with_params(params)
44
+ end
45
+
46
+ module OptionParserHelper
47
+ require 'optparse'
48
+ def process_params_from_options(banner,&block)
49
+ OptionParser.new do |opts|
50
+ opts.banner = banner
51
+ block.call(opts)
52
+ end.parse!
53
+ end
54
+
55
+ def show_help_and_exit(banner)
56
+ ARGV[0] = '--help'
57
+ OptionParser.new do |opts|
58
+ opts.banner = banner
59
+ end.parse!
60
+ end
61
+ end
62
+ extend OptionParserHelper
63
+ end
64
+ end
@@ -0,0 +1,121 @@
1
+ #
2
+ # Copyright (C) 2010-2016 dtk contributors
3
+ #
4
+ # This file is part of the dtk project.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ class DTK::Client::Execute::Script
19
+ class AddTenant < self
20
+ def self.ret_params_from_argv()
21
+ banner = "Usage: dtk-execute add-tenant TENANT-NAME CATALOG-USERNAME -p PASSWORD -v VERSION [-s SERVICE-INSTANCE]"
22
+ version = tenant_name = catalog_username = tenant_number = nil
23
+ if ARGV.size > 3
24
+ tenant_name = ARGV[1]
25
+ if tenant_name =~ /^dtk([0-9]+)$/
26
+ tenant_number = $1
27
+ else
28
+ raise ErrorUsage.new("TENANT-NAME must be of form 'dtkNUM', like dtk601")
29
+ end
30
+ catalog_username = ARGV[2]
31
+ else
32
+ show_help_and_exit(banner)
33
+ end
34
+
35
+ params = Hash.new
36
+ process_params_from_options(banner) do |opts|
37
+ opts.on( '-p', '--password PASSWORD', "Password for tenant and catalog" ) do |pw|
38
+ params[:password] = pw
39
+ end
40
+ opts.on( '-s', '--service SERVICE-INSTANCE', "Name of Service instance" ) do |s|
41
+ params[:service_instance] = s
42
+ end
43
+ opts.on( '-v', '--version VERSION', "Version of code" ) do |v|
44
+ params[:version] = v
45
+ end
46
+ end
47
+
48
+ # TODO: use opt parser to enforce that :password and :version options are manditory
49
+ unless params[:password]
50
+ raise ErrorUsage.new("Password is mandatory; use -p commnd line option")
51
+ end
52
+ unless params[:version]
53
+ raise ErrorUsage.new("Version is mandatory; use -v commnd line option")
54
+ end
55
+
56
+ service_instance = params[:service_instance] || "dtkhost#{tenant_number[0]}"
57
+ to_add = {
58
+ :tenant_name => tenant_name,
59
+ :catalog_username => catalog_username,
60
+ :service_instance => service_instance
61
+ }
62
+ params.merge(to_add)
63
+ end
64
+
65
+ def self.execute_with_params(params)
66
+ tenant_name = params[:tenant_name]
67
+ catalog_username = params[:catalog_username]
68
+ service = params[:service_instance]
69
+ password = params[:password]
70
+ version = params[:version]
71
+ server_node = params[:server_node_name] || 'server'
72
+ router_node = params[:router_node_name] || 'router'
73
+
74
+ component_with_namespace = "dtk-meta-user:dtk_tenant[#{tenant_name}]"
75
+ component_namespace, component = (component_with_namespace =~ /(^[^:]+):(.+$)/; [$1,$2])
76
+
77
+ av_pairs = {
78
+ :catalog_username => catalog_username,
79
+ :tenant_password => password,
80
+ :catalog_password => password,
81
+ :server_branch => version,
82
+ }
83
+
84
+ linked_component_instances =
85
+ [
86
+ "#{router_node}/dtk_nginx::vhosts_for_router",
87
+ "#{server_node}/dtk_postgresql::databases",
88
+ "#{server_node}/host::hostname"
89
+ ]
90
+
91
+ ExecuteContext(:print_results => true) do
92
+ result = call 'service/add_component',
93
+ :service => service,
94
+ :node => server_node,
95
+ :component => component,
96
+ :namespace => component_namespace,
97
+ :donot_update_workflow => true
98
+
99
+ av_pairs.each_pair do |a,v|
100
+ result = call 'service/set_attribute',
101
+ :service => service,
102
+ :attribute_path => "#{server_node}/#{component}/#{a}",
103
+ :value => v
104
+ end
105
+
106
+ linked_component_instances.each do |linked_component_instance|
107
+ result = call 'service/link_components',
108
+ :service => service,
109
+ :input_component => "#{server_node}/#{component}",
110
+ :output_component => linked_component_instance
111
+ end
112
+
113
+ result = call 'service/execute_workflow',
114
+ :service => service,
115
+ :workflow_name => 'add_tenant',
116
+ :workflow_params => {'name' => tenant_name}
117
+
118
+ end
119
+ end
120
+ end
121
+ end
File without changes
@@ -0,0 +1,70 @@
1
+ #
2
+ # Copyright (C) 2010-2016 dtk contributors
3
+ #
4
+ # This file is part of the dtk project.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ require 'optparse'
19
+ module DTK
20
+ module Client
21
+ class CommandBaseOptionParser
22
+ include CommandBase
23
+ def initialize(conn)
24
+ @conn = conn
25
+ end
26
+
27
+ def self.execute_from_cli(conn,argv,shell_execute=false)
28
+ return conn.connection_error if conn.connection_error
29
+ method, args_hash = OptionParser.parse_options(self,argv)
30
+ instance = new(conn)
31
+ raise Error.new("Illegal subcommand #{method}") unless instance.respond_to?(method)
32
+ instance.send(method,args_hash)
33
+ end
34
+ class << self
35
+ include Auxiliary
36
+ def command_name()
37
+ snake_form(self,"-")
38
+ end
39
+ end
40
+ end
41
+ class OptionParser
42
+ def self.parse_options(command_class,argv)
43
+ args_hash = Hash.new
44
+ unless subcommand = argv[0]
45
+ raise Error.new("No subcommand given")
46
+ end
47
+ method = subcommand.to_sym
48
+ unless parse_info = (command_class.const_get "CLIParseOptions")[subcommand.to_sym]
49
+ return [method,args_hash]
50
+ end
51
+ ::OptionParser.new do|opts|
52
+ opts.banner = "Usage: #{command_class.command_name} #{subcommand} [options]"
53
+ (parse_info[:options]||[]).each do |parse_info_option|
54
+ raise Error.new("missing param name") unless param_name = parse_info_option[:name]
55
+ raise Error.new("missing optparse spec") unless parse_info_option[:optparse_spec]
56
+ opts.on(*parse_info_option[:optparse_spec]) do |val|
57
+ args_hash[param_name.to_s] = val ? val : true
58
+ end
59
+ end
60
+
61
+ opts.on('-h', '--help', 'Display this screen') do
62
+ puts opts
63
+ exit
64
+ end
65
+ end.parse!(argv)
66
+ [method,args_hash]
67
+ end
68
+ end
69
+ end
70
+ end
@@ -0,0 +1,555 @@
1
+ #
2
+ # Copyright (C) 2010-2016 dtk contributors
3
+ #
4
+ # This file is part of the dtk project.
5
+ #
6
+ # Licensed under the Apache License, Version 2.0 (the "License");
7
+ # you may not use this file except in compliance with the License.
8
+ # You may obtain a copy of the License at
9
+ #
10
+ # http://www.apache.org/licenses/LICENSE-2.0
11
+ #
12
+ # Unless required by applicable law or agreed to in writing, software
13
+ # distributed under the License is distributed on an "AS IS" BASIS,
14
+ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15
+ # See the License for the specific language governing permissions and
16
+ # limitations under the License.
17
+ #
18
+ require 'thor'
19
+ require 'thor/group'
20
+ require 'readline'
21
+ require 'colorize'
22
+ require 'digest/sha1'
23
+
24
+ dtk_require("../../shell/interactive_wizard")
25
+ dtk_require("../../util/os_util")
26
+ dtk_require("../../util/console")
27
+ dtk_require_from_base('task_status')
28
+ dtk_require_from_base("command_helper")
29
+ dtk_require("../../context_router")
30
+ dtk_require_common_commands('thor/poller')
31
+
32
+ module DTK
33
+ module Client
34
+ class CommandBaseThor < Thor
35
+ dtk_nested_require('thor','common_option_defs')
36
+
37
+ include CommandBase
38
+ include CommandHelperMixin
39
+ include Poller
40
+
41
+ extend CommandBase
42
+ extend TaskStatusMixin
43
+ extend Console
44
+ extend CommonOptionDefs::ClassMixin
45
+
46
+ @@cached_response = {}
47
+ @@invalidate_map = []
48
+ TIME_DIFF = 60 # second(s)
49
+ EXTENDED_TIMEOUT = 360 # second(s)
50
+ HIDE_FROM_BASE_CONTEXT = "HIDE_FROM_BASE"
51
+
52
+ # thor command specific constants
53
+ ALT_IDENTIFIER_SEPARATOR = ':::'
54
+
55
+ def initialize(args, opts, config)
56
+ @conn = config[:conn]
57
+ super
58
+ end
59
+
60
+ def self.execute_from_cli(conn,method_name,context_params,thor_options,shell_execution=false)
61
+ @@shell_execution = shell_execution
62
+
63
+ if method_name == 'help'
64
+ ret = start([method_name] + context_params.method_arguments,:conn => conn)
65
+ else
66
+ ret = start([method_name, context_params] + (thor_options||[]),:conn => conn)
67
+ end
68
+
69
+ # special case where we have validation reply
70
+ if ret.kind_of?(Response)
71
+ if ret.validation_response?
72
+ ret = action_on_revalidation_response(ret, conn, method_name, context_params, thor_options, shell_execution)
73
+ end
74
+ end
75
+
76
+ ret.kind_of?(Response) ? ret : Response::NoOp.new
77
+ end
78
+
79
+ # TODO: Make sure that server responds with new format of ARGVS
80
+ def self.action_on_revalidation_response(validation_response, conn, method_name, context_params, thor_options, shell_execution)
81
+ puts "[NOTICE] #{validation_response.validation_message}"
82
+ actions = validation_response.validation_actions
83
+
84
+ actions.each_with_index do |action, i|
85
+ if Console.confirmation_prompt("Pre-action '#{action['action']}' needed, execute"+"?")
86
+ # we have hash map with values { :assembly_id => 2123123123, :option_1 => true }
87
+ # we translate to array of values, with action as first element
88
+
89
+ # def self.execute_from_cli(conn,method_name,context_params,options_args,shell_execution=false)
90
+ response = self.execute_from_cli(conn, action['action'], create_context_arguments(action['params']),[],shell_execution)
91
+ # we abort if error happens
92
+ ResponseErrorHandler.check(response)
93
+
94
+ if action['wait_for_complete']
95
+ entity_id, entity_type = action['wait_for_complete']['id'].to_s, action['wait_for_complete']['type']
96
+ puts "Waiting for task to complete ..."
97
+ task_status_aux(entity_id,entity_type,:wait => true)
98
+ end
99
+ else
100
+ # validation action are being skipped
101
+ return ""
102
+ end
103
+ end
104
+
105
+ puts "Executing original action: '#{method_name}' ..."
106
+ # if all executed correctly we run original action
107
+ return self.execute_from_cli(conn,method_name, context_params,thor_options,shell_execution)
108
+ end
109
+
110
+ def self.invalidate_entities(*array_of_entites)
111
+ # we handle to cases here
112
+ # n-context invalidation, whole structure
113
+ @@invalidate_map << array_of_entites.join('_').to_sym
114
+
115
+ # last entity
116
+ @@invalidate_map << array_of_entites.last.to_sym
117
+ end
118
+
119
+ # we take current timestamp and compare it to timestamp stored in @@cached_response
120
+ # if difference is greater than TIME_DIFF we send request again, if not we use
121
+ # response from cache
122
+ def self.get_cached_response(entity_name, url, subtype={})
123
+ subtype ||= {}
124
+ current_ts = Time.now.to_i
125
+ cache_id = (subtype.empty? ? :response : generate_cached_id(subtype))
126
+
127
+ # if @@cache_response is empty return true if not than return time difference between
128
+ # current_ts and ts stored in cache
129
+ time_difference = @@cached_response[entity_name].nil? ? true : ((current_ts - @@cached_response[entity_name][:ts]) > TIME_DIFF)
130
+
131
+ if (@@cached_response[entity_name])
132
+ time_difference = true if @@cached_response[entity_name][cache_id].nil?
133
+ end
134
+
135
+ if (time_difference || @@invalidate_map.include?(entity_name))
136
+ response = post rest_url(url), subtype
137
+
138
+ # we do not want to catch is if it is not valid
139
+ if response.nil? || response.empty?
140
+ DtkLogger.instance.debug("Response was nil or empty for that reason we did not cache it.")
141
+ return response
142
+ end
143
+
144
+ if response.ok?
145
+ response_hash = {cache_id => response, :ts => current_ts}
146
+
147
+ @@invalidate_map.delete(entity_name) if @@invalidate_map.include?(entity_name)
148
+
149
+ if @@cached_response[entity_name]
150
+ @@cached_response[entity_name].merge!(response_hash)
151
+ else
152
+ @@cached_response.store(entity_name, response_hash)
153
+ end
154
+ end
155
+ end
156
+
157
+ if @@cached_response[entity_name]
158
+ return @@cached_response[entity_name][cache_id]
159
+ else
160
+ return nil
161
+ end
162
+ end
163
+
164
+ def self.generate_cached_id(subtype)
165
+ values = ''
166
+ # subtype.sort.map do |key,value|
167
+ # removed sort since subtype is hash where keys are symbols,
168
+ # sort method uses the <=> comparison operator to put things into order but
169
+ # symbols don't have a <=> comparison operator in ruby 1.8.7
170
+ subtype.map do |key,value|
171
+ values += value.to_s
172
+ end
173
+
174
+ Digest::SHA1.hexdigest(values)
175
+ end
176
+
177
+ def self.create_context_arguments(params)
178
+ context_params = DTK::Shell::ContextParams.new
179
+ params.each do |k,v|
180
+ context_params.add_context_to_params(k,k,v)
181
+ end
182
+ return context_params
183
+ end
184
+
185
+ def self.list_method_supported?
186
+ return (respond_to?(:validation_list) || respond_to?(:whoami))
187
+ end
188
+
189
+ # returns all task names for given thor class with use friendly names (with '-' instead '_')
190
+ def self.task_names
191
+ all_tasks().map(&:first).collect { |item| item.gsub('_','-')}
192
+ end
193
+
194
+ def self.get_usage_info(entity_name, method)
195
+ # no need for nil checks since task will be found
196
+ # [0] element contains desire usage description
197
+ # [2] element contains method name with '_'
198
+ result = printable_tasks().select { |help_item| help_item[2].gsub('_','-') == method }.flatten[0]
199
+ # we add entity name with dashes
200
+ return result.gsub('dtk', "dtk #{entity_name.gsub('_','-')}")
201
+ end
202
+
203
+ # caches all the taks names for each possible tier and each thor class
204
+ # returnes it, executes only once and only on dtk-shell start
205
+ def self.tiered_task_names
206
+ # cached data
207
+ cached_tasks = {}
208
+
209
+ # get command name from namespace (which is derived by thor from file name)
210
+ command = namespace.split(':').last.gsub('_','-').upcase
211
+
212
+ # first elvel identifier
213
+ command_sym = command.downcase.to_sym
214
+ command_id_sym = (command.downcase + '_wid').to_sym
215
+
216
+ cached_tasks.store(command_sym, [])
217
+ cached_tasks.store(command_id_sym, [])
218
+
219
+ # n-context children
220
+ all_children = []
221
+ children = self.respond_to?(:all_children) ? self.all_children() : nil
222
+ all_children << children unless children.nil?
223
+
224
+ # some commands have multiple n-level contexts
225
+ # e.g. workspace_node_component, workspace_utils and workspace_node_utils
226
+ # we go through all of them and load them to 'all_children'
227
+ multi_context_children = self.respond_to?(:multi_context_children) ? self.multi_context_children() : nil
228
+ if multi_context_children
229
+ multi_context_children.each do |mc|
230
+ all_children << (mc.is_a?(Array) ? mc : multi_context_children)
231
+ end
232
+ end
233
+
234
+ # n-context-override task, special case which
235
+ override_task_obj = self.respond_to?(:override_allowed_methods) ? self.override_allowed_methods.dup : nil
236
+
237
+ # we seperate tier 1 and tier 2 tasks
238
+ all_tasks().each do |task|
239
+ # noralize task name with '-' since it will be displayed to user that way
240
+ task_name = task[0].gsub('_','-')
241
+ usage = task[1].usage
242
+ # we will match those commands that require identifiers (NAME/ID)
243
+ # e.g. ASSEMBLY-NAME/ID list ... => MATCH
244
+ # e.g. [ASSEMBLY-NAME/ID] list ... => MATCH
245
+ matched_data = task[1].usage.match(/\[?#{command}.?(NAME\/ID|ID\/NAME)\]?/)
246
+ matched_alt_identifiers_data = nil
247
+
248
+ # we chance alternate providers
249
+ if respond_to?(:alternate_identifiers)
250
+ alternate_identifiers = self.alternate_identifiers()
251
+
252
+ alternate_identifiers.each do |a_provider|
253
+ if matched_alt_identifiers_data = task[1].usage.match(/\[?#{a_provider}.?(NAME\/ID|ID\/NAME)\]?/)
254
+ command_alt_sym = "#{command}_#{a_provider}".downcase.to_sym
255
+ cached_tasks[command_alt_sym] = cached_tasks.fetch(command_alt_sym,[])
256
+ cached_tasks[command_alt_sym] << task_name
257
+ # when found break
258
+ break
259
+ end
260
+ end
261
+ end
262
+
263
+ # only if not matched alt data found continue with caching of task
264
+ unless matched_alt_identifiers_data
265
+ if matched_data.nil?
266
+ # no match it means it is tier 1 task, tier 1 => dtk:\assembly>
267
+ # using HIDE_FROM_BASE_CONTEXT to hide command from base context (e.g from dtk:/assembly>) ...
268
+ # ... but to be able to use that command in other context
269
+ # (e.g get-netstats removed from dtk:/assembly> but used in dtk:/assembly/assembly_id/utils)
270
+ cached_tasks[command_sym] << task_name unless usage.include?(HIDE_FROM_BASE_CONTEXT)
271
+ else
272
+ # match means it is tier 2 taks, tier 2 => dtk:\assembly\231312345>
273
+ cached_tasks[command_id_sym] << task_name
274
+ # if there are '[' it means it is optinal identifiers so it is tier 1 and tier 2 task
275
+ cached_tasks[command_sym] << task_name if matched_data[0].include?('[')
276
+ end
277
+
278
+ # n-level matching
279
+ all_children.each do |child|
280
+ current_children = []
281
+
282
+ child.each do |c|
283
+ current_children << c.to_s
284
+
285
+ # create entry e.g. assembly_node_id
286
+ child_id_sym = (command.downcase + '_' + current_children.join('_') + '_wid').to_sym
287
+
288
+ # n-context matching
289
+ matched_data = task[1].usage.match(/^\[?#{c.to_s.upcase}.?(NAME\/ID|ID\/NAME|ID|NAME)(\-?PATTERN)?\]?/)
290
+ if matched_data
291
+ cached_tasks[child_id_sym] = cached_tasks.fetch(child_id_sym,[]) << task_name
292
+ end
293
+
294
+ # override method list, we add these methods only once
295
+ if override_task_obj && !override_task_obj.is_completed?(c)
296
+ command_o_tasks, identifier_o_tasks = override_task_obj.get_all_tasks(c)
297
+ child_sym = (command.downcase + '_' + current_children.join('_')).to_sym
298
+
299
+ command_o_tasks.each do |o_task|
300
+ cached_tasks[child_sym] = cached_tasks.fetch(child_sym,[]) << o_task[0]
301
+ end
302
+
303
+ identifier_o_tasks.each do |o_task|
304
+ cached_tasks[child_id_sym] = cached_tasks.fetch(child_id_sym,[]) << o_task[0]
305
+ end
306
+
307
+ override_task_obj.add_to_completed(c)
308
+ end
309
+ end
310
+ end
311
+ end
312
+ end
313
+
314
+ # there is always help, and in all cases this is exception to the rule
315
+ cached_tasks[command_id_sym] << 'help'
316
+
317
+ return cached_tasks
318
+ end
319
+
320
+ # we make valid methods to make sure that when context changing
321
+ # we allow change only for valid ID/NAME
322
+ def self.valid_id?(value, conn, context_params)
323
+
324
+ context_list = self.get_identifiers(conn, context_params)
325
+ results = context_list.select { |e| e[:name].eql?(value) || e[:identifier].eql?(value.to_i)}
326
+
327
+ return results.empty? ? nil : results.first
328
+ end
329
+
330
+ def self.get_identifiers(conn, context_params)
331
+ @conn = conn if @conn.nil?
332
+
333
+ # we force raw output
334
+ # options = Thor::CoreExt::HashWithIndifferentAccess.new({'list' => true})
335
+
336
+ 3.downto(1) do
337
+ # get list data from one of the methods
338
+ if respond_to?(:validation_list)
339
+ response = validation_list(context_params)
340
+ else
341
+ clazz, endpoint, opts = whoami()
342
+ response = get_cached_response(clazz, endpoint, opts)
343
+ end
344
+
345
+ unless (response.nil? || response.empty?)
346
+ unless response['data'].nil?
347
+ identifiers = []
348
+ response['data'].each do |element|
349
+ # special flag to filter out data not needed here
350
+ next if element['dtk_context_hidden']
351
+
352
+ identifiers << { :name => element['display_name'], :identifier => element['id'], :shadow_entity => element['dtk_client_type'] }
353
+ end
354
+ return identifiers
355
+ end
356
+ end
357
+ unless response.nil?
358
+ break if response["status"].eql?('ok')
359
+ end
360
+ sleep(1)
361
+ end
362
+
363
+ DtkLogger.instance.warn("[WARNING] We were not able to check cached context, possible errors may occur.")
364
+ return []
365
+ end
366
+
367
+ no_tasks do
368
+
369
+ include CommonOptionDefs::Mixin
370
+
371
+ #
372
+ # Run shell command directly from main, use with CAUTION
373
+ #
374
+
375
+ def run_shell_command(line)
376
+ TOPLEVEL_BINDING.eval('self').execute_shell_command_internal(line)
377
+ end
378
+
379
+ ##
380
+ # Block that allows users to specify part of the code which is expected to run for longer duration
381
+ #
382
+ def extended_timeout
383
+ puts "Please wait, this could take a few minutes ..."
384
+ old_timeout = ::DTK::Client::Conn.get_timeout()
385
+ ::DTK::Client::Conn.set_timeout(EXTENDED_TIMEOUT)
386
+ result = yield
387
+ ::DTK::Client::Conn.set_timeout(old_timeout)
388
+ result
389
+ end
390
+
391
+ # Method not implemented error
392
+ def not_implemented
393
+ raise DTK::Client::DtkError, "Method NOT IMPLEMENTED!"
394
+ end
395
+
396
+ def raise_validation_error_method_usage(method_name)
397
+ usage_text = self.class.all_tasks[method_name][:usage]
398
+ raise DTK::Client::DtkValidationError, "Invalid method usage, use: #{usage_text}"
399
+ end
400
+
401
+ # returns method name and usage
402
+ def current_method_info
403
+ unless @_initializer[2][:current_task]
404
+ raise DTK::Client::DtkError, "You are using development mode, and you have newer version of Thor gem than specified by dtk-client"
405
+ end
406
+
407
+ return @_initializer[2][:current_task].name, @_initializer[2][:current_task].usage
408
+ end
409
+
410
+ # returns names of the arguments, after the method name
411
+ def method_argument_names
412
+ name, usage = current_method_info
413
+ results = usage.split(name.gsub(/_/,'-')).last || ""
414
+ return results.split(' ')
415
+ end
416
+
417
+ #TODO: can make more efficient by having rest call that returns name from id, rather than using 'list path'
418
+ #entity_id can be a name as well as an id
419
+ def get_name_from_id_helper(entity_id, entity_type=nil,list_command_path=nil, subtype=nil)
420
+ return entity_id unless is_numeric_id?(entity_id)
421
+
422
+ entity_id = entity_id.to_i
423
+ if entity_type.nil?
424
+ entity_type,list_command_path,subtype = self.class.whoami()
425
+ end
426
+
427
+ match = nil
428
+ response = self.class.get_cached_response(entity_type,list_command_path,subtype)
429
+ if response and response.ok? and response['data']
430
+ match = response['data'].find{|entity|entity_id == entity['id']}
431
+ end
432
+ unless match
433
+ raise DTK::Client::DtkError, "Not able to resolve entity name, please provide #{entity_type} name."
434
+ end
435
+ match['display_name']
436
+ end
437
+
438
+ def is_numeric_id?(possible_id)
439
+ !possible_id.to_s.match(/^[0-9]+$/).nil?
440
+ end
441
+
442
+ # helper for error messages; prints singular or plural version
443
+ # tem will be of form singural/plural or simple term in which case plural formed by adding 's'
444
+ def plural?(is_plural,term)
445
+ singular_plural = term.split('/')
446
+ if singular_plural.size == 1
447
+ singular_plural << "#{singular_plural[0]}s"
448
+ end
449
+ singular_plural[is_plural ? 1 : 0]
450
+ end
451
+
452
+ # removes nil values
453
+ def post_body(hash)
454
+ hash.inject(Hash.new){|h,(k,v)|v.nil? ? h : h.merge(k => v)}
455
+ end
456
+
457
+ # User input prompt
458
+ def user_input(message)
459
+ trap("INT", "SIG_IGN")
460
+ while line = Readline.readline("#{message}: ",true)
461
+ unless line.chomp.empty?
462
+ trap("INT", false)
463
+ return line
464
+ end
465
+ end
466
+ end
467
+
468
+ def get_type_and_raise_error_if_invalid(about, default_about, type_options)
469
+ about ||= default_about
470
+ raise DTK::Client::DtkError, "Not supported type '#{about}' for list for current context level. Possible type options: #{type_options.join(', ')}" unless type_options.include?(about)
471
+ return about, about[0..-2].to_sym
472
+ end
473
+
474
+ # check for delimiter, if present returns namespace and name for module/service
475
+ # returns: namespace, name
476
+ def get_namespace_and_name(input_remote_name, delimiter)
477
+ if (input_remote_name||'').include?(delimiter)
478
+ input_remote_name.split(delimiter)
479
+ # support ns/name as well as ns:name
480
+ elsif (input_remote_name||'').include?('/')
481
+ input_remote_name.split('/')
482
+ else
483
+ [nil, input_remote_name]
484
+ end
485
+ end
486
+
487
+ def get_namespace_and_name_for_component(component_full_name)
488
+ namespace, name = nil, ''
489
+
490
+ if (component_full_name||'').include?(':')
491
+ match = component_full_name.match(/(^[^:]+):{1}(.*$)/)
492
+ namespace, name = [$1,$2]
493
+
494
+ return [nil, component_full_name] if (name.include?(':') && !name.include?('::'))
495
+
496
+ # to be robust to user putting in ns::x::y which splits to ns=ns name=:x::y
497
+ name.gsub!(/^:/,'')
498
+
499
+ component_full_name = name
500
+ end
501
+
502
+ [namespace, component_full_name]
503
+ end
504
+ end
505
+
506
+
507
+ ##
508
+ # This is fix where we wanna exclude basename print when using dtk-shell.
509
+ # Thor has banner method, representing row when help is invoked. As such banner
510
+ # will print out basename first. e.g.
511
+ #
512
+ # dtk-shell assembly list [library|target] # List asssemblies in library or target
513
+ #
514
+ # Basename is derived from name of file, as such can be overriden to serve some other
515
+ # logic.
516
+ #
517
+ def self.basename
518
+ basename = super
519
+ basename = '' if basename == 'dtk-shell'
520
+ return basename
521
+ end
522
+
523
+ #
524
+ # Returns list of invisible contexts with sufix provided (if any)
525
+ #
526
+
527
+ def self.invisible_context_list(sufix = 'identifier')
528
+ self.respond_to?(:invisible_context) ? self.invisible_context.collect { |i| "#{i}-#{sufix}" } : []
529
+ end
530
+
531
+ desc "help [SUBCOMMAND]", "Describes available subcommands or one specific subcommand"
532
+ def help(*args)
533
+ puts # pretty print
534
+ not_dtk_clazz = true
535
+
536
+ if defined?(DTK::Client::Dtk)
537
+ not_dtk_clazz = !self.class.eql?(DTK::Client::Dtk)
538
+ end
539
+
540
+ # not_dtk_clazz - we don't use subcommand print in case of root DTK class
541
+ # for other classes Assembly, Node, etc. we print subcommand
542
+ # this gives us console output: dtk assembly converge ASSEMBLY-ID
543
+ #
544
+ # @@shell_execution - if we run from shell we don't want subcommand output
545
+ #
546
+
547
+ super(args.empty? ? nil : args.first, not_dtk_clazz && !@@shell_execution)
548
+
549
+ # we will print error in case configuration has reported error
550
+ @conn.print_warning if @conn.connection_error?
551
+ puts # pretty print
552
+ end
553
+ end
554
+ end
555
+ end