Class: Pocketknife

Inherits:
Object
  • Object
show all
Defined in:
lib/pocketknife.rb,
lib/pocketknife/node.rb,
lib/pocketknife/errors.rb,
lib/pocketknife/version.rb,
lib/pocketknife/node_manager.rb

Overview

Pocketknife

About

Pocketknife is a devops tool for managing computers running chef-solo. Using Pocketknife, you create a project that describes the configuration of your computers and then apply it to bring them to the intended state.

For information on using the pocketknife tool, please see the README.md file. The rest of this documentation is intended for those writing code using the Pocketknife API.

Important methods

  • Pocketknife.cli runs the command-line interpreter, whichi in turn executes the methods below.

  • #initialize creates a new Pocketknife instance.

  • #create creates a new project.

  • #deploy deploys configurations to nodes, which uploads and applies.

  • #upload uploads configurations to nodes.

  • #apply applies existing configurations to nodes.

  • #node finds a node to upload or apply configurations.

Important classes

  • Node describes how to upload and apply configurations to nodes, which are remote computers.

  • NodeManager finds, checks and manages nodes.

  • NodeError describes errors encountered when using nodes.

Defined Under Namespace

Modules: Version Classes: Error, Node, NodeManager

Constant Summary collapse

InvalidTransferMechanism =
Pocketknife::Error::InvalidTransferMechanism
NodeError =
Pocketknife::Error::NodeError
NoSuchNode =
Pocketknife::Error::NodeError::NoSuchNode
UnsupportedInstallationPlatform =
Pocketknife::Error::NodeError::UnsupportedInstallationPlatform
NotInstalling =
Pocketknife::Error::NodeError::NotInstalling
RsyncError =
Pocketknife::Error::NodeError::RsyncError
ExecutionError =
Pocketknife::Error::NodeError::ExecutionError

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(opts = {}) ⇒ Pocketknife

Instantiates a new Pocketknife.

