Class: Bolt::PAL::YamlPlan

Inherits:
Object
  • Object
show all
Defined in:
lib/bolt/pal/yaml_plan.rb,
lib/bolt/pal/yaml_plan/step.rb,
lib/bolt/pal/yaml_plan/loader.rb,
lib/bolt/pal/yaml_plan/evaluator.rb,
lib/bolt/pal/yaml_plan/parameter.rb,
lib/bolt/pal/yaml_plan/step/eval.rb,
lib/bolt/pal/yaml_plan/step/plan.rb,
lib/bolt/pal/yaml_plan/step/task.rb,
lib/bolt/pal/yaml_plan/transpiler.rb,
lib/bolt/pal/yaml_plan/step/script.rb,
lib/bolt/pal/yaml_plan/step/upload.rb,
lib/bolt/pal/yaml_plan/step/command.rb,
lib/bolt/pal/yaml_plan/step/message.rb,
lib/bolt/pal/yaml_plan/step/verbose.rb,
lib/bolt/pal/yaml_plan/step/download.rb,
lib/bolt/pal/yaml_plan/step/resources.rb

Defined Under Namespace

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

Constant Summary collapse

PLAN_KEYS =
Set['parameters', 'private', 'steps', 'return', 'version', 'description']
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
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
# 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
  @description = stringify(plan['description']) if plan['description']

  params_hash = stringify(plan.fetch('parameters', {}))
  # Ensure params is a hash
  unless params_hash.is_a?(Hash)
    raise Bolt::Error.new("Plan parameters must be a Hash", "bolt/invalid-plan")
  end

  # Munge parameters into an array of Parameter objects, which is what
  # the Puppet API expects
  @parameters = params_hash.map do |param, definition|
    Parameter.new(param, definition)
  end.freeze

  @private = plan['private']
  unless @private.nil? || @private.is_a?(TrueClass) || @private.is_a?(FalseClass)
    msg = "Plan #{@name} key 'private' must be a boolean, received: #{@private.inspect}"
    raise Bolt::Error.new(msg, "bolt/invalid-plan")
  end

  # Validate top level plan keys
  top_level_keys = plan.keys.to_set
  unless PLAN_KEYS.superset?(top_level_keys)
    invalid_keys = top_level_keys - PLAN_KEYS
    raise Bolt::Error.new("Plan contains illegal key(s) #{invalid_keys.to_a.inspect}",
                          "bolt/invalid-plan")
  end

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

  used_names = Set.new(@parameters.map(&:name))

  @steps = plan['steps'].each_with_index.map do |step, index|
    unless step.is_a?(Hash)
      raise Bolt::Error.new(
        "Parse error in step number #{index + 1}: Plan step must be an object with valid step keys.",
        'bolt/invalid-plan'
      )
    end

    # 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')

    step = Step.create(stringified_step, index + 1)
    duplicate_check(used_names, stringified_step['name'], index + 1)
    used_names << stringified_step['name'] if stringified_step['name']
    step
  end.freeze
  @return = plan['return']
end

Instance Attribute Details

#descriptionObject (readonly)

Returns the value of attribute description.



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

def description
  @description
end

#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

#privateObject (readonly)

Returns the value of attribute private.



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

def private
  @private
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



80
81
82
# File 'lib/bolt/pal/yaml_plan.rb', line 80

def body
  self
end

#duplicate_check(used_names, name, step_number) ⇒ Object



73
74
75
76
77
78
# File 'lib/bolt/pal/yaml_plan.rb', line 73

def duplicate_check(used_names, name, step_number)
  if used_names.include?(name)
    error_message = "Duplicate step name or parameter detected: #{name.inspect}"
    raise Step::StepError.new(error_message, name, step_number)
  end
end

#return_typeObject



84
85
86
# File 'lib/bolt/pal/yaml_plan.rb', line 84

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.



93
94
95
96
97
98
99
100
101
102
103
104
105
106
# File 'lib/bolt/pal/yaml_plan.rb', line 93

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