rhea 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (69) hide show
  1. checksums.yaml +4 -4
  2. data/README.md +165 -2
  3. data/app/assets/javascripts/rhea/application.js +2 -0
  4. data/app/assets/javascripts/rhea/main.js +3 -0
  5. data/app/assets/javascripts/vendor/jquery-1.11.3.js +10351 -0
  6. data/app/assets/stylesheets/rhea/application.css.sass +1 -0
  7. data/app/assets/stylesheets/rhea/layout.css.sass +31 -0
  8. data/app/controllers/rhea/base_controller.rb +26 -0
  9. data/app/controllers/rhea/commands_controller.rb +168 -0
  10. data/app/controllers/rhea/events_controller.rb +8 -0
  11. data/app/controllers/rhea/nodes_controller.rb +7 -0
  12. data/app/controllers/rhea/system_services_controller.rb +7 -0
  13. data/app/helpers/rhea/helper.rb +42 -0
  14. data/app/views/rhea/command_types/_list.html.haml +5 -0
  15. data/app/views/rhea/commands/_form.html.haml +22 -0
  16. data/app/views/rhea/commands/_table.html.haml +51 -0
  17. data/app/views/rhea/commands/index.html.haml +51 -0
  18. data/app/views/rhea/errors/index.html.haml +2 -0
  19. data/app/views/rhea/events/index.html.haml +24 -0
  20. data/app/views/rhea/layouts/application.html.haml +35 -0
  21. data/app/views/rhea/nodes/index.html.haml +29 -0
  22. data/app/views/rhea/system_services/index.html.haml +2 -0
  23. data/config/routes.rb +21 -0
  24. data/docs/commands.gif +0 -0
  25. data/docs/commands.mov +0 -0
  26. data/docs/events.png +0 -0
  27. data/docs/logo.png +0 -0
  28. data/docs/nodes.png +0 -0
  29. data/fixtures/vcr_cassettes/Rhea_Kubernetes_Commands_All/_perform/an_existing_rc/returns_the_rc.yml +197 -0
  30. data/fixtures/vcr_cassettes/Rhea_Kubernetes_Commands_Delete/_perform/an_existing_rc/deletes_the_rc.yml +163 -0
  31. data/fixtures/vcr_cassettes/Rhea_Kubernetes_Commands_Export/_perform/an_existing_rc/returns_the_data.yml +197 -0
  32. data/fixtures/vcr_cassettes/Rhea_Kubernetes_Commands_Get/_perform/an_existing_rc/gets_the_rc.yml +196 -0
  33. data/fixtures/vcr_cassettes/Rhea_Kubernetes_Commands_Import/_perform/no_existing_rcs/creates_the_rcs.yml +197 -0
  34. data/fixtures/vcr_cassettes/Rhea_Kubernetes_Commands_Redeploy/_perform/an_existing_rc/redeploys_the_rc.yml +424 -0
  35. data/fixtures/vcr_cassettes/Rhea_Kubernetes_Commands_Reschedule/_perform/an_existing_rc/reschedules_the_rc.yml +421 -0
  36. data/fixtures/vcr_cassettes/Rhea_Kubernetes_Commands_Scale/_perform/no_existing_rc/creates_an_rc.yml +165 -0
  37. data/lib/rhea.rb +15 -1
  38. data/lib/rhea/command.rb +35 -0
  39. data/lib/rhea/command_type.rb +35 -0
  40. data/lib/rhea/engine.rb +16 -0
  41. data/lib/rhea/kubernetes.rb +5 -0
  42. data/lib/rhea/kubernetes/api.rb +15 -0
  43. data/lib/rhea/kubernetes/commands/all.rb +16 -0
  44. data/lib/rhea/kubernetes/commands/base.rb +28 -0
  45. data/lib/rhea/kubernetes/commands/delete.rb +18 -0
  46. data/lib/rhea/kubernetes/commands/export.rb +23 -0
  47. data/lib/rhea/kubernetes/commands/get.rb +18 -0
  48. data/lib/rhea/kubernetes/commands/import.rb +20 -0
  49. data/lib/rhea/kubernetes/commands/redeploy.rb +22 -0
  50. data/lib/rhea/kubernetes/commands/reschedule.rb +21 -0
  51. data/lib/rhea/kubernetes/commands/scale.rb +108 -0
  52. data/lib/rhea/kubernetes/configuration.rb +30 -0
  53. data/lib/rhea/kubernetes/events/recent.rb +23 -0
  54. data/lib/rhea/kubernetes/nodes/all.rb +45 -0
  55. data/lib/rhea/kubernetes/system_services.rb +22 -0
  56. data/lib/rhea/version.rb +1 -1
  57. data/rhea.gemspec +11 -3
  58. data/spec/lib/rhea/kubernetes/commands/all_spec.rb +29 -0
  59. data/spec/lib/rhea/kubernetes/commands/delete_spec.rb +22 -0
  60. data/spec/lib/rhea/kubernetes/commands/export_spec.rb +31 -0
  61. data/spec/lib/rhea/kubernetes/commands/get_spec.rb +28 -0
  62. data/spec/lib/rhea/kubernetes/commands/import_spec.rb +39 -0
  63. data/spec/lib/rhea/kubernetes/commands/redeploy_spec.rb +35 -0
  64. data/spec/lib/rhea/kubernetes/commands/reschedule_spec.rb +32 -0
  65. data/spec/lib/rhea/kubernetes/commands/scale_spec.rb +31 -0
  66. data/spec/spec_helper.rb +2 -0
  67. data/spec/support/kubernetes_spec_helper.rb +43 -0
  68. data/spec/support/vcr.rb +9 -0
  69. metadata +170 -11
