Documentation ¶
Overview ¶
Package etcd connects to an etcd cluster to perform service registry operations.
Read this documentation at https://pkg.go.dev/github.com/CloudNativeSDWAN/cnwan-operator/pkg/servregistry/etcd
Etcd as a service registry ¶
etcd is a distributed and reliable key-value store, and while it is oblivious of the data you store there, it makes sense to use it as a Service Registry: for example, coreDNS can use etcd as a backend where to retrieve records from before answering to DNS queries.
Each object inserted to etcd will have a key which identifies it in some way and a value with all data that are relevant to the specific object.
To learn more about etcd, see https://etcd.io/.
To learn more about the objects mentioned above you can visit CN-WAN's servregistry package (https://pkg.go.dev/github.com/CloudNativeSDWAN/cnwan-operator/pkg/servregistry) for the technical documentation or CN-WAN Operator's official documentation (https://github.com/CloudNativeSDWAN/cnwan-operator).
Values ¶
Values are the service registry objects and can be one of the following:
- Namespace,
- Service,
- or Endpoint.
Please visit the links above to learn how those objects are implemented in go and their use/meaning, respectively.
Keys ¶
Being a flat key-value store, there is no real concept of hierarchy.
Thus, given that the objects defined above do need such structure, this will be emulated with the well-known use of prefixes, which will make the key resemble a path, for example:
/prefix-1/object-1-name/prefix-2/object-2-name
These are the keys that are used by this package:
- namespaces will have keys in the format of namespaces/<name>
for example:
namespaces/my-project
- services will have keys in the format of namespaces/namespace-name/services/service-name
for example:
namespaces/my-project/services/user-profile
- endpoints will have keys in the format of namespaces/namespace-name/services/service-name/endpoints/endpoint-name
for example:
namespaces/my-project/services/user-profile/endpoints/user-profile-1
Default global prefix ¶
A sort of "global" prefix can be used: something that specifies that all keys that begin with this prefix belong to the service registry. This is useful in case you are already using etcd for other purposes or plan to do so.
Unless an explicit prefix is passed, this package will use the default one:
/service-registry/
For example, a namespace key will be: /service-registry/namespaces/prod
Transactions ¶
Insertions, updates and deletions are all performed in transactions.
Usage ¶
Read the single functions documentation and the example to learn how to use this package.
Index ¶
- Variables
- func NewServiceRegistryWithEtcd(ctx context.Context, cli *clientv3.Client, prefix *string) (sr.ServiceRegistry, error)
- type KeyBuilder
- func (k *KeyBuilder) Clone() *KeyBuilder
- func (k *KeyBuilder) GetEndpoint() (name string)
- func (k *KeyBuilder) GetNamespace() (name string)
- func (k *KeyBuilder) GetService() (name string)
- func (k *KeyBuilder) IsValid() bool
- func (k *KeyBuilder) ObjectType() ObjectType
- func (k *KeyBuilder) SetEndpoint(name string) *KeyBuilder
- func (k *KeyBuilder) SetNamespace(name string) *KeyBuilder
- func (k *KeyBuilder) SetService(name string) *KeyBuilder
- func (k *KeyBuilder) String() string
- type ObjectType
Examples ¶
- KeyBuilder
- KeyBuilder (WithUnsupportedOperations)
- KeyFromNames
- KeyFromServiceRegistryObject (InvalidObject)
- KeyFromServiceRegistryObject (ValidObject)
- KeyFromString (InvalidKey)
- KeyFromString (ValidKey)
- KeyFromString (ValidKeyWithPrefix)
- NewServiceRegistryWithEtcd
- NewServiceRegistryWithEtcd (WithEmptyPrefix)
- NewServiceRegistryWithEtcd (WithPrefix)
Constants ¶
This section is empty.
Variables ¶
var ( // ErrNilClient is returned when the etcd client provided to // NewServiceRegistryWithEtcd is nil ErrNilClient error = errors.New("no etcd client provided") // ErrNilObject is returned when a function is provided with a nil // object. ErrNilObject error = errors.New("no object provided") // ErrUnknownObject is returned when the KeyBuilder is provided with an // object that is not a namespace, service or endpoint. ErrUnknownObject error = errors.New("object is unknown") )
These errors are thrown by the package when an incorrect value is provided to some of its functions, or when something unexpected happens.
Functions ¶
func NewServiceRegistryWithEtcd ¶
func NewServiceRegistryWithEtcd(ctx context.Context, cli *clientv3.Client, prefix *string) (sr.ServiceRegistry, error)
NewServiceRegistryWithEtcd returns an instance of ServiceRegistry as defined by with ETCD as a backend. https://pkg.go.dev/github.com/CloudNativeSDWAN/cnwan-operator/pkg/servregistry#ServiceRegistry.
If prefix is not nil, all data will be prefixed with the value you set on prefix, for example:
/my-prefix/my-data.
If you don't want any prefix, set the value of prefix to an empty string or just "/" and all keys will be prefixed by just "/".
You may even specify a prefix with multiple slashes: for example, if you have multiple clusters/environments, a key could be:
"cluster-1/service-registry".
Be aware that any leading AND trailing slashes will be removed to prevent key paths errors, but will be inserted correctly automatically when calling any of its methods.
Be careful with this value as it can potentially overwrite existing data.
If context is not nil, it will be used as the main context upon which all queries to etcd will be based on.
This method returns an error only if the client provided to it is nil.
Example ¶
This example shows how to start the etcd service registry without a custom global prefix. This means that default one will be used (/service-registry/)
clientConfig := clientv3.Config{ Endpoints: []string{ "10.11.12.13:2379", }, } cli, err := clientv3.New(clientConfig) if err != nil { fmt.Println("cannot establish connection to etcd:", err) os.Exit(1) } mainCtx, canc := context.WithCancel(context.Background()) // NewServiceRegistryWithEtcd returns an error only when the client is // nil: this is not our case and that's why we do not check the error // here. servreg, _ := NewServiceRegistryWithEtcd(mainCtx, cli, nil) // Do something with the service registry... _ = servreg // Do other stuf... // Cancel the context canc()
Output:
Example (WithEmptyPrefix) ¶
This example shows how to start the etcd service registry with a no prefix. Actually, only "/" will be used in that case.
clientConfig := clientv3.Config{ Endpoints: []string{ "10.11.12.13:2379", }, } prefix := "" cli, err := clientv3.New(clientConfig) if err != nil { fmt.Println("cannot establish connection to etcd:", err) os.Exit(1) } mainCtx, canc := context.WithCancel(context.Background()) // NewServiceRegistryWithEtcd returns an error only when the client is // nil: this is not our case and that's why we do not check the error // here. servreg, _ := NewServiceRegistryWithEtcd(mainCtx, cli, &prefix) // Do something with the service registry... _ = servreg // Do other stuf... // Cancel the context canc()
Output:
Example (WithPrefix) ¶
This example shows how to start the etcd service registry with a custom global prefix. As it is shown, you can even use multiple slashes.
clientConfig := clientv3.Config{ Endpoints: []string{ "10.11.12.13:2379", }, } prefix := "/app-1/service-registry/" cli, err := clientv3.New(clientConfig) if err != nil { fmt.Println("cannot establish connection to etcd:", err) os.Exit(1) } mainCtx, canc := context.WithCancel(context.Background()) // NewServiceRegistryWithEtcd returns an error only when the client is // nil: this is not our case and that's why we do not check the error // here. servreg, _ := NewServiceRegistryWithEtcd(mainCtx, cli, &prefix) // Do something with the service registry... _ = servreg // Do other stuf... // Cancel the context canc()
Output:
Types ¶
type KeyBuilder ¶
type KeyBuilder struct {
// contains filtered or unexported fields
}
KeyBuilder manages and builds an etcd key for service registry. It can create the appropriate path key based on the object type it is dealing with or make assumptions on what the value is based on its key path so that you know how to unmarshal its value.
Be aware that KeyBuilder will NOT include a prefix when it returns the key as a string, so you should either include it manually or use the namespace package (https://pkg.go.dev/go.etcd.io/etcd@v3.3.25+incompatible/clientv3/namespace).
Take a look at the examples to learn more about this.
NOTE: as written above, Key only makes **assumptions**: you need to check that the unmarshal operation was successful to make sure the object is correct. This is performed automatically by the Service Registry implementer, but you have to do it on your own in case you use it with a crude client.
Example ¶
This example shows how to use the Keybuilder without any names yet. It is useful in case you want to build a key based on some conditions.
In this very simple example, an environment variable is set to drive the conditions on how the key should be built.
namespaceName := "ns-name" serviceName := "serv-name" endpName := "endp-name" builder := &KeyBuilder{} os.Setenv("GET", "endpoint") builder.SetNamespace(namespaceName).SetService(serviceName) if os.Getenv("GET") == "endpoint" { builder.SetEndpoint(endpName) } fmt.Println(builder)
Output: namespaces/ns-name/services/serv-name/endpoints/endp-name
Example (WithUnsupportedOperations) ¶
This example shows how to use this package's KeyBuilder to build keys for etcd to use for operations that are not supported by this package, i.e. watching.
cli, err := clientv3.New(clientv3.Config{ Endpoints: []string{ "localhost:2379", }, }) if err != nil { fmt.Println("cannot establish connection to etcd:", err) os.Exit(1) } watcher := namespace.NewWatcher(cli.Watcher, "my-prefix/") builder := &KeyBuilder{} nsName := "namespace-name" ctx, canc := context.WithCancel(context.TODO()) defer canc() wchan := watcher.Watch(ctx, builder.SetNamespace(nsName).String()) for { w := <-wchan if w.Canceled { break } }
Output:
func KeyFromNames ¶
func KeyFromNames(names ...string) *KeyBuilder
KeyFromNames starts building a key based on the provided names. This method is useful in case you want build a string and already know the name and parents' names of the object that will be stored as value.
Example ¶
This example shows how to build a key starting from a list of names.
namespaceName := "ns-name" serviceName := "serv-name" key := KeyFromNames(namespaceName, serviceName) fmt.Println(key)
Output: namespaces/ns-name/services/serv-name
func KeyFromServiceRegistryObject ¶
func KeyFromServiceRegistryObject(object interface{}) (*KeyBuilder, error)
KeyFromServiceRegistryObject returns a KeyBuilder starting from a service registry object defined in https://pkg.go.dev/github.com/CloudNativeSDWAN/cnwan-operator/pkg/servregistry: for example a namespace, service or endpoint.
In case the a key couldn't be built this method either returns an error belonging to package mentioned above or ErrNilObject if the object is nil.
Example (InvalidObject) ¶
ns := &sr.Service{ // A service must belong to a namespace. // We comment this to make it an invalid object. // NsName : "namespace-name" Name: "service-name", Metadata: map[string]string{ "version": "v0.2.1", "commit-hash": "aqvkepsclg", }, } key, err := KeyFromServiceRegistryObject(ns) if err != nil { fmt.Println("error:", err) return } fmt.Println(key.String())
Output: error: namespace name is empty
Example (ValidObject) ¶
ns := &sr.Namespace{ Name: "namespace-name", Metadata: map[string]string{ "env": "beta", }, } key, err := KeyFromServiceRegistryObject(ns) if err != nil { // Handle the error... } fmt.Println(key.String())
Output: namespaces/namespace-name
func KeyFromString ¶
func KeyFromString(key string) *KeyBuilder
KeyFromString returns a KeyBuilder starting from a string, i.e.
/namespaces/namespace-name/services/service-name`
or
/something/something-name/another/another-name
This is very useful in case you want to check if the key is valid for the service registry.
Note that this WILL also strip any prefix from the key, so if you really need it you should either write it manually or use the namespace package (https://pkg.go.dev/go.etcd.io/etcd@v3.3.25+incompatible/clientv3/namespace) from etcd, which includes/excludes it automatically for each key.
Take a look at the examples to learn more.
Example (InvalidKey) ¶
key := "/objects/users/user-name" fmt.Println(KeyFromString(key).IsValid())
Output: false
Example (ValidKey) ¶
key := "namespaces/ns-name/services/service-name/endpoints/endpoint-name" fmt.Println(KeyFromString(key).IsValid())
Output: true
Example (ValidKeyWithPrefix) ¶
key := "/my/prefix/is/long/namespaces/ns-name/services/service-name/endpoints/endpoint-name" fmt.Println(KeyFromString(key).IsValid())
Output: true
func (*KeyBuilder) Clone ¶
func (k *KeyBuilder) Clone() *KeyBuilder
Clone returns another pointer to a KeyBuilder with the same settings as the one you're cloning from.
Since golang doesn't have a DeepCopy method, use this in case you want to generate other keys leaving this one intact.
func (*KeyBuilder) GetEndpoint ¶
func (k *KeyBuilder) GetEndpoint() (name string)
GetEndpoint returns the name of the endpoint, if set.
func (*KeyBuilder) GetNamespace ¶
func (k *KeyBuilder) GetNamespace() (name string)
GetNamespace returns the name of the namespace, if set.
func (*KeyBuilder) GetService ¶
func (k *KeyBuilder) GetService() (name string)
GetService returns the name of the service, if set.
func (*KeyBuilder) IsValid ¶
func (k *KeyBuilder) IsValid() bool
IsValid returns true if the key is a valid key for service registry and is the equivalent of doing:
k.ObjectType() != UnknownOrInvalidObject
func (*KeyBuilder) ObjectType ¶
func (k *KeyBuilder) ObjectType() ObjectType
ObjectType returns the assumed type of the object stored as value.
func (*KeyBuilder) SetEndpoint ¶
func (k *KeyBuilder) SetEndpoint(name string) *KeyBuilder
SetEndpoint sets the endpoint name.
func (*KeyBuilder) SetNamespace ¶
func (k *KeyBuilder) SetNamespace(name string) *KeyBuilder
SetNamespace sets the namespace name.
func (*KeyBuilder) SetService ¶
func (k *KeyBuilder) SetService(name string) *KeyBuilder
SetService sets the service name.
func (*KeyBuilder) String ¶
func (k *KeyBuilder) String() string
String "marshals" the key into a string.
This will not print any prefix and the key will never start with a `/`.
In case you need that, you will have to put that manually.
Note: this method will return an empty string if the key is not valid, i.e. when no namespace is set or the key is not suitable for service registry usage.
Make sure to call IsValid() before marshaling to string.
type ObjectType ¶
type ObjectType int
ObjectType identifies the type of the object we are dealing with and will build the key according to it, i.e. namespace, service or endpoint.
const ( // UnknownOrInvalidObject is an object that is neither a namespace, nor a service, // nor an endpoint and is thus not related to service registry. UnknownOrInvalidObject ObjectType = 0 // NamespaceObject represents a namespace. NamespaceObject ObjectType = iota // ServiceObject represents a service. ServiceObject ObjectType = iota // EndpointObject represents an endpoint. EndpointObject ObjectType = iota )
These constants define the object type that the key builder will deal with.