dapp 0.13.8 → 0.13.9

Sign up to get free protection for your applications and to get access to all the features.
Files changed (33) hide show
  1. checksums.yaml +4 -4
  2. data/config/en/net_status.yml +4 -0
  3. data/lib/dapp.rb +16 -3
  4. data/lib/dapp/cli/command/update.rb +2 -0
  5. data/lib/dapp/dapp/dapp_config.rb +1 -1
  6. data/lib/dapp/deployment/kubernetes.rb +1 -1
  7. data/lib/dapp/dimg/build/stage/base.rb +1 -1
  8. data/lib/dapp/dimg/config/directive/docker/artifact.rb +5 -0
  9. data/lib/dapp/helper/yaml.rb +23 -0
  10. data/lib/dapp/kube/cli/command/kube.rb +3 -1
  11. data/lib/dapp/kube/cli/command/kube/chart_create.rb +19 -0
  12. data/lib/dapp/kube/cli/command/kube/secret_edit.rb +24 -0
  13. data/lib/dapp/kube/dapp/command/chart_create.rb +104 -0
  14. data/lib/dapp/kube/dapp/command/common.rb +21 -2
  15. data/lib/dapp/kube/dapp/command/deploy.rb +50 -25
  16. data/lib/dapp/kube/dapp/command/secret_edit.rb +45 -0
  17. data/lib/dapp/kube/dapp/command/secret_extract.rb +1 -2
  18. data/lib/dapp/kube/dapp/command/secret_generate.rb +1 -2
  19. data/lib/dapp/kube/dapp/command/secret_regenerate.rb +2 -3
  20. data/lib/dapp/kube/dapp/dapp.rb +4 -0
  21. data/lib/dapp/kube/kubernetes.rb +6 -0
  22. data/lib/dapp/kube/kubernetes/client.rb +255 -0
  23. data/lib/dapp/kube/{client → kubernetes/client}/error.rb +8 -4
  24. data/lib/dapp/kube/kubernetes/client/resource/base.rb +21 -0
  25. data/lib/dapp/kube/kubernetes/client/resource/job.rb +27 -0
  26. data/lib/dapp/kube/kubernetes/client/resource/pod.rb +49 -0
  27. data/lib/dapp/kube/kubernetes/manager/base.rb +15 -0
  28. data/lib/dapp/kube/kubernetes/manager/container.rb +88 -0
  29. data/lib/dapp/kube/kubernetes/manager/job.rb +65 -0
  30. data/lib/dapp/kube/kubernetes/manager/pod.rb +35 -0
  31. data/lib/dapp/version.rb +1 -1
  32. metadata +17 -4
  33. data/lib/dapp/kube/client.rb +0 -241
