MuranoCLI 2.2.4 → 3.0.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (159) hide show
  1. checksums.yaml +4 -4
  2. data/.agignore +3 -0
  3. data/.gitignore +18 -1
  4. data/.rubocop.yml +222 -0
  5. data/.trustme.sh +185 -0
  6. data/.trustme.vim +24 -0
  7. data/Gemfile +23 -4
  8. data/LICENSE.txt +1 -1
  9. data/MuranoCLI.gemspec +43 -8
  10. data/README.markdown +9 -11
  11. data/Rakefile +187 -143
  12. data/TODO.taskpaper +2 -2
  13. data/bin/murano +51 -52
  14. data/docs/basic_example.rst +436 -0
  15. data/docs/completions/murano_completion-bash +3484 -0
  16. data/docs/demo.md +32 -32
  17. data/docs/develop.rst +391 -0
  18. data/lib/MrMurano.rb +21 -7
  19. data/lib/MrMurano/Account.rb +159 -174
  20. data/lib/MrMurano/Business.rb +381 -0
  21. data/lib/MrMurano/Config-Migrate.rb +32 -26
  22. data/lib/MrMurano/Config.rb +407 -128
  23. data/lib/MrMurano/Content.rb +191 -0
  24. data/lib/MrMurano/Gateway.rb +489 -0
  25. data/lib/MrMurano/Keystore.rb +48 -0
  26. data/lib/MrMurano/Passwords.rb +103 -0
  27. data/lib/MrMurano/ProjectFile.rb +121 -79
  28. data/lib/MrMurano/ReCommander.rb +114 -10
  29. data/lib/MrMurano/Setting.rb +90 -0
  30. data/lib/MrMurano/Solution-ServiceConfig.rb +89 -45
  31. data/lib/MrMurano/Solution-Services.rb +461 -166
  32. data/lib/MrMurano/Solution-Users.rb +70 -31
  33. data/lib/MrMurano/Solution.rb +372 -13
  34. data/lib/MrMurano/SolutionId.rb +73 -0
  35. data/lib/MrMurano/SyncRoot.rb +137 -0
  36. data/lib/MrMurano/SyncUpDown.rb +594 -284
  37. data/lib/MrMurano/Webservice-Cors.rb +71 -0
  38. data/lib/MrMurano/Webservice-Endpoint.rb +234 -0
  39. data/lib/MrMurano/Webservice-File.rb +193 -0
  40. data/lib/MrMurano/Webservice.rb +51 -0
  41. data/lib/MrMurano/commands.rb +18 -15
  42. data/lib/MrMurano/commands/business.rb +300 -6
  43. data/lib/MrMurano/commands/completion-bash.erb +166 -0
  44. data/lib/MrMurano/commands/{zshcomplete.erb → completion-zsh.erb} +0 -0
  45. data/lib/MrMurano/commands/completion.rb +76 -39
  46. data/lib/MrMurano/commands/config.rb +108 -44
  47. data/lib/MrMurano/commands/content.rb +115 -72
  48. data/lib/MrMurano/commands/cors.rb +29 -14
  49. data/lib/MrMurano/commands/devices.rb +286 -0
  50. data/lib/MrMurano/commands/domain.rb +52 -12
  51. data/lib/MrMurano/commands/gb.rb +24 -9
  52. data/lib/MrMurano/commands/globals.rb +64 -0
  53. data/lib/MrMurano/commands/init.rb +377 -155
  54. data/lib/MrMurano/commands/keystore.rb +92 -82
  55. data/lib/MrMurano/commands/link.rb +300 -0
  56. data/lib/MrMurano/commands/login.rb +74 -11
  57. data/lib/MrMurano/commands/logs.rb +63 -32
  58. data/lib/MrMurano/commands/mock.rb +57 -29
  59. data/lib/MrMurano/commands/password.rb +57 -39
  60. data/lib/MrMurano/commands/postgresql.rb +127 -94
  61. data/lib/MrMurano/commands/settings.rb +203 -0
  62. data/lib/MrMurano/commands/show.rb +79 -38
  63. data/lib/MrMurano/commands/solution.rb +423 -5
  64. data/lib/MrMurano/commands/solution_picker.rb +547 -0
  65. data/lib/MrMurano/commands/status.rb +195 -61
  66. data/lib/MrMurano/commands/sync.rb +78 -39
  67. data/lib/MrMurano/commands/timeseries.rb +71 -55
  68. data/lib/MrMurano/commands/tsdb.rb +113 -87
  69. data/lib/MrMurano/commands/usage.rb +57 -15
  70. data/lib/MrMurano/hash.rb +100 -10
  71. data/lib/MrMurano/http.rb +187 -43
  72. data/lib/MrMurano/makePretty.rb +16 -14
  73. data/lib/MrMurano/optparse.rb +2178 -0
  74. data/lib/MrMurano/progress.rb +138 -0
  75. data/lib/MrMurano/schema/resource-v1.0.0.yaml +32 -0
  76. data/lib/MrMurano/template/projectFile.murano.erb +16 -13
  77. data/lib/MrMurano/verbosing.rb +166 -29
  78. data/lib/MrMurano/version.rb +30 -1
  79. data/spec/Account-Passwords_spec.rb +21 -4
  80. data/spec/Account_spec.rb +69 -146
  81. data/spec/Business_spec.rb +290 -0
  82. data/spec/ConfigFile_spec.rb +1 -0
  83. data/spec/ConfigMigrate_spec.rb +12 -8
  84. data/spec/Config_spec.rb +40 -34
  85. data/spec/Content_spec.rb +363 -0
  86. data/spec/GatewayBase_spec.rb +54 -0
  87. data/spec/GatewayDevice_spec.rb +321 -0
  88. data/spec/GatewayResource_spec.rb +266 -0
  89. data/spec/GatewaySettings_spec.rb +120 -0
  90. data/spec/Http_spec.rb +18 -8
  91. data/spec/Mock_spec.rb +2 -2
  92. data/spec/ProjectFile_spec.rb +25 -14
  93. data/spec/Setting_spec.rb +110 -0
  94. data/spec/Solution-ServiceConfig_spec.rb +44 -5
  95. data/spec/Solution-ServiceEventHandler_spec.rb +23 -14
  96. data/spec/Solution-ServiceModules_spec.rb +47 -37
  97. data/spec/Solution-UsersRoles_spec.rb +10 -8
  98. data/spec/Solution_spec.rb +17 -8
  99. data/spec/SyncRoot_spec.rb +46 -20
  100. data/spec/SyncUpDown_spec.rb +437 -201
  101. data/spec/Verbosing_spec.rb +12 -4
  102. data/spec/{Solution-Cors_spec.rb → Webservice-Cors_spec.rb} +23 -20
  103. data/spec/{Solution-Endpoint_spec.rb → Webservice-Endpoint_spec.rb} +43 -41
  104. data/spec/{Solution-File_spec.rb → Webservice-File_spec.rb} +44 -33
  105. data/spec/Webservice-Setting_spec.rb +89 -0
  106. data/spec/_workspace.rb +4 -4
  107. data/spec/cmd_business_spec.rb +9 -4
  108. data/spec/cmd_common.rb +44 -1
  109. data/spec/cmd_content_spec.rb +43 -17
  110. data/spec/cmd_cors_spec.rb +4 -4
  111. data/spec/cmd_device_spec.rb +61 -16
  112. data/spec/cmd_domain_spec.rb +29 -6
  113. data/spec/cmd_init_spec.rb +281 -126
  114. data/spec/cmd_keystore_spec.rb +3 -3
  115. data/spec/cmd_link_spec.rb +98 -0
  116. data/spec/cmd_password_spec.rb +1 -1
  117. data/spec/cmd_setting_application_spec.rb +260 -0
  118. data/spec/cmd_setting_product_spec.rb +220 -0
  119. data/spec/cmd_status_spec.rb +223 -114
  120. data/spec/cmd_syncdown_spec.rb +115 -35
  121. data/spec/cmd_syncup_spec.rb +68 -15
  122. data/spec/cmd_usage_spec.rb +35 -8
  123. data/spec/fixtures/dumped_config +6 -4
  124. data/spec/fixtures/gateway_resource_files/resources.notyaml +12 -0
  125. data/spec/fixtures/gateway_resource_files/resources.yaml +13 -0
  126. data/spec/fixtures/gateway_resource_files/resources_invalid.yaml +13 -0
  127. data/spec/fixtures/mrmuranorc_deleted_bob +0 -2
  128. data/spec/fixtures/product_spec_files/lightbulb.yaml +20 -13
  129. data/spec/fixtures/{syncable_content → syncable_conflict}/services/devdata.lua +1 -1
  130. data/spec/fixtures/{syncable_content → syncable_conflict}/services/timers.lua +0 -0
  131. data/spec/spec_helper.rb +5 -0
  132. metadata +262 -171
  133. data/bin/mr +0 -8
  134. data/lib/MrMurano/Product-1P-Device.rb +0 -145
  135. data/lib/MrMurano/Product-Resources.rb +0 -205
  136. data/lib/MrMurano/Product.rb +0 -358
  137. data/lib/MrMurano/Solution-Cors.rb +0 -47
  138. data/lib/MrMurano/Solution-Endpoint.rb +0 -191
  139. data/lib/MrMurano/Solution-File.rb +0 -166
  140. data/lib/MrMurano/commands/assign.rb +0 -57
  141. data/lib/MrMurano/commands/businessList.rb +0 -45
  142. data/lib/MrMurano/commands/product.rb +0 -14
  143. data/lib/MrMurano/commands/productCreate.rb +0 -39
  144. data/lib/MrMurano/commands/productDelete.rb +0 -33
  145. data/lib/MrMurano/commands/productDevice.rb +0 -87
  146. data/lib/MrMurano/commands/productDeviceIdCmds.rb +0 -89
  147. data/lib/MrMurano/commands/productList.rb +0 -45
  148. data/lib/MrMurano/commands/productWrite.rb +0 -27
  149. data/lib/MrMurano/commands/solutionCreate.rb +0 -41
  150. data/lib/MrMurano/commands/solutionDelete.rb +0 -34
  151. data/lib/MrMurano/commands/solutionList.rb +0 -45
  152. data/spec/ProductBase_spec.rb +0 -113
  153. data/spec/ProductContent_spec.rb +0 -162
  154. data/spec/ProductResources_spec.rb +0 -329
  155. data/spec/Product_1P_Device_spec.rb +0 -202
  156. data/spec/Product_1P_RPC_spec.rb +0 -175
  157. data/spec/Product_spec.rb +0 -153
  158. data/spec/Solution-ServiceDevice_spec.rb +0 -176
  159. data/spec/cmd_assign_spec.rb +0 -51
