Module: Bolt::PlanCreator

Defined in:
lib/bolt/plan_creator.rb

Class Method Summary collapse

Class Method Details

.create_plan(plans_path, plan_name, outputter, is_puppet) ⇒ Object



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

def self.create_plan(plans_path, plan_name, outputter, is_puppet)
  _, name_segments, basename = segment_plan_name(plan_name)
  dir_path = plans_path.join(*name_segments)

  begin
    FileUtils.mkdir_p(dir_path)
  rescue Errno::EEXIST => e
    raise Bolt::Error.new(
      "#{e.message}; unable to create plan directory '#{dir_path}'",
      'bolt/existing-file-error'
    )
  end

  type = is_puppet ? 'pp' : 'yaml'
  plan_path = dir_path + "#{basename}.#{type}"
  plan_template = is_puppet ? puppet_plan(plan_name) : yaml_plan(plan_name)

  begin
    File.write(plan_path, plan_template)
  rescue Errno::EACCES => e
    raise Bolt::FileError.new(
      "#{e.message}; unable to create plan",
      plan_path
    )
  end

  if Bolt::Util.powershell?
    show_command = 'Get-BoltPlan -Name '
    run_command  = 'Invoke-BoltPlan -Name '
  else
    show_command = 'bolt plan show'
    run_command  = 'bolt plan run'
  end

  output = <<~OUTPUT
    Created plan '#{plan_name}' at '#{plan_path}'

    Show this plan with:
        #{show_command} #{plan_name}
    Run this plan with:
        #{run_command} #{plan_name}
  OUTPUT

  outputter.print_message(output)
  0
end

.puppet_plan(plan_name) ⇒ Object



140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
# File 'lib/bolt/plan_creator.rb', line 140

def self.puppet_plan(plan_name)
  <<~PUPPET
    # This is the structure of a simple plan. To learn more about writing
    # Puppet plans, see the documentation: http://pup.pt/bolt-puppet-plans

    # The summary sets the description of the plan that will appear
    # in 'bolt plan show' output. Bolt uses puppet-strings to parse the
    # summary and parameters from the plan.
    # @summary A plan created with bolt plan new.
    # @param targets The targets to run on.
    plan #{plan_name} (
      TargetSpec $targets = "localhost"
    ) {
      out::message("Hello from #{plan_name}")
      $command_result = run_command('whoami', $targets)
      return $command_result
    }
  PUPPET
end

.segment_plan_name(plan_name) ⇒ Object



101
102
103
104
105
106
107
108
109
# File 'lib/bolt/plan_creator.rb', line 101

def self.segment_plan_name(plan_name)
  prefix, *name_segments, basename = plan_name.split('::')

  # If the plan name is just the project name, then create an 'init' plan.
  # Otherwise, use the last name segment for the plan's filename.
  basename ||= 'init'

  [prefix, name_segments, basename]
end

.validate_input(project, plan_name) ⇒ Object



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

def self.validate_input(project, plan_name)
  if project.name.nil?
    raise Bolt::Error.new(
      "Project directory '#{project.path}' is not a named project. Unable to create "\
      "a project-level plan. To name a project, set the 'name' key in the 'bolt-project.yaml' "\
      "configuration file.",
      "bolt/unnamed-project-error"
    )
  end

  if plan_name !~ Bolt::Module::CONTENT_NAME_REGEX
    message = <<~MESSAGE.chomp
      Invalid plan name '#{plan_name}'. Plan names are composed of one or more name segments
      separated by double colons '::'.

      Each name segment must begin with a lowercase letter, and can only include lowercase
      letters, digits, and underscores.

      Examples of valid plan names:
          - #{project.name}
          - #{project.name}::my_plan
    MESSAGE

    raise Bolt::ValidationError, message
  end

  prefix, _, basename = segment_plan_name(plan_name)

  unless prefix == project.name
    message = "Incomplete plan name: A plan name must be prefixed with the name of the "\
      "project or module. Did you mean '#{project.name}::#{plan_name}'?"

    raise Bolt::ValidationError, message
  end

  %w[pp yaml].each do |ext|
    next unless (path = project.plans_path + "#{basename}.#{ext}").exist?
    raise Bolt::Error.new(
      "A plan with the name '#{plan_name}' already exists at '#{path}', nothing to do.",
      'bolt/existing-plan-error'
    )
  end
end

.yaml_plan(plan_name) ⇒ Object



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

def self.yaml_plan(plan_name)
  <<~YAML
    # This is the structure of a simple plan. To learn more about writing
    # YAML plans, see the documentation: http://pup.pt/bolt-yaml-plans

    # The description sets the description of the plan that will appear
    # in 'bolt plan show' output.
    description: A plan created with bolt plan new

    # The parameters key defines the parameters that can be passed to
    # the plan.
    parameters:
      targets:
        type: TargetSpec
        description: A list of targets to run actions on
        default: localhost

    # The steps key defines the actions the plan will take in order.
    steps:
      - message: Hello from #{plan_name}
      - name: command_step
        command: whoami
        targets: $targets

    # The return key sets the return value of the plan.
    return: $command_step
  YAML
end