class Dapp::Dimg::Builder::Chef::Cookbook

Локальный cookbook сборщик с правилами сборки dimg образов для chef сборщика.

Constants

CACHE_VERSION
CHECKSUM_PATTERNS

Attributes

berksfile[R]
builder[R]
enabled_modules[R]
enabled_recipes[R]
metadata[R]
path[R]
vendor_path[R]

Public Class Methods

new(builder, path:, berksfile:, metadata:, enabled_recipes:, enabled_modules:) click to toggle source

rubocop:disable Metrics/ParameterLists

# File lib/dapp/dimg/builder/chef/cookbook.rb, line 25
def initialize(builder, path:, berksfile:, metadata:, enabled_recipes:, enabled_modules:)
  @builder = builder
  @path = Pathname.new(path)
  @berksfile = berksfile
  @metadata = metadata
  @enabled_recipes = enabled_recipes
  @enabled_modules = enabled_modules
end

Public Instance Methods

checksum() click to toggle source

TODO: Сookbook'и зависимости на локальной файловой системе TODO: должны обрабатываться рекурсивно. Чтобы контрольная сумма TODO: учитывала все зависимости всех зависимых локальных cookbook'ов.

# File lib/dapp/dimg/builder/chef/cookbook.rb, line 50
def checksum
  @checksum ||= begin
    all_local_paths = local_paths
                      .product(CHECKSUM_PATTERNS)
                      .map {|cb, dir| Dir[cb.join(dir)]}
                      .flatten
                      .map(&Pathname.method(:new))

    builder.dimg.hashsum [
      CACHE_VERSION,
      builder.dimg.paths_content_hashsum(all_local_paths),
      *all_local_paths.map {|p| p.relative_path_from(path).to_s}.sort,
      berksfile.dump,
      metadata.dump
    ].compact
  end
end
local_paths() click to toggle source
# File lib/dapp/dimg/builder/chef/cookbook.rb, line 39
def local_paths
  @local_paths ||= [].tap do |paths|
    paths << path
    paths.push(*berksfile.local_cookbooks.values.map {|cookbook| cookbook[:path]})
    paths.uniq!
  end
end
name() click to toggle source

rubocop:enable Metrics/ParameterLists

# File lib/dapp/dimg/builder/chef/cookbook.rb, line 35
def name
  metadata.name
end
stage_checksum(stage) click to toggle source
# File lib/dapp/dimg/builder/chef/cookbook.rb, line 142
def stage_checksum(stage)
  paths = Dir[stage_cookbooks_path(stage).join('**/*')].map(&Pathname.method(:new))

  builder.dimg.hashsum [
    builder.dimg.paths_content_hashsum(paths),
    *paths.map {|p| p.relative_path_from(stage_cookbooks_path(stage)).to_s}.sort,
    stage == :before_install ? builder.chefdk_image : nil
  ].compact
end
stage_cookbooks_path(stage) click to toggle source

rubocop:enable Metrics/AbcSize

# File lib/dapp/dimg/builder/chef/cookbook.rb, line 125
def stage_cookbooks_path(stage)
  _stage_cookbooks_path(stage).tap do |_cookbooks_path|
    @stage_cookbooks_installed ||= {}
    @stage_cookbooks_installed[stage] ||= true.tap {install_stage_cookbooks(stage)}
  end
end
stage_enabled_modules(stage) click to toggle source
# File lib/dapp/dimg/builder/chef/cookbook.rb, line 132
def stage_enabled_modules(stage)
  @stage_enabled_modules ||= {}
  @stage_enabled_modules[stage] ||= enabled_modules.select {|cookbook| stage_entry_exist?(stage, cookbook, stage)}
end
stage_enabled_recipes(stage) click to toggle source
# File lib/dapp/dimg/builder/chef/cookbook.rb, line 137
def stage_enabled_recipes(stage)
  @stage_enabled_recipes ||= {}
  @stage_enabled_recipes[stage] ||= enabled_recipes.select {|recipe| stage_entry_exist?(stage, name, recipe)}
end
vendor!() click to toggle source

rubocop:disable Metrics/AbcSize

