Skip to content

Commit

Permalink
Only write to resources (ConfigMaps and Secrets) that are labeled as …
Browse files Browse the repository at this point in the history
…being under kubetruth management
  • Loading branch information
wr0ngway committed Feb 17, 2021
1 parent 35aeefc commit e36f661
Show file tree
Hide file tree
Showing 4 changed files with 104 additions and 28 deletions.
16 changes: 11 additions & 5 deletions lib/kubetruth/etl.rb
Original file line number Diff line number Diff line change
Expand Up @@ -147,9 +147,12 @@ def apply_config_maps(param_groups)

begin
logger.debug { "Namespace '#{kapi.namespace}'" }
data = kapi.get_config_map(config_map_name)
resource = kapi.get_config_map(config_map_name)
data = resource.data.to_h
logger.debug("Config map for '#{config_map_name}': #{data.inspect}")
if param_hash != data.transform_keys! {|k| k.to_s }
if ! kapi.under_management?(resource)
logger.info "Skipping config map '#{config_map_name}' as it doesn't have the kubetruth label"
elsif param_hash != data.transform_keys! {|k| k.to_s }
logger.info "Updating config map '#{config_map_name}' with params: #{param_hash.inspect}"
kapi.update_config_map(config_map_name, param_hash)
else
Expand Down Expand Up @@ -186,9 +189,12 @@ def apply_secrets(param_groups)

begin
logger.debug { "Namespace '#{kapi.namespace}'" }
data = kapi.get_secret(secret_name)
logger.debug("Secret for '#{secret_name}': #{data}")
if param_hash != data.transform_keys! {|k| k.to_s }
resource = kapi.get_secret(secret_name)
data = kapi.secret_hash(resource)
logger.debug { "Secret keys for '#{secret_name}': #{data.transform_keys! {|k| k.to_s }}" }
if ! kapi.under_management?(resource)
logger.info "Skipping secret '#{secret_name}' as it doesn't have a label indicating it is under kubetruth management"
elsif param_hash != data.transform_keys! {|k| k.to_s }
logger.info "Updating secret '#{secret_name}' with params: #{param_hash.keys.inspect}"
kapi.update_secret(secret_name, param_hash)
else
Expand Down
19 changes: 13 additions & 6 deletions lib/kubetruth/kubeapi.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,13 +9,15 @@ class KubeApi

attr_accessor :namespace

MANAGED_LABEL_KEY = "app.kubernetes.io/managed-by"
MANAGED_LABEL_VALUE = "kubetruth"
def initialize(namespace: nil, token: nil, api_url: nil)
namespace_path = '/var/run/secrets/kubernetes.io/serviceaccount/namespace'
ca_path = '/var/run/secrets/kubernetes.io/serviceaccount/ca.crt'
token_path = '/var/run/secrets/kubernetes.io/serviceaccount/token'

@namespace = namespace || File.read(namespace_path).chomp
@labels = {"app.kubernetes.io/managed-by" => "kubetruth"}
@labels = {MANAGED_LABEL_KEY => MANAGED_LABEL_VALUE}

@auth_options = {}
if token
Expand Down Expand Up @@ -53,15 +55,17 @@ def ensure_namespace
end
end

def under_management?(resource)
resource.metadata.labels[MANAGED_LABEL_KEY] == MANAGED_LABEL_VALUE
end

def get_config_map_names
client.get_config_maps(namespace: namespace).collect(&:metadata).collect(&:name)
end

def get_config_map(name)
# TODO: Make it so kubetruth skips over resources that it didn't create?
# e.g. client.get_config_map(name, namespace, label_selector: "app.kubernetes.io/managed-by=kubetruth") ?
resource = client.get_config_map(name, namespace)
resource.data.to_h
resource
end

def create_config_map(name, data)
Expand Down Expand Up @@ -89,10 +93,13 @@ def get_secret_names
client.get_secrets(namespace: namespace).collect(&:metadata).collect(&:name)
end

def secret_hash(resource)
Hash[resource.data.to_h.collect {|k, v| [k, Base64.decode64(v)]}]
end

def get_secret(name)
resource = client.get_secret(name, namespace)
data = Hash[resource.data.to_h.collect {|k, v| [k, Base64.decode64(v)]}]
data
resource
end

def create_secret(name, data)
Expand Down
59 changes: 55 additions & 4 deletions spec/kubetruth/etl_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -239,7 +239,10 @@ def kubeapi(ns)
Parameter.new(key: "param2", value: "value2", secret: false)
]
}
expect(@kubeapi).to receive(:get_config_map).with("group1").and_return({oldparam: "oldvalue"})
resource = Kubeclient::Resource.new
resource.data = {oldparam: "oldvalue"}
expect(@kubeapi).to receive(:get_config_map).with("group1").and_return(resource)
expect(@kubeapi).to receive(:under_management?).with(resource).and_return(true)
expect(@kubeapi).to_not receive(:create_config_map)
expect(@kubeapi).to receive(:update_config_map).with("group1", {"param1" => "value1", "param2" => "value2"})
etl.apply_config_maps(param_groups)
Expand All @@ -253,12 +256,33 @@ def kubeapi(ns)
Parameter.new(key: "param2", value: "value2", secret: false)
]
}
expect(@kubeapi).to receive(:get_config_map).with("group1").and_return({param1: "value1", param2: "value2"})
resource = Kubeclient::Resource.new
resource.data = {param1: "value1", param2: "value2"}
expect(@kubeapi).to receive(:get_config_map).with("group1").and_return(resource)
expect(@kubeapi).to receive(:under_management?).with(resource).and_return(true)
expect(@kubeapi).to_not receive(:create_config_map)
expect(@kubeapi).to_not receive(:update_config_map)
etl.apply_config_maps(param_groups)
end

