dtk-client 0.7.4.1 → 0.7.5

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. checksums.yaml +4 -4
  2. data/bin/dtk +10 -3
  3. data/bin/dtk-shell +1 -1
  4. data/lib/command_helpers/git_repo.rb +26 -20
  5. data/lib/command_helpers/jenkins_client.rb +4 -3
  6. data/lib/command_helpers/service_importer.rb +37 -25
  7. data/lib/commands.rb +2 -2
  8. data/lib/commands/common/thor/assembly_workspace.rb +185 -173
  9. data/lib/commands/common/thor/base_command_helper.rb +42 -0
  10. data/lib/commands/common/thor/clone.rb +1 -1
  11. data/lib/commands/common/thor/module.rb +37 -58
  12. data/lib/commands/common/thor/module/import.rb +1 -1
  13. data/lib/commands/common/thor/pull_from_remote.rb +7 -12
  14. data/lib/commands/common/thor/purge_clone.rb +1 -1
  15. data/lib/commands/common/thor/push_clone_changes.rb +3 -1
  16. data/lib/commands/common/thor/task_status.rb +52 -75
  17. data/lib/commands/common/thor/task_status/refresh_mode.rb +56 -0
  18. data/lib/commands/common/thor/task_status/snapshot_mode.rb +11 -0
  19. data/lib/commands/common/thor/task_status/stream_mode.rb +31 -0
  20. data/lib/commands/common/thor/task_status/stream_mode/element.rb +90 -0
  21. data/lib/commands/common/thor/task_status/stream_mode/element/no_results.rb +10 -0
  22. data/lib/commands/common/thor/task_status/stream_mode/element/render.rb +88 -0
  23. data/lib/commands/common/thor/task_status/stream_mode/element/stage.rb +13 -0
  24. data/lib/commands/common/thor/task_status/stream_mode/element/task_end.rb +10 -0
  25. data/lib/commands/common/thor/task_status/stream_mode/element/task_start.rb +10 -0
  26. data/lib/commands/thor/account.rb +10 -8
  27. data/lib/commands/thor/assembly.rb +9 -2
  28. data/lib/commands/thor/component_module.rb +0 -52
  29. data/lib/commands/thor/library.rb +1 -0
  30. data/lib/commands/thor/node.rb +1 -36
  31. data/lib/commands/thor/node_template.rb +4 -47
  32. data/lib/commands/thor/service.rb +57 -46
  33. data/lib/commands/thor/service_module.rb +2 -49
  34. data/lib/commands/thor/target.rb +7 -7
  35. data/lib/commands/thor/workspace.rb +44 -27
  36. data/lib/context_router.rb +4 -0
  37. data/lib/core.rb +71 -99
  38. data/lib/domain/response.rb +9 -0
  39. data/lib/domain/response/error_handler.rb +61 -0
  40. data/lib/dtk-client/version.rb +1 -1
  41. data/lib/dtk_client.rb +14 -0
  42. data/lib/dtk_error.rb +91 -0
  43. data/lib/error.rb +3 -9
  44. data/lib/execute/cli_pure/cli_rerouter.rb +82 -0
  45. data/lib/parser/adapters/thor.rb +3 -0
  46. data/lib/shell.rb +2 -1
  47. data/lib/shell/domain/context_params.rb +2 -0
  48. data/lib/util/console.rb +1 -1
  49. data/lib/util/os_util.rb +1 -0
  50. data/lib/util/remote_dependency_util.rb +20 -3
  51. data/lib/view_processor/table_print.rb +7 -25
  52. metadata +17 -5
  53. data/lib/commands/common/thor/test_action_agent.rb +0 -39
  54. data/lib/commands/thor/repo.rb +0 -35
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6a7875186712bb106f9fbbf11458c702e66041ae
4
- data.tar.gz: 23996874f5fb73b451fab137e6157252addb9224
3
+ metadata.gz: 90044fb0aeac2ab716a8bbad16eab81ba09b177f
4
+ data.tar.gz: af29727daadaa126bc8b3ea39e8515b49e8ace37
5
5
  SHA512:
