tobsch-krane 1.0.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.
- checksums.yaml +7 -0
 - data/.buildkite/pipeline.nightly.yml +43 -0
 - data/.github/probots.yml +2 -0
 - data/.gitignore +20 -0
 - data/.rubocop.yml +17 -0
 - data/.shopify-build/VERSION +1 -0
 - data/.shopify-build/kubernetes-deploy.yml +53 -0
 - data/1.0-Upgrade.md +185 -0
 - data/CHANGELOG.md +431 -0
 - data/CODE_OF_CONDUCT.md +46 -0
 - data/CONTRIBUTING.md +164 -0
 - data/Gemfile +16 -0
 - data/ISSUE_TEMPLATE.md +25 -0
 - data/LICENSE.txt +21 -0
 - data/README.md +655 -0
 - data/Rakefile +36 -0
 - data/bin/ci +21 -0
 - data/bin/setup +16 -0
 - data/bin/test +47 -0
 - data/dev.yml +28 -0
 - data/dev/flamegraph-from-tests +35 -0
 - data/exe/krane +5 -0
 - data/krane.gemspec +44 -0
 - data/lib/krane.rb +7 -0
 - data/lib/krane/bindings_parser.rb +88 -0
 - data/lib/krane/cli/deploy_command.rb +75 -0
 - data/lib/krane/cli/global_deploy_command.rb +54 -0
 - data/lib/krane/cli/krane.rb +91 -0
 - data/lib/krane/cli/render_command.rb +41 -0
 - data/lib/krane/cli/restart_command.rb +34 -0
 - data/lib/krane/cli/run_command.rb +54 -0
 - data/lib/krane/cli/version_command.rb +13 -0
 - data/lib/krane/cluster_resource_discovery.rb +113 -0
 - data/lib/krane/common.rb +23 -0
 - data/lib/krane/concerns/template_reporting.rb +29 -0
 - data/lib/krane/concurrency.rb +18 -0
 - data/lib/krane/container_logs.rb +106 -0
 - data/lib/krane/deferred_summary_logging.rb +95 -0
 - data/lib/krane/delayed_exceptions.rb +14 -0
 - data/lib/krane/deploy_task.rb +363 -0
 - data/lib/krane/deploy_task_config_validator.rb +29 -0
 - data/lib/krane/duration_parser.rb +27 -0
 - data/lib/krane/ejson_secret_provisioner.rb +154 -0
 - data/lib/krane/errors.rb +28 -0
 - data/lib/krane/formatted_logger.rb +57 -0
 - data/lib/krane/global_deploy_task.rb +210 -0
 - data/lib/krane/global_deploy_task_config_validator.rb +12 -0
 - data/lib/krane/kubeclient_builder.rb +156 -0
 - data/lib/krane/kubectl.rb +120 -0
 - data/lib/krane/kubernetes_resource.rb +621 -0
 - data/lib/krane/kubernetes_resource/cloudsql.rb +43 -0
 - data/lib/krane/kubernetes_resource/config_map.rb +22 -0
 - data/lib/krane/kubernetes_resource/cron_job.rb +18 -0
 - data/lib/krane/kubernetes_resource/custom_resource.rb +87 -0
 - data/lib/krane/kubernetes_resource/custom_resource_definition.rb +98 -0
 - data/lib/krane/kubernetes_resource/daemon_set.rb +90 -0
 - data/lib/krane/kubernetes_resource/deployment.rb +213 -0
 - data/lib/krane/kubernetes_resource/horizontal_pod_autoscaler.rb +65 -0
 - data/lib/krane/kubernetes_resource/ingress.rb +18 -0
 - data/lib/krane/kubernetes_resource/job.rb +60 -0
 - data/lib/krane/kubernetes_resource/network_policy.rb +22 -0
 - data/lib/krane/kubernetes_resource/persistent_volume_claim.rb +80 -0
 - data/lib/krane/kubernetes_resource/pod.rb +269 -0
 - data/lib/krane/kubernetes_resource/pod_disruption_budget.rb +23 -0
 - data/lib/krane/kubernetes_resource/pod_set_base.rb +71 -0
 - data/lib/krane/kubernetes_resource/pod_template.rb +20 -0
 - data/lib/krane/kubernetes_resource/replica_set.rb +92 -0
 - data/lib/krane/kubernetes_resource/resource_quota.rb +22 -0
 - data/lib/krane/kubernetes_resource/role.rb +22 -0
 - data/lib/krane/kubernetes_resource/role_binding.rb +22 -0
 - data/lib/krane/kubernetes_resource/secret.rb +24 -0
 - data/lib/krane/kubernetes_resource/service.rb +104 -0
 - data/lib/krane/kubernetes_resource/service_account.rb +22 -0
 - data/lib/krane/kubernetes_resource/stateful_set.rb +70 -0
 - data/lib/krane/label_selector.rb +42 -0
 - data/lib/krane/oj.rb +4 -0
 - data/lib/krane/options_helper.rb +39 -0
 - data/lib/krane/remote_logs.rb +60 -0
 - data/lib/krane/render_task.rb +118 -0
 - data/lib/krane/renderer.rb +118 -0
 - data/lib/krane/resource_cache.rb +68 -0
 - data/lib/krane/resource_deployer.rb +265 -0
 - data/lib/krane/resource_watcher.rb +171 -0
 - data/lib/krane/restart_task.rb +228 -0
 - data/lib/krane/rollout_conditions.rb +103 -0
 - data/lib/krane/runner_task.rb +212 -0
 - data/lib/krane/runner_task_config_validator.rb +18 -0
 - data/lib/krane/statsd.rb +65 -0
 - data/lib/krane/task_config.rb +22 -0
 - data/lib/krane/task_config_validator.rb +96 -0
 - data/lib/krane/template_sets.rb +173 -0
 - data/lib/krane/version.rb +4 -0
 - data/pull_request_template.md +8 -0
 - data/screenshots/deploy-demo.gif +0 -0
 - data/screenshots/migrate-logs.png +0 -0
 - data/screenshots/missing-secret-fail.png +0 -0
 - data/screenshots/success.png +0 -0
 - data/screenshots/test-output.png +0 -0
 - metadata +375 -0
 
