dtk-client 0.12.1 → 0.12.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (28) hide show
  1. checksums.yaml +4 -4
  2. data/dtk-client.gemspec +1 -1
  3. data/lib/cli/command/module/install.rb +9 -2
  4. data/lib/cli/command/options.rb +13 -1
  5. data/lib/cli/command/service.rb +1 -1
  6. data/lib/cli/command/service/add.rb +2 -1
  7. data/lib/cli/command/service/add_link.rb +2 -2
  8. data/lib/cli/command/service/delete.rb +2 -2
  9. data/lib/cli/command/service/describe.rb +2 -2
  10. data/lib/cli/command/service/exec.rb +1 -1
  11. data/lib/cli/command/service/exec_sync.rb +1 -1
  12. data/lib/cli/command/service/set_attribute.rb +101 -15
  13. data/lib/cli/command/service/set_default_target.rb +6 -3
  14. data/lib/cli/command/service/task_status.rb +1 -3
  15. data/lib/cli/command/token.rb +4 -1
  16. data/lib/cli/version.rb +1 -1
  17. data/lib/client/operation/module/install_from_catalog.rb +5 -3
  18. data/lib/client/operation/service/add.rb +19 -6
  19. data/lib/client/operation/service/add_link.rb +1 -1
  20. data/lib/client/operation/service/describe.rb +3 -4
  21. data/lib/client/operation/service/exec.rb +37 -11
  22. data/lib/client/operation/service/task_status.rb +4 -0
  23. data/lib/client/operation/service/task_status/stream_mode/element/hierarchical_task/result.rb +56 -3
  24. data/lib/client/operation/service/task_status/stream_mode/element/hierarchical_task/result/action.rb +5 -1
  25. data/lib/client/operation/service/task_status/stream_mode/element/hierarchical_task/result/components.rb +1 -0
  26. data/lib/client/operation/service/task_status/stream_mode/element/hierarchical_task/result/node_level.rb +10 -0
  27. data/lib/client/operation/service/uninstall.rb +6 -5
  28. metadata +5 -6
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: dd3532222d39184fd4e4b3dabe6089082d66997065b60710dac9315669f0bdb1
4
- data.tar.gz: 1a596bb53b1fa93c2dfc24a792836a5377fac27947acbaeea54bf069a45dce5b
3
+ metadata.gz: 42bebff35a619d489e4ad704b1782e3d6cf17c9ffa3750f4461ab0da5125efa3
4
+ data.tar.gz: 8dd31d44f79e12576c85f32535c6f62b008a78f92e58a8e79bad3795e4535143
5
5
  SHA512:
6
- metadata.gz: 6be78d166a7b90eddd422a3e39fc2f5cc12ac382a896c5bf6813bc9c85ddfbb8aaac111a6e963eb4bcad14c0d7c2ff569122d204194d8721ef17bcaf9b56a5e1
7
- data.tar.gz: 1fad3c04de2c251f96f84a3ba729e1713024d9bdb0891c6ab2b6c8bc0eb25ae3247c0aee258933c4b9ba4aa0daf72d35e026b94db5b949427a804c640cc37c17
6
+ metadata.gz: a0ea576d32dfeeb527c87dc7b863bcc8f44a6d56998994349943643dd05b570a3a62dac340b9168f77b7ab1d8912e6fc76f23a38092e6f6f4a1d85a50cf1058f
7
+ data.tar.gz: 730004649953bdf45399fd1b7a14a5f47208904fe8ea920ce4ee1fc9ee21efe1dc0069ccbb2ec2da6c98f629698cf7db10a3010af52ba4eb19a0c9b44bb29d0f
@@ -25,5 +25,5 @@ Gem::Specification.new do |spec|
25
25
  spec.add_dependency 'hirb', '0.7.3'
26
26
  spec.add_dependency 'mime-types', '~> 2.99.3'
27
27
  spec.add_dependency 'dtk-dsl', '~> 1.1.4'
28
- spec.add_dependency 'dtk-network-client', '1.0.3'
28
+ spec.add_dependency 'dtk-network-client', '1.0.4'
29
29
  end
@@ -30,6 +30,7 @@ module DTK::Client
30
30
  end
31
31
  sc.switch Token.update_deps
32
32
  sc.switch Token.update_lock
33
+ sc.switch Token.download_if_fail
33
34
 
34
35
  sc.action do |_global_options, options, args|
35
36
  module_name = args[0]
@@ -42,7 +43,8 @@ module DTK::Client
42
43
  version: options[:version],
43
44
  update_deps: options[:update_deps],
44
45
  has_directory_param: has_directory_param,
