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 }