6
- metadata.gz: 6b938efca3539c8ba284580cfc4f747c179e45939abe5025f479c696fa227618c4c98731e8b493f77834ed396c31b63f81230b3e324b8618be907da363efd3ea
7
- data.tar.gz: f428e0857dbea981c780d12bc8e4fb7cf31d2a249282302d29d358917364a52230426c9dcf6802e85289b9859c7cf15f223b194a7a7a6dbb2a21b2aacf0292f8
6
+ metadata.gz: 4d40f5739a3921835fb2c38cad3428134f807091327c50eb40c7066cc1fa8168dc1e8c60da2e26fd9c7ceb6c6940f6d81e74001717a6bd0c0f5335bb3abbf7ae
7
+ data.tar.gz: e2ab0c0121f8034da2262595f5f7caabe81d6cd85a37a72b7fe6983d480ea89503779e8bb2aef0e5efc4d8d512d8088929db0ec4f634a33a920444e0f2c77326
data/bin/dtk CHANGED
@@ -15,9 +15,11 @@ require File.expand_path('../lib/shell/domain/shadow_entity', File.dirname(__FIL
15
15
  require File.expand_path('../lib/commands/thor/account', File.dirname(__FILE__))
16
16
  require File.expand_path('../lib/shell/parse_monkey_patch', File.dirname(__FILE__))
17
17
  require File.expand_path('../lib/shell/help_monkey_patch', File.dirname(__FILE__))
18
+ require File.expand_path('../lib/execute/cli_pure/cli_rerouter', File.dirname(__FILE__))
18
19
 
19
20
 
20
21
  require 'shellwords'
22
+ require 'json'
21
23
 
22
24
  $: << "/usr/lib/ruby/1.8/" #TODO: put in to get around path problem in rvm 1.9.2 environment
23
25
 
@@ -47,9 +49,14 @@ end
47
49
  context = DTK::Shell::Context.new(true)
48
50
 
49
51
  begin
50
- entity_name, method_name, context_params, thor_options = context.get_dtk_command_parameters(entity_name, args)
51
-
52
- top_level_execute(entity_name, method_name, context_params, thor_options, false)
52
+ if ::DTK::CLIRerouter.is_candidate?(entity_name, args)
53
+ response_obj = ::DTK::CLIRerouter.new(entity_name, args).run()
54
+ puts response_obj.to_json
55
+ else
56
+ # default execution
57
+ entity_name, method_name, context_params, thor_options = context.get_dtk_command_parameters(entity_name, args)
58
+ top_level_execute(entity_name, method_name, context_params, thor_options, false)
59
+ end
53
60
  rescue DTK::Client::DtkError => e
54
61
  DtkLogger.instance.error(e.message, true)
55
62
  rescue Exception => e
data/bin/dtk-shell CHANGED
@@ -11,4 +11,4 @@ config_exists = ::DTK::Client::Configurator.check_config_exists
11
11
  # check if .add_direct_access file exists, if not then add direct access and create .add_direct_access file
12
12
  resolve_direct_access(::DTK::Client::Configurator.check_direct_access, config_exists)
13
13
 
14
- run_shell_command()
14
+ run_shell_command()
@@ -118,6 +118,7 @@ module DTK; module Client; class CommandHelper
118
118
  full_module_name = full_module_name(module_name,opts)
119
119
  repo_dir = local_repo_dir(type,full_module_name,opts[:version],opts)
120
120
  repo = create(repo_dir,opts[:local_branch])
121
+ opts.merge!(:full_module_name => full_module_name)
121
122
  response = pull_repo_changes_aux(repo,opts)
122
123
  response
123
124
  end
@@ -446,46 +447,51 @@ module DTK; module Client; class CommandHelper
446
447
  { "diffs" => (diffs[:diffs]||"").to_s, "status" => repo.local_summary() }
447
448
  end
448
449
 
449
- def pull_repo_changes_aux(repo,opts={})
450
+ def pull_repo_changes_aux(repo, opts = {})
450
451
  diffs = DiffSummary.new()
451
452
  hard_reset = opts[:hard_reset] || opts[:force]
452
453
 
453
454
  # default commit in case it is needed
454
- repo.stage_and_commit("Commit prior to pull from remote") if repo.changed?
455
+ repo.stage_and_commit('Commit prior to pull from remote') if repo.changed?
455
456
 
456
457
  if commit_sha = opts[:commit_sha]
457
- #no op if at commit_sha
458
+ # no op if at commit_sha
458
459
  return diffs if (commit_sha == repo.head_commit_sha()) && !hard_reset
459
460
  end
460
461
 
461
- if opts[:remote_repo] and opts[:remote_repo_url]
462
- repo.add_remote(opts[:remote_repo],opts[:remote_repo_url])
462
+ if opts[:remote_repo] && opts[:remote_repo_url]
463
+ repo.add_remote(opts[:remote_repo], opts[:remote_repo_url])
463
464
  end
464
465
 
465
466
  repo.fetch(remote(opts[:remote_repo]))
466
467
  local_branch = repo.local_branch_name
467
- remote_branch_ref = remote_branch_ref(local_branch,opts)
468
+ remote_branch_ref = remote_branch_ref(local_branch, opts)
468
469
 
469
470
  if hard_reset
470
- diffs = DiffSummary.diff(repo,local_branch, remote_branch_ref)
471
+ diffs = DiffSummary.diff(repo, local_branch, remote_branch_ref)
471
472
  repo.merge_theirs(remote_branch_ref)
472
- return({:diffs => diffs, :commit_sha => repo.head_commit_sha()})
473
+ return({ :diffs => diffs, :commit_sha => repo.head_commit_sha() })
473
474
  end
474
475
 
475
- #check if merge needed
476
- merge_rel = repo.merge_relationship(:remote_branch,remote_branch_ref)
476
+ # check if merge needed
477
+ merge_rel = repo.merge_relationship(:remote_branch, remote_branch_ref)
477
478
  if merge_rel == :equal
478
479
  { :diffs => diffs, :commit_sha => repo.head_commit_sha() }
479
- elsif [:branchpoint,:local_ahead].include?(merge_rel)
480
- raise ErrorUsage.new("Unable to do fast-forward merge. You can use --force but all changes in the service instance will be lost") unless opts[:force]
481
- # TODO: right now just wiping out what is in repo
482
- diffs = DiffSummary.diff(repo,local_branch, remote_branch_ref)
483
- repo.merge_theirs(remote_branch_ref)
484
- { :diffs => diffs, :commit_sha => repo.head_commit_sha() }
480
+ elsif [:branchpoint, :local_ahead].include?(merge_rel)
481
+ if opts[:force]
482
+ # TODO: right now just wiping out what is in repo
483
+ diffs = DiffSummary.diff(repo, local_branch, remote_branch_ref)
484
+ repo.merge_theirs(remote_branch_ref)
485
+ { :diffs => diffs, :commit_sha => repo.head_commit_sha() }
486
+ elsif opts[:ignore_dependency_merge_conflict]
487
+ { :diffs => diffs, :commit_sha => repo.head_commit_sha(), :custom_message => "Unable to do fast-forward merge. You can go to '#{opts[:full_module_name]}' and pull with --force option but all changes will be lost." }
488
+ else
489
+ raise Error.new('Unable to do fast-forward merge. You can use --force but all changes will be lost.')
490
+ end
485
491
  elsif merge_rel == :local_behind
486
- #see if any diffs between fetched remote and local branch
487
- #this has be done after commit
488
- diffs = DiffSummary.diff(repo,local_branch, remote_branch_ref)
492
+ # see if any diffs between fetched remote and local branch
493
+ # this has be done after commit
494
+ diffs = DiffSummary.diff(repo, local_branch, remote_branch_ref)
489
495
  return diffs unless diffs.any_diffs?()
490
496
 
491
497
  begin
@@ -494,7 +500,7 @@ module DTK; module Client; class CommandHelper
494
500
  puts e
495
501
  end
496
502
 
497
- if commit_sha and commit_sha != repo.head_commit_sha()
503
+ if commit_sha && commit_sha != repo.head_commit_sha()
498
504
  raise Error.new("Git synchronization problem: expected local head to have sha (#{commit_sha})")
499
505
  end
500
506
 
@@ -1,4 +1,5 @@
1
- require 'jenkins-client'
1
+ # TODO: Marked for removal [Haris] - Do we need this?
2
+ require 'jenkins-client'
2
3
  module DTK; module Client
3
4
  class JenkinsClient
4
5
  require File.expand_path('jenkins_client/config_xml', File.dirname(__FILE__))
@@ -58,7 +59,7 @@ module DTK; module Client
58
59
  def create_job(job_name,config_xml_contents)
59
60
  ::Jenkins::Client::Job.create(job_name, config_xml_contents)
60
61
  end
61
-
62
+
62
63
  def get_info()
63
64
  get('api/json')
64
65
  end
@@ -86,6 +87,6 @@ module DTK; module Client
86
87
  end
87
88
  end
88
89
  end; end
89
-
90
+
90
91
 
91
92
 
@@ -18,10 +18,10 @@ module DTK::Client
18
18
  ##
19
19
  # Method will trigger pull from dtkn for each existing module
20
20
  #
21
- def trigger_module_auto_pull(required_modules, force = false)
21
+ def trigger_module_auto_pull(required_modules, opts = {})
22
22
  return if required_modules.empty?
23
23
 
24
- if force || Console.confirmation_prompt("Do you want to update in addition to this module its dependent modules from the catalog?")
24
+ if opts[:force] || Console.confirmation_prompt("Do you want to update in addition to this module its dependent modules from the catalog?")
25
25
  required_modules.each do |r_module|
26
26
  module_name = full_module_name(r_module)
27
27
  module_type = r_module['type']
@@ -31,46 +31,56 @@ module DTK::Client
31
31
  new_context_params = DTK::Shell::ContextParams.new
32
32
  new_context_params.add_context_to_params(module_type, module_type)
33
33
  new_context_params.add_context_name_to_params(module_type, module_type, module_name)
34
- new_context_params.forward_options( { :skip_recursive_pull => true })
34
+
35
+ forwarded_opts = { skip_recursive_pull: true, ignore_dependency_merge_conflict: true }
36
+ forwarded_opts.merge!(:do_not_raise => true) if opts[:do_not_raise]
37
+ new_context_params.forward_options(forwarded_opts)
35
38
 
36
39
  response = ContextRouter.routeTask(module_type, "pull_dtkn", new_context_params, @conn)
37
40
 
38
- raise DTK::Client::DtkError, response.error_message unless response.ok?
41
+ unless response.ok?
42
+ if opts[:do_not_raise]
43
+ OsUtil.print("#{response.error_message}", :red)
44
+ else
45
+ raise DTK::Client::DtkError, response.error_message
46
+ end
47
+ end
39
48
  end
40
49
 
41
- print "Resuming pull ... " unless force
50
+ print "Resuming pull ... " unless opts[:force]
42
51
  end
43
52
  end
44
53
 
45
-
46
54
  ##
47
55
  # Method will trigger import for each missing module component
48
56
  #
49
- def trigger_module_auto_import(modules_to_import, required_modules, opts={})
50
- puts "Auto-importing missing module(s)"
51
- update_all, update_none = false, false
57
+ def trigger_module_auto_import(modules_to_import, required_modules, opts = {})
58
+ puts 'Auto-installing missing module(s)'
59
+ update_all = false
60
+ update_none = false
52
61
 
53
62
  # Print out or update installed modules from catalog
54
63
  required_modules.each do |r_module|
55
64
  module_name = full_module_name(r_module)
56
65
  module_type = r_module['type']
57
66
 
58
- print "Using #{module_type.gsub('_',' ')} '#{module_name}'\n"
67
+ print "Using #{module_type.gsub('_', ' ')} '#{module_name}'\n"
59
68
  next if update_none || opts[:update_none]
60
69
 
61
70
  if update_all
62
- trigger_module_auto_pull([r_module], true)
71
+ trigger_module_auto_pull([r_module], :force => true, :do_not_raise => true)
63
72
  else
64
- update = Console.confirmation_prompt_additional_options("Do you want to update dependent #{module_type.gsub('_',' ')} '#{module_name}' from the catalog?", ['all', 'none'])
73
+ options = required_modules.size > 1 ? %w(all none) : []
74
+ update = Console.confirmation_prompt_additional_options("Do you want to update dependent #{module_type.gsub('_', ' ')} '#{module_name}' from the catalog?", options)
65
75
  next unless update
66
76
 
67
77
  if update.to_s.eql?('all')
68
78
  update_all = true
69
- trigger_module_auto_pull([r_module], true)
79
+ trigger_module_auto_pull([r_module], :force => true, :do_not_raise => true)
70
80
  elsif update.to_s.eql?('none')
71
81
  update_none = true
72
82
  else
73
- trigger_module_auto_pull([r_module], true)
83
+ trigger_module_auto_pull([r_module], :force => true, :do_not_raise => true)
74
84
  end
75
85
  end
76
86
  end
@@ -84,25 +94,27 @@ module DTK::Client
84
94
  module_url = m_module['module_url']
85
95
 
86
96
  # descriptive message
87
- import_msg = "Importing #{module_type.gsub('_',' ')} '#{module_name}'"
97
+ importing = module_url ? "Importing" : "Installing"
98
+ import_msg = "#{importing} #{module_type.gsub('_', ' ')} '#{module_name}'"
88
99
  import_msg += " from git source #{module_url}" if module_url
89
100
  print "#{import_msg} ... "
90
101
 
91
- unless module_url
102
+ if module_url
103
+ # import from Git source
104
+ new_context_params = ::DTK::Shell::ContextParams.new([module_url, module_name])
105
+ new_context_params.forward_options(:internal_trigger => true)
106
+ response = ContextRouter.routeTask(module_type, 'import_git', new_context_params, @conn)
107
+ else
92
108
  # import from Repo Manager
93
109
  new_context_params = ::DTK::Shell::ContextParams.new([module_name])
94
110
  new_context_params.override_method_argument!('option_2', m_module['version'])
95
- new_context_params.forward_options( { :skip_cloning => false, :skip_auto_install => true, :module_type => module_type}).merge!(opts)
96
- response = ContextRouter.routeTask(module_type, "install", new_context_params, @conn)
97
- else
98
- # import from Git source
99
- new_context_params = ::DTK::Shell::ContextParams.new([module_url, module_name])
100
- new_context_params.forward_options( { :internal_trigger => true })
101
- response = ContextRouter.routeTask(module_type, "import_git", new_context_params, @conn)
111
+ new_context_params.forward_options(:skip_cloning => false, :skip_auto_install => true, :module_type => module_type).merge!(opts)
112
+ response = ContextRouter.routeTask(module_type, 'install', new_context_params, @conn)
102
113
  end
103
114
 
104
- puts(response.data(:does_not_exist) ? response.data(:does_not_exist) : "Done.")
105
- raise DTK::Client::DtkError, response.error_message unless response.ok?
115
+ ignore_component_error = (new_context_params.get_forwarded_options() || {})[:ignore_component_error] && module_type.eql?('component_module')
116
+ puts(response.data(:does_not_exist) ? response.data(:does_not_exist) : 'Done.')
117
+ raise DTK::Client::DtkError, response.error_message if !response.ok? && !ignore_component_error
106
118
  end
107
119
 
108
120
  Response::Ok.new()
data/lib/commands.rb CHANGED
@@ -25,7 +25,7 @@ module DTK
25
25
  DTK::Client::Session.get_connection()
26
26
  end
27
27
 
28
- def self.handle_argument_error(task, error)
28
+ def self.handle_argument_error(task, error)
29
29
  super
30
30
  end
31
31
 
@@ -35,6 +35,6 @@ module DTK
35
35
  self.class.pretty_print_cols()
36
36
  end
37
37
  end
38
-
38
+
39
39
  end
40
40
  end
@@ -56,8 +56,10 @@ module DTK::Client
56
56
 
57
57
  # mode will be :create or :update
58
58
  # service_module_name_x can be name or fullname (NS:MOduleName)
59
- def promote_assembly_aux(mode,assembly_or_workspace_id,service_module_name_x=nil,assembly_template_name=nil,opts={})
60
- namespace,local_clone_dir_exists = nil, nil
59
+ def promote_assembly_aux(mode, assembly_or_workspace_id, service_module_name_x = nil, assembly_template_name = nil, opts = {})
60
+ namespace = nil
61
+ local_clone_dir_exists = nil
62
+
61
63
  post_body = {
62
64
  :assembly_id => assembly_or_workspace_id,
63
65
  :mode => mode.to_s
@@ -66,7 +68,7 @@ module DTK::Client
66
68
  if service_module_name_x
67
69
  service_module_name = service_module_name_x
68
70
  if service_module_name_x =~ /(^[^:]+):([^:]+$)/
69
- namespace,service_module_name = [$1,$2]
71
+ namespace, service_module_name = [$1,$2]
70
72
  end
71
73
  post_body.merge!(:service_module_name => service_module_name)
72
74
  end
@@ -81,16 +83,16 @@ module DTK::Client
81
83
  post_body.merge!(:assembly_template_name => assembly_template_name) if assembly_template_name
82
84
  post_body.merge!(:use_module_namespace => true) if opts[:use_module_namespace]
83
85
  post_body.merge!(:description => opts[:description]) if opts[:description]
84
- response = post rest_url("assembly/promote_to_template"), post_body
86
+ response = post rest_url('assembly/promote_to_template'), post_body
85
87
  return response unless response.ok?()
86
88
 
87
- #synchronize_clone will load new assembly template into service clone on workspace (if it exists)
88
- commit_sha,workspace_branch,namespace,full_module_name,repo_url,version = response.data(:commit_sha,:workspace_branch,:module_namespace,:full_module_name,:repo_url,:version)
89
+ # synchronize_clone will load new assembly template into service clone on workspace (if it exists)
90
+ commit_sha, workspace_branch, namespace, full_module_name, repo_url, version = response.data(:commit_sha, :workspace_branch, :module_namespace, :full_module_name, :repo_url, :version)
89
91
  service_module_name ||= response.data(:module_name)
90
- opts = {:local_branch=>workspace_branch, :namespace => namespace}
92
+ opts = { :local_branch => workspace_branch, :namespace => namespace }
91
93
 
92
94
  if (mode == :update) || local_clone_dir_exists
93
- response = Helper(:git_repo).synchronize_clone(:service_module,service_module_name,commit_sha,opts)
95
+ response = Helper(:git_repo).synchronize_clone(:service_module, service_module_name, commit_sha, opts)
94
96
  else
95
97
  response = Helper(:git_repo).create_clone_with_branch(:service_module, service_module_name, repo_url, workspace_branch, version, namespace)
96
98
  end
@@ -111,44 +113,63 @@ module DTK::Client
111
113
  response = post rest_url("assembly/print_includes"),:assembly_id => assembly_or_workspace_id
112
114
  end
113
115
 
114
- def converge_aux(context_params)
115
- assembly_or_workspace_id,task_action,task_params_string = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID,:option_1,:option_2],method_argument_names)
116
-
117
- task_params = nil
118
- if task_params_string
119
- task_params = task_params_string.split(',').inject(Hash.new) do |h,av|
120
- av_split = av.split('=')
121
- unless av_split.size == 2
122
- raise DTK::Client::DtkValidationError, "The task parameters (#{task_params_string}) is ill-formed"
123
- end
124
- h.merge(av_split[0] => av_split[1])
125
- end
126
- end
116
+ def list_ad_hoc_actions_aux(context_params)
117
+ assembly_or_workspace_id = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID],method_argument_names)
127
118
 
