minimart 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/.gitignore +21 -0
- data/.rspec +3 -0
- data/.travis.yml +13 -0
- data/Gemfile +3 -0
- data/README.md +198 -0
- data/Rakefile +45 -0
- data/bin/minimart +4 -0
- data/lib/minimart.rb +15 -0
- data/lib/minimart/cli.rb +93 -0
- data/lib/minimart/commands/mirror.rb +30 -0
- data/lib/minimart/commands/web.rb +91 -0
- data/lib/minimart/configuration.rb +46 -0
- data/lib/minimart/cookbook.rb +173 -0
- data/lib/minimart/download/cookbook.rb +70 -0
- data/lib/minimart/download/git_cache.rb +60 -0
- data/lib/minimart/download/git_repository.rb +41 -0
- data/lib/minimart/error.rb +32 -0
- data/lib/minimart/inventory_requirement/base_requirement.rb +81 -0
- data/lib/minimart/inventory_requirement/git_requirement.rb +87 -0
- data/lib/minimart/inventory_requirement/git_requirements_builder.rb +101 -0
- data/lib/minimart/inventory_requirement/local_path_requirement.rb +45 -0
- data/lib/minimart/inventory_requirement/local_requirements_builder.rb +31 -0
- data/lib/minimart/inventory_requirement/supermarket_requirements_builder.rb +35 -0
- data/lib/minimart/mirror.rb +12 -0
- data/lib/minimart/mirror/dependency_graph.rb +73 -0
- data/lib/minimart/mirror/download_metadata.rb +57 -0
- data/lib/minimart/mirror/inventory_builder.rb +143 -0
- data/lib/minimart/mirror/inventory_configuration.rb +74 -0
- data/lib/minimart/mirror/inventory_requirements.rb +104 -0
- data/lib/minimart/mirror/local_store.rb +107 -0
- data/lib/minimart/mirror/source.rb +57 -0
- data/lib/minimart/mirror/source_cookbook.rb +77 -0
- data/lib/minimart/mirror/sources.rb +37 -0
- data/lib/minimart/output.rb +34 -0
- data/lib/minimart/utils/archive.rb +39 -0
- data/lib/minimart/utils/file_helper.rb +34 -0
- data/lib/minimart/utils/http.rb +60 -0
- data/lib/minimart/version.rb +3 -0
- data/lib/minimart/web.rb +10 -0
- data/lib/minimart/web/cookbook_show_page_generator.rb +78 -0
- data/lib/minimart/web/cookbooks.rb +83 -0
- data/lib/minimart/web/dashboard_generator.rb +46 -0
- data/lib/minimart/web/html_generator.rb +52 -0
- data/lib/minimart/web/markdown_parser.rb +47 -0
- data/lib/minimart/web/template_helper.rb +132 -0
- data/lib/minimart/web/universe_generator.rb +109 -0
- data/minimart.gemspec +36 -0
- data/spec/fixtures/sample_cookbook.tar.gz +0 -0
- data/spec/fixtures/sample_cookbook/Berksfile +3 -0
- data/spec/fixtures/sample_cookbook/CHANGELOG.md +3 -0
- data/spec/fixtures/sample_cookbook/Gemfile +16 -0
- data/spec/fixtures/sample_cookbook/LICENSE +3 -0
- data/spec/fixtures/sample_cookbook/README.md +42 -0
- data/spec/fixtures/sample_cookbook/Thorfile +5 -0
- data/spec/fixtures/sample_cookbook/Vagrantfile +90 -0
- data/spec/fixtures/sample_cookbook/chefignore +94 -0
- data/spec/fixtures/sample_cookbook/metadata.rb +9 -0
- data/spec/fixtures/sample_cookbook/recipes/default.rb +8 -0
- data/spec/fixtures/sample_inventory.yml +16 -0
- data/spec/fixtures/simple_git_inventory.yml +8 -0
- data/spec/fixtures/simple_inventory.yml +6 -0
- data/spec/fixtures/simple_local_path_inventory.yml +5 -0
- data/spec/fixtures/universe.json +42 -0
- data/spec/fixtures/vcr_cassettes/local_path_cookbooks.yml +3316 -0
- data/spec/fixtures/vcr_cassettes/location_specific_cookbooks.yml +3316 -0
- data/spec/fixtures/vcr_cassettes/supermarket_cookbooks_graph.yml +905 -0
- data/spec/fixtures/vcr_cassettes/supermarket_cookbooks_installing_cookbooks.yml +4218 -0
- data/spec/lib/minimart/cli_spec.rb +104 -0
- data/spec/lib/minimart/commands/mirror_spec.rb +37 -0
- data/spec/lib/minimart/commands/web_spec.rb +75 -0
- data/spec/lib/minimart/configuration_spec.rb +54 -0
- data/spec/lib/minimart/cookbook_spec.rb +137 -0
- data/spec/lib/minimart/download/cookbook_spec.rb +135 -0
- data/spec/lib/minimart/download/git_cache_spec.rb +69 -0
- data/spec/lib/minimart/download/git_repository_spec.rb +39 -0
- data/spec/lib/minimart/error_spec.rb +18 -0
- data/spec/lib/minimart/inventory_requirement/base_requirement_spec.rb +38 -0
- data/spec/lib/minimart/inventory_requirement/git_requirement_spec.rb +92 -0
- data/spec/lib/minimart/inventory_requirement/git_requirements_builder_spec.rb +130 -0
- data/spec/lib/minimart/inventory_requirement/local_path_requirement_spec.rb +35 -0
- data/spec/lib/minimart/inventory_requirement/local_requirements_builder_spec.rb +33 -0
- data/spec/lib/minimart/inventory_requirement/supermarket_requirements_builder_spec.rb +69 -0
- data/spec/lib/minimart/mirror/dependency_graph_spec.rb +123 -0
- data/spec/lib/minimart/mirror/download_metadata_spec.rb +73 -0
- data/spec/lib/minimart/mirror/inventory_builder_spec.rb +195 -0
- data/spec/lib/minimart/mirror/inventory_configuration_spec.rb +86 -0
- data/spec/lib/minimart/mirror/inventory_requirements_spec.rb +78 -0
- data/spec/lib/minimart/mirror/local_store_spec.rb +64 -0
- data/spec/lib/minimart/mirror/source_spec.rb +54 -0
- data/spec/lib/minimart/mirror/sources_spec.rb +50 -0
- data/spec/lib/minimart/output_spec.rb +29 -0
- data/spec/lib/minimart/utils/archive_spec.rb +38 -0
- data/spec/lib/minimart/utils/file_helper_spec.rb +43 -0
- data/spec/lib/minimart/utils/http_spec.rb +37 -0
- data/spec/lib/minimart/web/cookbook_show_page_generator_spec.rb +101 -0
- data/spec/lib/minimart/web/cookbooks_spec.rb +70 -0
- data/spec/lib/minimart/web/dashboard_generator_spec.rb +33 -0
- data/spec/lib/minimart/web/html_generator_spec.rb +34 -0
- data/spec/lib/minimart/web/markdown_parser_spec.rb +54 -0
- data/spec/lib/minimart/web/template_helper_spec.rb +86 -0
- data/spec/lib/minimart/web/universe_generator_spec.rb +125 -0
- data/spec/spec_helper.rb +35 -0
- data/spec/support/file_system.rb +22 -0
- data/web/_assets/javascripts/app.js +164 -0
- data/web/_assets/javascripts/backbone.min.js +6 -0
- data/web/_assets/javascripts/jquery.min.js +4 -0
- data/web/_assets/javascripts/jquery.tabslet.min.js +9 -0
- data/web/_assets/javascripts/manifest.js +5 -0
- data/web/_assets/javascripts/underscore.min.js +5 -0
- data/web/_assets/stylesheets/font-awesome.min.css +4 -0
- data/web/_assets/stylesheets/font-mfizz.css +318 -0
- data/web/_assets/stylesheets/main.css +720 -0
- data/web/_assets/stylesheets/manifest.css +4 -0
- data/web/_assets/stylesheets/normalize.css +427 -0
- data/web/assets/fonts/FontAwesome.otf +0 -0
- data/web/assets/fonts/font-mfizz.eot +0 -0
- data/web/assets/fonts/font-mfizz.svg +1344 -0
- data/web/assets/fonts/font-mfizz.ttf +0 -0
- data/web/assets/fonts/font-mfizz.woff +0 -0
- data/web/assets/fonts/fontawesome-webfont.eot +0 -0
- data/web/assets/fonts/fontawesome-webfont.svg +520 -0
- data/web/assets/fonts/fontawesome-webfont.ttf +0 -0
- data/web/assets/fonts/fontawesome-webfont.woff +0 -0
- data/web/assets/images/header-slim.jpg +0 -0
- data/web/assets/images/icon-search.png +0 -0
- data/web/assets/images/mad-glory-logo.png +0 -0
- data/web/assets/images/main-gradient.png +0 -0
- data/web/assets/images/top-bar-logo.png +0 -0
- data/web/assets/javascripts/application.min.js +5 -0
- data/web/assets/stylesheets/application.min.css +4 -0
- data/web/templates/cookbook_show.erb +96 -0
- data/web/templates/dashboard.erb +81 -0
- data/web/templates/layout.erb +38 -0
- metadata +433 -0
@@ -0,0 +1,57 @@
|
|
1
|
+
module Minimart
|
2
|
+
module Mirror
|
3
|
+
# This class can be used to parse, and create `.minimart.json` files to
|
4
|
+
# store information about when, and how Minimart downloaded a given cookbook.
|
5
|
+
class DownloadMetadata
|
6
|
+
|
7
|
+
FILE_NAME = '.minimart.json'
|
8
|
+
|
9
|
+
# @return [String] the path to the directory containing the cookbook.
|
10
|
+
attr_reader :path_to_cookbook
|
11
|
+
|
12
|
+
# @return [Hash] the contents of the metadata file.
|
13
|
+
attr_reader :metadata
|
14
|
+
|
15
|
+
# @param [String] path_to_cookbook The path to the directory containing the cookbook.
|
16
|
+
def initialize(path_to_cookbook)
|
17
|
+
@path_to_cookbook = path_to_cookbook
|
18
|
+
parse_file
|
19
|
+
end
|
20
|
+
|
21
|
+
# Write the given contents to the metadata file. This will overwrite any
|
22
|
+
# existing contents.
|
23
|
+
# @param [Hash] contents The hash of data to write to the file.
|
24
|
+
def write(contents = {})
|
25
|
+
File.open(file_path, 'w+') do |file|
|
26
|
+
@metadata = contents
|
27
|
+
@metadata.merge!('downloaded_at' => Time.now.utc.iso8601)
|
28
|
+
|
29
|
+
file.write(metadata.to_json)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# @return [Time] The downloaded_at time found in the metadata file.
|
34
|
+
def downloaded_at
|
35
|
+
return unless self['downloaded_at']
|
36
|
+
Time.iso8601(metadata['downloaded_at']).utc
|
37
|
+
end
|
38
|
+
|
39
|
+
def [](key)
|
40
|
+
metadata[key] if metadata
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def parse_file
|
46
|
+
return unless File.exists?(file_path)
|
47
|
+
file_contents = File.open(file_path).read
|
48
|
+
@metadata = JSON.parse(file_contents)
|
49
|
+
end
|
50
|
+
|
51
|
+
def file_path
|
52
|
+
File.join(path_to_cookbook, FILE_NAME)
|
53
|
+
end
|
54
|
+
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,143 @@
|
|
1
|
+
module Minimart
|
2
|
+
module Mirror
|
3
|
+
|
4
|
+
# InventoryBuilder coordinates downloading any cookbooks, and their dependencies.
|
5
|
+
class InventoryBuilder
|
6
|
+
|
7
|
+
# @return [Minimart::Mirror::InventoryConfiguration] The user specified inventory config.
|
8
|
+
attr_reader :inventory_configuration
|
9
|
+
|
10
|
+
# @return [Minimart::Mirror::DependencyGraph]
|
11
|
+
attr_reader :graph
|
12
|
+
|
13
|
+
# @return [Minimart::Mirror::LocalStore] the local store manages the inventory directory contents
|
14
|
+
attr_reader :local_store
|
15
|
+
|
16
|
+
# @param [String] inventory_directory The directory to store the inventory.
|
17
|
+
# @param [Minimart::Mirror::InventoryConfiguration] inventory_configuration The inventory as defined by a user of Minimart.
|
18
|
+
def initialize(inventory_directory, inventory_configuration)
|
19
|
+
@graph = DependencyGraph.new
|
20
|
+
@local_store = LocalStore.new(inventory_directory)
|
21
|
+
@inventory_configuration = inventory_configuration
|
22
|
+
end
|
23
|
+
|
24
|
+
# Build the inventory!
|
25
|
+
def build!
|
26
|
+
install_cookbooks_with_explicit_location
|
27
|
+
add_source_cookbooks_to_graph
|
28
|
+
add_requirements_to_graph
|
29
|
+
fetch_inventory
|
30
|
+
display_success_message
|
31
|
+
|
32
|
+
ensure
|
33
|
+
clear_cache
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
|
38
|
+
# First we must install any cookbooks with a location specification (git, local path, etc..).
|
39
|
+
# These cookbooks and their associated metadata (any dependencies they have) take
|
40
|
+
# precedence over information found elsewhere.
|
41
|
+
def install_cookbooks_with_explicit_location
|
42
|
+
inventory_requirements.each_with_explicit_location do |requirement|
|
43
|
+
requirement.fetch_cookbook do |cookbook|
|
44
|
+
validate_cookbook_against_local_store(cookbook, requirement)
|
45
|
+
add_artifact_to_graph(cookbook)
|
46
|
+
add_cookbook_to_local_store(cookbook.path, requirement.to_hash)
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Fetch the universe from any of the defined sources, and add them as artifacts
|
52
|
+
# to the dependency resolution graph.
|
53
|
+
def add_source_cookbooks_to_graph
|
54
|
+
sources.each_cookbook { |cookbook| add_artifact_to_graph(cookbook) }
|
55
|
+
end
|
56
|
+
|
57
|
+
# Add any cookbooks defined in the inventory file as a requirement to the graph
|
58
|
+
def add_requirements_to_graph
|
59
|
+
inventory_requirements.each do |requirement|
|
60
|
+
graph.add_requirement(requirement.requirements)
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def fetch_inventory
|
65
|
+
resolved_requirements.each do |resolved_requirement|
|
66
|
+
install_cookbook(*resolved_requirement)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
def resolved_requirements
|
71
|
+
graph.resolved_requirements
|
72
|
+
end
|
73
|
+
|
74
|
+
def install_cookbook(name, version)
|
75
|
+
if cookbook_already_installed?(name, version)
|
76
|
+
Configuration.output.puts_yellow("cookbook already installed: #{name}-#{version}.")
|
77
|
+
return
|
78
|
+
end
|
79
|
+
|
80
|
+
verify_dependency_can_be_installed(name, version)
|
81
|
+
|
82
|
+
source_cookbook = cookbook_from_source(name, version)
|
83
|
+
source_cookbook.fetch do |cookbook|
|
84
|
+
add_cookbook_to_local_store(cookbook.path, source_cookbook.to_hash)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def cookbook_already_installed?(name, version)
|
89
|
+
local_store.installed?(name, version)
|
90
|
+
end
|
91
|
+
|
92
|
+
def verify_dependency_can_be_installed(name, version)
|
93
|
+
return unless non_required_version?(name, version)
|
94
|
+
|
95
|
+
msg = "The dependency #{name}-#{version} could not be installed."
|
96
|
+
msg << " This is because a cookbook listed in the inventory depends on a version of '#{name}'"
|
97
|
+
msg << " that does not match the explicit requirements for the '#{name}' cookbook."
|
98
|
+
raise Error::BrokenDependency, msg
|
99
|
+
end
|
100
|
+
|
101
|
+
def non_required_version?(name, version)
|
102
|
+
!inventory_requirements.version_required?(name, version)
|
103
|
+
end
|
104
|
+
|
105
|
+
# We need to validate that if this cookbook is in the store
|
106
|
+
# it came from the same source, otherwise we could
|
107
|
+
# possibly override a cookbook
|
108
|
+
def validate_cookbook_against_local_store(cookbook, requirement)
|
109
|
+
local_store.validate_resolved_requirement(cookbook, requirement)
|
110
|
+
end
|
111
|
+
|
112
|
+
def add_cookbook_to_local_store(cookbook_path, data = {})
|
113
|
+
local_store.add_cookbook_from_path(cookbook_path, data)
|
114
|
+
end
|
115
|
+
|
116
|
+
def cookbook_from_source(name, version)
|
117
|
+
sources.find_cookbook(name, version)
|
118
|
+
end
|
119
|
+
|
120
|
+
def add_artifact_to_graph(cookbook)
|
121
|
+
graph.add_artifact(cookbook)
|
122
|
+
end
|
123
|
+
|
124
|
+
def inventory_requirements
|
125
|
+
inventory_configuration.requirements
|
126
|
+
end
|
127
|
+
|
128
|
+
def sources
|
129
|
+
inventory_configuration.sources
|
130
|
+
end
|
131
|
+
|
132
|
+
def display_success_message
|
133
|
+
Configuration.output.puts_green %{Minimart is done building your inventory!}
|
134
|
+
Configuration.output.puts_green %{The inventory can be found in #{local_store.directory_path}}
|
135
|
+
end
|
136
|
+
|
137
|
+
def clear_cache
|
138
|
+
Minimart::Download::GitCache.instance.clear
|
139
|
+
end
|
140
|
+
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
require 'erb'
|
3
|
+
|
4
|
+
require 'minimart/mirror/inventory_requirements'
|
5
|
+
require 'minimart/utils/file_helper'
|
6
|
+
|
7
|
+
module Minimart
|
8
|
+
module Mirror
|
9
|
+
|
10
|
+
# This class is responsible for parsing a user defined Minimart configuration file.
|
11
|
+
class InventoryConfiguration
|
12
|
+
|
13
|
+
# The path to the inventory configuration file
|
14
|
+
attr_reader :inventory_config_path
|
15
|
+
|
16
|
+
# @param [String] inventory_config_path The path to the inventory configuration file
|
17
|
+
def initialize(inventory_config_path)
|
18
|
+
@inventory_config_path = inventory_config_path
|
19
|
+
@configuration = parse_config_file
|
20
|
+
parse_global_configuration
|
21
|
+
end
|
22
|
+
|
23
|
+
# The collection of files defined in the inventory file
|
24
|
+
# @return [Minimart::Mirror::Sources]
|
25
|
+
def sources
|
26
|
+
@sources ||= Sources.new(raw_sources)
|
27
|
+
end
|
28
|
+
|
29
|
+
# The collection of cookbook requirements defined in the inventory file
|
30
|
+
# @return [Array]
|
31
|
+
def requirements
|
32
|
+
@cookbooks ||= InventoryRequirements.new(raw_cookbooks)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
# The raw parsed configuration file
|
38
|
+
attr_reader :configuration
|
39
|
+
|
40
|
+
def parse_config_file
|
41
|
+
unless Utils::FileHelper.file_exists?(inventory_config_path)
|
42
|
+
raise Error::InvalidInventoryError, 'The inventory configuration file could not be found'
|
43
|
+
end
|
44
|
+
|
45
|
+
file = File.open(inventory_config_path).read
|
46
|
+
erb = ERB.new(file).result(binding)
|
47
|
+
YAML.load(erb)
|
48
|
+
end
|
49
|
+
|
50
|
+
def parse_global_configuration
|
51
|
+
return unless (conf = configuration['configuration']) && conf.is_a?(Hash)
|
52
|
+
|
53
|
+
Minimart::Configuration.tap do |c|
|
54
|
+
c.chef_server_config = conf.fetch('chef', {})
|
55
|
+
c.github_config = conf.fetch('github', [])
|
56
|
+
c.verify_ssl = conf['verify_ssl']
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
def raw_sources
|
61
|
+
configuration['sources'] || []
|
62
|
+
end
|
63
|
+
|
64
|
+
def raw_cookbooks
|
65
|
+
configuration['cookbooks'].tap do |cookbooks|
|
66
|
+
if cookbooks.nil? || cookbooks.empty?
|
67
|
+
raise Error::InvalidInventoryError, 'Minimart could not find any cookbooks defined in the inventory'
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,104 @@
|
|
1
|
+
require 'minimart/inventory_requirement/supermarket_requirements_builder'
|
2
|
+
require 'minimart/inventory_requirement/git_requirements_builder'
|
3
|
+
require 'minimart/inventory_requirement/local_requirements_builder'
|
4
|
+
|
5
|
+
module Minimart
|
6
|
+
module Mirror
|
7
|
+
|
8
|
+
# The collection of requirements as defined in the inventory file.
|
9
|
+
class InventoryRequirements
|
10
|
+
include Enumerable
|
11
|
+
|
12
|
+
# @return [Hash<String, Hash>] The cookbooks listed in the inventory file
|
13
|
+
attr_reader :raw_cookbooks
|
14
|
+
|
15
|
+
# @param [Hash<String, Hash>] raw_cookbooks The cookbooks listed in the inventory file
|
16
|
+
def initialize(raw_cookbooks)
|
17
|
+
@raw_cookbooks = raw_cookbooks
|
18
|
+
@requirements = {}
|
19
|
+
|
20
|
+
parse_cookbooks
|
21
|
+
end
|
22
|
+
|
23
|
+
# Iterate over the requirements
|
24
|
+
# @yield [Minimart::Inventory::BaseRequirement]
|
25
|
+
def each(&block)
|
26
|
+
requirements.values.flatten.each &block
|
27
|
+
end
|
28
|
+
|
29
|
+
def each_with_explicit_location(&block)
|
30
|
+
each do |req|
|
31
|
+
block.call(req) if req.explicit_location?
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# This method will determine whether or not a version of a cookbook
|
36
|
+
# resolves a constraint defined in the inventory file. This method
|
37
|
+
# can be used to verify that we aren't downloading a non specified version
|
38
|
+
# of a cookbook (e.g. possibly downloading cookbooks that a user wants,
|
39
|
+
# but in versions that they do not)
|
40
|
+
# @param [String] name The name of the cookbook to verify
|
41
|
+
# @param [String] version The cookbook version to check
|
42
|
+
# @return [Boolean] Return true if this version solves a requirement
|
43
|
+
# as specified in the inventory. This method will also return true for
|
44
|
+
# any cookbooks not specified in the inventory.
|
45
|
+
def version_required?(name, version)
|
46
|
+
(!has_cookbook?(name)) ||
|
47
|
+
solves_explicit_requirement?(name, version)
|
48
|
+
end
|
49
|
+
|
50
|
+
private
|
51
|
+
|
52
|
+
attr_reader :requirements
|
53
|
+
|
54
|
+
def has_cookbook?(name)
|
55
|
+
requirements.has_key?(name)
|
56
|
+
end
|
57
|
+
|
58
|
+
def solves_explicit_requirement?(name, version)
|
59
|
+
requirements[name].each do |req|
|
60
|
+
next unless req.version_requirement?
|
61
|
+
return true if Semverse::Constraint.new(req.version_requirement).satisfies?(version)
|
62
|
+
end
|
63
|
+
return false
|
64
|
+
end
|
65
|
+
|
66
|
+
# Build Minimart::Inventory requirements from the inventory.
|
67
|
+
def parse_cookbooks
|
68
|
+
raw_cookbooks.each do |name, reqs|
|
69
|
+
@requirements[name] = build_requirements_for(name, (reqs || {}))
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
73
|
+
def build_requirements_for(name, reqs)
|
74
|
+
get_requirements_for(name, reqs).tap do |parsed_requirements|
|
75
|
+
validate_requirements(name, parsed_requirements)
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def get_requirements_for(name, reqs)
|
80
|
+
(market_requirements(name, reqs) +
|
81
|
+
git_requirements(name, reqs) +
|
82
|
+
local_path_requirements(name, reqs)).flatten.compact
|
83
|
+
end
|
84
|
+
|
85
|
+
def validate_requirements(name, reqs)
|
86
|
+
return unless reqs.nil? || reqs.empty?
|
87
|
+
raise Minimart::Error::InvalidInventoryError,
|
88
|
+
"Minimart could not find any requirements for '#{name}'"
|
89
|
+
end
|
90
|
+
|
91
|
+
def market_requirements(name, reqs)
|
92
|
+
InventoryRequirement::SupermarketRequirementsBuilder.new(name, reqs).build
|
93
|
+
end
|
94
|
+
|
95
|
+
def git_requirements(name, reqs)
|
96
|
+
InventoryRequirement::GitRequirementsBuilder.new(name, reqs).build
|
97
|
+
end
|
98
|
+
|
99
|
+
def local_path_requirements(name, reqs)
|
100
|
+
InventoryRequirement::LocalRequirementsBuilder.new(name, reqs).build
|
101
|
+
end
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
require 'minimart/mirror/download_metadata'
|
2
|
+
require 'minimart/utils/file_helper'
|
3
|
+
|
4
|
+
module Minimart
|
5
|
+
module Mirror
|
6
|
+
|
7
|
+
# The LocalStore manages what is stored in the local inventory, and adding
|
8
|
+
# cookbook directories to the local inventory. The LocalStore will automatically
|
9
|
+
# load anything that has been stored in the inventory upon initialization.
|
10
|
+
class LocalStore
|
11
|
+
|
12
|
+
# @return [String] the path to the local inventory
|
13
|
+
attr_reader :directory_path
|
14
|
+
|
15
|
+
# @param [String] directory_path The path to the local inventory
|
16
|
+
def initialize(directory_path)
|
17
|
+
@directory_path = directory_path
|
18
|
+
@cookbooks = {}
|
19
|
+
|
20
|
+
load_local_inventory
|
21
|
+
end
|
22
|
+
|
23
|
+
# :nodoc:
|
24
|
+
def add_cookbook_to_store(name, version)
|
25
|
+
cookbooks[name] ||= []
|
26
|
+
cookbooks[name] << version
|
27
|
+
end
|
28
|
+
|
29
|
+
# Copy a given cookbook to the local store, and record any metadata
|
30
|
+
# about how the cookbook was downloaded.
|
31
|
+
# @param [String] path The path to the cookbook to add to the store
|
32
|
+
# @param [Hash] download_data Any data to record about the cookbook in a Minimart metadata file.
|
33
|
+
def add_cookbook_from_path(path, download_data = {})
|
34
|
+
cookbook_from_path(path).tap do |cookbook|
|
35
|
+
add_cookbook_to_store(cookbook.name, cookbook.version)
|
36
|
+
copy_cookbook(cookbook.path, local_path_for(cookbook))
|
37
|
+
write_download_data(cookbook, download_data)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
# Determine whether or not a cookbook has been added to the LocalStore
|
42
|
+
# @return [Boolean]
|
43
|
+
def installed?(cookbook_name, cookbook_version)
|
44
|
+
!!(cookbooks[cookbook_name] &&
|
45
|
+
cookbooks[cookbook_name].include?(cookbook_version))
|
46
|
+
end
|
47
|
+
|
48
|
+
# Validate that a new resolved requirement is not in the local store
|
49
|
+
# with different requirements. If we download two different branches
|
50
|
+
# of the same cookbook and they both resolve to the same version, then we
|
51
|
+
# raise an exception.
|
52
|
+
# @param [Minimart::Cookbook] new_cookbook
|
53
|
+
# @param [Minimart::InventoryRequirement::BaseRequirement] requirement
|
54
|
+
# @raise [Minimart::Error::BrokenDependency]
|
55
|
+
def validate_resolved_requirement(new_cookbook, requirement)
|
56
|
+
return unless installed?(new_cookbook.name, new_cookbook.version)
|
57
|
+
|
58
|
+
existing_cookbook = load_cookbook(new_cookbook)
|
59
|
+
unless requirement.matching_source?(existing_cookbook.download_metadata)
|
60
|
+
raise Minimart::Error::BrokenDependency, "A version of #{new_cookbook} already exists in the inventory from a different source."
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
private
|
65
|
+
|
66
|
+
attr_reader :cookbooks
|
67
|
+
|
68
|
+
def copy_cookbook(source, destination)
|
69
|
+
FileUtils.rm_rf(destination) if Dir.exists?(destination)
|
70
|
+
FileUtils.cp_r(source, destination)
|
71
|
+
|
72
|
+
# clean destination directory
|
73
|
+
git_dir = File.join(destination, '/.git')
|
74
|
+
FileUtils.rm_rf(git_dir) if Dir.exists?(git_dir)
|
75
|
+
end
|
76
|
+
|
77
|
+
def load_local_inventory
|
78
|
+
Utils::FileHelper.find_cookbooks_in_directory(directory).each do |path|
|
79
|
+
cookbook = cookbook_from_path(path)
|
80
|
+
add_cookbook_to_store(cookbook.name, cookbook.version)
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def load_cookbook(cookbook)
|
85
|
+
cookbook_from_path(local_path_for(cookbook))
|
86
|
+
end
|
87
|
+
|
88
|
+
def cookbook_from_path(path)
|
89
|
+
Minimart::Cookbook.from_path(Utils::FileHelper.cookbook_path_in_directory(path))
|
90
|
+
end
|
91
|
+
|
92
|
+
def directory
|
93
|
+
# lazily create the directory to hold the store
|
94
|
+
@directory ||= FileUtils.mkdir_p(directory_path).first
|
95
|
+
end
|
96
|
+
|
97
|
+
def local_path_for(cookbook)
|
98
|
+
File.join(directory, "/#{cookbook}")
|
99
|
+
end
|
100
|
+
|
101
|
+
def write_download_data(cookbook, download_data = {})
|
102
|
+
Minimart::Mirror::DownloadMetadata.new(local_path_for(cookbook)).write(download_data)
|
103
|
+
end
|
104
|
+
|
105
|
+
end
|
106
|
+
end
|
107
|
+
end
|