Skip to content

Commit

Permalink
allow an alternate management label on kube resources
Browse files Browse the repository at this point in the history
  • Loading branch information
wr0ngway committed Jan 4, 2022
1 parent b66107a commit 2315f9e
Show file tree
Hide file tree
Showing 4 changed files with 76 additions and 5 deletions.
6 changes: 4 additions & 2 deletions lib/kubetruth/etl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -246,8 +246,6 @@ def kube_apply(parsed_yml)
namespace = parsed_yml["metadata"]["namespace"] = kubeapi.namespace
end

kubeapi.set_managed(parsed_yml)

ident = "'#{namespace}:#{kind}:#{name}'"
logger.info("Applying kubernetes resource #{ident}")

Expand All @@ -265,13 +263,17 @@ def kube_apply(parsed_yml)
# the server-side apply to do the right thing.
logger.info "Updating kubernetes resource #{ident}"
unless @dry_run
# copy the existing managed labels when updating since labels get replaced, not merged
kubeapi.copy_managed(resource, parsed_yml)
applied_resource = kubeapi.apply_resource(parsed_yml)
@wrote_crds = true if kind == "ProjectMapping" && applied_resource.metadata&.resourceVersion != resource.metadata&.resourceVersion
end
end
rescue Kubeclient::ResourceNotFoundError
logger.info "Creating kubernetes resource #{ident}"
unless @dry_run
# Set managed labels when creating.
kubeapi.set_managed(parsed_yml)
kubeapi.apply_resource(parsed_yml)
@wrote_crds = true if kind == "ProjectMapping"
end
Expand Down
18 changes: 17 additions & 1 deletion lib/kubetruth/kubeapi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ def self.instance

MANAGED_LABEL_KEY = "app.kubernetes.io/managed-by"
MANAGED_LABEL_VALUE = "kubetruth"
# an alternate label in case user already uses managed-by, we may want to
# make it more specific for patch only behavior (vs full ownership)
EDITABLE_LABEL_KEY = "app.kubernetes.io/editable-by"
EDITABLE_LABEL_VALUE = "kubetruth"

def initialize(namespace: nil, token: nil, api_url: nil)

Expand Down Expand Up @@ -88,7 +92,10 @@ def ensure_namespace(ns = namespace)

def under_management?(resource)
labels = resource&.[]("metadata")&.[]("labels")
labels.nil? ? false : resource["metadata"]["labels"][MANAGED_LABEL_KEY] == MANAGED_LABEL_VALUE
return false if labels.nil?
result = labels[MANAGED_LABEL_KEY] == MANAGED_LABEL_VALUE
result ||= labels[EDITABLE_LABEL_KEY] == EDITABLE_LABEL_VALUE
return result
end

def set_managed(resource)
Expand All @@ -97,6 +104,15 @@ def set_managed(resource)
resource["metadata"]["labels"][MANAGED_LABEL_KEY] = MANAGED_LABEL_VALUE
end


def copy_managed(source, dest)
source_labels = source&.[]("metadata")&.[]("labels") || {}
dest["metadata"] ||= {}
dest["metadata"]["labels"] ||= {}
dest["metadata"]["labels"][MANAGED_LABEL_KEY] = MANAGED_LABEL_VALUE if source_labels[MANAGED_LABEL_KEY] == MANAGED_LABEL_VALUE
dest["metadata"]["labels"][EDITABLE_LABEL_KEY] = EDITABLE_LABEL_VALUE if source_labels[EDITABLE_LABEL_KEY] == EDITABLE_LABEL_VALUE
end

