kubec 0.1.0 → 0.2.0

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 (43) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +2 -0
  3. data/.overcommit.yml +30 -0
  4. data/README.md +3 -5
  5. data/exe/kubec +5 -0
  6. data/kubec.gemspec +8 -3
  7. data/lib/Kubeconfig +4 -0
  8. data/lib/kubec/application.rb +110 -0
  9. data/lib/kubec/deploy.rb +1 -0
  10. data/lib/kubec/dsl/env.rb +14 -0
  11. data/lib/kubec/dsl/kubernetes.rb +16 -0
  12. data/lib/kubec/dsl/paths.rb +16 -0
  13. data/lib/kubec/dsl/stages.rb +32 -0
  14. data/lib/kubec/dsl.rb +14 -0
  15. data/lib/kubec/environment.rb +16 -0
  16. data/lib/kubec/install.rb +1 -0
  17. data/lib/kubec/kubernetes/config.rb +45 -0
  18. data/lib/kubec/kubernetes/container.rb +31 -0
  19. data/lib/kubec/kubernetes/deployment.rb +17 -0
  20. data/lib/kubec/kubernetes/has_attribute.rb +22 -0
  21. data/lib/kubec/kubernetes/metadata.rb +25 -0
  22. data/lib/kubec/kubernetes/service.rb +35 -0
  23. data/lib/kubec/kubernetes/template.rb +29 -0
  24. data/lib/kubec/kubernetes.rb +71 -0
  25. data/lib/kubec/setup.rb +10 -0
  26. data/lib/kubec/status/base.rb +52 -0
  27. data/lib/kubec/status/deployment.rb +54 -0
  28. data/lib/kubec/status/pod.rb +57 -0
  29. data/lib/kubec/status/service.rb +48 -0
  30. data/lib/kubec/status.rb +1 -0
  31. data/lib/kubec/tasks/deploy.rake +25 -0
  32. data/lib/kubec/tasks/install.rake +11 -0
  33. data/lib/kubec/tasks/status.rake +26 -0
  34. data/lib/kubec/templates/Kubeconfig +6 -0
  35. data/lib/kubec/templates/config/kubec/production.rb +0 -0
  36. data/lib/kubec/templates/config/kubec/staging.rb +0 -0
  37. data/lib/kubec/templates/config/kubec.rb +0 -0
  38. data/lib/kubec/utils/helper.rb +12 -0
  39. data/lib/kubec/utils/humanize_time.rb +42 -0
  40. data/lib/kubec/utils.rb +7 -0
  41. data/lib/kubec/version.rb +1 -1
  42. data/lib/kubec.rb +15 -1
  43. metadata +107 -14
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 7d080cd8618fd037e593cff3fd9483246d947e9b
4
- data.tar.gz: 5c7775cca3fa44bddf615f820b9b8671feb48dad
3
+ metadata.gz: e29547dc53808d55661f20349483c90c709d8ae4
4
+ data.tar.gz: c2de23b6dbc3444b4ec3d5017ad1e31f227416e2
5
5
  SHA512:
6
- metadata.gz: 39a8b7c2d514f96ac76a72c6f2720a16b50d0522b49b738026a3bb0488e98ddc72e9f6707e6f0d01cab97399c609db553cce0aacd0c8bc557c315a2a141da6f4
7
- data.tar.gz: c4740970b8e934ffa508f85d24e7a225dd1e58d8b3fc2c20135037e361c6344f295bb725635a3fe08012e2e6662156696026c6429d624f312e8c171bac86ae55
6
+ metadata.gz: 27ac3b15f987988bc7ec555a98824c3e7377aa386eae3aab00b36b166fad84f8c6ec7ba8acaea572ca295e52acc4dc733dce214e53045fe961c58c94e37f1c6b
7
+ data.tar.gz: 2ce1d12f2f50f085e1121ade09320841308545add1eaa5921e843740d553090e6316fe5754276543e421302edced92a21b96c1fec7b35f2d7ce3d76bd5556385
data/.gitignore CHANGED
@@ -9,3 +9,5 @@
9
9
 
10
10
  # rspec failure tracking
11
11
  .rspec_status
12
+
13
+ Gemfile.lock
data/.overcommit.yml ADDED
@@ -0,0 +1,30 @@
1
+ ## Use this file to configure the Overcommit hooks you wish to use. This will
2
+ # extend the default configuration defined in:
3
+ # https://github.com/brigade/overcommit/blob/master/config/default.yml
4
+ #
5
+ # At the topmost level of this YAML file is a key representing type of hook
6
+ # being run (e.g. pre-commit, commit-msg, etc.). Within each type you can
7
+ # customize each hook, such as whether to only run it on certain files (via
8
+ # `include`), whether to only display output if it fails (via `quiet`), etc.
9
+ #
10
+ # For a complete list of hooks, see:
11
+ # https://github.com/brigade/overcommit/tree/master/lib/overcommit/hook
12
+ #
13
+ # For a complete list of options that you can use to customize hooks, see:
14
+ # https://github.com/brigade/overcommit#configuration
15
+ #
16
+ # Uncomment the following lines to make the configuration take effect.
17
+
18
+ PreCommit:
19
+ AuthorName:
20
+ enabled: false
21
+ RuboCop:
22
+ enabled: true
23
+ on_warn: fail # Treat all warnings as failures
24
+
25
+ TrailingWhitespace:
26
+ enabled: true
27
+
28
+ PostCheckout:
29
+ IndexTags:
30
+ enabled: true # Generate a tags file with `ctags` each time HEAD changes
data/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # Kubec
2
2
 