45
- update_lock_file: options['update-lock']
46
+ update_lock_file: options['update-lock'],
47
+ download_if_fail: options[:download_if_fail]
46
48
  }
47
49
  Install.execute(self, opts_hash)
48
50
  nil
@@ -59,6 +61,7 @@ module DTK::Client
59
61
  @directory_path = opts[:directory_path]
60
62
  @has_directory_param = opts[:has_directory_param]
61
63
  @update_lock_file = opts[:update_lock_file]
64
+ @download_if_fail = opts[:download_if_fail]
62
65
  end
63
66
 
64
67
  def self.execute(context, opts = {})
@@ -81,6 +84,10 @@ module DTK::Client
81
84
 
82
85
  attr_reader :context
83
86
 
87
+ def download_if_fail
88
+ @download_if_fail ||= false
89
+ end
90
+
84
91
  def module_ref
85
92
  @module_ref ||= ret_module_ref
86
93
  end
@@ -104,7 +111,7 @@ module DTK::Client
104
111
  # server (the later step Operation::Module.install does this)
105
112
 
106
113
  # TODO: 3070: handle sitution where response is not ok
107
- install_response = Operation::Module.install_from_catalog(module_ref: self.module_ref, version: self.version, directory_path: self.directory_path?)
114
+ install_response = Operation::Module.install_from_catalog(module_ref: self.module_ref, version: self.version, directory_path: self.directory_path?, download_if_fail: self.download_if_fail)
108
115
 
109
116
  if client_installed_modules = (install_response && install_response.data[:installed_modules])
110
117
  opts_server_install = {
@@ -27,8 +27,20 @@ module DTK::Client
27
27
 
28
28
  def [](canonical_name_or_opt)
29
29
  key = Token.opt?(canonical_name_or_opt) || canonical_name_or_opt
30
- @opts_hash[key]
30
+ # TODO: check why this switch to below was needed was needed
31
+ #@opts_hash[key]
32
+ case key
33
+ when ::String, ::Symbol
34
+ @opts_hash[key]
35
+ when ::Array
36
+ if matching_key = key.find { |k| @opts_hash.has_key?(k) }
37
+ @opts_hash[matching_key]
38
+ end
39
+ else
40
+ raise Eroor, "Unexpected value of key.class: #{key.class}"
41
+ end
31
42
  end
43
+
32
44
  end
33
45
  end
34
46
  end
@@ -53,7 +53,7 @@ module DTK::Client
53
53
  'task-status',
54
54
  'uninstall',
55
55
  'describe',
56
- # 'add',
56
+ 'add',
57
57
  'add-component'
58
58
  ]
59
59
 
@@ -26,7 +26,8 @@ module DTK::Client; module CLI
26
26
  args = {
27
27
  service_instance: service_instance_in_options_or_context(options),
28
28
  path: options[:path],
29
- relative_path: options[:relative_path]
29
+ relative_path: options[:relative_path],
30
+ service_instance_dir: @base_dsl_file_obj.parent_dir
30
31
  }
31
32
  Operation::Service.add(args)
32
33
  end
@@ -44,10 +44,10 @@ module DTK::Client; module CLI
44
44
  :service_instance_dir => service_instance_dir
45
45
  }
46
46
 
47
- Operation::Service.link(args)
47
+ Operation::Service.add_link(args)
48
48
  end
49
49
  end
50
50
  end
51
51
  end
52
52
  end
53
- end; end
53
+ end; end
@@ -22,7 +22,7 @@ module DTK::Client; module CLI
22
22
  command_body c, :delete, 'Destroys the running infrastructure associated with the service instance' do |sc|
23
23
  sc.flag Token.delete_service_name
24
24
  sc.flag Token.directory_path, :desc => 'Absolute or relative path to service instance directory associated; not needed if executed in service instance directory'
25
- # sc.flag Token.path, :desc => "Delete specific part of service instance. Supported paths are 'dependencies/[name]', 'components/[name]', 'actions/[name]'"
25
+ sc.flag Token.path, :desc => "Delete specific part of service instance. Supported paths are 'dependencies/[name]', 'components/[name]', 'actions/[name]'"
26
26
 
27
27
  sc.switch Token.skip_prompt, :desc => 'Skip prompt that checks if user wants to delete the service instance'
28
28
  sc.switch Token.recursive, :desc => 'Delete all service instances staged into specified target'
@@ -40,7 +40,7 @@ module DTK::Client; module CLI
40
40
  :skip_prompt => options[:skip_prompt],
41
41
  :recursive => recursive,
42
42
  :force => force,
43
- # :path => options[:path]
43
+ :path => options[:path]
44
44
  }
45
45
  args[:directory_path] = directory_path || base_dsl_file_obj.parent_dir unless name