128
119
  post_body = {
129
- :assembly_id => assembly_or_workspace_id
120
+ :assembly_id => assembly_or_workspace_id,
121
+ :type => options.summary? ? :component_type : :component_instance
122
+ }
123
+
124
+ response = post rest_url("assembly/ad_hoc_action_list"), post_body
125
+ response.render_table()
126
+ end
127
+
128
+ # desc "SERVICE-NAME/ID execute-action COMPONENT-INSTANCE [ACTION-NAME [ACTION-PARAMS]]"
129
+ def execute_ad_hoc_action_aux(context_params)
130
+ assembly_or_workspace_id,component_id,method_name,action_params_string = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID,:option_1!,:option_2,:option_3],method_argument_names)
131
+
132
+ action_params = parse_params?(action_params_string)
133
+
134
+ post_body = {
135
+ :assembly_id => assembly_or_workspace_id,
136
+ :component_id => component_id
130
137
  }
138
+ post_body.merge!(:method_name => method_name) if method_name
139
+ post_body.merge!(:action_params => action_params) if action_params
140
+
141
+ response = post rest_url("assembly/ad_hoc_action_execute"), post_body
142
+ return response unless response.ok?
143
+
144
+ task_status_stream(assembly_or_workspace_id)
145
+ Response::Ok.new()
146
+ end
147
+
148
+ def converge_aux(context_params,opts={})
149
+ assembly_or_workspace_id,task_action,task_params_string = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID,:option_1,:option_2],method_argument_names)
131
150
 
