Skip to content

Commit

Permalink
Add an overide list and tests
Browse files Browse the repository at this point in the history
  • Loading branch information
dturn committed Nov 12, 2019
1 parent 26d014a commit 8aeb382
Show file tree
Hide file tree
Showing 6 changed files with 72 additions and 50 deletions.
56 changes: 32 additions & 24 deletions lib/krane/cluster_resource_discovery.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,41 +16,18 @@ def crds
end
end

def global_resource_kinds
@globals ||= fetch_resources(namespaced: false).map { |g| g["kind"] }
end

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
version = latest_version(api_versions[resource['apigroup'].to_s], resource['kind'])
[resource['apigroup'], version, resource['kind']].compact.join("/")
end.compact
end

private

# 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.
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

# kubectl api-resources -o wide returns 5 columns
# NAME SHORTNAMES APIGROUP NAMESPACED KIND VERBS
# SHORTNAMES and APIGROUP may be blank
Expand Down Expand Up @@ -85,6 +62,37 @@ def fetch_resources(namespaced: false)
end
end

private

def latest_version(versions, kind)
version_override = { "CronJob" => "v1beta1", "VolumeAttachment" => "v1beta1",
"CSIDriver" => "v1beta1", "Ingress" => "v1beta1", "CSINode" => "v1beta1" }

pattern = /v(?<major>\d+)(beta)?(?<minor>\d+)?/
latest = versions.sort_by do |version|
match = version.match(pattern)
[match[:major].to_i, (match[:minor] || 999).to_i]
end.last
version_override.fetch(kind, latest)
end

# 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.
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

def fetch_crds
raw_json, _, st = kubectl.run("get", "CustomResourceDefinition", output: "json", attempts: 5,
use_namespace: false)
Expand Down
5 changes: 1 addition & 4 deletions lib/krane/deploy_task.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,10 +10,7 @@ def initialize(**args)
end

def prune_whitelist
black_list = %w(batch/v1beta1/Job)
cluster_resource_discoverer.prunable_resources.select do |gvk|
@task_config.namespaced_kinds.any? { |g| gvk.include?(g) } && black_list.none? { |b| gvk.include?(b) }
end
cluster_resource_discoverer.prunable_resources(namespaced: true)
end
end
end
9 changes: 1 addition & 8 deletions lib/krane/task_config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,7 @@ def initialize(context, namespace, logger = nil)
def global_kinds
@global_kinds ||= begin
cluster_resource_discoverer = ClusterResourceDiscovery.new(task_config: self)
cluster_resource_discoverer.fetch_resources(only_globals: true).map { |g| g["kind"] }
end
end

def namespaced_kinds
@namespaced_kinds ||= begin
cluster_resource_discoverer = ClusterResourceDiscovery.new(task_config: self)
cluster_resource_discoverer.fetch_resources(only_namespaced: true).map { |g| g["kind"] }
cluster_resource_discoverer.fetch_resources(namespaced: false).map { |g| g["kind"] }
end
end
end
Expand Down
3 changes: 1 addition & 2 deletions test/helpers/fixture_deploy_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -185,8 +185,7 @@ def namespace_globals(fixtures, selector)
fixtures.each do |_, kinds_map|
kinds_map.each do |_, resources|
resources.each do |resource|
resource["metadata"]["name"] = (resource["metadata"]["name"] + @namespace)[0..63]
resource["metadata"]["name"] += "0" if resource["metadata"]["name"].end_with?("-")
resource["metadata"]["name"] = (resource["metadata"]["name"] + Digest::MD5.hexdigest(@namespace))[0..63]
resource["metadata"]["labels"] ||= {}
resource["metadata"]["labels"][selector_key] = selector_value
end
Expand Down
49 changes: 37 additions & 12 deletions test/unit/cluster_resource_discovery_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,56 @@ def test_fetch_resources_failure
assert_equal(resources, [])
end

def test_global_resource_kinds_success
crd = mocked_cluster_resource_discovery(api_resources_full_response)
kinds = crd.global_resource_kinds
assert_equal(kinds.length, api_resources_full_response.split("\n").length - 1)
def test_fetch_resources_not_namespaced
crd = mocked_cluster_resource_discovery(api_resources_not_namespaced_full_response)
kinds = crd.fetch_resources(namespaced: false).map { |r| r['kind'] }
assert_equal(kinds.length, api_resources_not_namespaced_full_response.split("\n").length - 1)
%w(MutatingWebhookConfiguration ComponentStatus CustomResourceDefinition).each do |kind|
assert_includes(kinds, kind)
end
end

def test_prunable_resources
def test_fetch_resources_namespaced
crd = mocked_cluster_resource_discovery(api_resources_namespaced_full_response, namespaced: true)
kinds = crd.fetch_resources(namespaced: true).map { |r| r['kind'] }
assert_equal(kinds.length, api_resources_namespaced_full_response.split("\n").length - 1)
%w(ConfigMap CronJob Deployment).each do |kind|
assert_includes(kinds, kind)
end
end

def test_prunable_global_resources
Krane::Kubectl.any_instance.stubs(:run).with("api-versions", attempts: 5, use_namespace: false)
.returns([api_versions_full_response, "", stub(success?: true)])
crd = mocked_cluster_resource_discovery(api_resources_full_response)
crd = mocked_cluster_resource_discovery(api_resources_not_namespaced_full_response)
kinds = crd.prunable_resources(namespaced: false)

assert_equal(kinds.length, 13)
%w(scheduling.k8s.io/v1beta1/PriorityClass storage.k8s.io/v1beta1/StorageClass).each do |kind|
assert_includes(kinds, kind)
%w(PriorityClass StorageClass).each do |expected_kind|
assert kinds.one? { |k| k.include?(expected_kind) }
end
%w(node namespace).each do |black_lised_kind|
assert_empty kinds.select { |k| k.downcase.include?(black_lised_kind) }
end
end

def test_prunable_namespaced_resources
Krane::Kubectl.any_instance.stubs(:run).with("api-versions", attempts: 5, use_namespace: false)
.returns([api_versions_full_response, "", stub(success?: true)])
crd = mocked_cluster_resource_discovery(api_resources_namespaced_full_response, namespaced: true)
kinds = crd.prunable_resources(namespaced: true)

assert_equal(kinds.length, 28)
%w(ConfigMap CronJob Deployment).each do |expected_kind|
assert kinds.one? { |k| k.include?(expected_kind) }
end
end

private

def mocked_cluster_resource_discovery(response, success: true)
def mocked_cluster_resource_discovery(response, success: true, namespaced: false)
Krane::Kubectl.any_instance.stubs(:run)
.with("api-resources", "--namespaced=false", attempts: 5, use_namespace: false, output: "wide")
.with("api-resources", "--namespaced=#{namespaced}", attempts: 5, use_namespace: false, output: "wide")
.returns([response, "", stub(success?: success)])
Krane::ClusterResourceDiscovery.new(task_config: task_config, namespace_tags: [])
end
Expand All @@ -45,7 +66,11 @@ def api_versions_full_response
File.read(File.join(fixture_path('for_unit_tests'), "api_versions.txt"))
end

def api_resources_full_response
File.read(File.join(fixture_path('for_unit_tests'), "api_resources.txt"))
def api_resources_namespaced_full_response
File.read(File.join(fixture_path('for_unit_tests'), "api_resources_namespaced.txt"))
end

def api_resources_not_namespaced_full_response
File.read(File.join(fixture_path('for_unit_tests'), "api_resources_global.txt"))
end
end

0 comments on commit 8aeb382

Please sign in to comment.