it "doesn't update config map if not under management" do
etl = described_class.new(init_args)
param_groups = {
{namespace: nil, name: "group1"} => [
Parameter.new(key: "param1", value: "value1", secret: false),
Parameter.new(key: "param2", value: "value2", secret: false)
]
}
resource = Kubeclient::Resource.new
resource.data = {oldparam: "oldvalue"}
expect(@kubeapi).to receive(:get_config_map).with("group1").and_return(resource)
expect(@kubeapi).to receive(:under_management?).with(resource).and_return(false)
expect(@kubeapi).to_not receive(:create_config_map)
expect(@kubeapi).to_not receive(:update_config_map)
etl.apply_config_maps(param_groups)
expect(Logging.contents).to match(/Skipping config map 'group1'/)
end

it "uses namespace for kube when supplied" do
etl = described_class.new(init_args.merge(namespace_template: "%{name}"))
param_groups = {
Expand Down Expand Up @@ -309,7 +333,11 @@ def kubeapi(ns)
Parameter.new(key: "param2", value: "value2", secret: true)
]
}
expect(@kubeapi).to receive(:get_secret).with("group1").and_return({oldparam: "oldvalue"})
resource = Kubeclient::Resource.new
resource.stringData = {oldparam: "oldvalue"}
expect(@kubeapi).to receive(:get_secret).with("group1").and_return(resource)
expect(@kubeapi).to receive(:under_management?).with(resource).and_return(true)
expect(@kubeapi).to receive(:secret_hash).with(resource).and_return({oldparam: "oldvalue"})
expect(@kubeapi).to_not receive(:create_secret)
expect(@kubeapi).to receive(:update_secret).with("group1", {"param1" => "value1", "param2" => "value2"})
etl.apply_secrets(param_groups)
Expand All @@ -323,10 +351,33 @@ def kubeapi(ns)
Parameter.new(key: "param2", value: "value2", secret: true)
]
}
expect(@kubeapi).to receive(:get_secret).with("group1").and_return({param1: "value1", param2: "value2"})
resource = Kubeclient::Resource.new
resource.stringData = {param1: "value1", param2: "value2"}
expect(@kubeapi).to receive(:get_secret).with("group1").and_return(resource)
expect(@kubeapi).to receive(:under_management?).with(resource).and_return(true)
expect(@kubeapi).to receive(:secret_hash).with(resource).and_return({param1: "value1", param2: "value2"})
expect(@kubeapi).to_not receive(:create_secret)
expect(@kubeapi).to_not receive(:update_secret)
etl.apply_secrets(param_groups)
end

it "doesn't update secret if not under management=" do
etl = described_class.new(init_args)
param_groups = {
{namespace: nil, name: "group1"} => [
Parameter.new(key: "param1", value: "value1", secret: true),
Parameter.new(key: "param2", value: "value2", secret: true)
]
}
resource = Kubeclient::Resource.new
resource.stringData = {oldparam: "oldvalue"}
expect(@kubeapi).to receive(:get_secret).with("group1").and_return(resource)
expect(@kubeapi).to receive(:under_management?).with(resource).and_return(false)
expect(@kubeapi).to receive(:secret_hash).with(resource).and_return({oldparam: "oldvalue"})
expect(@kubeapi).to_not receive(:create_secret)
expect(@kubeapi).to_not receive(:update_secret)
etl.apply_secrets(param_groups)
expect(Logging.contents).to match(/Skipping secret 'group1'/)
end

it "uses namespace for kube when supplied" do
Expand Down
38 changes: 25 additions & 13 deletions spec/kubetruth/kubeapi_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ def token; ""; end
expect { kapi.create_config_map("foo", {}) }.to raise_error(Kubeclient::ResourceNotFoundError, /namespaces.*not found/)
ns = kapi.ensure_namespace
kapi.create_config_map("foo", {bar: "baz"})
expect(kapi.get_config_map("foo")).to eq(bar: "baz")
expect(kapi.get_config_map("foo").data["bar"]).to eq("baz")
end

