From fd83ec48b9f08d91ab6ac02f0bd0bbb6c24f0c31 Mon Sep 17 00:00:00 2001 From: Vincent Janelle Date: Thu, 2 May 2024 15:52:39 -0700 Subject: [PATCH] (#2169) Pass choria federations to external discovery agent --- config/config.go | 1 - inter/imocks/util.go | 16 +- providers/discovery/external/external.go | 2 +- providers/discovery/external/external_test.go | 138 ++++++++++++------ providers/discovery/external/testdata/good.rb | 2 +- .../external/testdata/good_with_federation.rb | 43 ++++++ 6 files changed, 153 insertions(+), 49 deletions(-) create mode 100755 providers/discovery/external/testdata/good_with_federation.rb diff --git a/config/config.go b/config/config.go index 55aba011..973d8459 100644 --- a/config/config.go +++ b/config/config.go @@ -255,7 +255,6 @@ func NewConfigForTests() *Config { c.DisableSecurityProviderVerify = true c.LogFile = "discard" c.RPCAuthorization = false - c.Choria.FederationCollectives = []string{"alpha", "beta"} return c } diff --git a/inter/imocks/util.go b/inter/imocks/util.go index 8616fadf..426e88f2 100644 --- a/inter/imocks/util.go +++ b/inter/imocks/util.go @@ -76,6 +76,12 @@ func WithDDLFiles(kind string, plugin string, path string) fwMockOption { } } +func WithFederations(federations []string) fwMockOption { + return func(o *fwMockOpts) { + o.cfg.Choria.FederationCollectives = federations + } +} + func NewFrameworkForTests(ctrl *gomock.Controller, logWriter io.Writer, opts ...fwMockOption) (*MockFramework, *config.Config) { mopts := &fwMockOpts{ cfg: config.NewConfigForTests(), @@ -96,7 +102,15 @@ func NewFrameworkForTests(ctrl *gomock.Controller, logWriter io.Writer, opts ... fw.EXPECT().Configuration().Return(mopts.cfg).AnyTimes() fw.EXPECT().Logger(gomock.AssignableToTypeOf("")).Return(logrus.NewEntry(logger)).AnyTimes() fw.EXPECT().NewRequestID().Return(util.RandomHexString(), nil).AnyTimes() - fw.EXPECT().FederationCollectives().Return([]string{}).AnyTimes() + fw.EXPECT().FederationCollectives().DoAndReturn( + func() []string { + retval := strings.Split(os.Getenv("CHORIA_FED_COLLECTIVE"), ",") + if retval[0] == "" { + return []string{} + } else { + return retval + } + }).AnyTimes() fw.EXPECT().HasCollective(gomock.AssignableToTypeOf("")).DoAndReturn(func(c string) bool { for _, collective := range fw.Configuration().Collectives { if c == collective { diff --git a/providers/discovery/external/external.go b/providers/discovery/external/external.go index 848827cf..934fda13 100644 --- a/providers/discovery/external/external.go +++ b/providers/discovery/external/external.go @@ -71,7 +71,7 @@ func (e *External) Discover(ctx context.Context, opts ...DiscoverOption) (n []st collective: e.fw.Configuration().MainCollective, timeout: e.timeout, command: e.fw.Configuration().Choria.ExternalDiscoveryCommand, - federations: e.fw.Configuration().Choria.FederationCollectives, + federations: e.fw.FederationCollectives(), do: make(map[string]string), } diff --git a/providers/discovery/external/external_test.go b/providers/discovery/external/external_test.go index aacb68c7..4b611a74 100644 --- a/providers/discovery/external/external_test.go +++ b/providers/discovery/external/external_test.go @@ -34,62 +34,110 @@ var _ = Describe("External", func() { e *External ) - BeforeEach(func() { - mockctl = gomock.NewController(GinkgoT()) - fw, cfg = imock.NewFrameworkForTests(mockctl, GinkgoWriter) - cfg.Collectives = []string{"mcollective", "test"} + Context("command without federation", func() { + BeforeEach(func() { + mockctl = gomock.NewController(GinkgoT()) + fw, cfg = imock.NewFrameworkForTests(mockctl, GinkgoWriter) + cfg.Collectives = []string{"mcollective", "test"} - e = New(fw) - }) + e = New(fw) + }) + + AfterEach(func() { + mockctl.Finish() + }) + + Describe("New", func() { + It("Should initialize timeout to default", func() { + Expect(e.timeout).To(Equal(2 * time.Second)) + cfg.DiscoveryTimeout = 100 + e = New(fw) + Expect(e.timeout).To(Equal(100 * time.Second)) + }) + }) + + Describe("Discover", func() { + wd, _ := os.Getwd() + var f *protocol.Filter + BeforeEach(func() { + if runtime.GOOS == "windows" { + Skip("not tested on windows") + } - AfterEach(func() { - mockctl.Finish() + f = protocol.NewFilter() + f.AddAgentFilter("rpcutil") + err := f.AddFactFilter("country", "==", "mt") + Expect(err).ToNot(HaveOccurred()) + }) + It("Should request and return discovered nodes", func() { + cfg.Choria.ExternalDiscoveryCommand = filepath.Join(wd, "testdata/good.rb") + nodes, err := e.Discover(context.Background(), Filter(f), DiscoveryOptions(map[string]string{"foo": "bar"})) + Expect(err).ToNot(HaveOccurred()) + Expect(nodes).To(Equal([]string{"one", "two"})) + + cfg.Choria.ExternalDiscoveryCommand = filepath.Join(wd, "testdata/good_with_argument.rb") + " discover --test" + nodes, err = e.Discover(context.Background(), Filter(f), DiscoveryOptions(map[string]string{"foo": "bar"})) + Expect(err).ToNot(HaveOccurred()) + Expect(nodes).To(Equal([]string{"one", "two"})) + }) + + It("Should support command overrides via options", func() { + if runtime.GOOS == "windows" { + Skip("not tested on windows") + } + + cfg.Choria.ExternalDiscoveryCommand = filepath.Join(wd, "testdata/missing.rb") + cmd := filepath.Join(wd, "testdata/good_with_argument.rb") + " discover --test" + nodes, err := e.Discover(context.Background(), Filter(f), DiscoveryOptions(map[string]string{"command": cmd, "foo": "bar"})) + Expect(err).ToNot(HaveOccurred()) + Expect(nodes).To(Equal([]string{"one", "two"})) + }) + }) }) + Context("With federation", func() { + BeforeEach(func() { + mockctl = gomock.NewController(GinkgoT()) + fw, cfg = imock.NewFrameworkForTests(mockctl, GinkgoWriter, imock.WithFederations([]string{"alpha", "beta"})) + cfg.Collectives = []string{"mcollective", "test"} - Describe("New", func() { - It("Should initialize timeout to default", func() { - Expect(e.timeout).To(Equal(2 * time.Second)) - cfg.DiscoveryTimeout = 100 e = New(fw) - Expect(e.timeout).To(Equal(100 * time.Second)) }) - }) - Describe("Discover", func() { - wd, _ := os.Getwd() - var f *protocol.Filter - BeforeEach(func() { - if runtime.GOOS == "windows" { - Skip("not tested on windows") - } - - f = protocol.NewFilter() - f.AddAgentFilter("rpcutil") - err := f.AddFactFilter("country", "==", "mt") - Expect(err).ToNot(HaveOccurred()) + AfterEach(func() { + mockctl.Finish() }) - It("Should request and return discovered nodes", func() { - cfg.Choria.ExternalDiscoveryCommand = filepath.Join(wd, "testdata/good.rb") - nodes, err := e.Discover(context.Background(), Filter(f), DiscoveryOptions(map[string]string{"foo": "bar"})) - Expect(err).ToNot(HaveOccurred()) - Expect(nodes).To(Equal([]string{"one", "two"})) - - cfg.Choria.ExternalDiscoveryCommand = filepath.Join(wd, "testdata/good_with_argument.rb") + " discover --test" - nodes, err = e.Discover(context.Background(), Filter(f), DiscoveryOptions(map[string]string{"foo": "bar"})) - Expect(err).ToNot(HaveOccurred()) - Expect(nodes).To(Equal([]string{"one", "two"})) + + Describe("New", func() { + It("Should initialize timeout to default", func() { + Expect(e.timeout).To(Equal(2 * time.Second)) + cfg.DiscoveryTimeout = 100 + e = New(fw) + Expect(e.timeout).To(Equal(100 * time.Second)) + }) }) - It("Should support command overrides via options", func() { - if runtime.GOOS == "windows" { - Skip("not tested on windows") - } + Describe("Discover", func() { + wd, _ := os.Getwd() + var f *protocol.Filter + BeforeEach(func() { + if runtime.GOOS == "windows" { + Skip("not tested on windows") + } + err := os.Setenv("CHORIA_FED_COLLECTIVE", "alpha,beta") + Expect(err).ToNot(HaveOccurred()) - cfg.Choria.ExternalDiscoveryCommand = filepath.Join(wd, "testdata/missing.rb") - cmd := filepath.Join(wd, "testdata/good_with_argument.rb") + " discover --test" - nodes, err := e.Discover(context.Background(), Filter(f), DiscoveryOptions(map[string]string{"command": cmd, "foo": "bar"})) - Expect(err).ToNot(HaveOccurred()) - Expect(nodes).To(Equal([]string{"one", "two"})) + f = protocol.NewFilter() + f.AddAgentFilter("rpcutil") + err = f.AddFactFilter("country", "==", "mt") + Expect(err).ToNot(HaveOccurred()) + }) + It("Should request and return discovered nodes", func() { + cfg.Choria.ExternalDiscoveryCommand = filepath.Join(wd, "testdata/good_with_federation.rb") + nodes, err := e.Discover(context.Background(), Filter(f), DiscoveryOptions(map[string]string{"foo": "bar"})) + Expect(err).ToNot(HaveOccurred()) + Expect(nodes).To(Equal([]string{"one", "two"})) + }) }) + }) }) diff --git a/providers/discovery/external/testdata/good.rb b/providers/discovery/external/testdata/good.rb index 0c18670c..63ec4e8c 100755 --- a/providers/discovery/external/testdata/good.rb +++ b/providers/discovery/external/testdata/good.rb @@ -30,7 +30,7 @@ def write_output(output) "identity" => [] }, "collective" => "ginkgo", - "federations" => ["alpha", "beta"], + "federations" => [], "timeout" => 2, } diff --git a/providers/discovery/external/testdata/good_with_federation.rb b/providers/discovery/external/testdata/good_with_federation.rb new file mode 100755 index 00000000..84d73f72 --- /dev/null +++ b/providers/discovery/external/testdata/good_with_federation.rb @@ -0,0 +1,43 @@ +#!/usr/bin/env ruby + +require "json" +require "pp" + +def write_output(output) + File.open(ENV["CHORIA_EXTERNAL_REPLY"], "w") {|f| + f.puts(output.to_json) + } + exit +end + +if ENV["CHORIA_EXTERNAL_PROTOCOL"] != "io.choria.choria.discovery.v1.external_request" + write_output({"error" => "invalid protocol"}) + exit +end + +request = JSON.parse(File.read(ENV["CHORIA_EXTERNAL_REQUEST"])) +expected = { + "$schema" => "https://choria.io/schemas/choria/discovery/v1/external_request.json", + "options" => { + "foo" => "bar" + }, + "protocol" => "io.choria.choria.discovery.v1.external_request", + "filter" => { + "fact" => [{"fact" => "country", "operator"=>"==","value"=>"mt"}], + "cf_class"=>[], + "agent" => ["rpcutil"], + "compound" => [], + "identity" => [] + }, + "collective" => "ginkgo", + "federations" => ["alpha", "beta"], + "timeout" => 2, +} + +if request != expected + write_output({"error" => "invalid filter received: " + (request.to_a - expected.to_a).pretty_inspect}) + + # write_output({"error" => "invalid filter received: "+request.pretty_inspect}) +else + write_output({"protocol" => "io.choria.choria.discovery.v1.external_reply", "nodes" => ["one","two"]}) +end