@@ -0,0 +1,21 @@
1
+ module Rhea
2
+ module Kubernetes
3
+ module Commands
4
+ class Reschedule < Base
5
+ attr_accessor :command
6
+
7
+ def initialize(command_attributes)
8
+ self.command = Command.new(command_attributes)
9
+ end
10
+
11
+ def perform
12
+ command_attributes = command.attributes.slice(:image, :expression)
13
+ controller = Get.new(command_attributes).perform
14
+ process_count = controller.process_count
15
+ Scale.new(command_attributes.merge(process_count: 0)).perform
16
+ Scale.new(command_attributes.merge(process_count: process_count)).perform
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,108 @@
1
+ module Rhea
2
+ module Kubernetes
3
+ module Commands
4
+ class Scale < Base
5
+ attr_accessor :command
6
+
7
+ def initialize(command_attributes)
8
+ self.command = Command.new(command_attributes)
9
+ end
10
+
11
+ def perform
12
+ if is_replication_controller_running?
13
+ scale_replication_controller
14
+ else
15
+ start_replication_controller
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def is_replication_controller_running?
22
+ api.get_replication_controllers(label_selector: "name=#{command.key}").length > 0
23
+ end
24
+
25
+ def start_replication_controller
26
+ parsed_command_expression = parse_command_expression
27
+ raw_command_expression = parsed_command_expression[:raw_command_expression]
28
+ env_vars = parsed_command_expression[:env_vars]
29
+ formatted_env_vars = format_env_vars(env_vars)
30
+
31
+ container_config = {
32
+ 'name' => command.key,
33
+ 'image' => command.image,
34
+ 'env' => formatted_env_vars,
35
+ 'command' => raw_command_expression.split(/\s+/)
36
+ }.merge(Rhea.configuration.container_options)
37
+
38
+ controller = Kubeclient::ReplicationController.new
39
+ controller.metadata = {
40
+ 'name' => command.key,
41
+ 'namespace' => NAMESPACE,
42
+ 'labels' => {
43
+ 'name' => command.key
44
+ },
45
+ 'annotations' => {
46
+ 'rhea_command' => command.expression
47
+ }
48
+ }
49
+ controller.spec = {
50
+ 'replicas' => command.process_count,
51
+ 'selector' => {
52
+ 'name' => command.key
53
+ },
54
+ 'template' => {
55
+ 'metadata' => {
56
+ 'labels' => {
57
+ 'name' => command.key
58
+ },
59
+ 'annotations' => {
60
+ 'rhea_command' => command.expression
61
+ }
62
+ },
63
+ 'spec' => {
64
+ 'containers' => [container_config]
65
+ }
66
+ }
67
+ }
68
+
69
+ api.create_replication_controller(controller)
70
+ end
71
+
72
+ def scale_replication_controller
73
+ controller = api.get_replication_controllers(label_selector: "name=#{command.key}").first
74
+ controller.spec.replicas = command.process_count
75
+ api.update_replication_controller(controller)
76
+ end
77
+
78
+ def parse_command_expression
79
+ match = command.expression.match(/((?:[A-Z]+=[^\s]+\s+)+)?(.*)/)
80
+ env_vars_string = match[1]
81
+ raw_command_expression = match[2]
82
+ env_vars = {}
83
+ if env_vars_string.present?
84
+ env_vars_strings = env_vars_string.strip.split(/\s+/)
85
+ env_vars_strings.each do |string|
86
+ name, value = string.split('=')
87
+ env_vars[name] = value
88
+ end
89
+ end
90
+ {
91
+ raw_command_expression: raw_command_expression,
92
+ env_vars: env_vars
93
+ }
94
+ end
95
+
96
+ def format_env_vars(env_vars)
97
+ hash = env_vars.merge(Rhea.configuration.env_vars)
98
+ hash.map do |name, value|
99
+ {
100
+ 'name' => name,
101
+ 'value' => value
102
+ }
103
+ end
104
+ end
105
+ end
106
+ end
107
+ end
108
+ end
@@ -0,0 +1,30 @@
1
+ module Rhea
2
+ class Configuration
3
+ attr_accessor :command_types, :container_options, :default_command_type_key, :default_image, :env_vars, :kube_api
4
+
5
+ def initialize
6
+ self.command_types = [
7
+ {
8
+ key: 'default',
9
+ name: 'Default',
10
+ format: '$INPUT'
11
+ },
12
+ {
13
+ key: 'resque',
14
+ name: 'Resque',
15
+ format: 'QUEUES=$INPUT rake resque:work'
16
+ },
17
+ {
18
+ key: 'sidekiq',
19
+ name: 'Sidekiq',
20
+ format: 'bundle exec sidekiq $INPUT'
21
+ }
22
+ ]
23
+ self.container_options = {}
24
+ self.default_command_type_key = 'default'
25
+ self.default_image = nil
26
+ self.env_vars = {}
27
+ self.kube_api = {}
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ module Rhea
2
+ module Kubernetes
3
+ module Events
4
+ class Recent
5
+ def perform
6
+ api = Rhea::Kubernetes::Api.new
7
+ events = api.get_events
8
+ events.map! do |event|
9
+ OpenStruct.new(
10
+ hostname: event.source.host,
11
+ message: event.message,
12
+ type: event.reason,
13
+ resource_type: event.involvedObject.kind,
14
+ resource_id: event.involvedObject.name,
15
+ created_at: Time.parse(event.lastTimestamp)
16
+ )
17
+ end
18
+ events.sort_by(&:created_at).reverse
19
+ end
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,45 @@
1
+ module Rhea
2
+ module Kubernetes
3
+ module Nodes
4
+ class All
5
+ def perform
6
+ api = Rhea::Kubernetes::Api.new
7
+ pods = api.get_pods
8
+ hostnames_nodes = {}
9
+ pods.each do |pod|
10
+ command_expression = pod.metadata.annotations.rhea_command
11
+ next if command_expression.nil?
12
+ hostname = pod.spec.nodeName
13
+ hostnames_nodes[hostname] ||= {}
14
+ hostnames_nodes[hostname][:image] = pod[:table][:spec][:containers][0]['image']
15
+
16
+ started_at = pod.status.startTime
17
+ if started_at
18
+ started_at = Time.parse(started_at)
19
+ last_started_at = hostnames_nodes[hostname][:last_started_at]
20
+ if last_started_at.nil? || started_at > last_started_at
21
+ hostnames_nodes[hostname][:last_started_at] = started_at
22
+ end
23
+ end
24
+
25
+ phase = pod.status.phase
26
+ containers = pod.spec.containers
27
+ containers.each do |container|
28
+ image = container.image
29
+ command = Command.new(
30
+ expression: command_expression,
31
+ image: image
32
+ )
33
+ hostnames_nodes[hostname][:commands_phases] ||= {}
34
+ hostnames_nodes[hostname][:commands_phases][command] ||= []
35
+ hostnames_nodes[hostname][:commands_phases][command] << phase
36
+ end
37
+ end
38
+ hostnames_nodes.map do |hostname, node|
39
+ OpenStruct.new(node.merge(hostname: hostname))
40
+ end
41
+ end
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,22 @@
1
+ module Rhea
2
+ module Kubernetes
3
+ module SystemServices
4
+ module_function
5
+
6
+ def service_names_urls
7
+ @service_names_urls ||= begin
8
+ api_url = Rhea.configuration.kube_api[:url]
9
+ root_url = api_url.sub('/api/', '/')
10
+ services_url = "#{api_url}v1/proxy/namespaces/kube-system/services/"
11
+ {
12
+ 'Kubernetes UI' => "#{services_url}kube-ui/",
13
+ 'Grafana' => "#{services_url}monitoring-grafana/",
14
+ 'Kibana' => "#{services_url}kibana-logging/",
15
+ 'Logs' => "#{services_url}logs/",
16
+ 'Swagger UI' => "#{root_url}swagger-ui/"
17
+ }
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -1,3 +1,3 @@
1
1
  module Rhea
