kubes 0.5.0 → 0.6.3

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 (125) hide show
  1. checksums.yaml +4 -4
  2. data/.gcloudignore +22 -0
  3. data/.gitignore +1 -1
  4. data/CHANGELOG.md +22 -0
  5. data/Dockerfile +6 -6
  6. data/Dockerfile.alpine +20 -0
  7. data/README.md +54 -8
  8. data/docker/install/docker.sh +8 -0
  9. data/docker/install/gcloud.sh +18 -0
  10. data/docker/install/kubectl.sh +4 -0
  11. data/docs/_docs/config/hooks/kubes.md +1 -0
  12. data/docs/_docs/config/reference.md +1 -0
  13. data/docs/_docs/dsl/multiple-resources.md +3 -1
  14. data/docs/_docs/dsl/resources/secret.md +19 -2
  15. data/docs/_docs/generators.md +4 -4
  16. data/docs/_docs/helpers.md +16 -2
  17. data/docs/_docs/helpers/aws/advanced.md +10 -0
  18. data/docs/_docs/helpers/aws/advanced/secrets.md +131 -0
  19. data/docs/_docs/helpers/aws/advanced/ssm.md +78 -0
  20. data/docs/_docs/helpers/aws/secrets.md +18 -88
  21. data/docs/_docs/helpers/aws/ssm.md +20 -38
  22. data/docs/_docs/helpers/google/advanced.md +10 -0
  23. data/docs/_docs/helpers/google/advanced/secrets.md +78 -0
  24. data/docs/_docs/helpers/google/gke.md +89 -0
  25. data/docs/_docs/helpers/google/secrets.md +18 -27
  26. data/docs/_docs/intro.md +2 -11
  27. data/docs/_docs/intro/how-kubes-works.md +7 -11
  28. data/docs/_docs/layering.md +2 -0
  29. data/docs/_docs/layering/mix.md +99 -0
  30. data/docs/_docs/patterns/multiple-envs.md +55 -0
  31. data/docs/_docs/variables.md +23 -0
  32. data/docs/_docs/variables/advanced.md +62 -0
  33. data/docs/_docs/variables/basic.md +137 -0
  34. data/docs/_docs/vs.md +10 -0
  35. data/docs/_docs/vs/custom.md +109 -0
  36. data/docs/_docs/vs/helm.md +243 -0
  37. data/docs/_docs/vs/kustomize.md +167 -0
  38. data/docs/_includes/intro/features.md +11 -0
  39. data/docs/_includes/layering/layers.md +2 -4
  40. data/docs/_includes/sidebar.html +39 -0
  41. data/docs/_includes/vs/article.md +1 -0
  42. data/docs/_includes/vs/kubes/layering.md +10 -0
  43. data/docs/_includes/vs/kubes/structure.md +24 -0
  44. data/docs/_reference/kubes-new-help.md +15 -0
  45. data/docs/_reference/kubes-new-helper.md +25 -0
  46. data/docs/_reference/kubes-new-resource.md +56 -0
  47. data/docs/_reference/kubes-new-variable.md +20 -0
  48. data/docs/_reference/kubes-new.md +6 -38
  49. data/kubes.gemspec +2 -2
  50. data/lib/kubes.rb +4 -3
  51. data/lib/kubes/auth.rb +13 -1
  52. data/lib/kubes/auth/base.rb +21 -0
  53. data/lib/kubes/auth/ecr.rb +1 -15
  54. data/lib/kubes/auth/gcr.rb +24 -0
  55. data/lib/kubes/cli/apply.rb +0 -1
  56. data/lib/kubes/cli/help/new/helper.md +4 -0
  57. data/lib/kubes/cli/help/{new.md → new/resource.md} +3 -3
  58. data/lib/kubes/cli/new.rb +12 -94
  59. data/lib/kubes/cli/new/helper.rb +24 -0
  60. data/lib/kubes/cli/new/resource.rb +97 -0
  61. data/lib/kubes/cli/new/variable.rb +16 -0
  62. data/lib/kubes/cli/prune.rb +4 -2
  63. data/lib/kubes/command.rb +1 -1
  64. data/lib/kubes/compiler/decorator/base.rb +1 -1
  65. data/lib/kubes/compiler/dsl/core/base.rb +6 -9
  66. data/lib/kubes/compiler/layering.rb +21 -7
  67. data/lib/kubes/compiler/shared/custom_variables.rb +38 -0
  68. data/lib/kubes/compiler/shared/plugin_helpers.rb +14 -0
  69. data/lib/kubes/compiler/strategy.rb +7 -6
  70. data/lib/kubes/compiler/strategy/base.rb +59 -2
  71. data/lib/kubes/compiler/strategy/dsl.rb +0 -29
  72. data/lib/kubes/compiler/strategy/erb.rb +10 -22
  73. data/lib/kubes/compiler/util/normalize.rb +6 -3
  74. data/lib/kubes/compiler/util/yaml_dump.rb +4 -4
  75. data/lib/kubes/config.rb +14 -0
  76. data/lib/kubes/hooks/builder.rb +20 -5
  77. data/lib/kubes/hooks/concern.rb +1 -1
  78. data/lib/kubes/kubectl/batch.rb +7 -0
  79. data/lib/kubes/plugin.rb +14 -0
  80. data/lib/kubes/util/sh.rb +1 -1
  81. data/lib/kubes/version.rb +1 -1
  82. data/lib/templates/new/helper/file.rb +2 -0
  83. data/lib/templates/new/{dsl → resource/dsl}/backend_config.rb +0 -0
  84. data/lib/templates/new/{dsl → resource/dsl}/config_map.rb +0 -0
  85. data/lib/templates/new/{dsl → resource/dsl}/daemon_set.rb +0 -0
  86. data/lib/templates/new/{dsl → resource/dsl}/deployment.rb +0 -0
  87. data/lib/templates/new/{dsl → resource/dsl}/ingress.rb +0 -0
  88. data/lib/templates/new/{dsl → resource/dsl}/job.rb +0 -0
  89. data/lib/templates/new/{dsl → resource/dsl}/managed_certificate.rb +0 -0
  90. data/lib/templates/new/{dsl → resource/dsl}/namespace.rb +0 -0
  91. data/lib/templates/new/{dsl → resource/dsl}/network_policy.rb +0 -0
  92. data/lib/templates/new/{dsl → resource/dsl}/pod.rb +0 -0
  93. data/lib/templates/new/{dsl → resource/dsl}/role.rb +0 -0
  94. data/lib/templates/new/{dsl → resource/dsl}/role_binding.rb +0 -0
  95. data/lib/templates/new/{dsl → resource/dsl}/secret.rb +0 -0
  96. data/lib/templates/new/{dsl → resource/dsl}/service.rb +0 -0
  97. data/lib/templates/new/{dsl → resource/dsl}/service_account.rb +0 -0
  98. data/lib/templates/new/{yaml → resource/yaml}/backend_config.yaml +0 -0
  99. data/lib/templates/new/{yaml → resource/yaml}/config_map.yaml +0 -0
  100. data/lib/templates/new/{yaml → resource/yaml}/daemon_set.yaml +0 -0
  101. data/lib/templates/new/{yaml → resource/yaml}/deployment.yaml +0 -1
  102. data/lib/templates/new/{yaml → resource/yaml}/ingress.yaml +0 -0
  103. data/lib/templates/new/{yaml → resource/yaml}/job.yaml +0 -0
  104. data/lib/templates/new/{yaml → resource/yaml}/managed_certificate.yaml +0 -0
  105. data/lib/templates/new/{yaml → resource/yaml}/namespace.yaml +0 -0
  106. data/lib/templates/new/{yaml → resource/yaml}/network_policy.yaml +0 -0
  107. data/lib/templates/new/{yaml → resource/yaml}/pod.yaml +0 -0
  108. data/lib/templates/new/{yaml → resource/yaml}/role.yaml +0 -0
  109. data/lib/templates/new/{yaml → resource/yaml}/role_binding.yaml +0 -0
  110. data/lib/templates/new/{yaml → resource/yaml}/secret.yaml +0 -0
  111. data/lib/templates/new/{yaml → resource/yaml}/service.yaml +0 -0
  112. data/lib/templates/new/{yaml → resource/yaml}/service_account.yaml +0 -0
  113. data/lib/templates/new/variable/file.rb +1 -0
  114. data/spec/fixtures/multiple-files/{deployment-1.rb → .kubes/resources/web/deployment-1.rb} +0 -0
  115. data/spec/fixtures/multiple-files/{deployment-2.rb → .kubes/resources/web/deployment-2.rb} +0 -0
  116. data/spec/fixtures/project/.kubes/resources/{deployment.rb → web/deployment.rb} +0 -0
  117. data/spec/fixtures/project/.kubes/resources/{foobar.rb → web/empty.rb} +0 -0
  118. data/spec/fixtures/project/.kubes/resources/{service.rb → web/service.rb} +1 -1
  119. data/spec/fixtures/syntax/{network_policy.rb → .kubes/resources/web/network_policy.rb} +0 -0
  120. data/spec/fixtures/syntax/{pod.rb → .kubes/resources/web/pod.rb} +0 -0
  121. data/spec/kubes/compiler/strategy/dsl_spec.rb +2 -2
  122. data/spec/kubes/compiler_spec.rb +1 -1
  123. data/spec/kubes/dsl/network_policy_spec.rb +1 -1
  124. data/spec/kubes/dsl/pod_spec.rb +1 -1
  125. metadata +95 -56
