Class: Bolt::PAL::YamlPlan

Inherits:
Object
  • Object
show all
Defined in:
lib/bolt/pal/yaml_plan.rb,
lib/bolt/pal/yaml_plan/loader.rb,
lib/bolt/pal/yaml_plan/evaluator.rb

Defined Under Namespace

Classes: BareString, CodeLiteral, DoubleQuotedString, EvaluableString, Evaluator, Loader, Parameter

Constant Summary collapse

VAR_NAME_PATTERN =
/\A[a-z_][a-z0-9_]*\z/.freeze

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(name, plan) ⇒ YamlPlan

Returns a new instance of YamlPlan.



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
# File 'lib/bolt/pal/yaml_plan.rb', line 14

def initialize(name, plan)
  # Top-level plan keys aren't allowed to be Puppet code, so force them
  # all to strings.
  plan = Bolt::Util.walk_keys(plan) { |key| stringify(key) }

  @name = name.freeze

  # Nothing in parameters is allowed to be code, since no variables are defined yet
  params_hash = stringify(plan.fetch('parameters', {}))

  # Munge parameters into an array of Parameter objects, which is what
  # the Puppet API expects
  @parameters = params_hash.map do |param, definition|
    definition ||= {}
    type = Puppet::Pops::Types::TypeParser.singleton.parse(definition['type']) if definition.key?('type')
    Parameter.new(param, definition['default'], type)
  end.freeze

  @steps = plan['steps']&.map do |step|
    # Step keys also aren't allowed to be code and neither is the value of "name"
    stringified_step = Bolt::Util.walk_keys(step) { |key| stringify(key) }
    stringified_step['name'] = stringify(stringified_step['name']) if stringified_step.key?('name')
    stringified_step
  end.freeze

  @return = plan['return']

  validate
end

Instance Attribute Details

#nameObject (readonly)

Returns the value of attribute name.



12
13
14
# File 'lib/bolt/pal/yaml_plan.rb', line 12

def name
  @name
end

#parametersObject (readonly)

Returns the value of attribute parameters.



12
13
14
# File 'lib/bolt/pal/yaml_plan.rb', line 12

def parameters
  @parameters
end

#returnObject (readonly)

Returns the value of attribute return.



12
13
14
# File 'lib/bolt/pal/yaml_plan.rb', line 12

def return
  @return
end

#stepsObject (readonly)

Returns the value of attribute steps.



12
13
14
# File 'lib/bolt/pal/yaml_plan.rb', line 12

def steps
  @steps
end

Instance Method Details

#bodyObject



78
79
80
# File 'lib/bolt/pal/yaml_plan.rb', line 78

def body
  self
end

#return_typeObject



102
103
104
# File 'lib/bolt/pal/yaml_plan.rb', line 102

def return_type
  Puppet::Pops::Types::TypeParser.singleton.parse('Boltlib::PlanResult')
end

#stringify(value) ⇒ Object

Turn all “potential” strings in the object into actual strings. Because we interpret bare strings as potential Puppet code, even in places where Puppet code isn’t allowed (like some hash keys), we need to be able to force them back into regular strings, as if we had parsed them normally.



87
88
89
90
91
92
93
94
95
96
97
98
99
100
# File 'lib/bolt/pal/yaml_plan.rb', line 87

def stringify(value)
  case value
  when Array
    value.map { |element| stringify(element) }
  when Hash
    value.each_with_object({}) do |(k, v), o|
      o[stringify(k)] = stringify(v)
    end
  when EvaluableString
    value.value
  else
    value
  end
end

#validateObject



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
# File 'lib/bolt/pal/yaml_plan.rb', line 46

def validate
  unless @steps.is_a?(Array)
    raise Bolt::Error.new("Plan must specify an array of steps", "bolt/invalid-plan")
  end

  used_names = Set.new

  # Parameters come in a hash, so they must be unique
  @parameters.each do |param|
    unless param.name.is_a?(String) && param.name.match?(VAR_NAME_PATTERN)
      raise Bolt::Error.new("Invalid parameter name #{param.name.inspect}", "bolt/invalid-plan")
    end

    used_names << param.name
  end

  @steps.each do |step|
    next unless step.key?('name')

    unless step['name'].is_a?(String) && step['name'].match?(VAR_NAME_PATTERN)
      raise Bolt::Error.new("Invalid step name #{step['name'].inspect}", "bolt/invalid-plan")
    end

    if used_names.include?(step['name'])
      msg = "Step name #{step['name'].inspect} matches an existing parameter or step name"
      raise Bolt::Error.new(msg, "bolt/invalid-plan")
    end

    used_names << step['name']
  end
end