2
- VERSION = '0.0.1'
2
+ VERSION = '0.1.0'
3
3
  end
@@ -3,8 +3,8 @@ require File.expand_path('../lib/rhea/version', __FILE__)
3
3
  Gem::Specification.new do |s|
4
4
  s.authors = ['Tom Benner']
5
5
  s.email = ['tombenner@gmail.com']
6
- s.description = s.summary = %q{}
7
- s.homepage = 'https://github.com/tombenner/rhea'
6
+ s.description = s.summary = %q{A web UI for managing a Kubernetes cluster}
7
+ s.homepage = 'https://github.com/entelo/rhea'
8
8
 
9
9
  s.files = `git ls-files`.split($\)
10
10
  s.name = 'rhea'
@@ -12,5 +12,13 @@ Gem::Specification.new do |s|
12
12
  s.version = Rhea::VERSION
13
13
  s.license = 'MIT'
14
14
 
15
- s.add_development_dependency 'rspec'
15
+ s.add_dependency 'kubeclient', '~> 0.3.0'
16
+ s.add_dependency 'haml', '~> 4.0'
17
+ s.add_dependency 'kaminari', '~> 0.16'
18
+ s.add_dependency 'sass-rails', '~> 5.0'
19
+
20
+ s.add_development_dependency 'rspec', '~> 3.0'
21
+ s.add_development_dependency 'rspec-rails', '~> 3.0'
22
+ s.add_development_dependency 'vcr', '~> 2.9'
23
+ s.add_development_dependency 'webmock', '~> 1.8'
16
24
  end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rhea::Kubernetes::Commands::All, :vcr do