46
46
  Operation::Service.delete(args)
@@ -19,13 +19,13 @@ module DTK::Client; module CLI
19
19
  module Command
20
20
  module Service
21
21
  subcommand_def 'describe' do |c|
22
+ c.arg Token::Arg.path
22
23
  command_body c, 'describe', 'Describe service instance content' do |sc|
23
- sc.flag Token.path, :desc => "supported paths are 'dependencies', 'components/[name]', 'actions/[name]' "
24
24
  sc.switch Token.show_steps, :desc => 'Show steps that will be executed when action is executed'
25
25
  sc.action do |_global_options, options, _args|
26
26
  args = {
27
27
  service_instance: service_instance_in_options_or_context(options),
28
- path: options[:path],
28
+ path: _args[0],
29
29
  show_steps: options['show-steps']
30
30
  }
31
31
  Operation::Service.describe(args)
@@ -21,7 +21,7 @@ module DTK::Client
21
21
  subcommand_def 'exec' do |c|
22
22
  c.arg Token::Arg.action
23
23
  c.arg Token::Arg.action_params, :optional => true
24
- command_body c, :exec, 'Execute action asynchronously' do |sc|
24
+ command_body c, :exec, 'Execute action asynchronously. Parameters need to be within quotes (\'\' or "").' do |sc|
25
25
  sc.flag Token.directory_path, :desc => 'Absolute or relative path to service instance directory containing updates to pull; not need if in the service instance directory'
26
26
  sc.flag Token.attempts, :desc => "Number of attempts"
27
27
  sc.flag Token.sleep, :desc => "Number of sleep in seconds"
@@ -21,7 +21,7 @@ module DTK::Client
21
21
  subcommand_def 'exec-sync' do |c|
22
22
  c.arg Token::Arg.action
23
23
  c.arg Token::Arg.action_params, :optional => true
24
- command_body c, 'exec-sync', 'Execute action synchronously' do |sc|
24
+ command_body c, 'exec-sync', 'Execute action synchronously. Parameters need to be within quotes (\'\' or "").' do |sc|
25
25
  sc.flag Token.directory_path, :desc => 'Absolute or relative path to service instance directory containing updates to pull; not need if in the service instance directory'
26
26
  sc.flag Token.attempts, :desc => "Number of attempts"
27
27
  sc.flag Token.sleep, :desc => "Number of sleep in seconds"
@@ -19,31 +19,117 @@ module DTK::Client; module CLI
19
19
  module Command
20
20
  module Service
21
21
  subcommand_def 'set-attribute' do |c|
22
- c.arg Token::Arg.attribute_name
22
+ c.arg Token::Arg.attribute_name, :optional => true
23
23
  c.arg Token::Arg.attribute_value, :optional => true
24
- command_body c, 'set-attribute', 'Edit specific attribute.' do |sc|
24
+ command_body c, 'set-attribute', 'Set attribute value(s).' do |sc|
25
25
  sc.switch Token.u
26
26
  sc.flag Token.directory_path
27
- sc.action do |_global_options, options, _args|
27
+ sc.flag Token.param_file
28
+ sc.action do |_global_options, options, args|
29
+ service_instance = service_instance_in_options_or_context(options)
30
+ service_instance_dir = options[:d] || @base_dsl_file_obj.parent_dir
28
31
 
29
- service_instance = service_instance_in_options_or_context(options)
32
+ helper = SetAttributeHelper.new(service_instance, service_instance_dir)
30
33
 
31
- attribute_name = _args[0]
32
- options[:u] ? attribute_value = nil : attribute_value = _args[1]
34
+ attribute_name = args[0]
35
+ # if attribute_name not given then expected that a YAML file is given that has attribute values
36
+ unless attribute_name
37
+ name_value_pairs = helper.get_name_value_pairs_from_yaml_file(options)
38
+ # TODO: should extend the server-side to take list of attributes to set; hack to return a value ret
39
+ name_value_pairs.each_pair do |attribute_name, attribute_value|
40
+ begin
41
+ helper.set_single_attribute(attribute_name, attribute_value)
42
+ rescue Error
43
+ raise Error::Usage, "Error trying to set attribute '#{attribute_name}' from YAML file"
44
+ end
45
+ end
46
+ nil
47
+ else
48
+ attribute_value =
49
+ unless options[:u]
50
+ if ruby_obj_value = helper.get_yaml_from_file?(options)
51
+ ::JSON.generate(ruby_obj_value)
52
+ else
53
+ args[1] || raise(Error::Usage, "Either argument VALUE or -f option must be given to specify a value")
54
+ end
55
+ end
56
+ helper.set_single_attribute(attribute_name, attribute_value)
57
+ end
58
+ end
59
+ end
60
+ end
33
61
 