132
- response = post rest_url("assembly/find_violations"), post_body
151
+ task_params = parse_params?(task_params_string)
152
+
153
+ # check for violations
154
+ response = post rest_url("assembly/find_violations"), :assembly_id => assembly_or_workspace_id
133
155
  return response unless response.ok?
134
156
  if response.data and response.data.size > 0
135
- #TODO: may not directly print here; isntead use a lower level fn
136
157
  error_message = "The following violations were found; they must be corrected before workspace can be converged"
137
158
  DTK::Client::OsUtil.print(error_message, :red)
138
159
  return response.render_table(:violation)
139
160
  end
140
161
 
141
- post_body.merge!(:commit_msg => options.commit_msg) if options.commit_msg
142
- post_body.merge!(:task_action => task_action) if task_action
143
- post_body.merge!(:task_params => task_params) if task_params
144
-
162
+ post_body = PostBody.new(
163
+ :assembly_id => assembly_or_workspace_id,
164
+ :commit_msg? => options.commit_msg,
165
+ :task_action? => task_action,
166
+ :task_params? => task_params
167
+ )
145
168
  response = post rest_url("assembly/create_task"), post_body
146
169
  return response unless response.ok?
147
170
 
148
171
  if response.data
149
- confirmation_message = response.data["confirmation_message"]
150
-
151
- if confirmation_message
172
+ if confirmation_message = response.data["confirmation_message"]
152
173
  return unless Console.confirmation_prompt("Workspace service is stopped, do you want to start it"+'?')