3
- Welcome to your new gem! In this directory, you'll find the files you need to be able to package up your Ruby library into a gem. Put your Ruby code in the file `lib/kubec`. To experiment with that code, run `bin/console` for an interactive prompt.
3
+ [![Gem Version](https://badge.fury.io/rb/kubec.svg)](https://badge.fury.io/rb/kubec) [![Build Status](https://travis-ci.org/5xRuby/kubec.svg?branch=master)](https://travis-ci.org/5xRuby/kubec) [![Test Coverage](https://api.codeclimate.com/v1/badges/8a4e6cf54745423e198b/test_coverage)](https://codeclimate.com/github/5xRuby/kubec/test_coverage) [![Maintainability](https://api.codeclimate.com/v1/badges/8a4e6cf54745423e198b/maintainability)](https://codeclimate.com/github/5xRuby/kubec/maintainability)
4
4
 
5
- TODO: Delete this and the text above, and describe your gem
5
+ Kubec is a utility for generate kubernetes config and deploy.
6
6
 
7
7
  ## Installation
8
8
 
@@ -22,8 +22,6 @@ Or install it yourself as:
22
22
 
23
23
  ## Usage
24
24
 
25
- TODO: Write usage instructions here
26
-
27
25
  ## Development
28
26
 
29
27
  After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
@@ -32,4 +30,4 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
32
30
 
33
31
  ## Contributing
34
32
 
35
- Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/kubec.
33
+ Bug reports and pull requests are welcome on GitHub at https://github.com/5xruby/kubec.
data/exe/kubec ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'kubec'
4
+
5
+ Kubec::Application.new.run
data/kubec.gemspec CHANGED
@@ -5,7 +5,7 @@ require 'kubec/version'
5
5
  Gem::Specification.new do |spec|
6
6
  spec.name = 'kubec'
7
7
  spec.version = Kubec::VERSION
8
- spec.authors = %w[五倍紅寶石 蒼時弦也]
8
+ spec.authors = %w[5xRuby Aotokitsuruya]
9
9
  spec.email = %w[rubygems@5xruby.tw contact@frost.tw]
10
10
 
11
11
  spec.summary = 'Kubec is a utility for generate kubernetes config ' \
@@ -21,8 +21,13 @@ Gem::Specification.new do |spec|
21
21
  spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
22
  spec.require_paths = ['lib']
23
23
 
24
+ spec.add_dependency 'colorize'
25
+ spec.add_dependency 'hirb'
26
+ spec.add_dependency 'rake', '~> 10.0'
27
+
24
28
  spec.add_development_dependency 'bundler', '~> 1.16'
25
- spec.add_development_dependency 'rake', '~> 10.0'
26
29
  spec.add_development_dependency 'rspec', '~> 3.0'
27
- spec.add_development_dependency 'rubocop', '~> 0.5'
30
+ spec.add_development_dependency 'rubocop', '~> 0.49.0'
31
+ spec.add_development_dependency 'overcommit'
32
+ spec.add_development_dependency 'simplecov'
28
33
  end
data/lib/Kubeconfig ADDED
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env kubec
2
+
3
+ include Kubec::DSL
4
+ require 'kubec/install'
@@ -0,0 +1,110 @@
1
+ module Kubec
2
+ # Kubec Application
3
+ class Application < Rake::Application
4
+ DEFAULT_KUBECFILES = [
5
+ 'Kubeconfig',
6
+ 'kubeconfig',
7
+ 'Kubeconfig.rb',
8
+ 'kubeconfig.rb'
9
+ ].freeze
10
+
11
+ def self.config_exist?
12
+ DEFAULT_KUBECFILES.map { |f| File.exist?(f) }.reduce(:|)
13
+ end
14
+
15
+ def initialize
16
+ super
17
+ @rakefiles = DEFAULT_KUBECFILES.dup << default_kubeconfig
18
+ end
19
+
20
+ def name
21
+ 'kubec'
22
+ end
23
+
24
+ def run
25
+ Rake.application = self
26
+ super
27
+ end
28
+
29
+ def top_level_tasks
30
+ # rubocop:disable Metrics/LineLength
31
+ return @top_level_tasks if tasks_without_stage_dependency.include?(@top_level_tasks.first)
32
+ # rubocop:enable Metrics/LineLength
33
+
34
+ @top_level_tasks.unshift(ensure_environment.to_s)
35
+ end
36
+
37
+ private
38
+
39
+ def ensure_environment
40
+ Rake::Task.define_task(:ensure_environment) do
41
+ puts 'Kubeconfig isn\'t install or stage not configured'
42
+ exit 1
43
+ end
44
+ end
45
+
46
+ def tasks_without_stage_dependency
47
+ stages + default_tasks
48
+ end
49
+
50
+ def default_tasks
51
+ %w[install]
52
+ end
53
+
54
+ def handle_options
55
+ setup_options
56
+
57
+ OptionParser.new do |opts|
58
+ opts.on_tail('-h', '--help', '-H', 'Display this help message.') do
59
+ puts opts
60
+ exit
61
+ end
62
+
63
+ standard_rake_options.each { |args| opts.on(*args) }
64
+ opts.environment('KUBEOPT')
65
+ end.parse!
66
+ end
67
+
68
+ def sort_options(options)
69
+ supported_options = %w[tasks]
70
+
71
+ options.select! do |(switch, *)|
72
+ switch =~ /--#{Regexp.union(supported_options)}/
73
+ end
74
+
75
+ super.push(debug, version)
76
+ end
77
+
78
+ def setup_options
79
+ options.rakelib = ['rakelib']
80
+ options.debug = false
81
+ options.trace_output = STDERR
82
+ end
83
+
84
+ def default_kubeconfig
85
+ File.expand_path('../../Kubeconfig', __FILE__)
86
+ end
87
+
88
+ def debug
89
+ [
90
+ '--[no-]debug', '-D',
91
+ 'Display debug information',
92
+ lambda do |value|
93
+ options.debug = value
94
+ end
95
+ ]
96
+ end
97
+
98
+ def version
99
+ [
100
+ '--version', '-V',
101
+ 'Display the program version.',
102
+ lambda do |_value|
103
+ puts "Kubec Version: #{Kubec::VERSION} "\
104
+ "(Rake Version: #{Rake::VERSION})"
105
+ exit
106
+ end
107
+ ]
108
+ end
109
+ end
110
+ end
@@ -0,0 +1 @@
1
+ load File.expand_path('../tasks/deploy.rake', __FILE__)
@@ -0,0 +1,14 @@
1
+ module Kubec
2
+ module DSL
3
+ # ENV
4
+ module Env
5
+ extend Forwardable
6
+
7
+ def_delegators :env, :fetch, :set
8
+
9
+ def env
10
+ Environment.instance
11
+ end
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kubec
4
+ module DSL
5
+ # :nodoc:
6
+ module Kubernetes
7
+ extend Forwardable
8
+
9
+ def_delegators :kubernetes, :service, :deployment
10
+
11
+ def kubernetes
12
+ Kubec::Kubernetes.instance
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ module Kubec
2
+ module DSL
3
+ # :nodoc:
4
+ module Paths
5
+ def stage_config_path
6
+ Pathname.new 'config/kubec'
7
+ end
8
+
9
+ def stack_config_path
10
+ Pathname.new 'config/kubec.rb'
11
+ end
12
+ end
13
+ end
14
+ end
15
+
16
+ extend Kubec::DSL
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kubec
4
+ module DSL
5
+ # Stage
6
+ module Stages
7
+ RESERVED_NAMES = %w[deploy install status logs].freeze
8
+
9
+ def stages
10
+ names = Dir[stage_definitions].map { |f| File.basename(f, '.rb') }
11
+ assert_valid_stage_names(names)
12
+ names
13
+ end
14
+
15
+ def stage_definitions
16
+ stage_config_path.join('*.rb')
17
+ end
18
+
19
+ def stage_set?
20
+ !fetch(:stage, nil).nil?
21
+ end
22
+
23
+ def assert_valid_stage_names(names)
24
+ invalid = names.find { |n| RESERVED_NAMES.include?(n) }
25
+ return if invalid.nil?
26
+
27
+ # TODO: I18n Support
28
+ raise "Invalid stage name #{invalid}.rb"
29
+ end
30
+ end
31
+ end
32
+ end
data/lib/kubec/dsl.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'kubec/dsl/stages'
2
+ require 'kubec/dsl/paths'
3
+ require 'kubec/dsl/env'
4
+ require 'kubec/dsl/kubernetes'
5
+
6
+ module Kubec
7
+ # DSL
8
+ module DSL
9
+ include Env
10
+ include Paths
11
+ include Stages
12
+ include Kubernetes
13
+ end
14
+ end
@@ -0,0 +1,16 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kubec
4
+ # Environment
5
+ class Environment < Hash
6
+ include Singleton
7
+
8
+ def fetch(key, default = nil)
9
+ self[key.to_sym] || default
10
+ end
11
+
12
+ def set(key, value)
13
+ self[key.to_sym] = value
14
+ end
15
+ end
16
+ end
@@ -0,0 +1 @@
1
+ load File.expand_path('../tasks/install.rake', __FILE__)
@@ -0,0 +1,45 @@
1
+ module Kubec
2
+ class Kubernetes
3
+ # :nodoc:
4
+ class Config < Hash
5
+ extend Forwardable
6
+
7
+ attr_reader :name
8
+
9
+ def_delegators :metadata, :labels, :label
10
+
11
+ class << self
12
+ def api_version(version = nil)
13
+ return @api_version if version.nil?
14
+ @api_version = version
15
+ end
16
+
17
+ def kind
18
+ name.split('::').last
19
+ end
20
+ end
21
+
22
+ def initialize(name, &block)
23
+ @name = name.to_sym
24
+ prepare
25
+ instance_eval(&block)
26
+ end
27
+
28
+ def spec
29
+ self[:spec]
30
+ end
31
+
32
+ def metadata(&block)
33
+ return self[:metadata] unless block_given?
34
+ self[:metadata].instance_eval(&block)
35
+ end
36
+
37
+ def prepare
38
+ self[:apiVersion] = self.class.api_version
39
+ self[:kind] = self.class.kind
40
+ self[:metadata] = Metadata.new(@name)
41
+ self[:spec] = {}
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,31 @@
1
+ module Kubec
2
+ class Kubernetes
3
+ # :nodoc:
4
+ class Container < Hash
5
+ include HasAttribute
6
+
7
+ attribute :image
8
+ attribute :name
9
+ attribute :ports
10
+
11
+ def initialize(name, &block)
12
+ self[:name] = name
13
+ instance_eval(&block)
14
+ end
15
+
16
+ # TODO: Add object to check fields
17
+ def port(container_port, host_port = nil,
18
+ ip: nil, name: nil, protocol: nil)
19
+ self[:ports] ||= []
20
+ port = {
21
+ containerPort: container_port,
22
+ hostPort: host_port,
23
+ hostIP: ip,
24
+ name: name,
25
+ protocol: protocol
26
+ }.compact
27
+ self[:ports].push port
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,17 @@
1
+ module Kubec
2
+ class Kubernetes
3
+ # :nodoc:
4
+ class Deployment < Config
5
+ api_version 'extensions/v1beta1'
6
+
7
+ def replicas(size)
8
+ spec[:replicas] = size.to_i
9
+ end
10
+
11
+ def template(&block)
12
+ spec[:template] ||= Template.new
13
+ spec[:template].instance_eval(&block)
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,22 @@
1
+ module Kubec
2
+ class Kubernetes
3
+ # :nodoc:
4
+ module HasAttribute
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ end
8
+
9
+ # :nodoc:
10
+ module ClassMethods
11
+ def attribute(name)
12
+ name = name.to_sym
13
+ define_method(name) do |value|
14
+ return self[name] if value.nil?
15
+ self[name] = value
16
+ end
17
+ define_method("#{name}=") { |value| self[name] = value }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,25 @@
1
+ module Kubec
2
+ class Kubernetes
3
+ # :nodoc:
4
+ class Metadata < Hash
5
+ def initialize(name = nil)
6
+ self[:name] = name unless name.nil?
7
+ self[:namespace] = fetch(:stage, :staging)
8
+ end
9
+
10
+ def label(key, value)
11
+ self['labels'] ||= {}
12
+ self['labels'][key] = value
13
+ end
14
+
15
+ def labels=(labels = nil)
16
+ self['labels'] ||= {}
17
+ return self['labels'] if labels.nil?
18
+ # TODO: Check labels is valid
19
+ self['labels'] = labels
20
+ end
21
+
22
+ alias labels labels=
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,35 @@
1
+ module Kubec
2
+ class Kubernetes
3
+ # :nodoc:
4
+ class Service < Config
5
+ api_version 'v1'
6
+
7
+ def node_port
8
+ spec[:type] = 'NodePort'
9
+ end
10
+
11
+ def load_balancer(ip: nil)
12
+ spec[:type] = 'LoadBalancer'
13
+ spec[:loadBalancerIP] = ip unless ip.nil?
14
+ end
15
+
16
+ def port(port, target = nil)
17
+ spec[:ports] ||= []
18
+ target ||= port
19
+ spec[:ports].push port: port, targetPort: target
20
+ end
21
+
22
+ def select(key, value)
23
+ spec[:selector] ||= {}
24
+ spec[:selector][key] = value
25
+ end
26
+
27
+ def selector=(labels)
28
+ # TODO: Check labels type
29
+ spec[:selector] = labels
30
+ end
31
+
32
+ alias selector selector=
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,29 @@
1
+ module Kubec
2
+ class Kubernetes
3
+ # :nodoc:
4
+ class Template < Hash
5
+ extend Forwardable
6
+
7
+ def_delegators :metadata, :labels, :label
8
+
9
+ def initialize
10
+ self[:metadata] = Metadata.new
11
+ self[:spec] = {}
12
+ end
13
+
14
+ def metadata(&block)
15
+ return self[:metadata] unless block_given?
16
+ self[:metadata].instance_eval(&block)
17
+ end
18
+
19
+ def spec
20
+ self[:spec]
21
+ end
22
+
23
+ def container(name, &block)
24
+ spec[:containers] ||= []
25
+ spec[:containers].push Container.new(name, &block)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,71 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Kubec
4
+ # Kubernetes
5
+ class Kubernetes
6
+ autoload :Config, 'kubec/kubernetes/config'
7
+ autoload :Template, 'kubec/kubernetes/template'
8
+ autoload :Service, 'kubec/kubernetes/service'
9
+ autoload :Deployment, 'kubec/kubernetes/deployment'
10
+ autoload :Metadata, 'kubec/kubernetes/metadata'
11
+ autoload :Spec, 'kubec/kubernetes/spec'
12
+ autoload :Container, 'kubec/kubernetes/container'
13
+
14
+ autoload :HasAttribute, 'kubec/kubernetes/has_attribute'
15
+
16
+ include Singleton
17
+
18
+ APPLYABLE_TYPES = %i[service deployment].freeze
19
+
20
+ class << self
21
+ def apply(type)
22
+ # TODO: Raise an error
23
+ return unless APPLYABLE_TYPES.include?(type.to_sym)
24
+ return debug(type) if Rake.application.options.debug
25
+ # TODO: Replace with RESTful API
26
+ IO.popen('kubectl apply -f -', 'r+') do |io|
27
+ io.write convert_to_json(instance.send(type))
28
+ io.close_write
29
+ puts io.gets
30
+ end
31
+ end
32
+
33
+ def debug(type)
34
+ return unless APPLYABLE_TYPES.include?(type.to_sym)
35
+ puts JSON.pretty_generate(instance.send(type))
36
+ end
37
+
38
+ def ensure_namespace
39
+ return if Rake.application.options.debug
40
+ stage = fetch(:stage, :staging)
41
+
42
+ # TODO: Replace with RESTful API
43
+ `kubectl get ns #{stage} 2>&1`
44
+ `kubectl create ns #{stage}` unless $CHILD_STATUS.success?
45
+ end
46
+
47
+ def convert_to_json(items)
48
+ {
49
+ apiVersion: 'v1',
50
+ items: items,
51
+ kind: 'List'
52
+ }.to_json
53
+ end
54
+ end
55
+
56
+ def initialize
57
+ @services = []
58
+ @deployments = []
59
+ end
60
+
61
+ def service(name = nil, &block)
62
+ return @services if name.nil?
63
+ @services << Kubernetes::Service.new(name, &block)
64
+ end
65
+
66
+ def deployment(name = nil, &block)
67
+ return @deployments if name.nil?
68
+ @deployments << Kubernetes::Deployment.new(name, &block)
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,10 @@
1
+ include Kubec::DSL
2
+
3
+ stages.each do |stage|
4
+ Rake::Task.define_task(stage) do
5
+ set(:stage, stage)
6
+
7
+ load stage_config_path.join("#{stage}.rb")
8
+ load stack_config_path
9
+ end
10
+ end
@@ -0,0 +1,52 @@
1
+ module Kubec
2
+ # :nodoc:
3
+ module Status
4
+ autoload :Printer, 'kubec/status/printer'
5
+ autoload :Service, 'kubec/status/service'
6
+ autoload :Deployment, 'kubec/status/deployment'
7
+ autoload :Pod, 'kubec/status/pod'
8
+
9
+ # :nodoc:
10
+ class Base
11
+ include Enumerable
12
+
13
+ class << self
14
+ def fields(fields = nil)
15
+ return @fields if fields.nil?
16
+ @fields = fields
17
+ end
18
+
19
+ def print
20
+ if fields.nil?
21
+ ptus Hirb::Helpers::AutoTable.render(new)
22
+ else
23
+ puts Hirb::Helpers::AutoTable.render(new, fields: fields)
24
+ end
25
+ end
26
+ end
27
+
28
+ def initialize
29
+ @type = self.class.name.split('::').last.downcase
30
+ @result = `kubectl -n #{fetch(:stage, :staging)} get #{@type} -o json`
31
+ @success = $CHILD_STATUS.success?
32
+ @items = []
33
+
34
+ prepare
35
+ end
36
+
37
+ def each(&_block)
38
+ return unless @success
39
+ @items.each do |item|
40
+ yield self.class.const_get('Item').new(item)
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def prepare
47
+ return unless @success
48
+ @items = JSON.parse(@result).dig('items') || []
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,54 @@
1
+ module Kubec
2
+ module Status
3
+ # :nodoc:
4
+ class Deployment < Base
5
+ fields ['Name', 'Desire', 'Current', 'Up to date', 'Available', 'Age']
6
+
7
+ # :nodoc:
8
+ class Item < Hash
9
+ FIELDS = %i[name desire current up_to_date available age].freeze
10
+
11
+ attr_reader :spec, :metadata, :status
12
+
13
+ def initialize(data)
14
+ @data = data
15
+ @metadata = data.dig('metadata')
16
+ @spec = data.dig('spec')
17
+ @status = data.dig('status')
18
+
19
+ FIELDS.each { |field| send("setup_#{field}") }
20
+ end
21
+
22
+ protected
23
+
24
+ def setup_name
25
+ self['Name'] = metadata['name']
26
+ end
27
+
28
+ def setup_desire
29
+ self['Desire'] = spec['replicas'] || 1
30
+ end
31
+
32
+ def setup_current
33
+ self['Current'] = status['readyReplicas'] || 0
34
+ end
35
+
36
+ def setup_up_to_date
37
+ self['Up to date'] = status['updatedReplicas'] || 0
38
+ end
39
+
40
+ def setup_available
41
+ self['Available'] = status['availableReplicas'] || 0
42
+ end
43
+
44
+ def setup_age
45
+ created_at = DateTime.parse(metadata['creationTimestamp']).to_time
46
+ secs = (Time.now - created_at).ceil
47
+ self['Age'] =
48
+ Utils::HumanizeTime
49
+ .humanize(secs, short: true, join: false).first
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -0,0 +1,57 @@
1
+ module Kubec
2
+ module Status
3
+ # :nodoc:
4
+ class Pod < Base
5
+ fields %w[Name Ready Status Restarts Age]
6
+
7
+ # :nodoc:
8
+ class Item < Hash
9
+ FIELDS = %i[name ready status restarts age].freeze
10
+
11
+ attr_reader :spec, :metadata, :status, :containers
12
+
13
+ def initialize(data)
14
+ @data = data
15
+ @metadata = data.dig('metadata')
16
+ @spec = data.dig('spec')
17
+ @status = data.dig('status')
18
+ @containers = data.dig('status', 'containerStatuses')
19
+
20
+ FIELDS.each { |field| send("setup_#{field}") }
21
+ end
22
+
23
+ protected
24
+
25
+ def setup_name
26
+ self['Name'] = metadata['name']
27
+ end
28
+
29
+ def setup_ready
30
+ total = containers.size
31
+ ready = containers.select do |item|
32
+ item['state'].key?('running')
33
+ end.size
34
+ self['Ready'] = "#{ready}/#{total}"
35
+ end
36
+
37
+ def setup_status
38
+ self['Status'] = status['phase']
39
+ end
40
+
41
+ def setup_restarts
42
+ self['Restarts'] = containers.map do |c|
43
+ c['restartCount'].to_i
44
+ end.sum
45
+ end
46
+
47
+ def setup_age
48
+ created_at = DateTime.parse(status['startTime']).to_time
49
+ secs = (Time.now - created_at).ceil
50
+ self['Age'] =
51
+ Utils::HumanizeTime
52
+ .humanize(secs, short: true, join: false).first
53
+ end
54
+ end
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,48 @@
1
+ module Kubec
2
+ module Status
3
+ # :nodoc:
4
+ class Service < Base
5
+ fields ['Name', 'Cluster IP', 'Ports', 'Selector']
6
+
7
+ # :nodoc:
8
+ class Item < Hash
9
+ FIELDS = %i[name cluster_ip ports selector].freeze
10
+
11
+ attr_reader :spec, :metadata
12
+
13
+ def initialize(data)
14
+ @data = data
15
+ @metadata = data.dig('metadata')
16
+ @spec = data.dig('spec')
17
+
18
+ FIELDS.each { |field| send("setup_#{field}") }
19
+ end
20
+
21
+ protected
22
+
23
+ def setup_name
24
+ self['Name'] = metadata['name']
25
+ end
26
+
27
+ def setup_cluster_ip
28
+ self['Cluster IP'] = spec['clusterIP']
29
+ end
30
+
31
+ def setup_ports
32
+ self['Ports'] = spec['ports'].map do |port|
33
+ target = port['nodePort'] || port['targetPort']
34
+ "#{port['port']}:#{target}/#{port['protocol']}"
35
+ end.join(', ')
36
+ end
37
+
38
+ def setup_selector
39
+ self['Selector'] =
40
+ spec['selector']
41
+ .map(&:to_a)
42
+ .map { |pair| pair.join('=') }
43
+ .join(', ')
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1 @@
1
+ load File.expand_path('../tasks/status.rake', __FILE__)
@@ -0,0 +1,25 @@
1
+ namespace :deploy do
2
+ task :namespace do
3
+ Kubec::Kubernetes.ensure_namespace
4
+ end
5
+
6
+ # TODO: Add support for real kubectl
7
+ desc 'Apply Deployment to Kubernetes'
8
+ task :deployments do
9
+ Kubec::Utils::Helper.header 'Starting apply deployments'
10
+ Kubec::Kubernetes.apply(:deployment)
11
+ end
12
+
13
+ desc 'Apply Service to Kubernetes'
14
+ task :services do
15
+ Kubec::Utils::Helper.header 'Starting apply services'
16
+ Kubec::Kubernetes.apply(:service)
17
+ end
18
+ end
19
+
20
+ desc 'Deploy to Kubernetes'
21
+ task deploy: [
22
+ 'deploy:namespace',
23
+ 'deploy:deployments',
24
+ 'deploy:services'
25
+ ]
@@ -0,0 +1,11 @@
1
+ task :install do
2
+ template_path = File.expand_path('../../templates', __FILE__)
3
+
4
+ if Kubec::Application.config_exist?
5
+ warn '[SKIP] Kubec is already setup'
6
+ exit 1
7
+ end
8
+
9
+ FileUtils.cp_r("#{template_path}/.", '.')
10
+ puts 'Kubec Initialized'
11
+ end
@@ -0,0 +1,26 @@
1
+ namespace :status do
2
+ desc 'Show service status'
3
+ task :service do
4
+ Kubec::Utils::Helper.header 'Service Status'
5
+ Kubec::Status::Service.print
6
+ end
7
+
8
+ desc 'Show deployment status'
9
+ task :deployment do
10
+ Kubec::Utils::Helper.header 'Deployment Status'
11
+ Kubec::Status::Deployment.print
12
+ end
13
+
14
+ desc 'Show pod status'
15
+ task :pod do
16
+ Kubec::Utils::Helper.header 'Pod Status'
17
+ Kubec::Status::Pod.print
18
+ end
19
+ end
20
+
21
+ desc 'Show deploy status'
22
+ task status: [
23
+ 'status:service',
24
+ 'status:deployment',
25
+ 'status:pod'
26
+ ]
@@ -0,0 +1,6 @@
1
+ # Setup environement
2
+ require 'kubec/setup'
3
+
4
+ # Add deploy scripts
5
+ require 'kubec/deploy'
6
+ require 'kubec/status'
File without changes
File without changes
File without changes
@@ -0,0 +1,12 @@
1
+ module Kubec
2
+ module Utils
3
+ # :nodoc:
4
+ module Helper
5
+ class << self
6
+ def header(title, color: :green)
7
+ puts "=== #{title} ===".colorize(color)
8
+ end
9
+ end
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,42 @@
1
+ module Kubec
2
+ module Utils
3
+ # :nodoc:
4
+ module HumanizeTime
5
+ PAIRS = [
6
+ [60, :seconds],
7
+ [60, :minutes],
8
+ [24, :hours],
9
+ [1000, :days]
10
+ ].freeze
11
+
12
+ SHORT = {
13
+ seocnds: 's',
14
+ minutes: 'm',
15
+ hours: 'h',
16
+ days: 'd'
17
+ }.freeze
18
+
19
+ class << self
20
+ def humanize(secs, short: false, join: true)
21
+ list = convert(secs).map do |(count, name)|
22
+ if short
23
+ "#{count}#{SHORT[name]}"
24
+ else
25
+ "#{count} #{name}"
26
+ end
27
+ end
28
+ return list unless join
29
+ list.join(' ')
30
+ end
31
+
32
+ def convert(secs)
33
+ PAIRS.dup.map do |count, name|
34
+ next unless secs > 0
35
+ secs, n = secs.divmod(count)
36
+ [n, name]
37
+ end.compact.reverse
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,7 @@
1
+ module Kubec
2
+ # :nodoc:
3
+ module Utils
4
+ autoload :HumanizeTime, 'kubec/utils/humanize_time'
5
+ autoload :Helper, 'kubec/utils/helper'
6
+ end
7
+ end
data/lib/kubec/version.rb CHANGED
@@ -1,3 +1,3 @@
1
1
  module Kubec
2
- VERSION = '0.1.0'.freeze
2
+ VERSION = '0.2.0'.freeze
3
3
  end
data/lib/kubec.rb CHANGED
@@ -1,6 +1,20 @@
1
+ require 'rake'
2
+ require 'json'
3
+ require 'forwardable'
4
+ require 'pathname'
5
+ require 'singleton'
6
+ require 'hirb'
7
+ require 'colorize'
8
+ require 'English'
9
+
1
10
  require 'kubec/version'
2
11
 
3
12
  # Kuberentes Config Tool
4
13
  module Kubec
5
- # Your code goes here...
14
+ autoload :Application, 'kubec/application'
15
+ autoload :Environment, 'kubec/environment'
16
+ autoload :Kubernetes, 'kubec/kubernetes'
17
+ autoload :DSL, 'kubec/dsl'
18
+ autoload :Status, 'kubec/status/base'
19
+ autoload :Utils, 'kubec/utils'
6
20
  end
metadata CHANGED
@@ -1,30 +1,44 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kubec
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
- - 五倍紅寶石
8
- - 蒼時弦也
7
+ - 5xRuby
8
+ - Aotokitsuruya
9
9
  autorequire:
10
10
  bindir: exe
11
11
  cert_chain: []
12
- date: 2018-01-08 00:00:00.000000000 Z
12
+ date: 2018-01-12 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
- name: bundler
15
+ name: colorize
16
16
  requirement: !ruby/object:Gem::Requirement
17
17
  requirements:
18
- - - "~>"
18
+ - - ">="
19
19
  - !ruby/object:Gem::Version
20
- version: '1.16'
21
- type: :development
20
+ version: '0'
21
+ type: :runtime
22
22
  prerelease: false
23
23
  version_requirements: !ruby/object:Gem::Requirement
24
24
  requirements:
25
- - - "~>"
25
+ - - ">="
26
26
  - !ruby/object:Gem::Version
27
- version: '1.16'
27
+ version: '0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: hirb
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ">="
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ">="
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
28
42
  - !ruby/object:Gem::Dependency
29
43
  name: rake
30
44
  requirement: !ruby/object:Gem::Requirement
@@ -32,13 +46,27 @@ dependencies:
32
46
  - - "~>"
33
47
  - !ruby/object:Gem::Version
34
48
  version: '10.0'
35
- type: :development
49
+ type: :runtime
36
50
  prerelease: false
37
51
  version_requirements: !ruby/object:Gem::Requirement
38
52
  requirements:
39
53
  - - "~>"
40
54
  - !ruby/object:Gem::Version
41
55
  version: '10.0'
56
+ - !ruby/object:Gem::Dependency
57
+ name: bundler
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - "~>"
61
+ - !ruby/object:Gem::Version
62
+ version: '1.16'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - "~>"
68
+ - !ruby/object:Gem::Version
69
+ version: '1.16'
42
70
  - !ruby/object:Gem::Dependency
43
71
  name: rspec
44
72
  requirement: !ruby/object:Gem::Requirement
@@ -59,23 +87,53 @@ dependencies:
59
87
  requirements:
60
88
  - - "~>"
61
89
  - !ruby/object:Gem::Version
62
- version: '0.5'
90
+ version: 0.49.0
63
91
  type: :development
64
92
  prerelease: false
65
93
  version_requirements: !ruby/object:Gem::Requirement
66
94
  requirements:
67
95
  - - "~>"
68
96
  - !ruby/object:Gem::Version
69
- version: '0.5'
97
+ version: 0.49.0
98
+ - !ruby/object:Gem::Dependency
99
+ name: overcommit
100
+ requirement: !ruby/object:Gem::Requirement
101
+ requirements:
102
+ - - ">="
103
+ - !ruby/object:Gem::Version
104
+ version: '0'
105
+ type: :development
106
+ prerelease: false
107
+ version_requirements: !ruby/object:Gem::Requirement
108
+ requirements:
109
+ - - ">="
110
+ - !ruby/object:Gem::Version
111
+ version: '0'
112
+ - !ruby/object:Gem::Dependency
113
+ name: simplecov
114
+ requirement: !ruby/object:Gem::Requirement
115
+ requirements:
116
+ - - ">="
117
+ - !ruby/object:Gem::Version
118
+ version: '0'
119
+ type: :development
120
+ prerelease: false
121
+ version_requirements: !ruby/object:Gem::Requirement
122
+ requirements:
123
+ - - ">="
124
+ - !ruby/object:Gem::Version
125
+ version: '0'
70
126
  description: Kubec is a utility for generate kubernetes config and deploy.
71
127
  email:
72
128
  - rubygems@5xruby.tw
73
129
  - contact@frost.tw
74
- executables: []
130
+ executables:
131
+ - kubec
75
132
  extensions: []
76
133
  extra_rdoc_files: []
77
134
  files:
78
135
  - ".gitignore"
136
+ - ".overcommit.yml"
79
137
  - ".rspec"
80
138
  - ".rubocop.yml"
81
139
  - ".travis.yml"
@@ -84,8 +142,43 @@ files:
84
142
  - Rakefile
85
143
  - bin/console
86
144
  - bin/setup
145
+ - exe/kubec
87
146
  - kubec.gemspec
147
+ - lib/Kubeconfig
88
148
  - lib/kubec.rb
149
+ - lib/kubec/application.rb
150
+ - lib/kubec/deploy.rb
151
+ - lib/kubec/dsl.rb
152
+ - lib/kubec/dsl/env.rb
153
+ - lib/kubec/dsl/kubernetes.rb
154
+ - lib/kubec/dsl/paths.rb
155
+ - lib/kubec/dsl/stages.rb
156
+ - lib/kubec/environment.rb
157
+ - lib/kubec/install.rb
158
+ - lib/kubec/kubernetes.rb
159
+ - lib/kubec/kubernetes/config.rb
160
+ - lib/kubec/kubernetes/container.rb
161
+ - lib/kubec/kubernetes/deployment.rb
162
+ - lib/kubec/kubernetes/has_attribute.rb
163
+ - lib/kubec/kubernetes/metadata.rb
164
+ - lib/kubec/kubernetes/service.rb
165
+ - lib/kubec/kubernetes/template.rb
166
+ - lib/kubec/setup.rb
167
+ - lib/kubec/status.rb
168
+ - lib/kubec/status/base.rb
169
+ - lib/kubec/status/deployment.rb
170
+ - lib/kubec/status/pod.rb
171
+ - lib/kubec/status/service.rb
172
+ - lib/kubec/tasks/deploy.rake
173
+ - lib/kubec/tasks/install.rake
174
+ - lib/kubec/tasks/status.rake
175
+ - lib/kubec/templates/Kubeconfig
176
+ - lib/kubec/templates/config/kubec.rb
177
+ - lib/kubec/templates/config/kubec/production.rb
178
+ - lib/kubec/templates/config/kubec/staging.rb
179
+ - lib/kubec/utils.rb
180
+ - lib/kubec/utils/helper.rb
181
+ - lib/kubec/utils/humanize_time.rb
89
182
  - lib/kubec/version.rb
90
183
  homepage: https://kubec.5xruby.tw
91
184
  licenses: []