34
- options[:d].nil? ? service_instance_dir = @base_dsl_file_obj.parent_dir : service_instance_dir = options[:d]
62
+ class SetAttributeHelper
63
+ def initialize(service_instance, service_instance_dir)
64
+ @service_instance = service_instance
65
+ @service_instance_dir = service_instance_dir
66
+ end
35
67
 
36
- args = {
37
- :attribute_name => attribute_name,
38
- :service_instance => service_instance,
39
- :attribute_value => attribute_value,
40
- :service_instance_dir => @base_dsl_file_obj.parent_dir
41
- }
68
+ def set_single_attribute(attribute_name, attribute_value)
69
+ Operation::Service.set_attribute(
70
+ :attribute_name => attribute_name,
71
+ :attribute_value => attribute_value,
72
+ :service_instance => self.service_instance,
73
+ :service_instance_dir => self.service_instance_dir
74
+ )
75
+ end
42
76
 
43
- Operation::Service.set_attribute(args)
77
+ def get_yaml_from_file?(cli_options)
78
+ if param_file_path = cli_options[:f]
79
+ param_file = check_and_return_file_content(param_file_path)
80
+ yaml_to_ruby_obj(param_file, param_file_path)
44
81
  end
45
82
  end
83
+
84
+ def get_name_value_pairs_from_yaml_file(options)
85
+ unless ruby_obj = get_yaml_from_file?(options)
86
+ raise Error::Usage, "If NAME argument is not given then -f option must be given to specify a value"
87
+ end
88
+
89
+ # check that gile is form
90
+ #attributes:
91
+ # name1: val1
92
+ # ...
93
+ unless ruby_obj.kind_of?(::Hash) and ruby_obj.size == 1 and ruby_obj.keys.first == 'attributes'
94
+ raise Error::Usage, "If NAME argument is not given, the parameter file content must be YAML hash starting with key 'attributes'"
95
+ end
96
+ name_value_pairs = ruby_obj['attributes']
97
+ unless name_value_pairs.kind_of?(::Hash)
98
+ raise Error::Usage, "If NAME argument is not given, the parameter file content must be YAML hash with name/attribute values"
99
+ end
100
+ name_value_pairs.inject({}) do |h, (k, v)|
101
+ value =
102
+ case v
103
+ when ::Hash, ::Array
104
+ ::JSON.generate(v)
105
+ else
106
+ v
107
+ end
108
+ h.merge(k => value)
109
+ end
110
+ end
111
+
112
+ private
113
+
114
+ def check_and_return_file_content(path)
115
+ raise Error::Usage, "The file at path '#{path}' does not exist" unless File.file?(path)
116
+ File.open(path).read
117
+ end
118
+
119
+ def yaml_to_ruby_obj(text, path)
120
+ begin
121
+ ::YAML.load(text)
122
+ rescue
123
+ raise Error::Usage, "Content in file '#{path}' is ill-formed YAML"
124
+ end
125
+ end
126
+
127
+ protected
128
+
129
+ attr_reader :service_instance, :service_instance_dir
130
+
46
131
  end
132
+
47
133
  end
48
134
  end
49
- end; end
135
+ end; end
@@ -19,10 +19,13 @@ module DTK::Client
19
19
  module CLI::Command
20
20
  module Service
21
21
  subcommand_def 'set-default-target' do |c|
22
- c.arg Token::Arg.service_name
22
+ c.arg Token::Arg.service_name, optional: true
23
23
  command_body c, 'set-default-context', 'Create a new service instance to refer to staged infrastructure that then can be deployed' do |sc|
24
- sc.action do |_global_options, options, args|
25
- Operation::Service.set_default_target(:service_instance => args[0])
24
+ sc.flag Token.directory_path, :desc => 'Absolute or relative path to service instance directory containing updates to pull; not need if in the service instance directory'
25
+ sc.action do |_global_options, options, args|
26
+ service_instance = args[0]
27
+ service_instance ||= service_instance_in_options_or_context(options)
28
+ Operation::Service.set_default_target(:service_instance => service_instance)
26
29
  end
27
30
  end
28
31
  end
@@ -21,11 +21,9 @@ module DTK::Client; module CLI
21
21
  subcommand_def 'task-status' do |c|
22
22
  command_body c, 'task-status', "Get task status of the running or last running service instance task." do |sc|
23
23
  sc.flag Token.directory_path, :desc => 'Absolute or relative path to service instance directory; not needed if executed in the service instance directory'
24
- sc.flag Token.mode, :desc => 'Task status mode.'
25
-
24
+ sc.flag Token.mode, :desc => 'Task status mode (snapshot, refresh, stream).'
26
25
  sc.action do |_global_options, options, _args|