@@ -1,88 +1,83 @@
1
- require 'MrMurano/Solution-ServiceConfig'
2
-
3
- module MrMurano
4
- class Keystore < ServiceConfig
5
- def initialize
6
- super
7
- @serviceName = 'keystore'
8
- end
9
-
10
- def keyinfo
11
- call(:info)
12
- end
13
-
14
- def listkeys
15
- ret = call(:list)
16
- ret[:keys]
17
- end
18
-
19
- def getkey(key)
20
- ret = call(:get, :post, {:key=>key})
21
- ret[:value]
22
- end
23
-
24
- def setkey(key, value)
25
- call(:set, :post, { :key=>key, :value=>value })
26
- end
27
-
28
- def delkey(key)
29
- call(:delete, :post, {:key=>key})
30
- end
31
-
32
- def command(key, cmd, args)
33
- call(:command, :post, {:key=>key, :command=>cmd, :args=>args})
34
- end
1
+ # Last Modified: 2017.08.16 /coding: utf-8
2
+ # frozen_string_literal: true
35
3
 
36
- def clearall()
37
- call(:clear, :post, {})
38
- end
4
+ # Copyright © 2016-2017 Exosite LLC.
5
+ # License: MIT. See LICENSE.txt.
6
+ # vim:tw=0:ts=2:sw=2:et:ai
39
7
 