@@ -4,7 +4,6 @@ class Kubes::CLI
4
4
  compile
5
5
  logger.info "Deploying kubes resources"
6
6
  Kubes::Kubectl::Dispatcher.new(:apply, @options).run
7
- Prune.new(@options.merge(yes: true, quiet: true)).run if Kubes.config.auto_prune # prune old secrets and config maps
8
7
  end
9
8
  end
10
9
  end
@@ -0,0 +1,4 @@
1
+ ## Examples
2
+
3
+ $ kubes new helper custom
4
+ create .kubes/helpers/custom_helper.rb
@@ -1,8 +1,8 @@
1
1
  ## Examples
2
2
 
3
- $ kubes new ingress
3
+ $ kubes new resource ingress
4
4
  create .kubes/resources/web/ingress.yaml
5
- $ kubes new service_account
5
+ $ kubes new resource service_account
6
6
  create .kubes/resources/shared/service_account.yaml
7
7
  $
8
8
 
@@ -27,4 +27,4 @@ Here's a list of some of the supported resources.
27
27
  service
28
28
 
29
29
  Refer to the source code to all the resources that the generator supports:
30
- https://github.com/boltops-tools/kubes/blob/master/lib/templates/new/yaml
30
+ https://github.com/boltops-tools/kubes/blob/master/lib/templates/new/resource/yaml
@@ -1,97 +1,15 @@
1
1
  class Kubes::CLI