27
26
  service_instance = service_instance_in_options_or_context(options)
28
-
29
27
  args = {
30
28
  :service_instance => service_instance,
31
29
  :mode => options[:mode]
@@ -37,6 +37,7 @@ module DTK::Client
37
37
  :comma_seperated_contexts => Flag.new(:context, 'CONTEXT1,CONTEXT2,...' ,'Comma-seperated context service instance(s); if not specified, the default service instance serves as the context'),
38
38
  :module_ref => Flag.new(:m, ModuleRef::NamespaceModuleName.legal_form, 'Module name with namespace; not needed if command is executed from within the module directory'),
39
39
  :relative_path => Flag.new(:f, 'RELATIVE-FILE-PATH', 'Relative file path'),
40
+ :param_file => Flag.new(:f, 'PARAM-FILE', 'YAML file with command argument value'),
40
41
  :service_instance => Flag.new(:s, 'SERVICE-INSTANCE', 'Service instance name'),
41
42
  :service_name => Flag.new(:n, 'SERVICE-NAME', 'Service name'),
42
43
  :version => Flag.new(:v, 'VERSION', 'Version'),
@@ -76,7 +77,8 @@ module DTK::Client
76
77
  :update_deps => Switch.new('update-deps', "Skip prompt and update all dependencies or skip prompt and don't update all dependencies (on master)", :negatable => true, :default_value => 'prompt'),
77
78
  :skip_server => Switch.new('skip-server', 'Do not install module on server'),
78
79
  :update_lock => Switch.new([:u, 'update-lock'], 'Update lock file with new dependencies'),
79
- :show_steps => Switch.new('show-steps', 'Show steps')
80
+ :show_steps => Switch.new('show-steps', 'Show steps'),
81
+ :download_if_fail => Switch.new('download-if-fail', 'Download module content from dtkn if there is a runtime error.')
80
82
  }
81
83
 
82
84
  ARG_TOKENS = {
@@ -102,6 +104,7 @@ module DTK::Client
102
104
  :user => 'USER',
103
105
  :permissions => 'PERMISSIONS',
104
106
  :group => 'GROUP',
107
+ :path => 'PATH',
105
108
  }
106
109
 
107
110
  end
@@ -18,7 +18,7 @@
18
18
  module DTK
19
19
  module Client
20
20
  module CLI
21
- VERSION="0.12.1"
21
+ VERSION="0.12.2"
22
22
  end
23
23
  end
24
24
  end
@@ -19,7 +19,7 @@ module DTK::Client
19
19
  class Operation::Module
20
20
  class InstallFromCatalog < self
21
21
  attr_reader :version, :module_ref, :target_repo_dir
22
- def initialize(catalog, module_ref, directory_path, version, remote_module_info, type)
22
+ def initialize(catalog, module_ref, directory_path, version, remote_module_info, type, download_if_fail)
23
23
  @type = type
24
24
  @catalog = catalog
25
25
  @module_ref = module_ref
@@ -27,6 +27,7 @@ module DTK::Client
27
27
  @target_repo_dir = OsUtil.current_dir unless type == :dependency #ClientModuleDir.create_module_dir_from_path(directory_path || OsUtil.current_dir)
28
28
  @version = version # if nil wil be dynamically updated along with version attribute of @module_ref
29
29
  @remote_module_info = remote_module_info
30
+ @download_if_fail = download_if_fail
30
31
  end
31
32
  private :initialize
32
33
 
@@ -37,8 +38,9 @@ module DTK::Client
37
38
  directory_path = args[:directory_path]
38
39
  remote_module_info = args[:remote_module_info]
39
40
  type = args[:type]
41
+ download_if_fail = args[:download_if_fail]
40
42
  # will create different classes for different catalog types when we add support for them
41
- new('dtkn', module_ref, directory_path, version, remote_module_info, type).install_from_catalog
43
+ new('dtkn', module_ref, directory_path, version, remote_module_info, type, download_if_fail).install_from_catalog
42
44
  end
43
45
  end
44
46
 
@@ -50,7 +52,7 @@ module DTK::Client
50
52
  explicit_path: @directory_path,
51
53
  repo_dir: @directory_path || @target_repo_dir
52
54
  }
53
- installed_modules = DtkNetworkClient::Install.run(module_info, type: @type)
55
+ installed_modules = DtkNetworkClient::Install.run(module_info, type: @type, download_if_fail: @download_if_fail )
54
56
 
55
57
  { :installed_modules => installed_modules }
56
58
  end
@@ -20,14 +20,27 @@ module DTK::Client
20
20
  class Add < self