40
- end
41
- end
8
+ require 'MrMurano/Keystore'
9
+ require 'MrMurano/ReCommander'
10
+ require 'MrMurano/Solution-ServiceConfig'
42
11
 
43
12
  command :keystore do |c|
44
- c.syntax = %{murano keystore}
45
- c.summary = %{About Keystore}
46
- c.description = %{The Keystore sub-commands let you interact directly with the Keystore instance
47
- in a solution. This allows for easier debugging, being able to quickly get and
48
- set data. As well as calling any of the other supported REDIS commands.}
49
- c.action do |args, options|
13
+ c.syntax = %(murano keystore)
14
+ c.summary = %(About Keystore)
15
+ c.description = %(
16
+ The Keystore sub-commands let you interact directly with the Keystore instance
17
+ in a solution. This allows for easier debugging, being able to quickly get and
18
+ set data. As well as calling any of the other supported REDIS commands.
19
+ ).strip
20
+ c.project_not_required = true
21
+
22
+ c.action do |_args, _options|
50
23
  ::Commander::UI.enable_paging
51
24
  say MrMurano::SubCmdGroupHelp.new(c).get_help
52
25
  end
53
26
  end
54
27
 
55
28
  command 'keystore clearAll' do |c|
56
- c.syntax = %{murano keystore clearAll}
57
- c.description = %{Delete all keys in the keystore}
58
- c.action do |args,options|
29
+ c.syntax = %(murano keystore clearAll)
30
+ c.summary = %(Delete all keys in the keystore)
31
+ c.description = %(
32
+ Delete all keys in the keystore.
33
+ ).strip
34
+
35
+ c.action do |args, _options|
36
+ c.verify_arg_count!(args)
59
37
  sol = MrMurano::Keystore.new
