kube-platform 3.3.1.gk.0

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 (41) hide show
  1. checksums.yaml +7 -0
  2. data/Gemfile +31 -0
  3. data/README.md +192 -0
  4. data/bin/kube-platform +37 -0
  5. data/lib/kube-platform/application.rb +203 -0
  6. data/lib/kube-platform/cli.rb +114 -0
  7. data/lib/kube-platform/client.rb +217 -0
  8. data/lib/kube-platform/cluster.rb +224 -0
  9. data/lib/kube-platform/cluster_definition.rb +115 -0
  10. data/lib/kube-platform/configuration.rb +145 -0
  11. data/lib/kube-platform/exceptions.rb +9 -0
  12. data/lib/kube-platform/handlers/dockerhub_secret_copy.rb +52 -0
  13. data/lib/kube-platform/handlers/ebs_from_snapshot.rb +108 -0
  14. data/lib/kube-platform/handlers/handler.rb +36 -0
  15. data/lib/kube-platform/handlers/recreate_resource.rb +11 -0
  16. data/lib/kube-platform/handlers/secret_copy.rb +43 -0
  17. data/lib/kube-platform/handlers/wait_for_job_completion.rb +69 -0
  18. data/lib/kube-platform/handlers/wait_for_termination.rb +47 -0
  19. data/lib/kube-platform/health_check.rb +19 -0
  20. data/lib/kube-platform/health_checks/pods_ready.rb +188 -0
  21. data/lib/kube-platform/health_checks/r53_records.rb +82 -0
  22. data/lib/kube-platform/helpers/retry.rb +20 -0
  23. data/lib/kube-platform/images/descriptor.rb +49 -0
  24. data/lib/kube-platform/images/docker_hub_image.rb +49 -0
  25. data/lib/kube-platform/images/dockerhub_image_factory.rb +64 -0
  26. data/lib/kube-platform/images/kubernetes_docker_hub_secret_provider.rb +44 -0
  27. data/lib/kube-platform/images/repository.rb +77 -0
  28. data/lib/kube-platform/images/tag_associator.rb +80 -0
  29. data/lib/kube-platform/images/tagged_dockerhub_image.rb +36 -0
  30. data/lib/kube-platform/logger.rb +32 -0
  31. data/lib/kube-platform/manifest.rb +61 -0
  32. data/lib/kube-platform/pre_checks/r53_records.rb +66 -0
  33. data/lib/kube-platform/pre_checks/valid_platform_dependencies.rb +52 -0
  34. data/lib/kube-platform/pre_checks.rb +19 -0
  35. data/lib/kube-platform/resource.rb +152 -0
  36. data/lib/kube-platform/resource_repository.rb +73 -0
  37. data/lib/kube-platform/thor/descriptor_to_option_adapter.rb +33 -0
  38. data/lib/kube-platform/update_checker.rb +39 -0
  39. data/lib/kube-platform/version.rb +5 -0
  40. data/lib/kube-platform.rb +40 -0
  41. metadata +179 -0
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash/indifferent_access"
4
+
5
+ module KubePlatform
6
+ module Images
7
+ class Descriptor
8
+ DEFAULT_IMAGE_CONFIG = "../../../config/images.yaml"
9
+
10
+ attr_reader :name, :default_tag, :default_tag_from_key, :key, :description, :key_alias
11
+
12
+ def initialize(name:, default_tag:, default_tag_from_key:, key:, description:, key_alias: nil)
13
+ @name = name
14
+ @default_tag = default_tag
15
+ @default_tag_from_key = default_tag_from_key
16
+ @key = key.to_sym
17
+ @description = description
18
+ @key_alias = key_alias&.to_sym
19
+ end
20
+
21
+ class << self
22
+ def with_image_defaults
23
+ images_path = File.expand_path(File.join(__dir__, DEFAULT_IMAGE_CONFIG))
24
+ array_from_yaml_file(filename: images_path)
25
+ end
26
+
27
+ def array_from_yaml_file(filename:)
28
+ yaml_to_hash(filename).map do |item|
29
+ new(
30
+ name: item[:name],
31
+ default_tag: item[:default_tag],
32
+ default_tag_from_key: item[:default_tag_from_key],
33
+ key: item[:key],
34
+ description: item[:description],
35
+ key_alias: item[:key_alias]
36
+ )
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def yaml_to_hash(filename)
43
+ YAML.load_file(filename).with_indifferent_access[:images] or # TODO: get rid of indifferent access
44
+ raise ConfigException, "#{filename} does not contain a set of valid image descriptors"
45
+ end
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,49 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "base64"
4
+
5
+ module KubePlatform
6
+ module Images
7
+ class DockerHubImage
8
+ include Logger
9
+
10
+ attr_reader :image_name, :secret_provider
11
+
12
+ def initialize(image_name:, secret_provider:)
13
+ @image_name = image_name
14
+ @secret_provider = secret_provider
15
+ end
16
+
17
+ def tag_exist?(tag)
18
+ response = RestClient.get(
19
+ "https://index.docker.io/v2/#{image_name}/manifests/#{tag}",
20
+ Authorization: "Bearer #{dockerhub_token}", Accept: "application/json"
21
+ )
22
+ response.code == 200
23
+ rescue RestClient::RequestFailed
24
+ false
25
+ end
26
+
27
+ private
28
+
29
+ def basic_auth_secret
30
+ secret_provider.secret
31
+ end
32
+
33
+ def dockerhub_token
34
+ @dockerhub_token ||= read_dockerhub_token
35
+ end
36
+
37
+ def read_dockerhub_token
38
+ response = RestClient.get(
39
+ "https://auth.docker.io/token?service=registry.docker.io&scope=repository:#{image_name}:pull",
40
+ Authorization: "Basic #{basic_auth_secret}", Accept: "application/json"
41
+ )
42
+
43
+ JSON.parse(response.body)["token"]
44
+ rescue RestClient::RequestFailed => e
45
+ raise KubePlatformException, "Could not retrieve Docker Hub token: #{e.message}"
46
+ end
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,64 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KubePlatform
4
+ module Images
5
+ class DockerHubImageFactory
6
+ def initialize(secret_provider:)
7
+ @secret_provider = secret_provider
8
+ @image_cache = {}
9
+ @tagged_image_cache = {}
10
+ end
11
+
12
+ def build(image_name:, tag:)
13
+ if fully_qualified_docker_image?(tag)
14
+ parsed_tag = parse_tag_for_full_docker_image(tag)
15
+ tagged_dockerhub_image(parsed_tag[:image_name], parsed_tag[:image_tag])
16
+ else
17
+ tagged_dockerhub_image(image_name, tag)
18
+ end
19
+ end
20
+
21
+ private
22
+
23
+ def fully_qualified_docker_image?(image_tag)
24
+ image_tag.split(":").count > 1
25
+ end
26
+
27
+ def parse_tag_for_full_docker_image(image_tag)
28
+ split_tag = image_tag.split(":")
29
+ { image_name: split_tag[0], image_tag: split_tag[1] }
30
+ end
31
+
32
+ def tagged_dockerhub_image(image_name, image_tag)
33
+ retrieve_tagged_image(image_name, image_tag) || create_tagged_dockerhub_image(image_name, image_tag)
34
+ end
35
+
36
+ def retrieve_tagged_image(image_name, image_tag)
37
+ @tagged_image_cache[tagged_image_hash_key(image_name, image_tag)]
38
+ end
39
+
40
+ def tagged_image_hash_key(image_name, image_tag)
41
+ [image_name, image_tag]
42
+ end
43
+
44
+ def create_tagged_dockerhub_image(image_name, image_tag)
45
+ dockerhub_image = dockerhub_image(image_name)
46
+ @tagged_image_cache[tagged_image_hash_key(image_name, image_tag)] = KubePlatform::Images::TaggedDockerHubImage.new(
47
+ image: dockerhub_image, tag: image_tag
48
+ )
49
+ end
50
+
51
+ def dockerhub_image(image_name)
52
+ retrieve_cached_image(image_name) || create_dockerhub_image(image_name)
53
+ end
54
+
55
+ def retrieve_cached_image(image_name)
56
+ @image_cache[image_name]
57
+ end
58
+
59
+ def create_dockerhub_image(image_name)
60
+ @image_cache[image_name] = KubePlatform::Images::DockerHubImage.new(image_name: image_name, secret_provider: @secret_provider)
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,44 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KubePlatform
4
+ module Images
5
+ class KubernetesDockerHubSecretProvider
6
+ include Logger
7
+
8
+ attr_reader :client, :secret_namespace, :secret_name
9
+
10
+ def initialize(client:, secret_name:, secret_namespace:)
11
+ @client = client
12
+ @secret_name = secret_name
13
+ @secret_namespace = secret_namespace
14
+ end
15
+
16
+ def secret
17
+ basic_auth_secret
18
+ end
19
+
20
+ private
21
+
22
+ def basic_auth_secret
23
+ resource = client.get(secret_resource)
24
+ if resource.nil?
25
+ message = "Could not retrieve Docker Hub secret from #{secret_namespace}/#{secret_name}"
26
+ logger.error(message) # TODO: don't double-log this
27
+ raise KubePlatform::KubePlatformException, message
28
+ end
29
+
30
+ decode_token(resource.unwrap.data[".dockercfg"])
31
+ end
32
+
33
+ def secret_resource
34
+ @secret_resource ||= KubePlatform::Resource.from_spec(apiVersion: "v1", kind: "Secret",
35
+ metadata: { name: secret_name, namespace: secret_namespace })
36
+ end
37
+
38
+ def decode_token(data)
39
+ decoded = Base64.decode64(data)
40
+ JSON.parse(decoded).dig("https://index.docker.io/v1/", "auth") or raise TokenNotFound, "Token not found in #{decoded}"
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,77 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KubePlatform
4
+ module Images
5
+ class Repository
6
+ def initialize(image_descriptors:, image_tags:, image_factory:)
7
+ @image_descriptors = image_descriptors
8
+ @image_tags = image_tags.compact
9
+ @image_factory = image_factory
10
+ end
11
+
12
+ def get(key)
13
+ registry[key.to_sym]
14
+ end
15
+
16
+ def missing_images
17
+ registry.values.reject(&:exist?)
18
+ end
19
+
20
+ def key_tag_pairs
21
+ registry.each_with_object({}) { |(key, image), pairs| pairs[key] = image.tag }
22
+ end
23
+
24
+ def key_tag_pairs_without_defaults
25
+ @image_tags
26
+ end
27
+
28
+ def add_key_tag_pairs(additional_pairs)
29
+ @image_tags.merge!(additional_pairs) { |_key, old, _new| old }
30
+ rebuild_registry
31
+ end
32
+
33
+ def update_image_tag_defaults(key_tag_pairs)
34
+ (keys_with_default_tags & key_tag_pairs.keys).each do |key|
35
+ descriptor = descriptor_for_key(key)
36
+ registry[key] = @image_factory.build(image_name: descriptor.name, tag: key_tag_pairs[key])
37
+ end
38
+ end
39
+
40
+ private
41
+
42
+ def registry
43
+ @registry ||= build_registry
44
+ end
45
+
46
+ def build_registry
47
+ image_key_tag_map = associate_tags(@image_descriptors, @image_tags)
48
+
49
+ @image_descriptors.each_with_object({}) do |descriptor, registry|
50
+ key = descriptor.key
51
+ registry[key] = @image_factory.build(image_name: descriptor.name, tag: image_key_tag_map[key])
52
+ end
53
+ end
54
+
55
+ def rebuild_registry
56
+ @registry = nil
57
+ registry
58
+ end
59
+
60
+ def associate_tags(image_descriptors, image_tags)
61
+ TagAssociator.new(image_descriptors: image_descriptors, image_tags: image_tags).run
62
+ end
63
+
64
+ def keys_with_default_tags
65
+ registry.keys - @image_tags.keys
66
+ end
67
+
68
+ def descriptor_for_key(key)
69
+ key_descriptor_map[key]
70
+ end
71
+
72
+ def key_descriptor_map
73
+ @key_descriptor_map ||= @image_descriptors.each_with_object({}) { |descriptor, hash| hash[descriptor.key] = descriptor }
74
+ end
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,80 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KubePlatform
4
+ module Images
5
+ class TagAssociator
6
+ DescriptorTagPair = Struct.new(:descriptor, :tag)
7
+
8
+ def initialize(image_descriptors:, image_tags:)
9
+ @image_descriptors = image_descriptors
10
+ @image_tags = image_tags
11
+ @associated = {}
12
+ end
13
+
14
+ def run
15
+ descriptors_with_image_tags, descriptors_requiring_defaults = @image_descriptors.partition { |d| @image_tags.key?(d.key) }
16
+ associate_descriptors_with_image_tags(descriptors_with_image_tags)
17
+ associate_descriptors_with_defaults(descriptors_requiring_defaults)
18
+ transform_descriptor_tag_map_to_image_key_tag_map
19
+ end
20
+
21
+ private
22
+
23
+ def associate_descriptors_with_image_tags(descriptors)
24
+ descriptors.each { |d| associate_tag_to_descriptor(d, @image_tags[d.key]) }
25
+ end
26
+
27
+ def associate_tag_to_descriptor(descriptor, tag)
28
+ @associated[descriptor.key] = DescriptorTagPair.new(descriptor, tag)
29
+ end
30
+
31
+ def associate_descriptors_with_defaults(descriptors)
32
+ remaining_untagged = associate_tags_to_descriptors_containing_default_tag_attributes(descriptors)
33
+ remaining_untagged = associate_tags_to_descriptors_containing_references_to_other_descriptors(remaining_untagged)
34
+ remaining_untagged.empty? or
35
+ raise ConfigException, "No tag could be found for image with key '#{remaining_untagged.first.key}'"
36
+ end
37
+
38
+ def associate_tags_to_descriptors_containing_default_tag_attributes(descriptors)
39
+ descriptors.reject do |d|
40
+ if (tag = d.default_tag)
41
+ associate_tag_to_descriptor(d, tag)
42
+ true
43
+ else
44
+ false
45
+ end
46
+ end
47
+ end
48
+
49
+ def associate_tags_to_descriptors_containing_references_to_other_descriptors(descriptors)
50
+ descriptors.reject do |d|
51
+ if (ref = d.default_tag_from_key)
52
+ resolve_descriptor_reference(d, ref)
53
+ true
54
+ else
55
+ false
56
+ end
57
+ end
58
+ end
59
+
60
+ def resolve_descriptor_reference(descriptor, ref)
61
+ descriptor_tag_pair = @associated[ref.to_sym] or
62
+ raise ConfigException,
63
+ "#{descriptor.key} wants to use the image tag from the image with key '#{ref}', but that image does not have a tag"
64
+
65
+ if descriptor.name != descriptor_tag_pair.descriptor.name &&
66
+ ([descriptor.name, descriptor_tag_pair.descriptor.name] - ['invocaops/web_app', 'invocaops/ringswitch']).any?
67
+ raise ConfigException,
68
+ "#{descriptor.key} is trying to use the default_tag_from_key directive against #{ref}, "\
69
+ "but the image names don't match"
70
+ end
71
+
72
+ associate_tag_to_descriptor(descriptor, descriptor_tag_pair.tag)
73
+ end
74
+
75
+ def transform_descriptor_tag_map_to_image_key_tag_map
76
+ @associated.each_with_object({}) { |(key, descriptor_tag_pair), hash| hash[key] = descriptor_tag_pair.tag }
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,36 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KubePlatform
4
+ module Images
5
+ class TaggedDockerHubImage
6
+ include Logger
7
+
8
+ attr_reader :tag
9
+
10
+ def initialize(image:, tag:)
11
+ @image = image
12
+ @tag = tag
13
+ end
14
+
15
+ def name
16
+ "#{@image.image_name}:#{@tag}"
17
+ end
18
+
19
+ def exist?
20
+ defined?(@exists) or @exists = log_success_fail(@image.tag_exist?(@tag))
21
+ end
22
+
23
+ private
24
+
25
+ def log_success_fail(success)
26
+ if success
27
+ logger.info("Successfully located Docker Hub image for #{name}")
28
+ else
29
+ logger.info("Could not locate Docker Hub image for #{name}")
30
+ end
31
+
32
+ success
33
+ end
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,32 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "logger"
4
+
5
+ module KubePlatform
6
+ module Logger
7
+ ANSI_ESCAPE = "\x1B"
8
+ ANSI_GOTO_START_OF_LINE = ANSI_ESCAPE + "[0F"
9
+ ANSI_CLEAR_TO_END_OF_LINE = ANSI_ESCAPE + "[J"
10
+
11
+ class << self
12
+ attr_accessor :verbose
13
+ attr_writer :logger
14
+
15
+ def logger
16
+ @logger ||= ::Logger.new(STDOUT).tap do |logger|
17
+ logger.formatter = -> (severity, time, programname, msg) do
18
+ if STDOUT.isatty && !verbose
19
+ ANSI_GOTO_START_OF_LINE + ANSI_CLEAR_TO_END_OF_LINE + "%5s %s\n" % [severity, msg]
20
+ else
21
+ ::Logger::Formatter.new.call(severity, time, programname, msg)
22
+ end
23
+ end
24
+ end
25
+ end
26
+ end
27
+
28
+ def logger
29
+ Logger.logger
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,61 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "active_support/core_ext/hash"
4
+ require "active_support/core_ext/string"
5
+ require "jsonnet"
6
+
7
+ module KubePlatform
8
+ class Manifest
9
+ attr_reader :resource_dir, :filename, :config
10
+
11
+ FILENAME_ANNOTATION = "invoca.com/resource_filename"
12
+
13
+ def initialize(filename:, resource_dir:, config:)
14
+ @filename = filename
15
+ @resource_dir = resource_dir
16
+ @config = config
17
+ end
18
+
19
+ def cluster_definition_by_name(name)
20
+ raise_if_definition_not_found(name)
21
+ definitions[name]
22
+ end
23
+
24
+ def version
25
+ manifest[:version] or raise ManifestException, "The manifest does not contain a 'version' key"
26
+ end
27
+
28
+ private
29
+
30
+ def raise_if_definition_not_found(definition_name)
31
+ definitions[definition_name] or raise ManifestException, "A platform definition named #{definition_name} was not found in #{filename}"
32
+ end
33
+
34
+ def definitions
35
+ @definitions ||= manifest[:definitions].each_with_object({}) do |(name, definition), clusters|
36
+ clusters[name] = ClusterDefinition.new(name: name, definition: definition, resource_dir: resource_dir, config: config)
37
+ end
38
+ end
39
+
40
+ def manifest
41
+ @manifest ||= case filename
42
+ when /\.jsonnet$/
43
+ load_jsonnet_manifest
44
+ else
45
+ load_yaml_manifest
46
+ end.with_indifferent_access
47
+ end
48
+
49
+ def load_yaml_manifest
50
+ template = File.read(filename)
51
+ renderer = ERB.new(template)
52
+ YAML.safe_load(renderer.result(config.binding), aliases: true)
53
+ end
54
+
55
+ def load_jsonnet_manifest
56
+ vm = Jsonnet::VM.new
57
+ vm.tla_code("vars", config.to_json)
58
+ JSON.parse(vm.evaluate_file(filename))
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,66 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "aws-sdk-route53"
4
+ require_relative "../logger"
5
+ require_relative "../helpers/retry"
6
+
7
+ module KubePlatform
8
+ module PreChecks
9
+ class R53Records < PreCheck
10
+ include Logger
11
+
12
+ DEFAULT_CONFIG = { }.freeze
13
+
14
+ def initialize(name, config)
15
+ super(name, config.apply_defaults(DEFAULT_CONFIG))
16
+ end
17
+
18
+ def run(_client, _cluster_definition)
19
+ logger.info("Checking for Route53 DNS records")
20
+
21
+ exists = []
22
+
23
+ fully_qualified_names.each do |record|
24
+ if record_exists?(record)
25
+ exists << record
26
+ logger.error("Route53 record #{record} already exists!")
27
+ end
28
+ end
29
+
30
+ exists.length > 0 ? false : true
31
+ end
32
+
33
+ private
34
+
35
+ def fully_qualified_names
36
+ @fully_qualified_names ||= r53_records.map { |record| record.end_with?(".") ? record : "#{record}." }
37
+ end
38
+
39
+ def record_exists?(record)
40
+ response = r53_client.list_resource_record_sets(
41
+ hosted_zone_id: r53_zone_id,
42
+ start_record_name: record,
43
+ start_record_type: "A",
44
+ max_items: 1
45
+ )
46
+ response.resource_record_sets.first&.name == record
47
+ end
48
+
49
+ def r53_client
50
+ @r53_client ||= Aws::Route53::Client.new(region: region)
51
+ end
52
+
53
+ def region
54
+ config[:region]
55
+ end
56
+
57
+ def r53_zone_id
58
+ config[:r53_zone_id]
59
+ end
60
+
61
+ def r53_records
62
+ @r53_records ||= config[:r53_records]
63
+ end
64
+ end
65
+ end
66
+ end
@@ -0,0 +1,52 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "../health_check"
4
+ require_relative "../logger"
5
+
6
+ module KubePlatform
7
+ module PreChecks
8
+ class ValidPlatformDependencies < PreCheck
9
+ PlatformDependencyException = Class.new(KubePlatformException)
10
+
11
+ include Logger
12
+
13
+ def run(_client, cluster_definition)
14
+ pod_names = Set.new(cluster_definition.resources.map(&:name))
15
+ pods_with_invalid_dependencies = invalid_platform_startup_dependencies(cluster_definition, pod_names)
16
+ pods_with_invalid_dependencies.empty? or raise PlatformDependencyException, invalid_dependency_details(pods_with_invalid_dependencies)
17
+ logger.info("Platform startup dependencies are valid")
18
+ true
19
+ end
20
+
21
+ private
22
+
23
+ def invalid_platform_startup_dependencies(cluster_definition, pod_names)
24
+ cluster_definition.resources.map do |resource|
25
+ if (dependencies_csv = resource.pod_annotation(:platform_startup_dependencies))
26
+ unless valid_dependency_list?(dependencies_csv, pod_names)
27
+ { name: resource.name, dependencies: dependencies_csv }
28
+ end
29
+ end
30
+ end.compact
31
+ end
32
+
33
+ def valid_dependency_list?(dependencies_csv, expected_pod_names)
34
+ dependencies_csv.split(/, */).all? { |dependency| expected_pod_names.include?(dependency) }
35
+ end
36
+
37
+ def invalid_dependency_details(pods_with_invalid_dependencies)
38
+ if pods_with_invalid_dependencies.size > 5
39
+ <<~EOS.chomp
40
+ Found #{pods_with_invalid_dependencies.size} pod templates with startup dependencies that don't match existing pod names. Showing the first 5.
41
+ #{pods_with_invalid_dependencies.first(5).join("\n")}
42
+ EOS
43
+ else
44
+ <<~EOS.chomp
45
+ Found pod templates with startup dependencies that don't match existing pod names.
46
+ #{pods_with_invalid_dependencies.join("\n")}
47
+ EOS
48
+ end
49
+ end
50
+ end
51
+ end
52
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ module KubePlatform
4
+ class PreCheck
5
+ attr_reader :name, :config
6
+
7
+ def initialize(name, config)
8
+ @name = name
9
+ @config = config
10
+ end
11
+
12
+ class << self
13
+ def load(class_name:, name:, config:)
14
+ klass = "KubePlatform::PreChecks::#{class_name}".constantize
15
+ klass.new(name, config)
16
+ end
17
+ end
18
+ end
19
+ end