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.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +14 -0
  3. data/LICENSE +675 -0
  4. data/README.md +99 -0
  5. data/Rakefile +14 -0
  6. data/bin/smart-proxy-omaha-sync +35 -0
  7. data/bundler.d/omaha.rb +1 -0
  8. data/extra/foreman-proxy-omaha-sync.cron +2 -0
  9. data/lib/smart_proxy_omaha.rb +3 -0
  10. data/lib/smart_proxy_omaha/configuration_loader.rb +17 -0
  11. data/lib/smart_proxy_omaha/dependency_injection.rb +8 -0
  12. data/lib/smart_proxy_omaha/foreman_client.rb +11 -0
  13. data/lib/smart_proxy_omaha/http_download.rb +65 -0
  14. data/lib/smart_proxy_omaha/http_request.rb +20 -0
  15. data/lib/smart_proxy_omaha/http_shared.rb +30 -0
  16. data/lib/smart_proxy_omaha/metadata.rb +34 -0
  17. data/lib/smart_proxy_omaha/metadata_provider.rb +23 -0
  18. data/lib/smart_proxy_omaha/omaha_api.rb +33 -0
  19. data/lib/smart_proxy_omaha/omaha_http_config.ru +9 -0
  20. data/lib/smart_proxy_omaha/omaha_plugin.rb +18 -0
  21. data/lib/smart_proxy_omaha/omaha_protocol.rb +65 -0
  22. data/lib/smart_proxy_omaha/omaha_protocol/eventacknowledgeresponse.rb +9 -0
  23. data/lib/smart_proxy_omaha/omaha_protocol/handler.rb +84 -0
  24. data/lib/smart_proxy_omaha/omaha_protocol/noupdateresponse.rb +9 -0
  25. data/lib/smart_proxy_omaha/omaha_protocol/request.rb +101 -0
  26. data/lib/smart_proxy_omaha/omaha_protocol/response.rb +33 -0
  27. data/lib/smart_proxy_omaha/omaha_protocol/updateresponse.rb +35 -0
  28. data/lib/smart_proxy_omaha/release.rb +121 -0
  29. data/lib/smart_proxy_omaha/release_provider.rb +37 -0
  30. data/lib/smart_proxy_omaha/release_repository.rb +11 -0
  31. data/lib/smart_proxy_omaha/syncer.rb +48 -0
  32. data/lib/smart_proxy_omaha/version.rb +5 -0
  33. data/settings.d/omaha.yml.example +5 -0
  34. data/smart_proxy_omaha.gemspec +23 -0
  35. data/test/fixtures/request_update_complete_error.xml +7 -0
  36. data/test/fixtures/request_update_complete_noupdate.xml +9 -0
  37. data/test/fixtures/request_update_complete_update.xml +9 -0
  38. data/test/fixtures/request_update_download_started.xml +7 -0
  39. data/test/fixtures/response_update_complete_error.xml +5 -0
  40. data/test/fixtures/response_update_complete_noupdate.xml +7 -0
  41. data/test/fixtures/response_update_complete_update.xml +19 -0
  42. data/test/fixtures/stable.html +97 -0
  43. data/test/omaha/http_download_test.rb +34 -0
  44. data/test/omaha/http_request_test.rb +12 -0
  45. data/test/omaha/metadata_provider_test.rb +33 -0
  46. data/test/omaha/omaha_api_test.rb +75 -0
  47. data/test/omaha/omaha_protocol/request_test.rb +77 -0
  48. data/test/omaha/release_provider_test.rb +59 -0
  49. data/test/omaha/release_test.rb +110 -0
  50. data/test/omaha/syncer_test.rb +41 -0
  51. data/test/test_helper.rb +18 -0
  52. metadata +167 -0
