serverkit 0.0.1 → 0.0.2

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: a46215c909742a449201656305440413650b941f
4
- data.tar.gz: 1e23e1ae21759a680affbdf69389e1900d59e8d7
3
+ metadata.gz: 3973baadffe8fb3a024c183ff1736bce8f48c719
4
+ data.tar.gz: aac29f5ee26cc9c77d644b9c6dd7f2cd9c22eebd
5
5
  SHA512:
6
- metadata.gz: ea78669c98502fea368c1f8c9042249190b988c8a12e82400276f82c396c9edc38db5fd4d3f4dc6a791091d07fd172bb57fcd16bb7349ee7d6030cb4ac910ec3
7
- data.tar.gz: 1d4a29c33da839040b7ce029047a7f2f14d1d64663cd028505e98051c0b3f58b9695b661c52079376d97e26250edfad0e0c511b5d8502aec14598fd4ae7fd6ca
6
+ metadata.gz: 6e93dd18c08bb32e80d57ea3b59e28b1eece1e0d93a438e51bcfbb3d8aae8c2fe17cfb7a36e1a279bd46b648bc40365d3f540b2dfe3c8668402f7701b64b606e
7
+ data.tar.gz: 33152a234df68bcd557b6ef38f0df5a58b29d0ac192315ab7b87cd87c2aa69449e1893ad91b19bab124b623c9d5137a2e8e02987f84d9794138bc0a1f509ea1f
data/.rubocop.yml CHANGED
@@ -31,6 +31,12 @@ Style/DoubleNegation:
31
31
  Style/FormatString:
32
32
  Enabled: false
33
33
 
34
+ Style/GuardClause:
35
+ Enabled: false
36
+
37
+ Style/IfUnlessModifier:
38
+ Enabled: false
39
+
34
40
  Style/MultilineBlockChain:
35
41
  Enabled: false
36
42
 
data/CHANGELOG.md ADDED
@@ -0,0 +1,6 @@
1
+ ## 0.0.2
2
+ - Add `serverkit validate` action
3
+
4
+ ## 0.0.1
5
+ - 1st Release on 2015-03-30
6
+ - Support `serverkit check` and `serverkit apply` actions
data/README.md CHANGED
@@ -1,16 +1,19 @@
1
1
  # Serverkit [![Build Status](https://travis-ci.org/r7kamura/serverkit.svg)](https://travis-ci.org/r7kamura/serverkit) [![Code Climate](https://codeclimate.com/github/r7kamura/serverkit/badges/gpa.svg)](https://codeclimate.com/github/r7kamura/serverkit)
2
- Configuration management toolkit for building servers.
2
+ Configuration management toolkit for your infrastructure.
3
3
 
4
4
  ## Usage
5
5
  ```sh
6
6
  # Write your recipe
7
7
  vi recipe.yml
8
8
 
9
- # Check
10
- bundle exec serverkit check --recipe=recipe.yml
9
+ # Validate recipe
10
+ serverkit validate --recipe=recipe.yml
11
11
 
12
- # Apply
13
- bundle exec serverkit apply --recipe=recipe.yml
12
+ # Check differences
13
+ serverkit check --recipe=recipe.yml
14
+
15
+ # Apply migration
16
+ serverkit apply --recipe=recipe.yml
14
17
  ```
15
18
 
16
19
  ### Example
@@ -0,0 +1,9 @@
1
+ require "active_model"
2
+
3
+ class ReadableValidator < ActiveModel::EachValidator
4
+ def validate_each(record, attribute, value)
5
+ unless File.readable?(value)
6
+ record.errors.add(attribute, "is unreadable")
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ require "active_model"
2
+
3
+ class RequiredValidator < ActiveModel::EachValidator
4
+ def validate_each(record, attribute, value)
5
+ if value.nil?
6
+ record.errors.add(attribute, "is required")
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ require "serverkit/actions/base"
2
+
3
+ module Serverkit
4
+ module Actions
5
+ class Validate < Base
6
+ def call
7
+ if recipe.valid?
8
+ puts "Success"
9
+ else
10
+ abort recipe.errors.map { |error| "Error: #{error}" }.join("\n")
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -1,5 +1,6 @@
1
1
  require "serverkit/actions/apply"
2
2
  require "serverkit/actions/check"
3
+ require "serverkit/actions/validate"
3
4
  require "slop"
4
5
 
5
6
  module Serverkit
@@ -7,6 +8,7 @@ module Serverkit
7
8
  ACTION_NAMES = %w(
8
9
  apply
9
10
  check
11
+ validate
10
12
  )
11
13
 
12
14
  # @param [Array<String>] argv
@@ -24,6 +26,8 @@ module Serverkit
24
26
  apply
