kuber_kit 0.5.0 → 0.5.5

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 (35) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -0
  3. data/Gemfile.lock +3 -3
  4. data/TODO.md +5 -5
  5. data/example/configurations/review.rb +1 -1
  6. data/example/services/compose_app.rb +1 -1
  7. data/example/services/env_file.rb +1 -1
  8. data/example/services/ruby_app.rb +2 -1
  9. data/lib/kuber_kit.rb +6 -0
  10. data/lib/kuber_kit/actions/kubectl_attacher.rb +2 -2
  11. data/lib/kuber_kit/actions/kubectl_console.rb +2 -2
  12. data/lib/kuber_kit/actions/kubectl_describe.rb +2 -2
  13. data/lib/kuber_kit/actions/kubectl_get.rb +32 -0
  14. data/lib/kuber_kit/actions/kubectl_logs.rb +2 -2
  15. data/lib/kuber_kit/actions/service_checker.rb +7 -2
  16. data/lib/kuber_kit/actions/service_deployer.rb +85 -61
  17. data/lib/kuber_kit/cli.rb +14 -3
  18. data/lib/kuber_kit/configs.rb +2 -1
  19. data/lib/kuber_kit/container.rb +8 -0
  20. data/lib/kuber_kit/core/configuration.rb +4 -2
  21. data/lib/kuber_kit/core/configuration_definition.rb +7 -0
  22. data/lib/kuber_kit/core/configuration_factory.rb +1 -0
  23. data/lib/kuber_kit/core/dependencies/abstract_dependency_resolver.rb +75 -0
  24. data/lib/kuber_kit/core/image.rb +2 -1
  25. data/lib/kuber_kit/core/service.rb +4 -2
  26. data/lib/kuber_kit/core/service_definition.rb +13 -6
  27. data/lib/kuber_kit/core/service_factory.rb +1 -0
  28. data/lib/kuber_kit/image_compiler/image_dependency_resolver.rb +5 -53
  29. data/lib/kuber_kit/service_deployer/service_dependency_resolver.rb +14 -0
  30. data/lib/kuber_kit/service_deployer/service_list_resolver.rb +11 -6
  31. data/lib/kuber_kit/service_deployer/strategies/kubernetes.rb +9 -2
  32. data/lib/kuber_kit/shell/commands/kubectl_commands.rb +8 -0
  33. data/lib/kuber_kit/shell/local_shell.rb +15 -1
  34. data/lib/kuber_kit/version.rb +1 -1
  35. metadata +6 -2
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: da99b8b516e2fa2eb9090dc78084bc4b3d40f6d5856e4d3ecbbb993b1cbdef5d
4
- data.tar.gz: 73c76c08809d116b0c5f317959b45e5fb513b518592f791d8a0d32a185a5d88c
3
+ metadata.gz: 87f41f4c189d1bb0420bc1db160dbd53c1b2769541b0ad0e772a9b74f20cd3ed
4
+ data.tar.gz: d2b92edffabbe62b11801d09bd5f81cfd7a27be52aa0cea04ade796bc311f370
5
5
  SHA512:
6
- metadata.gz: 5953d83da7b07ebe1ee9be3ee803c6662c2a585bbc4b01a66ba9eb3232df86f11c6f9991e3e6ffd992828d25b5b9fc554bdb71ecd396026941b78f23448acb80
7
- data.tar.gz: a36e8746b7caddea4fe4342df5be1bd7c5304856a613e42b805b119013b9f31cc40c5de9be8587b2f13850f8bc9aa1e1e0179f88904d9ec1b0d3617285261a9b
6
+ metadata.gz: 0fcbdb08cf391d2ff480a7490261d7508f6e7d970b09091893fa4f6eb7338661df4f42349031303f16db404b74f3ca1f7fe9ec4f699f1c9052358cb661a2e4f8
7
+ data.tar.gz: 922e179ff88f29dcb15f64a66a4e6fb43c7ee63b5a44be44a86ba401b64685997af1bf370281be36e332e729347e27e2f8f6d8f8f6a5b648bd3a2f6205c079d6
data/CHANGELOG.md ADDED
@@ -0,0 +1,15 @@
1
+ **0.5.5**
2
+ - Added ability to skip services during deployment using -S option
3
+
4
+ **0.5.4**
5
+ - Added disabled services support
6
+
7
+ **0.5.3**
8
+ - Change the symbol to exclude service from "-" to "!", you can pass "-s !auth_app" to exclude "auth_app"
9
+ - Added kit get command to find pods
10
+
11
+ **0.5.2**
12
+ - Added dependencies support to services
13
+ - Added an option to deploy all services in `kit deloy`
14
+ - Wait for rollout to finish during service deploy
15
+ - Deploy services in batches, not all of the simultaneously
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- kuber_kit (0.5.0)
4
+ kuber_kit (0.5.5)
5
5
  cli-ui
6
6
  contracts-lite
7
7
  dry-auto_inject
@@ -12,7 +12,7 @@ PATH
12
12
  GEM
13
13
  remote: https://rubygems.org/
14
14
  specs:
15
- cli-ui (1.4.0)
15
+ cli-ui (1.5.1)
16
16
  coderay (1.1.3)
17
17
  concurrent-ruby (1.1.8)
18
18
  contracts-lite (0.15.0)
@@ -58,7 +58,7 @@ GEM
58
58
  thor (1.1.0)
59
59
  tty-color (0.6.0)
60
60
  tty-cursor (0.7.1)
61
- tty-prompt (0.23.0)
61
+ tty-prompt (0.23.1)
62
62
  pastel (~> 0.8)
63
63
  tty-reader (~> 0.8)
64
64
  tty-reader (0.9.0)
data/TODO.md CHANGED
@@ -1,8 +1,8 @@
1
- - do not show result for images list, if list is too large (Mikhail)
2
- - add kit get method for interactive kubernetes
3
- - kit status should show the list of services and their status, with ability to select & view logs
4
- - find a way to always deploy some service, e.g. for migrations and env_files
1
+ - add kit get method for more interactive kubernetes
2
+ - env files should use a separate deployment method (with change detection)
3
+ - add automatical confirmation support for service deployer
5
4
  - template should be able to set default attributes
