github.com/Cloud-Foundations/Dominator@v0.3.4/cmd/mdbd/server.go (about) 1 package main 2 3 import ( 4 "errors" 5 "io" 6 "sync" 7 8 "github.com/Cloud-Foundations/Dominator/lib/log" 9 "github.com/Cloud-Foundations/Dominator/lib/mdb" 10 "github.com/Cloud-Foundations/Dominator/lib/srpc" 11 "github.com/Cloud-Foundations/Dominator/lib/srpc/serverutil" 12 "github.com/Cloud-Foundations/Dominator/lib/stringutil" 13 "github.com/Cloud-Foundations/Dominator/proto/mdbserver" 14 ) 15 16 type rpcType struct { 17 currentMdb *mdb.Mdb 18 logger log.Logger 19 *serverutil.PerUserMethodLimiter 20 rwMutex sync.RWMutex 21 // Protected by lock. 22 updateChannels map[*srpc.Conn]chan<- mdbserver.MdbUpdate 23 } 24 25 func startRpcd(logger log.Logger) *rpcType { 26 rpcObj := &rpcType{ 27 logger: logger, 28 PerUserMethodLimiter: serverutil.NewPerUserMethodLimiter( 29 map[string]uint{ 30 "ListImages": 1, 31 }), 32 33 updateChannels: make(map[*srpc.Conn]chan<- mdbserver.MdbUpdate), 34 } 35 srpc.RegisterNameWithOptions("MdbServer", rpcObj, srpc.ReceiverOptions{ 36 PublicMethods: []string{ 37 "ListImages", 38 }}) 39 return rpcObj 40 } 41 42 func (t *rpcType) ListImages(conn *srpc.Conn, 43 request mdbserver.ListImagesRequest, 44 reply *mdbserver.ListImagesResponse) error { 45 currentMdb := t.currentMdb 46 if currentMdb == nil { 47 return nil 48 } 49 plannedImages := make(map[string]struct{}) 50 requiredImages := make(map[string]struct{}) 51 for _, machine := range currentMdb.Machines { 52 plannedImages[machine.PlannedImage] = struct{}{} 53 requiredImages[machine.RequiredImage] = struct{}{} 54 } 55 delete(plannedImages, "") 56 delete(requiredImages, "") 57 response := mdbserver.ListImagesResponse{ 58 PlannedImages: stringutil.ConvertMapKeysToList(plannedImages, false), 59 RequiredImages: stringutil.ConvertMapKeysToList(requiredImages, false), 60 } 61 *reply = response 62 return nil 63 } 64 65 func (t *rpcType) GetMdbUpdates(conn *srpc.Conn) error { 66 updateChannel := make(chan mdbserver.MdbUpdate, 10) 67 t.rwMutex.Lock() 68 t.updateChannels[conn] = updateChannel 69 t.rwMutex.Unlock() 70 defer func() { 71 close(updateChannel) 72 t.rwMutex.Lock() 73 delete(t.updateChannels, conn) 74 t.rwMutex.Unlock() 75 }() 76 currentMdb := t.currentMdb 77 if currentMdb != nil { 78 mdbUpdate := mdbserver.MdbUpdate{MachinesToAdd: currentMdb.Machines} 79 if err := conn.Encode(mdbUpdate); err != nil { 80 return err 81 } 82 if err := conn.Flush(); err != nil { 83 return err 84 } 85 } 86 closeChannel := conn.GetCloseNotifier() 87 for { 88 var err error 89 select { 90 case mdbUpdate := <-updateChannel: 91 if isEmptyUpdate(mdbUpdate) { 92 t.logger.Printf("Queue for: %s is filling up: dropping client") 93 return errors.New("update queue too full") 94 } 95 if err = conn.Encode(mdbUpdate); err != nil { 96 break 97 } 98 if err = conn.Flush(); err != nil { 99 break 100 } 101 case <-closeChannel: 102 break 103 } 104 if err != nil { 105 if err != io.EOF { 106 t.logger.Println(err) 107 return err 108 } else { 109 return nil 110 } 111 } 112 } 113 } 114 115 func (t *rpcType) pushUpdateToAll(old, new *mdb.Mdb) { 116 t.currentMdb = new 117 updateChannels := t.getUpdateChannels() 118 if len(updateChannels) < 1 { 119 return 120 } 121 mdbUpdate := mdbserver.MdbUpdate{} 122 if old == nil { 123 old = &mdb.Mdb{} 124 } 125 oldMachines := make(map[string]mdb.Machine, len(old.Machines)) 126 for _, machine := range old.Machines { 127 oldMachines[machine.Hostname] = machine 128 } 129 for _, newMachine := range new.Machines { 130 if oldMachine, ok := oldMachines[newMachine.Hostname]; ok { 131 if !newMachine.Compare(oldMachine) { 132 mdbUpdate.MachinesToUpdate = append(mdbUpdate.MachinesToUpdate, 133 newMachine) 134 } 135 } else { 136 mdbUpdate.MachinesToAdd = append(mdbUpdate.MachinesToAdd, 137 newMachine) 138 } 139 } 140 for _, machine := range new.Machines { 141 delete(oldMachines, machine.Hostname) 142 } 143 for name := range oldMachines { 144 mdbUpdate.MachinesToDelete = append(mdbUpdate.MachinesToDelete, name) 145 } 146 if isEmptyUpdate(mdbUpdate) { 147 t.logger.Println("Ignoring empty update") 148 return 149 } 150 for _, channel := range updateChannels { 151 sendUpdate(channel, mdbUpdate) 152 } 153 } 154 155 func (t *rpcType) getUpdateChannels() []chan<- mdbserver.MdbUpdate { 156 t.rwMutex.RLock() 157 defer t.rwMutex.RUnlock() 158 channels := make([]chan<- mdbserver.MdbUpdate, 0, len(t.updateChannels)) 159 for _, channel := range t.updateChannels { 160 channels = append(channels, channel) 161 } 162 return channels 163 } 164 165 func isEmptyUpdate(mdbUpdate mdbserver.MdbUpdate) bool { 166 if len(mdbUpdate.MachinesToAdd) > 0 { 167 return false 168 } 169 if len(mdbUpdate.MachinesToUpdate) > 0 { 170 return false 171 } 172 if len(mdbUpdate.MachinesToDelete) > 0 { 173 return false 174 } 175 return true 176 } 177 178 func sendUpdate(channel chan<- mdbserver.MdbUpdate, 179 mdbUpdate mdbserver.MdbUpdate) { 180 defer func() { recover() }() 181 if cap(channel)-len(channel) < 2 { 182 // Not enough room for an update and a possible "too much" message next 183 // time around: send a "too much" message now. 184 channel <- mdbserver.MdbUpdate{} 185 return 186 } 187 channel <- mdbUpdate 188 }