Class: Bolt::Project

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

Constant Summary collapse

BOLTDIR_NAME =
'Boltdir'
PROJECT_SETTINGS =
{
  "name"  => "The name of the project",
  "plans" => "An array of plan names to show, if they exist in the project."\
             "These plans are included in `bolt plan show` output",
  "tasks" => "An array of task names to show, if they exist in the project."\
             "These tasks are included in `bolt task show` output"
}.freeze

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(raw_data, path, type = 'option', logs = []) ⇒ Project

Returns a new instance of Project.



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

def initialize(raw_data, path, type = 'option', logs = [])
  @path = Pathname.new(path).expand_path

  @project_file = @path + 'bolt-project.yaml'

  @logs = logs
  @deprecations = []
  if (@path + 'bolt.yaml').file? && project_file?
    msg = "Project-level configuration in bolt.yaml is deprecated if using bolt-project.yaml. "\
      "Transport config should be set in inventory.yaml, all other config should be set in "\
      "bolt-project.yaml."
    @deprecations << { type: 'Using bolt.yaml for project configuration', msg: msg }
  end

  @inventory_file = @path + 'inventory.yaml'
  @modulepath = [(@path + 'modules').to_s, (@path + 'site-modules').to_s, (@path + 'site').to_s]
  @hiera_config = @path + 'hiera.yaml'
  @puppetfile = @path + 'Puppetfile'
  @rerunfile = @path + '.rerun.json'
  @resource_types = @path + '.resource_types'
  @type = type
  @downloads = @path + 'downloads'
  @plans_path = @path + 'plans'

  tc = Bolt::Config::INVENTORY_OPTIONS.keys & raw_data.keys
  if tc.any?
    msg = "Transport configuration isn't supported in bolt-project.yaml. Ignoring keys #{tc}"
    @logs << { warn: msg }
  end

  @data = raw_data.reject { |k, _| Bolt::Config::INVENTORY_OPTIONS.include?(k) }

  # Once bolt.yaml deprecation is removed, this attribute should be removed
  # and replaced with .project_file in lib/bolt/config.rb
  @config_file = if (Bolt::Config::BOLT_OPTIONS & @data.keys).any?
                   if (@path + 'bolt.yaml').file?
                     msg = "bolt-project.yaml contains valid config keys, bolt.yaml will be ignored"
                     @logs << { warn: msg }
                   end
                   @project_file
                 else
                   @path + 'bolt.yaml'
                 end
  validate if project_file?
end

Instance Attribute Details

#config_fileObject (readonly)

Returns the value of attribute config_file.



19
20
21
# File 'lib/bolt/project.rb', line 19

def config_file
  @config_file
end

#dataObject (readonly)

Returns the value of attribute data.



19
20
21
# File 'lib/bolt/project.rb', line 19

def data
  @data
end

#deprecationsObject (readonly)

Returns the value of attribute deprecations.



19
20
21
# File 'lib/bolt/project.rb', line 19

def deprecations
  @deprecations
end

#downloadsObject (readonly)

Returns the value of attribute downloads.



19
20
21
# File 'lib/bolt/project.rb', line 19

def downloads
  @downloads
end

#hiera_configObject (readonly)

Returns the value of attribute hiera_config.



19
20
21
# File 'lib/bolt/project.rb', line 19

def hiera_config
  @hiera_config
end

#inventory_fileObject (readonly)

Returns the value of attribute inventory_file.



19
20
21
# File 'lib/bolt/project.rb', line 19

def inventory_file
  @inventory_file
end

#logsObject (readonly)

Returns the value of attribute logs.



19
20
21
# File 'lib/bolt/project.rb', line 19

def logs
  @logs
end

#modulepathObject (readonly)

Returns the value of attribute modulepath.



19
20
21
# File 'lib/bolt/project.rb', line 19

def modulepath
  @modulepath
end

#pathObject (readonly)

Returns the value of attribute path.



19
20
21
# File 'lib/bolt/project.rb', line 19

def path
  @path
end

#plans_pathObject (readonly)

Returns the value of attribute plans_path.



19
20
21
# File 'lib/bolt/project.rb', line 19

def plans_path
  @plans_path
end

#project_fileObject (readonly)

Returns the value of attribute project_file.



19
20
21
# File 'lib/bolt/project.rb', line 19

def project_file
  @project_file
end

#puppetfileObject (readonly)

Returns the value of attribute puppetfile.



19
20
21
# File 'lib/bolt/project.rb', line 19

def puppetfile
  @puppetfile
end

#rerunfileObject (readonly)

Returns the value of attribute rerunfile.



19
20
21
# File 'lib/bolt/project.rb', line 19

def rerunfile
  @rerunfile
end

#resource_typesObject (readonly)

Returns the value of attribute resource_types.



19
20
21
# File 'lib/bolt/project.rb', line 19

def resource_types
  @resource_types
end

#typeObject (readonly)

Returns the value of attribute type.



19
20
21
# File 'lib/bolt/project.rb', line 19

def type
  @type
end

Class Method Details

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



50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# File 'lib/bolt/project.rb', line 50

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

  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, 'bolt-project.yaml')
  data = Bolt::Util.read_optional_yaml_hash(File.expand_path(project_file), 'project')
  default = type =~ /user|system/ ? 'default ' : ''
  exist = File.exist?(File.expand_path(project_file))
  logs << { info: "Loaded #{default}project from '#{fullpath}'" } if exist
  new(data, path, type, logs)
end

.default_project(logs = []) ⇒ Object



23
24
25
26
27
28
# File 'lib/bolt/project.rb', line 23

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

.find_boltdir(dir, logs = []) ⇒ Object

Search recursively up the directory hierarchy for the Project. Look for a directory called Boltdir or a file called bolt.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.



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

def self.find_boltdir(dir, logs = [])
  dir = Pathname.new(dir)

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

Instance Method Details

#check_deprecated_fileObject



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

def check_deprecated_file
  if (@path + 'project.yaml').file?
    msg = "Project configuration file 'project.yaml' is deprecated; use 'bolt-project.yaml' instead."
    Bolt::Logger.deprecation_warning('Using project.yaml instead of bolt-project.yaml', msg)
  end
end

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

Returns:

  • (Boolean)


127
128
129
# File 'lib/bolt/project.rb', line 127

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

#load_as_module?Boolean

Returns:

  • (Boolean)


136
137
138
# File 'lib/bolt/project.rb', line 136

def load_as_module?
  !name.nil?
end

#nameObject



140
141
142
# File 'lib/bolt/project.rb', line 140

def name
  @data['name']
end

#plansObject



148
149
150
# File 'lib/bolt/project.rb', line 148

def plans
  @data['plans']
end

#project_file?Boolean

Returns:

  • (Boolean)


132
133
134
# File 'lib/bolt/project.rb', line 132

def project_file?
  @project_file.file?
end

#tasksObject



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

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



121
122
123
124
125
# File 'lib/bolt/project.rb', line 121

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

#to_sObject



115
116
117
# File 'lib/bolt/project.rb', line 115

def to_s
  @path.to_s
end

#validateObject



152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
# File 'lib/bolt/project.rb', line 152

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::PAL::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
  else
    message = "No project name is specified in bolt-project.yaml. Project-level content will not be available."
    @logs << { warn: message }
  end

  %w[tasks plans].each do |conf|
    unless @data.fetch(conf, []).is_a?(Array)
      raise Bolt::ValidationError, "'#{conf}' in bolt-project.yaml must be an array"
    end
  end
end