6
5
  - template should be able to depend on image?
7
6
  - cleanup image builds older than some date
8
- - add some rotation for deploy log
7
+ - add some rotation for deploy log
8
+ - kit status should show the list of services and their status, with ability to select & view logs
@@ -2,5 +2,5 @@ KuberKit
2
2
  .define_configuration(:review)
3
3
  .use_registry(:review_default, as: :default)
4
4
  .use_artifact(:kuber_kit_repo, as: :kuber_kit_repo)
5
- #.deployer_require_confirimation
5
+ .deployer_require_confirimation
6
6
  #.use_build_server(:remote_bs)
@@ -1,5 +1,5 @@
1
1
  KuberKit
2
- .define_service(:auth_app)
2
+ .define_service(:compose_app)
3
3
  .template(:docker_compose)
4
4
  .images(:ruby_app)
5
5
  .tags(:compose)
@@ -1,7 +1,7 @@
1
1
  KuberKit
2
2
  .define_service(:env_file)
3
3
  .template(:env_file)
4
- .tags("env_file")
4
+ .tags("env_file", "minimal")
5
5
  .attributes(
6
6
  deployer_restart_enabled: false
7
7
  )
@@ -1,5 +1,6 @@
1
1
  KuberKit
2
2
  .define_service(:ruby_app)
3
+ .depends_on(:env_file)
3
4
  .template(:service)
4
5
  .images(:ruby_app)
5
- .tags("app")
6
+ .tags("app", "minimal")
data/lib/kuber_kit.rb CHANGED
@@ -46,6 +46,10 @@ module KuberKit
46
46
  autoload :BuildServer, 'core/build_servers/build_server'
47
47
  end
48
48
 
49
+ module Dependencies
50
+ autoload :AbstractDependencyResolver, 'core/dependencies/abstract_dependency_resolver'
51
+ end
52
+
49
53
  module EnvFiles
50
54
  autoload :EnvFileStore, 'core/env_files/env_file_store'
51
55
  autoload :AbstractEnvFile, 'core/env_files/abstract_env_file'
@@ -145,6 +149,7 @@ module KuberKit
145
149
  autoload :StrategyDetector, 'service_deployer/strategy_detector'
146
150
  autoload :Deployer, 'service_deployer/deployer'
147
151
  autoload :ServiceListResolver, 'service_deployer/service_list_resolver'
152
+ autoload :ServiceDependencyResolver, 'service_deployer/service_dependency_resolver'
148
153
 
149
154
  module Strategies
150
155
  autoload :Abstract, 'service_deployer/strategies/abstract'
@@ -171,6 +176,7 @@ module KuberKit
171
176
  autoload :KubectlAttacher, 'actions/kubectl_attacher'
172
177
  autoload :KubectlConsole, 'actions/kubectl_console'
173
178
  autoload :KubectlDescribe, 'actions/kubectl_describe'
179
+ autoload :KubectlGet, 'actions/kubectl_get'
174
180
  autoload :KubectlLogs, 'actions/kubectl_logs'
175
181
  autoload :KubectlEnv, 'actions/kubectl_env'
176
182
  end
@@ -2,7 +2,7 @@ class KuberKit::Actions::KubectlAttacher
2
2
  include KuberKit::Import[
3
3
  "shell.kubectl_commands",
4
4
  "shell.local_shell",
5
- "kubernetes.resources_fetcher",
5
+ "kubernetes.resource_selector",
6
6
  "ui"
7
7
  ]
8
8
 
@@ -12,7 +12,7 @@ class KuberKit::Actions::KubectlAttacher
12
12
  deployer_namespace = KuberKit.current_configuration.deployer_namespace
13
13
 
14
14
  if !pod_name
15
- pod_name = resources_fetcher.call("attach")
15
+ pod_name = resource_selector.call("attach")
16
16
  end
17
17
 
