etcd

package
v0.7.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Dec 1, 2021 License: Apache-2.0 Imports: 14 Imported by: 0

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

Examples

Constants

This section is empty.

Variables

View Source
var (
	// 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

This section is empty.

Types

type EtcdServReg added in v0.5.0

type EtcdServReg struct {
	// contains filtered or unexported fields
}

EtcdServReg is a wrap around an etcd client that allows you to perform service registry operations on etcd, such as storing, updating, deleting or retrieving a namespace, service, or endpoint. It is an implementation of ServiceRegistry defined in https://github.com/CloudNativeSDWAN/cnwan-operator/pkg/servregistry.

func NewServiceRegistryWithEtcd

func NewServiceRegistryWithEtcd(ctx context.Context, cli *clientv3.Client, prefix *string) *EtcdServReg

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:

func (*EtcdServReg) CreateEndp added in v0.5.0

func (e *EtcdServReg) CreateEndp(endp *sr.Endpoint) (*sr.Endpoint, error)

CreateEndp creates the endpoint.

Read the documentation for this method on servregistry's package.

func (*EtcdServReg) CreateNs added in v0.5.0

func (e *EtcdServReg) CreateNs(ns *sr.Namespace) (*sr.Namespace, error)

CreateNs creates the namespace.

Read the documentation for this method on servregistry's package.

func (*EtcdServReg) CreateServ added in v0.5.0

func (e *EtcdServReg) CreateServ(serv *sr.Service) (*sr.Service, error)

CreateServ creates the service.

func (*EtcdServReg) DeleteEndp added in v0.5.0

func (e *EtcdServReg) DeleteEndp(nsName, servName, endpName string) error

DeleteEndp deletes the endpoint.

Read the documentation for this method on servregistry's package.

func (*EtcdServReg) DeleteNs added in v0.5.0

func (e *EtcdServReg) DeleteNs(name string) error

DeleteNs deletes the namespace.

Read the documentation for this method on servregistry's package.

func (*EtcdServReg) DeleteServ added in v0.5.0

func (e *EtcdServReg) DeleteServ(nsName, servName string) error

DeleteServ deletes the service.

func (*EtcdServReg) ExtractData added in v0.5.0

func (e *EtcdServReg) ExtractData(ns *corev1.Namespace, serv *corev1.Service) (*sr.Namespace, *sr.Service, []*sr.Endpoint, error)

func (*EtcdServReg) GetEndp added in v0.5.0

func (e *EtcdServReg) GetEndp(nsName, servName, endpName string) (*sr.Endpoint, error)

GetEndp returns the endpoint, if it exists.

Read the documentation for this method on servregistry's package.

func (*EtcdServReg) GetNs added in v0.5.0

func (e *EtcdServReg) GetNs(name string) (*sr.Namespace, error)

GetNs returns the namespace if exists.

Read the documentation for this method on servregistry's package.

func (*EtcdServReg) GetServ added in v0.5.0

func (e *EtcdServReg) GetServ(nsName, servName string) (*sr.Service, error)

GetServ returns the service if exists.

func (*EtcdServReg) ListEndp added in v0.5.0

func (e *EtcdServReg) ListEndp(nsName, servName string) (endpList []*sr.Endpoint, err error)

ListServ returns a list of services inside the provided namespace.

Read the documentation for this method on servregistry's package.

func (*EtcdServReg) ListNs added in v0.5.0

func (e *EtcdServReg) ListNs() (nsList []*sr.Namespace, err error)

ListNs returns a list of all namespaces.

Read the documentation for this method on servregistry's package.

func (*EtcdServReg) ListServ added in v0.5.0

func (e *EtcdServReg) ListServ(nsName string) (servList []*sr.Service, err error)

ListServ returns a list of services inside the provided namespace.

func (*EtcdServReg) UpdateEndp added in v0.5.0

func (e *EtcdServReg) UpdateEndp(endp *sr.Endpoint) (*sr.Endpoint, error)

UpdateEndp updates the endpoint.

func (*EtcdServReg) UpdateNs added in v0.5.0

func (e *EtcdServReg) UpdateNs(ns *sr.Namespace) (*sr.Namespace, error)

UpdateNs updates the namespace.

Read the documentation for this method on servregistry's package.

func (*EtcdServReg) UpdateServ added in v0.5.0

func (e *EtcdServReg) UpdateServ(serv *sr.Service) (*sr.Service, error)

UpdateServ updates the service.

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.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL