MuranoCLI 2.2.4 → 3.0.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.agignore +3 -0
- data/.gitignore +18 -1
- data/.rubocop.yml +222 -0
- data/.trustme.sh +185 -0
- data/.trustme.vim +24 -0
- data/Gemfile +23 -4
- data/LICENSE.txt +1 -1
- data/MuranoCLI.gemspec +43 -8
- data/README.markdown +9 -11
- data/Rakefile +187 -143
- data/TODO.taskpaper +2 -2
- data/bin/murano +51 -52
- data/docs/basic_example.rst +436 -0
- data/docs/completions/murano_completion-bash +3484 -0
- data/docs/demo.md +32 -32
- data/docs/develop.rst +391 -0
- data/lib/MrMurano.rb +21 -7
- data/lib/MrMurano/Account.rb +159 -174
- data/lib/MrMurano/Business.rb +381 -0
- data/lib/MrMurano/Config-Migrate.rb +32 -26
- data/lib/MrMurano/Config.rb +407 -128
- data/lib/MrMurano/Content.rb +191 -0
- data/lib/MrMurano/Gateway.rb +489 -0
- data/lib/MrMurano/Keystore.rb +48 -0
- data/lib/MrMurano/Passwords.rb +103 -0
- data/lib/MrMurano/ProjectFile.rb +121 -79
- data/lib/MrMurano/ReCommander.rb +114 -10
- data/lib/MrMurano/Setting.rb +90 -0
- data/lib/MrMurano/Solution-ServiceConfig.rb +89 -45
- data/lib/MrMurano/Solution-Services.rb +461 -166
- data/lib/MrMurano/Solution-Users.rb +70 -31
- data/lib/MrMurano/Solution.rb +372 -13
- data/lib/MrMurano/SolutionId.rb +73 -0
- data/lib/MrMurano/SyncRoot.rb +137 -0
- data/lib/MrMurano/SyncUpDown.rb +594 -284
- data/lib/MrMurano/Webservice-Cors.rb +71 -0
- data/lib/MrMurano/Webservice-Endpoint.rb +234 -0
- data/lib/MrMurano/Webservice-File.rb +193 -0
- data/lib/MrMurano/Webservice.rb +51 -0
- data/lib/MrMurano/commands.rb +18 -15
- data/lib/MrMurano/commands/business.rb +300 -6
- data/lib/MrMurano/commands/completion-bash.erb +166 -0
- data/lib/MrMurano/commands/{zshcomplete.erb → completion-zsh.erb} +0 -0
- data/lib/MrMurano/commands/completion.rb +76 -39
- data/lib/MrMurano/commands/config.rb +108 -44
- data/lib/MrMurano/commands/content.rb +115 -72
- data/lib/MrMurano/commands/cors.rb +29 -14
- data/lib/MrMurano/commands/devices.rb +286 -0
- data/lib/MrMurano/commands/domain.rb +52 -12
- data/lib/MrMurano/commands/gb.rb +24 -9
- data/lib/MrMurano/commands/globals.rb +64 -0
- data/lib/MrMurano/commands/init.rb +377 -155
- data/lib/MrMurano/commands/keystore.rb +92 -82
- data/lib/MrMurano/commands/link.rb +300 -0
- data/lib/MrMurano/commands/login.rb +74 -11
- data/lib/MrMurano/commands/logs.rb +63 -32
- data/lib/MrMurano/commands/mock.rb +57 -29
- data/lib/MrMurano/commands/password.rb +57 -39
- data/lib/MrMurano/commands/postgresql.rb +127 -94
- data/lib/MrMurano/commands/settings.rb +203 -0
- data/lib/MrMurano/commands/show.rb +79 -38
- data/lib/MrMurano/commands/solution.rb +423 -5
- data/lib/MrMurano/commands/solution_picker.rb +547 -0
- data/lib/MrMurano/commands/status.rb +195 -61
- data/lib/MrMurano/commands/sync.rb +78 -39
- data/lib/MrMurano/commands/timeseries.rb +71 -55
- data/lib/MrMurano/commands/tsdb.rb +113 -87
- data/lib/MrMurano/commands/usage.rb +57 -15
- data/lib/MrMurano/hash.rb +100 -10
- data/lib/MrMurano/http.rb +187 -43
- data/lib/MrMurano/makePretty.rb +16 -14
- data/lib/MrMurano/optparse.rb +2178 -0
- data/lib/MrMurano/progress.rb +138 -0
- data/lib/MrMurano/schema/resource-v1.0.0.yaml +32 -0
- data/lib/MrMurano/template/projectFile.murano.erb +16 -13
- data/lib/MrMurano/verbosing.rb +166 -29
- data/lib/MrMurano/version.rb +30 -1
- data/spec/Account-Passwords_spec.rb +21 -4
- data/spec/Account_spec.rb +69 -146
- data/spec/Business_spec.rb +290 -0
- data/spec/ConfigFile_spec.rb +1 -0
- data/spec/ConfigMigrate_spec.rb +12 -8
- data/spec/Config_spec.rb +40 -34
- data/spec/Content_spec.rb +363 -0
- data/spec/GatewayBase_spec.rb +54 -0
- data/spec/GatewayDevice_spec.rb +321 -0
- data/spec/GatewayResource_spec.rb +266 -0
- data/spec/GatewaySettings_spec.rb +120 -0
- data/spec/Http_spec.rb +18 -8
- data/spec/Mock_spec.rb +2 -2
- data/spec/ProjectFile_spec.rb +25 -14
- data/spec/Setting_spec.rb +110 -0
- data/spec/Solution-ServiceConfig_spec.rb +44 -5
- data/spec/Solution-ServiceEventHandler_spec.rb +23 -14
- data/spec/Solution-ServiceModules_spec.rb +47 -37
- data/spec/Solution-UsersRoles_spec.rb +10 -8
- data/spec/Solution_spec.rb +17 -8
- data/spec/SyncRoot_spec.rb +46 -20
- data/spec/SyncUpDown_spec.rb +437 -201
- data/spec/Verbosing_spec.rb +12 -4
- data/spec/{Solution-Cors_spec.rb → Webservice-Cors_spec.rb} +23 -20
- data/spec/{Solution-Endpoint_spec.rb → Webservice-Endpoint_spec.rb} +43 -41
- data/spec/{Solution-File_spec.rb → Webservice-File_spec.rb} +44 -33
- data/spec/Webservice-Setting_spec.rb +89 -0
- data/spec/_workspace.rb +4 -4
- data/spec/cmd_business_spec.rb +9 -4
- data/spec/cmd_common.rb +44 -1
- data/spec/cmd_content_spec.rb +43 -17
- data/spec/cmd_cors_spec.rb +4 -4
- data/spec/cmd_device_spec.rb +61 -16
- data/spec/cmd_domain_spec.rb +29 -6
- data/spec/cmd_init_spec.rb +281 -126
- data/spec/cmd_keystore_spec.rb +3 -3
- data/spec/cmd_link_spec.rb +98 -0
- data/spec/cmd_password_spec.rb +1 -1
- data/spec/cmd_setting_application_spec.rb +260 -0
- data/spec/cmd_setting_product_spec.rb +220 -0
- data/spec/cmd_status_spec.rb +223 -114
- data/spec/cmd_syncdown_spec.rb +115 -35
- data/spec/cmd_syncup_spec.rb +68 -15
- data/spec/cmd_usage_spec.rb +35 -8
- data/spec/fixtures/dumped_config +6 -4
- data/spec/fixtures/gateway_resource_files/resources.notyaml +12 -0
- data/spec/fixtures/gateway_resource_files/resources.yaml +13 -0
- data/spec/fixtures/gateway_resource_files/resources_invalid.yaml +13 -0
- data/spec/fixtures/mrmuranorc_deleted_bob +0 -2
- data/spec/fixtures/product_spec_files/lightbulb.yaml +20 -13
- data/spec/fixtures/{syncable_content → syncable_conflict}/services/devdata.lua +1 -1
- data/spec/fixtures/{syncable_content → syncable_conflict}/services/timers.lua +0 -0
- data/spec/spec_helper.rb +5 -0
- metadata +262 -171
- data/bin/mr +0 -8
- data/lib/MrMurano/Product-1P-Device.rb +0 -145
- data/lib/MrMurano/Product-Resources.rb +0 -205
- data/lib/MrMurano/Product.rb +0 -358
- data/lib/MrMurano/Solution-Cors.rb +0 -47
- data/lib/MrMurano/Solution-Endpoint.rb +0 -191
- data/lib/MrMurano/Solution-File.rb +0 -166
- data/lib/MrMurano/commands/assign.rb +0 -57
- data/lib/MrMurano/commands/businessList.rb +0 -45
- data/lib/MrMurano/commands/product.rb +0 -14
- data/lib/MrMurano/commands/productCreate.rb +0 -39
- data/lib/MrMurano/commands/productDelete.rb +0 -33
- data/lib/MrMurano/commands/productDevice.rb +0 -87
- data/lib/MrMurano/commands/productDeviceIdCmds.rb +0 -89
- data/lib/MrMurano/commands/productList.rb +0 -45
- data/lib/MrMurano/commands/productWrite.rb +0 -27
- data/lib/MrMurano/commands/solutionCreate.rb +0 -41
- data/lib/MrMurano/commands/solutionDelete.rb +0 -34
- data/lib/MrMurano/commands/solutionList.rb +0 -45
- data/spec/ProductBase_spec.rb +0 -113
- data/spec/ProductContent_spec.rb +0 -162
- data/spec/ProductResources_spec.rb +0 -329
- data/spec/Product_1P_Device_spec.rb +0 -202
- data/spec/Product_1P_RPC_spec.rb +0 -175
- data/spec/Product_spec.rb +0 -153
- data/spec/Solution-ServiceDevice_spec.rb +0 -176
- data/spec/cmd_assign_spec.rb +0 -51
@@ -1,47 +1,53 @@
|
|
1
|
-
|
1
|
+
# Last Modified: 2017.08.16 /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
|
+
|
2
8
|
require 'fileutils'
|
9
|
+
require 'pathname'
|
3
10
|
require 'yaml'
|
4
11
|
require 'MrMurano/verbosing'
|
5
12
|
require 'MrMurano/Account'
|
6
13
|
require 'MrMurano/Config'
|
7
14
|
|
8
|
-
|
9
15
|
module MrMurano
|
10
16
|
class ConfigMigrate
|
11
17
|
include Verbose
|
12
18
|
|
13
19
|
def import_secret
|
14
20
|
solsecret = Pathname.new($cfg['location.base']) + '.Solutionfile.secret'
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
21
|
+
return unless solsecret.exist?
|
22
|
+
# Is in JSON, which as a subset of YAML, so use YAML parser
|
23
|
+
solsecret.open do |io|
|
24
|
+
# rubocop:disable Security/YAMLLoad: Prefer using YAML.safe_load over YAML.load.
|
25
|
+
# MAYBE/2017-07-02: Switch to YAML.safe_load.
|
26
|
+
ss = YAML.load(io)
|
27
|
+
|
28
|
+
pff = $cfg.file_at('passwords', :user)
|
29
|
+
pwd = MrMurano::Passwords.new(pff)
|
30
|
+
pwd.load
|
31
|
+
ps = pwd.get($cfg['net.host'], ss['email'])
|
32
|
+
if ps.nil?
|
33
|
+
pwd.set($cfg['net.host'], ss['email'], ss['password'])
|
34
|
+
pwd.save
|
35
|
+
elsif ps != ss['password']
|
36
|
+
y = ask('A different password for this account already exists. Overwrite? N/y')
|
37
|
+
if y =~ /^y/i
|
25
38
|
pwd.set($cfg['net.host'], ss['email'], ss['password'])
|
26
39
|
pwd.save
|
27
|
-
elsif ps != ss['password'] then
|
28
|
-
y = ask("A different password for this account already exists. Overwrite? N/y")
|
29
|
-
if y =~ /^y/i then
|
30
|
-
pwd.set($cfg['net.host'], ss['email'], ss['password'])
|
31
|
-
pwd.save
|
32
|
-
end
|
33
|
-
else
|
34
|
-
# already set, nothing to do.
|
35
40
|
end
|
36
|
-
|
37
|
-
$cfg.set('user.name', ss['email'])
|
38
|
-
$cfg.set('solution.id', ss['solution_id']) if ss.has_key? 'solution_id'
|
39
|
-
$cfg.set('product.id', ss['product_id']) if ss.has_key? 'product_id'
|
41
|
+
# else, already set, nothing to do.
|
40
42
|
end
|
43
|
+
|
44
|
+
$cfg.set('user.name', ss['email'])
|
45
|
+
$project.refresh_user_name
|
46
|
+
|
47
|
+
$cfg.set('application.id', ss['solution_id']) if ss.key? 'solution_id'
|
48
|
+
$cfg.set('product.id', ss['product_id']) if ss.key? 'product_id'
|
41
49
|
end
|
42
50
|
end
|
43
|
-
|
44
51
|
end
|
45
52
|
end
|
46
53
|
|
47
|
-
# vim: set ai et sw=2 ts=2 :
|
data/lib/MrMurano/Config.rb
CHANGED
@@ -1,109 +1,147 @@
|
|
1
|
-
|
2
|
-
|
1
|
+
# Last Modified: 2017.08.18 /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
|
+
|
3
8
|
require 'highline'
|
9
|
+
require 'inifile'
|
10
|
+
require 'pathname'
|
11
|
+
require 'rainbow'
|
12
|
+
require 'MrMurano/verbosing'
|
13
|
+
require 'MrMurano/SyncRoot'
|
4
14
|
|
5
15
|
module MrMurano
|
6
16
|
class Config
|
7
|
-
|
8
|
-
|
9
|
-
#
|
10
|
-
#
|
11
|
-
#
|
12
|
-
#
|
13
|
-
#
|
14
|
-
#
|
17
|
+
include Verbose
|
18
|
+
|
19
|
+
# Config scopes:
|
20
|
+
# :internal transient this-run-only things (also -c options)
|
21
|
+
# :specified from --configfile
|
22
|
+
# :env from ENV['MURANO_CONFIGFILE']
|
23
|
+
# :project .murano/config at project dir
|
24
|
+
# :user .murano/config at $HOME
|
25
|
+
# :defaults Internal hardcoded defaults
|
26
|
+
# NOTE: This list is ordered, such that values stored in upper scopes
|
27
|
+
# mask values of the same keys in the lower scopes.
|
28
|
+
CFG_SCOPES = %i[internal specified env project user defaults].freeze
|
29
|
+
|
15
30
|
ConfigFile = Struct.new(:kind, :path, :data) do
|
16
|
-
def load
|
31
|
+
def load
|
17
32
|
return if kind == :internal
|
18
33
|
return if kind == :defaults
|
19
|
-
|
20
|
-
|
34
|
+
# DEVs: Uncomment if you're trying to figure where settings are coming from.
|
35
|
+
# See also: murano config --locations
|
36
|
+
#puts "Loading config at: #{path}"
|
37
|
+
self[:path] = Pathname.new(path) unless path.is_a? Pathname
|
38
|
+
self[:data] = IniFile.new(filename: path.to_s) if self[:data].nil?
|
21
39
|
self[:data].restore
|
22
40
|
end
|
23
41
|
|
24
|
-
def write
|
42
|
+
def write
|
25
43
|
return if kind == :internal
|
26
44
|
return if kind == :defaults
|
27
|
-
|
28
|
-
|
45
|
+
if defined?($cfg) && !$cfg.nil? && $cfg['tool.dry']
|
46
|
+
# $cfg.nil? when run from spec tests that don't load it with:
|
47
|
+
# include_context "CI_CMD"
|
48
|
+
MrMurano::Verbose.warning('--dry: Not writing config file')
|
49
|
+
return
|
50
|
+
end
|
51
|
+
self[:path] = Pathname.new(path) unless path.is_a?(Pathname)
|
52
|
+
# Ensure path to the file exists.
|
53
|
+
unless path.dirname.exist?
|
54
|
+
path.dirname.mkpath
|
55
|
+
MrMurano::Config.fix_modes(path.dirname)
|
56
|
+
end
|
57
|
+
self[:data] = IniFile.new(filename: path.to_s) if self[:data].nil?
|
29
58
|
self[:data].save
|
30
|
-
path.chmod(
|
59
|
+
path.chmod(0o600)
|
31
60
|
end
|
32
61
|
end
|
33
62
|
|
34
|
-
|
63
|
+
attr_reader :paths
|
35
64
|
attr_reader :projectDir
|
65
|
+
attr_reader :project_exists
|
66
|
+
attr_writer :exclude_scopes
|
67
|
+
attr_accessor :curlfile_f
|
36
68
|
|
37
|
-
|
69
|
+
CFG_ENV_NAME = %(MURANO_CONFIGFILE)
|
70
|
+
CFG_FILE_NAME = %(.murano/config)
|
71
|
+
CFG_DIR_NAME = %(.murano)
|
38
72
|
|
39
|
-
|
40
|
-
|
41
|
-
|
73
|
+
CFG_OLD_ENV_NAME = %(MR_CONFIGFILE)
|
74
|
+
CFG_OLD_DIR_NAME = %(.mrmurano)
|
75
|
+
CFG_OLD_FILE_NAME = %(.mrmuranorc)
|
42
76
|
|
43
|
-
|
44
|
-
CFG_OLD_DIR_NAME=%[.mrmurano].freeze
|
45
|
-
CFG_OLD_FILE_NAME=%[.mrmuranorc].freeze
|
77
|
+
CFG_SOLUTION_ID_KEYS = %w[application.id product.id].freeze
|
46
78
|
|
47
|
-
def
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
end
|
53
|
-
|
54
|
-
def migrateOldEnv
|
55
|
-
unless ENV[CFG_OLD_ENV_NAME].nil? then
|
56
|
-
warning %{ENV "#{CFG_OLD_ENV_NAME}" is no longer supported. Rename it to "#{CFG_ENV_NAME}"}
|
57
|
-
unless ENV[CFG_ENV_NAME].nil? then
|
58
|
-
error %{Both "#{CFG_ENV_NAME}" and "#{CFG_OLD_ENV_NAME}" defined, please remove "#{CFG_OLD_ENV_NAME}".}
|
59
|
-
end
|
60
|
-
ENV[CFG_ENV_NAME] = ENV[CFG_OLD_ENV_NAME]
|
79
|
+
def migrate_old_env
|
80
|
+
return if ENV[CFG_OLD_ENV_NAME].nil?
|
81
|
+
warning %(ENV "#{CFG_OLD_ENV_NAME}" is no longer supported. Rename it to "#{CFG_ENV_NAME}")
|
82
|
+
unless ENV[CFG_ENV_NAME].nil?
|
83
|
+
warning %(Both "#{CFG_ENV_NAME}" and "#{CFG_OLD_ENV_NAME}" defined, please remove "#{CFG_OLD_ENV_NAME}".)
|
61
84
|
end
|
85
|
+
ENV[CFG_ENV_NAME] = ENV[CFG_OLD_ENV_NAME]
|
62
86
|
end
|
63
87
|
|
64
|
-
def
|
88
|
+
def migrate_old_config(where)
|
65
89
|
# Check for dir.
|
66
|
-
if (where + CFG_OLD_DIR_NAME).exist?
|
67
|
-
warning %
|
90
|
+
if (where + CFG_OLD_DIR_NAME).exist?
|
91
|
+
warning %(Moving old directory "#{CFG_OLD_DIR_NAME}" to "#{CFG_DIR_NAME}" in "#{where}")
|
68
92
|
(where + CFG_OLD_DIR_NAME).rename(where + CFG_DIR_NAME)
|
69
93
|
end
|
70
94
|
|
71
|
-
#
|
72
|
-
|
73
|
-
|
95
|
+
# Check for cfg.
|
96
|
+
# rubocop:disable Style/GuardClause
|
97
|
+
if (where + CFG_OLD_FILE_NAME).exist?
|
98
|
+
warning %(Moving old config "#{CFG_OLD_FILE_NAME}" to "#{CFG_FILE_NAME}" in "#{where}")
|
74
99
|
(where + CFG_DIR_NAME).mkpath
|
75
100
|
(where + CFG_OLD_FILE_NAME).rename(where + CFG_FILE_NAME)
|
76
101
|
end
|
77
102
|
end
|
78
103
|
|
79
|
-
def initialize
|
104
|
+
def initialize(cmd_runner=nil)
|
105
|
+
@runner = cmd_runner
|
106
|
+
@curlfile_f = nil
|
107
|
+
|
80
108
|
@paths = []
|
81
|
-
@paths << ConfigFile.new(:internal, nil, IniFile.new
|
109
|
+
@paths << ConfigFile.new(:internal, nil, IniFile.new)
|
82
110
|
# :specified --configfile FILE goes here. (see load_specific)
|
83
111
|
|
84
|
-
|
85
|
-
unless ENV[CFG_ENV_NAME].nil?
|
112
|
+
migrate_old_env
|
113
|
+
unless ENV[CFG_ENV_NAME].nil?
|
86
114
|
# if it exists, must be a file
|
87
115
|
# if it doesn't exist, that's ok
|
88
116
|
ep = Pathname.new(ENV[CFG_ENV_NAME])
|
89
|
-
if ep.file?
|
90
|
-
@paths << ConfigFile.new(:env, ep)
|
91
|
-
end
|
117
|
+
@paths << ConfigFile.new(:env, ep) if ep.file? || !ep.exist?
|
92
118
|
end
|
93
119
|
|
94
|
-
@
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
120
|
+
@project_dir, @project_exists = find_project_dir
|
121
|
+
# For murano init, do not use parent config file as project config.
|
122
|
+
if !@runner.nil? && @runner.active_command.restrict_to_cur_dir
|
123
|
+
pwd = Pathname.new(Dir.pwd).realpath
|
124
|
+
if @project_dir != pwd
|
125
|
+
@project_dir = pwd
|
126
|
+
@project_exists = false
|
127
|
+
end
|
128
|
+
end
|
129
|
+
@paths << ConfigFile.new(:project, @project_dir + CFG_FILE_NAME)
|
99
130
|
|
100
|
-
migrateOldConfig(Pathname.new(Dir.home))
|
101
131
|
@paths << ConfigFile.new(:user, Pathname.new(Dir.home) + CFG_FILE_NAME)
|
102
|
-
(Pathname.new(Dir.home) + CFG_DIR_NAME).mkpath
|
103
|
-
fixModes(Pathname.new(Dir.home) + CFG_DIR_NAME)
|
104
132
|
|
105
|
-
@paths << ConfigFile.new(:defaults, nil, IniFile.new
|
133
|
+
@paths << ConfigFile.new(:defaults, nil, IniFile.new)
|
106
134
|
|
135
|
+
# The user can exclude certain scopes.
|
136
|
+
@exclude_scopes = []
|
137
|
+
|
138
|
+
set_defaults
|
139
|
+
end
|
140
|
+
|
141
|
+
def set_defaults
|
142
|
+
# All these set()'s are against the :defaults config.
|
143
|
+
# So no disk writing ensues. And these serve as defaults
|
144
|
+
# unless, say, a SolutionFile says otherwise.
|
107
145
|
|
108
146
|
set('tool.verbose', false, :defaults)
|
109
147
|
set('tool.debug', false, :defaults)
|
@@ -113,31 +151,85 @@ module MrMurano
|
|
113
151
|
|
114
152
|
set('net.host', 'bizapi.hosted.exosite.io', :defaults)
|
115
153
|
|
116
|
-
set('location.base', @
|
154
|
+
set('location.base', @project_dir, :defaults) unless @project_dir.nil?
|
117
155
|
set('location.files', 'files', :defaults)
|
118
156
|
set('location.endpoints', 'routes', :defaults)
|
119
157
|
set('location.modules', 'modules', :defaults)
|
120
158
|
set('location.eventhandlers', 'services', :defaults)
|
121
|
-
set('location.
|
159
|
+
set('location.resources', 'specs/resources.yaml', :defaults)
|
122
160
|
set('location.cors', 'cors.yaml', :defaults)
|
123
161
|
|
124
|
-
set('sync.bydefault', SyncRoot.bydefault.join(' '), :defaults) if defined? SyncRoot
|
162
|
+
set('sync.bydefault', SyncRoot.instance.bydefault.join(' '), :defaults) if defined? SyncRoot
|
125
163
|
|
126
164
|
set('files.default_page', 'index.html', :defaults)
|
127
165
|
set('files.searchFor', '**/*', :defaults)
|
128
166
|
set('files.ignoring', '', :defaults)
|
129
167
|
|
130
|
-
set('endpoints.searchFor',
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
set('
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
168
|
+
set('endpoints.searchFor', %w[
|
169
|
+
{,../endpoints}/*.lua
|
170
|
+
{,../endpoints}s/*/*.lua
|
171
|
+
].join(' '), :defaults)
|
172
|
+
set('endpoints.ignoring', %w[
|
173
|
+
*_test.lua
|
174
|
+
*_spec.lua
|
175
|
+
.*
|
176
|
+
].join(' '), :defaults)
|
177
|
+
|
178
|
+
set('eventhandler.searchFor', %w[
|
179
|
+
*.lua
|
180
|
+
*/*.lua
|
181
|
+
{../eventhandlers,../event_handler}/*.lua
|
182
|
+
{../eventhandlers,../event_handler}/*/*.lua
|
183
|
+
].join(' '), :defaults)
|
184
|
+
set('eventhandler.ignoring', %w[
|
185
|
+
*_test.lua
|
186
|
+
*_spec.lua
|
187
|
+
.*
|
188
|
+
].join(' '), :defaults)
|
189
|
+
# 2017-08-07: device.datapoint is the v1 device event handler.
|
190
|
+
# It is deprecated and will be removed in 12 months.
|
191
|
+
# So it technically still works, but eventually will not.
|
192
|
+
# CAVEAT: One must manually enable the Device V1 service for a Murano 1.1 business.
|
193
|
+
# device2.event is the event handler that is created when two solutions
|
194
|
+
# are linked (say, an application and a product). Do not delete this.
|
195
|
+
# The interface service contains lots of simple device event handlers
|
196
|
+
# that we don't want to touch.
|
197
|
+
set('eventhandler.skiplist', %w[
|
198
|
+
device.service_call
|
199
|
+
device2.event
|
200
|
+
interface
|
201
|
+
webservice
|
202
|
+
websocket
|
203
|
+
].join(' '), :defaults)
|
204
|
+
# Do not delete boilerplate event handlers.
|
205
|
+
set('eventhandler.undeletable', %w[
|
206
|
+
*.event
|
207
|
+
timer.timer
|
208
|
+
tsdb.exportJob
|
209
|
+
user.account
|
210
|
+
].join(' '), :defaults)
|
211
|
+
|
212
|
+
# 2017-07-26: Nested Lua support: Change '*/*.lua' to '**/*.lua'.
|
213
|
+
# There are similar changes made to the ProjectFile,
|
214
|
+
# to modules.include and modules.exclude, which, if set,
|
215
|
+
# override modules.searchFor and modules.ignoring, respectively.
|
216
|
+
# NOTE: ** finds files in subdirs in subdirs,
|
217
|
+
# e.g., modules/subdir1/subdir2/<here>/and/<here>
|
218
|
+
# otherwise, */*.lua just finds files modules/subdir1/<here>.
|
219
|
+
set('modules.searchFor', %w[
|
220
|
+
*.lua
|
221
|
+
**/*.lua
|
222
|
+
].join(' '), :defaults)
|
223
|
+
|
224
|
+
set('modules.ignoring', %w[
|
225
|
+
*_test.lua
|
226
|
+
*_spec.lua
|
227
|
+
.*
|
228
|
+
].join(' '), :defaults)
|
229
|
+
|
230
|
+
set('modules.no-nesting', false, :defaults)
|
231
|
+
|
232
|
+
if Gem.win_platform?
|
141
233
|
set('diff.cmd', 'fc', :defaults)
|
142
234
|
else
|
143
235
|
set('diff.cmd', 'diff -u', :defaults)
|
@@ -148,46 +240,75 @@ module MrMurano
|
|
148
240
|
|
149
241
|
## Find the root of this project Directory.
|
150
242
|
#
|
151
|
-
# The Project dir is the directory between PWD and HOME
|
152
|
-
# order of preference):
|
243
|
+
# The Project dir is the directory between PWD and HOME
|
244
|
+
# that has one of (in order of preference):
|
153
245
|
# - .murano/config
|
154
246
|
# - .mrmuranorc
|
155
247
|
# - .murano/
|
156
248
|
# - .mrmurano/
|
157
|
-
def
|
158
|
-
|
159
|
-
|
160
|
-
dirNames=[CFG_DIR_NAME, CFG_OLD_DIR_NAME]
|
249
|
+
def find_project_dir
|
250
|
+
file_names = [CFG_FILE_NAME, CFG_OLD_FILE_NAME]
|
251
|
+
dir_names = [CFG_DIR_NAME, CFG_OLD_DIR_NAME]
|
161
252
|
home = Pathname.new(Dir.home).realpath
|
162
253
|
pwd = Pathname.new(Dir.pwd).realpath
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
254
|
+
# The home directory contains the user ~/.murano/config,
|
255
|
+
# so we cannot also have a project .murano/ directory.
|
256
|
+
return home, false if home == pwd
|
257
|
+
pwd.ascend do |path|
|
258
|
+
# Don't bother with home or looking above it.
|
259
|
+
break if path == home
|
260
|
+
file_names.each do |fname|
|
261
|
+
return path, true if (path + fname).exist?
|
171
262
|
end
|
172
|
-
|
173
|
-
if (
|
174
|
-
result = i
|
175
|
-
end
|
263
|
+
dir_names.each do |dname|
|
264
|
+
return path, true if (path + dname).directory?
|
176
265
|
end
|
177
266
|
end
|
178
|
-
|
179
267
|
# Now if nothing found, assume it will live in pwd.
|
180
|
-
result = Pathname.new(Dir.pwd)
|
181
|
-
|
268
|
+
result = Pathname.new(Dir.pwd)
|
269
|
+
[result, false]
|
270
|
+
end
|
271
|
+
private :find_project_dir
|
272
|
+
|
273
|
+
def prompt_if_logged_off
|
274
|
+
# MAYBE/2017-07-31: [lb] likes the idea of only prompting for the
|
275
|
+
# password for certain commands (e.g., `murano login` and `murano init`),
|
276
|
+
# but this might break a user's experience if they don't want to store
|
277
|
+
# their password in ~/.murano but instead always want to be prompted.
|
278
|
+
#!@runner.nil? && @runner.active_command.prompt_if_logged_off
|
279
|
+
# Disabling for now...
|
280
|
+
true
|
182
281
|
end
|
183
|
-
private :findProjectDir
|
184
282
|
|
185
|
-
def
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
283
|
+
def validate_cmd
|
284
|
+
# Most commands should be run from within a Murano project (sub-)directory.
|
285
|
+
# If user is running a project command not within a project directory,
|
286
|
+
# we'll print a message now and exit the app from run_active_command later.
|
287
|
+
unless @runner.nil?
|
288
|
+
the_cmd = @runner.active_command
|
289
|
+
unless the_cmd.name == 'help' || the_cmd.project_not_required || @project_exists
|
290
|
+
error %(The "#{the_cmd.name}" command only works in a Murano project.)
|
291
|
+
say %(Please change to a project directory, or run `murano init` to create a new project.)
|
292
|
+
# Note that commnander-rb uses an at_exit hook, which we hack around.
|
293
|
+
@runner.command_exit = 1
|
294
|
+
return
|
295
|
+
end
|
190
296
|
end
|
297
|
+
|
298
|
+
migrate_old_config(@project_dir)
|
299
|
+
migrate_old_config(Pathname.new(Dir.home))
|
300
|
+
end
|
301
|
+
|
302
|
+
def self.fix_modes(path)
|
303
|
+
if path.directory?
|
304
|
+
path.chmod(0o700)
|
305
|
+
elsif path.file?
|
306
|
+
path.chmod(0o600)
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
def fix_modes(path)
|
311
|
+
MrMurano::Config.fix_modes(path)
|
191
312
|
end
|
192
313
|
|
193
314
|
def file_at(name, scope=:project)
|
@@ -197,7 +318,7 @@ module MrMurano
|
|
197
318
|
when :specified
|
198
319
|
root = nil
|
199
320
|
when :project
|
200
|
-
root = @
|
321
|
+
root = @project_dir + CFG_DIR_NAME
|
201
322
|
when :user
|
202
323
|
root = Pathname.new(Dir.home) + CFG_DIR_NAME
|
203
324
|
when :defaults
|
@@ -209,9 +330,11 @@ module MrMurano
|
|
209
330
|
end
|
210
331
|
|
211
332
|
## Load all of the potential config files
|
212
|
-
def load
|
333
|
+
def load
|
213
334
|
# - read/write config file in [Project, User, System] (all are optional)
|
214
|
-
@paths.each
|
335
|
+
@paths.each(&:load)
|
336
|
+
# If user wants curl commands dumped to a file, open that file.
|
337
|
+
init_curl_file
|
215
338
|
end
|
216
339
|
|
217
340
|
## Load specified file into the config stack
|
@@ -222,52 +345,115 @@ module MrMurano
|
|
222
345
|
@paths.insert(1, spc)
|
223
346
|
end
|
224
347
|
|
225
|
-
## Get a value for key, looking at the
|
348
|
+
## Get a value for key, looking at the specified scopes
|
226
349
|
# key is <section>.<key>
|
227
350
|
def get(key, scope=CFG_SCOPES)
|
228
|
-
scope = [scope] unless scope.
|
229
|
-
paths = @paths.select{|p| scope.include? p.kind}
|
351
|
+
scope = [scope] unless scope.is_a? Array
|
352
|
+
paths = @paths.select { |p| scope.include? p.kind }
|
353
|
+
paths = paths.reject { |p| @exclude_scopes.include? p.kind }
|
354
|
+
|
355
|
+
section, ikey = key.split('.')
|
356
|
+
paths.each do |path|
|
357
|
+
next unless path.data.has_section?(section)
|
358
|
+
sec = path.data[section]
|
359
|
+
return sec if ikey.nil?
|
360
|
+
return sec[ikey] if sec.key?(ikey)
|
361
|
+
end
|
362
|
+
nil
|
363
|
+
end
|
364
|
+
|
365
|
+
## Get one or more key-values for the specified scopes, honoring '*' wildcard.
|
366
|
+
# key is <section>.<key>, <section>.*, or *.<key>
|
367
|
+
def get_wild(key, scope=CFG_SCOPES)
|
368
|
+
scope = [scope] unless scope.is_a? Array
|
369
|
+
paths = @paths.select { |p| scope.include? p.kind }
|
370
|
+
paths = paths.reject { |p| @exclude_scopes.include? p.kind }
|
230
371
|
|
372
|
+
seen = {}
|
373
|
+
|
374
|
+
kvals = []
|
375
|
+
is_wild = wild?(key)
|
231
376
|
section, ikey = key.split('.')
|
232
377
|
paths.each do |path|
|
233
|
-
if
|
378
|
+
if section != '*' || !is_wild
|
379
|
+
next unless path.data.has_section?(section)
|
234
380
|
sec = path.data[section]
|
235
|
-
|
236
|
-
|
237
|
-
|
381
|
+
if ikey != '*' || !is_wild
|
382
|
+
# blah.blug query
|
383
|
+
if !ikey.nil?
|
384
|
+
next unless sec.key?(ikey)
|
385
|
+
kvals += [["#{section}.#{ikey}", sec[ikey], path.kind]]
|
386
|
+
else
|
387
|
+
sec.each do |skey|
|
388
|
+
kvals += [["#{section}.#{skey}", sec[skey], path.kind]]
|
389
|
+
end
|
390
|
+
end
|
391
|
+
return kvals
|
392
|
+
else
|
393
|
+
# blah.* query
|
394
|
+
sec.each do |kv|
|
395
|
+
kvid = "#{section}.#{kv[0]}"
|
396
|
+
next if seen[kvid]
|
397
|
+
seen[kvid] = true
|
398
|
+
kvals += [[kvid, kv[1].to_s, path.kind]]
|
399
|
+
end
|
400
|
+
end
|
401
|
+
else
|
402
|
+
# *.blah query
|
403
|
+
path.data.each do |ini|
|
404
|
+
path.data[ini].keys.each do |skey|
|
405
|
+
next unless ikey == '*' || skey == ikey
|
406
|
+
kvid = "#{ini}.#{skey}"
|
407
|
+
next if seen[kvid]
|
408
|
+
seen[kvid] = true
|
409
|
+
kvals += [[kvid, path.data[ini][skey].to_s, path.kind]]
|
410
|
+
end
|
238
411
|
end
|
239
412
|
end
|
240
413
|
end
|
241
|
-
|
414
|
+
kvals.sort_by! { |kv| kv[0] }
|
415
|
+
kvals
|
242
416
|
end
|
243
417
|
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
418
|
+
def wild?(key)
|
419
|
+
section, ikey = key.split('.')
|
420
|
+
is_wild = (
|
421
|
+
!section.to_s.empty? &&
|
422
|
+
!ikey.to_s.empty? &&
|
423
|
+
((section == '*') || (ikey == '*'))
|
424
|
+
)
|
425
|
+
is_wild
|
252
426
|
end
|
253
427
|
|
254
428
|
def set(key, value, scope=:project)
|
255
429
|
section, ikey = key.split('.', 2)
|
256
|
-
raise
|
257
|
-
if
|
430
|
+
raise 'Invalid key' if section.nil?
|
431
|
+
if ikey.nil?
|
258
432
|
# If key isn't dotted, then assume the tool section.
|
259
433
|
ikey = section
|
260
434
|
section = 'tool'
|
261
435
|
end
|
262
436
|
|
263
|
-
paths = @paths.select{|p| scope == p.kind}
|
264
|
-
raise "Unknown scope" if paths.empty?
|
437
|
+
paths = @paths.select { |p| scope == p.kind }
|
438
|
+
raise "Unknown scope ‘#{scope}’" if paths.empty?
|
439
|
+
raise "Too many scopes ‘#{scope}’" if paths.length > 1
|
440
|
+
|
265
441
|
cfg = paths.first
|
266
442
|
data = cfg.data
|
267
443
|
tomod = data[section]
|
268
444
|
tomod[ikey] = value unless value.nil?
|
269
445
|
tomod.delete(ikey) if value.nil?
|
270
446
|
data[section] = tomod
|
447
|
+
# Remove empty sections to make test results more predictable.
|
448
|
+
# Interesting: IniFile.each only returns sections with key-vals,
|
449
|
+
# so call IniFile.each_section instead, which includes
|
450
|
+
# empty empty section. Here's what "each" looks like:
|
451
|
+
# data.each do |sectn, param, val|
|
452
|
+
# puts "#{param} = #{val} [in section: #{sectn}]"
|
453
|
+
data.each_section do |sectn|
|
454
|
+
data.delete_section(sectn) if data[sectn].empty?
|
455
|
+
end
|
456
|
+
|
271
457
|
cfg.write
|
272
458
|
end
|
273
459
|
|
@@ -276,16 +462,109 @@ module MrMurano
|
|
276
462
|
get(key)
|
277
463
|
end
|
278
464
|
|
279
|
-
# For setting internal, this-run-only values
|
465
|
+
# For setting internal, this-run-only values.
|
280
466
|
def []=(key, value)
|
281
467
|
set(key, value, :internal)
|
282
468
|
end
|
283
469
|
|
470
|
+
## Dump out a combined config
|
471
|
+
def dump
|
472
|
+
# have a fake, merge all into it, then dump it.
|
473
|
+
base = IniFile.new
|
474
|
+
@paths.reverse.each do |ini|
|
475
|
+
base.merge! ini.data
|
476
|
+
end
|
477
|
+
base.to_s
|
478
|
+
end
|
479
|
+
|
480
|
+
## Dump out locations of all known configs
|
481
|
+
def locations
|
482
|
+
locats = ''
|
483
|
+
first = true
|
484
|
+
puts ''
|
485
|
+
CFG_SCOPES.each do |scope|
|
486
|
+
locats += "\n" unless first
|
487
|
+
first = false
|
488
|
+
|
489
|
+
cfg_paths = @paths.select { |p| p.kind == scope }
|
490
|
+
|
491
|
+
msg = "Scope: ‘#{scope}’\n\n"
|
492
|
+
locats += Rainbow(msg).bright.underline
|
493
|
+
|
494
|
+
if !cfg_paths.empty?
|
495
|
+
cfg = cfg_paths.first
|
496
|
+
|
497
|
+
if !cfg.path.nil? && cfg.path.exist?
|
498
|
+
path = "Path: #{cfg.path}\n"
|
499
|
+
elsif %i[internal defaults].include? cfg.kind
|
500
|
+
# cfg.path is nil.
|
501
|
+
path = "Path: ‘#{scope}’ config is not saved.\n"
|
502
|
+
else
|
503
|
+
path = "Path: ‘#{scope}’ config does not exist.\n"
|
504
|
+
end
|
505
|
+
#locats += Rainbow(path).bright
|
506
|
+
locats += path
|
507
|
+
locats += "\n"
|
508
|
+
|
509
|
+
skip_content = false
|
510
|
+
if scope == :env
|
511
|
+
locats += "Use the environment variable, MURANO_CONFIGFILE, to specify this config file.\n"
|
512
|
+
locats += "\n"
|
513
|
+
if ENV['MURANO_PASSWORD'].to_s.empty?
|
514
|
+
locats += "The MURANO_PASSWORD environ is not set.\n"
|
515
|
+
else
|
516
|
+
locats += "The MURANO_PASSWORD environ is set and will be used.\n"
|
517
|
+
end
|
518
|
+
skip_content = !cfg.path.exist?
|
519
|
+
end
|
520
|
+
next if skip_content
|
521
|
+
locats += "\n" if scope == :env
|
522
|
+
|
523
|
+
base = IniFile.new
|
524
|
+
base.merge! cfg.data
|
525
|
+
content = base.to_s
|
526
|
+
if !content.empty?
|
527
|
+
locats += "Config:\n\n"
|
528
|
+
#locats += base.to_s
|
529
|
+
base.to_s.split("\n").each do |line|
|
530
|
+
locats += ' ' + line + "\n"
|
531
|
+
end
|
532
|
+
else
|
533
|
+
msg = "Config: Empty INI file.\n"
|
534
|
+
#locats += Rainbow(msg).aqua.bright
|
535
|
+
locats += msg
|
536
|
+
end
|
537
|
+
else
|
538
|
+
msg = "No config found for ‘#{scope}’.\n"
|
539
|
+
if scope != :specified
|
540
|
+
locats += Rainbow(msg).red.bright
|
541
|
+
else
|
542
|
+
locats += "Path: ‘#{scope}’ config does not exist.\n\n"
|
543
|
+
locats += "Use --configfile to specify this config file.\n"
|
544
|
+
end
|
545
|
+
end
|
546
|
+
end
|
547
|
+
locats
|
548
|
+
end
|
549
|
+
|
550
|
+
# To capture curl calls when running rspec, write to a file.
|
551
|
+
def init_curl_file
|
552
|
+
if self['tool.curldebug'] && !self['tool.curlfile'].to_s.strip.empty?
|
553
|
+
if @curlfile_f.nil?
|
554
|
+
@curlfile_f = File.open(self['tool.curlfile'], 'a')
|
555
|
+
# MEH: Call @curlfile_f.close() at some point? Or let Ruby do on exit.
|
556
|
+
@curlfile_f << Time.now.to_s + "\n"
|
557
|
+
@curlfile_f << "murano #{ARGV.join(' ')}\n"
|
558
|
+
@curlfile_f.flush
|
559
|
+
end
|
560
|
+
elsif !@curlfile_f.nil?
|
561
|
+
@curlfile_f.close
|
562
|
+
@curlfile_f = nil
|
563
|
+
end
|
564
|
+
end
|
284
565
|
end
|
285
566
|
|
286
567
|
class ConfigError < StandardError
|
287
568
|
end
|
288
|
-
|
289
569
|
end
|
290
570
|
|
291
|
-
# vim: set ai et sw=2 ts=2 :
|