class ChefCLI::PolicyfileServices::PushArchive
def archive_file_path
def archive_file_path File.expand_path(archive_file, root_dir) end
def http_client
- Api: - private
def http_client @http_client ||= Chef::ServerAPI.new(config.chef_server_url, signing_key_filename: config.client_key, client_name: config.node_name) end
def initialize(archive_file: nil, policy_group: nil, root_dir: nil, ui: nil, config: nil)
def initialize(archive_file: nil, policy_group: nil, root_dir: nil, ui: nil, config: nil) @archive_file = archive_file @policy_group = policy_group @root_dir = root_dir || Dir.pwd @ui = ui @config = config @policyfile_lock = nil end
def load_policy_data(policyfile_lock_path)
def load_policy_data(policyfile_lock_path) FFI_Yajl::Parser.parse(IO.read(policyfile_lock_path)) end
def looks_like_old_format_archive?(staging_dir)
def looks_like_old_format_archive?(staging_dir) cookbooks_dir = File.join(staging_dir, "cookbooks") data_bags_dir = File.join(staging_dir, "data_bags") cookbook_artifacts_dir = File.join(staging_dir, "cookbook_artifacts") policies_dir = File.join(staging_dir, "policies") policy_groups_dir = File.join(staging_dir, "policy_groups") # Old archives just had these two dirs have_old_dirs = File.exist?(cookbooks_dir) && File.exist?(data_bags_dir) # New archives created by `chef export` will have all of these; it's # also possible we'll encounter an "artisanal" archive, which might # only be missing one of these by accident. In that case we want to # trigger a different error than we're detecting here. have_any_new_dirs = File.exist?(cookbook_artifacts_dir) || File.exist?(policies_dir) || File.exist?(policy_groups_dir) have_old_dirs && !have_any_new_dirs end
def read_policyfile_lock(staging_dir)
def read_policyfile_lock(staging_dir) policyfile_lock_path = File.join(staging_dir, "Policyfile.lock.json") if looks_like_old_format_archive?(staging_dir) raise InvalidPolicyArchive, <<~MESSAGE This archive is in an unsupported format. This archive was created with an older version of ChefCLI. This version of ChefCLI does not support archives in the older format. Please Re-create the archive with a newer version of ChefCLI or Workstation. MESSAGE end unless File.exist?(policyfile_lock_path) raise InvalidPolicyArchive, "Archive does not contain a Policyfile.lock.json" end unless File.directory?(File.join(staging_dir, "cookbook_artifacts")) raise InvalidPolicyArchive, "Archive does not contain a cookbook_artifacts directory" end policy_data = load_policy_data(policyfile_lock_path) storage_config = Policyfile::StorageConfig.new.use_policyfile_lock(policyfile_lock_path) @policyfile_lock = ChefCLI::PolicyfileLock.new(storage_config).build_from_archive(policy_data) missing_cookbooks = policyfile_lock.cookbook_locks.select do |name, lock| !lock.installed? end unless missing_cookbooks.empty? message = "Archive does not have all cookbooks required by the Policyfile.lock. " + "Missing cookbooks: '#{missing_cookbooks.keys.join('", "')}'." raise InvalidPolicyArchive, message end end
def run
def run unless File.exist?(archive_file_path) raise InvalidPolicyArchive, "Archive file #{archive_file_path} not found" end stage_unpacked_archive do |staging_dir| read_policyfile_lock(staging_dir) uploader.upload end rescue => e raise PolicyfilePushArchiveError.new("Failed to publish archived policy", e) end
def stage_unpacked_archive
def stage_unpacked_archive p = Process.pid t = Time.new.utc.strftime("%Y%m%d%H%M%S") Dir.mktmpdir("chefcli-push-archive-#{p}-#{t}") do |staging_dir| unpack_to(staging_dir) yield staging_dir end end
def unpack_to(staging_dir)
def unpack_to(staging_dir) Mixlib::Archive.new(archive_file_path).extract(staging_dir) rescue => e raise InvalidPolicyArchive, "Archive file #{archive_file_path} could not be unpacked. #{e}" end
def uploader
- Api: - private
def uploader ChefCLI::Policyfile::Uploader.new(policyfile_lock, policy_group, ui:, http_client:, policy_document_native_api: config.policy_document_native_api) end