This file is indexed.

/usr/lib/ruby/vendor_ruby/chef/resource/chef_mirror.rb is in ruby-cheffish 4.0.0-1.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
require 'cheffish'
require 'cheffish/base_resource'
require 'chef/chef_fs/file_pattern'
require 'chef/chef_fs/file_system'
require 'chef/chef_fs/parallelizer'
require 'chef/chef_fs/file_system/chef_server_root_dir'
require 'chef/chef_fs/file_system/chef_repository_file_system_root_dir'

class Chef
  class Resource
    class ChefMirror < Cheffish::BaseResource
      resource_name :chef_mirror

      # Path of the data to mirror, e.g. nodes, nodes/*, nodes/mynode,
      # */*, **, roles/base, data/secrets, cookbooks/apache2, etc.
      property :path, String, name_property: true

      # Local path.  Can be a string (top level of repository) or hash
      # (:chef_repo_path, :node_path, etc.)
      # If neither chef_repo_path nor versioned_cookbooks are set, they default to their
      # Chef::Config values.  If chef_repo_path is set but versioned_cookbooks is not,
      # versioned_cookbooks defaults to true.
      property :chef_repo_path, [ String, Hash ]

      # Whether the repo path should contain cookbooks with versioned names,
      # i.e. cookbooks/mysql-1.0.0, cookbooks/mysql-1.2.0, etc.
      # Defaults to true if chef_repo_path is specified, or to Chef::Config.versioned_cookbooks otherwise.
      property :versioned_cookbooks, Boolean

      # Whether to purge deleted things: if we do not have cookbooks/x locally and we
      # *do* have cookbooks/x remotely, then :upload with purge will delete it.
      # Defaults to false.
      property :purge, Boolean

      # Whether to freeze cookbooks on upload
      property :freeze, Boolean

      # If this is true, only new files will be copied.  File contents will not be
      # diffed, so changed files will never be uploaded.
      property :no_diff, Boolean

      # Number of parallel threads to list/upload/download with.  Defaults to 10.
      property :concurrency, Integer, default: 10, desired_state: false


      action :upload do
        with_modified_config do
          copy_to(local_fs, remote_fs)
        end
      end

      action :download do
        with_modified_config do
          copy_to(remote_fs, local_fs)
        end
      end

      action_class.class_eval do

        def with_modified_config
          # pre-Chef-12 ChefFS reads versioned_cookbooks out of Chef::Config instead of
          # taking it as an input, so we need to modify it for the duration of copy_to
          @old_versioned_cookbooks = Chef::Config.versioned_cookbooks
          # If versioned_cookbooks is explicitly set, set it.
          if !new_resource.versioned_cookbooks.nil?
            Chef::Config.versioned_cookbooks = new_resource.versioned_cookbooks

          # If new_resource.chef_repo_path is set, versioned_cookbooks defaults to true.
          # Otherwise, it stays at its current Chef::Config value.
          elsif new_resource.chef_repo_path
            Chef::Config.versioned_cookbooks = true
          end

          begin
            yield
          ensure
            Chef::Config.versioned_cookbooks = @old_versioned_cookbooks
          end
        end

        def copy_to(src_root, dest_root)
          if new_resource.concurrency <= 0
            raise "chef_mirror.concurrency must be above 0!  Was set to #{new_resource.concurrency}"
          end
          # Honor concurrency
          Chef::ChefFS::Parallelizer.threads = new_resource.concurrency - 1

          # We don't let the user pass absolute paths; we want to reserve those for
          # multi-org support (/organizations/foo).
          if new_resource.path[0] == '/'
            raise "Absolute paths in chef_mirror not yet supported."
          end
          # Copy!
          path = Chef::ChefFS::FilePattern.new("/#{new_resource.path}")
          ui = CopyListener.new(self)
          error = Chef::ChefFS::FileSystem.copy_to(path, src_root, dest_root, nil, options, ui, proc { |p| p.path })

          if error
            raise "Errors while copying:#{ui.errors.map { |e| "#{e}\n" }.join('')}"
          end
        end

        def local_fs
          # If chef_repo_path is set to a string, put it in the form it usually is in
          # chef config (:chef_repo_path, :node_path, etc.)
          path_config = new_resource.chef_repo_path
          if path_config.is_a?(Hash)
            chef_repo_path = path_config.delete(:chef_repo_path)
          elsif path_config
            chef_repo_path = path_config
            path_config = {}
          else
            chef_repo_path = Chef::Config.chef_repo_path
            path_config = Chef::Config
          end
          chef_repo_path = Array(chef_repo_path).flatten

          # Go through the expected object paths and figure out the local paths for each.
          case repo_mode
          when 'hosted_everything'
            object_names = %w(acls clients cookbooks containers data_bags environments groups nodes roles)
          else
            object_names = %w(clients cookbooks data_bags environments nodes roles users)
          end

          object_paths = {}
          object_names.each do |object_name|
            variable_name = "#{object_name[0..-2]}_path" # cookbooks -> cookbook_path
            if path_config[variable_name.to_sym]
              paths = Array(path_config[variable_name.to_sym]).flatten
            else
              paths = chef_repo_path.map { |path| ::File.join(path, object_name) }
            end
            object_paths[object_name] = paths.map { |path| ::File.expand_path(path) }
          end

          # Set up the root dir
          Chef::ChefFS::FileSystem::ChefRepositoryFileSystemRootDir.new(object_paths)
        end

        def remote_fs
          config = {
            :chef_server_url => new_resource.chef_server[:chef_server_url],
            :node_name => new_resource.chef_server[:options][:client_name],
            :client_key => new_resource.chef_server[:options][:signing_key_filename],
            :repo_mode => repo_mode,
            :versioned_cookbooks => Chef::Config.versioned_cookbooks
          }
          Chef::ChefFS::FileSystem::ChefServerRootDir.new("remote", config)
        end

        def repo_mode
          new_resource.chef_server[:chef_server_url] =~ /\/organizations\// ? 'hosted_everything' : 'everything'
        end

        def options
          result = {
            :purge => new_resource.purge,
            :freeze => new_resource.freeze,
            :diff => new_resource.no_diff,
            :dry_run => whyrun_mode?
          }
          result[:diff] = !result[:diff]
          result[:repo_mode] = repo_mode
          result[:concurrency] = new_resource.concurrency if new_resource.concurrency
          result
        end

        def load_current_resource
        end

        class CopyListener
          def initialize(mirror)
            @mirror = mirror
            @errors = []
          end

          attr_reader :mirror
          attr_reader :errors

          # TODO output is not *always* indicative of a change.  We may want to give
          # ChefFS the ability to tell us that info.  For now though, assuming any output
          # means change is pretty damn close to the truth.
          def output(str)
            mirror.converge_by str do
            end
          end
          def warn(str)
            mirror.converge_by "WARNING: #{str}" do
            end
          end
          def error(str)
            mirror.converge_by "ERROR: #{str}" do
            end
            @errors << str
          end
        end
      end
    end
  end
end