cult 0.1.3.pre → 0.1.4.pre
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/README.md +53 -47
- data/cult.gemspec +6 -5
- data/exe/cult +19 -4
- data/lib/cult/cli/common.rb +1 -2
- data/lib/cult/cli/console_cmd.rb +2 -2
- data/lib/cult/cli/cri_extensions.rb +66 -13
- data/lib/cult/cli/init_cmd.rb +6 -7
- data/lib/cult/cli/node_cmd.rb +233 -67
- data/lib/cult/cli/provider_cmd.rb +16 -13
- data/lib/cult/cli/role_cmd.rb +25 -26
- data/lib/cult/cli/task_cmd.rb +13 -13
- data/lib/cult/commander.rb +53 -17
- data/lib/cult/commander_sync.rb +29 -0
- data/lib/cult/definition.rb +21 -49
- data/lib/cult/driver.rb +1 -1
- data/lib/cult/drivers/common.rb +12 -11
- data/lib/cult/drivers/digital_ocean_driver.rb +2 -2
- data/lib/cult/drivers/virtual_box_driver.rb +156 -0
- data/lib/cult/drivers/vultr_driver.rb +3 -3
- data/lib/cult/named_array.rb +103 -15
- data/lib/cult/node.rb +139 -12
- data/lib/cult/paramap.rb +209 -0
- data/lib/cult/project.rb +2 -17
- data/lib/cult/provider.rb +3 -1
- data/lib/cult/role.rb +12 -8
- data/lib/cult/task.rb +73 -45
- data/lib/cult/template.rb +3 -4
- data/lib/cult/transaction.rb +11 -5
- data/lib/cult/user_refinements.rb +1 -1
- data/lib/cult/version.rb +1 -1
- data/lib/cult.rb +32 -3
- data/skel/roles/{all → base}/role.json +0 -0
- data/skel/roles/{all/tasks/00000-do-something-cool → base/tasks/000-do-something-cool} +0 -0
- data/skel/roles/{all/tasks/sync → base/tasks/sync-host-map} +5 -5
- data/skel/roles/base/tasks/sync-leader-of +11 -0
- data/skel/roles/bootstrap/files/cult-motd +15 -3
- data/skel/roles/bootstrap/tasks/{00000-set-hostname → 000-set-hostname} +1 -1
- data/skel/roles/bootstrap/tasks/{00001-add-cult-user → 001-add-cult-user} +0 -6
- data/skel/roles/bootstrap/tasks/002-disable-root-user +7 -0
- data/skel/roles/bootstrap/tasks/{00002-install-cult-motd → 002-install-cult-motd} +1 -1
- metadata +29 -11
- data/lib/cult/cli/fleet_cmd.rb +0 -37
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA1:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: 69eca6bb220e6610aabe66cfa4727881535bf8d0
|
|
4
|
+
data.tar.gz: 90b206c03fa2fb8b9f7c6f743b9edaaca0ce643e
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 43ca3f870a7cad5335fbaa4785e29973364cc0a3a0410586f33c0fd24f61b628123078d8d09f8cfd6565fba29f1cc550d59fac142048c463ec25f424295115e4
|
|
7
|
+
data.tar.gz: e1e79c67bdb9b968f6d19a1c62a4c198f6828d2449df4fd33c3d2dd3e25ca644a1e77b8e6ba853eb58cb4cd088499f1515c331a5a554a27995149c5f37dd9827
|
data/README.md
CHANGED
|
@@ -3,7 +3,6 @@
|
|
|
3
3
|
|
|
4
4
|
|
|
5
5
|
## Introduction
|
|
6
|
-
|
|
7
6
|
Cult is a tool to manage fleets of servers. It tries to work in an obvious
|
|
8
7
|
way, and doesn't intend on you learning a bunch of new metaphors, languages,
|
|
9
8
|
and terminology.
|
|
@@ -56,7 +55,6 @@ Hopefully.
|
|
|
56
55
|
|
|
57
56
|
|
|
58
57
|
## Installation
|
|
59
|
-
|
|
60
58
|
Cult is written in Ruby and available as a gem, but it is an application, not a
|
|
61
59
|
library. It's not written in, and has no relation to Rails. It depends on
|
|
62
60
|
Ruby 2.3 or greater. If you've got Ruby installed, via any means, the following
|
|
@@ -72,13 +70,12 @@ use the driver.
|
|
|
72
70
|
Cult requires nothing to be installed on each node, other than an operating SSH
|
|
73
71
|
server and Bourne Shell. If you know you've got Bash on the other end, feel
|
|
74
72
|
free to write your tasks in Bash. If you want to write tasks in Ruby, Python,
|
|
75
|
-
Node or Perl, etc, one of your firsts Tasks in the `
|
|
73
|
+
Node or Perl, etc, one of your firsts Tasks in the `base` or `bootstrap` role
|
|
76
74
|
should be to `apt-get -y install {ruby,python,node}`. All subsequent tasks will
|
|
77
75
|
have that interpreter available.
|
|
78
76
|
|
|
79
77
|
|
|
80
78
|
## General Theory
|
|
81
|
-
|
|
82
79
|
I think a reasonable level of abstraction for cloud deployments is such:
|
|
83
80
|
|
|
84
81
|
1. *Nodes*: Actual machines, virtual or otherwise, running somewhere.
|
|
@@ -87,11 +84,11 @@ I think a reasonable level of abstraction for cloud deployments is such:
|
|
|
87
84
|
3. *Tasks*: Roles are made of tasks, which are basically scripts, called
|
|
88
85
|
things like `install-postgres` or `configure-nginx`. They're written in
|
|
89
86
|
your language of choice. When you being up a Node with a Role, all of the
|
|
90
|
-
Role's tasks are executed to get it up and running
|
|
87
|
+
Role's tasks are executed to get it up and running, and later to update
|
|
88
|
+
configuration.
|
|
91
89
|
4. *Providers*: Your VPS provider, e.g., Linode, DigitalOcean, etc. These
|
|
92
90
|
allow you to spawn and destroy nodes, and typically charge you a few cents
|
|
93
|
-
per node per hour.
|
|
94
|
-
really just an instance of a *Driver* with an API key configured.
|
|
91
|
+
per node per hour.
|
|
95
92
|
|
|
96
93
|
We hate adding anything on top of this because it increases complexity.
|
|
97
94
|
|
|
@@ -100,11 +97,11 @@ hatches like ERB templating on shell scripts and JSON files to do weird stuff.
|
|
|
100
97
|
|
|
101
98
|
|
|
102
99
|
### Drivers
|
|
103
|
-
|
|
104
100
|
Cult provides a handful of Drivers which can talk to common VPS providers,
|
|
105
|
-
initially DigitalOcean, Linode, and Vultr
|
|
106
|
-
|
|
107
|
-
|
|
101
|
+
initially DigitalOcean, Linode, and Vultr, and a VirtualBox driver for local
|
|
102
|
+
development. A driver is typically 200-300 lines of Ruby code, and is pretty
|
|
103
|
+
well isolated from the rest of Cult, so feel free to open a PR to add your
|
|
104
|
+
provider of choice. You can get a current list with:
|
|
108
105
|
|
|
109
106
|
$ cult provider drivers
|
|
110
107
|
|
|
@@ -119,61 +116,79 @@ default bootstrap role indeed does exactly this.)
|
|
|
119
116
|
|
|
120
117
|
|
|
121
118
|
### Nodes
|
|
122
|
-
|
|
123
119
|
A node is a physical instance running somewhere. A node has a name, like
|
|
124
|
-
'web1'
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
A node named `web1` is an idea, but hasn't spawned. A node named with its full
|
|
129
|
-
description should represent a real server somewhere, that is (hopefully)
|
|
130
|
-
running.
|
|
120
|
+
'web1-hr7sjdh8'. You create a node with `cult node new -r some-role`.
|
|
121
|
+
Multiple roles may be specified. You can check on all nodes with
|
|
122
|
+
`cult node ping`.
|
|
131
123
|
|
|
132
124
|
|
|
133
125
|
### Roles
|
|
134
126
|
A role is a collection of files (usually configuration files), Tasks (usually
|
|
135
|
-
shell scripts), and a configuration (`role.json`). A role can include
|
|
136
|
-
roles via `includes:`.
|
|
137
|
-
|
|
138
|
-
A Role's tasks are named like `000-a-descriptive-name`, because the only
|
|
139
|
-
ordering Cult does is asciibetical. Tasks named numerically are considered
|
|
140
|
-
build tasks. A Task named "sync" is built, shipped, and executed during
|
|
141
|
-
`cult fleet sync`. Other files are ignored, so you can symlink or whatever
|
|
142
|
-
between them (if you want some sort of meta-role or something.)
|
|
127
|
+
shell scripts), and a generated configuration (`role.json`). A role can include
|
|
128
|
+
other roles via `includes:`.
|
|
143
129
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
else you'd like.
|
|
130
|
+
Tasks and Files, and role.json are processed with ERB when used to create a
|
|
131
|
+
node. This lets you customize behavior based on the node, the role, the
|
|
132
|
+
provider, the project, /dev/urandom, or anything else you'd like.
|
|
148
133
|
|
|
149
134
|
|
|
150
135
|
#### Special Roles
|
|
151
136
|
There are two Roles generated by default that you should think of as
|
|
152
137
|
special-ish:
|
|
153
138
|
|
|
154
|
-
1. `
|
|
155
|
-
act as if it specified `includes: ['
|
|
156
|
-
in the `
|
|
157
|
-
out of it. A task can opt-out of including `
|
|
139
|
+
1. `base`: If a Role does not explicitly list an `includes` value, Cult will
|
|
140
|
+
act as if it specified `includes: ['base']`. In practice, this means tasks
|
|
141
|
+
in the `base` role are common to all nodes that haven't explicitly opted
|
|
142
|
+
out of it. A task can opt-out of including `base` with an explicit
|
|
158
143
|
`includes: []`.
|
|
159
144
|
2. `bootstrap`: The generator creates this role to be the first one ran on a
|
|
160
145
|
new node. Before it starts, we have a root account with an SSH key, when
|
|
161
146
|
`bootstrap` finishes, we have a `cult` user on the node with `sudo` access
|
|
162
147
|
who uses the same SSH key, and the root account is disabled. `bootstrap`
|
|
163
|
-
opts-out of `
|
|
148
|
+
opts-out of `base`. The generator also installs a MOTD banner so you'll
|
|
164
149
|
know Cult was enabled on the server, has a demo script that sets the
|
|
165
150
|
hostname.
|
|
166
151
|
|
|
152
|
+
### Tasks
|
|
153
|
+
Tasks are simple, but because they've been utilized in building real systems,
|
|
154
|
+
there's quite a few special ways to use them. A Role's tasks live in
|
|
155
|
+
`roles/name/tasks`, and its type is inferred by its filename.
|
|
156
|
+
|
|
157
|
+
#### Build Tasks
|
|
158
|
+
A Role's build tasks are named like `000-a-descriptive-name`, because the only
|
|
159
|
+
ordering Cult does is asciibetical. Build tasks are executed in order to build
|
|
160
|
+
a node out of a role.
|
|
167
161
|
|
|
168
|
-
## Usage
|
|
169
162
|
|
|
163
|
+
#### Sync Tasks
|
|
164
|
+
Sync tasks are meant to inform existing nodes of their state. They are
|
|
165
|
+
executed via `cult node sync`. They are named `sync-description`. By default,
|
|
166
|
+
sync tasks are in "pass 0", which work a lot like build tasks. If you have
|
|
167
|
+
the need to place a sync task into a separate, later pass, you can specify it
|
|
168
|
+
by naming the file something like `sync-P1-something`. Note that
|
|
169
|
+
`sync-something` and `sync-P0-something` mean the same thing. All sync tasks
|
|
170
|
+
of the same pass are executed in parallel. To guarantee an order, place a sync
|
|
171
|
+
task that must execute after another in a later pass.
|
|
172
|
+
|
|
173
|
+
|
|
174
|
+
## Usage
|
|
170
175
|
We're going to put together a complete usage guide, tutorial, and example repo
|
|
171
176
|
once Cult has settled down a bit. It's still pre-1.0 software, and we still
|
|
172
177
|
like breaking things to make it work better for us.
|
|
173
178
|
|
|
174
179
|
|
|
175
|
-
###
|
|
180
|
+
### Local VirtualBox-driver Development
|
|
181
|
+
The 'virtual-box' driver sees your VMs as images, and clones one to start from.
|
|
182
|
+
The guest must have the VirtualBox Guest Extensions installed and the first
|
|
183
|
+
network adapter set up to "Bridged", so it acts like an actual machine.
|
|
184
|
+
|
|
185
|
+
Cult expects to be able to login as "root" with the password "password", and
|
|
186
|
+
immediately locks the root account after provisioning. On ubuntu, you'll have
|
|
187
|
+
to re-enabled the account with `sudo passwd root` and/or `sudo passwd -u root`.
|
|
176
188
|
|
|
189
|
+
Note that VirtualBox is a little weird, and intended for development only.
|
|
190
|
+
|
|
191
|
+
### Spooky Secrets
|
|
177
192
|
* `cult console` is built to be really nice to use. If you're not afraid of
|
|
178
193
|
Ruby, the method names are chosen to read almost like pseudo-code. It
|
|
179
194
|
supports IRB, Pry, and Ripl with command-line flags.
|
|
@@ -187,18 +202,11 @@ like breaking things to make it work better for us.
|
|
|
187
202
|
`nodes[/^dev/].with(role: /httpd/).with(something: /else/)`
|
|
188
203
|
* The NamedArray stuff even works on the command-line with String arguments,
|
|
189
204
|
and will convert strings that start with '/' to Regexps to search by name.
|
|
190
|
-
* Although Cult will only *generate* JSON, not having comments and other
|
|
191
|
-
stuff is a pain. If you don't care about JSON-readability of your
|
|
192
|
-
`node.json`s or `role.json`s, you can just rename it to `node.yaml` or
|
|
193
|
-
`node.yml` and it'll get parsed with a YAML parser. Cult keeps transient
|
|
194
|
-
state in separate files for this reason: so it doesn't overwrite your
|
|
195
|
-
long-lived YAML replacements with JSON equivalents.
|
|
196
205
|
|
|
197
206
|
|
|
198
207
|
## Development
|
|
199
208
|
|
|
200
209
|
### History
|
|
201
|
-
|
|
202
210
|
Cult was developed to basically avoid Puppet, Chef, and Ansible. I know there
|
|
203
211
|
are some really large, successful deployments of all of these. I just think
|
|
204
212
|
they just do too much for me to feel safe with when shit goes crazy.
|
|
@@ -233,7 +241,6 @@ you feel a bit antsy. Think of it more as jazz improv than an orchestra.
|
|
|
233
241
|
|
|
234
242
|
|
|
235
243
|
### Contributing
|
|
236
|
-
|
|
237
244
|
We greatly appreciate bug reports, pull-requests, questions, and general
|
|
238
245
|
commentary in the GitHub Issues. These are all *contributions*. However,
|
|
239
246
|
before opening an issue demanding us to work on your feature that Cult *has to
|
|
@@ -253,7 +260,6 @@ the above items you away.
|
|
|
253
260
|
|
|
254
261
|
|
|
255
262
|
## License
|
|
256
|
-
|
|
257
263
|
Cult is available as open source software under the terms of the
|
|
258
264
|
[MIT License](http://opensource.org/licenses/MIT).
|
|
259
265
|
|
data/cult.gemspec
CHANGED
|
@@ -27,12 +27,13 @@ Gem::Specification.new do |spec|
|
|
|
27
27
|
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
|
28
28
|
spec.require_paths = ["lib"]
|
|
29
29
|
|
|
30
|
-
spec.required_ruby_version = '
|
|
30
|
+
spec.required_ruby_version = '>= 2.2'
|
|
31
31
|
|
|
32
|
-
spec.add_dependency "cri",
|
|
33
|
-
spec.add_dependency "net-ssh",
|
|
34
|
-
spec.add_dependency "net-scp",
|
|
35
|
-
spec.add_dependency "rainbow",
|
|
32
|
+
spec.add_dependency "cri", "~> 2.7"
|
|
33
|
+
spec.add_dependency "net-ssh", "~> 3.2"
|
|
34
|
+
spec.add_dependency "net-scp", "~> 1.2"
|
|
35
|
+
spec.add_dependency "rainbow", "~> 2.1"
|
|
36
|
+
spec.add_dependency "erubis", "~> 2.7.0"
|
|
36
37
|
|
|
37
38
|
spec.add_development_dependency "bundler", "~> 1.12"
|
|
38
39
|
spec.add_development_dependency "rake", "~> 10.0"
|
data/exe/cult
CHANGED
|
@@ -37,6 +37,7 @@ cult = Cri::Command.define do
|
|
|
37
37
|
|
|
38
38
|
flag :h, :help, 'Show this help' do |value, cmd|
|
|
39
39
|
puts cmd.help
|
|
40
|
+
exit
|
|
40
41
|
end
|
|
41
42
|
|
|
42
43
|
flag :y, :yes, 'Answer "yes" to any questions' do
|
|
@@ -52,13 +53,27 @@ cult = Cri::Command.define do
|
|
|
52
53
|
puts 'Copyright (C) 2016 Mike A. Owens, meter.md, and Contributors'
|
|
53
54
|
end
|
|
54
55
|
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
required :j, :jobs, 'Number of concurrent jobs. Defaults to max' do |value|
|
|
57
|
+
Cult.concurrency = case value
|
|
58
|
+
when /^(\d+)$/
|
|
59
|
+
$1.to_i
|
|
60
|
+
when 'max'
|
|
61
|
+
:max
|
|
62
|
+
else
|
|
63
|
+
fail Cult::CLI::CLIError, "--jobs must be a number or 'max'"
|
|
64
|
+
end
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
run(arguments: none) do |opts, args, cmd|
|
|
68
|
+
if opts.empty? && args.empty?
|
|
69
|
+
puts cmd.help
|
|
70
|
+
exit
|
|
71
|
+
end
|
|
57
72
|
end
|
|
58
73
|
end
|
|
59
74
|
|
|
60
|
-
Cult::CLI.commands.each do |
|
|
61
|
-
cult.add_command(
|
|
75
|
+
Cult::CLI.commands.each do |root_command|
|
|
76
|
+
cult.add_command(root_command)
|
|
62
77
|
end
|
|
63
78
|
|
|
64
79
|
if (env = ENV['CULT_PROJECT'])
|
data/lib/cult/cli/common.rb
CHANGED
|
@@ -193,8 +193,7 @@ module Cult
|
|
|
193
193
|
|
|
194
194
|
|
|
195
195
|
# Takes a list of keys and returns an array of objects that correspond
|
|
196
|
-
# to any of them.
|
|
197
|
-
# least one object.
|
|
196
|
+
# to any of them.
|
|
198
197
|
def fetch_items(*keys, **kw)
|
|
199
198
|
keys.flatten.map do |key|
|
|
200
199
|
fetch_item(key, method: :all, **kw)
|
data/lib/cult/cli/console_cmd.rb
CHANGED
|
@@ -50,7 +50,7 @@ module Cult
|
|
|
50
50
|
def console_cmd
|
|
51
51
|
Cri::Command.define do
|
|
52
52
|
name 'console'
|
|
53
|
-
summary 'Launch
|
|
53
|
+
summary 'Launch a REPL with the project loaded'
|
|
54
54
|
description <<~EOD.format_description
|
|
55
55
|
The Cult console loads your project, and starts a Ruby REPL. This can
|
|
56
56
|
be useful for troubleshooting, or just poking around the project.
|
|
@@ -63,7 +63,7 @@ module Cult
|
|
|
63
63
|
flag :p, :pry, 'Pry'
|
|
64
64
|
flag nil, :reexec, 'Console has been exec\'d for a reload'
|
|
65
65
|
|
|
66
|
-
run(arguments:
|
|
66
|
+
run(arguments: none) do |opts, args, cmd|
|
|
67
67
|
context = ConsoleContext.new(Cult.project, ARGV)
|
|
68
68
|
|
|
69
69
|
if opts[:reexec]
|
|
@@ -8,6 +8,42 @@ module Cult
|
|
|
8
8
|
end
|
|
9
9
|
end
|
|
10
10
|
|
|
11
|
+
# This allows further -- options to be passed as literals instead of
|
|
12
|
+
# being stripped.
|
|
13
|
+
# cult node ssh Something -- some-command -- something
|
|
14
|
+
module ArgumentArrayExtensions
|
|
15
|
+
attr_reader :explicit_tail
|
|
16
|
+
|
|
17
|
+
def initialize(raw_arguments)
|
|
18
|
+
@explicit_tail = []
|
|
19
|
+
|
|
20
|
+
super_super = Array.instance_method(:initialize).bind(self)
|
|
21
|
+
if (index = raw_arguments.index("--"))
|
|
22
|
+
@explicit_tail = raw_arguments[index + 1 .. -1]
|
|
23
|
+
processed = raw_arguments[0 ... index] + @explicit_tail
|
|
24
|
+
super_super.call(processed)
|
|
25
|
+
else
|
|
26
|
+
super_super.call(raw_arguments)
|
|
27
|
+
end
|
|
28
|
+
@raw_arguments = raw_arguments
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
::Cri::ArgumentArray.prepend(self)
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
# This extension stops option processing at the first non-option bare-word.
|
|
35
|
+
# Without it, further arguments that look like options are treated as such.
|
|
36
|
+
# use-case:
|
|
37
|
+
# cult node ssh SomeNode ls -l
|
|
38
|
+
module OptionParserExtensions
|
|
39
|
+
def run
|
|
40
|
+
peek = @unprocessed_arguments_and_options[0]
|
|
41
|
+
@no_more_options = true if peek && peek[0] != '-'
|
|
42
|
+
super
|
|
43
|
+
end
|
|
44
|
+
::Cri::OptionParser.prepend(self)
|
|
45
|
+
end
|
|
46
|
+
|
|
11
47
|
|
|
12
48
|
module CommandExtensions
|
|
13
49
|
def project_required?
|
|
@@ -27,42 +63,44 @@ module Cult
|
|
|
27
63
|
def block
|
|
28
64
|
lambda do |opts, args, cmd|
|
|
29
65
|
if project_required? && Cult.project.nil?
|
|
30
|
-
fail CLIError, "command '#{name}' requires a Cult project"
|
|
66
|
+
fail CLIError, "command '#{cmd.name}' requires a Cult project"
|
|
31
67
|
end
|
|
32
68
|
|
|
33
|
-
check_argument_spec!(args, argument_spec) if argument_spec
|
|
69
|
+
check_argument_spec!(args, argument_spec, cmd) if argument_spec
|
|
34
70
|
|
|
35
71
|
super.call(opts, args, cmd)
|
|
36
72
|
end
|
|
37
73
|
end
|
|
38
74
|
|
|
39
|
-
|
|
40
|
-
|
|
75
|
+
def check_argument_spec!(args, range, cmd)
|
|
76
|
+
range = (0..range) if range == Float::INFINITY
|
|
41
77
|
range = (range..range) if range.is_a?(Integer)
|
|
42
|
-
if range.end == -1
|
|
43
|
-
range = range.begin .. Float::INFINITY
|
|
44
|
-
end
|
|
45
78
|
|
|
46
79
|
unless range.cover?(args.size)
|
|
47
80
|
msg = case
|
|
48
81
|
when range.size == 1 && range.begin == 0
|
|
49
82
|
"accepts no arguments"
|
|
50
83
|
when range.size == 1 && range.begin == 1
|
|
51
|
-
"
|
|
84
|
+
"requires exactly one argument"
|
|
52
85
|
when range.begin == range.end
|
|
53
|
-
"
|
|
86
|
+
"requires exactly #{range.begin} arguments"
|
|
54
87
|
else
|
|
55
88
|
if range.end == Float::INFINITY
|
|
56
|
-
|
|
89
|
+
words =%w(zero one two three)
|
|
90
|
+
if range.begin < words.size
|
|
91
|
+
"requires #{words[range.begin]} or more arguments"
|
|
92
|
+
else
|
|
93
|
+
"requires #{range.begin}+ arguments"
|
|
94
|
+
end
|
|
57
95
|
else
|
|
58
96
|
"accepts #{range} arguments"
|
|
59
97
|
end
|
|
60
98
|
end
|
|
61
|
-
fail CLIError, "
|
|
99
|
+
fail CLIError, "command '#{cmd.name}' #{msg} (usage: #{cmd.usage})"
|
|
62
100
|
end
|
|
63
101
|
end
|
|
64
102
|
|
|
65
|
-
Cri::Command.prepend(self)
|
|
103
|
+
::Cri::Command.prepend(self)
|
|
66
104
|
end
|
|
67
105
|
|
|
68
106
|
|
|
@@ -72,12 +110,27 @@ module Cult
|
|
|
72
110
|
end
|
|
73
111
|
|
|
74
112
|
|
|
113
|
+
# Lets us say run(arguments: 1 .. unlimited) instead of
|
|
114
|
+
# run(arguments: 1 .. Float::INFINITY)
|
|
115
|
+
# or just outright:
|
|
116
|
+
# run(arguments: unlimited)
|
|
117
|
+
def unlimited
|
|
118
|
+
Float::INFINITY
|
|
119
|
+
end
|
|
120
|
+
|
|
121
|
+
# Lets us say run(arguments: none)
|
|
122
|
+
def none
|
|
123
|
+
0
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# This allows an explicit number of arguments to be passed to
|
|
127
|
+
# run, and halts with an error otherwise
|
|
75
128
|
def run(arguments: nil, &block)
|
|
76
129
|
@command.argument_spec = arguments if arguments
|
|
77
130
|
super(&block)
|
|
78
131
|
end
|
|
79
132
|
|
|
80
|
-
Cri::CommandDSL.prepend(self)
|
|
133
|
+
::Cri::CommandDSL.prepend(self)
|
|
81
134
|
end
|
|
82
135
|
end
|
|
83
136
|
end
|
data/lib/cult/cli/init_cmd.rb
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
require 'cult/drivers/load'
|
|
2
|
+
require 'json'
|
|
2
3
|
|
|
3
4
|
module Cult
|
|
4
5
|
module CLI
|
|
@@ -37,7 +38,7 @@ module Cult
|
|
|
37
38
|
be accomplished by hand, so if you want to change anything later, it's
|
|
38
39
|
not a big deal.
|
|
39
40
|
|
|
40
|
-
The project generated sets up a pretty common configuration:
|
|
41
|
+
The project generated sets up a pretty common configuration: a `base`
|
|
41
42
|
role, a 'bootstrap' role, and a demo task that puts a colorful banner
|
|
42
43
|
in each node's MOTD.
|
|
43
44
|
EOD
|
|
@@ -87,15 +88,13 @@ module Cult
|
|
|
87
88
|
FileUtils.mkdir_p(provider_dir)
|
|
88
89
|
|
|
89
90
|
|
|
90
|
-
provider_file = File.join(provider_dir,
|
|
91
|
-
|
|
92
|
-
File.write(provider_file, project.dump_object(provider_conf))
|
|
91
|
+
provider_file = File.join(provider_dir, "provider.json")
|
|
92
|
+
File.write(provider_file, JSON.pretty_generate(provider_conf))
|
|
93
93
|
|
|
94
94
|
|
|
95
|
-
defaults_file = File.join(provider_dir,
|
|
96
|
-
project.dump_name("defaults"))
|
|
95
|
+
defaults_file = File.join(provider_dir, "defaults.json")
|
|
97
96
|
defaults = Provider.generate_defaults(provider_conf)
|
|
98
|
-
File.write(defaults_file,
|
|
97
|
+
File.write(defaults_file, JSON.pretty_generate(defaults))
|
|
99
98
|
end
|
|
100
99
|
|
|
101
100
|
if opts[:git]
|