serverkit 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: e7d32c86ed73e992891971d99e1f5fed1c3d20e9
4
- data.tar.gz: 53173d404b9a4b86e7d0c84f6717531ab73e290c
3
+ metadata.gz: 612a45453d00dd4da8b327be21c8513dfb920292
4
+ data.tar.gz: 4beb20763b868203c302697f114b508c5f5f4cd8
5
5
  SHA512:
6
- metadata.gz: 3063377dafa07b971082c048a1795aa16a0b0e0c714f9779f599c2caced4a2486eec5fe27d5510b5df203a8748f736fa3b5cfeda23a5eebbb499ede87d76c042
7
- data.tar.gz: 2193c293ed47692cccfb58b0221086c5fb6cef4f232e61d7a64e69c35e3195c719f3fb4dbe337369bdd283754f9096848c8190dae087fc69f8146aaaeb3decad
6
+ metadata.gz: 4172b07769a11c4002e67caff3cb009262912e188b583893e4a9bf00a3a9432934088ae1c2c4df5bcf433a9e8621eaa1e33c122aab7547d03f118b39d0511ae6
7
+ data.tar.gz: 5068bbb81ac75922f293ff2061a41a22de161acfadf5fb64efd3100906e37014202fedec6604ecd748d1a5b716e3834e303e113be12b27c78ccee969d5610093
@@ -1,3 +1,9 @@
1
+ ## 0.2.0
2
+ - Support multiple hosts
3
+ - Change `--host=` option to `--hosts=`
4
+ - Change variables use on ERB template
5
+ - Force recipe validation on all actions
6
+
1
7
  ## 0.1.0
2
8
  - Support execution over SSH with --host option
3
9
  - Remove --recipe=... option and require recipe path in 2nd command-line argument
data/README.md CHANGED
@@ -62,12 +62,12 @@ Shows the difference between your recipe and the state of the target host.
62
62
 
63
63
  ```
64
64
  $ serverkit check recipe.yml
65
- [OK] install_mysql
66
- [OK] install_redis
67
- [OK] install_licecap
68
- [OK] install_alfred
69
- [NG] clone_dotfiles
70
- [NG] symlink_zshrc
65
+ [ OK ] install_mysql
66
+ [ OK ] install_redis
67
+ [ OK ] install_licecap
68
+ [ OK ] install_alfred
69
+ [ NG ] clone_dotfiles
70
+ [ NG ] symlink_zshrc
71
71
  ```
72
72
 
73
73
  ### serverkit apply
@@ -83,6 +83,16 @@ $ serverkit apply recipe.yml
83
83
  [DONE] symlink_zshrc
84
84
  ```
85
85
 
86
+ ### SSH support
87
+ Use `--hosts=` option to execute serverkit over SSH.
88
+ Serverkit does not require any installation on server-side.
89
+ If you want to specify SSH configuration, write it into your ~/.ssh/config.
90
+
91
+ ```
92
+ $ serverkit apply recipe.yml --hosts=alpha.example.com
93
+ $ serverkit apply recipe.yml --hosts=alpha.example.com,bravo.example.com
94
+ ```
95
+
86
96
  ## Recipe
87
97
  A recipe describes the desired state of your server.
88
98
  It is mostly a collection of resources, defined using certain patterns.
@@ -115,7 +125,7 @@ $ serverkit apply recipes/
115
125
  When using ERB recipe, you can also give optional variables file
116
126
  that defines configurations in a Hash object for ERB template.
117
127
  It supports similar format variation with Recipe.
118
- In ERB template, you can use given variables via `variables` method.
128
+ In ERB template, you can use given variables via methods named after its keys.
119
129
 
120
130
  ### Example
121
131
  This is an example recipe to install some packages, clone a git repository, and create a symlink.
@@ -137,12 +147,12 @@ resources:
137
147
  name: redis
138
148
  - id: clone_dotfiles
139
149
  type: git
140
- repository: git@github.com:<%= variables["dotfiles_repository"] %>.git
141
- path: /Users/<%= variables["user"] %>/src/github.com/<%= variables["dotfiles_repository"] %>
150
+ repository: git@github.com:<%= dotfiles_repository %>.git
151
+ path: /Users/<%= user %>/src/github.com/<%= dotfiles_repository %>
142
152
  - id: symlink_zshrc
143
153
  type: symlink
144
- source: /Users/<%= variables["user"] %>/.zshrc
145
- destination: /Users/<%= variables["user"] %>/src/github.com/<%= variables["dotfiles_repository"] %>/.zshrc
154
+ source: /Users/<%= user %>/.zshrc
155
+ destination: /Users/<%= user %>/src/github.com/<%= dotfiles_repository %>/.zshrc
146
156
  ```