| 
         @@ -0,0 +1,23 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module Krane
         
     | 
| 
      
 3 
     | 
    
         
            +
              class PodDisruptionBudget < KubernetesResource
         
     | 
| 
      
 4 
     | 
    
         
            +
                TIMEOUT = 10.seconds
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def status
         
     | 
| 
      
 7 
     | 
    
         
            +
                  exists? ? "Available" : "Not Found"
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def deploy_succeeded?
         
     | 
| 
      
 11 
     | 
    
         
            +
                  exists? && observed_generation == current_generation
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def deploy_method
         
     | 
| 
      
 15 
     | 
    
         
            +
                  # Required until https://github.com/kubernetes/kubernetes/issues/45398 changes
         
     | 
| 
      
 16 
     | 
    
         
            +
                  uses_generate_name? ? :create : :replace_force
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def timeout_message
         
     | 
| 
      
 20 
     | 
    
         
            +
                  UNUSUAL_FAILURE_MESSAGE
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
              end
         
     | 
| 
      
 23 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,71 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'krane/kubernetes_resource/pod'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Krane
         
     | 
| 
      
 5 
     | 
    
         
            +
              class PodSetBase < KubernetesResource
         
     | 
| 
      
 6 
     | 
    
         
            +
                def failure_message
         
     | 
| 
      
 7 
     | 
    
         
            +
                  pods.map(&:failure_message).compact.uniq.join("\n")
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def timeout_message
         
     | 
| 
      
 11 
     | 
    
         
            +
                  pods.map(&:timeout_message).compact.uniq.join("\n")
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def fetch_events(kubectl)
         
     | 
| 
      
 15 
     | 
    
         
            +
                  own_events = super
         
     | 
| 
      
 16 
     | 
    
         
            +
                  return own_events unless pods.present?
         
     | 
| 
      
 17 
     | 
    
         
            +
                  most_useful_pod = pods.find(&:deploy_failed?) || pods.find(&:deploy_timed_out?) || pods.first
         
     | 
| 
      
 18 
     | 
    
         
            +
                  own_events.merge(most_useful_pod.fetch_events(kubectl))
         
     | 
| 
      
 19 
     | 
    
         
            +
                end
         
     | 
| 
      
 20 
     | 
    
         
            +
             
     | 
| 
      
 21 
     | 
    
         
            +
                def fetch_debug_logs
         
     | 
| 
      
 22 
     | 
    
         
            +
                  logs = Krane::RemoteLogs.new(
         
     | 
| 
      
 23 
     | 
    
         
            +
                    logger: @logger,
         
     | 
| 
      
 24 
     | 
    
         
            +
                    parent_id: id,
         
     | 
| 
      
 25 
     | 
    
         
            +
                    container_names: container_names,
         
     | 
| 
      
 26 
     | 
    
         
            +
                    namespace: @namespace,
         
     | 
| 
      
 27 
     | 
    
         
            +
                    context: @context
         
     | 
| 
      
 28 
     | 
    
         
            +
                  )
         
     | 
| 
      
 29 
     | 
    
         
            +
                  logs.sync
         
     | 
| 
      
 30 
     | 
    
         
            +
                  logs
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def print_debug_logs?
         
     | 
| 
      
 34 
     | 
    
         
            +
                  pods.present? # the kubectl command times out if no pods exist
         
     | 
| 
      
 35 
     | 
    
         
            +
                end
         
     | 
| 
      
 36 
     | 
    
         
            +
             
     | 
| 
      
 37 
     | 
    
         
            +
                private
         
     | 
| 
      
 38 
     | 
    
         
            +
             
     | 
| 
      
 39 
     | 
    
         
            +
                def pods
         
     | 
| 
      
 40 
     | 
    
         
            +
                  raise NotImplementedError, "Subclasses must define a `pods` accessor"
         
     | 
| 
      
 41 
     | 
    
         
            +
                end
         
     | 
| 
      
 42 
     | 
    
         
            +
             
     | 
| 
      
 43 
     | 
    
         
            +
                def parent_of_pod?(_)
         
     | 
| 
      
 44 
     | 
    
         
            +
                  raise NotImplementedError, "Subclasses must define a `parent_of_pod?` method"
         
     | 
| 
      
 45 
     | 
    
         
            +
                end
         
     | 
| 
      
 46 
     | 
    
         
            +
             
     | 
| 
      
 47 
     | 
    
         
            +
                def container_names
         
     | 
