github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/providers/agent/mcorpc/external/provider_test.go (about) 1 // Copyright (c) 2020-2021, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package external 6 7 import ( 8 "bytes" 9 "context" 10 "io" 11 "os" 12 "path/filepath" 13 14 "github.com/golang/mock/gomock" 15 . "github.com/onsi/ginkgo/v2" 16 . "github.com/onsi/gomega" 17 "github.com/onsi/gomega/gbytes" 18 "github.com/sirupsen/logrus" 19 20 "github.com/choria-io/go-choria/build" 21 "github.com/choria-io/go-choria/config" 22 imock "github.com/choria-io/go-choria/inter/imocks" 23 addl "github.com/choria-io/go-choria/providers/agent/mcorpc/ddl/agent" 24 "github.com/choria-io/go-choria/server" 25 "github.com/choria-io/go-choria/server/agents" 26 ) 27 28 var _ = Describe("McoRPC/External", func() { 29 var ( 30 mockctl *gomock.Controller 31 fw *imock.MockFramework 32 mgr *MockAgentManager 33 conn *imock.MockConnector 34 cfg *config.Config 35 prov *Provider 36 ctx context.Context 37 cancel context.CancelFunc 38 ) 39 40 BeforeEach(func() { 41 build.TLS = "false" 42 43 mockctl = gomock.NewController(GinkgoT()) 44 45 buf := gbytes.NewBuffer() 46 fw, cfg = imock.NewFrameworkForTests(mockctl, buf) 47 fw.EXPECT().Configuration().Return(cfg).AnyTimes() 48 49 conn = imock.NewMockConnector(mockctl) 50 51 mgr = NewMockAgentManager(mockctl) 52 mgr.EXPECT().Choria().Return(fw).AnyTimes() 53 mgr.EXPECT().Logger().Return(fw.Logger("ginkgo")).AnyTimes() 54 55 lib, err := filepath.Abs("testdata") 56 Expect(err).ToNot(HaveOccurred()) 57 58 cfg.Choria.RubyLibdir = []string{lib} 59 60 prov = &Provider{ 61 cfg: cfg, 62 log: fw.Logger("x"), 63 agents: []*addl.DDL{}, 64 paths: make(map[string]string), 65 } 66 prov.log.Logger.SetLevel(logrus.DebugLevel) 67 68 ctx, cancel = context.WithCancel(context.Background()) 69 70 DeferCleanup(func() { 71 cancel() 72 mockctl.Finish() 73 }) 74 }) 75 76 Describe("reconcileAgents", func() { 77 var td string 78 var agentDir string 79 var err error 80 81 BeforeEach(func() { 82 td, err = os.MkdirTemp("", "") 83 Expect(err).ToNot(HaveOccurred()) 84 85 agentDir = filepath.Join(td, "mcollective", "agent") 86 87 Expect(os.MkdirAll(agentDir, 0700)).To(Succeed()) 88 89 DeferCleanup(func() { os.RemoveAll(td) }) 90 }) 91 92 copyAgentFile := func(f string) { 93 src, err := os.Open(filepath.Join("testdata", "mcollective", "agent", f)) 94 Expect(err).ToNot(HaveOccurred()) 95 defer src.Close() 96 97 dst, err := os.Create(filepath.Join(agentDir, f)) 98 Expect(err).ToNot(HaveOccurred()) 99 defer dst.Close() 100 101 _, err = io.Copy(dst, src) 102 Expect(err).ToNot(HaveOccurred()) 103 } 104 105 It("Should register new agents", func() { 106 mgr.EXPECT().RegisterAgent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(3) 107 Expect(prov.agents).To(BeEmpty()) 108 Expect(prov.reconcileAgents(ctx, mgr, conn)).To(Succeed()) 109 Expect(prov.agents).To(HaveLen(3)) 110 }) 111 112 It("Should upgrade changed agents", func() { 113 fileChangeGrace = 0 114 115 copyAgentFile("one.json") 116 prov.cfg.Choria.RubyLibdir = []string{td} 117 mgr.EXPECT().RegisterAgent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() 118 119 // load what we have 120 Expect(prov.agents).To(BeEmpty()) 121 Expect(prov.reconcileAgents(ctx, mgr, conn)).To(Succeed()) 122 Expect(prov.agents).To(HaveLen(1)) 123 Expect(prov.paths).To(HaveLen(1)) 124 125 ddlb, err := os.ReadFile(filepath.Join(agentDir, "one.json")) 126 Expect(err).ToNot(HaveOccurred()) 127 ddlb = bytes.Replace(ddlb, []byte(`"version": "5.0.0"`), []byte(`"version": "6.0.0"`), 1) 128 Expect(os.WriteFile(filepath.Join(agentDir, "one.json"), ddlb, 0700)).To(Succeed()) 129 130 mgr.EXPECT().ReplaceAgent("one", gomock.Any()).DoAndReturn(func(name string, agent agents.Agent) error { 131 Expect(agent.Metadata().Version).To(Equal("6.0.0")) 132 133 return nil 134 }).Times(1) 135 136 Expect(prov.reconcileAgents(ctx, mgr, conn)).To(Succeed()) 137 Expect(prov.agents).To(HaveLen(1)) 138 Expect(prov.paths).To(HaveLen(1)) 139 Expect(prov.agents[0].Metadata.Version).To(Equal("6.0.0")) 140 }) 141 142 It("Should remove orphaned agents", func() { 143 copyAgentFile("one.json") 144 copyAgentFile("go_agent.json") 145 prov.cfg.Choria.RubyLibdir = []string{td} 146 mgr.EXPECT().RegisterAgent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).Times(2) 147 148 Expect(prov.reconcileAgents(ctx, mgr, conn)).To(Succeed()) 149 Expect(prov.agents).To(HaveLen(2)) 150 151 Expect(os.Remove(filepath.Join(agentDir, "one.json"))).To(Succeed()) 152 153 mgr.EXPECT().UnregisterAgent("one", conn).Times(1) 154 Expect(prov.reconcileAgents(ctx, mgr, conn)).To(Succeed()) 155 Expect(prov.agents).To(HaveLen(1)) 156 Expect(prov.agents[0].Metadata.Name).To(Equal("echo")) 157 }) 158 159 It("Should work in sequence", func() { 160 fileChangeGrace = 0 161 prov.cfg.Choria.RubyLibdir = []string{td} 162 163 mgr.EXPECT().RegisterAgent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Times(3) 164 mgr.EXPECT().UnregisterAgent(gomock.Any(), conn).Times(2) 165 mgr.EXPECT().ReplaceAgent(gomock.Any(), gomock.Any()).Times(1) 166 167 Expect(prov.reconcileAgents(ctx, mgr, conn)).To(Succeed()) 168 Expect(prov.agents).To(BeEmpty()) 169 170 copyAgentFile("one.json") 171 Expect(prov.reconcileAgents(ctx, mgr, conn)).To(Succeed()) 172 Expect(prov.agents).To(HaveLen(1)) 173 Expect(prov.agents[0].Metadata.Name).To(Equal("one")) 174 175 copyAgentFile("go_agent.json") 176 Expect(prov.reconcileAgents(ctx, mgr, conn)).To(Succeed()) 177 Expect(prov.agents).To(HaveLen(2)) 178 Expect(prov.agents[0].Metadata.Name).To(Equal("one")) 179 Expect(prov.agents[1].Metadata.Name).To(Equal("echo")) 180 181 Expect(os.Remove(filepath.Join(agentDir, "one.json"))).To(Succeed()) 182 Expect(prov.reconcileAgents(ctx, mgr, conn)).To(Succeed()) 183 Expect(prov.agents).To(HaveLen(1)) 184 Expect(prov.agents[0].Metadata.Name).To(Equal("echo")) 185 186 ddlb, err := os.ReadFile(filepath.Join(agentDir, "go_agent.json")) 187 Expect(err).ToNot(HaveOccurred()) 188 ddlb = bytes.Replace(ddlb, []byte(`"version": "1.0.0"`), []byte(`"version": "6.0.0"`), 1) 189 Expect(os.WriteFile(filepath.Join(agentDir, "go_agent.json"), ddlb, 0700)).To(Succeed()) 190 Expect(prov.reconcileAgents(ctx, mgr, conn)).To(Succeed()) 191 Expect(prov.agents).To(HaveLen(1)) 192 Expect(prov.agents[0].Metadata.Name).To(Equal("echo")) 193 Expect(prov.agents[0].Metadata.Version).To(Equal("6.0.0")) 194 195 copyAgentFile("one.json") 196 Expect(prov.reconcileAgents(ctx, mgr, conn)).To(Succeed()) 197 Expect(prov.agents).To(HaveLen(2)) 198 Expect(prov.agents[0].Metadata.Name).To(Equal("echo")) 199 Expect(prov.agents[1].Metadata.Name).To(Equal("one")) 200 201 Expect(os.Remove(filepath.Join(agentDir, "go_agent.json"))).To(Succeed()) 202 Expect(prov.reconcileAgents(ctx, mgr, conn)).To(Succeed()) 203 Expect(prov.agents).To(HaveLen(1)) 204 Expect(prov.agents[0].Metadata.Name).To(Equal("one")) 205 }) 206 }) 207 208 Describe("Agents", func() { 209 It("Should return all the agent ddls", func() { 210 mgr.EXPECT().RegisterAgent(gomock.Any(), gomock.Any(), gomock.Any(), gomock.Any()).Return(nil).AnyTimes() 211 Expect(prov.reconcileAgents(ctx, mgr, conn)).To(Succeed()) 212 213 agents := prov.Agents() 214 Expect(prov.agents).To(HaveLen(3)) 215 Expect(prov.agents[0].Metadata.Name).To(Equal("echo")) 216 Expect(prov.agents[1].Metadata.Name).To(Equal("one")) 217 Expect(prov.agents[2].Metadata.Name).To(Equal("three")) 218 Expect(agents).To(Equal(prov.agents)) 219 220 Expect(prov.paths).To(HaveLen(3)) 221 }) 222 }) 223 224 Describe("shouldLoadAgent", func() { 225 It("Should correctly allow or deny agents", func() { 226 Expect(shouldLoadAgent("choria_util")).To(BeFalse()) 227 Expect(shouldLoadAgent("ginkgo")).To(BeTrue()) 228 }) 229 }) 230 231 Describe("Plugin", func() { 232 It("Should be a valid AgentProvider", func() { 233 p := server.AgentProvider(prov) 234 Expect(p).ToNot(BeNil()) 235 }) 236 }) 237 })