18
18
  kubectl_commands.exec(
@@ -2,7 +2,7 @@ class KuberKit::Actions::KubectlConsole
2
2
  include KuberKit::Import[
3
3
  "shell.kubectl_commands",
4
4
  "shell.local_shell",
5
- "kubernetes.resources_fetcher",
5
+ "kubernetes.resource_selector",
6
6
  "ui"
7
7
  ]
8
8
 
@@ -12,7 +12,7 @@ class KuberKit::Actions::KubectlConsole
12
12
  deployer_namespace = KuberKit.current_configuration.deployer_namespace
13
13
 
14
14
  if !pod_name
15
- pod_name = resources_fetcher.call("attach")
15
+ pod_name = resource_selector.call("attach")
16
16
  end
17
17
 
18
18
  kubectl_commands.exec(
@@ -2,7 +2,7 @@ class KuberKit::Actions::KubectlDescribe
2
2
  include KuberKit::Import[
3
3
  "shell.kubectl_commands",
4
4
  "shell.local_shell",
5
- "kubernetes.resources_fetcher",
5
+ "kubernetes.resource_selector",
6
6
  "ui"
7
7
  ]
8
8
 
@@ -12,7 +12,7 @@ class KuberKit::Actions::KubectlDescribe
12
12
  deployer_namespace = KuberKit.current_configuration.deployer_namespace
13
13
 
14
14
  if !resource_name
15
- resource_name = resources_fetcher.call("describe", include_ingresses: true, include_pods: true)
15
+ resource_name = resource_selector.call("describe", include_ingresses: true, include_pods: true)
16
16
  end
17
17
 
18
18
  args = nil
@@ -0,0 +1,32 @@
1
+ class KuberKit::Actions::KubectlGet
2
+ include KuberKit::Import[
3
+ "shell.kubectl_commands",
4
+ "shell.local_shell",
5
+ "kubernetes.resource_selector",
6
+ "ui"
7
+ ]
8
+
9
+ Contract Maybe[String], Hash => Any
10
+ def call(resource_name, options)
11
+ kubeconfig_path = KuberKit.current_configuration.kubeconfig_path
12
+ deployer_namespace = KuberKit.current_configuration.deployer_namespace
13
+
14
+ resources = kubectl_commands.get_resources(
15
+ local_shell, "pod",
16
+ kubeconfig_path: kubeconfig_path,
17
+ namespace: deployer_namespace
18
+ )
19
+
20
+ if resource_name
21
+ resources = resources.select{|r| r.include?(resource_name) }
22
+ end
23
+
24
+ ui.print_info("Pods", resources.join("\n"))
25
+
26
+ true
27
+ rescue KuberKit::Error => e
28
+ ui.print_error("Error", e.message)
29
+
30
+ false
31
+ end
32
+ end
@@ -2,7 +2,7 @@ class KuberKit::Actions::KubectlLogs
2
2
  include KuberKit::Import[
3
3
  "shell.kubectl_commands",
4
4
  "shell.local_shell",
5
- "kubernetes.resources_fetcher",
5
+ "kubernetes.resource_selector",
6
6
  "ui"
7
7
  ]
8
8
 
@@ -12,7 +12,7 @@ class KuberKit::Actions::KubectlLogs
12
12
  deployer_namespace = KuberKit.current_configuration.deployer_namespace
13
13
 
14
14
  if !pod_name
15
- pod_name = resources_fetcher.call("attach")
15
+ pod_name = resource_selector.call("attach")
16
16
  end
17
17
 
18
18
  args = nil
@@ -15,9 +15,14 @@ class KuberKit::Actions::ServiceChecker
15
15
  services = services.select{ |s| enabled_services.include?(s) }
16
16
  end
17
17
 
18
- deployments = resources_fetcher.call("deployments")
18
+ disabled_services = KuberKit.current_configuration.disabled_services.map(&:to_s)
19
+ if disabled_services.any?
20
+ services = services.select{ |s| !disabled_services.include?(s) }
21
+ end
22
+
23
+ resources = resources_fetcher.call("deployments") + resources_fetcher.call("cronjobs")
19
24
 
20
- missing_services = services.select{ |s| !deployments.include?(s.gsub("_", "-")) }
25
+ missing_services = services.select{ |s| !resources.include?(s.gsub("_", "-")) }
21
26
 
22
27
  ui.print_warning("Warning", "This command will only check services deployed using k8s")
23
28
 
@@ -2,9 +2,11 @@ class KuberKit::Actions::ServiceDeployer
2
2
  include KuberKit::Import[
3
3
  "actions.image_compiler",
4
4
  "service_deployer.service_list_resolver",
5
+ "service_deployer.service_dependency_resolver",
5
6
  "core.service_store",
6
7
  "shell.local_shell",
7
8
  "tools.process_cleaner",
9
+ "configs",
8
10
  "ui",
9
11
  service_deployer: "service_deployer.action_handler",
10
12
  ]
@@ -12,26 +14,37 @@ class KuberKit::Actions::ServiceDeployer
12
14
  Contract KeywordArgs[
13
15
  services: Maybe[ArrayOf[String]],
14
16
  tags: Maybe[ArrayOf[String]],
17
+ skip_services: Maybe[ArrayOf[String]],
15
18
  skip_compile: Maybe[Bool],
16
19
  require_confirmation: Maybe[Bool],
17
20
  ] => Any
18
- def call(services:, tags:, skip_compile: false, require_confirmation: false)
21
+ def call(services:, tags:, skip_services: nil, skip_compile: false, require_confirmation: false)
22
+ current_configuration = KuberKit.current_configuration
23
+
19
24
  if services.empty? && tags.empty?
20
25
  services, tags = show_tags_selection
21
26
  end
22
27
 
28
+
29
+ disabled_services = current_configuration.disabled_services.map(&:to_s)
30
+ disabled_services += skip_services if skip_services
31
+
23
32
  service_names = service_list_resolver.resolve(
24
33
  services: services || [],
25
34
  tags: tags || [],
26
- enabled_services: KuberKit.current_configuration.enabled_services.map(&:to_s)
27
- )
35
+ enabled_services: current_configuration.enabled_services.map(&:to_s),
36
+ disabled_services: disabled_services
37
+ ).map(&:to_sym)
38
+
39
+ # Return the list of services with all dependencies.
40
+ all_service_names = service_dependency_resolver.get_all(service_names)
28
41
 
29
- unless service_names.any?
42
+ unless all_service_names.any?
30
43
  ui.print_warning "ServiceDeployer", "No service found with given options, nothing will be deployed."
31
44
  return false
32
45
  end
33
46
 
34
- services_list = service_names.map(&:to_s).map(&:yellow).join(", ")
47
+ services_list = all_service_names.map(&:to_s).map(&:yellow).join(", ")
35
48
  ui.print_info "ServiceDeployer", "The following services will be deployed: #{services_list}"
36
49
 
37
50
  if require_confirmation
@@ -39,7 +52,7 @@ class KuberKit::Actions::ServiceDeployer
39
52
  return false unless ["confirm".green, "confirm", "yes"].include?(result)
40
53
  end
41
54
 
42
- services = service_names.map do |service_name|
55
+ services = all_service_names.map do |service_name|
43
56
  service_store.get_service(service_name.to_sym)
44
57
  end
45
58
 
@@ -50,9 +63,16 @@ class KuberKit::Actions::ServiceDeployer
50
63
  return false unless compile_result
51
64
  end
52
65
 
53
- deployment_result = deploy_services(service_names)
66
+ deployed_services = []
67
+ deployment_result = {}
68
+ service_dependency_resolver.each_with_deps(service_names) do |dep_service_names|
69
+ ui.print_debug("ServiceDeployer", "Scheduling to compile: #{dep_service_names.inspect}. Limit: #{configs.deploy_simultaneous_limit}")
70
+ result = deploy_simultaneously(dep_service_names)
71
+ deployed_services += dep_service_names
72
+ deployment_result = deployment_result.merge(result)
73
+ end
54
74
 
55
- { services: service_names, deployment: deployment_result }
75
+ { services: all_service_names, deployment: deployment_result }
56
76
  rescue KuberKit::Error => e
57
77
  ui.print_error("Error", e.message)
58
78
 
@@ -61,69 +81,73 @@ class KuberKit::Actions::ServiceDeployer
61
81
  process_cleaner.clean
62
82
  end
63
83
 
64
- def deploy_services(service_names)
65
- task_group = ui.create_task_group
84
+ private
85
+ def deploy_simultaneously(service_names)
86
+ task_group = ui.create_task_group
66
87
 
67
- deployer_result = {}
88
+ deployer_result = {}
68
89
 
69
- service_names.each do |service_name|
90
+ service_names.each do |service_name|
70
91
 
71
- ui.print_debug("ServiceDeployer", "Started deploying: #{service_name.to_s.green}")
72
- task_group.add("Deploying #{service_name.to_s.yellow}") do |task|
73
- deployer_result[service_name] = service_deployer.call(local_shell, service_name.to_sym)
92
+ ui.print_debug("ServiceDeployer", "Started deploying: #{service_name.to_s.green}")
93
+ task_group.add("Deploying #{service_name.to_s.yellow}") do |task|
94
+ deployer_result[service_name] = service_deployer.call(local_shell, service_name.to_sym)
74
95
 
75
- task.update_title("Deployed #{service_name.to_s.green}")
76
- ui.print_debug("ServiceDeployer", "Finished deploying: #{service_name.to_s.green}")
96
+ task.update_title("Deployed #{service_name.to_s.green}")
97
+ ui.print_debug("ServiceDeployer", "Finished deploying: #{service_name.to_s.green}")
98
+ end
77
99
  end
78
- end
79
100
 
80
- task_group.wait
81
-
82
- deployer_result
83
- end
101
+ task_group.wait
84
102
 
85
- def compile_images(images_names)
86
- return true if images_names.empty?
87
- image_compiler.call(images_names, {})
88
- end
103
+ deployer_result
104
+ end
89
105
 
90
- def show_tags_selection()
91
- specific_service_option = "deploy specific service"
92
-
93
- tags = [specific_service_option]
94
- tags += service_store
95
- .all_definitions
96
- .values
97
- .map(&:to_service_attrs)
98
- .map(&:tags)
99
- .flatten
100
- .uniq
101
- .sort
102
- .map(&:to_s)
103
-
104
- selected_tag = ui.prompt("Please select which tag to deploy", tags)
105
-
106
- if selected_tag == specific_service_option
107
- show_service_selection
108
- else
109
- [[], [selected_tag]]
106
+ def compile_images(images_names)
107
+ return true if images_names.empty?
108
+ image_compiler.call(images_names, {})
110
109
  end
111
- end
112
110
 
113
- def show_service_selection()
114
- services = service_store
115
- .all_definitions
116
- .values
117
- .map(&:service_name)
118
- .uniq
119
- .sort
120
- .map(&:to_s)
121
-
122
- if services.empty?
123
- return [[], []]
111
+ def show_tags_selection()
112
+ specific_service_option = "deploy specific service"
113
+ all_services_option = "deploy all services"
114
+
115
+ tags = [specific_service_option, all_services_option]
116
+ tags += service_store
117
+ .all_definitions
118
+ .values
119
+ .map(&:to_service_attrs)
120
+ .map(&:tags)
121
+ .flatten
122
+ .uniq
123
+ .sort
124
+ .map(&:to_s)
125
+
126
+ selected_tag = ui.prompt("Please select which tag to deploy", tags)
127
+
128
+ if selected_tag == specific_service_option
129
+ show_service_selection
130
+ elsif selected_tag == all_services_option
131
+ [["*"], []]
132
+ else
133
+ [[], [selected_tag]]
134
+ end
124
135
  end
125
136
 
126
- selected_service = ui.prompt("Please select which service to deploy", services)
127
- [[selected_service], []]
128
- end
137
+ def show_service_selection()
138
+ services = service_store
139
+ .all_definitions
140
+ .values
141
+ .map(&:service_name)
142
+ .uniq
143
+ .sort
144
+ .map(&:to_s)
145
+
146
+ if services.empty?
147
+ return [[], []]
148
+ end
149
+
150
+ selected_service = ui.prompt("Please select which service to deploy", services)
151
+ [[selected_service], []]
152
+ end
129
153
  end
data/lib/kuber_kit/cli.rb CHANGED
@@ -30,9 +30,10 @@ class KuberKit::CLI < Thor
30
30
  end
31
31
  end
32
32
 
33
- desc "deploy -t CONTEXT_NAME", "Deploy CONTEXT_NAME with kubectl"
33
+ desc "deploy -t TAG_NAME", "Deploy CONTEXT_NAME with kubectl"
34
34
  method_option :services, :type => :array, aliases: ["-s"], repeatable: true
35
35
  method_option :tags, :type => :array, aliases: ["-t"], repeatable: true
36
+ method_option :skip_services, :type => :array, aliases: ["-S"], repeatable: true
36
37
  method_option :skip_compile, :type => :boolean, aliases: ["-B"]
37
38
  method_option :require_confirmation, :type => :boolean, aliases: ["-r"]
38
39
  def deploy
@@ -46,6 +47,7 @@ class KuberKit::CLI < Thor
46
47
  result = KuberKit::Container['actions.service_deployer'].call(
47
48
  services: (options[:services] || []).flatten.uniq,
48
49
  tags: (options[:tags] || []).flatten.uniq,
50
+ skip_services: (options[:skip_services] || []).flatten.uniq,
49
51
  skip_compile: options[:skip_compile] || false,
50
52
  require_confirmation: require_confirmation
51
53
  )
@@ -59,8 +61,8 @@ class KuberKit::CLI < Thor
59
61
  end
60
62
  end
61
63
 
62
- desc "env ENV_FILE_NAME", "Return content of Env File ENV_FILE_NAME"
63
- def env(env_file_name)
64
+ desc "envfile ENV_FILE_NAME", "Return content of Env File ENV_FILE_NAME"
65
+ def envfile(env_file_name)
64
66
  setup(options)
65
67
 
66
68
  if KuberKit::Container['actions.configuration_loader'].call(options)
@@ -150,6 +152,15 @@ class KuberKit::CLI < Thor
150
152
  end
151
153
  end
152
154
 
155
+ desc "get RESOURCE_NAME", "List pods matching RESOURCE_NAME using kubectl"
156
+ def get(pod_name = nil)
157
+ setup(options)
158
+
159
+ if KuberKit::Container['actions.configuration_loader'].call(options.merge(load_inventory: false))
160
+ KuberKit::Container['actions.kubectl_get'].call(pod_name, options)
161
+ end
162
+ end
163
+
153
164
  desc "version", "Print current version"
154
165
  def version
155
166
  puts KuberKit::VERSION
@@ -4,7 +4,7 @@ class KuberKit::Configs
4
4
  AVAILABLE_CONFIGS = [
5
5
  :image_dockerfile_name, :image_build_context_dir, :image_tag, :docker_ignore_list, :image_compile_dir,
6
6
  :kuber_kit_dirname, :kuber_kit_min_version, :images_dirname, :services_dirname, :infra_dirname, :configurations_dirname,
7
- :artifact_clone_dir, :service_config_dir, :deployer_strategy, :compile_simultaneous_limit,
7
+ :artifact_clone_dir, :service_config_dir, :deployer_strategy, :compile_simultaneous_limit, :deploy_simultaneous_limit,
8
8
  :additional_images_paths, :deprecation_warnings_disabled, :log_file_path
9
9
  ]
10
10
  DOCKER_IGNORE_LIST = [
@@ -49,6 +49,7 @@ class KuberKit::Configs
49
49
  set :service_config_dir, "/tmp/kuber_kit/services"
50
50
  set :deployer_strategy, :kubernetes
51
51
  set :compile_simultaneous_limit, 5
52
+ set :deploy_simultaneous_limit, 5
52
53
  set :additional_images_paths, []
53
54
  set :deprecation_warnings_disabled, false
54
55
  set :log_file_path, "/tmp/kuber_kit.log"
@@ -45,6 +45,10 @@ class KuberKit::Container
45
45
  KuberKit::Actions::KubectlDescribe.new
46
46
  end
47
47
 
48
+ register "actions.kubectl_get" do
49
+ KuberKit::Actions::KubectlGet.new
50
+ end
51
+
48
52
  register "actions.kubectl_logs" do
49
53
  KuberKit::Actions::KubectlLogs.new
50
54
  end
@@ -257,6 +261,10 @@ class KuberKit::Container
257
261
  KuberKit::ServiceDeployer::ServiceListResolver.new
258
262
  end
259
263
 
264
+ register "service_deployer.service_dependency_resolver" do
265
+ KuberKit::ServiceDeployer::ServiceDependencyResolver.new
266
+ end
267
+
260
268
  register "service_deployer.strategies.kubernetes" do
261
269
  KuberKit::ServiceDeployer::Strategies::Kubernetes.new
262
270
  end
@@ -1,6 +1,6 @@
1
1
  class KuberKit::Core::Configuration
2
2
  attr_reader :name, :artifacts, :registries, :env_files, :templates, :kubeconfig_path,
3
- :services_attributes, :enabled_services, :build_servers, :global_build_vars,
3
+ :services_attributes, :enabled_services, :disabled_services, :build_servers, :global_build_vars,
4
4
  :deployer_strategy, :deployer_namespace, :deployer_require_confirimation
5
5
 
6
6
  Contract KeywordArgs[
@@ -12,6 +12,7 @@ class KuberKit::Core::Configuration
12
12
  kubeconfig_path: Maybe[String],
13
13
  services_attributes: HashOf[Symbol => Hash],
14
14
  enabled_services: ArrayOf[Symbol],
15
+ disabled_services: ArrayOf[Symbol],
15
16
  build_servers: ArrayOf[KuberKit::Core::BuildServers::AbstractBuildServer],
16
17
  global_build_vars: HashOf[Symbol => Any],
17
18
  deployer_strategy: Symbol,
@@ -19,7 +20,7 @@ class KuberKit::Core::Configuration
19
20
  deployer_require_confirimation: Bool,
20
21
  ] => Any
21
22
  def initialize(name:, artifacts:, registries:, env_files:, templates:, kubeconfig_path:,
22
- services_attributes:, enabled_services:, build_servers:, global_build_vars:,
23
+ services_attributes:, enabled_services:, disabled_services:, build_servers:, global_build_vars:,
23
24
  deployer_strategy:, deployer_namespace:, deployer_require_confirimation:)
24
25
  @name = name
25
26
  @artifacts = artifacts
@@ -30,6 +31,7 @@ class KuberKit::Core::Configuration
30
31
  @build_servers = build_servers
31
32
  @services_attributes = services_attributes
32
33
  @enabled_services = enabled_services
34
+ @disabled_services = disabled_services
33
35
  @global_build_vars = global_build_vars
34
36
  @deployer_strategy = deployer_strategy
35
37
  @deployer_namespace = deployer_namespace
@@ -12,6 +12,7 @@ class KuberKit::Core::ConfigurationDefinition
12
12
  @templates = {}
13
13
  @build_servers = []
14
14
  @enabled_services = []
15
+ @disabled_services = []
15
16
  @services_attributes = {}
16
17
  end
17
18
 
@@ -24,6 +25,7 @@ class KuberKit::Core::ConfigurationDefinition
24
25
  templates: @templates,
25
26
  kubeconfig_path: @kubeconfig_path,
26
27
  enabled_services: @enabled_services,
28
+ disabled_services: @disabled_services,
27
29
  build_servers: @build_servers,
28
30
  services_attributes: @services_attributes,
29
31
  global_build_vars: @global_build_vars,
@@ -116,6 +118,11 @@ class KuberKit::Core::ConfigurationDefinition
116
118
  raise KuberKit::Error, "#enabled_services method accepts only Array or Hash"
117
119
  end
118
120
 
121
+ def disabled_services(services)
122
+ @disabled_services += services.map(&:to_sym)
123
+ return self
124
+ end
125
+
119
126
  def service_attributes(services)
120
127
  @services_attributes = @services_attributes.merge(services)
121
128
  self
@@ -29,6 +29,7 @@ class KuberKit::Core::ConfigurationFactory
29
29
  build_servers: build_servers,
30
30
  services_attributes: configuration_attrs.services_attributes,
31
31
  enabled_services: configuration_attrs.enabled_services,
32
+ disabled_services: configuration_attrs.disabled_services,
32
33
  global_build_vars: configuration_attrs.global_build_vars || {},
33
34
  deployer_strategy: configuration_attrs.deployer_strategy || configs.deployer_strategy,
34
35
  deployer_namespace: configuration_attrs.deployer_namespace,
@@ -0,0 +1,75 @@
1
+ class KuberKit::Core::Dependencies::AbstractDependencyResolver
2
+ CircularDependencyError = Class.new(KuberKit::Error)
3
+ DependencyNotFoundError = Class.new(KuberKit::NotFoundError)
4
+
5
+ # Iterate over list of dependencies for items (including the items themself).
6
+ # Iteration will send the list to the callback block function
7
+ Contract Or[Symbol, ArrayOf[Symbol]], Proc => Any
8
+ def each_with_deps(item_names, &block)
9
+ resolved_dependencies = []
10
+ # Get first list of dependencies ready to resolve
11
+ next_dependencies = get_next(item_names, limit: dependency_batch_size)
12
+
13
+ # Call the block for each list of dependencies ready to resolve, then calculate the next list
14
+ while (next_dependencies - resolved_dependencies).any?
15
+ block.call(next_dependencies)
16
+ resolved_dependencies += next_dependencies
17
+ next_dependencies = get_next(item_names, resolved: resolved_dependencies, limit: dependency_batch_size)
18
+ end
19
+
20
+ (item_names - resolved_dependencies).each_slice(dependency_batch_size) do |group|
21
+ block.call(group)
22
+ end
23
+ end
24
+
25
+ # Returns next list of dependencies ready to resolve.
26
+ # Item is not ready to resolve if it has personal dependency.
27
+ # E.g. if "A" depends on "B" and "C", "C" depends on "D", then only "B" and "D" will be returned.
28
+ Contract Or[Symbol, ArrayOf[Symbol]], KeywordArgs[
29
+ resolved: Optional[ArrayOf[Symbol]],
30
+ limit: Optional[Maybe[Num]]
31
+ ] => Any
32
+ def get_next(item_names, resolved: [], limit: nil)
33
+ deps = Array(item_names).map { |i| get_recursive_deps(i) }.flatten.uniq
34
+
35
+ # Find out which dependencies are ready to resolve,
36
+ # they should not have unresolved personal dependencies
37
+ ready_to_resolve = deps.select do |dep_name|
38
+ unresolved_deps = get_deps(dep_name) - resolved
39
+ unresolved_deps.empty?
40
+ end
41
+ unresolved_deps = ready_to_resolve - resolved
42
+ unresolved_deps = unresolved_deps.take(limit) if limit
43
+ unresolved_deps
44
+ end
45
+
46
+ # Get all dependencies for items (including the items themself), without any limitations
47
+ Contract Or[Symbol, ArrayOf[Symbol]] => Any
48
+ def get_all(item_names)
49
+ deps = Array(item_names).map { |i| get_recursive_deps(i) }.flatten
50
+ (deps + item_names).uniq
51
+ end
52
+
53
+ def get_recursive_deps(item_name, dependency_tree: [])
54
+ deps = get_deps(item_name)
55
+
56
+ if dependency_tree.include?(item_name)
57
+ raise CircularDependencyError, "Circular dependency found for #{item_name}. Dependency tree: #{dependency_tree.inspect}"
58
+ end
59
+
60
+ child_deps = []
61
+ deps.each do |i|
62
+ child_deps += get_recursive_deps(i, dependency_tree: dependency_tree + [item_name])
63
+ end
64
+
65
+ (deps + child_deps).uniq
66
+ end
67
+
68
+ def get_deps(item_name)
69
+ raise "This method should be overriden"
70
+ end
71
+
72
+ def dependency_batch_size
73
+ raise "This method should be overriden"
74
+ end
75
+ end
@@ -1,5 +1,6 @@
1
1
  class KuberKit::Core::Image
2
- attr_reader :name, :dependencies, :registry, :dockerfile_path, :build_vars, :build_context_dir, :tag, :before_build_callback, :after_build_callback
2
+ attr_reader :name, :dependencies, :registry, :dockerfile_path, :build_vars, :build_context_dir, :tag,
3
+ :before_build_callback, :after_build_callback
3
4
 
4
5
  Contract KeywordArgs[
5
6
  name: Symbol,
@@ -1,18 +1,20 @@
1
1
  class KuberKit::Core::Service
2
2
  AttributeNotSet = Class.new(Indocker::Error)
3
3
 
4
- attr_reader :name, :template_name, :tags, :images, :attributes, :deployer_strategy
4
+ attr_reader :name, :dependencies, :template_name, :tags, :images, :attributes, :deployer_strategy
5
5
 
6
6
  Contract KeywordArgs[
7
7
  name: Symbol,
8
+ dependencies: ArrayOf[Symbol],
8
9
  template_name: Maybe[Symbol],
9
10
  tags: ArrayOf[Symbol],
10
11
  images: ArrayOf[Symbol],
11
12
  attributes: HashOf[Symbol => Any],
12
13
  deployer_strategy: Maybe[Symbol]
13
14
  ] => Any
14
- def initialize(name:, template_name:, tags:, images:, attributes:, deployer_strategy:)
15
+ def initialize(name:, dependencies:, template_name:, tags:, images:, attributes:, deployer_strategy:)
15
16
  @name = name
17
+ @dependencies = dependencies
16
18
  @template_name = template_name
17
19
  @tags = tags
18
20
  @images = images
@@ -1,22 +1,29 @@
1
1
  class KuberKit::Core::ServiceDefinition
2
- attr_reader :service_name, :template_name
2
+ attr_reader :service_name, :template_name, :dependencies
3
3
 
4
4
  Contract Or[Symbol, String] => Any
5
5
  def initialize(service_name)
6
6
  @service_name = service_name.to_sym
7
+ @dependencies = []
7
8
  end
8
9
 
9
10
  def to_service_attrs
10
11
  OpenStruct.new(
11
- name: @service_name,
12
- template_name: get_value(@template_name),
13
- tags: Array(get_value(@tags)).map(&:to_sym),
14
- images: Array(get_value(@images)).map(&:to_sym),
15
- attributes: get_value(@attributes),
12
+ name: @service_name,
13
+ dependencies: @dependencies,
14
+ template_name: get_value(@template_name),
15
+ tags: Array(get_value(@tags)).map(&:to_sym),
16
+ images: Array(get_value(@images)).map(&:to_sym),
17
+ attributes: get_value(@attributes),
16
18
  deployer_strategy: get_value(@deployer_strategy),
17
19
  )
18
20
  end
19
21
 
22
+ def depends_on(*value, &block)
23
+ @dependencies = Array(value).flatten
24
+ self
25
+ end
26
+
20
27
  def template(value = nil, &block)
21
28
  @template_name = block_given? ? block : value
22
29
 
@@ -7,6 +7,7 @@ class KuberKit::Core::ServiceFactory
7
7
 
8
8
  KuberKit::Core::Service.new(
9
9
  name: service_attrs.name,
10
+ dependencies: service_attrs.dependencies,
10
11
  template_name: service_attrs.template_name,
11
12
  tags: service_attrs.tags,
12
13
  images: service_attrs.images,
@@ -1,62 +1,14 @@
1
- class KuberKit::ImageCompiler::ImageDependencyResolver
2
- CircularDependencyError = Class.new(KuberKit::Error)
3
- DependencyNotFoundError = Class.new(KuberKit::NotFoundError)
4
-
1
+ class KuberKit::ImageCompiler::ImageDependencyResolver < KuberKit::Core::Dependencies::AbstractDependencyResolver
5
2
  include KuberKit::Import[
6
3
  "core.image_store",
7
4
  "configs"
8
5
  ]
9
-
10
- Contract Or[Symbol, ArrayOf[Symbol]], Proc => Any
11
- def each_with_deps(image_names, &block)
12
- compile_limit = configs.compile_simultaneous_limit
13
-
14
- resolved_dependencies = []
15
- next_dependencies = get_next(image_names, limit: compile_limit)
16
-
17
- while (next_dependencies - resolved_dependencies).any?
18
- block.call(next_dependencies)
19
- resolved_dependencies += next_dependencies
20
- next_dependencies = get_next(image_names, resolved: resolved_dependencies, limit: compile_limit)
21
- end
22
-
23
- (image_names - resolved_dependencies).each_slice(compile_limit) do |group|
24
- block.call(group)
25
- end
26
- end
27
6
 
28
- Contract Or[Symbol, ArrayOf[Symbol]], KeywordArgs[
29
- resolved: Optional[ArrayOf[Symbol]],
30
- limit: Optional[Maybe[Num]]
31
- ] => Any
32
- def get_next(image_names, resolved: [], limit: nil)
33
- deps = Array(image_names).map { |i| get_recursive_deps(i) }.flatten.uniq
34
-
35
- ready_to_resolve = deps.select do |dep_name|
36
- unresolved_deps = get_deps(dep_name) - resolved
37
- unresolved_deps.empty?
38
- end
39
- unresolved_deps = ready_to_resolve - resolved
40
- unresolved_deps = unresolved_deps.take(limit) if limit
41
- unresolved_deps
42
- end
43
-
44
- def get_recursive_deps(image_name, dependency_tree: [])
45
- deps = get_deps(image_name)
46
-
47
- if dependency_tree.include?(image_name)
48
- raise CircularDependencyError, "Circular dependency found for #{image_name}. Dependency tree: #{dependency_tree.inspect}"
49
- end
50
-
51
- child_deps = []
52
- deps.each do |i|
53
- child_deps += get_recursive_deps(i, dependency_tree: dependency_tree + [image_name])
54
- end
55
-
56
- (deps + child_deps).uniq
57
- end
58
-
59
7
  def get_deps(image_name)
60
8
  image_store.get_definition(image_name).dependencies
61
9
  end
10
+
11
+ def dependency_batch_size
12
+ configs.compile_simultaneous_limit
13
+ end
62
14
  end
@@ -0,0 +1,14 @@
1
+ class KuberKit::ServiceDeployer::ServiceDependencyResolver < KuberKit::Core::Dependencies::AbstractDependencyResolver
2
+ include KuberKit::Import[
3
+ "core.service_store",
4
+ "configs"
5
+ ]
6
+
7
+ def get_deps(service_name)
8
+ service_store.get_definition(service_name).dependencies
9
+ end
10
+
11
+ def dependency_batch_size
12
+ configs.deploy_simultaneous_limit
13
+ end
14
+ end
@@ -4,11 +4,12 @@ class KuberKit::ServiceDeployer::ServiceListResolver
4
4
  ]
5
5
 
6
6
  Contract KeywordArgs[
7
- services: Optional[ArrayOf[String]],
8
- tags: Optional[ArrayOf[String]],
9
- enabled_services: Optional[ArrayOf[String]]
7
+ services: Optional[ArrayOf[String]],
8
+ tags: Optional[ArrayOf[String]],
9
+ enabled_services: Optional[ArrayOf[String]],
10
+ disabled_services: Optional[ArrayOf[String]]
10
11
  ] => ArrayOf[String]
11
- def resolve(services: [], tags: [], enabled_services: [])
12
+ def resolve(services: [], tags: [], enabled_services: [], disabled_services: [])
12
13
  all_definitions = service_store.all_definitions.values
13
14
 
14
15
  included_services, excluded_services = split_by_inclusion(services)
@@ -36,14 +37,18 @@ class KuberKit::ServiceDeployer::ServiceListResolver
36
37
  included_services = included_services.select{ |s| enabled_services.include?(s) }
37
38
  end
38
39
 
40
+ if disabled_services.any?
41
+ included_services = included_services.select{ |s| !disabled_services.include?(s) }
42
+ end
43
+
39
44
  included_services
40
45
  end
41
46
 
42
47
  Contract Array => Array
43
48
  def split_by_inclusion(array)
44
- excluded, included = array.partition{|e| e.start_with?('-') }
49
+ excluded, included = array.partition{|e| e.start_with?('^') }
45
50
 
46
- excluded.map!{ |item| item.gsub(/^\-/, "") }
51
+ excluded.map!{ |item| item.gsub(/^\^/, "") }
47
52
 
48
53
  [included, excluded]
49
54
  end
@@ -9,7 +9,8 @@ class KuberKit::ServiceDeployer::Strategies::Kubernetes < KuberKit::ServiceDeplo
9
9
  :resource_type,
10
10
  :resource_name,
11
11
  :delete_if_exists,
12
- :restart_if_exists
12
+ :restart_if_exists,
13
+ :wait_for_rollout
13
14
  ]
14
15
 
15
16
  Contract KuberKit::Shell::AbstractShell, KuberKit::Core::Service => Any
@@ -41,12 +42,18 @@ class KuberKit::ServiceDeployer::Strategies::Kubernetes < KuberKit::ServiceDeplo
41
42
 
42
43
  apply_result = kubectl_commands.apply_file(shell, config_path, kubeconfig_path: kubeconfig_path, namespace: namespace)
43
44
 
44
- restart_enabled = strategy_options.fetch(:restart_if_exists, true)
45
+ restart_enabled = strategy_options.fetch(:restart_if_exists, true)
46
+ wait_for_rollout = strategy_options.fetch(:wait_for_rollout, true)
45
47
  if restart_enabled && resource_exists
46
48
  kubectl_commands.rolling_restart(
47
49
  shell, resource_type, resource_name,
48
50
  kubeconfig_path: kubeconfig_path, namespace: namespace
49
51
  )
52
+
53
+ kubectl_commands.rollout_status(
54
+ shell, resource_type, resource_name, wait: true,
55
+ kubeconfig_path: kubeconfig_path, namespace: namespace
56
+ ) if wait_for_rollout
50
57
  end
51
58
 
52
59
  apply_result
@@ -102,4 +102,12 @@ class KuberKit::Shell::Commands::KubectlCommands
102
102
  }
103
103
  }, kubeconfig_path: kubeconfig_path, namespace: namespace)