60
38
  sol.clearall
61
39
  end
62
40
  end
63
41
 
64
42
  command 'keystore info' do |c|
65
- c.syntax = %{murano keystore info}
66
- c.description = %{Show info about the Keystore}
67
- c.action do |args,options|
43
+ c.syntax = %(murano keystore info)
44
+ c.summary = %(Show info about the Keystore)
45
+ c.description = %(
46
+ Show info about the Keystore.
47
+ ).strip
48
+
49
+ c.action do |args, _options|
50
+ c.verify_arg_count!(args)
68
51
  sol = MrMurano::Keystore.new
69
52
  sol.outf sol.keyinfo
70
53
  end
71
54
  end
72
55
 
73
56
  command 'keystore list' do |c|
74
- c.syntax = %{murano keystore list}
75
- c.description = %{List all of the keys in the Keystore}
76
- c.action do |args,options|
57
+ c.syntax = %(murano keystore list)
58
+ c.summary = %(List all of the keys in the Keystore)
59
+ c.description = %(
60
+ List all of the keys in the Keystore.
61
+ ).strip
62
+
63
+ c.action do |args, _options|
64
+ c.verify_arg_count!(args)
77
65
  sol = MrMurano::Keystore.new
66
+ # FIXME/2017-06-14: This outputs nothing if not list, unlike other
67
+ # list commands that say, e.g., "No solutions found"
78
68
  sol.outf sol.listkeys
79
69
  end
80
70
  end
81
71
 
82
72
  command 'keystore get' do |c|
83
- c.syntax = %{murano keystore get <key>}
84
- c.description = %{Get the value of a key in the Keystore}
85
- c.action do |args,options|
73
+ c.syntax = %(murano keystore get <key>)
74
+ c.summary = %(Get the value of a key in the Keystore)
75
+ c.description = %(
76
+ Get the value of a key in the Keystore.
77
+ ).strip
78
+
79
+ c.action do |args, _options|
80
+ c.verify_arg_count!(args, 1, ['Missing key'])
86
81
  sol = MrMurano::Keystore.new
87
82
  ret = sol.getkey(args[0])
88
83
  sol.outf ret
@@ -90,18 +85,31 @@ command 'keystore get' do |c|
90
85
  end
91
86
 
92
87
  command 'keystore set' do |c|
93
- c.syntax = %{murano keystore set <key> <value...>}
94
- c.description = %{Set the value of a key in the Keystore}
95
- c.action do |args,options|
88
+ c.syntax = %(murano keystore set <key> <value...>)
89
+ c.summary = %(Set the value of a key in the Keystore)
90
+ c.description = %(
91
+ Set the value of a key in the Keystore.
92
+ ).strip
93
+
94
+ c.action do |args, _options|
95
+ c.verify_arg_count!(args, nil, ['Missing key', 'Missing value(s)'])
96
96
  sol = MrMurano::Keystore.new
97
97
  sol.setkey(args[0], args[1..-1].join(' '))
98
98
  end
99
99
  end
100
100
 
101
101
  command 'keystore delete' do |c|