@@ -0,0 +1,15 @@
1
+ module Dapp
2
+ module Kube
3
+ module Kubernetes::Manager
4
+ class Base
5
+ attr_reader :dapp
6
+ attr_reader :name
7
+
8
+ def initialize(dapp, name)
9
+ @dapp = dapp
10
+ @name = name
11
+ end
12
+ end # Base
13
+ end # Kubernetes::Manager
14
+ end # Kube
15
+ end # Dapp
@@ -0,0 +1,88 @@
1
+ module Dapp
2
+ module Kube
3
+ module Kubernetes::Manager
4
+ class Container < Base
5
+ attr_reader :pod_manager
6
+
7
+ def initialize(dapp, name, pod_manager)
8
+ super(dapp, name)
9
+
10
+ @pod_manager = pod_manager
11
+ @processed_containers_ids = []
12
+ @processed_log_till_time = nil
13
+ @processed_log_timestamps = Set.new
14
+ end
15
+
16
+ def wait_till_created!
17
+ pod_manager.wait_till_running!
18
+
19
+ loop do
20
+ pod = Kubernetes::Client::Resource::Pod.new(dapp.kubernetes.pod(pod_manager.name))
21
+ break unless pod.container_state(name).first == 'waiting'
22
+ sleep 0.1
23
+ end
24
+ end
25
+
26
+ def watch_till_terminated!
27
+ pod = Kubernetes::Client::Resource::Pod.new(dapp.kubernetes.pod(pod_manager.name))
28
+ _, container_state_data = pod.container_state(name)
29
+ return if @processed_containers_ids.include? container_state_data['containerID']
30
+
31
+ wait_till_created!
32
+
33
+ pod = Kubernetes::Client::Resource::Pod.new(dapp.kubernetes.pod(pod_manager.name))
34
+ container_state, container_state_data = pod.container_state(name)
35
+
36
+ dapp.log_process("Watch pod's '#{pod_manager.name}' container '#{name}' log") do
37
+ loop do
38
+ pod = Kubernetes::Client::Resource::Pod.new(dapp.kubernetes.pod(pod_manager.name))
39
+ container_state, container_state_data = pod.container_state(name)
40
+
41
+ chunk_lines_by_time = dapp.kubernetes.pod_log(pod_manager.name, container: name, timestamps: true, sinceTime: @processed_log_till_time)
42
+ .lines
43
+ .map do |line|
44
+ timestamp, _, data = line.partition(' ')
45
+ [timestamp, data]
46
+ end
47
+ .reject {|timestamp, _| @processed_log_timestamps.include? timestamp}
48
+
49
+ chunk_lines_by_time.each do |timestamp, data|
50
+ puts data
51
+ @processed_log_timestamps.add timestamp
52
+ end
53
+
54
+ if container_state == 'terminated'
55
+ failed = (container_state_data['exitCode'].to_i != 0)
56
+
57
+ warn("".tap do |msg|
58
+ msg << "Pod's '#{pod_manager.name}' container '#{name}' has been terminated unsuccessfuly: "
59
+ msg << container_state_data.to_s
60
+ end) if failed
61
+
62
+ @processed_containers_ids << container_state_data['containerID']
63
+
64
+ break
65
+ end
66
+
67
+ @processed_log_till_time = chunk_lines_by_time.last.first if chunk_lines_by_time.any?
68
+
69
+ sleep 0.1 if chunk_lines_by_time.empty?
70
+ end
71
+ end # log_process
72
+ end
73
+
74
+ def done?
75
+ pod = Kubernetes::Client::Resource::Pod.new(dapp.kubernetes.pod(pod_manager.name))
76
+ container_state, container_state_data = pod.container_state(name)
77
+ if container_state == 'terminated'
78
+ failed = (container_state_data['exitCode'].to_i != 0)
79
+ return true if pod.restart_policy == 'Never'
80
+ return true if not failed and pod.restart_policy == 'OnFailure'
81
+ end
82
+
83
+ return false
84
+ end
85
+ end # Container
86
+ end # Kubernetes::Manager
87
+ end # Kube
88
+ end # Dapp
@@ -0,0 +1,65 @@
1
+ module Dapp
2
+ module Kube
3
+ module Kubernetes::Manager
4
+ class Job < Base
5
+ def initialize(dapp, name)
6
+ super(dapp, name)
7
+
8
+ @processed_pods_names = []
9
+ end
10
+
11
+ def wait_till_exists!
12
+ loop do
13
+ break if dapp.kubernetes.job?(name)
14
+ sleep 0.1
15
+ end
16
+ end
17
+
18
+ def watch_till_done!
19
+ wait_till_exists!
20
+
21
+ job = Kubernetes::Client::Resource::Job.new(dapp.kubernetes.job(name))
22
+
23
+ loop do
24
+ # Получить очередной pod для обработки
25
+ process_pod = dapp.kubernetes.pod_list.fetch('items', [])
26
+ .select do |pod_spec|
27
+ pod_spec.fetch('metadata', {}).fetch('labels', {})['controller-uid'] == job.uid
28
+ end
29
+ .reject do |pod_spec|
30
+ @processed_pods_names.include? pod_spec.fetch('metadata', {})['name']
31
+ end
32
+ .sort_by do |pod_spec|
33
+ Time.parse(pod_spec.fetch('metadata', {})['creationTimestamp'])
34
+ end
35
+ .map {|pod_spec| Kubernetes::Client::Resource::Pod.new(pod_spec)}
36
+ .first
37
+
38
+ if process_pod.nil?
39
+ job = Kubernetes::Client::Resource::Job.new(dapp.kubernetes.job(name))
40
+ if job.succeeded?
41
+ break
42
+ elsif job.failed?
43
+ warn "Job '#{name}' has been failed: #{job.spec['status']}"
44
+ break
45
+ end
46
+
47
+ sleep 0.1
48
+
49
+ next
50
+ end
51
+
52
+ pod_manager = Kubernetes::Manager::Pod.new(dapp, process_pod.name)
53
+ begin
54
+ pod_manager.watch_till_done!
55
+ rescue Kubernetes::Client::Error::Pod::NotFound => err
56
+ warn "Pod '#{err.net_status.fetch(:response_body, {}).fetch('details', {})['name']}' has been deleted"
57
+ ensure
58
+ @processed_pods_names << process_pod.name
59
+ end
60
+ end
61
+ end
62
+ end # Job
63
+ end # Kubernetes::Manager
64
+ end # Kube
65
+ end # Dapp
@@ -0,0 +1,35 @@
1
+ module Dapp
2
+ module Kube
3
+ module Kubernetes::Manager
4
+ class Pod < Base
5
+ def containers
6
+ pod = Kubernetes::Client::Resource::Pod.new(dapp.kubernetes.pod(name))
7
+ @containers ||= pod.containers_names.map do |container_name|
8
+ Container.new(dapp, container_name, self)
9
+ end
10
+ end
11
+
12
+ def wait_till_running!
13
+ loop do
14
+ pod = Kubernetes::Client::Resource::Pod.new(dapp.kubernetes.pod(name))
15
+ break if pod.phase == 'Running'
16
+ sleep 0.1
17
+ end
18
+ end
19
+
20
+ def watch_till_done!
21
+ process_queue = containers.map {|c| [c, nil]}
22
+ loop do
23
+ container, last_processed_at = process_queue.shift
24
+ break unless container
25
+
26
+ sleep 1 if last_processed_at and (Time.now - last_processed_at < 1)
27
+
28
+ container.watch_till_terminated!
29
+ process_queue.push([container, Time.now]) unless container.done?
30
+ end
31
+ end
32
+ end # Pod
33
+ end # Kubernetes::Manager
34
+ end # Kube
35
+ end # Dapp
data/lib/dapp/version.rb CHANGED
@@ -1,4 +1,4 @@
1
1
  module Dapp
