-
Notifications
You must be signed in to change notification settings - Fork 170
Export archive #432
Export archive #432
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,6 +16,10 @@ | |
# | ||
|
||
require 'fileutils' | ||
require 'tmpdir' | ||
require 'zlib' | ||
|
||
require 'archive/tar/minitar' | ||
|
||
require 'chef-dk/service_exceptions' | ||
require 'chef-dk/policyfile_lock' | ||
|
@@ -38,9 +42,10 @@ class ExportRepo | |
attr_reader :root_dir | ||
attr_reader :export_dir | ||
|
||
def initialize(policyfile: nil, export_dir: nil, root_dir: nil, force: false) | ||
def initialize(policyfile: nil, export_dir: nil, root_dir: nil, archive: false, force: false) | ||
@root_dir = root_dir | ||
@export_dir = File.expand_path(export_dir) | ||
@archive = archive | ||
@force_export = force | ||
|
||
@policy_data = nil | ||
|
@@ -49,6 +54,12 @@ def initialize(policyfile: nil, export_dir: nil, root_dir: nil, force: false) | |
policyfile_rel_path = policyfile || "Policyfile.rb" | ||
policyfile_full_path = File.expand_path(policyfile_rel_path, root_dir) | ||
@storage_config = Policyfile::StorageConfig.new.use_policyfile(policyfile_full_path) | ||
|
||
@staging_dir = nil | ||
end | ||
|
||
def archive? | ||
@archive | ||
end | ||
|
||
def policy_name | ||
|
@@ -74,24 +85,59 @@ def policyfile_lock | |
@policyfile_lock || validate_lockfile | ||
end | ||
|
||
def archive_file_location | ||
return nil unless archive? | ||
filename = "#{policyfile_lock.name}-#{policyfile_lock.revision_id}.tgz" | ||
File.join(export_dir, filename) | ||
end | ||
|
||
def export | ||
create_repo_structure | ||
copy_cookbooks | ||
create_policyfile_data_item | ||
with_staging_dir do | ||
create_repo_structure | ||
copy_cookbooks | ||
create_policyfile_data_item | ||
if archive? | ||
create_archive | ||
else | ||
mv_staged_repo | ||
end | ||
end | ||
rescue => error | ||
msg = "Failed to export policy (in #{policyfile_filename}) to #{export_dir}" | ||
raise PolicyfileExportRepoError.new(msg, error) | ||
end | ||
|
||
private | ||
|
||
def create_repo_structure | ||
FileUtils.rm_rf(cookbooks_dir) | ||
FileUtils.rm_rf(policyfiles_data_bag_dir) | ||
def with_staging_dir | ||
p = Process.pid | ||
t = Time.new.utc.strftime("%Y%m%d%H%M%S") | ||
Dir.mktmpdir("chefdk-export-#{p}-#{t}") do |d| | ||
begin | ||
@staging_dir = d | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could you just yield staging_dir? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Lemme see how many places I'll have to thread it around. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I looked into it and it made everything a lot uglier. In this case I don't think it's that big a deal since everything that depends on There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. fair enough |
||
yield | ||
ensure | ||
@staging_dir = nil | ||
end | ||
end | ||
end | ||
|
||
def create_archive | ||
Zlib::GzipWriter.open(archive_file_location) do |gz_file| | ||
Dir.chdir(staging_dir) do | ||
Archive::Tar::Minitar.pack(".", gz_file) | ||
end | ||
end | ||
end | ||
|
||
def staging_dir | ||
@staging_dir | ||
end | ||
|
||
def create_repo_structure | ||
FileUtils.mkdir_p(export_dir) | ||
FileUtils.mkdir_p(cookbooks_dir) | ||
FileUtils.mkdir_p(policyfiles_data_bag_dir) | ||
FileUtils.mkdir_p(cookbooks_staging_dir) | ||
FileUtils.mkdir_p(policyfiles_data_bag_staging_dir) | ||
end | ||
|
||
def copy_cookbooks | ||
|
@@ -102,7 +148,7 @@ def copy_cookbooks | |
|
||
def copy_cookbook(lock) | ||
dirname = "#{lock.name}-#{lock.dotted_decimal_identifier}" | ||
export_path = File.join(export_dir, "cookbooks", dirname) | ||
export_path = File.join(staging_dir, "cookbooks", dirname) | ||
metadata_rb_path = File.join(export_path, "metadata.rb") | ||
FileUtils.cp_r(lock.cookbook_path, export_path) | ||
FileUtils.rm_f(metadata_rb_path) | ||
|
@@ -117,8 +163,6 @@ def copy_cookbook(lock) | |
end | ||
|
||
def create_policyfile_data_item | ||
policy_id = "#{policyfile_lock.name}-#{POLICY_GROUP}" | ||
item_path = File.join(export_dir, "data_bags", "policyfiles", "#{policy_id}.json") | ||
|
||
lock_data = policyfile_lock.to_lock.dup | ||
|
||
|
@@ -139,6 +183,17 @@ def create_policyfile_data_item | |
end | ||
end | ||
|
||
def mv_staged_repo | ||
# If we got here, either these dirs are empty/don't exist or force is | ||
# set to true. | ||
FileUtils.rm_rf(cookbooks_dir) | ||
FileUtils.rm_rf(policyfiles_data_bag_dir) | ||
|
||
FileUtils.mv(cookbooks_staging_dir, export_dir) | ||
FileUtils.mkdir_p(export_data_bag_dir) | ||
FileUtils.mv(policyfiles_data_bag_staging_dir, export_data_bag_dir) | ||
end | ||
|
||
def validate_lockfile | ||
return @policyfile_lock if @policyfile_lock | ||
@policyfile_lock = ChefDK::PolicyfileLock.new(storage_config).build_from_lock_data(policy_data) | ||
|
@@ -164,7 +219,7 @@ def assert_lockfile_exists! | |
end | ||
|
||
def assert_export_dir_clean! | ||
if !force_export? && !conflicting_fs_entries.empty? | ||
if !force_export? && !conflicting_fs_entries.empty? && !archive? | ||
msg = "Export dir (#{export_dir}) not clean. Refusing to export. (Conflicting files: #{conflicting_fs_entries.join(', ')})" | ||
raise ExportDirNotEmpty, msg | ||
end | ||
|
@@ -183,8 +238,28 @@ def cookbooks_dir | |
File.join(export_dir, "cookbooks") | ||
end | ||
|
||
def export_data_bag_dir | ||
File.join(export_dir, "data_bags") | ||
end | ||
|
||
def policyfiles_data_bag_dir | ||
File.join(export_dir, "data_bags", "policyfiles") | ||
File.join(export_data_bag_dir, "policyfiles") | ||
end | ||
|
||
def policy_id | ||
"#{policyfile_lock.name}-#{POLICY_GROUP}" | ||
end | ||
|
||
def item_path | ||
File.join(staging_dir, "data_bags", "policyfiles", "#{policy_id}.json") | ||
end | ||
|
||
def cookbooks_staging_dir | ||
File.join(staging_dir, "cookbooks") | ||
end | ||
|
||
def policyfiles_data_bag_staging_dir | ||
File.join(staging_dir, "data_bags", "policyfiles") | ||
end | ||
|
||
end | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@jdmundrawala do you know if this will cause any issues with permissions on windows? Should I put it inside the export dir instead?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
it seems fine. It should put it in applocal things. Is there something I can test out for you? Dir.mktmpdir ran without issue
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm trying to think back to the old times before we fixed all this in Chef. IIRC, both windows and selinux had similar issues, where files created in tmp would have severely restricted permissions, and then we would
mv
them to the final location, which would not update the permissions and everything would be borked. What we're doing here is copying the cookbooks to this tempdir, and creating other stuff withFile.open(item_path, "wb+")
. I think this means it will get the permissions of a newly created file? Then wemv
it at the end, so it will keep the permissions it had. I'm trying to figure out if this will cause a problem if you were to try to run chef-client local mode on the same box after runningchef export
.