153
174
  post_body.merge!(:start_assembly=>true)
154
175
  response = post rest_url("assembly/create_task"), post_body
@@ -158,8 +179,28 @@ module DTK::Client
158
179
 
159
180
  # execute task
160
181
  task_id = response.data(:task_id)
161
- post rest_url("task/execute"), "task_id" => task_id
182
+ response = post rest_url("task/execute"), "task_id" => task_id
183
+ return response unless response.ok?
184
+
185
+ if opts[:mode] == :stream
186
+ task_status_stream(assembly_or_workspace_id)
187
+ end
188
+
189
+ Response::Ok.new()
190
+ end
191
+
192
+ def parse_params?(params_string)
193
+ if params_string
194
+ params_string.split(',').inject(Hash.new) do |h,av|
195
+ av_split = av.split('=')
196
+ unless av_split.size == 2
197
+ raise DtkValidationError, "The parameter string (#{params_string}) is ill-formed"
198
+ end
199
+ h.merge(av_split[0] => av_split[1])
200
+ end
201
+ end
162
202
  end
203
+ private :parse_params?
163
204
 
164
205
  def edit_module_aux(context_params)
165
206
  assembly_or_workspace_id, component_module_name = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID,:option_1!],method_argument_names)
@@ -208,15 +249,24 @@ module DTK::Client
208
249
  response = post rest_url("assembly/prepare_for_edit_module"), post_body
209
250
  end
210
251
 
211
- def edit_workflow_aux(context_params)
212
- assembly_or_workspace_id = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID],method_argument_names)
252
+ def edit_or_create_workflow_aux(context_params,opts={})
253
+ option_1 = (opts[:create] ? :option_1! : :option_1)
254
+ assembly_or_workspace_id, workflow_name = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID,option_1],method_argument_names)
213
255
  post_body = {
214
- :assembly_id => assembly_or_workspace_id,
215
- :module_type => 'service_module',
256
+ :assembly_id => assembly_or_workspace_id,
257
+ :module_type => 'service_module',
216
258
  :modification_type => 'workflow'
217
259
  }
260
+ if workflow_name
261
+ post_body.merge!(:task_action => workflow_name)
262
+ end
263
+ if opts[:create]
264
+ post_body.merge!(:create => true)
265
+ post_body.merge!(:base_task_action => opts[:create_from]) if opts[:create_from]
266
+ end
218
267
  response = post rest_url("assembly/prepare_for_edit_module"), post_body
219
268
  return response unless response.ok?
269
+
220
270
  assembly_name,service_module_id,service_module_name,version,repo_url,branch,branch_head_sha,edit_file = response.data(:assembly_name,:module_id,:full_module_name,:version,:repo_url,:workspace_branch,:branch_head_sha,:edit_file)
221
271
  edit_opts = {
222
272
  :automatically_clone => true,
@@ -234,6 +284,7 @@ module DTK::Client
234
284
  :modification_type => :workflow,
235
285
  :edit_file => edit_file
236
286
  }
287
+ edit_opts.merge!(:task_action => workflow_name) if workflow_name
237
288
  version = nil #TODO: version associated with assembly is passed in edit_opts, which is a little confusing
238
289
  edit_aux(:service_module,service_module_id,service_module_name,version,edit_opts)
239
290
  end
@@ -257,14 +308,14 @@ module DTK::Client
257
308
  end
258
309
 
259
310
  def push_module_updates_aux(context_params)
260
- assembly_or_workspace_id, component_module_name = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID,:option_1!],method_argument_names)
311
+ assembly_or_workspace_id, component_module_name = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID, :option_1!], method_argument_names)
261
312
  post_body = {
262
313
  :assembly_id => assembly_or_workspace_id,
263
314
  :module_name => component_module_name,
264
315
  :module_type => 'component_module'
265
316
  }
266
317
  post_body.merge!(:force => true) if options.force?