147
157
 
148
158
  ## Resource
@@ -1,9 +1,9 @@
1
1
  resources:
2
2
  - id: clone_dotfiles
3
3
  type: git
4
- repository: git@github.com:<%= variables["dotfiles_github_repository"] %>.git
5
- path: /Users/<%= variables["user"] %>/src/github.com/<%= variables["dotfiles_github_repository"] %>
4
+ repository: git@github.com:<%= dotfiles_github_repository %>.git
5
+ path: /Users/<%= user %>/src/github.com/<%= dotfiles_github_repository %>
6
6
  - id: symlink_zshrc
7
7
  type: symlink
8
- source: /Users/<%= variables["user"] %>/.zshrc
9
- destination: /Users/<%= variables["user"] %>/src/github.com/<%= variables["dotfiles_github_repository"] %>/linked/.zshrc
8
+ source: /Users/<%= user %>/.zshrc
9
+ destination: /Users/<%= user %>/src/github.com/<%= dotfiles_github_repository %>/linked/.zshrc
@@ -2,7 +2,7 @@ require "active_model"
2
2
 
3
3
  class ReadableValidator < ActiveModel::EachValidator
4
4
  def validate_each(record, attribute, value)
5
- unless File.readable?(value)
5
+ if !value.nil? && !File.readable?(value)
6
6
  record.errors.add(attribute, "can't be unreadable path")
7
7
  end
8
8
  end
@@ -1,22 +1,24 @@
1
1
  require "serverkit/actions/base"
2
+ require "thread"
2
3
 
3
4
  module Serverkit
4
5
  module Actions
5
6
  class Apply < Base
6
- def call
7
- recipe.resources.each do |resource|
8
- resource.backend = backend
9
- if resource.check
10
- puts "[SKIP] #{resource.id}"
11
- else
12
- resource.apply
13
- if resource.check
14
- puts "[DONE] #{resource.id}"
15
- else
16
- puts "[FAIL] #{resource.id}"
7
+ def run
8
+ backends.map do |backend|
9
+ Thread.new do
10
+ recipe.resources.map(&:dup).each do |resource|
11
+ resource.backend = backend
12
+ if resource.check
13
+ puts "[SKIP] #{resource.id} on #{host_for(backend)}"
14
+ else
15
+ resource.apply
16
+ result = resource.check ? "DONE" : "FAIL"
17
+ puts "[#{result}] #{resource.id} on #{host_for(backend)}"
18
+ end
17
19
  end
18
20
  end
19
- end
21
+ end.each(&:join)
20
22
  end
21
23
  end
22
24
  end
@@ -14,48 +14,65 @@ module Serverkit
14
14
  @argv = argv
15
15
  end
16
16
 
17
+ def call
18
+ setup
19
+ run
20
+ end
21
+
17
22
  private
18
23
 
19
24
  def abort_with_errors
20
25
  abort recipe.errors.map { |error| "Error: #{error}" }.join("\n")
21
26
  end
22
27
 
23
- # @return [Specinfra::Backend::Base]
24
- def backend
25
- @backend ||= backend_class.new(backend_options)
28
+ # @return [Array<Specinfra::Backend::Base>]
29
+ def backends
30
+ if options[:hosts]
31
+ hosts.map do |host|
32
+ backend_class.new(
33
+ disable_sudo: true,
34
+ host: host,
35
+ ssh_options: {
36
+ user: ssh_user_for(host),
37
+ },
38
+ )
39
+ end
40
+ else
41
+ [backend_class.new]
42
+ end
26
43
  end
