dtk-client 0.7.1.1 → 0.7.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 (41) hide show
  1. checksums.yaml +5 -13
  2. data/Gemfile +1 -1
  3. data/Gemfile_dev +1 -0
  4. data/bin/dtk-execute +1 -18
  5. data/dtk-client.gemspec +1 -1
  6. data/lib/client.rb +0 -5
  7. data/lib/command_helpers/git_repo.rb +43 -12
  8. data/lib/commands/common/thor/assembly_workspace.rb +3 -3
  9. data/lib/commands/common/thor/module.rb +81 -7
  10. data/lib/commands/common/thor/module/import.rb +14 -4
  11. data/lib/commands/common/thor/pull_from_remote.rb +2 -0
  12. data/lib/commands/common/thor/push_clone_changes.rb +1 -1
  13. data/lib/commands/common/thor/push_to_remote.rb +18 -4
  14. data/lib/commands/common/thor/remotes.rb +54 -0
  15. data/lib/commands/common/thor/{set_required_params.rb → set_required_attributes.rb} +1 -1
  16. data/lib/commands/thor/component.rb +2 -2
  17. data/lib/commands/thor/component_module.rb +56 -7
  18. data/lib/commands/thor/dtk.rb +7 -7
  19. data/lib/commands/thor/node.rb +4 -4
  20. data/lib/commands/thor/node_group.rb +4 -4
  21. data/lib/commands/thor/remotes.rb +32 -0
  22. data/lib/commands/thor/service.rb +9 -5
  23. data/lib/commands/thor/service_module.rb +41 -5
  24. data/lib/commands/thor/test_module.rb +43 -3
  25. data/lib/commands/thor/workspace.rb +8 -4
  26. data/lib/core.rb +3 -0
  27. data/lib/domain/git_adapter.rb +15 -8
  28. data/lib/domain/response.rb +39 -26
  29. data/lib/dtk-client/version.rb +1 -1
  30. data/lib/execute.rb +1 -1
  31. data/lib/execute/command/api_call/service.rb +1 -0
  32. data/lib/execute/script.rb +50 -0
  33. data/lib/execute/script/add_tenant.rb +91 -0
  34. data/lib/execute/script/add_tenant_without_router.rb +87 -0
  35. data/lib/shell/context.rb +15 -9
  36. data/lib/shell/domain/context_params.rb +2 -0
  37. data/lib/shell/help_monkey_patch.rb +4 -0
  38. data/lib/util/os_util.rb +2 -1
  39. metadata +27 -24
  40. data/lib/execute/examples.rb +0 -3
  41. data/lib/execute/examples/add_tenant.rb +0 -42
@@ -0,0 +1,54 @@
1
+
2
+ module DTK::Client
3
+ module RemotesMixin
4
+
5
+ def remote_remove_aux(context_params)
6
+ module_id, remote_name = context_params.retrieve_arguments([::DTK::Client::ModuleMixin::REQ_MODULE_ID,:option_1!], method_argument_names)
7
+ module_type = get_module_type(context_params)
8
+
9
+ unless options.force
10
+ return unless Console.confirmation_prompt("Are you sure you want to remove '#{remote_name}'?")
11
+ end
12
+
13
+ post_body = {
14
+ "#{module_type}_id".to_sym => module_id,
15
+ :remote_name => remote_name
16
+ }
17
+
18
+ response = post rest_url("#{module_type}/remove_git_remote"), post_body
19
+ return response unless response.ok?
20
+ OsUtil.print("Successfully removed remote '#{remote_name}'", :green)
21
+ nil
22
+ end
23
+
24
+ def remote_add_aux(context_params)
25
+ module_id, remote_name, remote_url = context_params.retrieve_arguments([::DTK::Client::ModuleMixin::REQ_MODULE_ID,:option_1!,:option_2!], method_argument_names)
26
+ module_type = get_module_type(context_params)
27
+
28
+ post_body = {
29
+ "#{module_type}_id".to_sym => module_id,
30
+ :remote_name => remote_name,
31
+ :remote_url => remote_url
32
+ }
33
+
34
+ response = post rest_url("#{module_type}/add_git_remote"), post_body
35
+ return response unless response.ok?
36
+ OsUtil.print("Successfully added remote '#{remote_name}'", :green)
37
+ nil
38
+ end
39
+
40
+ def remote_list_aux(context_params)
41
+ module_id = context_params.retrieve_arguments([::DTK::Client::ModuleMixin::REQ_MODULE_ID], method_argument_names)
42
+ module_type = get_module_type(context_params)
43
+
44
+ post_body = {
45
+ "#{module_type}_id".to_sym => module_id
46
+ }
47
+
48
+ response = post rest_url("#{module_type}/info_git_remote"), post_body
49
+ response.render_table(:remotes)
50
+ end
51
+
52
+
53
+ end
54
+ end
@@ -1,6 +1,6 @@
1
1
  module DTK::Client