| 
      
 48 
     | 
    
         
            +
                  regular_containers = @definition["spec"]["template"]["spec"]["containers"].map { |c| c["name"] }
         
     | 
| 
      
 49 
     | 
    
         
            +
                  init_containers = @definition["spec"]["template"]["spec"].fetch("initContainers", {}).map { |c| c["name"] }
         
     | 
| 
      
 50 
     | 
    
         
            +
                  regular_containers + init_containers
         
     | 
| 
      
 51 
     | 
    
         
            +
                end
         
     | 
| 
      
 52 
     | 
    
         
            +
             
     | 
| 
      
 53 
     | 
    
         
            +
                def find_pods(cache)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  all_pods = cache.get_all(Pod.kind, @instance_data["spec"]["selector"]["matchLabels"])
         
     | 
| 
      
 55 
     | 
    
         
            +
             
     | 
| 
      
 56 
     | 
    
         
            +
                  all_pods.each_with_object([]) do |pod_data, relevant_pods|
         
     | 
| 
      
 57 
     | 
    
         
            +
                    next unless parent_of_pod?(pod_data)
         
     | 
| 
      
 58 
     | 
    
         
            +
                    pod = Pod.new(
         
     | 
| 
      
 59 
     | 
    
         
            +
                      namespace: namespace,
         
     | 
| 
      
 60 
     | 
    
         
            +
                      context: context,
         
     | 
| 
      
 61 
     | 
    
         
            +
                      definition: pod_data,
         
     | 
| 
      
 62 
     | 
    
         
            +
                      logger: @logger,
         
     | 
| 
      
 63 
     | 
    
         
            +
                      parent: "#{name.capitalize} #{type}",
         
     | 
| 
      
 64 
     | 
    
         
            +
                      deploy_started_at: @deploy_started_at
         
     | 
| 
      
 65 
     | 
    
         
            +
                    )
         
     | 
| 
      
 66 
     | 
    
         
            +
                    pod.sync(cache)
         
     | 
| 
      
 67 
     | 
    
         
            +
                    relevant_pods << pod
         
     | 
| 
      
 68 
     | 
    
         
            +
                  end
         
     | 
| 
      
 69 
     | 
    
         
            +
                end
         
     | 
| 
      
 70 
     | 
    
         
            +
              end
         
     | 
| 
      
 71 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,20 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module Krane
         
     | 
| 
      
 3 
     | 
    
         
            +
              class PodTemplate < KubernetesResource
         
     | 
| 
      
 4 
     | 
    
         
            +
                def status
         
     | 
| 
      
 5 
     | 
    
         
            +
                  exists? ? "Available" : "Not Found"
         
     | 
| 
      
 6 
     | 
    
         
            +
                end
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def deploy_succeeded?
         
     | 
| 
      
 9 
     | 
    
         
            +
                  exists?
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def deploy_failed?
         
     | 
| 
      
 13 
     | 
    
         
            +
                  false
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def timeout_message
         
     | 
| 
      
 17 
     | 
    
         
            +
                  UNUSUAL_FAILURE_MESSAGE
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
              end
         
     | 
| 
      
 20 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,92 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'krane/kubernetes_resource/pod_set_base'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Krane
         
     | 
| 
      
 5 
     | 
    
         
            +
              class ReplicaSet < PodSetBase
         
     | 
| 
      
 6 
     | 
    
         
            +
                TIMEOUT = 5.minutes
         
     | 
| 
      
 7 
     | 
    
         
            +
                attr_reader :pods
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def initialize(namespace:, context:, definition:, logger:, statsd_tags: nil,
         
     | 
| 
      
 10 
     | 
    
         
            +
                  parent: nil, deploy_started_at: nil)
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @parent = parent
         
     | 
| 
      
 12 
     | 
    
         
            +
                  @deploy_started_at = deploy_started_at
         
     | 
| 
      
 13 
     | 
    
         
            +
                  @pods = []
         
     | 
| 
      
 14 
     | 
    
         
            +
                  super(namespace: namespace, context: context, definition: definition,
         
     | 
| 
      
 15 
     | 
    
         
            +
                        logger: logger, statsd_tags: statsd_tags)
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def sync(cache)
         
     | 
| 
      
 19 
     | 
    
         
            +
                  super
         
     | 
| 
      
 20 
     | 
    
         
            +
                  @pods = fetch_pods_if_needed(cache) || []
         
     | 
| 
      
 21 
     | 
    
         
            +
                end
         
     | 
| 
      
 22 
     | 
    
         
            +
             
     | 
| 
      
 23 
     | 
    
         
            +
                def status
         
     | 
| 
      
 24 
     | 
    
         
            +
                  return super unless rollout_data.present?
         
     | 
| 
      
 25 
     | 
    
         
            +
                  rollout_data.map { |state_replicas, num| "#{num} #{state_replicas.chop.pluralize(num)}" }.join(", ")
         
     | 
| 
      
 26 
     | 
    
         
            +
                end
         
     | 
| 
      
 27 
     | 
    
         
            +
             
     | 
| 
      
 28 
     | 
    
         
            +
                def deploy_succeeded?
         
     | 
| 
      
 29 
     | 
    
         
            +
                  return false if stale_status?
         
     | 
| 
      
 30 
     | 
    
         
            +
                  desired_replicas == rollout_data["availableReplicas"].to_i &&
         
     | 