27
44
 
28
45
  def backend_class
29
- if host
46
+ if options[:hosts]
30
47
  Specinfra::Backend::Ssh
31
48
  else
32
49
  Specinfra::Backend::Exec
33
50
  end
34
51
  end
35
52
 
36
- def backend_options
37
- {
38
- disable_sudo: true,
39
- host: host,
40
- ssh_options: {
41
- user: ssh_user,
42
- },
43
- }
53
+ def hosts
54
+ options[:hosts].split(",")
44
55
  end
45
56
 
46
- def host
47
- options[:host]
57
+ # @param [Specinfra::Backend::Base]
58
+ # @return [String]
59
+ def host_for(backend)
60
+ backend.get_config(:host) || "localhost"
48
61
  end
49
62
 
50
63
  # @return [Slop] Command-line options
51
64
  def options
52
65
  @options ||= Slop.parse!(@argv, help: true) do
53
66
  banner "Usage: serverkit ACTION [options]"
54
- on "--host=", "Pass hostname to use SSH"
67
+ on "--hosts=", "Pass hostname to execute command over SSH"
55
68
  on "--variables=", "Path to variables file for ERB recipe"
56
69
  end
57
70
  end
58
71
 
72
+ def setup
73
+ abort_with_errors unless recipe.valid?
74
+ end
75
+
59
76
  # @return [Serverkit::Recipe]
60
77
  def recipe
61
78
  @recipe ||= Loaders::RecipeLoader.new(recipe_path, variables_path: options[:variables]).load
@@ -66,8 +83,9 @@ module Serverkit
66
83
  @argv[1] or raise Errors::MissingRecipePathArgumentError
67
84
  end
68
85
 
86
+ # @param [String] host
69
87
  # @return [String] User name used on SSH
70
- def ssh_user
88
+ def ssh_user_for(host)
71
89
  Net::SSH::Config.for(host)[:user] || Etc.getlogin
72
90
  end
73
91
  end
@@ -1,17 +1,19 @@
1
1
  require "serverkit/actions/base"
2
+ require "thread"
2
3
 
3
4
  module Serverkit
4
5
  module Actions
5
6
  class Check < Base
6
- def call
7
- recipe.resources.each do |resource|
8
- resource.backend = backend
9
- if resource.check
10
- puts "[OK] #{resource.id}"
11
- else
12
- puts "[NG] #{resource.id}"
7
+ def run
8
+ backends.map do |backend|
9
+ Thread.new do
10
+ recipe.resources.map(&:dup).each do |resource|
11
+ resource.backend = backend
12
+ result = resource.check ? "OK" : "NG"
13
+ puts "[ #{result} ] #{resource.id} on #{host_for(backend)}"
14
+ end
13
15
  end
14
- end
16
+ end.each(&:join)
15
17
  end
16
18
  end
17
19
  end
@@ -1,15 +1,10 @@
1
1
  require "serverkit/actions/base"
2
- require "yaml"
3
2
 
4
3
  module Serverkit
5
4
  module Actions
6
5
  class Inspect < Base
7
- def call
8
- if recipe.valid?
9
- puts JSON.pretty_generate(recipe.to_hash)
10
- else
11
- abort_with_errors
12
- end
6
+ def run
7
+ puts JSON.pretty_generate(recipe.to_hash)
13
8
  end
14
9
  end
15
10
  end
@@ -3,12 +3,8 @@ require "serverkit/actions/base"
3
3
  module Serverkit
4
4
  module Actions
5
5
  class Validate < Base
6
- def call
7
- if recipe.valid?
8
- puts "Success"
9
- else
10
- abort_with_errors
11
- end
6
+ def run
7
+ puts "Success"
12
8
  end
13
9
  end
14
10
  end
@@ -28,7 +28,7 @@ module Serverkit
28
28
  else
