wss_agent 0.0.15
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.
- checksums.yaml +7 -0
- data/.gitignore +14 -0
- data/.rspec +2 -0
- data/.travis.yml +6 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +201 -0
- data/README.md +73 -0
- data/Rakefile +10 -0
- data/bin/wss_agent +13 -0
- data/lib/config/custom_default.yml +5 -0
- data/lib/config/default.yml +11 -0
- data/lib/wss_agent/cli.rb +49 -0
- data/lib/wss_agent/client.rb +59 -0
- data/lib/wss_agent/configure.rb +90 -0
- data/lib/wss_agent/gem_sha1.rb +76 -0
- data/lib/wss_agent/project.rb +45 -0
- data/lib/wss_agent/response.rb +57 -0
- data/lib/wss_agent/response_inventory.rb +28 -0
- data/lib/wss_agent/response_policies.rb +76 -0
- data/lib/wss_agent/specifications.rb +150 -0
- data/lib/wss_agent/version.rb +4 -0
- data/lib/wss_agent.rb +40 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_CLI/update/when_not_found_token/should_display_error_message.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_error/response_should_be_success.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_error/should_response_json_data.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_error/should_return_message_response.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_error/should_return_status_of_response.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_timeout/response_should_be_success.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_timeout/should_response_json_data.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_timeout/should_return_message_response.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/server_timeout/should_return_status_of_response.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/success/response_should_be_success.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/success/should_response_json_data.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/success/should_return_message_response.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Client/_update/success/should_return_status_of_response.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Specifications/_check_policies/should_check_policies.yml +50 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Specifications/_update/should_update_list_gems_on_server.yml +50 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Specifications/_update/when_check_policies_is_true/and_check_policies_return_a_violation/should_not_update_inventory.yml +2984 -0
- data/spec/fixtures/vcr_cassettes/WssAgent_Specifications/_update/when_check_policies_is_true/and_check_policies_returns_without_a_violation/should_update_inventory.yml +2984 -0
- data/spec/fixtures/wss_agent.yml +9 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/wss_agent/cli_spec.rb +57 -0
- data/spec/wss_agent/client_spec.rb +137 -0
- data/spec/wss_agent/configure_spec.rb +148 -0
- data/spec/wss_agent/specifications_spec.rb +137 -0
- data/wss_agent.gemspec +38 -0
- metadata +390 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
module WssAgent
|
2
|
+
class Response
|
3
|
+
SUCCESS_STATUS = 1
|
4
|
+
BAD_REQUEST_STATUS = 2
|
5
|
+
SERVER_ERROR_STATUS = 3
|
6
|
+
|
7
|
+
attr_reader :response, :status, :message, :response_data, :data
|
8
|
+
|
9
|
+
def initialize(response)
|
10
|
+
@response = response
|
11
|
+
if response.is_a?(Faraday::Error::ClientError)
|
12
|
+
parse_error
|
13
|
+
else
|
14
|
+
parse_response
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def parse_error
|
19
|
+
@status = SERVER_ERROR_STATUS
|
20
|
+
@message = response.message
|
21
|
+
end
|
22
|
+
|
23
|
+
def parse_response
|
24
|
+
if response.success?
|
25
|
+
begin
|
26
|
+
@response_data = Oj.load(response.body)
|
27
|
+
@status = @response_data['status'].to_i
|
28
|
+
@message = @response_data['message']
|
29
|
+
rescue
|
30
|
+
@status = SERVER_ERROR_STATUS
|
31
|
+
@message = response.body
|
32
|
+
end
|
33
|
+
else
|
34
|
+
@status = SERVER_ERROR_STATUS
|
35
|
+
@message = response.body
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def response_success?
|
40
|
+
if response.is_a?(Faraday::Error::ClientError)
|
41
|
+
false
|
42
|
+
else
|
43
|
+
response.success?
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def success?
|
48
|
+
response_success? && status == SUCCESS_STATUS
|
49
|
+
end
|
50
|
+
|
51
|
+
def data
|
52
|
+
@data ||= Oj.load(response_data['data'])
|
53
|
+
rescue
|
54
|
+
response_data && response_data.key?('data') ? response_data['data'] : nil
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module WssAgent
|
2
|
+
class ResponseInventory < Response
|
3
|
+
|
4
|
+
def message
|
5
|
+
if success?
|
6
|
+
@message = "White Source update results: \n"
|
7
|
+
@message << " White Source organization: #{data['organization']} \n"
|
8
|
+
|
9
|
+
unless data['createdProjects'].empty?
|
10
|
+
@message << " #{data['createdProjects'].size} newly created projects: "
|
11
|
+
@message << data['createdProjects'].join(' ')
|
12
|
+
else
|
13
|
+
@message << " No new projects found \n"
|
14
|
+
end
|
15
|
+
|
16
|
+
unless data['updatedProjects'].empty?
|
17
|
+
@message << " #{data['updatedProjects'].size} existing projects were updated: "
|
18
|
+
@message << data['updatedProjects'].join(' ')
|
19
|
+
else
|
20
|
+
@message << "\n No projects were updated \n"
|
21
|
+
end
|
22
|
+
@message
|
23
|
+
else
|
24
|
+
super
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,76 @@
|
|
1
|
+
module WssAgent
|
2
|
+
class ResponsePolicies < Response
|
3
|
+
REJECT_ACTION = 'Reject'
|
4
|
+
|
5
|
+
def parse_response
|
6
|
+
if response.success?
|
7
|
+
begin
|
8
|
+
@response_data = Oj.load(response.body)
|
9
|
+
@status = @response_data['status'].to_i
|
10
|
+
@message = @response_data['message']
|
11
|
+
check_new_projects
|
12
|
+
check_existing_projects
|
13
|
+
rescue
|
14
|
+
@status = SERVER_ERROR_STATUS
|
15
|
+
@message = response.body
|
16
|
+
end
|
17
|
+
else
|
18
|
+
@status = SERVER_ERROR_STATUS
|
19
|
+
@message = response.body
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
|
24
|
+
def message
|
25
|
+
if success?
|
26
|
+
if policy_violations?
|
27
|
+
@message = [
|
28
|
+
'Some dependencies do not conform with open source policies',
|
29
|
+
'List of violations:'
|
30
|
+
]
|
31
|
+
@message << policy_violations.each_with_index.map { |j, i|
|
32
|
+
"#{i+1}. Package: #{j['resource']['displayName']} - #{j['policy']['displayName']}"
|
33
|
+
}.join("\n")
|
34
|
+
@message.join("\n")
|
35
|
+
else
|
36
|
+
"All dependencies conform with open source policies"
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
def policy_violations
|
42
|
+
@policy_violations || []
|
43
|
+
end
|
44
|
+
|
45
|
+
def policy_violations?
|
46
|
+
!policy_violations.nil? &&
|
47
|
+
!policy_violations.empty? &&
|
48
|
+
policy_violations.size > 0
|
49
|
+
end
|
50
|
+
|
51
|
+
def check_existing_projects
|
52
|
+
data['existingProjects'].each { |_proj_name, resource| check(resource) }
|
53
|
+
end
|
54
|
+
|
55
|
+
def check_new_projects
|
56
|
+
data['newProjects'].each { |_proj_name, resource| check(resource) }
|
57
|
+
end
|
58
|
+
|
59
|
+
def add_resource(resource)
|
60
|
+
@policy_violations ||= []
|
61
|
+
@policy_violations << resource
|
62
|
+
end
|
63
|
+
|
64
|
+
def check(resource)
|
65
|
+
if resource.key?('resource') && resource.key?('policy') &&
|
66
|
+
(resource['policy']['actionType'] == REJECT_ACTION)
|
67
|
+
add_resource({'resource' => resource['resource'],
|
68
|
+
'policy' => resource['policy']})
|
69
|
+
end
|
70
|
+
|
71
|
+
if resource.key?('children') && resource['children'].is_a?(Array)
|
72
|
+
resource['children'].each { |j| check(j) }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
require "digest"
|
2
|
+
|
3
|
+
module WssAgent
|
4
|
+
class Specifications
|
5
|
+
|
6
|
+
class << self
|
7
|
+
# Get dependencies
|
8
|
+
#
|
9
|
+
# @param [Hash]
|
10
|
+
# @option options [Boolean] 'all' if true then get all dependencies (include development dependencies)
|
11
|
+
# @option options [String] 'excludes' list gem name which need to exclude from end list
|
12
|
+
def specs(options = {})
|
13
|
+
list_gems = Bundler.load.specs.to_a
|
14
|
+
if options['all']
|
15
|
+
# get all gems
|
16
|
+
list = {}
|
17
|
+
list_gems.each { |j| list[j.name] = j }
|
18
|
+
list_gems.each { |j|
|
19
|
+
list = gem_dependencies(list, j.dependencies, options)
|
20
|
+
}
|
21
|
+
|
22
|
+
list_gems = list.values
|
23
|
+
end
|
24
|
+
list_gems
|
25
|
+
end
|
26
|
+
|
27
|
+
# Display list dependencies
|
28
|
+
#
|
29
|
+
# @param (see Specifications#specs)
|
30
|
+
def list(options = {})
|
31
|
+
new(specs(options)).call
|
32
|
+
end
|
33
|
+
|
34
|
+
# Send gem list to server
|
35
|
+
#
|
36
|
+
# @param (see Specifications#specs)
|
37
|
+
def update(options = {})
|
38
|
+
wss_client = WssAgent::Client.new
|
39
|
+
if WssAgent::Configure['check_policies']
|
40
|
+
policy_results = wss_client.check_policies(WssAgent::Specifications.list(options))
|
41
|
+
if policy_results.success? && policy_results.policy_violations?
|
42
|
+
puts policy_results.message
|
43
|
+
return false
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
result = wss_client.update(WssAgent::Specifications.list(options))
|
48
|
+
if result.success?
|
49
|
+
WssAgent.logger.debug result.data
|
50
|
+
puts result.message
|
51
|
+
else
|
52
|
+
ap "synchronization errors occur: status: #{result.status}, message: #{result.message}"
|
53
|
+
end
|
54
|
+
|
55
|
+
result.success?
|
56
|
+
end
|
57
|
+
|
58
|
+
# checking dependencies that they conforms with company policy.
|
59
|
+
#
|
60
|
+
# @param (see Specifications#specs)
|
61
|
+
def check_policies(options = {})
|
62
|
+
wss_client = WssAgent::Client.new
|
63
|
+
result = wss_client.check_policies(WssAgent::Specifications.list(options))
|
64
|
+
if result.success?
|
65
|
+
WssAgent.logger.debug result.data
|
66
|
+
puts result.message
|
67
|
+
else
|
68
|
+
ap "check policies errors occur: #{result.status}, message: #{result.message}, data: #{result.data}"
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
# Get all dependencies includes development
|
73
|
+
#
|
74
|
+
# @param [Array<Spec>] array for gems
|
75
|
+
# @param [Array<Dependencies>]
|
76
|
+
# @param [Hash]
|
77
|
+
# @options options [String] :excludes list gems to exclude
|
78
|
+
#
|
79
|
+
# @return [Array<Spec>] list
|
80
|
+
def gem_dependencies(list, gem_dependencies, options = {})
|
81
|
+
gem_dependencies.each do |gd|
|
82
|
+
if options['excludes'] && options['excludes'].to_s.split(',').include?(gd.name)
|
83
|
+
next
|
84
|
+
end
|
85
|
+
gs = gd.matching_specs.first
|
86
|
+
if gs
|
87
|
+
unless list[gs.name]
|
88
|
+
list[gs.name] = gs
|
89
|
+
unless gs.dependencies.empty?
|
90
|
+
list = gem_dependencies(list, gs.dependencies, options)
|
91
|
+
end
|
92
|
+
end
|
93
|
+
else
|
94
|
+
unless list[gd.name]
|
95
|
+
list[gd.name] = Gem::Specification.new(gd.name,
|
96
|
+
gd.requirements_list.last.scan(/[\d\.\w]+/).first)
|
97
|
+
rm_dep = remote_dependencies(gd.name, gd.requirements_list.last)
|
98
|
+
unless rm_dep.empty?
|
99
|
+
list = gem_dependencies(list, rm_dep, options)
|
100
|
+
end
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
list
|
106
|
+
end
|
107
|
+
|
108
|
+
# Load dependencies from rubygems
|
109
|
+
#
|
110
|
+
# @param gem_name [String] name gem
|
111
|
+
# @params version [String] version gem
|
112
|
+
#
|
113
|
+
# @return [Array<Gem::Dependency>] list gem dependencies
|
114
|
+
def remote_dependencies(gem_name, version)
|
115
|
+
conn = Faraday.new(url: 'https://rubygems.org') do |h|
|
116
|
+
h.headers[:content_type] = 'application/x-www-form-urlencoded'
|
117
|
+
h.request :url_encoded
|
118
|
+
h.adapter :excon
|
119
|
+
end
|
120
|
+
response = conn.get("/api/v1/gems/#{gem_name}.json")
|
121
|
+
dep_list = Oj.load(response.body)
|
122
|
+
dep_list['dependencies'].values.flatten.
|
123
|
+
map { |j| Gem::Dependency.new(j['name'], Gem::Requirement.new(j['requirements'].split(','))) }
|
124
|
+
end
|
125
|
+
end # end class << self
|
126
|
+
|
127
|
+
def initialize(gem_specs)
|
128
|
+
@gem_specs = gem_specs
|
129
|
+
end
|
130
|
+
|
131
|
+
def call
|
132
|
+
@gem_specs.map do |spec|
|
133
|
+
next if spec.name == WssAgent::NAME
|
134
|
+
gem_item(spec)
|
135
|
+
end.compact
|
136
|
+
end
|
137
|
+
|
138
|
+
def gem_item(spec)
|
139
|
+
{
|
140
|
+
'groupId' => spec.name,
|
141
|
+
'artifactId' => spec.file_name,
|
142
|
+
'version' => spec.version.to_s,
|
143
|
+
'sha1' => GemSha1.new(spec).sha1,
|
144
|
+
'optional' => false,
|
145
|
+
'children' => [],
|
146
|
+
'exclusions' => []
|
147
|
+
}
|
148
|
+
end
|
149
|
+
end
|
150
|
+
end
|
data/lib/wss_agent.rb
ADDED
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'thor'
|
2
|
+
require 'net/http'
|
3
|
+
require 'awesome_print'
|
4
|
+
require 'yaml'
|
5
|
+
require 'oj'
|
6
|
+
require 'faraday'
|
7
|
+
require 'faraday_middleware'
|
8
|
+
require 'yell'
|
9
|
+
require 'wss_agent/version'
|
10
|
+
require 'wss_agent/specifications'
|
11
|
+
require 'wss_agent/configure'
|
12
|
+
require 'wss_agent/cli'
|
13
|
+
require 'wss_agent/response'
|
14
|
+
require 'wss_agent/response_policies'
|
15
|
+
require 'wss_agent/response_inventory'
|
16
|
+
require 'wss_agent/client'
|
17
|
+
require 'wss_agent/gem_sha1'
|
18
|
+
require 'wss_agent/project'
|
19
|
+
|
20
|
+
|
21
|
+
module WssAgent
|
22
|
+
# Your code goes here...
|
23
|
+
|
24
|
+
class WssAgentError < StandardError
|
25
|
+
def self.status_code(code)
|
26
|
+
define_method(:status_code) { code }
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
class TokenNotFound < WssAgentError; status_code(10) ; end
|
31
|
+
class ApiUrlNotFound < WssAgentError; status_code(11) ; end
|
32
|
+
|
33
|
+
def self.logger
|
34
|
+
@logger ||= Yell.new STDOUT, level: [:info]
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.enable_debug!
|
38
|
+
@logger ||= Yell.new STDOUT, level: [:debug, :info, :warn, :error, :fatal, :unknown]
|
39
|
+
end
|
40
|
+
end
|