cult 0.1.1.pre

Sign up to get free protection for your applications and to get access to all the features.
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: []