berkshelf-api 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +17 -0
- data/.ruby-version +1 -0
- data/.travis.yml +17 -0
- data/CONTRIBUTING.md +33 -0
- data/Gemfile +40 -0
- data/Guardfile +20 -0
- data/LICENSE +201 -0
- data/README.md +37 -0
- data/Thorfile +39 -0
- data/berkshelf-api.gemspec +35 -0
- data/bin/berks-api +5 -0
- data/lib/berkshelf-api.rb +1 -0
- data/lib/berkshelf/api.rb +25 -0
- data/lib/berkshelf/api/application.rb +114 -0
- data/lib/berkshelf/api/cache_builder.rb +60 -0
- data/lib/berkshelf/api/cache_builder/worker.rb +116 -0
- data/lib/berkshelf/api/cache_builder/worker/chef_server.rb +46 -0
- data/lib/berkshelf/api/cache_builder/worker/opscode.rb +59 -0
- data/lib/berkshelf/api/cache_manager.rb +96 -0
- data/lib/berkshelf/api/config.rb +23 -0
- data/lib/berkshelf/api/cucumber.rb +11 -0
- data/lib/berkshelf/api/dependency_cache.rb +123 -0
- data/lib/berkshelf/api/endpoint.rb +17 -0
- data/lib/berkshelf/api/endpoint/v1.rb +19 -0
- data/lib/berkshelf/api/errors.rb +8 -0
- data/lib/berkshelf/api/generic_server.rb +50 -0
- data/lib/berkshelf/api/logging.rb +37 -0
- data/lib/berkshelf/api/mixin.rb +7 -0
- data/lib/berkshelf/api/mixin/services.rb +48 -0
- data/lib/berkshelf/api/rack_app.rb +5 -0
- data/lib/berkshelf/api/remote_cookbook.rb +3 -0
- data/lib/berkshelf/api/rest_gateway.rb +62 -0
- data/lib/berkshelf/api/rspec.rb +20 -0
- data/lib/berkshelf/api/rspec/server.rb +29 -0
- data/lib/berkshelf/api/site_connector.rb +7 -0
- data/lib/berkshelf/api/site_connector/opscode.rb +162 -0
- data/lib/berkshelf/api/srv_ctl.rb +63 -0
- data/lib/berkshelf/api/version.rb +5 -0
- data/spec/fixtures/reset.pem +27 -0
- data/spec/spec_helper.rb +53 -0
- data/spec/support/actor_mocking.rb +7 -0
- data/spec/support/chef_server.rb +73 -0
- data/spec/unit/berkshelf/api/application_spec.rb +24 -0
- data/spec/unit/berkshelf/api/cache_builder/worker/chef_server_spec.rb +59 -0
- data/spec/unit/berkshelf/api/cache_builder/worker/opscode_spec.rb +41 -0
- data/spec/unit/berkshelf/api/cache_builder/worker_spec.rb +80 -0
- data/spec/unit/berkshelf/api/cache_builder_spec.rb +37 -0
- data/spec/unit/berkshelf/api/cache_manager_spec.rb +123 -0
- data/spec/unit/berkshelf/api/config_spec.rb +24 -0
- data/spec/unit/berkshelf/api/dependency_cache_spec.rb +109 -0
- data/spec/unit/berkshelf/api/endpoint/v1_spec.rb +18 -0
- data/spec/unit/berkshelf/api/logging_spec.rb +28 -0
- data/spec/unit/berkshelf/api/mixin/services_spec.rb +68 -0
- data/spec/unit/berkshelf/api/rack_app_spec.rb +6 -0
- data/spec/unit/berkshelf/api/rest_gateway_spec.rb +26 -0
- data/spec/unit/berkshelf/api/site_connector/opscode_spec.rb +85 -0
- data/spec/unit/berkshelf/api/srv_ctl_spec.rb +56 -0
- metadata +293 -0
data/bin/berks-api
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'berkshelf/api'
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'celluloid'
|
2
|
+
require 'hashie'
|
3
|
+
require 'ridley'
|
4
|
+
require 'faraday'
|
5
|
+
require 'json'
|
6
|
+
|
7
|
+
module Berkshelf
|
8
|
+
module API
|
9
|
+
require_relative 'api/errors'
|
10
|
+
require_relative 'api/logging'
|
11
|
+
require_relative 'api/mixin'
|
12
|
+
require_relative 'api/generic_server'
|
13
|
+
|
14
|
+
require_relative 'api/application'
|
15
|
+
require_relative 'api/cache_builder'
|
16
|
+
require_relative 'api/cache_manager'
|
17
|
+
require_relative 'api/config'
|
18
|
+
require_relative 'api/dependency_cache'
|
19
|
+
require_relative 'api/endpoint'
|
20
|
+
require_relative 'api/rack_app'
|
21
|
+
require_relative 'api/remote_cookbook'
|
22
|
+
require_relative 'api/site_connector'
|
23
|
+
require_relative 'api/srv_ctl'
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,114 @@
|
|
1
|
+
trap 'INT' do
|
2
|
+
Berkshelf::API::Application.shutdown
|
3
|
+
end
|
4
|
+
|
5
|
+
trap 'TERM' do
|
6
|
+
Berkshelf::API::Application.shutdown
|
7
|
+
end
|
8
|
+
|
9
|
+
module Berkshelf::API
|
10
|
+
class ApplicationSupervisor < Celluloid::SupervisionGroup
|
11
|
+
# @option options [Boolean] :disable_http (false)
|
12
|
+
# run the application without the rest gateway
|
13
|
+
def initialize(registry, options = {})
|
14
|
+
super(registry)
|
15
|
+
supervise_as(:cache_manager, Berkshelf::API::CacheManager)
|
16
|
+
supervise_as(:cache_builder, Berkshelf::API::CacheBuilder)
|
17
|
+
|
18
|
+
unless options[:disable_http]
|
19
|
+
require_relative 'rest_gateway'
|
20
|
+
supervise_as(:rest_gateway, Berkshelf::API::RESTGateway, options)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
module Application
|
26
|
+
class << self
|
27
|
+
extend Forwardable
|
28
|
+
include Berkshelf::API::Logging
|
29
|
+
include Berkshelf::API::Mixin::Services
|
30
|
+
|
31
|
+
def_delegators :registry, :[], :[]=
|
32
|
+
|
33
|
+
def config
|
34
|
+
@config ||= begin
|
35
|
+
Berkshelf::API::Config.from_file(Berkshelf::API::Config.default_path)
|
36
|
+
rescue
|
37
|
+
Berkshelf::API::Config.new
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# @option options [String, Fixnum] :log_location (STDOUT)
|
42
|
+
# @option options [String, nil] :log_level ("INFO")
|
43
|
+
# - "DEBUG
|
44
|
+
# - "INFO"
|
45
|
+
# - "WARN"
|
46
|
+
# - "ERROR"
|
47
|
+
# - "FATAL"
|
48
|
+
def configure_logger(options = {})
|
49
|
+
Logging.init(level: options[:log_level], location: options[:log_location])
|
50
|
+
end
|
51
|
+
|
52
|
+
def instance
|
53
|
+
return @instance if @instance
|
54
|
+
|
55
|
+
raise NotStartedError, "application not running"
|
56
|
+
end
|
57
|
+
|
58
|
+
# The Actor registry for Berkshelf::API.
|
59
|
+
#
|
60
|
+
# @note Berkshelf::API uses it's own registry instead of Celluloid::Registry.root to
|
61
|
+
# avoid conflicts in the larger namespace. Use Berkshelf::API::Application[] to access Berkshelf::API
|
62
|
+
# actors instead of Celluloid::Actor[].
|
63
|
+
#
|
64
|
+
# @return [Celluloid::Registry]
|
65
|
+
def registry
|
66
|
+
@registry ||= Celluloid::Registry.new
|
67
|
+
end
|
68
|
+
|
69
|
+
# Run the application in the foreground (sleep on main thread)
|
70
|
+
#
|
71
|
+
# @option options [Boolean] :disable_http (false)
|
72
|
+
# run the application without the rest gateway
|
73
|
+
def run(options = {})
|
74
|
+
loop do
|
75
|
+
supervisor = run!(options)
|
76
|
+
|
77
|
+
sleep 0.1 while supervisor.alive?
|
78
|
+
|
79
|
+
break if @shutdown
|
80
|
+
|
81
|
+
log.error "!!! #{self} crashed. Restarting..."
|
82
|
+
end
|
83
|
+
end
|
84
|
+
|
85
|
+
# Run the application in the background
|
86
|
+
#
|
87
|
+
# @option options [Boolean] :disable_http (false)
|
88
|
+
# run the application without the rest gateway
|
89
|
+
# @option options [Boolean] :eager_build (false)
|
90
|
+
# automatically begin and loop all cache builders
|
91
|
+
#
|
92
|
+
# @return [Berkshelf::API::Application]
|
93
|
+
def run!(options = {})
|
94
|
+
options = { disable_http: false, eager_build: false }.merge(options)
|
95
|
+
configure_logger(options)
|
96
|
+
@instance = ApplicationSupervisor.new(registry, options)
|
97
|
+
cache_builder.async(:build_loop) if options[:eager_build]
|
98
|
+
@instance
|
99
|
+
end
|
100
|
+
|
101
|
+
# @return [Boolean]
|
102
|
+
def running?
|
103
|
+
instance.alive?
|
104
|
+
rescue NotStartedError
|
105
|
+
false
|
106
|
+
end
|
107
|
+
|
108
|
+
def shutdown
|
109
|
+
@shutdown = true
|
110
|
+
instance.terminate
|
111
|
+
end
|
112
|
+
end
|
113
|
+
end
|
114
|
+
end
|
@@ -0,0 +1,60 @@
|
|
1
|
+
module Berkshelf::API
|
2
|
+
class CacheBuilder
|
3
|
+
require_relative 'cache_builder/worker'
|
4
|
+
|
5
|
+
class WorkerSupervisor < Celluloid::SupervisionGroup; end
|
6
|
+
|
7
|
+
BUILD_INTERVAL = 5.0
|
8
|
+
|
9
|
+
include Berkshelf::API::GenericServer
|
10
|
+
include Berkshelf::API::Logging
|
11
|
+
|
12
|
+
server_name :cache_builder
|
13
|
+
finalizer :finalize_callback
|
14
|
+
|
15
|
+
def initialize
|
16
|
+
log.info "Cache Builder starting..."
|
17
|
+
@worker_registry = Celluloid::Registry.new
|
18
|
+
@worker_supervisor = WorkerSupervisor.new(@worker_registry)
|
19
|
+
@building = false
|
20
|
+
|
21
|
+
Application.config.endpoints.each do |endpoint|
|
22
|
+
@worker_supervisor.supervise(CacheBuilder::Worker[endpoint.type], endpoint.options)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
# Issue a single build command to all workers
|
27
|
+
#
|
28
|
+
# @return [Array]
|
29
|
+
def build
|
30
|
+
workers.collect { |actor| actor.future(:build) }.map(&:value)
|
31
|
+
end
|
32
|
+
|
33
|
+
# Issue a build command to all workers at the scheduled interval
|
34
|
+
#
|
35
|
+
# @param [Fixnum, Float] interval
|
36
|
+
def build_loop(interval = BUILD_INTERVAL)
|
37
|
+
return if @building
|
38
|
+
|
39
|
+
loop do
|
40
|
+
@building = true
|
41
|
+
build
|
42
|
+
sleep BUILD_INTERVAL
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
# Return the list of running workers
|
47
|
+
#
|
48
|
+
# @return [Array<CacheBuilder::Worker::Base>]
|
49
|
+
def workers
|
50
|
+
@worker_supervisor.actors
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def finalize_callback
|
56
|
+
log.info "Cache Builder shutting down..."
|
57
|
+
@worker_supervisor.terminate if @worker_supervisor && @worker_supervisor.alive?
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
@@ -0,0 +1,116 @@
|
|
1
|
+
module Berkshelf::API
|
2
|
+
class CacheBuilder
|
3
|
+
module Worker
|
4
|
+
class Base
|
5
|
+
class << self
|
6
|
+
# @param [#to_s, nil] type
|
7
|
+
def worker_type(type = nil)
|
8
|
+
return @worker_type if @worker_type
|
9
|
+
@worker_type = type.to_s
|
10
|
+
Worker.register(@worker_type, self)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
include Celluloid
|
15
|
+
include Berkshelf::API::Logging
|
16
|
+
include Berkshelf::API::Mixin::Services
|
17
|
+
|
18
|
+
attr_reader :options
|
19
|
+
|
20
|
+
def initialize(options = {}); end
|
21
|
+
|
22
|
+
# @abstract
|
23
|
+
#
|
24
|
+
# @param [RemoteCookbook] remote
|
25
|
+
#
|
26
|
+
# @return [Ridley::Chef::Cookbook::Metadata]
|
27
|
+
def metadata(remote)
|
28
|
+
raise RuntimeError, "must be implemented"
|
29
|
+
end
|
30
|
+
|
31
|
+
# @abstract
|
32
|
+
#
|
33
|
+
# @return [Array<RemoteCookbook>]
|
34
|
+
# The list of cookbooks this builder can find
|
35
|
+
def cookbooks
|
36
|
+
raise RuntimeError, "must be implemented"
|
37
|
+
end
|
38
|
+
|
39
|
+
def build
|
40
|
+
log.info "#{self} building..."
|
41
|
+
log.info "#{self} determining if the cache is stale..."
|
42
|
+
if stale?
|
43
|
+
log.info "#{self} cache is stale."
|
44
|
+
update_cache
|
45
|
+
else
|
46
|
+
log.info "#{self} cache is up to date."
|
47
|
+
end
|
48
|
+
|
49
|
+
log.info "clearing diff"
|
50
|
+
clear_diff
|
51
|
+
end
|
52
|
+
|
53
|
+
# @return [Array<Array<RemoteCookbook>, Array<RemoteCookbook>>]
|
54
|
+
def diff
|
55
|
+
@diff ||= cache_manager.diff(cookbooks)
|
56
|
+
end
|
57
|
+
|
58
|
+
def update_cache
|
59
|
+
created_cookbooks, deleted_cookbooks = diff
|
60
|
+
|
61
|
+
log.info "#{self} adding (#{created_cookbooks.length}) items..."
|
62
|
+
created_cookbooks.collect do |remote|
|
63
|
+
[ remote, future(:metadata, remote) ]
|
64
|
+
end.each do |remote, metadata|
|
65
|
+
cache_manager.add(remote, metadata.value)
|
66
|
+
end
|
67
|
+
|
68
|
+
log.info "#{self} removing (#{deleted_cookbooks.length}) items..."
|
69
|
+
deleted_cookbooks.each { |remote| cache_manager.remove(remote.name, remote.version) }
|
70
|
+
|
71
|
+
log.info "#{self} cache updated."
|
72
|
+
cache_manager.save
|
73
|
+
end
|
74
|
+
|
75
|
+
def stale?
|
76
|
+
created_cookbooks, deleted_cookbooks = diff
|
77
|
+
created_cookbooks.any? || deleted_cookbooks.any?
|
78
|
+
end
|
79
|
+
|
80
|
+
private
|
81
|
+
|
82
|
+
def clear_diff
|
83
|
+
@diff = nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
class << self
|
88
|
+
# @param [#to_s] name
|
89
|
+
#
|
90
|
+
# @return [Worker::Base]
|
91
|
+
def [](name)
|
92
|
+
types[name.to_s]
|
93
|
+
end
|
94
|
+
|
95
|
+
# @param [#to_s] name
|
96
|
+
# @param [Worker::Base] klass
|
97
|
+
def register(name, klass)
|
98
|
+
name = name.to_s
|
99
|
+
if types.has_key?(name)
|
100
|
+
raise RuntimeError, "worker already registered with the name '#{name}'"
|
101
|
+
end
|
102
|
+
types[name] = klass
|
103
|
+
end
|
104
|
+
|
105
|
+
# @return [Hash]
|
106
|
+
def types
|
107
|
+
@types ||= Hash.new
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
end
|
112
|
+
end
|
113
|
+
|
114
|
+
Dir["#{File.dirname(__FILE__)}/worker/*.rb"].sort.each do |path|
|
115
|
+
require_relative "worker/#{File.basename(path, '.rb')}"
|
116
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
module Berkshelf::API
|
2
|
+
class CacheBuilder
|
3
|
+
module Worker
|
4
|
+
class ChefServer < Worker::Base
|
5
|
+
worker_type "chef_server"
|
6
|
+
|
7
|
+
finalizer :finalize_callback
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@connection = Ridley::Client.new_link(server_url: options[:url], client_key: options[:client_key],
|
11
|
+
client_name: options[:client_name], ssl: { verify: options[:ssl_verify] })
|
12
|
+
super
|
13
|
+
end
|
14
|
+
|
15
|
+
# @return [Array<RemoteCookbook>]
|
16
|
+
# The list of cookbooks this builder can find
|
17
|
+
def cookbooks
|
18
|
+
[].tap do |cookbook_versions|
|
19
|
+
connection.cookbook.all.each do |cookbook, versions|
|
20
|
+
versions.each do |version|
|
21
|
+
cookbook_versions << RemoteCookbook.new(cookbook, version, self.class.worker_type,
|
22
|
+
@connection.server_url)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param [RemoteCookbook] remote
|
29
|
+
#
|
30
|
+
# @return [Ridley::Chef::Cookbook::Metadata]
|
31
|
+
def metadata(remote)
|
32
|
+
metadata_hash = connection.cookbook.find(remote.name, remote.version).metadata
|
33
|
+
Ridley::Chef::Cookbook::Metadata.from_hash(metadata_hash)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
attr_reader :connection
|
39
|
+
|
40
|
+
def finalize_callback
|
41
|
+
connection.terminate if connection && connection.alive?
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
module Berkshelf::API
|
2
|
+
class CacheBuilder
|
3
|
+
module Worker
|
4
|
+
class Opscode < Worker::Base
|
5
|
+
worker_type "opscode"
|
6
|
+
|
7
|
+
finalizer :finalize_callback
|
8
|
+
|
9
|
+
def initialize(options = {})
|
10
|
+
@connection = Berkshelf::API::SiteConnector::Opscode.pool_link(size: 25)
|
11
|
+
super
|
12
|
+
end
|
13
|
+
|
14
|
+
# @return [Array<RemoteCookbook>]
|
15
|
+
# The list of cookbooks this builder can find
|
16
|
+
def cookbooks
|
17
|
+
[].tap do |cookbook_versions|
|
18
|
+
connection.cookbooks.collect do |cookbook|
|
19
|
+
[ cookbook, connection.future(:versions, cookbook) ]
|
20
|
+
end.each do |cookbook, versions|
|
21
|
+
versions.value.each do |version|
|
22
|
+
cookbook_versions << RemoteCookbook.new(cookbook, version, self.class.worker_type, @connection.api_uri)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# @param [RemoteCookbook] remote
|
29
|
+
#
|
30
|
+
# @return [Ridley::Chef::Cookbook::Metadata]
|
31
|
+
def metadata(remote)
|
32
|
+
Dir.mktmpdir do |destination|
|
33
|
+
connection.download(remote.name, remote.version, destination)
|
34
|
+
load_metadata(destination, remote.name)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
private
|
39
|
+
|
40
|
+
attr_accessor :connection
|
41
|
+
|
42
|
+
def finalize_callback
|
43
|
+
connection.terminate if connection && connection.alive?
|
44
|
+
end
|
45
|
+
|
46
|
+
def load_metadata(directory, cookbook)
|
47
|
+
# The community site does not enforce the name of the cookbook contained in the archive
|
48
|
+
# downloaded and extracted. This will just find the first metadata.json and load it.
|
49
|
+
file = Dir["#{directory}/**/*/metadata.json"].first
|
50
|
+
metadata = File.read(file)
|
51
|
+
Ridley::Chef::Cookbook::Metadata.from_json(metadata)
|
52
|
+
rescue JSON::ParserError => ex
|
53
|
+
log.warn "Error loading metadata for #{cookbook} from: #{file}"
|
54
|
+
abort MetadataLoadError.new(ex)
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,96 @@
|
|
1
|
+
module Berkshelf::API
|
2
|
+
class CacheManager
|
3
|
+
class << self
|
4
|
+
attr_writer :cache_file
|
5
|
+
|
6
|
+
# @return [String]
|
7
|
+
def cache_file
|
8
|
+
@cache_file ||= File.expand_path("~/.berkshelf/api-server/cerch")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
include Berkshelf::API::GenericServer
|
13
|
+
include Berkshelf::API::Logging
|
14
|
+
|
15
|
+
SAVE_INTERVAL = 30.0
|
16
|
+
|
17
|
+
server_name :cache_manager
|
18
|
+
finalizer :finalize_callback
|
19
|
+
exclusive :add, :clear, :remove, :save
|
20
|
+
|
21
|
+
attr_reader :cache
|
22
|
+
|
23
|
+
def initialize
|
24
|
+
log.info "Cache Manager starting..."
|
25
|
+
@cache = DependencyCache.new
|
26
|
+
load_save if File.exist?(self.class.cache_file)
|
27
|
+
every(SAVE_INTERVAL) { save }
|
28
|
+
end
|
29
|
+
|
30
|
+
# @param [RemoteCookbook] cookbook
|
31
|
+
# @param [Ridley::Chef::Cookbook::Metadata] metadata
|
32
|
+
#
|
33
|
+
# @return [Hash]
|
34
|
+
def add(cookbook, metadata)
|
35
|
+
@cache.add(cookbook, metadata)
|
36
|
+
end
|
37
|
+
|
38
|
+
# Clear any items added to the cache
|
39
|
+
#
|
40
|
+
# @return [Hash]
|
41
|
+
def clear
|
42
|
+
@cache.clear
|
43
|
+
end
|
44
|
+
|
45
|
+
# Check if the cache knows about the given cookbook version
|
46
|
+
#
|
47
|
+
# @param [#to_s] name
|
48
|
+
# @param [#to_s] version
|
49
|
+
#
|
50
|
+
# @return [Boolean]
|
51
|
+
def has_cookbook?(name, version)
|
52
|
+
@cache.has_cookbook?(name, version)
|
53
|
+
end
|
54
|
+
|
55
|
+
def load_save
|
56
|
+
@cache = DependencyCache.from_file(self.class.cache_file)
|
57
|
+
end
|
58
|
+
|
59
|
+
# Remove the cached item matching the given name and version
|
60
|
+
#
|
61
|
+
# @param [#to_s] name
|
62
|
+
# @param [#to_s] version
|
63
|
+
#
|
64
|
+
# @return [DependencyCache]
|
65
|
+
def remove(name, version)
|
66
|
+
@cache.remove(name, version)
|
67
|
+
end
|
68
|
+
|
69
|
+
def save
|
70
|
+
log.info "Saving the cache to: #{self.class.cache_file}"
|
71
|
+
cache.save(self.class.cache_file)
|
72
|
+
log.info "Cache saved!"
|
73
|
+
end
|
74
|
+
|
75
|
+
# @param [Array<RemoteCookbook>] cookbooks
|
76
|
+
# An array of RemoteCookbooks representing all the cookbooks on the indexed site
|
77
|
+
#
|
78
|
+
# @return [Array<Array<RemoteCookbook>, Array<RemoteCookbook>>]
|
79
|
+
# A tuple of Arrays of RemoteCookbooks
|
80
|
+
# The first array contains items not in the cache
|
81
|
+
# The second array contains items in the cache, but not in the cookbooks parameter
|
82
|
+
def diff(cookbooks)
|
83
|
+
known_cookbooks = cache.cookbooks
|
84
|
+
created_cookbooks = cookbooks - known_cookbooks
|
85
|
+
deleted_cookbooks = known_cookbooks - cookbooks
|
86
|
+
[ created_cookbooks, deleted_cookbooks ]
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
|
91
|
+
def finalize_callback
|
92
|
+
log.info "Cache Manager shutting down..."
|
93
|
+
self.save
|
94
|
+
end
|
95
|
+
end
|
96
|
+
end
|