foreman_rh_cloud 3.0.21.1 → 3.0.22
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/controllers/insights_cloud/api/machine_telemetries_controller.rb +17 -7
- data/app/models/insights_client_report_status.rb +58 -0
- data/app/services/foreman_rh_cloud/cloud_auth.rb +12 -0
- data/app/services/foreman_rh_cloud/cloud_request.rb +14 -0
- data/app/services/foreman_rh_cloud/cloud_request_forwarder.rb +1 -6
- data/app/services/foreman_rh_cloud/remediations_retriever.rb +1 -4
- data/app/subscribers/foreman_rh_cloud/insights_subscriber.rb +9 -0
- data/lib/foreman_inventory_upload/generators/fact_helpers.rb +19 -0
- data/lib/foreman_inventory_upload/generators/slice.rb +6 -6
- data/lib/foreman_rh_cloud/engine.rb +4 -1
- data/lib/foreman_rh_cloud/version.rb +1 -1
- data/lib/insights_cloud/async/insights_full_sync.rb +4 -14
- data/lib/insights_cloud/async/insights_resolutions_sync.rb +1 -4
- data/lib/insights_cloud/async/insights_rules_sync.rb +2 -7
- data/lib/inventory_sync/async/inventory_full_sync.rb +2 -1
- data/lib/inventory_sync/async/inventory_scheduled_sync.rb +8 -0
- data/lib/inventory_sync/async/query_inventory_job.rb +1 -4
- data/lib/tasks/rh_cloud_inventory.rake +6 -0
- data/package.json +2 -2
- data/test/factories/inventory_upload_factories.rb +1 -1
- data/test/jobs/insights_full_sync_test.rb +3 -0
- data/test/jobs/insights_resolutions_sync_test.rb +3 -0
- data/test/jobs/insights_rules_sync_test.rb +3 -0
- data/test/jobs/inventory_full_sync_test.rb +3 -0
- data/test/models/insights_client_report_status_test.rb +77 -0
- data/test/unit/slice_generator_test.rb +66 -29
- data/webpack/ForemanInventoryUpload/Components/FullScreenModal/FullScreenModal.js +1 -1
- data/webpack/ForemanInventoryUpload/Components/FullScreenModal/__tests__/__snapshots__/FullScreenModal.test.js.snap +1 -1
- data/webpack/ForemanInventoryUpload/Components/FullScreenModal/fullScreenModal.scss +14 -16
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/SyncButtonActions.js +28 -63
- data/webpack/ForemanInventoryUpload/Components/PageHeader/components/SyncButton/__tests__/__snapshots__/integrations.test.js.snap +2 -3
- data/webpack/ForemanInventoryUpload/Components/Terminal/Terminal.js +1 -1
- data/webpack/ForemanInventoryUpload/Components/Terminal/__tests__/Terminal.test.js +1 -1
- data/webpack/ForemanInventoryUpload/Components/Terminal/__tests__/__snapshots__/Terminal.test.js.snap +2 -2
- data/webpack/ForemanInventoryUpload/Components/Terminal/terminal.scss +25 -27
- data/webpack/InsightsCloudSync/Components/InsightsTable/InsightsTableActions.js +19 -19
- data/webpack/InsightsCloudSync/Components/InsightsTable/__tests__/__snapshots__/InsightsTableActions.test.js.snap +14 -14
- data/webpack/InsightsCloudSync/Components/RemediationModal/RemediateButton.js +1 -0
- data/webpack/InsightsCloudSync/InsightsCloudSync.js +4 -1
- data/webpack/InsightsCloudSync/InsightsCloudSyncActions.js +44 -20
- data/webpack/InsightsCloudSync/InsightsCloudSyncConstants.js +2 -0
- data/webpack/InsightsCloudSync/__tests__/__snapshots__/InsightsCloudSyncActions.test.js.snap +11 -7
- data/webpack/common/ForemanTasks/ForemanTasksActions.js +64 -0
- data/webpack/common/ForemanTasks/ForemanTasksHelpers.js +7 -0
- data/webpack/common/ForemanTasks/index.js +1 -0
- metadata +10 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 6edd44f8ce4af74d7affca479d84e6012b8a8d363bafcab2ef95fb97eaae5eac
|
4
|
+
data.tar.gz: 865870711b1216ea512f5bc5c304f46df1f176b568e4fbb39f6e4686efa68cfb
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: c0f841e299dd1304590bef74ade8c7b10c9f5392ce7bf164f99243c4cf9d1c68d0061dbb83281bc84123539edca21fdbbcd3bbf1f82e3234a0afd6437cca1c0d
|
7
|
+
data.tar.gz: ea5d3651236539899e42085000c4f0aa2401229aff79ef22afaee9eef5c277af38054b0e347fc17d6f119267b1b17410d610b6144883b6b19ec1bdf3ac568d98
|
@@ -10,27 +10,28 @@ module InsightsCloud::Api
|
|
10
10
|
|
11
11
|
skip_after_action :log_response_body, :only => [:forward_request]
|
12
12
|
skip_before_action :check_media_type, :only => [:forward_request]
|
13
|
+
after_action :update_host_insights_status, only: [:forward_request]
|
13
14
|
|
14
15
|
# The method that "proxies" requests over to Cloud
|
15
16
|
def forward_request
|
16
17
|
certs = candlepin_id_cert @organization
|
17
|
-
cloud_response = ::ForemanRhCloud::CloudRequestForwarder.new.forward_request(request, controller_name, @branch_id, certs)
|
18
|
+
@cloud_response = ::ForemanRhCloud::CloudRequestForwarder.new.forward_request(request, controller_name, @branch_id, certs)
|
18
19
|
|
19
|
-
if cloud_response.code == 401
|
20
|
+
if @cloud_response.code == 401
|
20
21
|
return render json: {
|
21
22
|
:message => 'Authentication to the Insights Service failed.',
|
22
23
|
:headers => {},
|
23
24
|
}, status: :bad_gateway
|
24
25
|
end
|
25
26
|
|
26
|
-
if cloud_response.headers[:content_disposition]
|
27
|
-
return send_data cloud_response, disposition: cloud_response.headers[:content_disposition], type: cloud_response.headers[:content_type]
|
27
|
+
if @cloud_response.headers[:content_disposition]
|
28
|
+
return send_data @cloud_response, disposition: @cloud_response.headers[:content_disposition], type: @cloud_response.headers[:content_type]
|
28
29
|
end
|
29
30
|
|
30
|
-
assign_header(response, cloud_response, :x_resource_count, true)
|
31
|
-
assign_header(response, cloud_response, :x_rh_insights_request_id, false)
|
31
|
+
assign_header(response, @cloud_response, :x_resource_count, true)
|
32
|
+
assign_header(response, @cloud_response, :x_rh_insights_request_id, false)
|
32
33
|
|
33
|
-
render json: cloud_response, status: cloud_response.code
|
34
|
+
render json: @cloud_response, status: @cloud_response.code
|
34
35
|
end
|
35
36
|
|
36
37
|
def branch_info
|
@@ -69,5 +70,14 @@ module InsightsCloud::Api
|
|
69
70
|
@branch_id = cp_owner_id(@organization)
|
70
71
|
return render_message "Branch ID not found for organization #{@organization.title}", :status => 400 unless @branch_id
|
71
72
|
end
|
73
|
+
|
74
|
+
def update_host_insights_status
|
75
|
+
return unless request.path == '/redhat_access/r/insights/platform/ingress/v1/upload' ||
|
76
|
+
request.path.include?('/redhat_access/r/insights/uploads/')
|
77
|
+
|
78
|
+
data = @cloud_response.code.to_s.start_with?('2')
|
79
|
+
host_status = @host.get_status(InsightsClientReportStatus)
|
80
|
+
host_status.update(reported_at: Time.now.utc, status: host_status.to_status(data: data))
|
81
|
+
end
|
72
82
|
end
|
73
83
|
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
class InsightsClientReportStatus < HostStatus::Status
|
2
|
+
REPORT_INTERVAL = 48.hours
|
3
|
+
|
4
|
+
REPORTING = 0 # host_registration_insights = true & getting data
|
5
|
+
NO_REPORT = 1 # host_registration_insights = true & not getting data
|
6
|
+
NOT_MANAGED = 2 # host_registration_insights = false
|
7
|
+
NOT_MANAGED_WITH_DATA = 3 # host_registration_insights = false & getting data
|
8
|
+
|
9
|
+
def self.status_name
|
10
|
+
N_('Insights')
|
11
|
+
end
|
12
|
+
|
13
|
+
def to_label(_options = {})
|
14
|
+
case status
|
15
|
+
when REPORTING
|
16
|
+
N_('Reporting')
|
17
|
+
when NO_REPORT
|
18
|
+
N_('Not reporting')
|
19
|
+
when NOT_MANAGED
|
20
|
+
N_('Not reporting (not set by registration)')
|
21
|
+
when NOT_MANAGED_WITH_DATA
|
22
|
+
N_('Reporting (not set by registration)')
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def to_global(_options = {})
|
27
|
+
case status
|
28
|
+
when REPORTING
|
29
|
+
::HostStatus::Global::OK
|
30
|
+
when NO_REPORT
|
31
|
+
::HostStatus::Global::ERROR
|
32
|
+
when NOT_MANAGED
|
33
|
+
::HostStatus::Global::OK
|
34
|
+
when NOT_MANAGED_WITH_DATA
|
35
|
+
::HostStatus::Global::WARN
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def to_status(data: false)
|
40
|
+
if insights_param
|
41
|
+
return REPORTING if data
|
42
|
+
return in_interval? ? REPORTING : NO_REPORT
|
43
|
+
end
|
44
|
+
|
45
|
+
data ? NOT_MANAGED_WITH_DATA : NOT_MANAGED
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
def in_interval?
|
51
|
+
return false unless reported_at
|
52
|
+
(Time.now.utc - reported_at).to_i < REPORT_INTERVAL.to_i
|
53
|
+
end
|
54
|
+
|
55
|
+
def insights_param
|
56
|
+
host.host_params_hash.dig('host_registration_insights', :value)
|
57
|
+
end
|
58
|
+
end
|
@@ -2,6 +2,8 @@ module ForemanRhCloud
|
|
2
2
|
module CloudAuth
|
3
3
|
extend ActiveSupport::Concern
|
4
4
|
|
5
|
+
include CloudRequest
|
6
|
+
|
5
7
|
def rh_credentials
|
6
8
|
@rh_credentials ||= query_refresh_token
|
7
9
|
end
|
@@ -24,5 +26,15 @@ module ForemanRhCloud
|
|
24
26
|
Foreman::Logging.exception('Unable to authenticate using rh_cloud_token setting', e)
|
25
27
|
raise ::Foreman::WrappedException.new(e, N_('Unable to authenticate using rh_cloud_token setting'))
|
26
28
|
end
|
29
|
+
|
30
|
+
def execute_cloud_request(params)
|
31
|
+
final_params = {
|
32
|
+
headers: {
|
33
|
+
Authorization: "Bearer #{rh_credentials}",
|
34
|
+
},
|
35
|
+
}.deep_merge(params)
|
36
|
+
|
37
|
+
super(final_params)
|
38
|
+
end
|
27
39
|
end
|
28
40
|
end
|
@@ -0,0 +1,14 @@
|
|
1
|
+
module ForemanRhCloud
|
2
|
+
module CloudRequest
|
3
|
+
extend ActiveSupport::Concern
|
4
|
+
|
5
|
+
def execute_cloud_request(params)
|
6
|
+
final_params = {
|
7
|
+
verify_ssl: ForemanRhCloud.verify_ssl_method,
|
8
|
+
proxy: ForemanRhCloud.transformed_http_proxy_string(logger: logger),
|
9
|
+
}.deep_merge(params)
|
10
|
+
|
11
|
+
RestClient::Request.execute(final_params)
|
12
|
+
end
|
13
|
+
end
|
14
|
+
end
|
@@ -2,7 +2,7 @@ require 'rest-client'
|
|
2
2
|
|
3
3
|
module ForemanRhCloud
|
4
4
|
class CloudRequestForwarder
|
5
|
-
include
|
5
|
+
include ForemanRhCloud::CloudRequest
|
6
6
|
|
7
7
|
def forward_request(original_request, controller_name, branch_id, certs)
|
8
8
|
forward_params = prepare_forward_params(original_request, branch_id)
|
@@ -22,7 +22,6 @@ module ForemanRhCloud
|
|
22
22
|
def prepare_request_opts(original_request, forward_payload, forward_params, certs)
|
23
23
|
base_params = {
|
24
24
|
method: original_request.method,
|
25
|
-
verify_ssl: ForemanRhCloud.verify_ssl_method,
|
26
25
|
payload: forward_payload,
|
27
26
|
headers: {
|
28
27
|
params: forward_params,
|
@@ -33,10 +32,6 @@ module ForemanRhCloud
|
|
33
32
|
base_params.merge(path_params(original_request.path, certs))
|
34
33
|
end
|
35
34
|
|
36
|
-
def execute_cloud_request(request_opts)
|
37
|
-
RestClient::Request.execute request_opts
|
38
|
-
end
|
39
|
-
|
40
35
|
def prepare_forward_payload(original_request, controller_name)
|
41
36
|
forward_payload = original_request.request_parameters[controller_name]
|
42
37
|
|
@@ -62,14 +62,11 @@ module ForemanRhCloud
|
|
62
62
|
end
|
63
63
|
|
64
64
|
def query_playbook
|
65
|
-
|
65
|
+
execute_cloud_request(
|
66
66
|
method: :post,
|
67
67
|
url: InsightsCloud.playbook_url,
|
68
|
-
verify_ssl: ForemanRhCloud.verify_ssl_method,
|
69
|
-
proxy: ForemanRhCloud.transformed_http_proxy_string(logger: logger),
|
70
68
|
headers: {
|
71
69
|
content_type: :json,
|
72
|
-
Authorization: "Bearer #{rh_credentials}",
|
73
70
|
},
|
74
71
|
payload: playbook_request.to_json
|
75
72
|
)
|
@@ -10,6 +10,8 @@ module ForemanInventoryUpload
|
|
10
10
|
CLOUD_AZURE = 'azure'
|
11
11
|
CLOUD_ALIBABA = 'alibaba'
|
12
12
|
|
13
|
+
UUID_REGEX = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i
|
14
|
+
|
13
15
|
def fact_value(host, fact_name)
|
14
16
|
value_record = host.fact_values.find do |fact_value|
|
15
17
|
fact_value.fact_name_id == ForemanInventoryUpload::Generators::Queries.fact_names[fact_name]
|
@@ -104,6 +106,23 @@ module ForemanInventoryUpload
|
|
104
106
|
def obfuscate_ip(ip, ips_dict)
|
105
107
|
"10.230.230.#{ips_dict.count + 1}"
|
106
108
|
end
|
109
|
+
|
110
|
+
def bios_uuid(host)
|
111
|
+
value = fact_value(host, 'dmi::system::uuid') || ''
|
112
|
+
uuid_value(value)
|
113
|
+
end
|
114
|
+
|
115
|
+
def uuid_value(value)
|
116
|
+
uuid_match = UUID_REGEX.match(value)
|
117
|
+
uuid_match&.to_s
|
118
|
+
end
|
119
|
+
|
120
|
+
def uuid_value!(value)
|
121
|
+
uuid = uuid_value(value)
|
122
|
+
raise Foreman::Exception.new(N_('Value %{value} is not a valid UUID') % {value: value}) if value && uuid.empty?
|
123
|
+
|
124
|
+
uuid
|
125
|
+
end
|
107
126
|
end
|
108
127
|
end
|
109
128
|
end
|
@@ -25,7 +25,7 @@ module ForemanInventoryUpload
|
|
25
25
|
|
26
26
|
def report_slice(hosts_batch)
|
27
27
|
@stream.object do
|
28
|
-
@stream.simple_field('report_slice_id', @slice_id)
|
28
|
+
@stream.simple_field('report_slice_id', uuid_value!(@slice_id))
|
29
29
|
@stream.array_field('hosts', :last) do
|
30
30
|
first = true
|
31
31
|
hosts_batch.each do |host|
|
@@ -45,10 +45,10 @@ module ForemanInventoryUpload
|
|
45
45
|
@stream.object do
|
46
46
|
@stream.simple_field('fqdn', fqdn(host))
|
47
47
|
@stream.simple_field('account', account_id(host.organization).to_s)
|
48
|
-
@stream.simple_field('subscription_manager_id', host.subscription_facet&.uuid)
|
49
|
-
@stream.simple_field('satellite_id', host.subscription_facet&.uuid)
|
50
|
-
@stream.simple_field('bios_uuid',
|
51
|
-
@stream.simple_field('vm_uuid', fact_value(host, 'virt::uuid'))
|
48
|
+
@stream.simple_field('subscription_manager_id', uuid_value!(host.subscription_facet&.uuid))
|
49
|
+
@stream.simple_field('satellite_id', uuid_value!(host.subscription_facet&.uuid))
|
50
|
+
@stream.simple_field('bios_uuid', bios_uuid(host))
|
51
|
+
@stream.simple_field('vm_uuid', uuid_value(fact_value(host, 'virt::uuid')))
|
52
52
|
report_ip_addresses(host, host_ips_cache)
|
53
53
|
report_mac_addresses(host)
|
54
54
|
@stream.object_field('system_profile') do
|
@@ -85,7 +85,7 @@ module ForemanInventoryUpload
|
|
85
85
|
@stream.object do
|
86
86
|
@stream.simple_field('namespace', namespace)
|
87
87
|
@stream.simple_field('key', key)
|
88
|
-
@stream.simple_field('value', value, :last)
|
88
|
+
@stream.simple_field('value', value.to_s, :last)
|
89
89
|
end
|
90
90
|
@stream.comma unless last
|
91
91
|
end
|
@@ -78,7 +78,10 @@ module ForemanRhCloud
|
|
78
78
|
|
79
79
|
register_global_js_file 'global'
|
80
80
|
|
81
|
-
register_custom_status
|
81
|
+
register_custom_status InventorySync::InventoryStatus
|
82
|
+
register_custom_status InsightsClientReportStatus
|
83
|
+
|
84
|
+
subscribe 'host_created.event.foreman', ForemanRhCloud::InsightsSubscriber
|
82
85
|
|
83
86
|
extend_page 'hosts/show' do |context|
|
84
87
|
context.add_pagelet :main_tabs,
|
@@ -50,28 +50,18 @@ module InsightsCloud
|
|
50
50
|
end
|
51
51
|
|
52
52
|
def query_insights_hits
|
53
|
-
hits_response =
|
53
|
+
hits_response = execute_cloud_request(
|
54
54
|
method: :get,
|
55
|
-
url: InsightsCloud.hits_export_url
|
56
|
-
verify_ssl: ForemanRhCloud.verify_ssl_method,
|
57
|
-
proxy: ForemanRhCloud.transformed_http_proxy_string(logger: logger),
|
58
|
-
headers: {
|
59
|
-
Authorization: "Bearer #{rh_credentials}",
|
60
|
-
}
|
55
|
+
url: InsightsCloud.hits_export_url
|
61
56
|
)
|
62
57
|
|
63
58
|
JSON.parse(hits_response)
|
64
59
|
end
|
65
60
|
|
66
61
|
def query_insights_rules
|
67
|
-
rules_response =
|
62
|
+
rules_response = execute_cloud_request(
|
68
63
|
method: :get,
|
69
|
-
url: InsightsCloud.rules_url
|
70
|
-
verify_ssl: ForemanRhCloud.verify_ssl_method,
|
71
|
-
proxy: ForemanRhCloud.transformed_http_proxy_string(logger: logger),
|
72
|
-
headers: {
|
73
|
-
Authorization: "Bearer #{rh_credentials}",
|
74
|
-
}
|
64
|
+
url: InsightsCloud.rules_url
|
75
65
|
)
|
76
66
|
|
77
67
|
JSON.parse(rules_response)
|
@@ -22,14 +22,11 @@ module InsightsCloud
|
|
22
22
|
private
|
23
23
|
|
24
24
|
def query_insights_resolutions(rule_ids)
|
25
|
-
resolutions_response =
|
25
|
+
resolutions_response = execute_cloud_request(
|
26
26
|
method: :post,
|
27
27
|
url: InsightsCloud.resolutions_url,
|
28
|
-
verify_ssl: ForemanRhCloud.verify_ssl_method,
|
29
|
-
proxy: ForemanRhCloud.transformed_http_proxy_string(logger: logger),
|
30
28
|
headers: {
|
31
29
|
content_type: :json,
|
32
|
-
Authorization: "Bearer #{rh_credentials}",
|
33
30
|
},
|
34
31
|
payload: {
|
35
32
|
issues: rule_ids,
|
@@ -37,14 +37,9 @@ module InsightsCloud
|
|
37
37
|
private
|
38
38
|
|
39
39
|
def query_insights_rules(offset)
|
40
|
-
rules_response =
|
40
|
+
rules_response = execute_cloud_request(
|
41
41
|
method: :get,
|
42
|
-
url: InsightsCloud.rules_url(offset: offset)
|
43
|
-
verify_ssl: ForemanRhCloud.verify_ssl_method,
|
44
|
-
proxy: ForemanRhCloud.transformed_http_proxy_string(logger: logger),
|
45
|
-
headers: {
|
46
|
-
Authorization: "Bearer #{rh_credentials}",
|
47
|
-
}
|
42
|
+
url: InsightsCloud.rules_url(offset: offset)
|
48
43
|
)
|
49
44
|
|
50
45
|
JSON.parse(rules_response)
|
@@ -24,6 +24,7 @@ module InventorySync
|
|
24
24
|
|
25
25
|
logger.debug("Synced hosts amount: #{host_statuses[:sync]}")
|
26
26
|
logger.debug("Disconnected hosts amount: #{host_statuses[:disconnect]}")
|
27
|
+
output[:host_statuses] = host_statuses
|
27
28
|
end
|
28
29
|
|
29
30
|
def update_statuses_batch
|
@@ -53,7 +54,7 @@ module InventorySync
|
|
53
54
|
end
|
54
55
|
|
55
56
|
def host_statuses
|
56
|
-
|
57
|
+
@host_statuses ||= {
|
57
58
|
sync: 0,
|
58
59
|
disconnect: 0,
|
59
60
|
}
|
@@ -4,6 +4,14 @@ module InventorySync
|
|
4
4
|
include ::Actions::RecurringAction
|
5
5
|
|
6
6
|
def plan
|
7
|
+
unless Setting[:allow_auto_inventory_upload]
|
8
|
+
logger.debug(
|
9
|
+
'The scheduled process is disabled due to the "allow_auto_inventory_upload"
|
10
|
+
setting being set to false.'
|
11
|
+
)
|
12
|
+
return
|
13
|
+
end
|
14
|
+
|
7
15
|
Organization.unscoped.each do |org|
|
8
16
|
plan_org_sync(org)
|
9
17
|
end
|
@@ -29,13 +29,10 @@ module InventorySync
|
|
29
29
|
private
|
30
30
|
|
31
31
|
def query_inventory(page = 1)
|
32
|
-
hosts_inventory_response =
|
32
|
+
hosts_inventory_response = execute_cloud_request(
|
33
33
|
method: :get,
|
34
34
|
url: ForemanInventoryUpload.inventory_export_url,
|
35
|
-
verify_ssl: ForemanRhCloud.verify_ssl_method,
|
36
|
-
proxy: ForemanRhCloud.transformed_http_proxy_string(logger: logger),
|
37
35
|
headers: {
|
38
|
-
Authorization: "Bearer #{rh_credentials}",
|
39
36
|
params: {
|
40
37
|
per_page: 100,
|
41
38
|
page: page,
|
@@ -23,6 +23,12 @@ namespace :rh_cloud_inventory do
|
|
23
23
|
organizations = [ENV['organization_id']]
|
24
24
|
base_folder = ENV['target'] || Dir.pwd
|
25
25
|
|
26
|
+
unless File.writable?(base_folder)
|
27
|
+
puts "#{base_folder} is not writable by the current process"
|
28
|
+
base_folder = Dir.mktmpdir
|
29
|
+
puts "Using #{base_folder} for the output"
|
30
|
+
end
|
31
|
+
|
26
32
|
unless portal_user || organizations.empty?
|
27
33
|
puts "Must specify either portal_user or organization_id"
|
28
34
|
end
|
data/package.json
CHANGED