267
- response = post(rest_url("assembly/promote_module_updates"),post_body)
318
+ response = post(rest_url('assembly/promote_module_updates'), post_body)
268
319
  return response unless response.ok?
269
320
  return Response::Ok.new() unless response.data(:any_updates)
270
321
  if dsl_parsing_errors = response.data(:dsl_parsing_errors)
@@ -272,11 +323,12 @@ module DTK::Client
272
323
  OsUtil.print(error_message, :red)
273
324
  return Response::NoOp.new()
274
325
  end
275
- module_name,namespace,branch,ff_change = response.data(:module_name,:module_namespace,:workspace_branch,:fast_forward_change)
326
+ module_name, namespace, branch, ff_change = response.data(:module_name, :module_namespace, :workspace_branch, :fast_forward_change)
276
327
  ff_change ||= true
277
- opts = {:local_branch => branch,:namespace => namespace}
278
- opts.merge!(:hard_reset => true) if !ff_change
279
- response = Helper(:git_repo).pull_changes?(:component_module,module_name,opts)
328
+ opts = { :local_branch => branch, :namespace => namespace }
329
+ opts.merge!(:hard_reset => true) unless ff_change
330
+ opts.merge!(:force => true) if options.force?
331
+ response = Helper(:git_repo).pull_changes?(:component_module, module_name, opts)
280
332
  return response unless response.ok?()
281
333
  Response::Ok.new()
282
334
  end
@@ -323,9 +375,9 @@ module DTK::Client
323
375
  response = Helper(:git_repo).pull_changes?(:component_module, module_name, edit_opts.merge!(opts))
324
376
  return response unless response.ok?()
325
377
 
326
- edit_opts.merge!(:force_parse => true, :update_from_includes => true, :print_dependencies => true, :remote_branch => local_branch)
378
+ edit_opts.merge!(:force_parse => true, :update_from_includes => true, :print_dependencies => true, :remote_branch => local_branch, :force_clone => true)
327
379
  response = push_clone_changes_aux(:component_module, module_id, nil, "Pull base module updates", true, edit_opts)
328
-
380
+
329
381
  unless response.ok?()
330
382
  # if parsing error on assembly module (components/attributes/link_defs integrity violations) do git reset --hard
331
383
  Helper(:git_repo).hard_reset_branch_to_sha(:component_module, module_name, edit_opts)
@@ -336,24 +388,46 @@ module DTK::Client
336
388
  end
337
389
 
338
390
  def workflow_info_aux(context_params)
339
- assembly_or_workspace_id = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID],method_argument_names)
391
+ assembly_or_workspace_id,workflow_name = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID,:option_1],method_argument_names)
340
392
  post_body = {
341
393
  :assembly_id => assembly_or_workspace_id,
342
394
  :subtype => 'instance'
343
395
  }
396
+ post_body.merge!(:task_action => workflow_name) if workflow_name
344
397
  post(rest_url("assembly/info_about_task"),post_body)
345
398
  end
346
399
 
400
+ def workflow_list_aux(context_params)
401
+ assembly_or_workspace_id = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID],method_argument_names)
402
+ post_body = {
403
+ :assembly_id => assembly_or_workspace_id
404
+ }
405
+ response = post(rest_url("assembly/task_action_list"),post_body)
406
+ data_type = 'task_action'
407
+ response.render_table(data_type)
408
+ end
409
+
347
410
  def task_status_aw_aux(context_params)
348
411
  assembly_or_workspace_id = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID],method_argument_names)
349
- response = task_status_aux(assembly_or_workspace_id,:assembly,:wait => options.wait?,:summarize => options.summarize?)
412
+ mode =
413
+ if options.wait?
414
+ :refresh
415
+ else
416
+ if options.has_key?('mode') and options.mode.nil?
417
+ raise DtkError::Usage.new("option --mode needs an argument")
418
+ end
419
+ (options.mode || :snapshot).to_sym
420
+ end
421
+ response = task_status_aux(mode,assembly_or_workspace_id,:assembly,:summarize => options.summarize?)
350
422
 
351
423
  # TODO: Hack which is necessery for the specific problem (DTK-725), we don't get proper error message when there is a timeout doing converge
352
- unless response == true
353
- return response.merge("data" => [{ "errors" => {"message" => "Task does not exist for workspace."}}]) unless response["data"]
354
- response["data"].each do |data|
355
- if data["errors"]
356
- data["errors"]["message"] = "[TIMEOUT ERROR] Server is taking too long to respond." if data["errors"]["message"] == "error"
424
+ unless mode == :stream
425
+ unless response == true
426
+ return response.merge("data" => [{ "errors" => {"message" => "Task does not exist for workspace."}}]) unless response["data"]
427
+ response["data"].each do |data|
428
+ if data["errors"]
429
+ data["errors"]["message"] = "[TIMEOUT ERROR] Server is taking too long to respond." if data["errors"]["message"] == "error"
430
+ end
357
431
  end
358
432
  end
359
433
  end
@@ -407,103 +481,6 @@ module DTK::Client
407
481
  list_aux(context_params)
408
482
  end
409
483
 