# File lib/dapp/dimg/builder/chef/cookbook.rb, line 76
def vendor!
  volumes_from = [builder.dimg.dapp.toolchain_container, builder.dimg.dapp.base_container, builder.chefdk_container]

  builder.dimg.dapp.log_secondary_process(builder.dimg.dapp.t(code: _vendor_process_name)) do
    tmp_berksfile_path = builder.dimg.tmp_path.join("Berksfile.#{SecureRandom.uuid}")
    File.open(tmp_berksfile_path, 'w') {|file| file.write berksfile.dump}

    tmp_metadata_path = builder.dimg.tmp_path.join("metadata.rb.#{SecureRandom.uuid}")
    File.open(tmp_metadata_path, 'w') {|file| file.write metadata.dump}

    vendor_commands = [
      "#{builder.dimg.dapp.mkdir_bin} -p ~/.ssh",
      'echo "Host *" >> ~/.ssh/config',
      'echo "    StrictHostKeyChecking no" >> ~/.ssh/config',
      *local_paths.map {|path| "#{builder.dimg.dapp.rsync_bin} --inplace --archive --relative #{path} /tmp/local_cookbooks"},
      "cd /tmp/local_cookbooks/#{path}",
      "cp #{tmp_berksfile_path} Berksfile",
      "cp #{tmp_metadata_path} metadata.rb",
      "#{builder.berks_bin} vendor /tmp/cookbooks",
      ["#{builder.dimg.dapp.find_bin} /tmp/cookbooks -type d -exec #{builder.dimg.dapp.bash_bin} -ec '",
       "#{builder.dimg.dapp.install_bin} -o #{Process.uid} -g #{Process.gid} ",
       "--mode $(#{builder.dimg.dapp.stat_bin} -c %a {}) -d ",
       "#{_vendor_path}/$(echo {} | #{builder.dimg.dapp.sed_bin} -e \"s/^\\/tmp\\/cookbooks//\")' \\;"].join,
      ["#{builder.dimg.dapp.find_bin} /tmp/cookbooks -type f -exec #{builder.dimg.dapp.bash_bin} -ec '",
       "#{builder.dimg.dapp.install_bin} -o #{Process.uid} -g #{Process.gid} ",
       "--mode $(#{builder.dimg.dapp.stat_bin} -c %a {}) {} ",
       "#{_vendor_path}/$(echo {} | #{builder.dimg.dapp.sed_bin} -e \"s/\\/tmp\\/cookbooks//\")' \\;"].join,
      ["#{builder.dimg.dapp.install_bin} -o #{Process.uid} -g #{Process.gid} ",
       "--mode 0644 <(#{builder.dimg.dapp.date_bin} +%s.%N) #{_vendor_path.join('.created_at')}"].join
    ]

    builder.dimg.dapp.shellout!(
      [ "#{builder.dimg.dapp.host_docker} run --rm",
        volumes_from.map {|container| "--volumes-from #{container}"}.join(' '),
        *local_paths.map {|path| "--volume #{path}:#{path}"},
        "--volume #{builder.dimg.tmp_path}:#{builder.dimg.tmp_path}",
        ("--volume #{builder.dimg.dapp.ssh_auth_sock}:/tmp/dapp-ssh-agent" if builder.dimg.dapp.ssh_auth_sock),
        "--volume #{_vendor_path.tap(&:mkpath)}:#{_vendor_path}",
        ('--env SSH_AUTH_SOCK=/tmp/dapp-ssh-agent' if builder.dimg.dapp.ssh_auth_sock),
        ''.tap do |cmd|
          cmd << "dappdeps/berksdeps:0.1.0 #{builder.dimg.dapp.bash_bin}"
          cmd << " -ec '#{builder.dimg.dapp.shellout_pack(vendor_commands.join(' && '))}'"
        end ].compact.join(' '),
      verbose: true
    )
  end
end

Protected Instance Methods

_stage_cookbooks_path(stage) click to toggle source

rubocop:enable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength

# File lib/dapp/dimg/builder/chef/cookbook.rb, line 280
def _stage_cookbooks_path(stage)
  builder.stage_build_path(stage).join('cookbooks')
end
_vendor_lock_name() click to toggle source
# File lib/dapp/dimg/builder/chef/cookbook.rb, line 162
def _vendor_lock_name
  "#{builder.dimg.dapp.name}.cookbooks.#{checksum}"
end
_vendor_path() click to toggle source
# File lib/dapp/dimg/builder/chef/cookbook.rb, line 154
def _vendor_path
  builder.dimg.build_path.join('cookbooks', checksum)
end
_vendor_process_name() click to toggle source
# File lib/dapp/dimg/builder/chef/cookbook.rb, line 158
def _vendor_process_name
  'process.vendoring_builder_cookbooks'
