diff --git a/lib/kubetruth/etl.rb b/lib/kubetruth/etl.rb index 57abaf8..1809978 100644 --- a/lib/kubetruth/etl.rb +++ b/lib/kubetruth/etl.rb @@ -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 @@ -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 diff --git a/lib/kubetruth/kubeapi.rb b/lib/kubetruth/kubeapi.rb index 20e6d0d..2961e75 100644 --- a/lib/kubetruth/kubeapi.rb +++ b/lib/kubetruth/kubeapi.rb @@ -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 @@ -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) @@ -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) diff --git a/spec/kubetruth/etl_spec.rb b/spec/kubetruth/etl_spec.rb index 7553114..f22e8b7 100644 --- a/spec/kubetruth/etl_spec.rb +++ b/spec/kubetruth/etl_spec.rb @@ -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) @@ -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 = { @@ -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) @@ -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 diff --git a/spec/kubetruth/kubeapi_spec.rb b/spec/kubetruth/kubeapi_spec.rb index 2c1e821..93b64c1 100644 --- a/spec/kubetruth/kubeapi_spec.rb +++ b/spec/kubetruth/kubeapi_spec.rb @@ -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 @@ -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 @@ -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 @@ -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 @@ -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