410
- # desc "WORKSPACE-NAME/ID list-assemblies","List assemblies for current workspace."
411
- # def list_assemblies(context_params)
412
- # data_type = :assembly
413
- # post_body = { :subtype => 'instance', :detail_level => 'nodes' }
414
- # rest_endpoint = "assembly/list"
415
- # response = post rest_url(rest_endpoint), post_body
416
-
417
- # response.render_table(data_type)
418
- # return response
419
- # end
420
-
421
- # desc "WORKSPACE-NAME/ID list-assemblies","List assemblies for current workspace."
422
- # def list_assemblies(context_params)
423
- # data_type = :assembly
424
- # post_body = { :subtype => 'instance', :detail_level => 'nodes' }
425
- # rest_endpoint = "assembly/list"
426
- # response = post rest_url(rest_endpoint), post_body
427
-
428
- # response.render_table(data_type)
429
- # return response
430
- # end
431
-
432
- # desc "WORKSPACE-NAME/ID list-assemblies","List assemblies for current workspace."
433
- # def list_assemblies(context_params)
434
- # workspace_id, node_id, component_id, attribute_id, about = context_params.retrieve_arguments([:workspace_id,:node_id,:component_id,:attribute_id,:option_1],method_argument_names)
435
- # detail_to_include = nil
436
-
437
- # if about
438
- # case about
439
- # when "nodes"
440
- # data_type = :node
441
- # when "components"
442
- # data_type = :component
443
- # detail_to_include = [:component_dependencies]
444
- # when "attributes"
445
- # data_type = :attribute
446
- # detail_to_include = [:attribute_links]
447
- # when "tasks"
448
- # data_type = :task
449
- # else
450
- # raise_validation_error_method_usage('list')
451
- # end
452
- # end
453
-
454
- # post_body = {
455
- # :assembly_id => workspace_id,
456
- # :node_id => node_id,
457
- # :component_id => component_id,
458
- # :subtype => 'instance'
459
- # }
460
- # post_body.merge!(:detail_to_include => detail_to_include) if detail_to_include
461
- # rest_endpoint = "assembly/info_about"
462
-
463
- # if context_params.is_last_command_eql_to?(:attribute)
464
- # raise DTK::Client::DtkError, "Not supported command for current context level." if attribute_id
465
- # about, data_type = get_type_and_raise_error_if_invalid(about, "attributes", ["attributes"])
466
- # elsif context_params.is_last_command_eql_to?(:component)
467
- # if component_id
468
- # about, data_type = get_type_and_raise_error_if_invalid(about, "attributes", ["attributes"])
469
- # else
470
- # about, data_type = get_type_and_raise_error_if_invalid(about, "components", ["attributes", "components"])
471
- # end
472
- # elsif context_params.is_last_command_eql_to?(:node)
473
- # if node_id
474
- # about, data_type = get_type_and_raise_error_if_invalid(about, "components", ["attributes", "components"])
475
- # data_type = :workspace_attribute
476
- # else
477
- # about, data_type = get_type_and_raise_error_if_invalid(about, "nodes", ["attributes", "components", "nodes"])
478
- # end
479
- # else
480
- # if workspace_id
481
- # about, data_type = get_type_and_raise_error_if_invalid(about, "nodes", ["attributes", "components", "nodes", "tasks"])
482
- # else
483
- # data_type = :assembly
484
- # post_body = { :subtype => 'instance', :detail_level => 'nodes' }
485
- # rest_endpoint = "assembly/list"
486
- # end
487
- # end
488
-
489
- # post_body[:about] = about
490
- # response = post rest_url(rest_endpoint), post_body
491
-
492
- # if (data_type.to_s.eql?("workspace_attribute") && response["data"])
493
- # response["data"].each do |data|
494
- # unless(data["linked_to_display_form"].to_s.empty?)
495
- # data_type = :workspace_attribute_w_link
496
- # break
497
- # end
498
- # end
499
- # end
500
-
501
- # # set render view to be used
502
- # response.render_table(data_type)
503
-
504
- # return response
505
- # end
506
-
507
484
  def link_attribute_aux(context_params)
508
485
  assembly_or_workspace_id, target_attr_term, source_attr_term = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID,:option_1!,:option_2!],method_argument_names)
509
486
  post_body = {
@@ -514,22 +491,20 @@ module DTK::Client
514
491
  post rest_url("assembly/add_ad_hoc_attribute_links"), post_body
515
492
  end
516
493
 
517
- def list_attribute_mappings_aux(context_params)
518
- post_body = Helper(:service_link).post_body_with_id_keys(context_params,method_argument_names)
519
- post rest_url("assembly/list_attribute_mappings"), post_body
520
- end
521
-
522
494
  def create_component_aux(context_params)
523
495
  # If method is invoked from 'assembly/node' level retrieve node_id argument
524
496
  # directly from active context
525
497
  if context_params.is_there_identifier?(:node)
526
- mapping = [REQ_ASSEMBLY_OR_WS_ID,:node_id!,:option_1!]
498
+ mapping = [REQ_ASSEMBLY_OR_WS_ID, :node_id!, :option_1!]
499
+ assembly_id, node_id, component_template_id = context_params.retrieve_arguments(mapping, method_argument_names)
527
500
  else
528
501
  # otherwise retrieve node_id from command options
529
- mapping = [REQ_ASSEMBLY_OR_WS_ID,:option_1!,:option_2!]
502
+ mapping = [REQ_ASSEMBLY_OR_WS_ID, :option_1!]
503
+ assembly_id, component_template_id = context_params.retrieve_arguments(mapping, method_argument_names)
504
+ node_id = nil
530
505
  end
531
506
 
532
- assembly_id,node_id,component_template_id = context_params.retrieve_arguments(mapping,method_argument_names)
507
+ # assembly_id,node_id,component_template_id = context_params.retrieve_arguments(mapping,method_argument_names)
533
508
  namespace, component_template_id = get_namespace_and_name_for_component(component_template_id)
534
509
 
535
510
  post_body = {
@@ -549,21 +524,24 @@ module DTK::Client
549
524
 
550
525
  def link_components_aux(context_params)
551
526
  post_body = link_unlink_components__ret_post_body(context_params)
552
- post rest_url("assembly/add_service_link"), post_body
527
+ post rest_url('assembly/add_service_link'), post_body
553
528
  end
554
529
 
555
530
  def link_unlink_components__ret_post_body(context_params)
556
531
  if context_params.is_last_command_eql_to?(:component)
557
- assembly_or_workspace_id,dep_cmp,antec_cmp,dependency_name = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID,:component_id!,:option_1!,:option_2],method_argument_names)
532
+ assembly_or_workspace_id, dep_cmp, antec_cmp, dependency_name = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID, :component_id!, :option_1!, :option_2], method_argument_names)
558
533
  else
559
- assembly_or_workspace_id,dep_cmp,antec_cmp,dependency_name = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID,:option_1!,:option_2!,:option_3],method_argument_names)
534
+ assembly_or_workspace_id, dep_cmp, antec_cmp, dependency_name = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID, :option_1!, :option_2!, :option_3], method_argument_names)
560
535
  end
536
+
537
+ antec_cmp = "assembly_wide/#{antec_cmp}" unless antec_cmp.include?('/')
561
538
  post_body = {
562
539
  :assembly_id => assembly_or_workspace_id,
563
540
  :input_component_id => dep_cmp,
564
541
  :output_component_id => antec_cmp
565
542
  }