25
27
  when action_name == "check"
26
28
  check
29
+ when action_name == "validate"
30
+ validate
27
31
  end
28
32
  rescue Slop::MissingOptionError => exception
29
33
  abort "Error: #{exception}"
@@ -67,5 +71,9 @@ module Serverkit
67
71
  on "-r", "--recipe=", "Path to recipe file", required: true
68
72
  end
69
73
  end
74
+
75
+ def validate
76
+ Actions::Validate.new(options).call
77
+ end
70
78
  end
71
79
  end
@@ -0,0 +1,6 @@
1
+ module Serverkit
2
+ module Errors
3
+ class Base
4
+ end
5
+ end
6
+ end
@@ -0,0 +1,16 @@
1
+ require "serverkit/errors/base"
2
+
3
+ module Serverkit
4
+ module Errors
5
+ class InvalidRecipeType < Base
6
+ # @param [Class] type
7
+ def initialize(type)
8
+ @type = type
9
+ end
10
+
11
+ def to_s
12
+ "Recipe data must be a Hash, not #{@type}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ require "serverkit/errors/base"
2
+
3
+ module Serverkit
4
+ module Errors
5
+ class InvalidResourcesType < Base
6
+ # @param [Class] type
7
+ def initialize(type)
8
+ @type = type
9
+ end
10
+
11
+ def to_s
12
+ "resources property must be an Array, not #{@type}"
13
+ end
14
+ end
15
+ end
16
+ end
@@ -1,4 +1,6 @@
1
1
  require "active_support/core_ext/string/inflections"
2
+ require "serverkit/errors/invalid_recipe_type"
3
+ require "serverkit/errors/invalid_resources_type"
2
4
  require "serverkit/resources/file"
3
5
  require "serverkit/resources/git"
4
6
  require "serverkit/resources/homebrew"
@@ -11,39 +13,85 @@ module Serverkit
11
13
  class << self
12
14
  # @param [String] path
13
15
  def load_from_path(path)
14
- raw_recipe = YAML.load_file(path)
15
- new(raw_recipe)
16
+ recipe_data = YAML.load_file(path)
17
+ new(recipe_data)
16
18
  end
17
19
  end
18
20
 
19
- # @param [Hash] raw_recipe
20
- def initialize(raw_recipe)
21
- @raw_recipe = raw_recipe
21
+ attr_reader :recipe_data
22
+
23
+ # @param [Hash] recipe_data
24
+ def initialize(recipe_data)
25
+ @recipe_data = recipe_data
22
26
  end
23
27
 
24
- # @return [Array<Serverkit::Resources::Base>]
25
- # @todo Delegate to resource builder
26
- def resources
27
- @resources ||= resource_properties.map do |properties|
28
- Resources.const_get(properties["type"].camelize, false).new(properties)
28
+ # @return [Array<Serverkit::Errors::Base>]
29
+ def errors
30
+ @errors ||= begin
31
+ case
32
+ when !has_valid_typed_recipe_data?
33
+ errors_for_invalid_typed_recipe_data
34
+ when !has_valid_typed_resources_property?
35
+ errors_for_invalid_typed_resources_property
36
+ else
37
+ errors_in_resources
38
+ end
29
39
  end
30
40
  end
31
41
 
32
42
  # @return [Array<String>]
33
43
  def hosts
34
- @raw_recipe["hosts"]
44
+ @recipe_data["hosts"]
45
+ end
46
+
47
+ # @return [Array<Serverkit::Resources::Base>]
48
+ # @todo Delegate to resource builder
49
+ def resources
50
+ @resources ||= resources_property.map do |attributes|
51
+ Resources.const_get(attributes["type"].camelize, false).new(attributes)
52
+ end
35
53
  end
36
54
 
37
55
  # @return [true, false] Flag to use SSH (default: true)
38
56
  def ssh?
39
- @raw_recipe["ssh"] != false
57
+ @recipe_data["ssh"] != false
58
+ end
59
+
60
+ def valid?
61
+ errors.empty?
40
62
  end
41
63
 
42
64
  private
43
65
 
66
+ # @return [Array<Serverkit::Errors::Base>]
67
+ def errors_for_invalid_typed_recipe_data
68
+ [Errors::InvalidRecipeType.new(@recipe_data.class)]
69
+ end
70
+
71
+ # @return [Array<Serverkit::Errors::Base>]
72
+ def errors_for_invalid_typed_resources_property
73
+ [Errors::InvalidResourcesType.new(resources_property.class)]
74
+ end
75
+
76
+ # @return [Array<Serverkit::Errors::Base>]
77
+ def errors_in_resources
78
+ resources.flat_map do |resource|
79
+ resource.validate
80
+ resource.errors.to_a
81
+ end
82
+ end
83
+
84
+ def has_valid_typed_resources_property?
85
+ resources_property.is_a?(Array)
86
+ end
87
+
88
+ def has_valid_typed_recipe_data?
89
+ @recipe_data.is_a?(Hash)
90
+ end
91
+
44
92
  # @return [Array<String>]