2
- class New < Sequence
3
- argument :kind
4
-
5
- def self.options
6
- [
7
- [:app, aliases: ["a"], default: "demo", desc: "App name"],
8
- [:force, aliases: ["y"], type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files"],
9
- [:role, aliases: ["r"], desc: "Role. IE: web, clock, worker, migrate, etc. Defaults to convention: web or shared when not set"],
10
- [:type, aliases: ["t"], default: "yaml", desc: "Type: dsl or yaml"],
11
- ]
12
- end
13
- options.each { |args| class_option(*args) }
14
-
15
- private
16
- def app
17
- options[:app]
18
- end
19
-
20
- def role
21
- role = options[:role]
22
- return role if role
23
- shared = %w[
24
- config_map
25
- namespace
26
- network_policy
27
- persistent_volume
28
- persistent_volume_claim
29
- secret
30
- service_account
31
- ]
32
- if shared.include?(full_kind)
33
- "shared"
34
- elsif full_kind == "job"
35
- "migrate"
36
- else
37
- "web"
38
- end
39
- end
40
-
41
- def full_kind
42
- # shorthands
43
- map = {
44
- cj: "cron_job",
45
- cm: "config_map",
46
- crd: "custom_resource_definition",
47
- crds: "custom_resource_definition",
48
- cs: "component_statuses",
49
- csr: "certificate_signing_request",
50
- deploy: "deployment",
51
- ds: "daemonset",
52
- ep: "endpoints",
53
- ev: "event",
54
- hpa: "horizontal_pod_autoscaler",
55
- ing: "ingress",
56
- limits: "limit_range",
57
- netpol: "network_policy",
58
- no: "node",
59
- ns: "namespace",
60
- pc: "priority_class",
61
- pdb: "pod_disruption_budget",
62
- po: "pod",
63
- psp: "pods_ecurity_policy",
64
- pv: "persistent_volume",
65
- pvc: "persistent_volume_claim",
66
- quota: "resource_quota",
67
- rc: "replication_controller",
68
- rs: "replica_set",
69
- sa: "service_account",
70
- sc: "storage_classes",
71
- sgp: "security_group_policy",
72
- sts: "stateful_set",
73
- svc: "service",
74
- }.stringify_keys!
75
- map[kind] || kind
76
- end
77
-
78
- def file
79
- ext = options[:type] == "yaml" ? "yaml" : "rb"
80
- "#{full_kind}.#{ext}"
81
- end
82
-
83
- public
84
- def set_template_source
85
- path = File.expand_path("../../templates/new/#{options[:type]}/#{file}", __dir__)
86
- unless File.exist?(path)
87
- logger.info "ERROR: Generator for #{file} not supported".color(:red)
88
- exit 1
89
- end
90
- set_source("new/#{options[:type]}")
91
- end
92
-
93
- def create_resource
94
- template file, ".kubes/resources/#{role}/#{file}"
95
- end
2
+ class New < Kubes::Command
3
+ long_desc Help.text("new/resource")
4
+ Resource.options.each { |args| option(*args) }
5
+ register(Resource, "resource", "resource", "Generates Kubes Kubernetes resource definition.")
6
+
7
+ long_desc Help.text("new/helper")
8
+ Helper.options.each { |args| option(*args) }
9
+ register(Helper, "helper", "helper", "Generates kubes helper file.")
10
+
11
+ long_desc Help.text("new/variable")
12
+ Variable.options.each { |args| option(*args) }
13
+ register(Variable, "variable", "variable", "Generates kubes variable file.")
96
14
  end
97
15
  end
@@ -0,0 +1,24 @@
1
+ class Kubes::CLI::New
2
+ class Helper < Kubes::CLI::Sequence
3
+ argument :name, default: "custom"
4
+
5
+ def self.options
6
+ [
7
+ [:force, aliases: ["y"], type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files"],
8
+ ]
9
+ end
10
+ options.each { |args| class_option(*args) }
11
+
12
+ private
13
+ def underscored_name
14
+ name.include?("_helper") ? name : "#{name}_helper"
15
+ end
16
+
17
+ public
18
+ def create_helper
19
+ set_source("new/helper")
20
+ file = "#{underscored_name}.rb"
21
+ template "file.rb", ".kubes/helpers/#{file}"
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,97 @@
1
+ class Kubes::CLI::New
2
+ class Resource < Kubes::CLI::Sequence
3
+ argument :kind
4
+
5
+ def self.options
6
+ [
7
+ [:app, aliases: ["a"], default: "demo", desc: "App name"],
8
+ [:force, aliases: ["y"], type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files"],
9
+ [:role, aliases: ["r"], desc: "Role. IE: web, clock, worker, migrate, etc. Defaults to convention: web or shared when not set"],
10
+ [:type, aliases: ["t"], default: "yaml", desc: "Type: dsl or yaml"],
11
+ ]
12
+ end
13
+ options.each { |args| class_option(*args) }
14
+
15
+ private
16
+ def app
17
+ options[:app]
18
+ end
19
+
20
+ def role
21
+ role = options[:role]
22
+ return role if role
23
+ shared = %w[
24
+ config_map
25
+ namespace
26
+ network_policy
27
+ persistent_volume
28
+ persistent_volume_claim
29
+ secret
30
+ service_account
31
+ ]
32
+ if shared.include?(full_kind)
33
+ "shared"
34
+ elsif full_kind == "job"
35
+ "migrate"
36
+ else
37
+ "web"
38
+ end
39
+ end
40
+
41
+ def full_kind
42
+ # shorthands
43
+ map = {
44
+ cj: "cron_job",
45
+ cm: "config_map",
46
+ crd: "custom_resource_definition",
47
+ crds: "custom_resource_definition",
48
+ cs: "component_statuses",
49
+ csr: "certificate_signing_request",
50
+ deploy: "deployment",
51
+ ds: "daemonset",
52
+ ep: "endpoints",
53
+ ev: "event",
54
+ hpa: "horizontal_pod_autoscaler",
55
+ ing: "ingress",
56
+ limits: "limit_range",
57
+ netpol: "network_policy",
58
+ no: "node",
59
+ ns: "namespace",
60
+ pc: "priority_class",
61
+ pdb: "pod_disruption_budget",
62
+ po: "pod",
63
+ psp: "pods_ecurity_policy",
64
+ pv: "persistent_volume",
65
+ pvc: "persistent_volume_claim",
66
+ quota: "resource_quota",
67
+ rc: "replication_controller",
68
+ rs: "replica_set",
69
+ sa: "service_account",
70
+ sc: "storage_classes",
71
+ sgp: "security_group_policy",
72
+ sts: "stateful_set",
73
+ svc: "service",
74
+ }.stringify_keys!
75
+ map[kind] || kind
76
+ end
77
+
78
+ def file
79
+ ext = options[:type] == "yaml" ? "yaml" : "rb"
80
+ "#{full_kind}.#{ext}"
81
+ end
82
+
83
+ public
84
+ def set_template_source
85
+ path = File.expand_path("../../../templates/new/resource/#{options[:type]}/#{file}", __dir__)
86
+ unless File.exist?(path)
87
+ logger.info "ERROR: Generator for #{file} not supported".color(:red)
88
+ exit 1
89
+ end
90
+ set_source("new/resource/#{options[:type]}")
91
+ end
92
+
93
+ def create_resource
94
+ template file, ".kubes/resources/#{role}/#{file}"
95
+ end
96
+ end
97
+ end
@@ -0,0 +1,16 @@
1
+ class Kubes::CLI::New
2
+ class Variable < Kubes::CLI::Sequence
3
+ def self.options
4
+ [
5
+ [:force, aliases: ["y"], type: :boolean, desc: "Bypass overwrite are you sure prompt for existing files"],
6
+ ]
7
+ end
8
+ options.each { |args| class_option(*args) }
9
+
10
+ def create_helper
11
+ set_source("new/variable")
12
+ file = "#{Kubes.env}.rb"
13
+ template "file.rb", ".kubes/variables/#{file}"
14
+ end
15
+ end
16
+ end
@@ -2,15 +2,17 @@ class Kubes::CLI
2
2
  class Prune < Base
3
3
  KINDS = %w[ConfigMap Secret]
4
4
  extend Memoist
5
+ include Kubes::Hooks::Concern
5
6
  include Kubes::Util::Sure
6
7
 
7
8
  def run
8
9
  return unless anything_to_prune?
9
10
  logger.info "Pruning old resources: #{KINDS.join(', ')}"
10
-
11
11
  perform(preview: true) unless @options[:yes]
12
12
  sure?("This will prune/delete resources. Are you sure?")
13
- perform(preview: false)
13
+ run_hooks("kubes.rb", name: "prune") do
14
+ perform(preview: false)
15
+ end
14
16
  end
15
17
 
16
18
  def fetcher
@@ -57,7 +57,7 @@ module Kubes
57
57
  end
58
58
 
59
59
  def check_project!(command_name)
60
- return if %w[init new].include?(command_name)
60
+ return if %w[-h help init new].include?(command_name)
61
61
  Kubes.check_project!
62
62
  end
63
63
 
@@ -11,7 +11,7 @@ module Kubes::Compiler::Decorator
11
11
  end
12
12
 
13
13
  def result
14
- if @data.is_a?(Kubes::Compiler::Dsl::Core::Blocks)
14
+ if @data.key?(Kubes::Compiler::Dsl::Core::Blocks)
15
15
  @data.results.each { |k,v| process(v) } # returns nil
16
16
  else
17
17
  process # processes and returns @data
@@ -3,25 +3,22 @@ module Kubes::Compiler::Dsl::Core
3
3
  extend Fields
4
4
  include DslEvaluator
5
5
  include Helpers
6
- include Kubes::Compiler::Layering
6
+ include Kubes::Compiler::Shared::CustomHelpers
7
+ include Kubes::Compiler::Shared::CustomVariables
8
+ include Kubes::Compiler::Shared::PluginHelpers
7
9
 
8
10
  def initialize(options={})
9
11
  @options = options
10
12
  @name = options[:name]
11
13
  @path = options[:path]
14
+ load_plugin_helpers
15
+ load_custom_variables
16
+ load_custom_helpers
12
17
  end
13
18
 
14
19
  def run
15
- evaluate_files(pre_layers)
16
20
  evaluate_file(@path) # main resource definition
17
- evaluate_files(post_layers)
18
21
  result if respond_to?(:result) # block form doesnt have result
19
22
  end
20
-
21
- def evaluate_files(paths)
22
- paths.each do |path|
23
- evaluate_file(path)
24
- end
25
- end
26
23
  end
27
24
  end
@@ -11,14 +11,26 @@ class Kubes::Compiler
11
11
  all = all.pluralize
12
12
  end
13
13
  layers = [
14
- "#{all}#{ext}",
15
- "#{all}/#{Kubes.env}#{ext}",
16
- "#{kind}#{ext}",
17
- "#{kind}/#{Kubes.env}#{ext}",
14
+ "#{all}",
15
+ "#{all}/#{Kubes.env}",
16
+ "#{kind}",
17
+ "#{kind}/#{Kubes.env}",
18
18
  ]
19
- layers.map do |layer|
19
+ layers = add_exts(layers)
20
+ layers.map! do |layer|
20
21
  "#{Kubes.root}/.kubes/resources/base/#{layer}"
21
22
  end
23
+ layers.select { |layer| File.exist?(layer) }
24
+ end
25
+
26
+ def add_exts(layers)
27
+ layers.map do |layer|
28
+ [
29
+ "#{layer}.rb",
30
+ "#{layer}.yaml",
31
+ "#{layer}.yml",
32
+ ]
33
+ end.flatten
22
34
  end
23
35
 
24
36
  def post_layers
@@ -31,9 +43,11 @@ class Kubes::Compiler
31
43
  "base",
32
44
  Kubes.env.to_s
33
45
  ]
34
- layers.map do |layer|
35
- "#{kind_path}/#{layer}#{ext}"
46
+ layers = add_exts(layers)
47
+ layers.map! do |layer|
48
+ "#{kind_path}/#{layer}"
36
49
  end
50
+ layers.select { |layer| File.exist?(layer) }
37
51
  end
38
52
  end
39
53
  end
@@ -0,0 +1,38 @@
1
+ module Kubes::Compiler::Shared
2
+ module CustomVariables
3
+ include DslEvaluator
4
+
5
+ # Load custom variables from project
6
+ @@custom_variables_loaded = false
7
+ def load_custom_variables
8
+ return if Kubes.kustomize?
9
+
10
+ ext = File.extname(@path)
11
+ role = @path.sub(%r{.*\.kubes/resources/},'').sub(ext,'').split('/').first # IE: web
12
+ kind = File.basename(@path).sub(ext,'') # IE: deployment
13
+ all = "all"
14
+ if @block_form
15
+ kind = kind.pluralize
16
+ all = all.pluralize
17
+ end
18
+
19
+ layers = [
20
+ "base.rb",
21
+ "#{Kubes.env}.rb",
22
+ "base/all.rb",
23
+ "base/all/#{Kubes.env}.rb",
24
+ "base/#{kind}.rb",
25
+ "base/#{kind}/base.rb",
26
+ "base/#{kind}/#{Kubes.env}.rb",
27
+ "#{role}/#{kind}.rb",
28
+ "#{role}/#{kind}/base.rb",
29
+ "#{role}/#{kind}/#{Kubes.env}.rb",
30
+ ]
31
+
32
+ layers.each do |layer|
33
+ path = "#{Kubes.root}/.kubes/variables/#{layer}"
34
+ evaluate_file(path)
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,14 @@
1
+ module Kubes::Compiler::Shared
2
+ module PluginHelpers
3
+ # Load plugin helper methods from project
4
+ @@plugin_helpers_loaded = false
5
+ def load_plugin_helpers
6
+ return if @@plugin_helpers_loaded
7
+ Kubes::Plugin.plugins.each do |klass|
8
+ helpers_class = "#{klass}::Helpers".constantize # IE: KubesAws::Helpers
9
+ self.class.send :include, helpers_class
10
+ end
11
+ @@plugin_helpers_loaded = true
12
+ end
13
+ end
14
+ end