2
2
  module SetRequiredParamsMixin
3
- def set_required_params_aux(id,type,subtype=nil)
3
+ def set_required_attributes_aux(id,type,subtype=nil)
4
4
  id_field = "#{type}_id".to_sym
5
5
  post_body = {
6
6
  id_field => id,
@@ -16,7 +16,7 @@ module DTK::Client
16
16
  res = get_cached_response(:component_template, "component_module/info_about", { :component_module_id => component_module_id, :about => :components})
17
17
  else
18
18
  assembly_or_worspace_id, node_id, node_name = context_params.retrieve_arguments([[:service_id, :workspace_id], :node_id!, :node_name!])
19
-
19
+
20
20
  post_body = {
21
21
  :assembly_id => assembly_or_worspace_id,
22
22
  :node_id => node_id,
@@ -30,7 +30,7 @@ module DTK::Client
30
30
  else
31
31
  response = get_cached_response(:node_component, "node/info_about", post_body)
32
32
  end
33
-
33
+
34
34
  modified_response = response.clone_me()
35
35
  modified_response['data'].each { |e| e['display_name'] = e['display_name'].split('/').last }
36
36
 
@@ -9,7 +9,7 @@ module DTK::Client
9
9
  end
10
10
 
11
11
  def self.valid_children()
12
- [:component]
12
+ [:component, :remotes]
13
13
  end
14
14
 
15
15
  # this includes children of children - has to be sorted by n-level access
@@ -17,6 +17,10 @@ module DTK::Client
17
17
  [:component]
18
18
  end
19
19
 
20
+ def self.multi_context_children()
21
+ [[:component], [:remotes], [:component, :remotes]]
22
+ end
23
+
20
24
  def self.valid_child?(name_of_sub_context)
21
25
  return ComponentModule.valid_children().include?(name_of_sub_context.to_sym)
22
26
  end
@@ -32,13 +36,19 @@ module DTK::Client
32
36
  :self => [
33
37
  ["list"," list [--remote] [--diff] [-n NAMESPACE]","# List loaded or remote component modules. Use --diff to compare loaded and remote component modules."]
34
38
  ],
35
- :"component" => [
39
+ :component => [
36
40
  ["list","list","# List all component templates."],
37
41
  ["list-attributes","list-attributes", "# List all attributes for given component."]
42
+ ],
43
+ :remotes => [
44
+ ["push-remote", "push-remote [REMOTE-NAME] [--force]", "# Push local changes to remote git repository"],
45
+ ["list-remotes", "list-remotes", "# List git remotes for given module"],
46
+ ["add-remote", "add-remote REMOTE-NAME REMOTE-URL", "# Add git remote for given module"],
47
+ ["remove-remote", "remove-remote REPO-NAME [-y]", "# Remove git remote for given module"]
38
48
  ]
39
49
  },
40
50
  :identifier_only => {
41
- :"component" => [
51
+ :component => [
42
52
  ["list-attributes","list-attributes", "# List all attributes for given component."]
43
53
  ]
44
54
  }
@@ -167,8 +177,18 @@ module DTK::Client
167
177
  :desc => "DTK Repo Manager from which to resolve requested module."
168
178
  def install(context_params)
169
179
  response = install_module_aux(context_params)
170
- @@invalidate_map << :component_module if response && response.ok?
171
-
180
+ if response && response.ok?
181
+ @@invalidate_map << :component_module
182
+ # TODO: hack before clean up way to indicate to better format what is passed as hash; these lines print the created module,
183
+ # not the module_directory
184
+ if module_directory = response.data(:module_directory)
185
+ split = module_directory.split('/')
186
+ if split.size > 2
187
+ installed_module = split[split.size-2..split.size-1].join(':')
188
+ response = Response::Ok.new('installed_module' => installed_module)
189
+ end
190
+ end
191
+ end
172
192
  response
173
193
  end
174
194
 
@@ -389,7 +409,7 @@ module DTK::Client
389
409
  end
390
410
 
391
411
  # desc "COMPONENT-MODULE-NAME/ID push-dtkn [-n NAMESPACE] [-m COMMIT-MSG]", "Push changes from local copy of component module to remote repository (dtkn)."
392
- desc "COMPONENT-MODULE-NAME/ID push-dtkn [-n NAMESPACE]", "Push changes from local copy of component module to remote repository (dtkn)."
412
+ desc "COMPONENT-MODULE-NAME/ID push-dtkn [-n NAMESPACE] [--force]", "Push changes from local copy of component module to remote repository (dtkn)."
393
413
  method_option "message",:aliases => "-m" ,
394
414
  :type => :string,
395
415
  :banner => "COMMIT-MSG",
@@ -399,7 +419,7 @@ module DTK::Client
399
419
  :banner => "NAMESPACE",
400
420
  :desc => "Remote namespace"
401
421
  #hidden option for dev
402
- method_option 'force-parse', :aliases => '-f', :type => :boolean, :default => false
422
+ method_option :force, :type => :boolean, :default => false, :aliases => '-f'
403
423
  def push_dtkn(context_params, internal_trigger=false)
404
424
  push_dtkn_module_aux(context_params, internal_trigger)
405
425
  end
@@ -415,6 +435,35 @@ module DTK::Client
415
435
  # list_diffs_module_aux(context_params)
416
436
  end
417
437
 
438
+ # REMOTE INTERACTION
439
+
440
+ desc "HIDE_FROM_BASE push-remote [REMOTE-NAME] [--force]", "Push local changes to remote git repository"
441
+ method_option :force, :type => :boolean, :default => false
442
+ def push_remote(context_params)
443
+ push_remote_module_aux(context_params)
444
+ end
445
+
446
+ desc "HIDE_FROM_BASE list-remotes", "List git remotes for given module"
447
+ def list_remotes(context_params)
448
+ remote_list_aux(context_params)
449
+ end
450
+
451
+ desc "HIDE_FROM_BASE add-remote REMOTE-NAME REMOTE-URL", "Add git remote for given module"
452
+ def add_remote(context_params)
453
+ remote_add_aux(context_params)
454
+ end
455
+
456
+ desc "HIDE_FROM_BASE remove-remote REPO-NAME [-y]", "Remove git remote for given module"
457
+ method_option :force, :aliases => '-y', :type => :boolean, :default => false
458
+ def remove_remote(context_params)
459
+ remote_remove_aux(context_params)
460
+ end
461
+
462
+ desc "COMPONENT-MODULE-NAME/ID fork NAMESPACE", "Fork component module to new namespace"
463
+ def fork(context_params)
464
+ fork_aux(context_params)
465
+ end
466
+
418
467
  #
419
468
  # DEVELOPMENT MODE METHODS
420
469
  #
@@ -1,12 +1,12 @@
1
1
 
2
2
  module DTK::Client
3
3
 
4
- # Following are descriptions of available commands
4
+ # Following are descriptions of available commands
5
5
  class Dtk < CommandBaseThor
6
6
 
7
7
  # entities that are not available on root but later in n-context
8
8
  def self.additional_entities()
9
- ['component','attribute','utils','node','task','component-template','assembly']
9
+ ['component','attribute','utils','node','task','component-template','assembly','remotes']
10
10
  end
11
11
 
12
12
  desc "workspace","Sandbox for development and testing"
@@ -18,7 +18,7 @@ module DTK::Client
18
18
  def target
19
19
  # API descriptor, SYM_LINK!
20
20
  end
21
-
21
+
22
22
 
23
23
  # NOTE
24
24
  # Following methods are just api descriptors, invocation happens at "bin/dtk" entry point
@@ -37,7 +37,7 @@ module DTK::Client
37
37
  # # API descriptor
38
38
  # end
39
39
 
40
- #TODO: not exposed
40
+ #TODO: not exposed
41
41
  #desc "dependency","DESCRIPTION TO BE ADDED."
42
42
  #def dependency
43
43
  # # API descriptor
@@ -61,7 +61,7 @@ module DTK::Client
61
61
  # desc "node", "Commands to list, query, and delete/destroy node instances."
62
62
  # def node
63
63
  # # API descriptor
64
- # end
64
+ # end
65
65
 
66
66
  # desc "node-group", "Add/Destroy/List available groups of nodes."
67
67
  # def node_group
@@ -82,7 +82,7 @@ module DTK::Client
82
82
  #desc "repo", "Part of dtk client which enables us to sync, destroy, view available repos."
83
83
  #def repo
84
84
  # # API descriptor
85
- #end
85
+ #end
86
86
 
87
87
  #TODO: not supported yet
88
88
  #desc "project", "View available projects."
@@ -112,7 +112,7 @@ module DTK::Client
112
112
  # API descriptor
113
113
  end
114
114
  end
115
-
115
+
116
116
 
117
117
  desc "provider", "Manage infrastructure providers and deployment targets (ie: EC2 and us-east)"
118
118
  def provider
@@ -1,5 +1,5 @@
1
1
  dtk_require_common_commands('thor/task_status')
2
- dtk_require_common_commands('thor/set_required_params')
2
+ dtk_require_common_commands('thor/set_required_attributes')
3
3
  dtk_require_common_commands('thor/test_action_agent')
4
4
 
5
5
  module DTK::Client
@@ -242,10 +242,10 @@ module DTK::Client
242
242
  post rest_url("node/set_attributes"), post_body
243
243
  end
244
244
 
245
- desc "NODE-NAME/ID set-required-params", "Interactive dialog to set required params that are not currently set"
246
- def set_required_params(context_params)
245
+ desc "NODE-NAME/ID set-required-attributes", "Interactive dialog to set required attributes that are not currently set"
246
+ def set_required_attributes(context_params)
247
247
  node_id = context_params.retrieve_arguments([:node_id!],method_argument_names)
248
- set_required_params_aux(node_id,:node)
248
+ set_required_attributes_aux(node_id,:node)
249
249
  end
250
250
 
251
251
  # desc "NODE-NAME/ID create-component COMPONENT-TEMPLATE-NAME/ID [-v VERSION]", "Add component template to node"
@@ -1,5 +1,5 @@
1
1
  dtk_require_common_commands('thor/task_status')
2
- dtk_require_common_commands('thor/set_required_params')
2
+ dtk_require_common_commands('thor/set_required_attributes')
3
3
  module DTK::Client
4
4
  class NodeGroup < CommandBaseThor
5
5
  no_tasks do
@@ -27,10 +27,10 @@ module DTK::Client
27
27
  post rest_url("node_group/set_attributes"), post_body
28
28
  end
29
29
 
30
- desc "NODE-GROUP-NAME/ID set-required-params", "Interactive dialog to set required params that are not currently set"
31
- def set_required_params(context_params)
30
+ desc "NODE-GROUP-NAME/ID set-required-attributes", "Interactive dialog to set required attributes that are not currently set"
31
+ def set_required_attributes(context_params)
32
32
  node_group_id = context_params.retrieve_arguments([:node_group_id!],method_argument_names)
33
- set_required_params_aux(node_group_id,:node_group)
33
+ set_required_attributes_aux(node_group_id,:node_group)
34
34
  end
35
35
 
36
36
  desc "create NODE-GROUP-NAME [-t TARGET-ID] [--spans-target]", "Create node group"
@@ -0,0 +1,32 @@
1
+ module DTK::Client
2
+ class Remotes < CommandBaseThor
3
+
4
+ def self.valid_children()
5
+ []
6
+ end
7
+
8
+ # REMOTE INTERACTION
9
+ desc "push-remote", "Push local changes to remote git repository"
10
+ method_option :force, :aliases => '--force', :type => :boolean, :default => false
11
+ def push_remote(context_params)
12
+ raise "NOT IMPLEMENTED"
13
+ end
14
+
15
+ desc "list-remotes", "List git remotes for given module"
16
+ def list_remotes(context_params)
17
+ raise "NOT IMPLEMENTED"
18
+ end
19
+
20
+ desc "add-remote", "Add git remote for given module"
21
+ def add_remote(context_params)
22
+ raise "NOT IMPLEMENTED"
23
+ end
24
+
25
+ desc "remove-remote", "Remove git remote for given module"
26
+ method_option :force, :aliases => '-y', :type => :boolean, :default => false
27
+ def remove_remote(context_params)
28
+ raise "NOT IMPLEMENTED"
29
+ end
30
+
31
+ end
32
+ end
@@ -6,7 +6,7 @@ dtk_require_from_base("dtk_logger")
6
6
  dtk_require_from_base("util/os_util")
7
7
  dtk_require_from_base("command_helper")
8
8
  dtk_require_common_commands('thor/task_status')
9
- dtk_require_common_commands('thor/set_required_params')
9
+ dtk_require_common_commands('thor/set_required_attributes')
10
10
  dtk_require_common_commands('thor/edit')
11
11
  dtk_require_common_commands('thor/purge_clone')
12
12
  dtk_require_common_commands('thor/assembly_workspace')
@@ -149,7 +149,8 @@ module DTK::Client
149
149
  ['list-components',"list-components","# List components."]
150
150
  ],
151
151
  :utils => [
152
- ['execute-tests',"execute-tests [--component COMPONENT-NAME] [--timeout TIMEOUT]","# Execute tests. --component filters execution per component, --timeout changes default execution timeout."],
152
+ # TODO: DTK-2027 might subsume by the dtk actions; currently server changes does not support this command
153
+ # ['execute-tests',"execute-tests [--component COMPONENT-NAME] [--timeout TIMEOUT]","# Execute tests. --component filters execution per component, --timeout changes default execution timeout."],
153
154
  ['get-netstats',"get-netstats","# Get netstats."],
154
155
  ['get-ps',"get-ps [--filter PATTERN]","# Get ps."],
155
156
  ['grep',"grep LOG-PATH NODE-ID-PATTERN GREP-PATTERN [--first]","# Grep log from multiple nodes. --first option returns first match (latest log entry)."],
@@ -689,6 +690,8 @@ TODO: will put in dot release and will rename to 'extend'
689
690
  get_netstats_aux(context_params)
690
691
  end
691
692
 
693
+ =begin
694
+ # TODO: DTK-2027 might subsume by the dtk actions; currently server changes does not support this command
692
695
  # using HIDE_FROM_BASE to hide this command from base context (dtk:/assembly>)
693
696
  desc "HIDE_FROM_BASE execute-tests [--component COMPONENT-NAME] [--timeout TIMEOUT]", "Execute tests. --component filters execution per component, --timeout changes default execution timeout"
694
697
  method_option :component, :type => :string, :desc => "Component name"
@@ -696,6 +699,7 @@ TODO: will put in dot release and will rename to 'extend'
696
699
  def execute_tests(context_params)
697
700
  execute_tests_aux(context_params)
698
701
  end
702
+ =end
699
703
 
700
704
  # using HIDE_FROM_BASE to hide this command from base context (dtk:/assembly>)
701
705
  desc "HIDE_FROM_BASE get-ps [--filter PATTERN]", "Get ps"
@@ -704,10 +708,10 @@ TODO: will put in dot release and will rename to 'extend'
704
708
  get_ps_aux(context_params)
705
709
  end
706
710
 
707
- desc "SERVICE-NAME/ID set-required-params", "Interactive dialog to set required params that are not currently set"
708
- def set_required_params(context_params)
711
+ desc "SERVICE-NAME/ID set-required-attributes", "Interactive dialog to set required attributes that are not currently set"
712
+ def set_required_attributes(context_params)
709
713
  assembly_id = context_params.retrieve_arguments([:service_id!],method_argument_names)
710
- set_required_params_aux(assembly_id,:assembly,:instance)
714
+ set_required_attributes_aux(assembly_id,:assembly,:instance)
711
715
  end
712
716
 
713
717
  # using HIDE_FROM_BASE to hide this command from base context (dtk:/assembly>)
@@ -26,11 +26,15 @@ module DTK::Client
26
26
  end
27
27
 
28
28
  def self.valid_children()
29
- [:"assembly"]
29
+ [:assembly, :remotes]
30
30
  end
31
31
 
32
32
  def self.all_children()
33
- [:"assembly"]
33
+ [:assembly]
34
+ end
35
+
36
+ def self.multi_context_children()
37
+ [[:assembly], [:remotes], [:assembly, :remotes]]
34
38
  end
35
39
 
36
40
  def self.valid_child?(name_of_sub_context)
@@ -55,8 +59,14 @@ module DTK::Client
55
59
  :self => [
56
60
  ["list"," list [--remote] [--diff] [-n NAMESPACE]","# List service modules (local/remote). Use --diff to compare loaded and remote modules."]
57
61
  ],
58
- :"assembly" => [
62
+ :assembly => [
59
63
  ["list","list","# List assemblies for given service module."]
64
+ ],
65
+ :remotes => [
66
+ ["push-remote", "push-remote [REMOTE-NAME] [--force]", "# Push local changes to remote git repository"],
67
+ ["list-remotes", "list-remotes", "# List git remotes for given module"],
68
+ ["add-remote", "add-remote REMOTE-NAME REMOTE-URL", "# Add git remote for given module"],
69
+ ["remove-remote", "remove-remote REPO-NAME [-y]", "# Remove git remote for given module"]
60
70
  ]
61
71
  },
62
72
  :identifier_only => {
@@ -64,7 +74,7 @@ module DTK::Client
64
74
  ["list-assemblies","list-assemblies","# List assemblies associated with service module."],
65
75
  ["list-modules","list-modules","# List modules associated with service module."]
66
76
  ],
67
- :"assembly" => [
77
+ :assembly => [
68
78
  ["info","info","# Info for given assembly in current service module."],
69
79
  ["stage", "stage [INSTANCE-NAME] [-t TARGET-NAME/ID]", "# Stage assembly in target."],
70
80
  # ["deploy","deploy [-v VERSION] [INSTANCE-NAME] [-t TARGET-NAME/ID] [-m COMMIT-MSG]", "# Stage and deploy assembly in target."],
@@ -522,7 +532,7 @@ module DTK::Client
522
532
  end
523
533
 
524
534
  # desc "SERVICE-MODULE-NAME/ID push-dtkn [-n NAMESPACE] [-m COMMIT-MSG]", "Push changes from local copy of service module to remote repository (dtkn)."
525
- desc "SERVICE-MODULE-NAME/ID push-dtkn [-n NAMESPACE]", "Push changes from local copy of service module to remote repository (dtkn)."
535
+ desc "SERVICE-MODULE-NAME/ID push-dtkn [-n NAMESPACE] [--force]", "Push changes from local copy of service module to remote repository (dtkn)."
526
536
  method_option "message",:aliases => "-m" ,
527
537
  :type => :string,
528
538
  :banner => "COMMIT-MSG",
@@ -531,6 +541,7 @@ module DTK::Client
531
541
  :type => :string,
532
542
  :banner => "NAMESPACE",
533
543
  :desc => "Remote namespace"
544
+ method_option :force, :type => :boolean, :default => false, :aliases => '-f'
534
545
  def push_dtkn(context_params, internal_trigger=false)
535
546
  push_dtkn_module_aux(context_params, internal_trigger)
536
547
  end
@@ -569,6 +580,31 @@ module DTK::Client
569
580
  response
570
581
  end
571
582
 
583
+
584
+ # REMOTE INTERACTION
585
+
586
+ desc "HIDE_FROM_BASE push-remote [REMOTE-NAME] [--force]", "Push local changes to remote git repository"
587
+ method_option :force, :type => :boolean, :default => false
588
+ def push_remote(context_params)
589
+ push_remote_module_aux(context_params)
590
+ end
591
+
592
+ desc "HIDE_FROM_BASE list-remotes", "List git remotes for given module"
593
+ def list_remotes(context_params)
594
+ remote_list_aux(context_params)
595
+ end
596
+
597
+ desc "HIDE_FROM_BASE add-remote REMOTE-NAME REMOTE-URL", "Add git remote for given module"
598
+ def add_remote(context_params)
599
+ remote_add_aux(context_params)
600
+ end
601
+
602
+ desc "HIDE_FROM_BASE remove-remote REPO-NAME [-y]", "Remove git remote for given module"
603
+ method_option :force, :aliases => '-y', :type => :boolean, :default => false
604
+ def remove_remote(context_params)
605
+ remote_remove_aux(context_params)
606
+ end
607
+
572
608
  #
573
609
  # DEVELOPMENT MODE METHODS
574
610
  #