21
21
  def self.execute(args = Args.new)
22
22
  wrap_operation(args) do |args|
23
- service_instance = args.required(:service_instance)
24
- path = args.required(:path)
25
- relative_path = args.required(:relative_path)
26
-
27
- content = yaml_to_hash(FileHelper.get_content?(relative_path))
23
+ service_instance = args.required(:service_instance)
24
+ path = args.required(:path)
25
+ relative_path = args.required(:relative_path)
26
+ service_instance_dir = args[:service_instance_dir]
27
+ content = FileHelper.get_content?(relative_path)
28
28
 
29
29
  query_string_hash = QueryStringHash.new(:service_instance => service_instance, path: path, content: content)
30
- rest_post "#{BaseRoute}/add_by_path", query_string_hash
30
+ response = rest_post "#{BaseRoute}/add_by_path", query_string_hash
31
+
32
+ if path.include?('actions/')
33
+ repo_info_args = Args.new(
34
+ :service_instance => service_instance,
35
+ :commit_message => "Updating changes to service instance '#{service_instance}'",
36
+ :branch => response.required(:base_module, :branch, :name),
37
+ :repo_url => response.required(:base_module, :repo, :url),
38
+ :service_instance_dir => service_instance_dir
39
+ )
40
+ ClientModuleDir::GitRepo.pull_from_service_repo(repo_info_args)
41
+ end
42
+
43
+ response
31
44
  end
32
45
  end
33
46
  end
@@ -17,7 +17,7 @@
17
17
  #
18
18
  module DTK::Client
19
19
  class Operation::Service
20
- class Link < self
20
+ class AddLink < self
21
21
  def self.execute(args = Args.new)
22
22
  wrap_operation(args) do |args|
23
23
  service_instance = args.required(:service_instance)
@@ -25,20 +25,19 @@ module DTK::Client
25
25
  show_steps = args[:show_steps]
26
26
  query_string_hash = QueryStringHash.new
27
27
 
28
- raise Error, 'Option --show-steps can only be used with actions path' if show_steps && !actions_path_valid?(path)
28
+ error_msg = 'Option --show-steps can only be used with actions path. The pattern must have form actions/ACTION-NAME.'
29
+ raise Error, error_msg if show_steps && !actions_path_valid?(path)
29
30
 
30
31
  query_string_hash.merge!(path: path) if path
31
32
  query_string_hash.merge!(show_steps: show_steps) if show_steps
32
33
  response = rest_get "#{BaseRoute}/#{service_instance}/describe", query_string_hash
33
-
34
34
  response.set_render_as_table! if show_steps
35
35
  response
36
36
  end
37
37
  end
38
-
39
38
  def self.actions_path_valid?(path)
40
39
  prefix, suffix = (path||'').split('/')
41
- prefix.eql? 'actions'
40
+ prefix.eql?('actions') && suffix && !suffix.empty?
42
41
  end
43
42
 
44
43
  end
@@ -26,9 +26,10 @@ module DTK::Client
26
26
  directory_path = args[:directory_path]
27
27
  breakpoint = args[:breakpoint]
28
28
  attempts = args[:attempts] || ""
29
- sleep = args[:sleep] || ""
30
- # parse params and return format { 'p_name1' => 'p_value1' , 'p_name2' => 'p_value2' }
31
- task_params = parse_params?(action_params)||{}
29
+ sleep = args[:sleep] || ""
30
+
31
+ task_params = {}
32
+ process_action_params(action_params, task_params) unless action_params.eql? nil
32
33
 
33
34
  # this is temporary fix to handle new node as component format ec2::node[node_name]/action
34
35
  # will transform ec2::node[node_name]/action to node_name/action
@@ -80,17 +81,42 @@ module DTK::Client
80
81
 
81
82
  private
82
83
 
83
- def self.parse_params?(params_string)
84
- if params_string
85
- params_string.split(',').inject(Hash.new) do |h,av|
86
- av_split = av.split('=')
87
- unless av_split.size == 2
88
- raise Error::Usage, "The parameter string (#{params_string}) is ill-formed"
89
- end
90
- h.merge(av_split[0] => av_split[1])
84
+ def self.process_action_params(action_params, params)
85
+ if match = action_params.match(/([^=]+)=(.+)/)
86
+ extracted_value, new_params = extract_action_value(match[2])
87
+ params.merge!(match[1] => extracted_value)
88
+ process_action_params(new_params, params) unless new_params.empty?
89
+ end
90
+ end
91
+
92
+ def self.extract_action_value(ac_params)
93
+ if ac_params.start_with?('[')
94
+ match_and_return(ac_params, :array)
95
+ elsif ac_params.start_with?('{')
96
+ match_and_return(ac_params, :hash)
97
+ else
98
+ if match = ac_params.match(/([^,]+),(.*)/)
99
+ return [match[1], match[2]]
100
+ else
101
+ return [ac_params, ""]
91
102
  end