def get_resource(resource_name, name, namespace: nil, apiVersion: nil)
apiVersion_client(apiVersion).get_entity(resource_name, name, namespace || self.namespace)
end
Expand Down
5 changes: 3 additions & 2 deletions spec/kubetruth/etl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ class ForceExit < Exception; end
parsed_yml = YAML.load(resource_yml)
resource = Kubeclient::Resource.new(parsed_yml.merge(data: {param1: "oldvalue"}))
expect(@kubeapi).to receive(:get_resource).with("configmaps", "group1", namespace: @kubeapi.namespace, apiVersion: "v1").and_return(resource)
expect(@kubeapi).to receive(:set_managed)
expect(@kubeapi).to receive(:copy_managed)
expect(@kubeapi).to receive(:under_management?).and_return(true)
expect(@kubeapi).to receive(:apply_resource).with(parsed_yml)
etl.kube_apply(parsed_yml)
Expand All @@ -303,7 +303,7 @@ class ForceExit < Exception; end
parsed_yml = YAML.load(resource_yml)
resource = Kubeclient::Resource.new(parsed_yml)
expect(@kubeapi).to receive(:get_resource).with("configmaps", "group1", namespace: @kubeapi.namespace, apiVersion: "v1").and_return(resource)
expect(@kubeapi).to receive(:set_managed)
expect(@kubeapi).to_not receive(:set_managed)
expect(@kubeapi).to receive(:under_management?).and_return(false)
expect(@kubeapi).to_not receive(:apply_resource)
etl.kube_apply(parsed_yml)
Expand Down Expand Up @@ -381,6 +381,7 @@ class ForceExit < Exception; end

expect(etl.instance_variable_get(:@wrote_crds)).to eq(false)
expect(@kubeapi).to receive(:get_resource).and_return(resource)
allow(@kubeapi).to receive(:copy_managed)
expect(@kubeapi).to receive(:apply_resource).and_return(resource)
etl.kube_apply(parsed_yml)
expect(etl.instance_variable_get(:@wrote_crds)).to eq(false)
Expand Down
52 changes: 52 additions & 0 deletions spec/kubetruth/kubeapi_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -264,6 +264,13 @@ def apiserver; "https://127.0.0.1"; end
expect(kubeapi.under_management?(resource)).to eq(true)
end

it "handles alternate labels" do
resource = Kubeclient::Resource.new
resource.metadata = {}
resource.metadata.labels = {KubeApi::EDITABLE_LABEL_KEY => KubeApi::EDITABLE_LABEL_VALUE}
expect(kubeapi.under_management?(resource)).to eq(true)
end

end

describe "set_managed" do
Expand Down Expand Up @@ -292,6 +299,51 @@ def apiserver; "https://127.0.0.1"; end

end

describe "copy_managed" do

it "handles empty labels" do
src, dest = Kubeclient::Resource.new, Kubeclient::Resource.new
kubeapi.copy_managed(src, dest)
expect(dest.metadata.labels.to_h).to eq({})
end

it "handles missing labels" do
src, dest = Kubeclient::Resource.new, Kubeclient::Resource.new
src.metadata = {}
src.metadata.labels = {foo: "bar"}
kubeapi.copy_managed(src, dest)
expect(dest.metadata.labels.to_h).to eq({})
end

it "handles managed label" do
src, dest = Kubeclient::Resource.new, Kubeclient::Resource.new
src = Kubeclient::Resource.new
src.metadata = {}
src.metadata.labels = {KubeApi::MANAGED_LABEL_KEY => KubeApi::MANAGED_LABEL_VALUE}
kubeapi.copy_managed(src, dest)
expect(dest.metadata.labels.to_h).to eq({KubeApi::MANAGED_LABEL_KEY.to_sym => KubeApi::MANAGED_LABEL_VALUE})
end

it "handles editable label" do
src, dest = Kubeclient::Resource.new, Kubeclient::Resource.new
src = Kubeclient::Resource.new
src.metadata = {}
src.metadata.labels = {KubeApi::EDITABLE_LABEL_KEY => KubeApi::EDITABLE_LABEL_VALUE}
kubeapi.copy_managed(src, dest)
expect(dest.metadata.labels.to_h).to eq({KubeApi::EDITABLE_LABEL_KEY.to_sym => KubeApi::EDITABLE_LABEL_VALUE})
end

it "skips labels with wrong value" do
src, dest = Kubeclient::Resource.new, Kubeclient::Resource.new
src = Kubeclient::Resource.new
src.metadata = {}
src.metadata.labels = {KubeApi::MANAGED_LABEL_KEY => "foo", KubeApi::EDITABLE_LABEL_KEY => "foo"}
kubeapi.copy_managed(src, dest)
expect(dest.metadata.labels.to_h).to eq({})
end

end

describe "get_resource" do

it "raise when resource doesn't exist" do
Expand Down

0 comments on commit 2315f9e

Please sign in to comment.