github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/server/registration/registration.go (about) 1 // Copyright (c) 2017-2022, R.I. Pienaar and the Choria Project contributors 2 // 3 // SPDX-License-Identifier: Apache-2.0 4 5 package registration 6 7 import ( 8 "context" 9 "fmt" 10 "math/rand" 11 "sync" 12 "time" 13 14 "github.com/choria-io/go-choria/config" 15 "github.com/choria-io/go-choria/inter" 16 "github.com/choria-io/go-choria/internal/util" 17 "github.com/choria-io/go-choria/providers/registration" 18 "github.com/choria-io/go-choria/server/data" 19 20 "github.com/sirupsen/logrus" 21 ) 22 23 type ChoriaFramework interface { 24 NewMessage(payload []byte, agent string, collective string, msgType string, request inter.Message) (msg inter.Message, err error) 25 Configuration() *config.Config 26 } 27 28 // Registrator is a full managed registration plugin 29 type Registrator interface { 30 Init(cfg *config.Config, l *logrus.Entry) 31 StartRegistration(context.Context, *sync.WaitGroup, int, chan *data.RegistrationItem) 32 } 33 34 // RegistrationDataProvider is a provider for data that can be registered 35 // into a running server instance using AddRegistrationProvider() 36 type RegistrationDataProvider interface { 37 StartRegistration(context.Context, *sync.WaitGroup, int, chan *data.RegistrationItem) 38 } 39 40 // Connection provides the connection to the middleware 41 type Connection interface { 42 Publish(msg inter.Message) error 43 IsConnected() bool 44 } 45 46 // Manager of registration plugins 47 type Manager struct { 48 log *logrus.Entry 49 choria ChoriaFramework 50 cfg *config.Config 51 connector Connection 52 datac chan *data.RegistrationItem 53 si registration.ServerInfoSource 54 } 55 56 // New creates a new instance of the registration subsystem manager 57 func New(c ChoriaFramework, si registration.ServerInfoSource, conn Connection, logger *logrus.Entry) *Manager { 58 r := &Manager{ 59 log: logger.WithFields(logrus.Fields{"subsystem": "registration"}), 60 choria: c, 61 si: si, 62 cfg: c.Configuration(), 63 connector: conn, 64 datac: make(chan *data.RegistrationItem, 1), 65 } 66 67 return r 68 } 69 70 // Start initializes the fully managed registration plugins and start publishing 71 // their data 72 func (reg *Manager) Start(ctx context.Context, wg *sync.WaitGroup) error { 73 defer wg.Done() 74 75 if reg.cfg.RegistrationCollective == "" { 76 reg.cfg.RegistrationCollective = reg.cfg.MainCollective 77 } 78 79 var err error 80 var registrator Registrator 81 82 for _, rtype := range reg.cfg.Registration { 83 switch rtype { 84 case "": 85 return nil 86 case "file_content": 87 registrator, err = registration.NewFileContent(reg.cfg, reg.si, reg.log) 88 if err != nil { 89 return fmt.Errorf("cannot start File Content Registrator: %s", err) 90 } 91 92 case "inventory_content": 93 registrator, err = registration.NewInventoryContent(reg.cfg, reg.si, reg.log) 94 if err != nil { 95 return fmt.Errorf("cannot start File Content Registrator: %s", err) 96 } 97 98 default: 99 return fmt.Errorf("unknown registration plugin: %s", reg.cfg.Registration) 100 } 101 102 reg.log.Infof("Starting registration worker for %s", rtype) 103 err = reg.RegisterProvider(ctx, wg, registrator) 104 if err != nil { 105 reg.log.Errorf("Could not register registration worker for %s: %s", rtype, err) 106 } 107 } 108 109 return nil 110 } 111 112 // RegisterProvider creates a publisher for a new provider 113 func (reg *Manager) RegisterProvider(ctx context.Context, wg *sync.WaitGroup, provider RegistrationDataProvider) error { 114 wg.Add(1) 115 go reg.registrationWorker(ctx, wg, provider) 116 117 return nil 118 } 119 120 func (reg *Manager) registrationWorker(ctx context.Context, wg *sync.WaitGroup, registrator RegistrationDataProvider) { 121 defer wg.Done() 122 123 if reg.cfg.RegistrationSplay { 124 sleepTime := time.Duration(rand.Intn(reg.cfg.RegisterInterval)) * time.Second 125 reg.log.Infof("Sleeping %s seconds before first poll due to RegistrationSplay", sleepTime) 126 err := util.InterruptibleSleep(ctx, sleepTime) 127 if err != nil { 128 reg.log.Infof("Registration system exiting on shut down") 129 return 130 } 131 } 132 133 wg.Add(1) 134 go registrator.StartRegistration(ctx, wg, reg.cfg.RegisterInterval, reg.datac) 135 136 for { 137 select { 138 case msg := <-reg.datac: 139 reg.publish(msg) 140 case <-ctx.Done(): 141 reg.log.Infof("Registration system exiting on shut down") 142 return 143 } 144 } 145 } 146 147 func (reg *Manager) publish(rmsg *data.RegistrationItem) { 148 if rmsg == nil { 149 reg.log.Warnf("Received nil data from Registratoin Plugin, skipping") 150 return 151 } 152 153 if rmsg.Data == nil { 154 reg.log.Warnf("Received nil data from Registratoin Plugin, skipping") 155 return 156 } 157 158 if len(rmsg.Data) == 0 { 159 reg.log.Warnf("Received empty data from Registratoin Plugin, skipping") 160 return 161 } 162 163 if rmsg.TargetAgent == "" { 164 rmsg.TargetAgent = "registration" 165 } 166 167 msg, err := reg.choria.NewMessage(rmsg.Data, rmsg.TargetAgent, reg.cfg.RegistrationCollective, "request", nil) 168 if err != nil { 169 reg.log.Warnf("Could not create Message for registration data: %s", err) 170 return 171 } 172 173 msg.SetReplyTo("dev.null") 174 msg.SetCustomTarget(rmsg.Destination) 175 176 if reg.connector.IsConnected() { 177 err = reg.connector.Publish(msg) 178 if err != nil { 179 reg.log.Warnf("Could not publish registration Message: %s", err) 180 return 181 } 182 } else { 183 reg.log.Warnf("Skipping registration publish while not connected to the network") 184 } 185 }