github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/providers/registration/inventory_content.go (about) 1 // Copyright (c) 2021, 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 "encoding/json" 10 "fmt" 11 "math/rand" 12 "sync" 13 "time" 14 15 "github.com/choria-io/go-choria/internal/util" 16 "github.com/sirupsen/logrus" 17 18 "github.com/choria-io/go-choria/config" 19 "github.com/choria-io/go-choria/server/agents" 20 "github.com/choria-io/go-choria/server/data" 21 "github.com/choria-io/go-choria/statistics" 22 ) 23 24 // InventoryContent is a fully managed registration plugin for the choria server instance 25 // it reads the server inventory and publishing it to the collective regularly 26 type InventoryContent struct { 27 c *config.Config 28 log *logrus.Entry 29 si ServerInfoSource 30 } 31 32 const inventoryContentProtocol = "choria:registration:inventorycontent:1" 33 34 type InventoryData struct { 35 Agents []agents.Metadata `json:"agents"` 36 Classes []string `json:"classes"` 37 Facts json.RawMessage `json:"facts"` 38 Status *statistics.InstanceStatus `json:"status"` 39 Collectives []string `json:"collectives"` 40 BuildInfo *InventoryBuildInfo `json:"build_info"` 41 AutoAgents []*InventoryMachineState `json:"machines"` 42 } 43 44 type InventoryBuildInfo struct { 45 Version string `json:"version"` 46 SHA string `json:"sha"` 47 } 48 49 type InventoryContentMessage struct { 50 Protocol string `json:"protocol"` 51 Content json.RawMessage `json:"content,omitempty"` 52 ZContent []byte `json:"zcontent,omitempty"` 53 } 54 55 type InventoryMachineState struct { 56 Name string `json:"name"` 57 Version string `json:"version"` 58 State string `json:"state"` 59 } 60 61 // NewInventoryContent creates a new fully managed registration plugin instance 62 func NewInventoryContent(c *config.Config, si ServerInfoSource, logger *logrus.Entry) (*InventoryContent, error) { 63 reg := &InventoryContent{si: si} 64 65 reg.Init(c, logger) 66 67 return reg, nil 68 } 69 70 // Init sets up the plugin 71 func (ic *InventoryContent) Init(c *config.Config, logger *logrus.Entry) { 72 ic.c = c 73 ic.log = logger.WithFields(logrus.Fields{"registration": "inventory"}) 74 75 ic.log.Infof("Configured Inventory Registration") 76 } 77 78 // StartRegistration starts stats a publishing loop 79 func (ic *InventoryContent) StartRegistration(ctx context.Context, wg *sync.WaitGroup, interval int, output chan *data.RegistrationItem) { 80 defer wg.Done() 81 82 delay := time.Duration(rand.Intn(4)+1) * time.Second 83 ic.log.Infof("Sleeping %v before first registration publish", delay) 84 err := util.InterruptibleSleep(ctx, delay) 85 if err != nil { 86 return 87 } 88 89 err = ic.publish(output) 90 if err != nil { 91 ic.log.Errorf("Could not create registration data: %s", err) 92 } 93 94 ticker := time.NewTicker(time.Duration(interval) * time.Second) 95 96 for { 97 select { 98 case <-ticker.C: 99 err = ic.publish(output) 100 if err != nil { 101 ic.log.Errorf("Could not create registration data: %s", err) 102 } 103 104 case <-ctx.Done(): 105 return 106 } 107 } 108 } 109 110 func (ic *InventoryContent) publish(output chan *data.RegistrationItem) error { 111 ic.log.Infof("Starting inventory registration poll") 112 113 bi := ic.si.BuildInfo() 114 115 idata := &InventoryData{ 116 Classes: ic.si.Classes(), 117 Facts: ic.si.Facts(), 118 Collectives: ic.c.Collectives, 119 Status: ic.si.Status(), 120 AutoAgents: []*InventoryMachineState{}, 121 BuildInfo: &InventoryBuildInfo{ 122 Version: bi.Version(), 123 SHA: bi.SHA(), 124 }, 125 } 126 127 for _, a := range ic.si.KnownAgents() { 128 agent, ok := ic.si.AgentMetadata(a) 129 if ok { 130 idata.Agents = append(idata.Agents, agent) 131 } 132 } 133 134 ms, err := ic.si.MachinesStatus() 135 if err == nil { 136 for _, m := range ms { 137 idata.AutoAgents = append(idata.AutoAgents, &InventoryMachineState{ 138 Name: m.Name, 139 Version: m.Version, 140 State: m.State, 141 }) 142 } 143 } 144 145 msg := &InventoryContentMessage{Protocol: inventoryContentProtocol} 146 147 dat, err := json.Marshal(idata) 148 if err != nil { 149 return err 150 } 151 152 if ic.c.Choria.InventoryContentCompression { 153 zdat, err := compress(dat) 154 if err != nil { 155 ic.log.Warnf("Could not compress registration data: %s", err) 156 } else { 157 msg.ZContent = zdat 158 } 159 } 160 161 if msg.ZContent == nil { 162 msg.Content = dat 163 } 164 165 jdat, err := json.Marshal(msg) 166 if err != nil { 167 return fmt.Errorf("could not json marshal registration message: %s", err) 168 } 169 170 item := &data.RegistrationItem{ 171 Data: jdat, 172 Destination: ic.c.Choria.InventoryContentRegistrationTarget, 173 } 174 175 if item.Destination == "" { 176 item.TargetAgent = "registration" 177 } 178 179 output <- item 180 181 return nil 182 }