data/README.md ADDED
@@ -0,0 +1,99 @@
1
+ # Smart Proxy - Omaha
2
+
3
+ This plug-in adds support for the Omaha Procotol to Foreman's Smart Proxy. It is used when updating CoreOS clusters.
4
+ The Smart Proxy Omaha plugin acts as a mirror for Omaha releases and sends reports to Foreman about the update activity of the managed hosts. It needs the [Foreman Omaha plugin](https://github.com/theforeman/foreman_omaha) installed to work properly.
5
+
6
+ ## How it works
7
+
8
+ The operatingsystem packages install a cronjob, that runs the binary `smart-proxy-omaha-sync` nightly to sync omaha content from the upstream mirror servers.
9
+ Your CoreOS hosts can then be configured to update against the smart proxy. The proxy then uploads facts and reports to Foreman.
10
+ Omaha content is served directly from the proxy.
11
+
12
+ ## Compatibility
13
+
14
+ | Foreman Proxy Version | Plugin Version |
15
+ | --------------------- | -------------- |
16
+ | >= 1.12 | any |
17
+
18
+ ## Installation
19
+
20
+ To be able to use Foreman for your Omaha updates, you need to install the [Foreman Omaha plugin](https://github.com/theforeman/foreman_omaha), install this smart-proxy plugin and configure your CoreOS hosts.
21
+
22
+ For the Smart Proxy plugin, follow the [smart-proxy plugin installation instructions](http://projects.theforeman.org/projects/foreman/wiki/How_to_Install_a_Smart-Proxy_Plugin). You usually just need to install the `rubygem-smart_proxy_omaha` package and restart the `foreman-proxy` service.
23
+
24
+ For the initial sync of releases you need to run the binary `smart-proxy-omaha-sync` as `foreman-proxy` user.
25
+
26
+ Do not forget to register the smart proxy in Foreman via the user interface.
27
+
28
+ ## Host Configuration
29
+
30
+ You need to configure your CoreOS hosts to connect to the Omaha smart-proxy for updates. You can either configure your servers manually or use cloud-config.
31
+
32
+ ### Using Config File
33
+
34
+ Edit `/etc/coreos/update.conf`
35
+
36
+ ```
37
+ GROUP=stable
38
+ SERVER=https://omahaproxy.example.com:8443/omaha/v1/update
39
+ ```
40
+
41
+ Restart update engine
42
+
43
+ ```bash
44
+ sudo systemctl restart update-engine
45
+ ```
46
+
47
+ ### Using Cloud-Config
48
+
49
+ ```yaml
50
+ #cloud-config
51
+ coreos:
52
+ update:
53
+ group: "stable"
54
+ server: "https://omahaproxy.example.com:8443/omaha/v1/update"
55
+ ```
56
+
57
+ ### Release channels
58
+
59
+ All three default release channels (alpha, beta, stable) are supported. You cannot define custom channels right now.
60
+
61
+ ### Testing
62
+
63
+ To test if a client can successfully check for updates, these commands may help:
64
+
65
+ ```bash
66
+ $ update_engine_client -check_for_update
67
+ $ journalctl -u update-engine.service
68
+ ```
69
+
70
+ ## Proxy Support
71
+
72
+ In the settings file you can specify a http proxy that is used to download Omaha content.
73
+ You need to allow https access to these servers:
74
+
75
+ * alpha.release.core-os.net
76
+ * beta.release.core-os.net
77
+ * stable.release.core-os.net
78
+ * update.release.core-os.net
79
+
80
+ ## Make it High Available
81
+
82
+ In order to make the Omaha Smart Proxy high available or add additional capacity, just scale out and put a loadbalancer in front of the proxies.
83
+
84
+ ## Copyright
85
+
86
+ Copyright (c) 2016 The Foreman developers
87
+
88
+ This program is free software: you can redistribute it and/or modify
89
+ it under the terms of the GNU General Public License as published by
90
+ the Free Software Foundation, either version 3 of the License, or
91
+ (at your option) any later version.
92
+
93
+ This program is distributed in the hope that it will be useful,
94
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
95
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
96
+ GNU General Public License for more details.
97
+
98
+ You should have received a copy of the GNU General Public License
99
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
data/Rakefile ADDED
@@ -0,0 +1,14 @@
1
+ require 'rake'
2
+ require 'rake/testtask'
3
+
4
+ desc 'Default: run unit tests.'
5
+ task default: :test
6
+
7
+ desc 'Test the Foreman Proxy plugin.'
8
+ Rake::TestTask.new(:test) do |t|
9
+ t.libs << '.'
10
+ t.libs << 'lib'
11
+ t.libs << 'test'
12
+ t.test_files = FileList['test/**/*_test.rb']
13
+ t.verbose = true
14
+ end
@@ -0,0 +1,35 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ $LOAD_PATH.unshift '/usr/share/foreman-proxy/lib'
4
+ $LOAD_PATH.unshift '/usr/share/foreman-proxy/modules'
5
+
6
+ require 'smart_proxy'
7
+ require 'smart_proxy_main'
8
+ require 'smart_proxy_omaha'
9
+ require 'smart_proxy_omaha/syncer'
10
+
11
+ module Proxy::LogBuffer
12
+ class Decorator
13
+ @@instance = Proxy::LogBuffer::Decorator.new(::Logger.new(STDOUT), 'STDOUT')
14
+ end
15
+ end
16
+ include Proxy::Log
17
+
18
+ ::Proxy::PluginInitializer.new(::Proxy::Plugins.instance).initialize_plugins
19
+
20
+ unless ::Proxy::Plugins.instance.plugin_enabled?(:omaha)
21
+ logger.info "Omaha plugin not enabled. Exiting."
22
+ exit
23
+ end
24
+
25
+ if !Proxy::SETTINGS.foreman_url
26
+ logger.error "Foreman URL not configured"
27
+ exit false
28
+ end
29
+
30
+ begin
31
+ Proxy::Omaha::Syncer.new.run
32
+ rescue StandardError => e
33
+ logger.error "#{e}"
34
+ exit false
35
+ end
@@ -0,0 +1 @@
1
+ gem 'smart_proxy_omaha'
@@ -0,0 +1,2 @@
1
+ # Sync omaha releases once a day
2
+ 0 22 * * * foreman-proxy smart-proxy-omaha-sync >>/var/log/foreman-proxy/cron.log 2>&1
@@ -0,0 +1,3 @@
1
+ require 'smart_proxy_omaha/version'
2
+ require 'smart_proxy_omaha/configuration_loader'
3
+ require 'smart_proxy_omaha/omaha_plugin'
@@ -0,0 +1,17 @@
1
+ module ::Proxy::Omaha
2
+ class ConfigurationLoader
3
+ def load_classes
4
+ require 'smart_proxy_omaha/dependency_injection'
5
+ require 'smart_proxy_omaha/foreman_client'
6
+ require 'smart_proxy_omaha/omaha_api'
7
+ end
8
+
9
+ def load_dependency_injection_wirings(container_instance, settings)
10
+ container_instance.singleton_dependency :foreman_client_impl, Proxy::Omaha::ForemanClient
11
+ container_instance.singleton_dependency :release_repository_impl, Proxy::Omaha::ReleaseRepository
12
+ container_instance.singleton_dependency :metadata_provider_impl, (lambda do
13
+ Proxy::Omaha::MetadataProvider.new(:contentpath => settings[:contentpath])
14
+ end)
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,8 @@
1
+ module Proxy::Omaha
2
+ module DependencyInjection
3
+ include Proxy::DependencyInjection::Accessors
4
+ def container_instance
5
+ @container_instance ||= ::Proxy::Plugins.instance.find { |p| p[:name] == :omaha }[:di_container]
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,11 @@
1
+ module Proxy::Omaha
2
+ class ForemanClient < Proxy::HttpRequest::ForemanRequest
3
+ def post_facts(factsdata)
4
+ send_request(request_factory.create_post('api/hosts/facts', factsdata))
5
+ end
6
+
7
+ def post_report(report)
8
+ send_request(request_factory.create_post('api/omaha_reports', report))
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,65 @@
1
+ require 'thread'
2
+ require 'smart_proxy_omaha/http_shared'
3
+
4
+ module Proxy::Omaha
5
+ class HttpDownload
6
+ include Proxy::Log
7
+ include HttpShared
8
+
9
+ attr_accessor :dst, :src, :result
10
+
11
+ def initialize(src, dst)
12
+ @src = src
13
+ @dst = dst
14
+ end
15
+
16
+ def start
17
+ @task = Thread.new do
18
+ @result = run
19
+ end
20
+ @task.abort_on_exception = true
21
+ @task
22
+ end
23
+
24
+ def run
25
+ with_filelock do
26
+ logger.info "Downloading #{src} to #{dst}."
27
+ res = download
28
+ logger.info "Finished downloading #{dst}."
29
+ res
30
+ end
31
+ end
32
+
33
+ def join
34
+ @task.join
35
+ end
36
+
37
+ private
38
+
39
+ def download
40
+ http, request = connection_factory(src)
41
+
42
+ http.request(request) do |response|
43
+ open(dst, 'w') do |io|
44
+ response.read_body do |chunk|
45
+ io.write chunk
46
+ end
47
+ end
48
+ end
49
+ true
50
+ end
51
+
52
+ def with_filelock
53
+ lock = Proxy::FileLock.try_locking(dst)
54
+ if lock.nil?
55
+ false
56
+ else
57
+ begin
58
+ yield
59
+ ensure
60
+ Proxy::FileLock.unlock(lock)
61
+ end
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,20 @@
1
+ require 'smart_proxy_omaha/http_shared'
2
+
3
+ module Proxy::Omaha
4
+ class HttpRequest
5
+ include Proxy::Log
6
+ include HttpShared
7
+
8
+ def get(url)
9
+ http, request = connection_factory(url)
10
+
11
+ Timeout::timeout(10) do
12
+ response = http.request(request)
13
+
14
+ raise "Error retrieving from #{url}: #{response.class}" unless ["200", "201"].include?(response.code)
15
+
16
+ response.body
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,30 @@
1
+ require 'net/http'
2
+ require 'net/https'
3
+ require 'uri'
4
+
5
+ module Proxy::Omaha
6
+ module HttpShared
7
+ def connection_factory(url)
8
+ uri = URI.parse(url)
9
+
10
+ if Proxy::Omaha::Plugin.settings.proxy.to_s.empty?
11
+ proxy_host = nil
12
+ proxy_port = nil
13
+ else
14
+ proxy = URI.parse(Proxy::Omaha::Plugin.settings.proxy)
15
+ proxy_host = proxy.host
16
+ proxy_port = proxy.port
17
+ end
18
+
19
+ http = Net::HTTP.new(uri.host, uri.port, proxy_host, proxy_port)
20
+
21
+ if uri.scheme == 'https'
22
+ http.use_ssl = true
23
+ end
24
+
25
+ request = Net::HTTP::Get.new(uri.request_uri)
26
+
27
+ [http, request]
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,34 @@
1
+ module Proxy::Omaha
2
+ class Metadata
3
+ attr_accessor :release, :sha1_b64, :sha256_b64, :size, :track
4
+
5
+ def initialize(params)
6
+ symbolize_keys_deep!(params)
7
+ @release = params.fetch(:release)
8
+ @sha1_b64 = params.fetch(:sha1_b64)
9
+ @sha256_b64 = params.fetch(:sha256_b64)
10
+ @size = params.fetch(:size)
11
+ @track = params.fetch(:track)
12
+ end
13
+
14
+ def to_json
15
+ {
16
+ :release => release,
17
+ :sha1_b64 => sha1_b64,
18
+ :sha256_b64 => sha256_b64,
19
+ :size => size,
20
+ :track => track,
21
+ }.to_json
22
+ end
23
+
24
+ private
25
+
26
+ def symbolize_keys_deep!(h)
27
+ h.keys.each do |k|
28
+ ks = k.to_sym
29
+ h[ks] = h.delete k
30
+ symbolize_keys_deep! h[ks] if h[ks].is_a? Hash
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,23 @@
1
+ module Proxy::Omaha
2
+ class MetadataProvider
3
+ attr_accessor :contentpath
4
+
5
+ def initialize(options)
6
+ @contentpath = options.fetch(:contentpath)
7
+ end
8
+
9
+ def get(track, release)
10
+ Metadata.new(JSON.parse(File.read(metadata_file(track, release))))
11
+ end
12
+
13
+ def store(metadata)
14
+ File.write(metadata_file(metadata.track, metadata.release), metadata.to_json)
15
+ end
16
+
17
+ private
18
+
19
+ def metadata_file(track, release)
20
+ File.join(contentpath, track, release.to_s, 'metadata.json')
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,33 @@
1
+ require 'sinatra'
2
+ require 'smart_proxy_omaha/omaha_protocol'
3
+
4
+ module Proxy::Omaha
5
+
6
+ class Api < ::Sinatra::Base
7
+ extend Proxy::Omaha::DependencyInjection
8
+
9
+ helpers ::Proxy::Helpers
10
+
11
+ inject_attr :foreman_client_impl, :foreman_client
12
+ inject_attr :release_repository_impl, :release_repository
13
+ inject_attr :metadata_provider_impl, :metadata_provider
14
+
15
+ post '/v1/update' do
16
+ request.body.rewind
17
+ request_body = request.body.read
18
+ omaha_request = Proxy::Omaha::OmahaProtocol::Request.new(
19
+ request_body,
20
+ :ip => request.ip,
21
+ :base_url => request.base_url
22
+ )
23
+ omaha_handler = Proxy::Omaha::OmahaProtocol::Handler.new(
24
+ :request => omaha_request,
25
+ :foreman_client => foreman_client,
26
+ :repository => release_repository,
27
+ :metadata_provider => metadata_provider
28
+ )
29
+ response = omaha_handler.handle
30
+ response.to_xml
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,9 @@
1
+ require 'smart_proxy_omaha/omaha_api'
2
+
3
+ map '/omaha' do
4
+ run Proxy::Omaha::Api
5
+ end
6
+
7
+ map "/omahareleases" do
8
+ run Rack::Directory.new(Proxy::Omaha::Plugin.settings.contentpath)
9
+ end
@@ -0,0 +1,18 @@
1
+ module Proxy::Omaha
2
+ class NotFound < RuntimeError; end
3
+
4
+ class Plugin < ::Proxy::Plugin
5
+ plugin 'omaha', Proxy::Omaha::VERSION
6
+
7
+ http_rackup_path File.expand_path('omaha_http_config.ru', File.expand_path('../', __FILE__))
8
+ https_rackup_path File.expand_path('omaha_http_config.ru', File.expand_path('../', __FILE__))
9
+
10
+ load_classes ::Proxy::Omaha::ConfigurationLoader
11
+ load_dependency_injection_wirings ::Proxy::Omaha::ConfigurationLoader
12
+
13
+ default_settings :sync_releases => 0,
14
+ :contentpath => '/var/lib/foreman-proxy/omaha/content'
15
+
16
+ validate_readable :contentpath
17
+ end
18
+ end