blob: e9d5b9c0985948b677864ce3dcfd1364611f6603 [file] [log] [blame]
// Copyright 2015 The Vanadium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package discovery
import (
"unicode/utf8"
"v.io/v23/context"
"v.io/v23/discovery"
"v.io/v23/security"
"v.io/v23/verror"
)
const (
// Limit the maximum length of instance id. Some plugins are using an instance id
// as a key (e.g., as a hostname in mDNS) and so its size can be limited by their
// protocols.
maxInstanceIdLen = 32
)
var (
errAlreadyBeingAdvertised = verror.Register(pkgPath+".errAlreadyBeingAdvertised", verror.NoRetry, "{1:}{2:} already being advertised")
errInstanceIdTooLong = verror.Register(pkgPath+".errInstanceIdTooLong", verror.NoRetry, "{1:}{2:} instance id too long")
errInvalidInstanceId = verror.Register(pkgPath+".errInvalidInstanceId", verror.NoRetry, "{1:}{2:} instance id not valid")
errNoInterfaceName = verror.Register(pkgPath+".errNoInterfaceName", verror.NoRetry, "{1:}{2:} interface name not provided")
errNotPackableAttributes = verror.Register(pkgPath+".errNotPackableAttributes", verror.NoRetry, "{1:}{2:} attribute not packable")
errNoAddresses = verror.Register(pkgPath+".errNoAddress", verror.NoRetry, "{1:}{2:} address not provided")
errNotPackableAddresses = verror.Register(pkgPath+".errNotPackableAddresses", verror.NoRetry, "{1:}{2:} address not packable")
)
// Advertise implements discovery.Advertiser.
func (ds *ds) Advertise(ctx *context.T, service *discovery.Service, visibility []security.BlessingPattern) (<-chan struct{}, error) {
if len(service.InterfaceName) == 0 {
return nil, verror.New(errNoInterfaceName, ctx)
}
if len(service.Addrs) == 0 {
return nil, verror.New(errNoAddresses, ctx)
}
if len(service.InstanceId) == 0 {
var err error
if service.InstanceId, err = newInstanceId(); err != nil {
return nil, err
}
}
if len(service.InstanceId) > maxInstanceIdLen {
return nil, verror.New(errInstanceIdTooLong, ctx)
}
if !utf8.ValidString(service.InstanceId) {
return nil, verror.New(errInvalidInstanceId, ctx)
}
if err := validateAttributes(service.Attrs); err != nil {
return nil, err
}
ad := Advertisement{
ServiceUuid: NewServiceUUID(service.InterfaceName),
Service: copyService(service),
}
if err := encrypt(&ad, visibility); err != nil {
return nil, err
}
ctx, cancel, err := ds.addTask(ctx)
if err != nil {
return nil, err
}
if !ds.addAd(ad.Service.InstanceId) {
cancel()
ds.removeTask(ctx)
return nil, verror.New(errAlreadyBeingAdvertised, ctx)
}
done := make(chan struct{})
barrier := NewBarrier(func() {
ds.removeAd(ad.Service.InstanceId)
ds.removeTask(ctx)
close(done)
})
for _, plugin := range ds.plugins {
if err := plugin.Advertise(ctx, ad, barrier.Add()); err != nil {
cancel()
return nil, err
}
}
return done, nil
}
func (ds *ds) addAd(id string) bool {
ds.mu.Lock()
if _, exist := ds.ads[id]; exist {
ds.mu.Unlock()
return false
}
ds.ads[id] = struct{}{}
ds.mu.Unlock()
return true
}
func (ds *ds) removeAd(id string) {
ds.mu.Lock()
delete(ds.ads, id)
ds.mu.Unlock()
}