29
29
  raise Errors::UnknownActionNameError
30
30
  end
31
- rescue Errors::Base, Slop::MissingOptionError => exception
31
+ rescue Errors::Base, Slop::MissingArgumentError, Slop::MissingOptionError => exception
32
32
  abort "Error: #{exception}"
33
33
  end
34
34
 
@@ -15,7 +15,6 @@ module Serverkit
15
15
  @path = path
16
16
  end
17
17
 
18
- # @todo Rescue Error::ENOENT error
19
18
  def load
20
19
  case
21
20
  when !pathname.exist?
@@ -5,7 +5,7 @@ require "serverkit/recipe"
5
5
  module Serverkit
6
6
  module Loaders
7
7
  class RecipeLoader < BaseLoader
8
- DEFAULT_VARIABLES = {}
8
+ DEFAULT_VARIABLES_DATA = {}
9
9
 
10
10
  # @param [String] path
11
11
  # @param [String, nil] variables_path
@@ -17,8 +17,9 @@ module Serverkit
17
17
  private
18
18
 
19
19
  # @note Override
20
+ # @return [Binding]
20
21
  def binding_for_erb
21
- ErbBindingContext.new(variables_data).binding
22
+ variables.to_mash.binding
22
23
  end
23
24
 
24
25
  # @note Override to pass @variables_path
@@ -45,30 +46,16 @@ module Serverkit
45
46
  Serverkit::Recipe
46
47
  end
47
48
 
48
- # @return [Hash]
49
- def variables_data
49
+ # @return [Serverkit::Variables]
50
+ def variables
50
51
  @variables ||= begin
51
52
  if has_variables_path?
52
- load_variables.to_hash
53
+ load_variables
53
54
  else
54
- DEFAULT_VARIABLES.dup
55
+ Variables.new(DEFAULT_VARIABLES_DATA.dup)
55
56
  end
56
57
  end
57
58
  end
58
-
59
- class ErbBindingContext
60
- attr_reader :variables
61
-
62
- # @param [hash] variables
63
- def initialize(variables)
64
- @variables = variables
65
- end
66
-
67
- # @note Change method visibility to public
68
- def binding
69
- super
70
- end
71
- end
72
59
  end
73
60
  end
74
61
  end
@@ -1,4 +1,5 @@
1
1
  require "active_support/core_ext/hash/deep_merge"
2
+ require "hashie/mash"
2
3
 
3
4
  module Serverkit
4
5
  class Variables
@@ -23,9 +24,16 @@ module Serverkit
23
24
  )
24
25
  end
25
26
 
26
- # @return [Hash]
27
- def to_hash
28
- @variables_data.dup
27
+ # @return [Hashie::Mash]
28
+ def to_mash
29
+ BindableMash.new(@variables_data.dup)
30
+ end
31
+
32
+ class BindableMash < Hashie::Mash
33
+ # @note Override visibility from private to public
34
+ def binding
35
+ super
36
+ end
29
37
  end
30
38
  end
31
39
  end
@@ -1,3 +1,3 @@
1
1
  module Serverkit
2
- VERSION = "0.1.0"
2
+ VERSION = "0.2.0"
3
3
  end
@@ -18,6 +18,7 @@ Gem::Specification.new do |spec|
18
18
 
19
19
  spec.add_runtime_dependency "activemodel"
20
20
  spec.add_runtime_dependency "activesupport"
21
+ spec.add_runtime_dependency "hashie"
21
22
  spec.add_runtime_dependency "highline"
22
23
  spec.add_runtime_dependency "slop", "~> 3.4"
23
24
  spec.add_runtime_dependency "specinfra"
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: serverkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryo Nakamura
@@ -38,6 +38,20 @@ dependencies:
38
38
  - - ">="
39
39
  - !ruby/object:Gem::Version
40
40
  version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: hashie
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :runtime
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
41
55
  - !ruby/object:Gem::Dependency
42
56
  name: highline
43
57
  requirement: !ruby/object:Gem::Requirement