45
- def resource_properties
46
- @raw_recipe["resources"] || []
93
+ def resources_property
94
+ @recipe_data["resources"] || []
47
95
  end
48
96
  end
49
97
  end
@@ -1,16 +1,28 @@
1
+ require "active_model"
2
+
1
3
  module Serverkit
2
4
  module Resources
3
5
  class Base
6
+ class << self
7
+ # @note DSL method to define attribute with its validations
8
+ def attribute(name, options = {})
9
+ default = options.delete(:default)
10
+ define_method(name) do
11
+ @attributes[name.to_s] || default
12
+ end
13
+ validates name, options unless options.empty?
14
+ end
15
+ end
16
+
17
+ include ActiveModel::Validations
18
+
4
19
  attr_accessor :backend
5
20
 
6
- # @param [Hash] properties
7
- def initialize(properties)
8
- @properties = properties
9
- end
21
+ attribute :name, required: true
10
22
 
11
- # @return [String]
12
- def name
13
- @properties["name"]
23
+ # @param [Hash] attributes
24
+ def initialize(attributes)
25
+ @attributes = attributes
14
26
  end
15
27
 
16
28
  private
@@ -1,9 +1,16 @@
1
1
  require "digest"
2
+ require "readable_validator"
3
+ require "required_validator"
2
4
  require "serverkit/resources/base"
3
5
 
4
6
  module Serverkit
5
7
  module Resources
6
8
  class File < Base
9
+ attribute :destination, required: true
10
+ attribute :group
11
+ attribute :owner
12
+ attribute :source, readable: true, required: true
13
+
7
14
  def apply
8
15
  send_file if file_sendable?
9
16
  change_group unless has_valid_group?
@@ -25,20 +32,10 @@ module Serverkit
25
32
  run_command_from_identifier(:change_file_owner, destination, owner)
26
33
  end
27
34
 
28
- # @return [String]
29
- def destination
30
- @properties["destination"]
31
- end
32
-
33
35
  def file_sendable?
34
36
  !has_file? || !has_same_content?
35
37
  end
36
38
 
37
- # @return [String]
38
- def group
39
- @properties["group"]
40
- end
41
-
42
39
  def has_file?
43
40
  check_command_from_identifier(:check_file_is_file, destination)
44
41
  end
@@ -55,17 +52,11 @@ module Serverkit
55
52
  owner.nil? || check_command_from_identifier(:check_file_is_owned_by, destination, owner)
56
53
  end
57
54
 
58
- # @todo Rescue Errno::ENOENT from File.read
59
55
  # @return [String]
60
56
  def local_file_sha256sum
61
57
  ::Digest::SHA256.hexdigest(::File.read(source))
62
58
  end
63
59
 
64
- # @return [String]
65
- def owner
66
- @properties["owner"]
67
- end
68
-
69
60
  # @return [String]
70
61
  def remote_file_sha256sum
71
62
  run_command_from_identifier(:get_file_sha256sum, destination).stdout.rstrip
@@ -74,11 +65,6 @@ module Serverkit
74
65
  def send_file
75
66
  run_command_from_identifier(:send_file, source, destination)
76
67
  end
77
-
78
- # @return [String]
79
- def source
80
- @properties["source"]
81
- end
82
68
  end
83
69
  end
84
70
  end
@@ -5,6 +5,10 @@ module Serverkit
5
5
  class Git < Base
6
6
  DEFAULT_STATUS = "cloned"
7
7
 
8
+ attribute :path, required: true
9
+ attribute :repository, required: true
10
+ attribute :status, default: DEFAULT_STATUS
11
+
8
12
  def apply
9
13
  clone if clonable?
10
14
  update if updatable?
@@ -48,21 +52,6 @@ module Serverkit
48
52
  run_command("cd #{path} && git ls-remote origin HEAD").stdout.split.first
49
53
  end
50
54
 
51
- # @return [String] Where to locate cloned repository
52
- def path
53
- @properties["path"]
54
- end
55
-
56
- # @return [String] Git repository URL
57
- def repository
58
- @properties["repository"]
59
- end
60
-
61
- # @return [String]
62
- def status
63
- @properties["status"] || DEFAULT_STATUS
64
- end
65
-
66
55
  def updatable?
67
56
  status == "updated" && !updated?
68
57
  end
@@ -3,6 +3,8 @@ require "serverkit/resources/base"
3
3
  module Serverkit