| 
      
 31 
     | 
    
         
            +
                  desired_replicas == rollout_data["readyReplicas"].to_i
         
     | 
| 
      
 32 
     | 
    
         
            +
                end
         
     | 
| 
      
 33 
     | 
    
         
            +
             
     | 
| 
      
 34 
     | 
    
         
            +
                def deploy_failed?
         
     | 
| 
      
 35 
     | 
    
         
            +
                  pods.present? &&
         
     | 
| 
      
 36 
     | 
    
         
            +
                  pods.all?(&:deploy_failed?) &&
         
     | 
| 
      
 37 
     | 
    
         
            +
                  !stale_status?
         
     | 
| 
      
 38 
     | 
    
         
            +
                end
         
     | 
| 
      
 39 
     | 
    
         
            +
             
     | 
| 
      
 40 
     | 
    
         
            +
                def desired_replicas
         
     | 
| 
      
 41 
     | 
    
         
            +
                  return -1 unless exists?
         
     | 
| 
      
 42 
     | 
    
         
            +
                  @instance_data["spec"]["replicas"].to_i
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                def ready_replicas
         
     | 
| 
      
 46 
     | 
    
         
            +
                  return -1 unless exists?
         
     | 
| 
      
 47 
     | 
    
         
            +
                  rollout_data['readyReplicas'].to_i
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                def available_replicas
         
     | 
| 
      
 51 
     | 
    
         
            +
                  return -1 unless exists?
         
     | 
| 
      
 52 
     | 
    
         
            +
                  rollout_data["availableReplicas"].to_i
         
     | 
| 
      
 53 
     | 
    
         
            +
                end
         
     | 
| 
      
 54 
     | 
    
         
            +
             
     | 
| 
      
 55 
     | 
    
         
            +
                private
         
     | 
| 
      
 56 
     | 
    
         
            +
             
     | 
| 
      
 57 
     | 
    
         
            +
                def stale_status?
         
     | 
| 
      
 58 
     | 
    
         
            +
                  observed_generation != current_generation
         
     | 
| 
      
 59 
     | 
    
         
            +
                end
         
     | 
| 
      
 60 
     | 
    
         
            +
             
     | 
| 
      
 61 
     | 
    
         
            +
                def fetch_pods_if_needed(cache)
         
     | 
| 
      
 62 
     | 
    
         
            +
                  # If the ReplicaSet doesn't exist, its pods won't either
         
     | 
| 
      
 63 
     | 
    
         
            +
                  return unless exists?
         
     | 
| 
      
 64 
     | 
    
         
            +
                  # If the status hasn't been updated yet, we're not going to make a determination anyway
         
     | 
| 
      
 65 
     | 
    
         
            +
                  return if stale_status?
         
     | 
| 
      
 66 
     | 
    
         
            +
                  # If we don't want any pods at all, we don't need to look for them
         
     | 
| 
      
 67 
     | 
    
         
            +
                  return if desired_replicas == 0
         
     | 
| 
      
 68 
     | 
    
         
            +
                  # We only need to fetch pods so that deploy_failed? can check that they aren't ALL bad.
         
     | 
| 
      
 69 
     | 
    
         
            +
                  # If we can already tell some pods are ok from the RS data, don't bother fetching them (which can be expensive)
         
     | 
| 
      
 70 
     | 
    
         
            +
                  # Lower numbers here make us more susceptible to being fooled by replicas without probes briefly appearing ready
         
     | 
| 
      
 71 
     | 
    
         
            +
                  return if ready_replicas > 1
         
     | 
| 
      
 72 
     | 
    
         
            +
             
     | 
| 
      
 73 
     | 
    
         
            +
                  find_pods(cache)
         
     | 
| 
      
 74 
     | 
    
         
            +
                end
         
     | 
| 
      
 75 
     | 
    
         
            +
             
     | 
| 
      
 76 
     | 
    
         
            +
                def rollout_data
         
     | 
| 
      
 77 
     | 
    
         
            +
                  return { "replicas" => 0 } unless exists?
         
     | 
| 
      
 78 
     | 
    
         
            +
                  { "replicas" => 0 }.merge(
         
     | 
| 
      
 79 
     | 
    
         
            +
                    @instance_data["status"].slice("replicas", "availableReplicas", "readyReplicas")
         
     | 
| 
      
 80 
     | 
    
         
            +
                  )
         
     | 
| 
      
 81 
     | 
    
         
            +
                end
         
     | 
| 
      
 82 
     | 
    
         
            +
             
     | 
| 
      
 83 
     | 
    
         
            +
                def parent_of_pod?(pod_data)
         
     | 
| 
      
 84 
     | 
    
         
            +
                  return false unless pod_data.dig("metadata", "ownerReferences")
         
     | 
