Class: Bolt::Project

Inherits:
Object
  • Object
show all
Defined in:
lib/bolt/project.rb

Constant Summary collapse

BOLTDIR_NAME =
'Boltdir'
CONFIG_NAME =
'bolt-project.yaml'

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(data, path, type = 'option') ⇒ Project

Returns a new instance of Project.



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
# File 'lib/bolt/project.rb', line 103

def initialize(data, path, type = 'option')
  @type              = type
  @path              = Pathname.new(path).expand_path
  @project_file      = @path + CONFIG_NAME
  @inventory_file    = @path + 'inventory.yaml'
  @hiera_config      = @path + 'hiera.yaml'
  @puppetfile        = @path + 'Puppetfile'
  @rerunfile         = @path + '.rerun.json'
  @resource_types    = @path + '.resource_types'
  @downloads         = @path + 'downloads'
  @plans_path        = @path + 'plans'
  @managed_moduledir = @path + '.modules'
  @backup_dir        = @path + '.bolt-bak'
  @plugin_cache_file = @path + '.plugin_cache.json'
  @plan_cache_file   = @path + '.plan_cache.json'
  @task_cache_file   = @path + '.task_cache.json'
  @modulepath        = [(@path + 'modules').to_s]
  @manifests         = @path + 'manifests'

  if (tc = Bolt::Config::INVENTORY_OPTIONS.keys & data.keys).any?
    Bolt::Logger.warn(
      "project_transport_config",
      "Transport configuration isn't supported in bolt-project.yaml. Ignoring keys #{tc}."
    )
  end

  @data = data.slice(*Bolt::Config::PROJECT_OPTIONS)

  if @data['rerunfile']
    @rerunfile = File.expand_path(@data['rerunfile'], @path)
  end

  validate if project_file?
end

Instance Attribute Details

#backup_dirObject (readonly)

Returns the value of attribute backup_dir.



14
15
16
# File 'lib/bolt/project.rb', line 14

def backup_dir
  @backup_dir
end

#dataObject (readonly)

Returns the value of attribute data.



14
15
16
# File 'lib/bolt/project.rb', line 14

def data
  @data
end

#downloadsObject (readonly)

Returns the value of attribute downloads.



14
15
16
# File 'lib/bolt/project.rb', line 14

def downloads
  @downloads
end

#hiera_configObject (readonly)

Returns the value of attribute hiera_config.



14
15
16
# File 'lib/bolt/project.rb', line 14

def hiera_config
  @hiera_config
end

#inventory_fileObject (readonly)

Returns the value of attribute inventory_file.



14
15
16
# File 'lib/bolt/project.rb', line 14

def inventory_file
  @inventory_file
end

#managed_moduledirObject (readonly)

Returns the value of attribute managed_moduledir.



14
15
16
# File 'lib/bolt/project.rb', line 14

def managed_moduledir
  @managed_moduledir
end

#manifestsObject (readonly)

Returns the value of attribute manifests.



14
15
16
# File 'lib/bolt/project.rb', line 14

def manifests
  @manifests
end

#modulepathObject (readonly)

Returns the value of attribute modulepath.



14
15
16
# File 'lib/bolt/project.rb', line 14

def modulepath
  @modulepath
end

#pathObject (readonly)

Returns the value of attribute path.



14
15
16
# File 'lib/bolt/project.rb', line 14

def path
  @path
end

#plan_cache_fileObject (readonly)

Returns the value of attribute plan_cache_file.



14
15
16
# File 'lib/bolt/project.rb', line 14

def plan_cache_file
  @plan_cache_file
end

#plans_pathObject (readonly)

Returns the value of attribute plans_path.



14
15
16
# File 'lib/bolt/project.rb', line 14

def plans_path
  @plans_path
end

#plugin_cache_fileObject (readonly)

Returns the value of attribute plugin_cache_file.



14
15
16
# File 'lib/bolt/project.rb', line 14

def plugin_cache_file
  @plugin_cache_file
end

#project_fileObject (readonly)

Returns the value of attribute project_file.



14
15
16
# File 'lib/bolt/project.rb', line 14

def project_file
  @project_file
end

#puppetfileObject (readonly)

Returns the value of attribute puppetfile.



14
15
16
# File 'lib/bolt/project.rb', line 14

def puppetfile
  @puppetfile
end

#rerunfileObject (readonly)

Returns the value of attribute rerunfile.



14
15
16
# File 'lib/bolt/project.rb', line 14

def rerunfile
  @rerunfile
end

#resource_typesObject (readonly)

Returns the value of attribute resource_types.



14
15
16
# File 'lib/bolt/project.rb', line 14

def resource_types
  @resource_types
end

#task_cache_fileObject (readonly)

Returns the value of attribute task_cache_file.



14
15
16
# File 'lib/bolt/project.rb', line 14

def task_cache_file
  @task_cache_file
end

#typeObject (readonly)

Returns the value of attribute type.



14
15
16
# File 'lib/bolt/project.rb', line 14

def type
  @type
end

Class Method Details

.create_project(path, type = 'option') ⇒ Object



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
# File 'lib/bolt/project.rb', line 48