2
- VERSION = '0.13.8'.freeze
2
+ VERSION = '0.13.9'.freeze
3
3
  BUILD_CACHE_VERSION = 15
4
4
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: dapp
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.13.8
4
+ version: 0.13.9
5
5
  platform: ruby
6
6
  authors:
7
7
  - Dmitry Stolyarov
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-07-20 00:00:00.000000000 Z
11
+ date: 2017-07-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mixlib-shellout
@@ -619,23 +619,26 @@ files:
619
619
  - lib/dapp/helper/sha256.rb
620
620
  - lib/dapp/helper/tar.rb
621
621
  - lib/dapp/helper/trivia.rb
622
+ - lib/dapp/helper/yaml.rb
622
623
  - lib/dapp/kube.rb
623
624
  - lib/dapp/kube/cli/cli.rb
624
625
  - lib/dapp/kube/cli/command/base.rb
625
626
  - lib/dapp/kube/cli/command/kube.rb
627
+ - lib/dapp/kube/cli/command/kube/chart_create.rb
626
628
  - lib/dapp/kube/cli/command/kube/deploy.rb
627
629
  - lib/dapp/kube/cli/command/kube/dismiss.rb
628
630
  - lib/dapp/kube/cli/command/kube/minikube_setup.rb
631
+ - lib/dapp/kube/cli/command/kube/secret_edit.rb
629
632
  - lib/dapp/kube/cli/command/kube/secret_extract.rb
630
633
  - lib/dapp/kube/cli/command/kube/secret_generate.rb
631
634
  - lib/dapp/kube/cli/command/kube/secret_key_generate.rb
632
635
  - lib/dapp/kube/cli/command/kube/secret_regenerate.rb
