github.com/binkynet/BinkyNet@v1.12.1-0.20240421190447-da4e34c20be0/discovery/discovery_listener.go (about)

     1  // Copyright 2019 Ewout Prangsma
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  //
     7  //     http://www.apache.org/licenses/LICENSE-2.0
     8  //
     9  // Unless required by applicable law or agreed to in writing, software
    10  // distributed under the License is distributed on an "AS IS" BASIS,
    11  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  // See the License for the specific language governing permissions and
    13  // limitations under the License.
    14  //
    15  // Author Ewout Prangsma
    16  //
    17  
    18  package discovery
    19  
    20  import (
    21  	"context"
    22  
    23  	"github.com/grandcat/zeroconf"
    24  	"github.com/rs/zerolog"
    25  
    26  	api "github.com/binkynet/BinkyNet/apis/v1"
    27  )
    28  
    29  // ServiceListener is a service that listens for service registrations for
    30  // a specific type of service.
    31  type ServiceListener struct {
    32  	log         zerolog.Logger
    33  	serviceType string
    34  	all         bool
    35  	cb          ServiceChangedCallback
    36  }
    37  
    38  // ServiceChangedCallback is type for callbacks a service change
    39  // has been detected.
    40  type ServiceChangedCallback func(info api.ServiceInfo)
    41  
    42  // NewServiceListener creates and initializes a ServiceListener.
    43  // If all is false, only changes in service info are reported.
    44  // If all is true, all service info messages are reported.
    45  func NewServiceListener(log zerolog.Logger, serviceType string, all bool, cb ServiceChangedCallback) *ServiceListener {
    46  	return &ServiceListener{
    47  		log:         log,
    48  		serviceType: serviceType,
    49  		all:         all,
    50  		cb:          cb,
    51  	}
    52  }
    53  
    54  // Run the listener until the given context is canceled.
    55  func (l *ServiceListener) Run(ctx context.Context) error {
    56  	log := l.log.With().Str("serviceType", l.serviceType).Logger()
    57  	resolver, err := zeroconf.NewResolver(nil)
    58  	if err != nil {
    59  		log.Debug().Err(err).Msg("NewResolver failed")
    60  		return err
    61  	}
    62  
    63  	entries := make(chan *zeroconf.ServiceEntry)
    64  	go func(results <-chan *zeroconf.ServiceEntry) {
    65  		var lastInfo api.ServiceInfo
    66  		for entry := range results {
    67  			info, err := api.ParseServiceInfo(entry)
    68  			if err != nil {
    69  				log.Debug().Err(err).Msg("Failed to parse service entry")
    70  			} else {
    71  				if l.all || info.String() != lastInfo.String() {
    72  					lastInfo = *info
    73  					l.log.Info().
    74  						Str("service", entry.Service).
    75  						Str("address", info.GetApiAddress()).
    76  						Int32("port", info.GetApiPort()).
    77  						Str("version", info.GetApiVersion()).
    78  						Msg("Service change detected")
    79  					l.cb(lastInfo)
    80  				}
    81  			}
    82  		}
    83  	}(entries)
    84  
    85  	if err := resolver.Browse(ctx, l.serviceType, "local.", entries); err != nil {
    86  		log.Debug().Err(err).Msg("Browse failed")
    87  		return err
    88  	}
    89  	<-ctx.Done()
    90  	return nil
    91  }