def self.create_project(path, type = 'option')
  fullpath = Pathname.new(path).expand_path

  if type == 'user'
    begin
      # This is already expanded if the type is user
      FileUtils.mkdir_p(path)
    rescue StandardError
      Bolt::Logger.warn(
        "non_writeable_project",
        "Could not create default project at #{path}. Continuing without a writeable project. "\
        "Log and rerun files will not be written."
      )
    end
  end

  if type == 'option' && !File.directory?(path)
    raise Bolt::Error.new("Could not find project at #{path}", "bolt/project-error")
  end

  if !Bolt::Util.windows? && type != 'environment' && fullpath.world_writable?
    raise Bolt::Error.new(
      "Project directory '#{fullpath}' is world-writable which poses a security risk. Set "\
      "BOLT_PROJECT='#{fullpath}' to force the use of this project directory.",
      "bolt/world-writable-error"
    )
  end

  project_file = File.join(fullpath, CONFIG_NAME)
  data         = Bolt::Util.read_optional_yaml_hash(File.expand_path(project_file), 'project')
  default      = type =~ /user|system/ ? 'default ' : ''

  if File.exist?(File.expand_path(project_file))
    Bolt::Logger.info("Loaded #{default}project from '#{fullpath}'")
  end

  Bolt::Validator.new.tap do |validator|
    validator.validate(data, schema, project_file)
    validator.warnings.each { |warning| Bolt::Logger.warn(warning[:id], warning[:msg]) }
    validator.deprecations.each { |dep| Bolt::Logger.deprecate(dep[:id], dep[:msg]) }
  end

  new(data, path, type)
end

.default_projectObject



20
21
22
23
24
25
# File 'lib/bolt/project.rb', line 20

def self.default_project
  create_project(File.expand_path(File.join('~', '.puppetlabs', 'bolt')), 'user')
# If homedir isn't defined use the system config path
rescue ArgumentError
  create_project(Bolt::Config.system_path, 'system')
end

.find_boltdir(dir) ⇒ Object

Search recursively up the directory hierarchy for the Project. Look for a directory called Boltdir or a file called bolt-project.yaml (for a control repo type Project). Otherwise, repeat the check on each directory up the hierarchy, falling back to the default if we reach the root.



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
# File 'lib/bolt/project.rb', line 31

def self.find_boltdir(dir)
  dir = Pathname.new(dir)

  if (dir + BOLTDIR_NAME).directory?
    create_project(dir + BOLTDIR_NAME, 'embedded')
  elsif (dir + CONFIG_NAME).file?
    create_project(dir, 'local')
  elsif dir.root?
    default_project
  else
    Bolt::Logger.debug(
      "Did not detect Boltdir or bolt-project.yaml at '#{dir}'. This directory won't be loaded as a project."
    )
    find_boltdir(dir.parent)
  end
end

.schemaObject

Builds the schema for bolt-project.yaml used by the validator.



95
96
97
98
99
100
101
# File 'lib/bolt/project.rb', line 95

def self.schema
  {
    type:        Hash,
    properties:  Bolt::Config::PROJECT_OPTIONS.map { |opt| [opt, _ref: opt] }.to_h,
    definitions: Bolt::Config::OPTIONS
  }
end

Instance Method Details

#disable_warningsObject



183
184
185
# File 'lib/bolt/project.rb', line 183

def disable_warnings
  @data['disable-warnings'] || []
end

#eql?(other) ⇒ Boolean Also known as: ==

Returns:

  • (Boolean)


150
151
152
# File 'lib/bolt/project.rb', line 150

def eql?(other)
  path == other.path
end

#load_as_module?Boolean

Returns:

  • (Boolean)


159
160
161
# File 'lib/bolt/project.rb', line 159

def load_as_module?
  !name.nil?
end

#module_installObject



179
180
181
# File 'lib/bolt/project.rb', line 179

def module_install
  @data['module-install']
end

#modulesObject



187
188
189
190
191
192
193
194
195
196
# File 'lib/bolt/project.rb', line 187

def modules
  mod_data = @data['modules'] || []
  @modules ||= mod_data.map do |mod|
    if mod.is_a?(String)
      { 'name' => mod }
    else
      mod
    end
  end
end

#nameObject



163
164
165
# File 'lib/bolt/project.rb', line 163

def name
  @data['name']
end

#plansObject



171
172
173
# File 'lib/bolt/project.rb', line 171

def plans
  @data['plans']
end

#plugin_cacheObject



175
176
177
# File 'lib/bolt/project.rb', line 175

def plugin_cache
  @data['plugin-cache']
end

#project_file?Boolean

Returns:

  • (Boolean)


155
156
157
# File 'lib/bolt/project.rb', line 155

def project_file?
  @project_file.file?
end

#tasksObject



167
168
169
# File 'lib/bolt/project.rb', line 167

def tasks
  @data['tasks']
end

#to_hObject

This API is used to prepend the project as a module to Puppet’s internal module_references list. CHANGE AT YOUR OWN RISK



144
145
146
147
148
# File 'lib/bolt/project.rb', line 144

def to_h
  { path: @path.to_s,
    name: name,
    load_as_module?: load_as_module? }
end

#to_sObject



138
139
140
# File 'lib/bolt/project.rb', line 138

def to_s
  @path.to_s
end

#validateObject



198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
# File 'lib/bolt/project.rb', line 198

def validate
  if name
    if name !~ Bolt::Module::MODULE_NAME_REGEX
      raise Bolt::ValidationError, <<~ERROR_STRING
      Invalid project name '#{name}' in bolt-project.yaml; project name must begin with a lowercase letter
      and can include lowercase letters, numbers, and underscores.
      ERROR_STRING
    elsif Dir.children(Bolt::Config::Modulepath::BOLTLIB_PATH).include?(name)
      raise Bolt::ValidationError, "The project '#{name}' will not be loaded. The project name conflicts "\
        "with a built-in Bolt module of the same name."
    end
  elsif name.nil? &&
        (File.directory?(plans_path) ||
        File.directory?(@path + 'tasks') ||
        File.directory?(@path + 'files'))
    message = "No project name is specified in bolt-project.yaml. Project-level content will not be available."

    Bolt::Logger.warn("missing_project_name", message)
  end
end