4
+ include KubernetesSpecHelper
5
+
6
+ before(:each) { delete_replication_controllers }
7
+ after(:each) { delete_replication_controllers }
8
+
9
+ describe '#perform' do
10
+ context 'an existing rc' do
11
+ let(:process_count) { 1 }
12
+
13
+ before :each do
14
+ Rhea::Kubernetes::Commands::Scale.new(expression: command_expression, process_count: process_count).perform
15
+ end
16
+
17
+ it 'returns the rc' do
18
+ replication_controllers = described_class.new.perform
19
+ expected_attributes = {
20
+ expression: command_expression,
21
+ image: kube_image,
22
+ process_count: process_count
23
+ }
24
+ expect(replication_controllers.length).to eq(1)
25
+ expect(replication_controllers[0].attributes).to include(expected_attributes)
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rhea::Kubernetes::Commands::Delete, :vcr do
4
+ include KubernetesSpecHelper
5
+
6
+ before(:each) { delete_replication_controllers }
7
+ after(:each) { delete_replication_controllers }
8
+
9
+ describe '#perform' do
10
+ context 'an existing rc' do
11
+ before :each do
12
+ Rhea::Kubernetes::Commands::Scale.new(expression: command_expression, process_count: 1).perform
13
+ end
14
+
15
+ it 'deletes the rc' do
16
+ described_class.new(expression: command_expression).perform
17
+ url = "#{kube_authed_api_url}replicationcontrollers/#{kube_replication_controller_name}"
18
+ expect(WebMock).to have_requested(:delete, url)
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,31 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rhea::Kubernetes::Commands::Export, :vcr do
4
+ include KubernetesSpecHelper
5
+
6
+ before(:each) { delete_replication_controllers }
7
+ after(:each) { delete_replication_controllers }
8
+
9
+ describe '#perform' do
10
+ context 'an existing rc' do
11
+ let(:process_count) { 1 }
12
+
13
+ before :each do
14
+ Rhea::Kubernetes::Commands::Scale.new(expression: command_expression, process_count: process_count).perform
15
+ end
16
+
17
+ it 'returns the data' do
18
+ data = described_class.new.perform
19
+ expect(data[:version]).to eq(Rhea::VERSION)
20
+ expect(data[:created_at]).to be_a(Time)
21
+ expect(data[:commands]).to eq([
22
+ {
23
+ expression: command_expression,
24
+ image: kube_image,
25
+ process_count: process_count
26
+ }
27
+ ])
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,28 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rhea::Kubernetes::Commands::Get, :vcr do
4
+ include KubernetesSpecHelper
5
+
6
+ before(:each) { delete_replication_controllers }
7
+ after(:each) { delete_replication_controllers }
8
+
9
+ describe '#perform' do
10
+ context 'an existing rc' do
11
+ let(:process_count) { 1 }
12
+
13
+ before :each do
14
+ Rhea::Kubernetes::Commands::Scale.new(expression: command_expression, image: kube_image, process_count: process_count).perform
15
+ end
16
+
17
+ it 'gets the rc' do
18
+ replication_controller = described_class.new(expression: command_expression).perform
19
+ expected_attributes = {
20
+ expression: command_expression,
21
+ image: kube_image,
22
+ process_count: process_count
23
+ }
24
+ expect(replication_controller.attributes).to include(expected_attributes)
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,39 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rhea::Kubernetes::Commands::Import, :vcr do
4
+ include KubernetesSpecHelper
5
+
6
+ before(:each) { delete_replication_controllers }
7
+ after(:each) { delete_replication_controllers }
8
+
9
+ describe '#perform' do
10
+ context 'no existing rcs' do
11
+ let(:process_count) { 1 }
12
+ let(:data) do
13
+ {
14
+ version: Rhea::VERSION,
15
+ created_at: Time.now,
16
+ commands: [
17
+ {
18
+ expression: command_expression,
19
+ image: kube_image,
20
+ process_count: process_count
21
+ }.stringify_keys
22
+ ]
23
+ }.stringify_keys
24
+ end
25
+
26
+ it 'creates the rcs' do
27
+ described_class.new(data).perform
28
+ replication_controllers = Rhea::Kubernetes::Commands::All.new.perform
29
+ expected_attributes = {
30
+ expression: command_expression,
31
+ image: kube_image,
32
+ process_count: process_count
33
+ }
34
+ expect(replication_controllers.length).to eq(1)
35
+ expect(replication_controllers[0].attributes).to include(expected_attributes)
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe Rhea::Kubernetes::Commands::Redeploy, :vcr do
4
+ include KubernetesSpecHelper
5
+
6
+ before(:each) { delete_replication_controllers }
7
+ after(:each) { delete_replication_controllers }
8
+
9
+ describe '#perform' do
10
+ context 'an existing rc' do
11
+ let(:process_count) { 2 }
12
+
13
+ before :each do
14
+ Rhea::Kubernetes::Commands::Scale.new(expression: command_expression, process_count: process_count).perform
15
+ end
16
+
17
+ it 'redeploys the rc' do
18
+ described_class.new(expression: command_expression).perform
19
+ replication_controller = Rhea::Kubernetes::Commands::Get.new(expression: command_expression).perform
20
+
21
+ expected_attributes = {
22
+ expression: command_expression,
23
+ image: kube_image,
24
+ process_count: process_count
25
+ }
26
+ expect(replication_controller.attributes).to include(expected_attributes)
27
+
28
+ # One POST for the initial creation, and a second POST for the recreation
29
+ expect(WebMock).to have_requested(:post, "#{kube_authed_api_url}replicationcontrollers").twice
30
+ expect(WebMock).to have_requested(:delete, "#{kube_authed_api_url}replicationcontrollers/#{kube_replication_controller_name}")
31
+ expect(WebMock).to have_requested(:put, "#{kube_authed_api_url}replicationcontrollers/#{kube_replication_controller_name}")
32
+ end
33
+ end
34
+ end
35
+ end