633
- - lib/dapp/kube/client.rb
634
- - lib/dapp/kube/client/error.rb
636
+ - lib/dapp/kube/dapp/command/chart_create.rb
635
637
  - lib/dapp/kube/dapp/command/common.rb
636
638
  - lib/dapp/kube/dapp/command/deploy.rb
637
639
  - lib/dapp/kube/dapp/command/dismiss.rb
638
640
  - lib/dapp/kube/dapp/command/minikube_setup.rb
641
+ - lib/dapp/kube/dapp/command/secret_edit.rb
639
642
  - lib/dapp/kube/dapp/command/secret_extract.rb
640
643
  - lib/dapp/kube/dapp/command/secret_generate.rb
641
644
  - lib/dapp/kube/dapp/command/secret_key_generate.rb
@@ -644,6 +647,16 @@ files:
644
647
  - lib/dapp/kube/error/base.rb
645
648
  - lib/dapp/kube/error/command.rb
646
649
  - lib/dapp/kube/error/kubernetes.rb
650
+ - lib/dapp/kube/kubernetes.rb
651
+ - lib/dapp/kube/kubernetes/client.rb
652
+ - lib/dapp/kube/kubernetes/client/error.rb
653
+ - lib/dapp/kube/kubernetes/client/resource/base.rb
654
+ - lib/dapp/kube/kubernetes/client/resource/job.rb
655
+ - lib/dapp/kube/kubernetes/client/resource/pod.rb
656
+ - lib/dapp/kube/kubernetes/manager/base.rb
657
+ - lib/dapp/kube/kubernetes/manager/container.rb
658
+ - lib/dapp/kube/kubernetes/manager/job.rb
659
+ - lib/dapp/kube/kubernetes/manager/pod.rb
647
660
  - lib/dapp/kube/secret.rb
648
661
  - lib/dapp/prctl.rb
649
662
  - lib/dapp/version.rb