| 
      
 85 
     | 
    
         
            +
                  pod_data["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == @instance_data["metadata"]["uid"] }
         
     | 
| 
      
 86 
     | 
    
         
            +
                end
         
     | 
| 
      
 87 
     | 
    
         
            +
             
     | 
| 
      
 88 
     | 
    
         
            +
                def unmanaged?
         
     | 
| 
      
 89 
     | 
    
         
            +
                  @parent.blank?
         
     | 
| 
      
 90 
     | 
    
         
            +
                end
         
     | 
| 
      
 91 
     | 
    
         
            +
              end
         
     | 
| 
      
 92 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module Krane
         
     | 
| 
      
 3 
     | 
    
         
            +
              class ResourceQuota < KubernetesResource
         
     | 
| 
      
 4 
     | 
    
         
            +
                TIMEOUT = 30.seconds
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def status
         
     | 
| 
      
 7 
     | 
    
         
            +
                  exists? ? "In effect" : "Not Found"
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def deploy_succeeded?
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @instance_data.dig("spec", "hard") == @instance_data.dig("status", "hard")
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def deploy_failed?
         
     | 
| 
      
 15 
     | 
    
         
            +
                  false
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def timeout_message
         
     | 
| 
      
 19 
     | 
    
         
            +
                  UNUSUAL_FAILURE_MESSAGE
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module Krane
         
     | 
| 
      
 3 
     | 
    
         
            +
              class Role < KubernetesResource
         
     | 
| 
      
 4 
     | 
    
         
            +
                TIMEOUT = 30.seconds
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def status
         
     | 
| 
      
 7 
     | 
    
         
            +
                  exists? ? "Created" : "Not Found"
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def deploy_succeeded?
         
     | 
| 
      
 11 
     | 
    
         
            +
                  exists?
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def deploy_failed?
         
     | 
| 
      
 15 
     | 
    
         
            +
                  false
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def timeout_message
         
     | 
| 
      
 19 
     | 
    
         
            +
                  UNUSUAL_FAILURE_MESSAGE
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module Krane
         
     | 
| 
      
 3 
     | 
    
         
            +
              class RoleBinding < KubernetesResource
         
     | 
| 
      
 4 
     | 
    
         
            +
                TIMEOUT = 30.seconds
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def status
         
     | 
| 
      
 7 
     | 
    
         
            +
                  exists? ? "Created" : "Not Found"
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def deploy_succeeded?
         
     | 
| 
      
 11 
     | 
    
         
            +
                  exists?
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def deploy_failed?
         
     | 
| 
      
 15 
     | 
    
         
            +
                  false
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def timeout_message
         
     | 
| 
      
 19 
     | 
    
         
            +
                  UNUSUAL_FAILURE_MESSAGE
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,24 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module Krane
         
     | 
| 
      
 3 
     | 
    
         
            +
              class Secret < KubernetesResource
         
     | 
| 
      
 4 
     | 
    
         
            +
                TIMEOUT = 30.seconds
         
     | 
| 
      
 5 
     | 
    
         
            +
                SENSITIVE_TEMPLATE_CONTENT = true
         
     | 
| 
      
 6 
     | 
    
         
            +
                SERVER_DRY_RUNNABLE = true
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def status
         
     | 
| 
      
 9 
     | 
    
         
            +
                  exists? ? "Available" : "Not Found"
         
     | 
| 
      
 10 
     | 
    
         
            +
                end
         
     | 
| 
      
 11 
     | 
    
         
            +
             
     | 
| 
      
 12 
     | 
    
         
            +
                def deploy_succeeded?
         
     | 
| 
      
 13 
     | 
    
         
            +
                  exists?
         
     | 
| 
      
 14 
     | 
    
         
            +
                end
         
     | 
| 
      
 15 
     | 
    
         
            +
             
     | 
| 
      
 16 
     | 
    
         
            +
                def deploy_failed?
         
     | 
| 
      
 17 
     | 
    
         
            +
                  false
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def timeout_message
         
     | 
| 
      
 21 
     | 
    
         
            +
                  UNUSUAL_FAILURE_MESSAGE
         
     | 
| 
      
 22 
     | 
    
         
            +
                end
         
     | 
| 
      
 23 
     | 
    
         
            +
              end
         
     | 
| 
      
 24 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,104 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'krane/kubernetes_resource/pod'
         
     | 
| 
      
 3 
     | 
    
         
            +
             
     | 
| 
      
 4 
     | 
    
         
            +
            module Krane
         
     | 
| 
      
 5 
     | 
    
         
            +
              class Service < KubernetesResource
         
     | 
| 
      
 6 
     | 
    
         
            +
                TIMEOUT = 7.minutes
         
     | 
| 
      
 7 
     | 
    
         
            +
             
     | 
| 
      
 8 
     | 
    
         
            +
                def sync(cache)
         
     | 
| 
      
 9 
     | 
    
         
            +
                  super
         
     | 
| 
      
 10 
     | 
    
         
            +
                  if exists? && selector.present?
         
     | 
| 
      
 11 
     | 
    
         
            +
                    @related_pods = cache.get_all(Pod.kind, selector)
         
     | 
| 
      
 12 
     | 
    
         
            +
                    @related_workloads = fetch_related_workloads(cache)
         
     | 
| 
      
 13 
     | 
    
         
            +
                  else
         
     | 
| 
      
 14 
     | 
    
         
            +
                    @related_pods = []
         
     | 
| 
      
 15 
     | 
    
         
            +
                    @related_workloads = []
         
     | 
| 
      
 16 
     | 
    
         
            +
                  end
         
     | 
| 
      
 17 
     | 
    
         
            +
                end
         
     | 
| 
      
 18 
     | 
    
         
            +
             
     | 
| 
      
 19 
     | 
    
         
            +
                def status
         
     | 
| 
      
 20 
     | 
    
         
            +
                  if !exists?
         
     | 
| 
      
 21 
     | 
    
         
            +
                    "Not found"
         
     | 
| 
      
 22 
     | 
    
         
            +
                  elsif requires_publishing? && !published?
         
     | 
| 
      
 23 
     | 
    
         
            +
                    "LoadBalancer IP address is not provisioned yet"
         
     | 
| 
      
 24 
     | 
    
         
            +
                  elsif !requires_endpoints?
         
     | 
| 
      
 25 
     | 
    
         
            +
                    "Doesn't require any endpoints"
         
     | 
| 
      
 26 
     | 
    
         
            +
                  elsif selects_some_pods?
         
     | 
| 
      
 27 
     | 
    
         
            +
                    "Selects at least 1 pod"
         
     | 
| 
      
 28 
     | 
    
         
            +
                  else
         
     | 
| 
      
 29 
     | 
    
         
            +
                    "Selects 0 pods"
         
     | 
| 
      
 30 
     | 
    
         
            +
                  end
         
     | 
| 
      
 31 
     | 
    
         
            +
                end
         
     | 
| 
      
 32 
     | 
    
         
            +
             
     | 
| 
      
 33 
     | 
    
         
            +
                def deploy_succeeded?
         
     | 
| 
      
 34 
     | 
    
         
            +
                  return false unless exists?
         
     | 
| 
      
 35 
     | 
    
         
            +
                  return published? if requires_publishing?
         
     | 
| 
      
 36 
     | 
    
         
            +
                  return exists? unless requires_endpoints?
         
     | 
| 
      
 37 
     | 
    
         
            +
                  # We can't use endpoints if we want the service to be able to fail fast when the pods are down
         
     | 
| 
      
 38 
     | 
    
         
            +
                  exposes_zero_replica_workload? || selects_some_pods?
         
     | 
| 
      
 39 
     | 
    
         
            +
                end
         
     | 
| 
      
 40 
     | 
    
         
            +
             
     | 
| 
      
 41 
     | 
    
         
            +
                def deploy_failed?
         
     | 
| 
      
 42 
     | 
    
         
            +
                  false
         
     | 
| 
      
 43 
     | 
    
         
            +
                end
         
     | 
| 
      
 44 
     | 
    
         
            +
             
     | 
| 
      
 45 
     | 
    
         
            +
                def timeout_message
         
     | 
| 
      
 46 
     | 
    
         
            +
                  "This service does not seem to select any pods and this is likely invalid. "\
         
     | 
| 
      
 47 
     | 
    
         
            +
                  "Please confirm the spec.selector is correct and the targeted workload is healthy."
         
     | 
| 
      
 48 
     | 
    
         
            +
                end
         
     | 
| 
      
 49 
     | 
    
         
            +
             
     | 
| 
      
 50 
     | 
    
         
            +
                private
         
     | 
| 
      
 51 
     | 
    
         
            +
             
     | 
| 
      
 52 
     | 
    
         
            +
                def fetch_related_workloads(cache)
         
     | 
| 
      
 53 
     | 
    
         
            +
                  related_deployments = cache.get_all(Deployment.kind)
         
     | 
| 
      
 54 
     | 
    
         
            +
                  related_statefulsets = cache.get_all(StatefulSet.kind)
         
     | 
| 
      
 55 
     | 
    
         
            +
                  (related_deployments + related_statefulsets).select do |workload|
         
     | 
| 
      
 56 
     | 
    
         
            +
                    selector.all? { |k, v| workload['spec']['template']['metadata']['labels'][k] == v }
         
     | 
| 
      
 57 
     | 
    
         
            +
                  end
         
     | 
| 
      
 58 
     | 
    
         
            +
                end
         
     | 
| 
      
 59 
     | 
    
         
            +
             
     | 
| 
      
 60 
     | 
    
         
            +
                def exposes_zero_replica_workload?
         
     | 
| 
      
 61 
     | 
    
         
            +
                  return false unless related_replica_count
         
     | 
| 
      
 62 
     | 
    
         
            +
                  related_replica_count == 0
         
     | 
| 
      
 63 
     | 
    
         
            +
                end
         
     | 
| 
      
 64 
     | 
    
         
            +
             
     | 
| 
      
 65 
     | 
    
         
            +
                def requires_endpoints?
         
     | 
| 
      
 66 
     | 
    
         
            +
                  # services of type External don't have endpoints
         
     | 
| 
      
 67 
     | 
    
         
            +
                  return false if external_name_svc?
         
     | 
| 
      
 68 
     | 
    
         
            +
             
     | 
| 
      
 69 
     | 
    
         
            +
                  # problem counting replicas - by default, assume endpoints are required
         
     | 
| 
      
 70 
     | 
    
         
            +
                  return true if related_replica_count.blank?
         
     | 
| 
      
 71 
     | 
    
         
            +
             
     | 
| 
      
 72 
     | 
    
         
            +
                  related_replica_count > 0
         
     | 
| 
      
 73 
     | 
    
         
            +
                end
         
     | 
| 
      
 74 
     | 
    
         
            +
             
     | 
| 
      
 75 
     | 
    
         
            +
                def selects_some_pods?
         
     | 
| 
      
 76 
     | 
    
         
            +
                  return false unless selector.present?
         
     | 
| 
      
 77 
     | 
    
         
            +
                  @related_pods.present?
         
     | 
| 
      
 78 
     | 
    
         
            +
                end
         
     | 
| 
      
 79 
     | 
    
         
            +
             
     | 
| 
      
 80 
     | 
    
         
            +
                def selector
         
     | 
| 
      
 81 
     | 
    
         
            +
                  @definition["spec"].fetch("selector", {})
         
     | 
| 
      
 82 
     | 
    
         
            +
                end
         
     | 
| 
      
 83 
     | 
    
         
            +
             
     | 
| 
      
 84 
     | 
    
         
            +
                def related_replica_count
         
     | 
| 
      
 85 
     | 
    
         
            +
                  return 0 unless selector.present?
         
     | 
| 
      
 86 
     | 
    
         
            +
             
     | 
| 
      
 87 
     | 
    
         
            +
                  if @related_workloads.present?
         
     | 
| 
      
 88 
     | 
    
         
            +
                    @related_workloads.inject(0) { |sum, d| sum + d["spec"]["replicas"].to_i }
         
     | 
| 
      
 89 
     | 
    
         
            +
                  end
         
     | 
| 
      
 90 
     | 
    
         
            +
                end
         
     | 
| 
      
 91 
     | 
    
         
            +
             
     | 
| 
      
 92 
     | 
    
         
            +
                def external_name_svc?
         
     | 
| 
      
 93 
     | 
    
         
            +
                  @definition["spec"]["type"] == "ExternalName"
         
     | 
| 
      
 94 
     | 
    
         
            +
                end
         
     | 
| 
      
 95 
     | 
    
         
            +
             
     | 
| 
      
 96 
     | 
    
         
            +
                def requires_publishing?
         
     | 
| 
      
 97 
     | 
    
         
            +
                  @definition["spec"]["type"] == "LoadBalancer"
         
     | 
| 
      
 98 
     | 
    
         
            +
                end
         
     | 
| 
      
 99 
     | 
    
         
            +
             
     | 
| 
      
 100 
     | 
    
         
            +
                def published?
         
     | 
| 
      
 101 
     | 
    
         
            +
                  @instance_data.dig('status', 'loadBalancer', 'ingress').present?
         
     | 
| 
      
 102 
     | 
    
         
            +
                end
         
     | 
| 
      
 103 
     | 
    
         
            +
              end
         
     | 
| 
      
 104 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,22 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            module Krane
         
     | 
| 
      
 3 
     | 
    
         
            +
              class ServiceAccount < KubernetesResource
         
     | 
| 
      
 4 
     | 
    
         
            +
                TIMEOUT = 30.seconds
         
     | 
| 
      
 5 
     | 
    
         
            +
             
     | 
| 
      
 6 
     | 
    
         
            +
                def status
         
     | 
| 
      
 7 
     | 
    
         
            +
                  exists? ? "Created" : "Not Found"
         
     | 
| 
      
 8 
     | 
    
         
            +
                end
         
     | 
| 
      
 9 
     | 
    
         
            +
             
     | 
| 
      
 10 
     | 
    
         
            +
                def deploy_succeeded?
         
     | 
| 
      
 11 
     | 
    
         
            +
                  exists?
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def deploy_failed?
         
     | 
| 
      
 15 
     | 
    
         
            +
                  false
         
     | 
| 
      
 16 
     | 
    
         
            +
                end
         
     | 
| 
      
 17 
     | 
    
         
            +
             
     | 
| 
      
 18 
     | 
    
         
            +
                def timeout_message
         
     | 
| 
      
 19 
     | 
    
         
            +
                  UNUSUAL_FAILURE_MESSAGE
         
     | 
| 
      
 20 
     | 
    
         
            +
                end
         
     | 
| 
      
 21 
     | 
    
         
            +
              end
         
     | 
| 
      
 22 
     | 
    
         
            +
            end
         
     | 
| 
         @@ -0,0 +1,70 @@ 
     | 
|
| 
      
 1 
     | 
    
         
            +
            # frozen_string_literal: true
         
     | 
| 
      
 2 
     | 
    
         
            +
            require 'krane/kubernetes_resource/pod_set_base'
         
     | 
| 
      
 3 
     | 
    
         
            +
            module Krane
         
     | 
| 
      
 4 
     | 
    
         
            +
              class StatefulSet < PodSetBase
         
     | 
| 
      
 5 
     | 
    
         
            +
                TIMEOUT = 10.minutes
         
     | 
| 
      
 6 
     | 
    
         
            +
                ONDELETE = 'OnDelete'
         
     | 
| 
      
 7 
     | 
    
         
            +
                attr_reader :pods
         
     | 
| 
      
 8 
     | 
    
         
            +
             
     | 
| 
      
 9 
     | 
    
         
            +
                def sync(cache)
         
     | 
| 
      
 10 
     | 
    
         
            +
                  super
         
     | 
| 
      
 11 
     | 
    
         
            +
                  @pods = exists? ? find_pods(cache) : []
         
     | 
| 
      
 12 
     | 
    
         
            +
                end
         
     | 
| 
      
 13 
     | 
    
         
            +
             
     | 
| 
      
 14 
     | 
    
         
            +
                def status
         
     | 
| 
      
 15 
     | 
    
         
            +
                  return super unless @instance_data["status"].present?
         
     | 
| 
      
 16 
     | 
    
         
            +
                  rollout_data = @instance_data["status"].slice("replicas", "readyReplicas", "currentReplicas")
         
     | 
| 
      
 17 
     | 
    
         
            +
                  rollout_data.map { |state_replicas, num| "#{num} #{state_replicas.chop.pluralize(num)}" }.join(", ")
         
     | 
| 
      
 18 
     | 
    
         
            +
                end
         
     | 
| 
      
 19 
     | 
    
         
            +
             
     | 
| 
      
 20 
     | 
    
         
            +
                def deploy_succeeded?
         
     | 
| 
      
 21 
     | 
    
         
            +
                  if update_strategy == ONDELETE
         
     | 
| 
      
 22 
     | 
    
         
            +
                    # Gem cannot monitor update since it doesn't occur until delete
         
     | 
| 
      
 23 
     | 
    
         
            +
                    unless @success_assumption_warning_shown
         
     | 
| 
      
 24 
     | 
    
         
            +
                      @logger.warn("WARNING: Your StatefulSet's updateStrategy is set to OnDelete, "\
         
     | 
| 
      
 25 
     | 
    
         
            +
                                   "which means updates will not be applied until its pods are deleted. "\
         
     | 
| 
      
 26 
     | 
    
         
            +
                                   "Consider switching to rollingUpdate.")
         
     | 
| 
      
 27 
     | 
    
         
            +
                      @success_assumption_warning_shown = true
         
     | 
| 
      
 28 
     | 
    
         
            +
                    end
         
     | 
| 
      
 29 
     | 
    
         
            +
                    true
         
     | 
| 
      
 30 
     | 
    
         
            +
                  else
         
     | 
| 
      
 31 
     | 
    
         
            +
                    observed_generation == current_generation &&
         
     | 
| 
      
 32 
     | 
    
         
            +
                    status_data['currentRevision'] == status_data['updateRevision'] &&
         
     | 
| 
      
 33 
     | 
    
         
            +
                    desired_replicas == status_data['readyReplicas'].to_i &&
         
     | 
| 
      
 34 
     | 
    
         
            +
                    desired_replicas == status_data['currentReplicas'].to_i
         
     | 
| 
      
 35 
     | 
    
         
            +
                  end
         
     | 
| 
      
 36 
     | 
    
         
            +
                end
         
     | 
| 
      
 37 
     | 
    
         
            +
             
     | 
| 
      
 38 
     | 
    
         
            +
                def deploy_failed?
         
     | 
| 
      
 39 
     | 
    
         
            +
                  return false if update_strategy == ONDELETE
         
     | 
| 
      
 40 
     | 
    
         
            +
                  pods.present? && pods.any?(&:deploy_failed?) &&
         
     | 
| 
      
 41 
     | 
    
         
            +
                  observed_generation == current_generation
         
     | 
| 
      
 42 
     | 
    
         
            +
                end
         
     | 
| 
      
 43 
     | 
    
         
            +
             
     | 
| 
      
 44 
     | 
    
         
            +
                private
         
     | 
| 
      
 45 
     | 
    
         
            +
             
     | 
| 
      
 46 
     | 
    
         
            +
                def update_strategy
         
     | 
| 
      
 47 
     | 
    
         
            +
                  if exists?
         
     | 
| 
      
 48 
     | 
    
         
            +
                    @instance_data['spec']['updateStrategy']['type']
         
     | 
| 
      
 49 
     | 
    
         
            +
                  else
         
     | 
| 
      
 50 
     | 
    
         
            +
                    'Unknown'
         
     | 
| 
      
 51 
     | 
    
         
            +
                  end
         
     | 
| 
      
 52 
     | 
    
         
            +
                end
         
     | 
| 
      
 53 
     | 
    
         
            +
             
     | 
| 
      
 54 
     | 
    
         
            +
                def status_data
         
     | 
| 
      
 55 
     | 
    
         
            +
                  return { 'readyReplicas' => '-1', 'currentReplicas' => '-2' } unless exists?
         
     | 
| 
      
 56 
     | 
    
         
            +
                  @instance_data["status"]
         
     | 
| 
      
 57 
     | 
    
         
            +
                end
         
     | 
| 
      
 58 
     | 
    
         
            +
             
     | 
| 
      
 59 
     | 
    
         
            +
                def desired_replicas
         
     | 
| 
      
 60 
     | 
    
         
            +
                  return -1 unless exists?
         
     | 
| 
      
 61 
     | 
    
         
            +
                  @instance_data["spec"]["replicas"].to_i
         
     | 
| 
      
 62 
     | 
    
         
            +
                end
         
     | 
| 
      
 63 
     | 
    
         
            +
             
     | 
| 
      
 64 
     | 
    
         
            +
                def parent_of_pod?(pod_data)
         
     | 
| 
      
 65 
     | 
    
         
            +
                  return false unless pod_data.dig("metadata", "ownerReferences")
         
     | 
| 
      
 66 
     | 
    
         
            +
                  pod_data["metadata"]["ownerReferences"].any? { |ref| ref["uid"] == @instance_data["metadata"]["uid"] } &&
         
     | 
| 
      
 67 
     | 
    
         
            +
                  @instance_data["status"]["currentRevision"] == pod_data["metadata"]["labels"]["controller-revision-hash"]
         
     | 
| 
      
 68 
     | 
    
         
            +
                end
         
     | 
| 
      
 69 
     | 
    
         
            +
              end
         
     | 
| 
      
 70 
     | 
    
         
            +
            end
         
     |