92
103
  end
93
104
  end
105
+
106
+ def self.match_and_return(ac_params, type)
107
+ match = ac_params.match(RegexTypes[type])
108
+ return [match[1], prettify(match[2])]
109
+ end
110
+
111
+ RegexTypes = {
112
+ array: Regexp.new(/([^\]]+\])(.*)/),
113
+ hash: Regexp.new(/([^\}]+\})(.*)/)
114
+ }
115
+
116
+ def self.prettify(remaining)
117
+ remaining.sub!(",", "") unless remaining.empty?
118
+ remaining
119
+ end
94
120
  end
95
121
  end
96
122
  end
@@ -59,7 +59,11 @@ module DTK::Client
59
59
  when :snapshot
60
60
  SnapshotMode.new(mode, service_instance).task_status(opts)
61
61
  when :stream
62
+ begin
62
63
  StreamMode.new(mode, service_instance).get_and_render(opts)
64
+ rescue Interrupt => e
65
+ puts "Exiting ..."
66
+ end
63
67
  else
64
68
  raise Error::Usage.new("Illegal mode '#{mode}'; legal modes are: #{LEGAL_MODES.join(', ')}")
65
69
  end
@@ -26,6 +26,7 @@ module DTK::Client; class Operation::Service::TaskStatus::StreamMode::Element
26
26
  super
27
27
  @errors = hash['errors'] || []
28
28
  @info = hash['info'] || []
29
+ @action_results = hash['action_results'] || []
29
30
  end
30
31
 
31
32
  # This can be over-written
@@ -44,9 +45,12 @@ module DTK::Client; class Operation::Service::TaskStatus::StreamMode::Element
44
45
 
45
46
  attr_reader :errors
46
47
  attr_reader :info
48
+ attr_reader :action_results
47
49
 
48
50
  def render_errors(results_per_node)
49
- return unless results_per_node.find { |result| not result.errors.empty?}
51
+ return unless results_per_node.find do |result|
52
+ not result.errors.empty?
53
+ end
50
54
  first_time = true
51
55
  results_per_node.each do |result|
52
56
  if first_time
@@ -72,6 +76,20 @@ module DTK::Client; class Operation::Service::TaskStatus::StreamMode::Element
72
76
  end
73
77
  end
74
78
 
79
+ def render_output(results_per_node)
80
+ return unless results_per_node.find do |result|
81
+ not result.action_results.empty?
82
+ end
83
+ first_time = true
84
+ results_per_node.each do |result|
85
+ if first_time
86
+ render_line 'OUTPUT:'
87
+ first_time = false
88
+ end
89
+ result.render_node_output
90
+ end
91
+ end
92
+
75
93
  def render_node_errors
76
94
  return if @errors.empty?
77
95
  render_node_term
@@ -94,6 +112,34 @@ module DTK::Client; class Operation::Service::TaskStatus::StreamMode::Element
94
112
  end
95
113
  end
96
114
 
115
+ def render_node_output
116
+ return if @action_results.empty?
117
+ @action_results.each do |output|
118
+ if dynamic_attrs = output['dynamic_attributes']
119
+ render_dynamic_attrs(dynamic_attrs)
120
+ end
121
+ end
122
+ end
123
+
124
+ def render_dynamic_attrs(dynamic_attrs)
125
+ dynamic_attrs.each do |name, opts|
126
+ next unless opts['value']
127
+ out = opts['value']
128
+
129
+ if disp_form = opts['display_format']
130
+ out =
131
+ case disp_form
132
+ when 'yaml'
133
+ out.to_yaml
134
+ when 'json'
135
+ out.to_json
136
+ end
137
+ end
138
+ render_output_line(name + ':', out)
139
+ render_empty_line
140
+ end
141
+ end
142
+
97
143
  def render_error_line(line, opts = {})
98
144
  render_line(line, ErrorRenderOpts.merge(opts))
99
145
  end
@@ -101,8 +147,15 @@ module DTK::Client; class Operation::Service::TaskStatus::StreamMode::Element
101
147
  def render_info_line(line, opts = {})
102
148
  render_line(line)
103
149
  end
104
- ErrorRenderOpts = { :tabs => 1}
105
-
150
+
151
+ def render_output_line(attr_name, attr_value)
152
+ render_line attr_name, RenderAttrNameOpts
153
+ render_line attr_value, RenderAttrValOpts
154
+ end
155
+ ErrorRenderOpts = { :tabs => 1 }
156
+ RenderAttrNameOpts = { :tabs => 1 }
157
+ RenderAttrValOpts = { :tabs => 2 }
158
+
106
159
  end
