The purpose of the SDK is to hide the lower level setup and communication details of Network Service Mesh (NSM). Using it eases the implementation of Network Service Clients and Endpoints. Please refer to the glossary for more detailed explanation of the terminology.
The current SDK targets Golang as the main Client and Endpoint implementation language. Other languages (C/C++, Python) might be considered for the future.
NSM comes with an admission controller implementation, which will allow for simple init and sidecar container approach when migrating existing services. This is approach is suitable for simpler solutions like web services, where no advanced interface knowledge is needed.
As noted, the SDK is a higher layer abstraction of the underlying gRPC API.
The client uses a named socket to talk to its local Network Service Manager (NSMgr). The socket is located in TBD. The gRPC itself is described in networkservice.proto.
The endpoint implements a communication over a socket to its local NSMgr. The socket is the same as what the client uses. The gRPC for registering an endpoint is implemented as service NetworkServiceRegistry
in registry.proto.
NSM SDK configuration is common for both Client and Endpoint APIs. It is done by setting the values of the following structure:
type NSConfiguration struct {
NsmServerSocket string
NsmClientSocket string
Workspace string
AdvertiseNseName string // ADVERTISE_NSE_NAME
OutgoingNscName string // OUTGOING_NSC_NAME
AdvertiseNseLabels string // ADVERTISE_NSE_LABELS
OutgoingNscLabels string // OUTGOING_NSC_LABELS
NscInterfaceName string // NSC_INTERFACE_NAME
MechanismType string // MECHANISM_TYPE
IPAddress string // IP_ADDRESS
Routes []string // ROUTES
}
Note that some of the members of this structure can be initialized through the environment variables shown as comments above. A detailed explanation of each configuration option follows:
NsmServerSocket
- [ system ], NS manager communication socketNsmClientSocket
- [ system ], NS manager communication socketWorkspace
- [ system ], Kubernetes Pod namespaceAdvertiseNseName
- [ADVERTISE_NSE_NAME
], the endpoint name, as advertised to the NS registryOutgoingNscName
- [OUTGOING_NSC_NAME
], the endpoint name, as the client looks up in the NS registryAdvertiseNseLabels
- [ADVERTISE_NSE_LABELS
], the endpoint labels, as advertised to the NS registry. Used in NSM's selector to match the DestinationSelector. The format islabel1=value1,label2=value2
OutgoingNscLabels
- [OUTGOING_NSC_LABELS
], the endpoint labels, as send by the client . Used in NSM's selector to match the SourceSelector. The format is the same asAdvertiseNseLabels
NscInterfaceName
- [NSC_INTERFACE_NAME
], the name off th interface as injected on the client sideMechanismType
- [MECHANISM_TYPE
], enforce a particular Mechanism type. Currentlykernel
ormem
. Defaults tokernel
IPAddress
- [IP_ADDRESS
], the IP network to initialize a prefix pool in the IPAM compositeRoutes
- [ROUTES
], list of routes that will be set into connection's context by Client
The NSM Client's main task is to request a connection to a particular Network Service through NSM. The NSM SDK provides two API sets for implementing clients.
The following code snippet illustrates its usage.
import "github.com/networkservicemesh/networkservicemesh/sdk/client"
...
client, err := client.NewNSMClient(context.Background(), nil)
if err != nil {
// Handle the error
}
// Ensure the client is terminated at the end
defer client.Destroy()
conn, err := client.Connect(context.Background(), "eth101", "kernel", "Primary interface")
// Please pass a proper context object with opentracing for creating a proper span's
// Run the actual code
// Close the current active connection
client.Close(context.Background(), conn)
This will create a client, configure it using the environment variables as described in Configuration
and connect a Kernel interface called eth101
. During the life cycle of the client, an arbitrary Connect requests can be invoked. The Destroy call ensures these are all terminated, if not already done by calling Close.
The more advanced API call is the client list. It follows the same Connect/Close/Destroy pattern as teh simple client. The difference is that it spawns multiple clients which are configured by an env variable NS_NETWORKSERVICEMESH_IO
. It takes a comma separated list of URLs with the following format:
${nsname}/${interface}?${label1}=${value1}&${label2}=${value2}
A simple example will be:
NS_NETWORKSERVICEMESH_IO=icmp?app=responder&version=v1,http?app=nginx
Invoking the client list API with this environment set will initiate a connection to a service named icmp
and the connection request will be labelled with app=responder
and version=v1
. Additionally a second connection to a service http
and its request will be labelled app=nginx
. The admission hook leverages this configuration method.
A simplified example code which demonstrates the ClientList usage is shown below.
import "github.com/networkservicemesh/networkservicemesh/sdk/client"
...
client, err := client.NewNSMClientList(context.Background(), nil, nil)
if err != nil {
// Handle the error
}
// Ensure the client is terminated at the end
defer client.Destroy()
if err := client.Connect(context.Background(), "nsm", "kernel", "Primary interface"); err != nil {
// Handle the error
}
The following code implements a simple endpoint that upon request will create an empty connection object and assign it a pair of IP addresses.
import "github.com/networkservicemesh/networkservicemesh/sdk/endpoint"
...
composite := endpoint.NewCompositeEndpoint(
endpoint.NewConnectionEndpoint(nil),
endpoint.NewIpamEndpoint(nil),
)
nsmEndpoint, err := endpoint.NewNSMEndpoint(context.Backgroud(), nil, nil, composite)
if err != nil {
// Handle the error
}
nsmEndpoint.Start()
defer nsmEndpoint.Delete()
As there is no explicit configuration, the ConnectionEndpoint, IpamEndpoint and the composed nsmEndpoint are initialized with the matching environment variables.
The NSM SDK Endpoint API enables plugging together different functionalities based on the CompositeEndpoint
interface. The basic principle is that the Request
call handlers are chained together in a process called composition. The function call to create such composite is NewCompositeEndpoint(endpoints ...ChainedEndpoint) networkservice.NetworkServiceServer
. Its argument order determines the order of Request
call chaining. The arguments implement the networkservice.NetworkServiceServer
interface.
Writing a new endpoint is done by extending the networkservice.NetworkServiceServer
structure.
Request(context.Context, *NetworkServiceRequest) (*connection.Connection, error)
- the request handler. The contract here is that the implementer should call next composite's Request method and should return whatever should be the incoming connection. Example: check the implementation insdk/endpoint/monitor.go
.Close(context.Context, *connection.Connection) (*empty.Empty, error)
- the close handler. The implementer should ensure that next composite's Close method is called before returning.
In case endpoint need some initialization logic it could implement endpoint.Initable
interface and method
Init(context *InitContext) error
- an init function to be called before the endpoint GRPC listener is started but after the NSM endpoint is created.
To create mutator to sets routes for IPContext
you can use a enpoint.CreateRouteMutator
function.
Example of usage:
routeEndpoint := endpoint.NewCustomFuncEndpoint("route",endpoint.CreateRouteMutator([]string{"dst addr"}))
The SDK comes with a set of useful composites, that can be chained together and as part of more complex scenarios.
client
- creates a downlink connection, i.e. to the next endpoint. This connection is available through theendpoint.ClientConnection(ctx)
method.connection
- returns a basic initialized connection, with the configured Mechanism set. Usually used at the "top" of the composite chain.ipam
- receives a connection and assigns it an IP pair from the configure prefix pool.monitor
- adds connection to the monitoring mechanism. Typically would be at the top of the composite chain.dns
- add DNS servers to ConnectionContext available in two flavors:-
NewAddDNSConfigs(...connectioncontext.DNSConfig)
- Adds DNSConfigs to your connectionContext
-
NewAddDnsConfigDstIp(searchDomains...string)
- Adds DNSConfig using the DstIp from ConnectionContext as the DNS Server IP
customfunc
- allows for specifying a custom connection mutator, it also accept ctx.Context to access extra prameters.
memif-connect
- receives a connection and creates a DataChange (or appends an existing DataChange) with a Memif interface for it. This DataChange and the name of the created interface is available through thevppagent.Config(ctx)/vppagent.WithConfig(ctx)
methods.client-memif-connect
- receives a downlink(outgoing) connection from theclient
composite's and creates a DataChange with a Memif interface for it. This DataChange and the name of the created interface is available through thevppagent.Config(ctx)
method.cross-connect
- receives names of created interfaces and creates a DataChange (or appends an existing DataChange) with cross-connect configuration. This DataChange is available through thevppagent.Config(ctx)
method.acl
- receives a name of created interface and creates a DataChange (or appends an existing DataChange) with ACLs for this interface. This DataChange is available through thevppagent.Config(ctx)
method.commit
- receives a DataChange and writes it to VPP Agent.