cult 0.1.1.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.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/Gemfile +4 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +240 -0
  6. data/Rakefile +6 -0
  7. data/cult +1 -0
  8. data/cult.gemspec +38 -0
  9. data/doc/welcome.txt +1 -0
  10. data/exe/cult +86 -0
  11. data/lib/cult/artifact.rb +45 -0
  12. data/lib/cult/cli/common.rb +265 -0
  13. data/lib/cult/cli/console_cmd.rb +124 -0
  14. data/lib/cult/cli/cri_extensions.rb +84 -0
  15. data/lib/cult/cli/init_cmd.rb +116 -0
  16. data/lib/cult/cli/load.rb +26 -0
  17. data/lib/cult/cli/node_cmd.rb +205 -0
  18. data/lib/cult/cli/provider_cmd.rb +123 -0
  19. data/lib/cult/cli/role_cmd.rb +149 -0
  20. data/lib/cult/cli/task_cmd.rb +140 -0
  21. data/lib/cult/commander.rb +103 -0
  22. data/lib/cult/config.rb +22 -0
  23. data/lib/cult/definition.rb +112 -0
  24. data/lib/cult/driver.rb +88 -0
  25. data/lib/cult/drivers/common.rb +192 -0
  26. data/lib/cult/drivers/digital_ocean_driver.rb +179 -0
  27. data/lib/cult/drivers/linode_driver.rb +282 -0
  28. data/lib/cult/drivers/load.rb +26 -0
  29. data/lib/cult/drivers/script_driver.rb +27 -0
  30. data/lib/cult/drivers/vultr_driver.rb +217 -0
  31. data/lib/cult/named_array.rb +129 -0
  32. data/lib/cult/node.rb +62 -0
  33. data/lib/cult/project.rb +169 -0
  34. data/lib/cult/provider.rb +134 -0
  35. data/lib/cult/role.rb +213 -0
  36. data/lib/cult/skel.rb +85 -0
  37. data/lib/cult/task.rb +64 -0
  38. data/lib/cult/template.rb +92 -0
  39. data/lib/cult/transferable.rb +61 -0
  40. data/lib/cult/version.rb +3 -0
  41. data/lib/cult.rb +4 -0
  42. data/skel/.cultconsolerc +4 -0
  43. data/skel/.cultrc.erb +29 -0
  44. data/skel/README.md.erb +22 -0
  45. data/skel/keys/.keep +0 -0
  46. data/skel/nodes/.keep +0 -0
  47. data/skel/providers/.keep +0 -0
  48. data/skel/roles/all/role.json +4 -0
  49. data/skel/roles/all/tasks/00000-do-something-cool +27 -0
  50. data/skel/roles/bootstrap/files/cult-motd +45 -0
  51. data/skel/roles/bootstrap/role.json +4 -0
  52. data/skel/roles/bootstrap/tasks/00000-set-hostname +22 -0
  53. data/skel/roles/bootstrap/tasks/00001-add-cult-user +21 -0
  54. data/skel/roles/bootstrap/tasks/00002-install-cult-motd +9 -0
  55. metadata +183 -0