107
160
  end
108
161
  end; end
@@ -26,12 +26,12 @@ module DTK::Client; class Operation::Service::TaskStatus::StreamMode::Element::H
26
26
  attr_reader :action_results
27
27
 
28
28
  def render_results(results_per_node)
29
+ render_info(results_per_node)
29
30
  if any_results?(results_per_node)
30
31
  render_line 'RESULTS:'
31
32
  render_empty_line
32
33
  results_per_node.each { |result| result.render }
33
34
  else
34
- render_info(results_per_node)
35
35
  render_errors(results_per_node)
36
36
  end
37
37
  end
@@ -72,6 +72,10 @@ module DTK::Client; class Operation::Service::TaskStatus::StreamMode::Element::H
72
72
  render_line 'STDERR:'
73
73
  render_action_output stderr
74
74
  end
75
+ if dynamic_attrs = action_result['dynamic_attributes']
76
+ render_line 'OUTPUT:'
77
+ render_dynamic_attrs(dynamic_attrs)
78
+ end
75
79
  end
76
80
 
77
81
  def render_action_output(line)
@@ -21,6 +21,7 @@ module DTK::Client; class Operation::Service::TaskStatus::StreamMode::Element::H
21
21
  def render_results(results_per_node)
22
22
  render_errors(results_per_node)
23
23
  render_info(results_per_node)
24
+ render_output(results_per_node)
24
25
  end
25
26
  end
26
27
  end
@@ -18,6 +18,16 @@
18
18
  module DTK::Client; class Operation::Service::TaskStatus::StreamMode::Element::HierarchicalTask
19
19
  class Results
20
20
  class NodeLevel < self
21
+ def render
22
+ not_first_time = nil
23
+ render_node_term
24
+ @action_results.each do |action_result|
25
+ render_action_result_lines(action_result, :first_time => not_first_time.nil?)
26
+ not_first_time ||= true
27
+ end
28
+ render_empty_line
29
+ end
30
+
21
31
  def render_results(results_per_node)
22
32
  render_errors(results_per_node)
23
33
  end
@@ -51,14 +51,15 @@ module DTK::Client
51
51
  :force => force
52
52
  )
53
53
  response = rest_post("#{BaseRoute}/uninstall", post_body)
54
-
54
+ path = ClientModuleDir.ret_base_path(:service, service_instance) unless path
55
55
  ClientModuleDir.rm_f(path) if args[:purge]
56
-
57
56
  if message = response.data(:message) || "DTK service '#{service_instance}' has been uninstalled successfully."
58
- Dir.glob("*", File::FNM_DOTMATCH).each do |f|
59
- ClientModuleDir.rm_f(f) if f.include? '.task_id_'
57
+ if Dir.exists?(path) && !args[:purge]
58
+ Dir.entries(path).each do |f|
59
+ ClientModuleDir.rm_f("#{path}/#{f}") if f.include? '.task_id_'
60
+ end
61
+ ClientModuleDir.create_file_with_content("#{path}/.task_id_#{response.data(:task_id)}", '') if response.data(:task_id)
60
62
  end
61
- ClientModuleDir.create_file_with_content(".task_id_#{response.data(:task_id)}", '') if response.data(:task_id)
62
63
  OsUtil.print_info(message)
63
64
  end
64
65
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dtk-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.1
4
+ version: 0.12.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Reactor8
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2018-11-18 00:00:00.000000000 Z
11
+ date: 2019-02-08 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: dtk-common-core
@@ -128,14 +128,14 @@ dependencies:
128
128
  requirements:
129
129
  - - '='
130
130
  - !ruby/object:Gem::Version
131
- version: 1.0.3
131
+ version: 1.0.4
132
132
  type: :runtime
133
133
  prerelease: false
134
134
  version_requirements: !ruby/object:Gem::Requirement
135
135
  requirements:
136
136
  - - '='
137
137
  - !ruby/object:Gem::Version
138
- version: 1.0.3
138
+ version: 1.0.4
139
139
  description: Command line tool to interact with a DTK Server and DTK Service Catalog.
140
140
  email: support@reactor8.com
141
141
  executables:
@@ -429,8 +429,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
429
429
  - !ruby/object:Gem::Version
430
430
  version: '0'
431
431
  requirements: []
432
- rubyforge_project:
433
- rubygems_version: 2.7.8
432
+ rubygems_version: 3.0.2
434
433
  signing_key:
435
434
  specification_version: 4
436
435
  summary: DTK CLI client for DTK server interaction.