@@ -1,241 +0,0 @@
1
- module Dapp
2
- module Kube
3
- class Client
4
- def initialize(namespace: nil)
5
- @namespace = namespace
6
- @query_parameters = {}
7
- end
8
-
9
- def namespace
10
- @namespace || kube_context_config['context']['namespace'] || 'default'
11
- end
12
-
13
- # Чтобы не перегружать методы явной передачей namespace.
14
- # Данный метод может пригодиться только в ситуации, когда надо указать другой namespace,
15
- # в большинстве случаев используется namespace из конструктора.
16
- def with_namespace(namespace, &blk)
17
- old_namespace = @namespace
18
- begin
19
- @namespace = namespace
20
- return yield
21
- ensure
22
- @namespace = old_namespace
23
- end
24
- end
25
-
26
- def with_query(query, &blk)
27
- old_query = @query_parameters
28
- begin
29
- @query_parameters = query
30
- return yield
31
- ensure
32
- @query_parameters = old_query
33
- end
34
- end
35
-
36
- # NOTICE: Название метода аналогично kind'у выдаваемого результата.
37
- # NOTICE: В данном случае в результате kind=DeploymentList.
38
- # NOTICE: Методы создания/обновления/удаления сущностей kubernetes заканчиваются на '!'. Например, create_deployment!.
39
-
40
- {
41
- '/api/v1' => [:service, :replicationcontroller, :pod],
42
- '/apis/extensions/v1beta1' => [:deployment, :replicaset],
43
- '/apis/batch/v1' => [:job]
44
- }.each do |api, objects|
45
- objects.each do |object|
46
- define_method :"#{object}_list" do |**query_parameters|
47
- request!(:get, "#{api}/namespaces/#{namespace}/#{object}s", **query_parameters)
48
- end
49
-
50
- define_method object do |name, **query_parameters|
51
- request!(:get, "#{api}/namespaces/#{namespace}/#{object}s/#{name}", **query_parameters)
52
- end
53
-
54
- define_method "#{object}_status" do |name, **query_parameters|
55
- request!(:get, "#{api}/namespaces/#{namespace}/#{object}s/#{name}/status", **query_parameters)
56
- end
57
-
58
- define_method :"create_#{object}!" do |spec, **query_parameters|
59
- request!(:post, "#{api}/namespaces/#{namespace}/#{object}s", body: spec, **query_parameters)
60
- end
61
-
62
- define_method :"replace_#{object}!" do |name, spec, **query_parameters|
63
- request!(:put, "#{api}/namespaces/#{namespace}/#{object}s/#{name}", body: spec, **query_parameters)
64
- end
65
-
66
- define_method :"delete_#{object}!" do |name, **query_parameters|
67
- request!(:delete, "#{api}/namespaces/#{namespace}/#{object}s/#{name}", **query_parameters)
68
- end
69
-
70
- define_method :"delete_#{object}s!" do |**query_parameters|
71
- request!(:delete, "#{api}/namespaces/#{namespace}/#{object}s", **query_parameters)
72
- end
73
-
74
- define_method :"#{object}?" do |name, **query_parameters|
75
- public_send(:"#{object}_list", **query_parameters)['items'].map { |item| item['metadata']['name'] }.include?(name)
76
- end
77
- end
78
- end
79
-
80
- def namespace_list(**query_parameters)
81
- request!(:get, '/api/v1/namespaces', **query_parameters)
82
- end
83
-
84
- def namespace?(name, **query_parameters)
85
- namespace_list(**query_parameters)['items'].map { |item| item['metadata']['name'] }.include?(name)
86
- end
87
-
88
- def create_namespace!(name, **query_parameters)
89
- request!(:post, '/api/v1/namespaces', body: { metadata: { name: name } }, **query_parameters)
90
- end
91
-
92
- def delete_namespace!(name, **query_parameters)
93
- request!(:delete, "/api/v1/namespaces/#{name}", **query_parameters)
94
- end
95
-
96
- def pod_log(name, follow: false, **query_parameters, &blk)
97
- excon_parameters = follow ? { response_block: blk } : {}
98
- request!(:get,
99
- "/api/v1/namespaces/#{namespace}/pods/#{name}/log",
100
- excon_parameters: excon_parameters,
101
- **{ follow: follow }.merge(query_parameters))
102
- rescue Excon::Error::Timeout
103
- raise Error::Timeout
104
- end
105
-
106
- def event_list(**query_parameters)
107
- request!(:get, "/api/v1/namespaces/#{namespace}/events", **query_parameters)
108
- end
109
-
110
- protected
111
-
112
- # query_parameters — соответствует 'Query Parameters' в документации kubernetes
113
- # excon_parameters — соответствует опциям Excon
114
- # body — hash для http-body, соответствует 'Body Parameters' в документации kubernetes, опционален
115
- def request!(method, path, body: nil, excon_parameters: {}, **query_parameters)
116
- with_connection(excon_parameters: excon_parameters) do |conn|
117
- request_parameters = {method: method, path: path, query: @query_parameters.merge(query_parameters)}
118
- request_parameters[:body] = JSON.dump(body) if body
119
- load_body! conn.request(request_parameters), request_parameters
120
- end
121
- end
122
-
123
- def load_body!(response, request_parameters)
124
- response_ok = response.status.to_s.start_with? '2'
125
-
126
- if response_ok
127
- JSON.parse(response.body)
128
- else
129
- err_data = {}
130
- err_data[:response_http_status] = response.status
131
- if response_body = (JSON.parse(response.body) rescue nil)
132
- err_data[:response_body] = response_body
133
- else
134
- err_data[:response_raw_body] = response.body
135
- end
136
- err_data[:request_parameters] = request_parameters
137
-
138
- if response.status.to_s.start_with? '5'
139
- raise Error::Base, code: :server_error, data: err_data
140
- elsif response.status.to_s == '404'
141
- raise Error::NotFound, data: err_data
142
- else not response.status.to_s.start_with? '2'
143
- raise Error::Base, code: :bad_request, data: err_data
144
- end
145
- end
146
- end
147
-
148
- def with_connection(excon_parameters: {}, &blk)
149
- old_ssl_ca_file = Excon.defaults[:ssl_ca_file]
150
- old_ssl_cert_store = Excon.defaults[:ssl_cert_store]
151
- old_middlewares = Excon.defaults[:middlewares].dup
152
-
153
- begin
154
- ssl_cert_store = OpenSSL::X509::Store.new
155
- if ssl_ca_file = kube_config.fetch('clusters', [{}]).first.fetch('cluster', {}).fetch('certificate-authority', nil)
156
- ssl_cert_store.add_file ssl_ca_file
157
- elsif ssl_ca_data = kube_config.fetch('clusters', [{}]).first.fetch('cluster', {}).fetch('certificate-authority-data', nil)
158
- ssl_cert_store.add_cert OpenSSL::X509::Certificate.new(Base64.decode64(ssl_ca_data))
159
- end
160
-
161
- Excon.defaults[:ssl_ca_file] = nil
162
- Excon.defaults[:ssl_cert_store] = ssl_cert_store
163
- Excon.defaults[:middlewares] << Excon::Middleware::RedirectFollower
164
-
165
- connection = begin
166
- Excon.new(kube_cluster_config['cluster']['server'], **kube_server_options(excon_parameters)).tap(&:get)
167
- rescue Excon::Error::Socket => err
168
- raise Error::ConnectionRefused,
169
- code: :kube_server_connection_refused,
170
- data: { kube_cluster_config: kube_cluster_config, kube_user_config: kube_user_config, error: err.message }
171
- end
172
-
173
- return yield connection
174
- ensure
175
- Excon.defaults[:ssl_ca_file] = old_ssl_ca_file
176
- Excon.defaults[:ssl_cert_store] = old_ssl_cert_store
177
- Excon.defaults[:middlewares] = old_middlewares
178
- end
179
- end
180
-
181
- def kube_server_options(excon_parameters = {})
182
- {}.tap do |opts|
183
- client_cert = kube_config.fetch('users', [{}]).first.fetch('user', {}).fetch('client-certificate', nil)
184
- opts[:client_cert] = client_cert if client_cert
185
-
186
- client_cert_data = kube_config.fetch('users', [{}]).first.fetch('user', {}).fetch('client-certificate-data', nil)
187
- opts[:client_cert_data] = Base64.decode64(client_cert_data) if client_cert_data
188
-
189
- client_key = kube_config.fetch('users', [{}]).first.fetch('user', {}).fetch('client-key', nil)
190
- opts[:client_key] = client_key if client_key
191
-
192
- client_key_data = kube_config.fetch('users', [{}]).first.fetch('user', {}).fetch('client-key-data', nil)
193
- opts[:client_key_data] = Base64.decode64(client_key_data) if client_key_data
194
- end
195
- end
196
-
197
- def kube_user_config
198
- @kube_user_config ||= begin
199
- kube_config.fetch('users', []).find {|user| user['name'] == kube_context_config['context']['user']} || begin
200
- raise Error::BadConfig, code: :kube_user_not_found, data: {context: kube_context_config}
201
- end
202
- end
203
- end
204
-
205
- def kube_cluster_config
206
- @kube_cluster_config ||= begin
207
- kube_config.fetch('clusters', []).find {|cluster| cluster['name'] == kube_context_config['context']['cluster']} || begin
208
- raise Error::BadConfig, code: :kube_cluster_not_found, data: {context: kube_context_config}
209
- end
210
- end
211
- end
212
-
213
- def kube_context_config
214
- @kube_context_config ||= begin
215
- kube_config.fetch('contexts', []).find {|context| context['name'] == kube_context_name} || begin
216
- raise Error::BadConfig, code: :kube_context_not_found, data: {context_name: kube_context_name}
217
- end
218
- end
219
- end
220
-
221
- def kube_context_name
222
- @kube_context_name ||= kube_config['current-context'] || begin
223
- if context = kube_config.fetch('contexts', []).first
224
- warn "[WARN] .kube/config current-context is not set, using context '#{context['name']}'"
225
- context['name']
226
- end
227
- end
228
- end
229
-
230
- def kube_config
231
- @kube_config ||= begin
232
- if File.exist?((kube_config_path = File.join(ENV['HOME'], '.kube/config')))
233
- YAML.load_file(kube_config_path)
234
- else
235
- raise Error::Base, code: :kube_config_not_found, data: { path: kube_config_path }
236
- end
237
- end
238
- end
239
- end
240
- end
241
- end