102
- c.syntax = %{murano keystore delete <key>}
103
- c.description = %{Delete a key from the Keystore}
104
- c.action do |args,options|
102
+ c.syntax = %(murano keystore delete <key>)
103
+ c.summary = %(Delete a key from the Keystore)
104
+ c.description = %(
105
+ Delete a key from the Keystore.
106
+ ).strip
107
+
108
+ # MAYBE?/2017-08-16: Verify on delete.
109
+ #c.option('-y', '--[no-]yes', %(Answer "yes" to all prompts and run non-interactively))
110
+
111
+ c.action do |args, _options|
112
+ c.verify_arg_count!(args, 1, ['Missing key'])
105
113
  sol = MrMurano::Keystore.new
106
114
  sol.delkey(args[0])
107
115
  end
@@ -110,28 +118,31 @@ alias_command 'keystore rm', 'keystore delete'
110
118
  alias_command 'keystore del', 'keystore delete'
111
119
 
112
120
  command 'keystore command' do |c|
113
- c.syntax = %{murano keystore command <command> <key> <args...>}
114
- c.summary = %{Call some Redis commands in the Keystore}
115
- c.description = %{Call some Redis commands in the Keystore.
121
+ c.syntax = %(murano keystore command <command> <key> [<args...>])
122
+ c.summary = %(Call some Redis commands in the Keystore)
123
+ c.description = %(
124
+ Call some Redis commands in the Keystore.
116
125
 
117
126
  Only a subset of all Redis commands is supported.
118
- See http://docs.exosite.com/murano/services/keystore/#command for current list.
119
- }
120
- c.example %{murano keystore command lpush mykey myvalue}, %{Push a value onto list}
121
- c.example %{murano keystore command lpush mykey A B C}, %{Push three values onto list}
122
- c.example %{murano keystore command lrem mykey 0 B}, %{Remove all B values from list}
123
- c.action do |args,options|
127
+
128
+ For current list, see:
129
+
130
+ http://docs.exosite.com/murano/services/keystore/#command
131
+ ).strip
132
+ c.example %(murano keystore command lpush mykey myvalue), %(Push a value onto list)
133
+ c.example %(murano keystore command lpush mykey A B C), %(Push three values onto list)
134
+ c.example %(murano keystore command lrem mykey 0 B), %(Remove all B values from list)
135
+
136
+ c.action do |args, _options|
137
+ #c.verify_arg_count!(args, nil, ['Missing command', 'Missing key', 'Missing value(s)'])
138
+ c.verify_arg_count!(args, nil, ['Missing command', 'Missing key'])
124
139
  sol = MrMurano::Keystore.new
125
- if args.count < 2 then
126
- sol.error "Not enough params"
140
+ ret = sol.command(args[1], args[0], args[2..-1])
141
+ if ret.key?(:value)
142
+ sol.outf ret[:value]
127
143
  else
128
- ret = sol.command(args[1], args[0], args[2..-1])
129
- if ret.has_key?(:value) then
130
- sol.outf ret[:value]
131
- else
132
- sol.error "#{ret[:code]}: #{ret.message}"
133
- sol.outf ret[:error] if ($cfg['tool.debug'] and ret.has_key?(:error))
134
- end
144
+ sol.error "#{ret[:code]}: #{ret.message}"
145
+ sol.outf ret[:error] if $cfg['tool.debug'] && ret.key?(:error)
135
146
  end
136
147
  end
137
148
  end
@@ -154,4 +165,3 @@ alias_command 'keystore scard', 'keystore command', 'scard'
154
165
  alias_command 'keystore smembers', 'keystore command', 'smembers'
155
166
  alias_command 'keystore spop', 'keystore command', 'spop'
156
167
 
