github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/aagent/watchers/machineswatcher/machines_test.go (about) 1 // Copyright (c) 2021-2022, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package machines 6 7 import ( 8 "crypto/ed25519" 9 "crypto/rand" 10 "encoding/base64" 11 "encoding/hex" 12 "os" 13 "testing" 14 "time" 15 16 "github.com/choria-io/go-choria/aagent/model" 17 "github.com/golang/mock/gomock" 18 . "github.com/onsi/ginkgo/v2" 19 . "github.com/onsi/gomega" 20 ) 21 22 func TestMachine(t *testing.T) { 23 RegisterFailHandler(Fail) 24 RunSpecs(t, "AAgent/Watchers/MachinesWatcher") 25 } 26 27 var _ = Describe("AAgent/Watchers/MachinesWatcher", func() { 28 var ( 29 w *Watcher 30 machine *model.MockMachine 31 mockctl *gomock.Controller 32 td string 33 err error 34 ) 35 36 BeforeEach(func() { 37 td, err = os.MkdirTemp("", "") 38 Expect(err).ToNot(HaveOccurred()) 39 mockctl = gomock.NewController(GinkgoT()) 40 41 machine = model.NewMockMachine(mockctl) 42 machine.EXPECT().Directory().Return(td).AnyTimes() 43 machine.EXPECT().SignerKey().Return("").AnyTimes() 44 45 wi, err := New(machine, "machines", nil, "", "", "1m", time.Hour, map[string]any{ 46 "data_item": "spec", 47 }) 48 Expect(err).ToNot(HaveOccurred()) 49 w = wi.(*Watcher) 50 }) 51 52 AfterEach(func() { 53 mockctl.Finish() 54 os.RemoveAll(td) 55 }) 56 57 Describe("loadAndValidateData", func() { 58 var ( 59 data *Specification 60 pri ed25519.PrivateKey 61 pub ed25519.PublicKey 62 spec []byte 63 ) 64 65 BeforeEach(func() { 66 pub, pri, err = ed25519.GenerateKey(rand.Reader) 67 Expect(err).ToNot(HaveOccurred()) 68 spec = []byte("[]") 69 data = &Specification{ 70 Machines: []byte(base64.StdEncoding.EncodeToString(spec)), 71 } 72 data.Signature = hex.EncodeToString(ed25519.Sign(pri, spec)) 73 machine.EXPECT().DataGet(gomock.Eq("spec")).Return(data, true).AnyTimes() 74 }) 75 76 It("Should function without a signature", func() { 77 data.Signature = "" 78 machine.EXPECT().SignerKey().Return("").AnyTimes() 79 spec, err := w.loadAndValidateData() 80 Expect(err).ToNot(HaveOccurred()) 81 Expect(spec).ToNot(BeNil()) 82 }) 83 84 It("Should handle data with no signatures when signature is required", func() { 85 err = w.setProperties(map[string]any{ 86 "data_item": "spec", 87 "public_key": "x", 88 }) 89 Expect(err).ToNot(HaveOccurred()) 90 data.Signature = "" 91 machine.EXPECT().DataDelete(gomock.Eq("spec")) 92 machine.EXPECT().Errorf(gomock.Any(), gomock.Eq("No signature found in specification, removing data")) 93 spec, err := w.loadAndValidateData() 94 Expect(err).To(MatchError("invalid data_item")) 95 Expect(spec).To(BeNil()) 96 }) 97 98 It("Should handle data with corrupt signatures", func() { 99 err = w.setProperties(map[string]any{ 100 "data_item": "spec", 101 "public_key": hex.EncodeToString(pub), 102 }) 103 Expect(err).ToNot(HaveOccurred()) 104 data.Signature = "x" 105 106 machine.EXPECT().DataDelete(gomock.Eq("spec")) 107 machine.EXPECT().Errorf(gomock.Any(), gomock.Eq("invalid signature string, removing data %s: %s"), gomock.Eq("spec"), gomock.Any()) 108 spec, err := w.loadAndValidateData() 109 Expect(err).To(MatchError("invalid data_item")) 110 Expect(spec).To(BeNil()) 111 }) 112 113 It("Should handle data with invalid signatures", func() { 114 err = w.setProperties(map[string]any{ 115 "data_item": "spec", 116 "public_key": hex.EncodeToString(pub), 117 }) 118 Expect(err).ToNot(HaveOccurred()) 119 data.Signature = hex.EncodeToString(ed25519.Sign(pri, []byte("wrong"))) 120 121 machine.EXPECT().DataDelete(gomock.Eq("spec")) 122 machine.EXPECT().Errorf(gomock.Any(), gomock.Eq("Signature in data_item %s did not verify using configured public key '%s', removing data"), gomock.Eq("spec"), gomock.Eq(hex.EncodeToString(pub))) 123 spec, err := w.loadAndValidateData() 124 Expect(err).To(MatchError("invalid data_item")) 125 Expect(spec).To(BeNil()) 126 }) 127 128 It("Should allow overrides from config", func() { 129 machine := model.NewMockMachine(mockctl) 130 machine.EXPECT().Directory().Return(td).AnyTimes() 131 machine.EXPECT().SignerKey().Return(hex.EncodeToString(pub)).AnyTimes() 132 machine.EXPECT().DataGet(gomock.Eq("spec")).Return(data, true).AnyTimes() 133 134 wi, err := New(machine, "machines", nil, "", "", "1m", time.Hour, map[string]any{ 135 "data_item": "spec", 136 "public_key": "other", 137 }) 138 Expect(err).ToNot(HaveOccurred()) 139 w = wi.(*Watcher) 140 141 Expect(w.properties.PublicKey).To(Equal(hex.EncodeToString(pub))) 142 143 spec, err := w.loadAndValidateData() 144 Expect(err).ToNot(HaveOccurred()) 145 Expect(spec).To(Equal([]byte("[]"))) 146 }) 147 148 It("Should handle valid signatures", func() { 149 err = w.setProperties(map[string]any{ 150 "data_item": "spec", 151 "public_key": hex.EncodeToString(pub), 152 }) 153 Expect(err).ToNot(HaveOccurred()) 154 155 spec, err := w.loadAndValidateData() 156 Expect(err).ToNot(HaveOccurred()) 157 Expect(spec).To(Equal([]byte("[]"))) 158 }) 159 }) 160 })