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  }