it "sets labels when creating namespace" do
Expand All @@ -86,9 +86,9 @@ def token; ""; end
expect { kubeapi.get_config_map("foo") }.to raise_error(Kubeclient::ResourceNotFoundError)
cm = kubeapi.create_config_map("foo", {bar: "baz"})
expect(kubeapi.get_config_map_names).to include("foo")
expect(kubeapi.get_config_map("foo")).to eq({bar: "baz"})
expect(kubeapi.get_config_map("foo").data["bar"]).to eq("baz")
kubeapi.update_config_map("foo", {bar: "bum"})
expect(kubeapi.get_config_map("foo")).to eq({bar: "bum"})
expect(kubeapi.get_config_map("foo").data["bar"]).to eq("bum")
kubeapi.delete_config_map("foo")
expect { kubeapi.get_config_map("foo") }.to raise_error(Kubeclient::ResourceNotFoundError)
end
Expand All @@ -103,18 +103,18 @@ def token; ""; end
expect { ns1_kapi.get_config_map("foo") }.to raise_error(Kubeclient::ResourceNotFoundError)
cm = ns1_kapi.create_config_map("foo", {bar: "baz"})
expect(ns1_kapi.get_config_map_names).to include("foo")
expect(ns1_kapi.get_config_map("foo")).to eq({bar: "baz"})
expect(ns1_kapi.get_config_map("foo").data["bar"]).to eq("baz")
ns1_kapi.update_config_map("foo", {bar: "bum"})
expect(ns1_kapi.get_config_map("foo")).to eq({bar: "bum"})
expect(ns1_kapi.get_config_map("foo").data["bar"]).to eq("bum")
ns1_kapi.delete_config_map("foo")
expect { ns1_kapi.get_config_map("foo") }.to raise_error(Kubeclient::ResourceNotFoundError)

expect { ns2_kapi.get_config_map("foo") }.to raise_error(Kubeclient::ResourceNotFoundError)
cm = ns2_kapi.create_config_map("foo", {bar: "baz"})
expect(ns2_kapi.get_config_map_names).to include("foo")
expect(ns2_kapi.get_config_map("foo")).to eq({bar: "baz"})
expect(ns2_kapi.get_config_map("foo").data["bar"]).to eq("baz")
ns2_kapi.update_config_map("foo", {bar: "bum"})
expect(ns2_kapi.get_config_map("foo")).to eq({bar: "bum"})
expect(ns2_kapi.get_config_map("foo").data["bar"]).to eq("bum")
ns2_kapi.delete_config_map("foo")
expect { ns2_kapi.get_config_map("foo") }.to raise_error(Kubeclient::ResourceNotFoundError)
end
Expand Down Expand Up @@ -144,9 +144,13 @@ def token; ""; end
expect { kubeapi.get_secret("foo") }.to raise_error(Kubeclient::ResourceNotFoundError)
secret = kubeapi.create_secret("foo", {bar: "baz"})
expect(kubeapi.get_secret_names).to include("foo")
expect(kubeapi.get_secret("foo")).to eq({bar: "baz"})
resource = kubeapi.get_secret("foo")
data = kubeapi.secret_hash(resource)
expect(data).to eq({bar: "baz"})
kubeapi.update_secret("foo", {bar: "bum"})
expect(kubeapi.get_secret("foo")).to eq({bar: "bum"})
resource = kubeapi.get_secret("foo")
data = kubeapi.secret_hash(resource)
expect(data).to eq({bar: "bum"})
kubeapi.delete_secret("foo")
expect { kubeapi.get_secret("foo") }.to raise_error(Kubeclient::ResourceNotFoundError)
end
Expand All @@ -161,18 +165,26 @@ def token; ""; end
expect { ns1_kapi.get_secret("foo") }.to raise_error(Kubeclient::ResourceNotFoundError)
secret = ns1_kapi.create_secret("foo", {bar: "baz"})
expect(ns1_kapi.get_secret_names).to include("foo")
expect(ns1_kapi.get_secret("foo")).to eq({bar: "baz"})
resource = ns1_kapi.get_secret("foo")
data = ns1_kapi.secret_hash(resource)
expect(data).to eq({bar: "baz"})
ns1_kapi.update_secret("foo", {bar: "bum"})
expect(ns1_kapi.get_secret("foo")).to eq({bar: "bum"})
resource = ns1_kapi.get_secret("foo")
data = ns1_kapi.secret_hash(resource)
expect(data).to eq({bar: "bum"})
ns1_kapi.delete_secret("foo")
expect { ns1_kapi.get_secret("foo") }.to raise_error(Kubeclient::ResourceNotFoundError)

expect { ns2_kapi.get_secret("foo") }.to raise_error(Kubeclient::ResourceNotFoundError)
secret = ns2_kapi.create_secret("foo", {bar: "baz"})
expect(ns2_kapi.get_secret_names).to include("foo")
expect(ns2_kapi.get_secret("foo")).to eq({bar: "baz"})
resource = ns2_kapi.get_secret("foo")
data = ns2_kapi.secret_hash(resource)
expect(data).to eq({bar: "baz"})
ns2_kapi.update_secret("foo", {bar: "bum"})
expect(ns2_kapi.get_secret("foo")).to eq({bar: "bum"})
resource = ns2_kapi.get_secret("foo")
data = ns2_kapi.secret_hash(resource)
expect(data).to eq({bar: "bum"})
ns2_kapi.delete_secret("foo")
expect { ns2_kapi.get_secret("foo") }.to raise_error(Kubeclient::ResourceNotFoundError)
end
Expand Down

0 comments on commit e36f661

Please sign in to comment.