diff --git a/lib/kubetruth/etl.rb b/lib/kubetruth/etl.rb index 8114bef..6953359 100644 --- a/lib/kubetruth/etl.rb +++ b/lib/kubetruth/etl.rb @@ -16,6 +16,7 @@ class ETL def initialize(dry_run: false) @dry_run = dry_run + @wrote_crds = false end def kubeapi @@ -28,6 +29,12 @@ def interruptible_sleep(interval) end def watch_crds_to_interrupt(&block) + if @wrote_crds + logger.info {"Skipping Poller sleep to handle the recent application of kubetruth CRDs"} + @wrote_crds = false + return + end + begin begin watcher = kubeapi.watch_project_mappings @@ -256,11 +263,17 @@ def kube_apply(parsed_yml) # work as there a bunch of fields we don't control, so we just rely on # the server-side apply to do the right thing. logger.info "Updating kubernetes resource #{ident}" - kubeapi.apply_resource(parsed_yml) unless @dry_run + unless @dry_run + 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}" - kubeapi.apply_resource(parsed_yml) unless @dry_run + unless @dry_run + kubeapi.apply_resource(parsed_yml) + @wrote_crds = true if kind == "ProjectMapping" + end end end diff --git a/spec/kubetruth/etl_spec.rb b/spec/kubetruth/etl_spec.rb index 31035f6..71d7f60 100644 --- a/spec/kubetruth/etl_spec.rb +++ b/spec/kubetruth/etl_spec.rb @@ -131,6 +131,28 @@ class ForceExit < Exception; end end + it "skips next sleep when crds get written" do + watcher = double() + expect(@kubeapi).to receive(:watch_project_mappings).and_return(watcher) + allow(watcher).to receive(:each) + allow(watcher).to receive(:finish) + expect(etl).to receive(:interruptible_sleep).and_raise(ForceExit) + + count = 0 + begin + etl.with_polling(0.2) do + if count == 0 + etl.instance_variable_set(:@wrote_crds, true) + end + count += 1 + end + rescue ForceExit + end + + expect(etl.instance_variable_get(:@wrote_crds)).to eq(false) + expect(count).to eq(2) + end + end describe "#load_config" do @@ -246,6 +268,7 @@ class ForceExit < Exception; end expect(@kubeapi).to receive(:apply_resource).with(parsed_yml) etl.kube_apply(parsed_yml) expect(Logging.contents).to match(/Creating kubernetes resource/) + expect(etl.instance_variable_get(:@wrote_crds)).to eq(false) end it "calls to kube to update existing resource" do @@ -265,6 +288,7 @@ class ForceExit < Exception; end expect(@kubeapi).to receive(:apply_resource).with(parsed_yml) etl.kube_apply(parsed_yml) expect(Logging.contents).to match(/Updating kubernetes resource/) + expect(etl.instance_variable_get(:@wrote_crds)).to eq(false) end it "skips call to kube for existing resource not under management" do @@ -324,6 +348,49 @@ class ForceExit < Exception; end expect(Logging.contents).to match(/Creating kubernetes resource/) end + it "registers project mapping writes on new resource" do + resource_yml = <<~EOF + apiVersion: v1 + kind: ProjectMapping + metadata: + name: "pm1" + spec: + "scope": "override" + EOF + parsed_yml = YAML.load(resource_yml) + resource = Kubeclient::Resource.new(parsed_yml.merge(metadata: {resourceVersion: "123"})) + + expect(etl.instance_variable_get(:@wrote_crds)).to eq(false) + expect(@kubeapi).to receive(:get_resource).and_raise(Kubeclient::ResourceNotFoundError.new(1, "", 2)) + expect(@kubeapi).to receive(:apply_resource).and_return(resource) + etl.kube_apply(parsed_yml) + expect(etl.instance_variable_get(:@wrote_crds)).to eq(true) + end + + it "registers project mapping writes on updated resource" do + resource_yml = <<~EOF + apiVersion: v1 + kind: ProjectMapping + metadata: + name: "pm1" + spec: + "scope": "override" + EOF + parsed_yml = YAML.load(resource_yml) + resource = Kubeclient::Resource.new(parsed_yml.merge(metadata: {resourceVersion: "123"})) + + expect(etl.instance_variable_get(:@wrote_crds)).to eq(false) + expect(@kubeapi).to receive(:get_resource).and_return(resource) + expect(@kubeapi).to receive(:apply_resource).and_return(resource) + etl.kube_apply(parsed_yml) + expect(etl.instance_variable_get(:@wrote_crds)).to eq(false) + + resource.metadata.resourceVersion = "456" + expect(@kubeapi).to receive(:apply_resource).and_return(resource) + etl.kube_apply(parsed_yml) + expect(etl.instance_variable_get(:@wrote_crds)).to eq(true) + end + end describe "#apply" do