104
104
  end
105
+
106
+ def rollout_status(shell, resource_type, resource_name, wait: true, kubeconfig_path: nil, namespace: nil)
107
+ command_parts = []
108
+ command_parts << %Q{rollout status #{resource_type} #{resource_name}}
109
+ command_parts << "-w" if wait
110
+
111
+ kubectl_run(shell, command_parts, kubeconfig_path: kubeconfig_path, namespace: namespace)
112
+ end
105
113
  end
@@ -1,6 +1,8 @@
1
1
  require 'fileutils'
2
2
 
3
3
  class KuberKit::Shell::LocalShell < KuberKit::Shell::AbstractShell
4
+ MAX_LINES_TO_PRINT = 50
5
+
4
6
  include KuberKit::Import[
5
7
  "shell.command_counter",
6
8
  "shell.rsync_commands",
@@ -20,7 +22,19 @@ class KuberKit::Shell::LocalShell < KuberKit::Shell::AbstractShell
20
22
  end
21
23
 
22
24
  if result && result != "" && log_command
23
- ui.print_debug("LocalShell", "Finished [#{command_number}] with result: \n ----\n#{result.grey}\n ----")
25
+ print_result = result
26
+ print_result_lines = print_result.split("\n")
27
+
28
+ if print_result_lines.count >= MAX_LINES_TO_PRINT
29
+ print_result = [
30
+ "[Result is too long, showing only first and last items]".yellow,
31
+ print_result_lines.first,
32
+ "[#{print_result_lines.count - 2} lines not showing]".yellow,
33
+ print_result_lines.last
34
+ ].join("\n")
35
+ end
36
+
37
+ ui.print_debug("LocalShell", "Finished [#{command_number}] with result: \n ----\n#{print_result.grey}\n ----")
24
38
  end
25
39
 
26
40
  if $?.exitstatus != 0
@@ -1,3 +1,3 @@
1
1
  module KuberKit
2
- VERSION = "0.5.0"
2
+ VERSION = "0.5.5"
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: kuber_kit
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.0
4
+ version: 0.5.5
5
5
  platform: ruby
6
6
  authors:
7
7
  - Iskander Khaziev
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-02-24 00:00:00.000000000 Z
11
+ date: 2021-05-12 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: contracts-lite
@@ -165,6 +165,7 @@ files:
165
165
  - ".ruby-gemset"
166
166
  - ".ruby-version"
167
167
  - ".travis.yml"
168
+ - CHANGELOG.md
168
169
  - Gemfile
169
170
  - Gemfile.lock
170
171
  - LICENSE.txt
@@ -210,6 +211,7 @@ files:
210
211
  - lib/kuber_kit/actions/kubectl_console.rb
211
212
  - lib/kuber_kit/actions/kubectl_describe.rb
212
213
  - lib/kuber_kit/actions/kubectl_env.rb
214
+ - lib/kuber_kit/actions/kubectl_get.rb
213
215
  - lib/kuber_kit/actions/kubectl_logs.rb
214
216
  - lib/kuber_kit/actions/service_checker.rb
215
217
  - lib/kuber_kit/actions/service_deployer.rb
@@ -239,6 +241,7 @@ files:
239
241
  - lib/kuber_kit/core/context_helper/context_vars.rb
240
242
  - lib/kuber_kit/core/context_helper/image_helper.rb
241
243
  - lib/kuber_kit/core/context_helper/service_helper.rb
244
+ - lib/kuber_kit/core/dependencies/abstract_dependency_resolver.rb
242
245
  - lib/kuber_kit/core/env_files/abstract_env_file.rb
243
246
  - lib/kuber_kit/core/env_files/artifact_file.rb
244
247
  - lib/kuber_kit/core/env_files/env_file_store.rb
@@ -281,6 +284,7 @@ files:
281
284
  - lib/kuber_kit/preprocessing/text_preprocessor.rb
282
285
  - lib/kuber_kit/service_deployer/action_handler.rb
283
286
  - lib/kuber_kit/service_deployer/deployer.rb
287
+ - lib/kuber_kit/service_deployer/service_dependency_resolver.rb
284
288
  - lib/kuber_kit/service_deployer/service_list_resolver.rb
285
289
  - lib/kuber_kit/service_deployer/strategies/abstract.rb
286
290
  - lib/kuber_kit/service_deployer/strategies/docker.rb