157
- # vim: set ai et sw=2 ts=2 :
@@ -0,0 +1,300 @@
1
+ # Last Modified: 2017.08.17 /coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ # Copyright © 2016-2017 Exosite LLC.
5
+ # License: MIT. See LICENSE.txt.
6
+ # vim:tw=0:ts=2:sw=2:et:ai
7
+
8
+ require 'MrMurano/Account'
9
+ require 'MrMurano/ReCommander'
10
+ require 'MrMurano/Solution'
11
+ require 'MrMurano/Solution-ServiceConfig'
12
+ require 'MrMurano/SolutionId'
13
+
14
+ MSG_SERVICE_LINKS_NONE_FOUND = 'No service links found' unless defined? MSG_SERVICE_LINKS_NONE_FOUND
15
+
16
+ command 'link' do |c|
17
+ c.syntax = %(murano link)
18
+ c.summary = %(Use the link commands to manage solution links)
19
+ c.description = %(
20
+ Use the link commands to manage solution links.
21
+ ).strip
22
+ c.project_not_required = true
23
+
24
+ c.action do |_args, _options|
25
+ ::Commander::UI.enable_paging
26
+ say(MrMurano::SubCmdGroupHelp.new(c).get_help)
27
+ end
28
+ end
29
+
30
+ command 'link list' do |c|
31
+ c.syntax = 'murano link list [--options]'
32
+ c.summary = %(List the solutions that are linked)
33
+ c.description = %(
34
+ List the solutions that are linked.
35
+ ).strip
36
+
37
+ # MAYBE: Here and elsewhere: hyphenate, e.g., --id-only
38
+ c.option '--idonly', 'Only return the ids'
39
+ #c.option '--[no-]brief', 'Show fewer fields: only name, key, and service'
40
+ c.option '--[no-]full', 'Show all fields, not just name, key, and service'
41
+ c.option '--[no-]all', 'Show links for all Solutions in Business, not just Project'
42
+
43
+ c.action do |args, options|
44
+ c.verify_arg_count!(args)
45
+
46
+ MrMurano::Verbose.whirly_start('Fetching product list...')
47
+ biz = MrMurano::Business.new
48
+ products = biz.products
49
+ MrMurano::Verbose.whirly_stop
50
+ pids = products.map(&:apiId)
51
+
52
+ sol_opts = { biz: biz, type: :application }
53
+ sol_opts[:match_sid] = $cfg['application.id'] unless options.all
54
+ appl = solution_find_or_create(**sol_opts)
55
+
56
+ if !appl.nil?
57
+ MrMurano::Verbose.whirly_msg('Fetching application services...')
58
+ sercfg = MrMurano::ServiceConfig.new(appl.sid)
59
+ #scfgs = sercfg.list('?select=service,id,solution_id,script_key,alias')
60
+ scfgs = sercfg.list
61
+ MrMurano::Verbose.whirly_stop
62
+ else
63
+ sercfg = MrMurano::ServiceConfig.new(MrMurano::SolutionId::INVALID_SID)
64
+ scfgs = []
65
+ end
66
+
67
+ scfgs.select! { |s| pids.include? s[:service] }
68
+ # MAYBE/2017-08-16: filter by $cfg['product.id'] if !options.all
69
+
70
+ io = File.open(options.output, 'w') if options.output
71
+
72
+ if options.idonly
73
+ headers = [:service]
74
+ scfgs = scfgs.map { |row| [row[:service]] }
75
+ elsif !options.full
76
+ headers = %i[name script_key service]
77
+ scfgs = scfgs.map { |r| headers.map { |h| r[h] } }
78
+ else
79
+ headers = (scfgs.first || {}).keys
80
+ scfgs = scfgs.map { |r| headers.map { |h| r[h] } }
81
+ end
82
+
83
+ sercfg.outf(scfgs, io) do |dd, ios|
84
+ if options.idonly
85
+ ios.puts dd.join(' ')
86
+ elsif dd.any?
87
+ MrMurano::Verbose.tabularize(
88
+ {
89
+ headers: headers.map(&:to_s),
90
+ rows: dd,
91
+ },
92
+ ios,
93
+ )
94
+ else
95
+ MrMurano::Verbose.error(MSG_SERVICE_LINKS_NONE_FOUND)
96
+ exit 0
97
+ end
98
+ end
99
+ io.close unless io.nil?
100
+ end
101
+ end
102
+ alias_command 'assign list', 'link list'
103
+ alias_command 'links list', 'link list'
104
+
105
+ command 'link set' do |c|
106
+ c.syntax = 'murano link set [--options] [<product-id> [<application-id>]]'
107
+ c.summary = %(Link a solution to an event handler)
108
+ c.description = %(
109
+ Link a solution to an event handler of another solution.
110
+ ).strip
111
+
112
+ # Add soln pickers: --application* and --product*
113
+ cmd_option_application_pickers(c)
114
+ cmd_option_product_pickers(c)
115
+
116
+ c.action do |args, options|
117
+ c.verify_arg_count!(args, 2)
118
+ pid = args.shift
119
+ aid = args.shift
120
+ sol_a, sol_b = get_two_solutions!(aid, pid, **options.__hash__, skip_verify: false)
121
+ link_opts = { warn_on_conflict: true }
122
+ link_solutions(sol_a, sol_b, link_opts)
123
+ end
124
+ end
125
+ alias_command 'assign set', 'link set'
126
+
127
+ command 'link unset' do |c|
128
+ c.syntax = 'murano link unset [--options] [<product-id> [<application-id>]]'
129
+ c.summary = %(Unlink a solution from an event handler)
130
+ c.description = %(
131
+ Unlink a solution from an event handler of another solution.
132
+ ).strip
133
+
134
+ # Add soln pickers: --application* and --product*
135
+ cmd_option_application_pickers(c)
136
+ cmd_option_product_pickers(c)
137
+
138
+ c.action do |args, options|
139
+ c.verify_arg_count!(args, 2)
140
+ pid = args.shift
141
+ aid = args.shift
142
+ sol_a, sol_b = get_two_solutions!(aid, pid, **options.__hash__, skip_verify: false)
143
+ unlink_solutions(sol_a, sol_b)
144
+ end
145
+ end
146
+ alias_command 'assign unset', 'link unset'
147
+
148
+ def link_solutions(sol_a, sol_b, options)
149
+ warn_on_conflict = options[:warn_on_conflict] || false
150
+ verbose = options[:verbose] || false
151
+
152
+ if sol_a.nil? || sol_a.sid.to_s.empty? || sol_b.nil? || sol_b.sid.to_s.empty?
153
+ msg = 'Missing Solution(s) (Applications or Products): Nothing to link'
154
+ if warn_on_conflict
155
+ sercfg.warning msg
156
+ else
157
+ say(msg)
158
+ end
159
+ return
160
+ end
161
+
162
+ if sol_a.is_a?(MrMurano::Product) && sol_b.is_a?(MrMurano::Application)
163
+ # If the order is backwards, Murano will return
164
+ # Net::HTTPFailedDependency/424 "No such Service XXX"
165
+ tmp = sol_a
166
+ sol_a = sol_b
167
+ sol_b = tmp
168
+ # MAYBE/2017-08-16: Are there plans for linking other types of things?
169
+ # What about Application to Application, or Product to Product?
170
+ end
171
+
172
+ # Get services for solution to which being linked (application),
173
+ # and look for linkee (product) service.
174
+ sercfg = MrMurano::ServiceConfig.new(sol_a.sid)
175
+ MrMurano::Verbose.whirly_msg 'Fetching services...'
176
+ scfgs = sercfg.search(sol_b.sid)
177
+ svc_cfg_exists = scfgs.any?
178
+ MrMurano::Verbose.whirly_stop
179
+
180
+ # Create the service configuration.
181
+ unless svc_cfg_exists
182
+ MrMurano::Verbose.whirly_msg 'Linking solutions...'
183
+ # Call Murano.
184
+ _ret = sercfg.create(sol_b.sid, sol_b.name) do |request, http|
185
+ response = http.request(request)
186
+ MrMurano::Verbose.whirly_stop
187
+ if response.is_a?(Net::HTTPSuccess)
188
+ say("Linked #{sol_b.quoted_name} to #{sol_a.quoted_name}")
189
+ elsif response.is_a?(Net::HTTPConflict)
190
+ svc_cfg_exists = true
191
+ else
192
+ MrMurano::Verbose.error(
193
+ "Unable to link solutions: ‘#{Rainbow(response.message).underline}’"
194
+ )
195
+ sercfg.showHttpError(request, response)
196
+ end
197
+ end
198
+ end
199
+ if svc_cfg_exists
200
+ msg = 'Solutions already linked'
201
+ if warn_on_conflict
202
+ sercfg.warning msg
203
+ else
204
+ say(msg)
205
+ end
206
+ end
207
+ puts '' if verbose
208
+
209
+ # Get event handlers for application, and look for product event handler.
210
+ MrMurano::Verbose.whirly_msg 'Fetching handlers...'
211
+ evthlr = MrMurano::EventHandlerSolnApp.new(sol_a.sid)
212
+ hdlrs = evthlr.search(sol_b.sid)
213
+ evt_hlr_exists = hdlrs.any?
214
+ MrMurano::Verbose.whirly_stop
215
+
216
+ # Create the event handler, using a simple script,
217
+ # like the web UI does (yeti yeti spaghetti).
218
+ unless evt_hlr_exists
219
+ MrMurano::Verbose.whirly_msg 'Setting default event handler...'
220
+ # Call Murano.
221
+ evthlr.default_event_script(sol_b.sid) do |request, http|
222
+ response = http.request(request)
223
+ MrMurano::Verbose.whirly_stop
224
+ if response.is_a?(Net::HTTPSuccess)
225
+ say('Created default event handler')
226
+ elsif response.is_a?(Net::HTTPConflict)
227
+ evt_hlr_exists = true
228
+ else
229
+ MrMurano::Verbose.error(
230
+ "Failed to create default event handler: ‘#{Rainbow(response.message).underline}’"
231
+ )
232
+ evthlr.showHttpError(request, response)
233
+ end
234
+ end
235
+ end
236
+ if evt_hlr_exists
237
+ msg = 'Event handler already created'
238
+ if warn_on_conflict
239
+ sercfg.warning msg
240
+ else
241
+ say(msg)
242
+ end
243
+ end
244
+ puts '' if verbose
245
+ end
246
+
247
+ def unlink_solutions(sol_a, sol_b)
248
+ sercfg = MrMurano::ServiceConfig.new(sol_a.sid)
249
+ MrMurano::Verbose.whirly_msg 'Fetching services...'
250
+ #scfgs = sercfg.list('?select=service,id,solution_id,script_key,alias')
251
+ scfgs = sercfg.search(sol_b.sid)
252
+ MrMurano::Verbose.whirly_stop
253
+
254
+ if scfgs.length > 1
255
+ sercfg.warning "More than one service configuration found: #{scfgs}"
256
+ elsif scfgs.empty?
257
+ sercfg.warning 'No matching service configurations found; nothing to unlink'
258
+ #exit 1
259
+ end
260
+
261
+ sercfg.debug "Found #{scfgs.length} configurations to unlink from the Application"
262
+
263
+ scfgs.each do |svc|
264
+ sercfg.debug "Deleting #{svc[:service]} : #{svc[:script_key]} : #{svc[:id]}"
265
+ ret = sercfg.remove(svc[:id])
266
+ if !ret.nil?
267
+ msg = "Unlinked ‘#{svc[:script_key]}’"
268
+ msg += " from #{sol_a.quoted_name}" unless sol_a.quoted_name.to_s.empty?
269
+ say(msg)
270
+ else
271
+ sercfg.warning "Failed to unlink ‘#{svc[:id]}’"
272
+ end
273
+ end
274
+
275
+ MrMurano::Verbose.whirly_msg 'Fetching handlers...'
276
+ evthlr = MrMurano::EventHandlerSolnApp.new(sol_a.sid)
277
+ hdlrs = evthlr.search(sol_b.sid)
278
+ #evt_hlr_exists = hdlrs.any?
279
+ MrMurano::Verbose.whirly_stop
280
+
281
+ if hdlrs.length > 1
282
+ sercfg.warning "More than one event handler found: #{hdlrs}"
283
+ elsif hdlrs.empty?
284
+ sercfg.warning 'No matching event handlers found; nothing to delete'
285
+ #exit 1
286
+ end
287
+
288
+ hdlrs.each do |evth|
289
+ evthlr.debug "Deleting #{evth[:service]} : #{evth[:alias]} : #{evth[:id]}"
290
+ ret = evthlr.remove(evth[:id])
291
+ if !ret.nil?
292
+ msg = "Removed ‘#{evth[:alias]}’"
293
+ msg += " from #{sol_a.quoted_name}" unless sol_a.quoted_name.to_s.empty?
294
+ say(msg)
295
+ else
296
+ MrMurano::Verbose.warning "Failed to remove handler ‘#{svc[:id]}’"
297
+ end
298
+ end
299
+ end
300
+