class Krane::ClusterResourceDiscovery

Public Class Methods

new(task_config:, namespace_tags: []) click to toggle source
# File lib/krane/cluster_resource_discovery.rb, line 7
def initialize(task_config:, namespace_tags: [])
  @task_config = task_config
  @namespace_tags = namespace_tags
end

Public Instance Methods

crds() click to toggle source
# File lib/krane/cluster_resource_discovery.rb, line 12
def crds
  @crds ||= fetch_crds.map do |cr_def|
    CustomResourceDefinition.new(namespace: namespace, context: context, logger: logger,
      definition: cr_def, statsd_tags: @namespace_tags)
  end
end
global_resource_kinds() click to toggle source
# File lib/krane/cluster_resource_discovery.rb, line 19
def global_resource_kinds
  @globals ||= fetch_resources(namespaced: false).map { |g| g["kind"] }
end
prunable_resources(namespaced:) click to toggle source
# File lib/krane/cluster_resource_discovery.rb, line 23
def prunable_resources(namespaced:)
  black_list = %w(Namespace Node)
  api_versions = fetch_api_versions

  fetch_resources(namespaced: namespaced).map do |resource|
    next unless resource['verbs'].one? { |v| v == "delete" }
    next if black_list.include?(resource['kind'])
    version = api_versions[resource['apigroup'].to_s].last
    [resource['apigroup'], version, resource['kind']].compact.join("/")
  end.compact
end

Private Instance Methods

fetch_api_versions() click to toggle source

kubectl api-versions returns a list of group/version strings e.g. autoscaling/v2beta2 A kind may not exist in all versions of the group.

# File lib/krane/cluster_resource_discovery.rb, line 39
def fetch_api_versions
  raw, _, st = kubectl.run("api-versions", attempts: 5, use_namespace: false)
  # The "core" group is represented by an empty string
  versions = { "" => %w(v1) }
  if st.success?
    rows = raw.split("\n")
    rows.each do |group_version|
      group, version = group_version.split("/")
      versions[group] ||= []
      versions[group] << version
    end
  end
  versions
end
fetch_crds() click to toggle source
# File lib/krane/cluster_resource_discovery.rb, line 88
def fetch_crds
  raw_json, _, st = kubectl.run("get", "CustomResourceDefinition", output: "json", attempts: 5,
    use_namespace: false)
  if st.success?
    JSON.parse(raw_json)["items"]
  else
    []
  end
end
fetch_resources(namespaced: false) click to toggle source

kubectl api-resources -o wide returns 5 columns NAME SHORTNAMES APIGROUP NAMESPACED KIND VERBS SHORTNAMES and APIGROUP may be blank VERBS is an array serviceaccounts sa <blank> true ServiceAccount [create delete deletecollection get list patch update watch]

# File lib/krane/cluster_resource_discovery.rb, line 59
def fetch_resources(namespaced: false)
  command = %w(api-resources)
  command << "--namespaced=#{namespaced}"
  raw, _, st = kubectl.run(*command, output: "wide", attempts: 5,
    use_namespace: false)
  if st.success?
    rows = raw.split("\n")
    header = rows[0]
    resources = rows[1..-1]
    full_width_field_names = header.downcase.scan(/[a-z]+[\W]*/)
    cursor = 0
    fields = full_width_field_names.each_with_object({}) do |name, hash|
      start = cursor
      cursor = start + name.length
      # Last field should consume the remainder of the line
      cursor = 0 if full_width_field_names.last == name.strip
      hash[name.strip] = [start, cursor - 1]
    end
    resources.map do |resource|
      resource = fields.map { |k, (s, e)| [k.strip, resource[s..e].strip] }.to_h
      # Manually parse verbs: "[get list]" into %w(get list)
      resource["verbs"] = resource["verbs"][1..-2].split
      resource
    end
  else
    []
  end
end
kubectl() click to toggle source
# File lib/krane/cluster_resource_discovery.rb, line 98
def kubectl
  @kubectl ||= Kubectl.new(task_config: @task_config, log_failure_by_default: true)
end