@@ -0,0 +1,92 @@
1
+ require 'erb'
2
+ require 'json'
3
+
4
+ module Cult
5
+ class Template
6
+
7
+ # Alright! We found a use for refinements!
8
+ module Refinements
9
+ module Util
10
+ module_function
11
+ def squote(s)
12
+ "'" + s.gsub("'", "\\\\\'") + "'"
13
+ end
14
+
15
+
16
+ def quote(s)
17
+ s.to_json
18
+ end
19
+
20
+
21
+ def slash(s)
22
+ Shellwords.escape(s)
23
+ end
24
+ end
25
+
26
+ refine String do
27
+ def quote
28
+ Util.quote(self)
29
+ end
30
+ alias_method :q, :quote
31
+
32
+
33
+ def squote
34
+ Util.squote(self)
35
+ end
36
+ alias_method :sq, :squote
37
+
38
+
39
+ def slash
40
+ Util.slash(self)
41
+ end
42
+ end
43
+
44
+ refine Array do
45
+ def quote(sep = ' ')
46
+ map {|v| Util.quote(v) }.join(sep)
47
+ end
48
+ alias_method :q, :quote
49
+
50
+
51
+ def squote(sep = ' ')
52
+ map {|v| Util.squote(v) }.join(sep)
53
+ end
54
+ alias_method :sq, :squote
55
+
56
+
57
+ def slash
58
+ map {|v| Util.slash(v) }.join(' ')
59
+ end
60
+ end
61
+ end
62
+
63
+ class Context
64
+ using Refinements
65
+
66
+ def initialize(pwd: nil, **kw)
67
+ @pwd = pwd
68
+ kw.each do |k,v|
69
+ define_singleton_method(k) { v }
70
+ end
71
+ end
72
+
73
+
74
+ def _process(template)
75
+ Dir.chdir(@pwd || Dir.pwd) do
76
+ ::ERB.new(template).result(binding)
77
+ end
78
+ end
79
+ end
80
+
81
+
82
+ def initialize(pwd: nil, **kw)
83
+ @context = Context.new(pwd: pwd, **kw)
84
+ end
85
+
86
+
87
+ def process(text)
88
+ @context._process(text)
89
+ end
90
+
91
+ end
92
+ end
@@ -0,0 +1,61 @@
1
+ require 'cult/template'
2
+
3
+ module Cult
4
+ module Transferable
5
+
6
+ module ClassMethods
7
+ def collection_name
8
+ name.split('::')[-1].downcase + 's'
9
+ end
10
+ end
11
+
12
+
13
+ def self.included(cls)
14
+ cls.extend(ClassMethods)
15
+ end
16
+
17
+
18
+ def collection_name
19
+ self.class.collection_name
20
+ end
21
+
22
+
23
+ def remote_path
24
+ File.join(role.remote_path, role_relative_path)
25
+ end
26
+
27
+
28
+ def role_relative_path
29
+ File.join(collection_name, relative_path)
30
+ end
31
+
32
+
33
+ def binary?
34
+ !! File.read(path, 512).match(/[\x00-\x08]/)
35
+ end
36
+
37
+
38
+ def contents(project, role, node, pwd: nil)
39
+ if binary?
40
+ File.read(path)
41
+ else
42
+ erb = Template.new(pwd: pwd, project: project, role: role, node: node)
43
+ erb.process File.read(path)
44
+ end
45
+ end
46
+
47
+
48
+ def name
49
+ prefix = File.join(role.path, collection_name) + "/"
50
+ if path.start_with?(prefix)
51
+ path[prefix.size .. -1]
52
+ end
53
+ end
54
+
55
+
56
+ def file_mode
57
+ File.stat(path).mode & 0777
58
+ end
59
+
60
+ end
61
+ end
@@ -0,0 +1,3 @@
1
+ module Cult
2
+ VERSION = '0.1.1.pre'
3
+ end
data/lib/cult.rb ADDED
@@ -0,0 +1,4 @@
1
+ require 'cult/version'
2
+
3
+ module Cult
4
+ end
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This file is loaded right before `cult console` is launched. You have access
4
+ # to the same scope as the console.
data/skel/.cultrc.erb ADDED
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ # This file is loaded every time the cult command is run in the project
4
+ # directory. If there's any special setup you'd like to do, this is the place
5
+ # to do it.
6
+
7
+ # Note: This file MUST exist, even if you're not doing anything with it. It's
8
+ # how the Cult tool decides whether a directory is a project or not.
9
+
10
+
11
+ Cult.project.instance_eval do
12
+ # In this this scope, you're in a context very similar to the console, where
13
+ # `nodes`, `roles`, `providers` etc, work as you'd expect.
14
+
15
+ # This marks the version of Cult the project was created with. If future
16
+ # versions of Cult have backward-incompatible changes, it'll know you expect
17
+ # the behavior of this version.
18
+ self.cult_version = <%= Cult::VERSION.sq %>
19
+
20
+ # Git integration is great, it basically makes sure you stay in sync. If
21
+ # this was enabled with the generator (it <%=project.git? ? 'was' : "wasn\'t" %>), you've already got a Git
22
+ # project and everything was committed.
23
+ self.git_integration = <%= !! project.git_integration %>
24
+
25
+ # If you don't set this, the project's default_provider will be nil until one
26
+ # is created, and otherwise, the first one. This really only comes into play
27
+ # when you're using multiple providers.
28
+ # self.default_provider = providers[/my-vps/]
29
+ end
@@ -0,0 +1,22 @@
1
+ # <%= project.name %>
2
+
3
+ This is project is managed by [Cult][1]. You'll need to install it to do
4
+ anything useful.
5
+
6
+ ## A Tour
7
+
8
+ Cult projects have a defined directory structure:
9
+
10
+ * `keys/{public,private}`: SSH keys you'll need to log in to the fleet. Cult
11
+ will work without these, assuming the keys you'll need are known to SSH when
12
+ you try to command nodes. It is up to your policy if `keys/private` gets
13
+ checked into source control.
14
+ * `nodes/(nodename)`: Nodes are created by the `cult nodes create your-node-name`
15
+ command, and live here. A node represents information about a living,
16
+ breathing actual machine out there.
17
+ * `roles/(rolename)`: A node has one or more roles. When you synchronize a
18
+ node, it's applying rules to conform to its role. Generate new roles with
19
+ `cult roles create your-role-name`
20
+ * `vps`: Configuration for your VPS.
21
+
22
+ [1]: https://github.com/metermd/cult/ "Cult"
data/skel/keys/.keep ADDED
File without changes
data/skel/nodes/.keep ADDED
File without changes
File without changes
@@ -0,0 +1,4 @@
1
+ {
2
+ "includes": [],
3
+ "user": "cult"
4
+ }
@@ -0,0 +1,27 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+
4
+ # This will be the first (normal) Task to be run on your new server. Rename
5
+ # it, and make it do something useful.
6
+
7
+ # This file will be processed by ERB right before it's shipped to a Node, so
8
+ # you can customize it based on the node, roles, project, etc.
9
+
10
+ # The ERB helper has quite a few methods for shell-escaping, for example:
11
+
12
+ # The "q" method quotes a string with double-quotes.
13
+ echo <%= node.name.q %>
14
+
15
+ # The "sq" method single-quotes the string.
16
+ echo <%= node.name.sq %>
17
+
18
+ # "slash" does shell slash-escaping, e.g., I\'m\ Awesome
19
+ echo <%= node.name.slash %>
20
+
21
+ # The same methods work on Array (over each item), and have an optional
22
+ # separator argument, which defaults to ' '
23
+ echo <%= node.roles.sq %>
24
+
25
+ # This script executes in the task directory, so files are accessible as
26
+ # "files/something". Text files in files/ are also pre-processed through IRB,
27
+ # with the same context.
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+
4
+ NODE=<%= node.name.sq %>
5
+ ROLES=<%= node.roles.map(&:name).sq %>
6
+ SPAWN=<%= Time.now.to_s.sq %>
7
+
8
+ colorize() {
9
+ i=124
10
+ d=1
11
+ while IFS='' read -r LINE ; do
12
+ printf "\e[38;5;${i}m$LINE\e[0m\n"
13
+ i=$(($i + $d))
14
+ if [ $i -eq 124 ] ; then
15
+ d=1
16
+ elif [ $i -eq 135 ] ; then
17
+ d=-1
18
+ fi
19
+ done
20
+ }
21
+
22
+ inf() {
23
+ TEXT=$(echo "$1" | fold -s -w 60 | fmt -s -c -w 78)
24
+ printf '\e[38;5;8m%s\e[0m\n' "$TEXT"
25
+ }
26
+
27
+ # We don't want everyone to have to install figlet.
28
+ cat <<EOD | colorize
29
+
30
+ @@@@@@@ @@@ @@@ @@@ @@@@@@@
31
+ @@@@@@@@ @@@ @@@ @@@ @@@@@@@
32
+ !@@ @@! @@@ @@! @@!
33
+ !@! !@! @!@ !@! !@!
34
+ !@! @!@ !@! @!! @!!
35
+ !!! !@! !!! !!! !!!
36
+ :!! !!: !!! !!: !!:
37
+ :!: :!: !:! :!: :!:
38
+ ::: ::: ::::: :: :: :::: ::
39
+ :: :: : : : : : :: : : :
40
+
41
+ EOD
42
+
43
+ inf "node: $NODE, since $SPAWN"
44
+ inf "roles: $ROLES"
45
+ echo
@@ -0,0 +1,4 @@
1
+ {
2
+ "includes": [],
3
+ "user": "root"
4
+ }
@@ -0,0 +1,22 @@
1
+ #!/bin/sh
2
+ set -e
3
+
4
+ # We make sure this node has a valid hostname. Different VPS providers handle
5
+ # this differently, and sudo will be weird if it can't resolve its hostname.
6
+
7
+ NODE_NAME=<%= node.name.q %>
8
+
9
+ echo "$NODE_NAME" > /etc/hostname
10
+ hostname "$NODE_NAME"
11
+
12
+ HOSTS=$(cat /etc/hosts | sed -e 's/#.*//g' | sed -e 's/\t/ /g' | tr -s ' ' | \
13
+ grep -v '^\s*$' | cut -d' ' -f2-)
14
+
15
+ # See if we're in hosts
16
+ if ! echo "$HOSTS" | grep -q "$NODE_NAME" ; then
17
+ echo "127.0.1.1 $NODE_NAME" >> /etc/hosts
18
+ echo "::1 $NODE_NAME" >> /etc/hosts
19
+ fi
20
+
21
+ # Make the SSH experience a little nicer with the full hostname in the prompt
22
+ echo "export PS1='\u@\H:\w\$ '" > /etc/bash.bashrc
@@ -0,0 +1,21 @@
1
+ #!/bin/sh
2
+ set -e
3
+
4
+ # We move root's authorized_keys to a new 'cult' user, disable the root account,
5
+ # and give 'cult' sudo superpowers.
6
+
7
+ adduser --disabled-password --gecos 'Cult Deployment Account' cult
8
+
9
+ mkdir -p /home/cult/.ssh
10
+ mv "$HOME/.ssh/authorized_keys" /home/cult/.ssh
11
+ chown -R cult:cult /home/cult/.ssh
12
+ chmod -R 0700 /home/cult/.ssh
13
+ chmod -R 0600 /home/cult/.ssh/*
14
+
15
+ echo 'cult ALL=(ALL) NOPASSWD:ALL' > /etc/sudoers.d/cult-nopasswd
16
+
17
+ # disable root account
18
+ passwd -l root
19
+ sed -i.bak -e 's/^PermitRootLogin.*/PermitRootLogin no/' /etc/ssh/sshd_config
20
+
21
+ systemctl reload sshd
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env bash
2
+ set -e
3
+
4
+ if [ -d "/etc/update-motd.d" ]; then
5
+ sudo cp "files/cult-motd" "/etc/update-motd.d/99-cult-motd"
6
+ sudo chmod +x "/etc/update-motd.d/99-cult-motd"
7
+ elif [ -f "/etc/motd" ]; then
8
+ ./files/cult-motd | sudo tee -a "/etc/motd"
9
+ fi
metadata ADDED
@@ -0,0 +1,183 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cult
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1.pre
5
+ platform: ruby
6
+ authors:
7
+ - Mike Owens
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2016-08-28 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: cri
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 2.7.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 2.7.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: net-ssh
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: 3.2.0
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: 3.2.0
41
+ - !ruby/object:Gem::Dependency
42
+ name: net-scp
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: 1.2.1
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: 1.2.1
55
+ - !ruby/object:Gem::Dependency
56
+ name: rainbow
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: 2.1.0
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: 2.1.0
69
+ - !ruby/object:Gem::Dependency
70
+ name: bundler
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '1.12'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '1.12'
83
+ - !ruby/object:Gem::Dependency
84
+ name: rake
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '10.0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '10.0'
97
+ description:
98
+ email:
99
+ - mike@meter.md
100
+ executables:
101
+ - cult
102
+ extensions: []
103
+ extra_rdoc_files: []
104
+ files:
105
+ - ".gitignore"
106
+ - Gemfile
107
+ - LICENSE.txt
108
+ - README.md
109
+ - Rakefile
110
+ - cult
111
+ - cult.gemspec
112
+ - doc/welcome.txt
113
+ - exe/cult
114
+ - lib/cult.rb
115
+ - lib/cult/artifact.rb
116
+ - lib/cult/cli/common.rb
117
+ - lib/cult/cli/console_cmd.rb
118
+ - lib/cult/cli/cri_extensions.rb
119
+ - lib/cult/cli/init_cmd.rb
120
+ - lib/cult/cli/load.rb
121
+ - lib/cult/cli/node_cmd.rb
122
+ - lib/cult/cli/provider_cmd.rb
123
+ - lib/cult/cli/role_cmd.rb
124
+ - lib/cult/cli/task_cmd.rb
125
+ - lib/cult/commander.rb
126
+ - lib/cult/config.rb
127
+ - lib/cult/definition.rb
128
+ - lib/cult/driver.rb
129
+ - lib/cult/drivers/common.rb
130
+ - lib/cult/drivers/digital_ocean_driver.rb
131
+ - lib/cult/drivers/linode_driver.rb
132
+ - lib/cult/drivers/load.rb
133
+ - lib/cult/drivers/script_driver.rb
134
+ - lib/cult/drivers/vultr_driver.rb
135
+ - lib/cult/named_array.rb
136
+ - lib/cult/node.rb
137
+ - lib/cult/project.rb
138
+ - lib/cult/provider.rb
139
+ - lib/cult/role.rb
140
+ - lib/cult/skel.rb
141
+ - lib/cult/task.rb
142
+ - lib/cult/template.rb
143
+ - lib/cult/transferable.rb
144
+ - lib/cult/version.rb
145
+ - skel/.cultconsolerc
146
+ - skel/.cultrc.erb
147
+ - skel/README.md.erb
148
+ - skel/keys/.keep
149
+ - skel/nodes/.keep
150
+ - skel/providers/.keep
151
+ - skel/roles/all/role.json
152
+ - skel/roles/all/tasks/00000-do-something-cool
153
+ - skel/roles/bootstrap/files/cult-motd
154
+ - skel/roles/bootstrap/role.json
155
+ - skel/roles/bootstrap/tasks/00000-set-hostname
156
+ - skel/roles/bootstrap/tasks/00001-add-cult-user
157
+ - skel/roles/bootstrap/tasks/00002-install-cult-motd
158
+ homepage: https://github.com/metermd/cult
159
+ licenses:
160
+ - MIT
161
+ metadata:
162
+ allowed_push_host: https://rubygems.org
163
+ post_install_message:
164
+ rdoc_options: []
165
+ require_paths:
166
+ - lib
167
+ required_ruby_version: !ruby/object:Gem::Requirement
168
+ requirements:
169
+ - - "~>"
170
+ - !ruby/object:Gem::Version
171
+ version: '2.3'
172
+ required_rubygems_version: !ruby/object:Gem::Requirement
173
+ requirements:
174
+ - - ">"
175
+ - !ruby/object:Gem::Version
176
+ version: 1.3.1
177
+ requirements: []
178
+ rubyforge_project:
179
+ rubygems_version: 2.6.6
180
+ signing_key:
181
+ specification_version: 4
182
+ summary: Fleet Management like its 1990
183
+ test_files: []