4
4
  module Resources
5
5
  class HomebrewCask < Base
6
+ attribute :package, required: true
7
+
6
8
  def apply
7
9
  run_command("brew cask install #{package}")
8
10
  end
@@ -11,13 +13,6 @@ module Serverkit
11
13
  def check
12
14
  check_command("/usr/local/bin/brew cask list -1 | grep -E '^#{package}$'")
13
15
  end
14
-
15
- private
16
-
17
- # @return [String]
18
- def package
19
- @properties["package"]
20
- end
21
16
  end
22
17
  end
23
18
  end
@@ -3,6 +3,8 @@ require "serverkit/resources/base"
3
3
  module Serverkit
4
4
  module Resources
5
5
  class Package < Base
6
+ attribute :package, required: true
7
+
6
8
  def apply
7
9
  run_command_from_identifier(:install, package)
8
10
  end
@@ -11,13 +13,6 @@ module Serverkit
11
13
  def check
12
14
  check_command_from_identifier(:check_package_is_installed, package)
13
15
  end
14
-
15
- private
16
-
17
- # @return [String]
18
- def package
19
- @properties["package"]
20
- end
21
16
  end
22
17
  end
23
18
  end
@@ -3,6 +3,9 @@ require "serverkit/resources/base"
3
3
  module Serverkit
4
4
  module Resources
5
5
  class Symlink < Base
6
+ attribute :destination, required: true
7
+ attribute :source, required: true
8
+
6
9
  def apply
7
10
  run_command_from_identifier(:link_file_to, source, destination)
8
11
  end
@@ -11,18 +14,6 @@ module Serverkit
11
14
  def check
12
15
  check_command_from_identifier(:check_file_is_linked_to, source, destination)
13
16
  end
14
-
15
- private
16
-
17
- # @return [String]
18
- def destination
19
- @properties["destination"]
20
- end
21
-
22
- # @return [String]
23
- def source
24
- @properties["source"]
25
- end
26
17
  end
27
18
  end
28
19
  end
@@ -1,3 +1,3 @@
1
1
  module Serverkit
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
data/serverkit.gemspec CHANGED
@@ -7,7 +7,7 @@ Gem::Specification.new do |spec|
7
7
  spec.version = Serverkit::VERSION
8
8
  spec.authors = ["Ryo Nakamura"]
9
9
  spec.email = ["r7kamura@gmail.com"]
10
- spec.summary = "Configuration management toolkit for building servers."
10
+ spec.summary = "Configuration management toolkit for your infrastructure."
11
11
  spec.homepage = "https://github.com/r7kamura/serverkit"
12
12
  spec.license = "MIT"
13
13
  spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
@@ -15,6 +15,7 @@ Gem::Specification.new do |spec|
15
15
  spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
16
16
  spec.require_paths = ["lib"]
17
17
 
18
+ spec.add_runtime_dependency "activemodel"
18
19
  spec.add_runtime_dependency "activesupport"
19
20
  spec.add_runtime_dependency "slop", "~> 3.4"
20
21
  spec.add_runtime_dependency "specinfra"
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: serverkit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Ryo Nakamura
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-03-29 00:00:00.000000000 Z
11
+ date: 2015-03-30 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activemodel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: activesupport
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -134,17 +148,24 @@ files:
134
148
  - ".rspec"
135
149
  - ".rubocop.yml"
136
150
  - ".travis.yml"
151
+ - CHANGELOG.md
137
152
  - Gemfile
138
153
  - LICENSE.txt
139
154
  - README.md
140
155
  - Rakefile
141
156
  - bin/serverkit
142
157
  - examples/recipe.yml
158
+ - lib/readable_validator.rb
159
+ - lib/required_validator.rb
143
160
  - lib/serverkit.rb
144
161
  - lib/serverkit/actions/apply.rb
145
162
  - lib/serverkit/actions/base.rb
146
163
  - lib/serverkit/actions/check.rb
164
+ - lib/serverkit/actions/validate.rb
147
165
  - lib/serverkit/command.rb
166
+ - lib/serverkit/errors/base.rb
167
+ - lib/serverkit/errors/invalid_recipe_type.rb
168
+ - lib/serverkit/errors/invalid_resources_type.rb
148
169
  - lib/serverkit/recipe.rb
149
170
  - lib/serverkit/resources/base.rb
150
171
  - lib/serverkit/resources/file.rb
@@ -178,6 +199,6 @@ rubyforge_project:
178
199
  rubygems_version: 2.4.5
179
200
  signing_key:
180
201
  specification_version: 4
181
- summary: Configuration management toolkit for building servers.
202
+ summary: Configuration management toolkit for your infrastructure.
182
203
  test_files: []
183
204
  has_rdoc: