dapp 0.13.8 → 0.13.9

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 (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