smart_proxy_omaha 0.0.1
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/Gemfile +14 -0
- data/LICENSE +675 -0
- data/README.md +99 -0
- data/Rakefile +14 -0
- data/bin/smart-proxy-omaha-sync +35 -0
- data/bundler.d/omaha.rb +1 -0
- data/extra/foreman-proxy-omaha-sync.cron +2 -0
- data/lib/smart_proxy_omaha.rb +3 -0
- data/lib/smart_proxy_omaha/configuration_loader.rb +17 -0
- data/lib/smart_proxy_omaha/dependency_injection.rb +8 -0
- data/lib/smart_proxy_omaha/foreman_client.rb +11 -0
- data/lib/smart_proxy_omaha/http_download.rb +65 -0
- data/lib/smart_proxy_omaha/http_request.rb +20 -0
- data/lib/smart_proxy_omaha/http_shared.rb +30 -0
- data/lib/smart_proxy_omaha/metadata.rb +34 -0
- data/lib/smart_proxy_omaha/metadata_provider.rb +23 -0
- data/lib/smart_proxy_omaha/omaha_api.rb +33 -0
- data/lib/smart_proxy_omaha/omaha_http_config.ru +9 -0
- data/lib/smart_proxy_omaha/omaha_plugin.rb +18 -0
- data/lib/smart_proxy_omaha/omaha_protocol.rb +65 -0
- data/lib/smart_proxy_omaha/omaha_protocol/eventacknowledgeresponse.rb +9 -0
- data/lib/smart_proxy_omaha/omaha_protocol/handler.rb +84 -0
- data/lib/smart_proxy_omaha/omaha_protocol/noupdateresponse.rb +9 -0
- data/lib/smart_proxy_omaha/omaha_protocol/request.rb +101 -0
- data/lib/smart_proxy_omaha/omaha_protocol/response.rb +33 -0
- data/lib/smart_proxy_omaha/omaha_protocol/updateresponse.rb +35 -0
- data/lib/smart_proxy_omaha/release.rb +121 -0
- data/lib/smart_proxy_omaha/release_provider.rb +37 -0
- data/lib/smart_proxy_omaha/release_repository.rb +11 -0
- data/lib/smart_proxy_omaha/syncer.rb +48 -0
- data/lib/smart_proxy_omaha/version.rb +5 -0
- data/settings.d/omaha.yml.example +5 -0
- data/smart_proxy_omaha.gemspec +23 -0
- data/test/fixtures/request_update_complete_error.xml +7 -0
- data/test/fixtures/request_update_complete_noupdate.xml +9 -0
- data/test/fixtures/request_update_complete_update.xml +9 -0
- data/test/fixtures/request_update_download_started.xml +7 -0
- data/test/fixtures/response_update_complete_error.xml +5 -0
- data/test/fixtures/response_update_complete_noupdate.xml +7 -0
- data/test/fixtures/response_update_complete_update.xml +19 -0
- data/test/fixtures/stable.html +97 -0
- data/test/omaha/http_download_test.rb +34 -0
- data/test/omaha/http_request_test.rb +12 -0
- data/test/omaha/metadata_provider_test.rb +33 -0
- data/test/omaha/omaha_api_test.rb +75 -0
- data/test/omaha/omaha_protocol/request_test.rb +77 -0
- data/test/omaha/release_provider_test.rb +59 -0
- data/test/omaha/release_test.rb +110 -0
- data/test/omaha/syncer_test.rb +41 -0
- data/test/test_helper.rb +18 -0
- metadata +167 -0
@@ -0,0 +1,65 @@
|
|
1
|
+
require 'nokogiri'
|
2
|
+
require 'smart_proxy_omaha/release_repository'
|
3
|
+
require 'smart_proxy_omaha/metadata'
|
4
|
+
require 'smart_proxy_omaha/metadata_provider'
|
5
|
+
require 'smart_proxy_omaha/omaha_protocol/response'
|
6
|
+
require 'smart_proxy_omaha/omaha_protocol/request'
|
7
|
+
require 'smart_proxy_omaha/omaha_protocol/eventacknowledgeresponse'
|
8
|
+
require 'smart_proxy_omaha/omaha_protocol/noupdateresponse'
|
9
|
+
require 'smart_proxy_omaha/omaha_protocol/updateresponse'
|
10
|
+
require 'smart_proxy_omaha/omaha_protocol/handler'
|
11
|
+
|
12
|
+
module Proxy::Omaha::OmahaProtocol
|
13
|
+
COREOS_APPID = 'e96281a6-d1af-4bde-9a0a-97b76e56dc57'
|
14
|
+
|
15
|
+
EVENT_TYPES = {
|
16
|
+
0 => 'unknown',
|
17
|
+
1 => 'download complete',
|
18
|
+
2 => 'install complete',
|
19
|
+
3 => 'update complete',
|
20
|
+
4 => 'uninstall',
|
21
|
+
5 => 'download started',
|
22
|
+
6 => 'install started',
|
23
|
+
9 => 'new application install started',
|
24
|
+
10 => 'setup started',
|
25
|
+
11 => 'setup finished',
|
26
|
+
12 => 'update application started',
|
27
|
+
13 => 'update download started',
|
28
|
+
14 => 'update download finished',
|
29
|
+
15 => 'update installer started',
|
30
|
+
16 => 'setup update begin',
|
31
|
+
17 => 'setup update complete',
|
32
|
+
20 => 'register product complete',
|
33
|
+
30 => 'OEM install first check',
|
34
|
+
40 => 'app-specific command started',
|
35
|
+
41 => 'app-specific command ended',
|
36
|
+
100 => 'setup failure',
|
37
|
+
102 => 'COM server failure',
|
38
|
+
103 => 'setup update failure',
|
39
|
+
800 => 'ping'
|
40
|
+
}.freeze
|
41
|
+
|
42
|
+
EVENT_RESULTS = {
|
43
|
+
0 => 'error',
|
44
|
+
1 => 'success',
|
45
|
+
2 => 'success reboot',
|
46
|
+
3 => 'success restart browser',
|
47
|
+
4 => 'cancelled',
|
48
|
+
5 => 'error installer MSI',
|
49
|
+
6 => 'error installer other',
|
50
|
+
7 => 'noupdate',
|
51
|
+
8 => 'error installer system',
|
52
|
+
9 => 'update deferred',
|
53
|
+
10 => 'handoff error'
|
54
|
+
}.freeze
|
55
|
+
|
56
|
+
def self.event_description(id)
|
57
|
+
id = 0 unless EVENT_TYPES.key?(id)
|
58
|
+
EVENT_TYPES[id]
|
59
|
+
end
|
60
|
+
|
61
|
+
def self.event_result(id)
|
62
|
+
id = 0 unless EVENT_RESULTS.key?(id)
|
63
|
+
EVENT_RESULTS[id]
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module Proxy::Omaha::OmahaProtocol
|
2
|
+
class Handler
|
3
|
+
include ::Proxy::Log
|
4
|
+
attr_reader :request, :foreman_client, :repository, :metadata_provider
|
5
|
+
|
6
|
+
def initialize(options = {})
|
7
|
+
@request = options.fetch(:request)
|
8
|
+
@foreman_client = options.fetch(:foreman_client)
|
9
|
+
@repository = options.fetch(:repository)
|
10
|
+
@metadata_provider = options.fetch(:metadata_provider)
|
11
|
+
end
|
12
|
+
|
13
|
+
def handle
|
14
|
+
logger.info "OmahaHandler: Received #{request.event_description} event with result: #{request.event_result}"
|
15
|
+
|
16
|
+
unless request.from_coreos?
|
17
|
+
logger.error "Appid does not match CoreOS. Aborting Omaha request."
|
18
|
+
return Proxy::Omaha::OmahaProtocol::Eventacknowledgeresponse.new(
|
19
|
+
:appid => request.appid,
|
20
|
+
:base_url => request.base_url,
|
21
|
+
:status => 'error-unknownApplication'
|
22
|
+
)
|
23
|
+
end
|
24
|
+
|
25
|
+
unless ['stable', 'beta', 'alpha'].include?(request.track)
|
26
|
+
logger.error "Unknown track requested. Aborting Omaha request."
|
27
|
+
return Proxy::Omaha::OmahaProtocol::Eventacknowledgeresponse.new(
|
28
|
+
:appid => request.appid,
|
29
|
+
:base_url => request.base_url,
|
30
|
+
:status => 'error-unknownApplication'
|
31
|
+
)
|
32
|
+
end
|
33
|
+
|
34
|
+
upload_facts
|
35
|
+
process_report
|
36
|
+
|
37
|
+
if request.updatecheck
|
38
|
+
handle_update
|
39
|
+
else
|
40
|
+
handle_event
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def upload_facts
|
47
|
+
foreman_client.post_facts(request.facts_data.to_json)
|
48
|
+
end
|
49
|
+
|
50
|
+
def handle_event
|
51
|
+
Proxy::Omaha::OmahaProtocol::Eventacknowledgeresponse.new(
|
52
|
+
:appid => request.appid,
|
53
|
+
:base_url => request.base_url
|
54
|
+
)
|
55
|
+
end
|
56
|
+
|
57
|
+
def process_report
|
58
|
+
report = {
|
59
|
+
'host' => request.hostname,
|
60
|
+
'status' => request.to_status.to_s,
|
61
|
+
'omaha_version' => request.version,
|
62
|
+
'reported_at' => Time.now.getutc.to_s
|
63
|
+
}
|
64
|
+
foreman_client.post_report({'omaha_report' => report}.to_json)
|
65
|
+
end
|
66
|
+
|
67
|
+
def handle_update
|
68
|
+
latest_os = repository.latest_os(request.track)
|
69
|
+
if !latest_os.nil? && latest_os > Gem::Version.new(request.version)
|
70
|
+
Proxy::Omaha::OmahaProtocol::Updateresponse.new(
|
71
|
+
:appid => request.appid,
|
72
|
+
:metadata => metadata_provider.get(request.track, latest_os),
|
73
|
+
:board => request.board,
|
74
|
+
:base_url => request.base_url
|
75
|
+
)
|
76
|
+
else
|
77
|
+
Proxy::Omaha::OmahaProtocol::Noupdateresponse.new(
|
78
|
+
:appid => request.appid,
|
79
|
+
:base_url => request.base_url
|
80
|
+
)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,101 @@
|
|
1
|
+
require 'resolv'
|
2
|
+
require 'ipaddr'
|
3
|
+
|
4
|
+
module Proxy::Omaha::OmahaProtocol
|
5
|
+
class Request
|
6
|
+
attr_reader :appid, :version, :track, :updatecheck, :eventtype, :eventresult, :board,
|
7
|
+
:alephversion, :oemversion, :oem,
|
8
|
+
:platform, :osmajor, :osminor, :hostname, :ipaddress, :ipaddress6,
|
9
|
+
:body, :ip, :base_url
|
10
|
+
|
11
|
+
def initialize(body, options)
|
12
|
+
@body = body
|
13
|
+
@ip = options.fetch(:ip)
|
14
|
+
@base_url = options.fetch(:base_url)
|
15
|
+
parse_request
|
16
|
+
parse_ipaddress
|
17
|
+
raise "Could not determine request hostname." if hostname.nil?
|
18
|
+
end
|
19
|
+
|
20
|
+
def facts_data
|
21
|
+
{
|
22
|
+
:name => hostname,
|
23
|
+
:facts => to_facts.merge({:_type => :foreman_omaha, :_timestamp => Time.now})
|
24
|
+
}
|
25
|
+
end
|
26
|
+
|
27
|
+
def to_status
|
28
|
+
return :downloading if eventtype == 13 && eventresult == 1
|
29
|
+
return :downloaded if eventtype == 14 && eventresult == 1
|
30
|
+
return :installed if eventtype == 3 && eventresult == 1
|
31
|
+
return :instance_hold if eventtype == 800 && eventresult == 1
|
32
|
+
return :complete if eventtype == 3 && eventresult == 2
|
33
|
+
return :error if eventtype == 3 && eventresult == 0
|
34
|
+
:unknown
|
35
|
+
end
|
36
|
+
|
37
|
+
def event_description
|
38
|
+
Proxy::Omaha::OmahaProtocol.event_description(eventtype)
|
39
|
+
end
|
40
|
+
|
41
|
+
def event_result
|
42
|
+
Proxy::Omaha::OmahaProtocol.event_result(eventresult)
|
43
|
+
end
|
44
|
+
|
45
|
+
def from_coreos?
|
46
|
+
appid == Proxy::Omaha::OmahaProtocol::COREOS_APPID
|
47
|
+
end
|
48
|
+
|
49
|
+
private
|
50
|
+
|
51
|
+
def parse_request
|
52
|
+
xml_request = Nokogiri::XML(body)
|
53
|
+
@appid = xml_request.xpath('/request/app/@appid').to_s.gsub(/^{?([a-f0-9\-]+)}?$/, '\1')
|
54
|
+
@version = xml_request.xpath('/request/app/@version').to_s
|
55
|
+
@osmajor = version.gsub(/^(\d+)\.\d\.\d$/, '\1')
|
56
|
+
@osminor = version.gsub(/^\d+\.(\d\.\d)$/, '\1')
|
57
|
+
@track = xml_request.xpath('/request/app/@track').to_s
|
58
|
+
@board = xml_request.xpath('/request/app/@board').to_s
|
59
|
+
@alephversion = xml_request.xpath('/request/app/@alephversion').to_s
|
60
|
+
@oemversion = xml_request.xpath('/request/app/@oemversion').to_s
|
61
|
+
@oem = xml_request.xpath('/request/app/@oem').to_s
|
62
|
+
@platform = xml_request.xpath('/request/os/@platform').to_s
|
63
|
+
@platform = 'CoreOS' if @platform.empty?
|
64
|
+
@updatecheck = !xml_request.xpath('/request/app/updatecheck').to_s.empty?
|
65
|
+
@eventtype = xml_request.xpath('/request/app/event/@eventtype').to_s.to_i
|
66
|
+
@eventresult = xml_request.xpath('/request/app/event/@eventresult').to_s.to_i
|
67
|
+
end
|
68
|
+
|
69
|
+
def parse_ipaddress
|
70
|
+
ipaddr = IPAddr.new(ip) rescue nil
|
71
|
+
return if ipaddr.nil?
|
72
|
+
@ipaddress = ipaddr.to_s if ipaddr.ipv4?
|
73
|
+
@ipaddress6 = ipaddr.to_s if ipaddr.ipv6?
|
74
|
+
@hostname = lookup_hostname(ipaddr.to_s)
|
75
|
+
end
|
76
|
+
|
77
|
+
def lookup_hostname(hostip)
|
78
|
+
Resolv.getname(hostip)
|
79
|
+
rescue Resolv::ResolvError
|
80
|
+
nil
|
81
|
+
end
|
82
|
+
|
83
|
+
def to_facts
|
84
|
+
{
|
85
|
+
:appid => appid,
|
86
|
+
:version => version,
|
87
|
+
:track => track,
|
88
|
+
:board => board,
|
89
|
+
:alephversion => alephversion,
|
90
|
+
:oemversion => oemversion,
|
91
|
+
:oem => oem,
|
92
|
+
:platform => platform,
|
93
|
+
:osmajor => osmajor,
|
94
|
+
:osminor => osminor,
|
95
|
+
:ipaddress => ipaddress,
|
96
|
+
:ipaddress6 => ipaddress6,
|
97
|
+
:hostname => hostname
|
98
|
+
}
|
99
|
+
end
|
100
|
+
end
|
101
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'uri'
|
2
|
+
|
3
|
+
module Proxy::Omaha::OmahaProtocol
|
4
|
+
class Response
|
5
|
+
include ::Proxy::Log
|
6
|
+
|
7
|
+
attr_reader :appid, :base_url, :host, :status
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@appid = options.fetch(:appid)
|
11
|
+
@base_url = options.fetch(:base_url)
|
12
|
+
@host = URI.parse(base_url).host
|
13
|
+
@status = options.fetch(:status, 'ok')
|
14
|
+
end
|
15
|
+
|
16
|
+
def to_xml
|
17
|
+
xml.to_xml
|
18
|
+
end
|
19
|
+
|
20
|
+
protected
|
21
|
+
|
22
|
+
def xml
|
23
|
+
@xml ||= Nokogiri::XML::Builder.new(:encoding => 'UTF-8') do |xml|
|
24
|
+
xml.response(:protocol => '3.0', :server => host) do
|
25
|
+
xml.daystart(:elapsed_seconds => 0)
|
26
|
+
xml.app(:app_id => appid, :status => status) do
|
27
|
+
xml_response(xml)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module Proxy::Omaha::OmahaProtocol
|
2
|
+
class Updateresponse < Response
|
3
|
+
attr_reader :metadata, :release, :architecture, :sha1_b64, :name, :size, :sha256_b64, :server, :track
|
4
|
+
|
5
|
+
def initialize(options = {})
|
6
|
+
@metadata = options.fetch(:metadata)
|
7
|
+
@architecture = options.fetch(:board)
|
8
|
+
@name = 'update.gz'
|
9
|
+
@size = metadata.size
|
10
|
+
@sha1_b64 = metadata.sha1_b64
|
11
|
+
@sha256_b64 = metadata.sha256_b64
|
12
|
+
@release = metadata.release
|
13
|
+
@track = metadata.track
|
14
|
+
super
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def xml_response(xml)
|
20
|
+
xml.updatecheck(:status => 'ok') do
|
21
|
+
xml.urls do
|
22
|
+
xml.url(:codebase => "#{base_url}/omahareleases/#{track}/#{release}/")
|
23
|
+
end
|
24
|
+
xml.manifest(:version => release) do
|
25
|
+
xml.packages do
|
26
|
+
xml.package(:hash => sha1_b64, :name => name, :size => size, :required => false)
|
27
|
+
end
|
28
|
+
xml.actions do
|
29
|
+
xml.action(:event => 'postinstall', :sha256 => sha256_b64, :needsadmin => false, :IsDelta => false, :DisablePayloadBackoff => true, :ChromeOSVersion => '')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,121 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'smart_proxy_omaha/http_download'
|
3
|
+
require 'smart_proxy_omaha/metadata_provider'
|
4
|
+
|
5
|
+
module Proxy::Omaha
|
6
|
+
class Release
|
7
|
+
include Proxy::Log
|
8
|
+
|
9
|
+
attr_accessor :track, :version
|
10
|
+
|
11
|
+
def initialize(options)
|
12
|
+
@track = options.fetch(:track).to_s
|
13
|
+
@version = Gem::Version.new(options.fetch(:version))
|
14
|
+
end
|
15
|
+
|
16
|
+
def path
|
17
|
+
@path ||= File.join(Proxy::Omaha::Plugin.settings.contentpath, track, version.to_s)
|
18
|
+
end
|
19
|
+
|
20
|
+
def metadata
|
21
|
+
metadata_provider.get(track, release)
|
22
|
+
end
|
23
|
+
|
24
|
+
def exists?
|
25
|
+
File.directory?(path)
|
26
|
+
end
|
27
|
+
|
28
|
+
def valid?
|
29
|
+
expected_files_exist?
|
30
|
+
end
|
31
|
+
|
32
|
+
def create
|
33
|
+
logger.debug "Creating #{track} #{version}"
|
34
|
+
return false unless create_path
|
35
|
+
return false unless download
|
36
|
+
return false unless create_metadata
|
37
|
+
true
|
38
|
+
end
|
39
|
+
|
40
|
+
def <=>(other)
|
41
|
+
return unless self.class === other
|
42
|
+
version.<=>(other.version)
|
43
|
+
end
|
44
|
+
|
45
|
+
def ==(other)
|
46
|
+
self.class === other && track == other.track && version == other.version
|
47
|
+
end
|
48
|
+
|
49
|
+
def to_s
|
50
|
+
version.to_s
|
51
|
+
end
|
52
|
+
|
53
|
+
def download
|
54
|
+
sources.map do |url|
|
55
|
+
file = URI.parse(url).path.split('/').last
|
56
|
+
dst = File.join(path, file)
|
57
|
+
logger.debug "Downloading file #{url} to #{dst}"
|
58
|
+
task = ::Proxy::Omaha::HttpDownload.new(url, dst)
|
59
|
+
task.start
|
60
|
+
task
|
61
|
+
end.each(&:join).map(&:result).all?
|
62
|
+
end
|
63
|
+
|
64
|
+
def create_metadata
|
65
|
+
metadata_provider.store(Metadata.new(
|
66
|
+
:track => track,
|
67
|
+
:release => version.to_s,
|
68
|
+
:size => File.size(updatefile),
|
69
|
+
:sha1_b64 => Digest::SHA1.file(updatefile).base64digest,
|
70
|
+
:sha256_b64 => Digest::SHA256.file(updatefile).base64digest,
|
71
|
+
))
|
72
|
+
true
|
73
|
+
rescue
|
74
|
+
false
|
75
|
+
end
|
76
|
+
|
77
|
+
def create_path
|
78
|
+
FileUtils.mkdir_p(path)
|
79
|
+
true
|
80
|
+
rescue
|
81
|
+
false
|
82
|
+
end
|
83
|
+
|
84
|
+
def updatefile
|
85
|
+
File.join(path, 'update.gz')
|
86
|
+
end
|
87
|
+
|
88
|
+
def sources
|
89
|
+
upstream = "https://#{track}.release.core-os.net/amd64-usr/#{version}"
|
90
|
+
[
|
91
|
+
"#{upstream}/coreos_production_pxe.vmlinuz",
|
92
|
+
"#{upstream}/coreos_production_pxe_image.cpio.gz",
|
93
|
+
"https://update.release.core-os.net/amd64-usr/#{version}/update.gz"
|
94
|
+
]
|
95
|
+
end
|
96
|
+
|
97
|
+
def expected_files
|
98
|
+
sources.map { |source| File.basename(source) }
|
99
|
+
end
|
100
|
+
|
101
|
+
def expected_files_exist?
|
102
|
+
expected_files.map {|file| File.file?(File.join(path, file)) }.all?
|
103
|
+
end
|
104
|
+
|
105
|
+
def purge
|
106
|
+
FileUtils.rm(Dir.glob(File.join(path, '*')), :force => true)
|
107
|
+
FileUtils.remove_dir(path)
|
108
|
+
true
|
109
|
+
rescue
|
110
|
+
false
|
111
|
+
end
|
112
|
+
|
113
|
+
private
|
114
|
+
|
115
|
+
def metadata_provider
|
116
|
+
MetadataProvider.new(
|
117
|
+
:contentpath => Proxy::Omaha::Plugin.settings.contentpath
|
118
|
+
)
|
119
|
+
end
|
120
|
+
end
|
121
|
+
end
|