566
543
  post_body.merge!(:dependency_name => dependency_name) if dependency_name
544
+
567
545
  post_body
568
546
  end
569
547
 
@@ -593,15 +571,6 @@ module DTK::Client
593
571
  response.render_table(:possible_service_connection)
594
572
  end
595
573
 
596
- def list_smoketests(context_params)
597
- assembly_or_workspace_id = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID],method_argument_names)
598
-
599
- post_body = {
600
- :assembly_id => assembly_or_workspace_id
601
- }
602
- post rest_url("assembly/list_smoketests"), post_body
603
- end
604
-
605
574
  def info_aux(context_params)
606
575
  assembly_or_workspace_id, node_id, component_id, attribute_id = context_params.retrieve_arguments([REQ_ASSEMBLY_OR_WS_ID, :node_id, :component_id, :attribute_id],method_argument_names)
607
576
  is_json_return = context_params.get_forwarded_options[:json_return] || false
@@ -726,18 +695,42 @@ module DTK::Client
726
695
 
727
696
  def set_attribute_aux(context_params)
728
697
  if context_params.is_there_identifier?(:attribute)
729
- mapping = (options.unset? ? [REQ_ASSEMBLY_OR_WS_ID,:attribute_id!] : [REQ_ASSEMBLY_OR_WS_ID,:attribute_id!,:option_1!])
698
+ mapping = (options.unset? ? [REQ_ASSEMBLY_OR_WS_ID, :attribute_id!] : [REQ_ASSEMBLY_OR_WS_ID, :attribute_id!, :option_1!])
730
699
  else
731
- mapping = (options.unset? ? [REQ_ASSEMBLY_OR_WS_ID,:option_1!] : [REQ_ASSEMBLY_OR_WS_ID,:option_1!,:option_2!])
700
+ mapping = (options.unset? ? [REQ_ASSEMBLY_OR_WS_ID, :option_1!] : [REQ_ASSEMBLY_OR_WS_ID, :option_1!, :option_2!])
732
701
  end
733
702
 
734
- assembly_or_workspace_id, pattern, value = context_params.retrieve_arguments(mapping,method_argument_names)
703
+ assembly_or_workspace_id, pattern, value = context_params.retrieve_arguments(mapping, method_argument_names)
735
704
  post_body = {
736
705
  :assembly_id => assembly_or_workspace_id,
737
706
  :pattern => pattern
738
707
  }
708
+
709
+ raise DTK::Client::DtkValidationError, 'Please use only component-attribute (-c) or node-attribute (-n) option' if options.component_attribute? && options.node_attribute?
710
+
711
+ # if try to set service instance attribute but using -n option to sepicify it is node attribute, say that node attribute does not exist
712
+ raise DTK::Client::DtkError, "[ERROR] Node attribute '#{pattern}' does not exist" if options.node_attribute? && !pattern.include?('/')
713
+
714
+ # make sure -c and -n are used only with node or cmp attributes directly on service instance
715
+ validate_service_instance_node_or_cmp_attrs(pattern, options) if options.component_attribute? || options.node_attribute?
716
+
717
+ post_body.merge!(:component_attribute => true) if options.component_attribute?
718
+ post_body.merge!(:node_attribute => true) if options.node_attribute? || context_params.is_there_identifier?(:node)
739
719
  post_body.merge!(:value => value) unless options.unset?
740
- post rest_url("assembly/set_attributes"), post_body
720
+
721
+ response = post rest_url('assembly/set_attributes'), post_body
722
+ return response unless response.ok?
723
+
724
+ if r_data = response.data
725
+ if r_data.is_a?(Hash) && (ambiguous = r_data['ambiguous'])
726
+ unless ambiguous.empty?
727
+ msg = "It is ambiguous whether '#{ambiguous.join(', ')}' #{ambiguous.size == 1 ? 'is' : 'are'} node or component attribute(s). Run set-attribute again with one of options -c [--component-attribute] or -n [--node-attribute]."
728
+ raise DTK::Client::DtkError, msg
729
+ end
730
+ end
731
+ end
732
+
733
+ Response::Ok.new()
741
734
  end
742
735
 
743
736
  def create_attribute_aux(context_params)
@@ -890,14 +883,24 @@ module DTK::Client
890
883
  return unless Console.confirmation_prompt("Are you sure you want to delete #{what} '#{component_id}'"+'?')
891
884
  end
892
885
 
886
+ if node_id.nil? && !(component_id.to_s =~ /^[0-9]+$/)
887
+ if component_id.to_s.include?('/')
888
+ node_id, component_id = component_id.split('/')
889
+ node_name = node_id
890
+ else
891
+ node_id = node_name = 'assembly_wide'
892
+ end
893
+ end
894
+
893
895
  post_body = {
894
896
  :assembly_id => assembly_or_workspace_id,
895
- :node_id => node_id,
896
897
  :component_id => component_id
897
898
  }
898
899
 
899
900
  # delete component by name (e.g. delete-component dtk_java)
900
901
  post_body.merge!(:cmp_full_name => "#{node_name}/#{component_id}") if (node_name && !(component_id.to_s =~ /^[0-9]+$/))
902
+ post_body.merge!(:node_id => node_id) if node_id
903
+
901
904
  response = post(rest_url("assembly/delete_component"),post_body)
902
905
  end
903
906
 
@@ -1413,5 +1416,14 @@ module DTK::Client
1413
1416
  post rest_url("assembly/clear_tasks"), post_body
1414
1417
  end
1415
1418
 
1419
+ def validate_service_instance_node_or_cmp_attrs(pattern, options)
1420
+ split_pattern = pattern.split('/')
1421
+ return if split_pattern.size == 2
1422
+ if options.node_attribute?
1423
+ raise DTK::Client::DtkError, 'Please use -n option only with service instance node attributes (node_name/attribute_name)'
1424
+ elsif options.component_attribute?
1425
+ raise DTK::Client::DtkError, 'Please use -c option only with service instance component attributes (cmp_name/attribute_name)'
1426
+ end
1427
+ end
1416
1428
  end
1417
1429
  end