end
install_stage_cookbooks(stage) click to toggle source
# File lib/dapp/dimg/builder/chef/cookbook.rb, line 170
def install_stage_cookbooks(stage)
  _stage_cookbooks_path(stage).mkpath

  stage_install_paths(stage).each do |cookbook_path, paths|
    cookbook = cookbook_path.basename.to_s

    paths.each do |from, to|
      if from.nil?
        to_path = _stage_cookbooks_path(stage).join(cookbook, 'recipes/void.rb')
        to_path.parent.mkpath
        FileUtils.touch to_path
      else
        from_path = cookbook_path.join(from)
        to_path = _stage_cookbooks_path(stage).join(cookbook, to)
        if from_path.directory? && to_path.exist?
          Dir[from_path.join('**/*')]
            .map(&Pathname.method(:new))
            .each do |from_subpath|
              next if from_subpath.directory?

              to_subpath = to_path.join(from_subpath.relative_path_from(from_path))
              if to_subpath.exist?
                raise(Error::Chef,
                      code: :stage_path_overlap,
                      data: { stage: stage,
                              cookbook: cookbook,
                              from: from_subpath.relative_path_from(cookbook_path),
                              to: to_subpath.relative_path_from(_stage_cookbooks_path(stage).join(cookbook)) })
              end

              to_subpath.parent.mkpath
              FileUtils.cp_r from_subpath, to_subpath
            end
        else
          to_path.parent.mkpath
          FileUtils.cp_r from_path, to_path
        end
      end
    end
  end
end
stage_entry_exist?(stage, cookbook, entrypoint) click to toggle source
# File lib/dapp/dimg/builder/chef/cookbook.rb, line 166
def stage_entry_exist?(stage, cookbook, entrypoint)
  stage_cookbooks_path(stage).join(cookbook, 'recipes', "#{entrypoint}.rb").exist?
end
stage_install_paths(stage) click to toggle source

rubocop:disable Metrics/PerceivedComplexity, Metrics/CyclomaticComplexity, Metrics/MethodLength

# File lib/dapp/dimg/builder/chef/cookbook.rb, line 213
def stage_install_paths(stage)
  select_existing_paths = proc do |cookbook_path, paths|
    paths.select {|from, _| cookbook_path.join(from).exist?}
  end

  common_paths = [['metadata.json', 'metadata.json']]

  Dir[vendor_path.join('*')]
    .map(&Pathname.method(:new))
    .map do |cookbook_path|
    cookbook_name = File.basename cookbook_path
    is_builder = (cookbook_name == name)
    is_dimod = cookbook_name.start_with? 'dimod-'
    dimod_enabled = is_dimod && enabled_modules.include?(cookbook_name)

    paths = if is_builder
      common_dapp_paths = select_existing_paths.call(
        cookbook_path,
        [
          *common_paths,
          ["files/#{stage}/common", 'files/default'],
          ["templates/#{stage}/common", 'templates/default'],
          *enabled_recipes.flat_map do |recipe|
            [["files/#{stage}/#{recipe}", 'files/default'],
             ["templates/#{stage}/#{recipe}", 'templates/default']]
          end
        ]
      )

      recipe_paths = enabled_recipes
                     .map {|recipe| ["recipes/#{stage}/#{recipe}.rb", "recipes/#{recipe}.rb"]}
                     .select {|from, _| cookbook_path.join(from).exist?}

      if recipe_paths.any?
        [*recipe_paths, *common_dapp_paths]
      else
        [nil, *common_dapp_paths]
      end
    elsif is_dimod && dimod_enabled
      common_dimod_paths = select_existing_paths.call(
        cookbook_path,
        [
          *common_paths,
          ["files/#{stage}", 'files/default'],
          ['files/common', 'files/default'],
          ["templates/#{stage}", 'templates/default'],
          ['templates/common', 'templates/default'],
          ["attributes/#{stage}.rb", "attributes/#{stage}.rb"],
          ['attributes/common.rb', 'attributes/common.rb']
        ]
      )

      recipe_path = "recipes/#{stage}.rb"
      if cookbook_path.join(recipe_path).exist?
        [[recipe_path, recipe_path], *common_dimod_paths]
      else
        [nil, *common_dimod_paths]
      end
    elsif !is_dimod
      [['.', '.']]
    end

    [cookbook_path, paths] if paths && paths.any?
  end.compact
end