Parameters:

  • [Nil, (Hash)

    a customizable set of options



197
198
199
200
201
202
203
204
# File 'lib/pocketknife.rb', line 197

def initialize(opts={})
  self.verbosity   = opts[:verbosity]
  self.can_install = opts[:install]
  self.runlist     = opts[:runlist]
  self.transfer_mechanism = opts[:transfer_mechanism] || :rsync

  self.node_manager = NodeManager.new(self)
end

Instance Attribute Details

#can_installNil, Boolean

Should Chef and its dependencies be installed automatically if not found on a node?

Returns:

  • (Nil, Boolean)

    true means perform the installation without prompting, false means quit if Chef isn’t found, and nil means prompt the user to decide this interactively.



182
183
184
# File 'lib/pocketknife.rb', line 182

def can_install
  @can_install
end

#node_managerPocketknife::NodeManager

Returns This instance’s node manager.

Returns:



185
186
187
# File 'lib/pocketknife.rb', line 185

def node_manager
  @node_manager
end

#runlistNil, String

Returns Override runlist with a comma-separated list of recipes and roles.

Returns:

  • (Nil, String)

    Override runlist with a comma-separated list of recipes and roles.



188
189
190
# File 'lib/pocketknife.rb', line 188

def runlist
  @runlist
end

#transfer_mechanismSymbol

Returns Use :rsync or :tar to transfer files.

Returns:

  • (Symbol)

    Use :rsync or :tar to transfer files.



191
192
193
# File 'lib/pocketknife.rb', line 191

def transfer_mechanism
  @transfer_mechanism
end

#verbosityNil, Boolean

Amount of detail to display.

Returns:

  • (Nil, Boolean)

    true means verbose, nil means normal, false means quiet.



177
178
179
# File 'lib/pocketknife.rb', line 177

def verbosity
  @verbosity
end

Class Method Details

.cli(args) ⇒ void

This method returns an undefined value.

Runs the interpreter using arguments provided by the command-line. Run pocketknife -h or review the code below to see what command-line arguments are accepted.

Example:

# Display command-line help:
Pocketknife.cli('-h')

Parameters:

  • args (Array<String>)

    A list of arguments from the command-line, which may include options (e.g. -h).

Raises:

  • (SystemExit)

    Something catastrophic happened, e.g. user passed invalid options to interpreter.



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
100
101
102
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
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
# File 'lib/pocketknife.rb', line 62

def self.cli(args)
  pocketknife = Pocketknife.new
  timer = Time.now

  OptionParser.new do |parser|
    parser.banner = <<-HERE
USAGE: pocketknife [options] [nodes]

EXAMPLES:
# Create a new project called PROJECT
pocketknife -c PROJECT

# Apply configuration to a node called NODE
pocketknife NODE

OPTIONS:
    HERE

    options = {}

    parser.on("-c", "--create PROJECT", "Create project") do |name|
      pocketknife.create(name)
      return
    end

    parser.on("-V", "--version", "Display version number") do |name|
      puts "Pocketknife #{Pocketknife::Version::STRING}"
      return
    end

    parser.on("-v", "--verbose", "Display detailed status information") do |name|
      pocketknife.verbosity = true
    end

    parser.on("-q", "--quiet", "Display minimal status information") do |v|
      pocketknife.verbosity = false
    end

    parser.on("-u", "--upload", "Upload configuration, but don't apply it") do |v|
      options[:upload] = true
    end

    parser.on("-a", "--apply", "Runs cheef to apply already-uploaded configuration") do |v|
      options[:apply] = true
    end

    parser.on("-i", "--install", "Install Chef automatically") do |v|
      pocketknife.can_install = true
    end

    parser.on("-I", "--noinstall", "Don't install Chef automatically") do |v|
      pocketknife.can_install = false
    end

    parser.on("-r", "--runlist RUNLIST", "Override runlist with a comma-separated list of recipes and roles") do |v|
      pocketknife.runlist = v
    end

    transfer_mechanisms = %w[rsync tar]
    parser.on("-t", "--transfer MECHANISM", transfer_mechanisms, "Specify transfer mechanism (#{transfer_mechanisms.join(', ')})") do |v|
      pocketknife.transfer_mechanism = v.to_sym
    end

    begin
      arguments = parser.parse!
    rescue OptionParser::ParseError => e
      puts parser
      puts
      puts "ERROR: #{e}"
      exit -1
    end

    nodes = arguments

    if nodes.empty?
      puts parser
      puts
      puts "ERROR: No nodes specified."
      exit -1
    end

    begin
      if options[:upload]
        pocketknife.upload(nodes)
      end

      if options[:apply]
        pocketknife.apply(nodes)
      end

      if not options[:upload] and not options[:apply]
        pocketknife.deploy(nodes)
      end

      pocketknife.say("* SUCCESS! Took #{"%0.2f" % [Time.now-timer]} seconds")
    rescue NodeError => e
      puts "! #{e.node}: #{e}"
      exit -1
    rescue Errno::ENOENT => e
      puts "! #{e.message}"
      exit -1
    end
  end
end

.versionString

Returns the software’s version.

Returns:

  • (String)

    A version string, e.g. 0.0.1.



170
171
172
# File 'lib/pocketknife.rb', line 170

def self.version
  return Pocketknife::Version::STRING
end

Instance Method Details

#apply(nodes) ⇒ void

This method returns an undefined value.

Applies configurations to remote nodes.

Parameters:

  • nodes (Array<String>)

    A list of node names.



294
295
296
297
298
299
300
# File 'lib/pocketknife.rb', line 294

def apply(nodes)
  node_manager.assert_known(nodes)

  for node in nodes
    node_manager.find(node).apply
  end
end

#create(project) {|path| ... } ⇒ void

This method returns an undefined value.

Creates a new project directory.

Parameters:

  • project (String)

    The name of the project directory to create.

Yields:

  • status information to the optionally supplied block.

Yield Parameters:

  • path (String)

    The path of the file or directory created.



233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
# File 'lib/pocketknife.rb', line 233

def create(project)
  self.say("* Creating project in directory: #{project}")

  dir = Pathname.new(project)

  %w[
    nodes
    roles
    cookbooks
    site-cookbooks
  ].each do |subdir|
    target = (dir + subdir)
    unless target.exist?
      FileUtils.mkdir_p(target)
      self.say("- #{target}/")
    end
  end

  return true
end

#deploy(nodes) ⇒ void

This method returns an undefined value.

Deploys configuration to the nodes, calls #upload and #apply.

Parameters:

  • nodes (Array<String>)

    A list of node names.



266
267
268
269
270
271
272
273
274
# File 'lib/pocketknife.rb', line 266

def deploy(nodes)
  node_manager.assert_known(nodes)

  Node.prepare_upload do
    for node in nodes
      node_manager.find(node).deploy
    end
  end
end

#node(name) ⇒ Pocketknife::Node

Returns a node.

Parameters:

  • name (String)

    The name of the node.

Returns:



258
259
260
# File 'lib/pocketknife.rb', line 258

def node(name)
  return node_manager.find(name)
end

#say(message, importance = nil) ⇒ void

This method returns an undefined value.

Displays a message, but only if it’s important enough.

Parameters:

  • message (String)

    The message to display.

  • importance (Nil, Boolean) (defaults to: nil)

    How important is this? true means important, nil means normal, false means unimportant.



211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
# File 'lib/pocketknife.rb', line 211

def say(message, importance=nil)
  display = \
    case self.verbosity
    when true
      true
    when nil
      importance != false
    else
      importance == true
    end

  if display
    puts message
  end
end

#upload(nodes) ⇒ void

This method returns an undefined value.

Uploads configuration information to remote nodes.

Parameters:

  • nodes (Array<String>)

    A list of node names.



280
281
282
283
284
285
286
287
288
# File 'lib/pocketknife.rb', line 280

def upload(nodes)
  node_manager.assert_known(nodes)

  Node.prepare_upload do
    for node in nodes
      node_manager.find(node).upload
    end
  end
end