diff --git a/examples/4g-network/k8s/endpoint-mme.yaml b/examples/4g-network/k8s/endpoint-mme.yaml index 44c9c23..e993ca7 100644 --- a/examples/4g-network/k8s/endpoint-mme.yaml +++ b/examples/4g-network/k8s/endpoint-mme.yaml @@ -8,6 +8,17 @@ spec: networkservicemesh.io/app: "mme" template: metadata: + annotations: + ns.networkservicemesh.io/endpoints: | + { + "name": "4g-network", + "tracerEnabled": true, + "networkServices": [ + {"link": "s1-mme", "labels": "app=mme", "ipaddress": "10.60.1.0/24"}, + {"link": "s6a", "labels": "app=mme", "ipaddress": "10.60.2.0/24"}, + {"link": "s11", "labels": "app=mme", "ipaddress": "10.60.3.0/24"} + ] + } labels: networkservicemesh.io/app: "mme" networkservicemesh.io/impl: "4g-network" @@ -16,22 +27,23 @@ spec: - name: sidecar-nse image: networkservicemesh/4g-network-sidecar-nse:latest imagePullPolicy: IfNotPresent - env: - - name: ENDPOINT_NETWORK_SERVICE - value: "4g-network" - - name: ENDPOINT_LABELS - value: "app=mme" - - name: TRACER_ENABLED - value: "true" - - name: IP_ADDRESS - value: "10.60.1.0/24" resources: limits: networkservicemesh.io/socket: 1 + volumeMounts: + - name: nsm-endpoints + mountPath: /etc/nsminfo - name: mme image: alpine:latest command: ["tail", "-f", "/dev/null"] imagePullPolicy: IfNotPresent + volumes: + - name: nsm-endpoints + downwardAPI: + items: + - path: endpoints + fieldRef: + fieldPath: metadata.annotations['ns.networkservicemesh.io/endpoints'] metadata: name: mme namespace: default diff --git a/examples/4g-network/k8s/endpoint-p-gw-c.yaml b/examples/4g-network/k8s/endpoint-p-gw-c.yaml index 29112cc..540de42 100644 --- a/examples/4g-network/k8s/endpoint-p-gw-c.yaml +++ b/examples/4g-network/k8s/endpoint-p-gw-c.yaml @@ -8,6 +8,17 @@ spec: networkservicemesh.io/app: "p-gw-c" template: metadata: + annotations: + ns.networkservicemesh.io/endpoints: | + { + "name": "4g-network", + "tracerEnabled": true, + "networkServices": [ + {"link": "s5s8-c", "labels": "app=p-gw-c", "ipAddress": "10.60.4.0/24"}, + {"link": "gx", "labels": "app=p-gw-c", "ipAddress": "10.60.5.0/24"}, + {"link": "sxb", "labels": "app=p-gw-c", "ipAddress": "10.60.6.0/24"} + ] + } labels: networkservicemesh.io/app: "p-gw-c" networkservicemesh.io/impl: "4g-network" @@ -16,22 +27,23 @@ spec: - name: sidecar-nse image: networkservicemesh/4g-network-sidecar-nse:latest imagePullPolicy: IfNotPresent - env: - - name: ENDPOINT_NETWORK_SERVICE - value: "4g-network" - - name: ENDPOINT_LABELS - value: "app=p-gw-c" - - name: TRACER_ENABLED - value: "true" - - name: IP_ADDRESS - value: "10.60.2.0/24" resources: limits: networkservicemesh.io/socket: 1 + volumeMounts: + - name: nsm-endpoints + mountPath: /etc/nsminfo - name: p-gw-c image: alpine:latest command: ["tail", "-f", "/dev/null"] imagePullPolicy: IfNotPresent + volumes: + - name: nsm-endpoints + downwardAPI: + items: + - path: endpoints + fieldRef: + fieldPath: metadata.annotations['ns.networkservicemesh.io/endpoints'] metadata: name: p-gw-c namespace: default diff --git a/examples/4g-network/k8s/endpoint-s-gw-u.yaml b/examples/4g-network/k8s/endpoint-s-gw-u.yaml index 15f5dd0..57882c6 100644 --- a/examples/4g-network/k8s/endpoint-s-gw-u.yaml +++ b/examples/4g-network/k8s/endpoint-s-gw-u.yaml @@ -8,6 +8,17 @@ spec: networkservicemesh.io/app: "s-gw-u" template: metadata: + annotations: + ns.networkservicemesh.io/endpoints: | + { + "name": "4g-network", + "tracerEnabled": true, + "networkServices": [ + {"link": "s1-u", "labels": "app=s-gw-u", "ipAddress": "10.60.7.0/24"}, + {"link": "sxa", "labels": "app=s-gw-u", "ipAddress": "10.60.8.0/24"}, + {"link": "s5s8-u", "labels": "app=s-gw-u", "ipAddress": "10.60.9.0/24"} + ] + } labels: networkservicemesh.io/app: "s-gw-u" networkservicemesh.io/impl: "4g-network" @@ -16,22 +27,23 @@ spec: - name: sidecar-nse image: networkservicemesh/4g-network-sidecar-nse:latest imagePullPolicy: IfNotPresent - env: - - name: ENDPOINT_NETWORK_SERVICE - value: "4g-network" - - name: ENDPOINT_LABELS - value: "app=s-gw-u" - - name: TRACER_ENABLED - value: "true" - - name: IP_ADDRESS - value: "10.60.3.0/24" resources: limits: networkservicemesh.io/socket: 1 + volumeMounts: + - name: nsm-endpoints + mountPath: /etc/nsminfo - name: s-gw-u image: alpine:latest command: ["tail", "-f", "/dev/null"] imagePullPolicy: IfNotPresent + volumes: + - name: nsm-endpoints + downwardAPI: + items: + - path: endpoints + fieldRef: + fieldPath: metadata.annotations['ns.networkservicemesh.io/endpoints'] metadata: name: s-gw-u namespace: default diff --git a/examples/4g-network/k8s/endpoint-tdf-u.yaml b/examples/4g-network/k8s/endpoint-tdf-u.yaml index 4e64f7f..72e8898 100644 --- a/examples/4g-network/k8s/endpoint-tdf-u.yaml +++ b/examples/4g-network/k8s/endpoint-tdf-u.yaml @@ -8,6 +8,16 @@ spec: networkservicemesh.io/app: "tdf-u" template: metadata: + annotations: + ns.networkservicemesh.io/endpoints: | + { + "name": "4g-network", + "tracerEnabled": true, + "networkServices": [ + {"link": "sgi", "labels": "app=tdf-u", "ipAddress": "10.60.10.0/24"}, + {"link": "sxc", "labels": "app=tdf-u", "ipAddress": "10.60.11.0/24"} + ] + } labels: networkservicemesh.io/app: "tdf-u" networkservicemesh.io/impl: "4g-network" @@ -16,22 +26,23 @@ spec: - name: sidecar-nse image: networkservicemesh/4g-network-sidecar-nse:latest imagePullPolicy: IfNotPresent - env: - - name: ENDPOINT_NETWORK_SERVICE - value: "4g-network" - - name: ENDPOINT_LABELS - value: "app=tdf-u" - - name: TRACER_ENABLED - value: "true" - - name: IP_ADDRESS - value: "10.60.4.0/24" resources: limits: networkservicemesh.io/socket: 1 + volumeMounts: + - name: nsm-endpoints + mountPath: /etc/nsminfo - name: tdf-u image: alpine:latest command: ["tail", "-f", "/dev/null"] imagePullPolicy: IfNotPresent + volumes: + - name: nsm-endpoints + downwardAPI: + items: + - path: endpoints + fieldRef: + fieldPath: metadata.annotations['ns.networkservicemesh.io/endpoints'] metadata: name: tdf-u namespace: default diff --git a/examples/4g-network/sidecar-nse/cmd/endpoints.go b/examples/4g-network/sidecar-nse/cmd/endpoints.go new file mode 100644 index 0000000..a28ae0a --- /dev/null +++ b/examples/4g-network/sidecar-nse/cmd/endpoints.go @@ -0,0 +1,44 @@ +// Copyright 2020 Samsung Electronics +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import "encoding/json" + +// Endpoint contains the information to create NSM objects +type Endpoint struct { + Name string `json:"name"` + TracerEnabled bool `json:"tracerEnabled"` + NetworkServices []NetworkService `json:"networkServices"` +} + +// NetworkService contains the information to create NSM objects +type NetworkService struct { + Link string `json:"link"` + Labels string `json:"labels"` + IPAddress string `json:"ipAddress"` + Route string `json:"route"` +} + +// GetEndpoint parses a stream of bytes to a Endpoint struct +func GetEndpoint(endpointsFile []byte) (Endpoint, error) { + var endpointsConfig Endpoint + + if err := json.Unmarshal(endpointsFile, &endpointsConfig); err != nil { + return endpointsConfig, err + } + + return endpointsConfig, nil +} diff --git a/examples/4g-network/sidecar-nse/cmd/endpoints_test.go b/examples/4g-network/sidecar-nse/cmd/endpoints_test.go new file mode 100644 index 0000000..30f67f7 --- /dev/null +++ b/examples/4g-network/sidecar-nse/cmd/endpoints_test.go @@ -0,0 +1,87 @@ +// Copyright 2020 Samsung Electronics +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "reflect" + "strings" + "testing" +) + +func TestRead(t *testing.T) { + testCases := []struct { + label string + input []byte + expectedError string + expected Endpoint + }{ + { + label: "Empty annotation file", + input: []byte(``), + expectedError: "unexpected end of JSON input", + }, + { + label: "Invalid annotation format", + input: []byte(`{}`), + }, + { + label: "Read annotations file successfuly", + input: []byte(`{ + "name": "lte-network", + "networkServices": [ + {"link": "s5u", "labels": "app=pgw-s5u", "ipAddress": "172.25.0.0/24"}, + {"link": "sgi", "labels": "app=http-server-sgi", "ipAddress": "10.0.1.0/24", "route": "10.0.3.0/24"} + ] + }`), + expected: Endpoint{ + Name: "lte-network", + NetworkServices: []NetworkService{ + NetworkService{ + Link: "s5u", + Labels: "app=pgw-s5u", + IPAddress: "172.25.0.0/24", + }, + NetworkService{ + Link: "sgi", + Labels: "app=http-server-sgi", + IPAddress: "10.0.1.0/24", + Route: "10.0.3.0/24", + }, + }, + }, + }, + } + + for _, testCase := range testCases { + t.Run(testCase.label, func(t *testing.T) { + got, err := GetEndpoint(testCase.input) + if err != nil { + if testCase.expectedError == "" { + t.Fatalf("GetEndpoints returned an unexpected error %s", err) + } + if !strings.Contains(err.Error(), testCase.expectedError) { + t.Fatalf("GetEndpoints returned an unexpected error %s", err) + } + } else { + if !reflect.DeepEqual(testCase.expected, got) { + t.Errorf("GetEndpoints returned unexpected result: got %v;"+ + " expected %v", got, testCase.expected) + } + } + }) + } + +} diff --git a/examples/4g-network/sidecar-nse/cmd/main.go b/examples/4g-network/sidecar-nse/cmd/main.go index b5ade2d..fc13a34 100644 --- a/examples/4g-network/sidecar-nse/cmd/main.go +++ b/examples/4g-network/sidecar-nse/cmd/main.go @@ -17,7 +17,11 @@ package main import ( "context" + "io/ioutil" + "os" + "strconv" + "github.com/networkservicemesh/networkservicemesh/controlplane/api/networkservice" "github.com/networkservicemesh/networkservicemesh/pkg/tools" "github.com/networkservicemesh/networkservicemesh/sdk/common" "github.com/networkservicemesh/networkservicemesh/sdk/endpoint" @@ -25,29 +29,65 @@ import ( ) func main() { + logrus.Info("Starting nse...") // Capture signals to cleanup before exiting c := tools.NewOSSignalChannel() + endpointsFile, err := os.Open("/etc/nsminfo/endpoints") + if err != nil { + logrus.Fatalf("Unable to read the endpoints file: %v", err) + } + defer endpointsFile.Close() + byteValue, _ := ioutil.ReadAll(endpointsFile) + + endpointsConfig, err := GetEndpoint(byteValue) + if err != nil { + logrus.Fatalf("Unable to parse the endpoints file: %v", err) + } + + registrations := []endpoint.Registration{} + switchEndpoint := NewSwitchEndpoint("link") configuration := common.FromEnv() - podName := endpoint.CreatePodNameMutator() - composite := endpoint.NewCompositeEndpoint( - endpoint.NewMonitorEndpoint(configuration), - endpoint.NewConnectionEndpoint(configuration), - endpoint.NewIpamEndpoint(configuration), - endpoint.NewCustomFuncEndpoint("podName", podName), - ) + configuration.EndpointNetworkService = endpointsConfig.Name + os.Setenv("TRACER_ENABLED", strconv.FormatBool(endpointsConfig.TracerEnabled)) + for _, config := range endpointsConfig.NetworkServices { + service := common.FromEnv() + service.EndpointNetworkService = endpointsConfig.Name + service.EndpointLabels = config.Labels + service.IPAddress = config.IPAddress - nsmEndpoint, err := endpoint.NewNSMEndpoint(context.Background(), configuration, composite) + endpoints := []networkservice.NetworkServiceServer{ + endpoint.NewConnectionEndpoint(service), + endpoint.NewIpamEndpoint(service), + endpoint.NewCustomFuncEndpoint("podName", endpoint.CreatePodNameMutator()), + } + + if config.Route != "" { + routeAddr := endpoint.CreateRouteMutator([]string{config.Route}) + endpoints = append(endpoints, endpoint.NewCustomFuncEndpoint("route", routeAddr)) + } + + switchEndpoint.Childs[config.Link] = endpoint.NewCompositeEndpoint(endpoints...) + registrations = append(registrations, endpoint.MakeRegistration(service)) + } + + nsEndpoint, err := endpoint.NewNSMEndpoint( + context.Background(), + configuration, + endpoint.NewCompositeEndpoint( + endpoint.NewMonitorEndpoint(configuration), + switchEndpoint, + ), + endpoint.WithRegistrations(registrations...), + ) + defer nsEndpoint.Delete() if err != nil { logrus.Fatalf("%v", err) } - - if err := nsmEndpoint.Start(); err != nil { + if err := nsEndpoint.Start(); err != nil { logrus.Fatalf("Unable to start the endpoint: %v", err) } - defer func() { _ = nsmEndpoint.Delete() }() - // Capture signals to cleanup before exiting <-c } diff --git a/examples/4g-network/sidecar-nse/cmd/switch.go b/examples/4g-network/sidecar-nse/cmd/switch.go new file mode 100644 index 0000000..7315085 --- /dev/null +++ b/examples/4g-network/sidecar-nse/cmd/switch.go @@ -0,0 +1,69 @@ +// Copyright 2020 Samsung Electronics +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at: +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package main + +import ( + "context" + + "github.com/golang/protobuf/ptypes/empty" + "github.com/networkservicemesh/networkservicemesh/controlplane/api/connection" + "github.com/networkservicemesh/networkservicemesh/controlplane/api/networkservice" + "github.com/pkg/errors" +) + +// SwitchEndpoint is a simple NetworkServiceServer composition primitive which +// forwards incoming requests into one of its child NetworkServiceServer's based +// on the specified label. +type SwitchEndpoint struct { + Label string + Childs map[string]networkservice.NetworkServiceServer +} + +// Request implements NetworkServiceServer interface method. +func (s *SwitchEndpoint) Request(ctx context.Context, request *networkservice.NetworkServiceRequest) (*connection.Connection, error) { + e, err := s.selectEndpoint(request.Connection) + if err == nil { + return e.Request(ctx, request) + } + + return nil, err +} + +// Close implements NetworkServiceServer interface method. +func (s *SwitchEndpoint) Close(ctx context.Context, connection *connection.Connection) (*empty.Empty, error) { + e, err := s.selectEndpoint(connection) + if err == nil { + return e.Close(ctx, connection) + } + + return nil, err +} + +func (s *SwitchEndpoint) selectEndpoint(c *connection.Connection) (networkservice.NetworkServiceServer, error) { + result := s.Childs[c.Labels[s.Label]] + if result == nil { + return nil, errors.New("Couldn't match the connection") + } + return result, nil +} + +// NewSwitchEndpoint creates a new SwitchEndpoint object +func NewSwitchEndpoint(label string) *SwitchEndpoint { + return &SwitchEndpoint{ + Label: label, + Childs: map[string]networkservice.NetworkServiceServer{}, + } +} diff --git a/go.mod b/go.mod index 6a78255..a65b1aa 100644 --- a/go.mod +++ b/go.mod @@ -23,9 +23,9 @@ require ( replace ( github.com/census-instrumentation/opencensus-proto v0.1.0-0.20181214143942-ba49f56771b8 => github.com/census-instrumentation/opencensus-proto v0.0.3-0.20181214143942-ba49f56771b8 - github.com/networkservicemesh/networkservicemesh => github.com/networkservicemesh/networkservicemesh v0.0.0-20200328192804-8d64ff42c90d - github.com/networkservicemesh/networkservicemesh/controlplane/api => github.com/networkservicemesh/networkservicemesh/controlplane/api v0.0.0-20200328192804-8d64ff42c90d - github.com/networkservicemesh/networkservicemesh/pkg => github.com/networkservicemesh/networkservicemesh/pkg v0.0.0-20200328192804-8d64ff42c90d - github.com/networkservicemesh/networkservicemesh/sdk => github.com/networkservicemesh/networkservicemesh/sdk v0.0.0-20200328192804-8d64ff42c90d - github.com/networkservicemesh/networkservicemesh/utils => github.com/networkservicemesh/networkservicemesh/utils v0.0.0-20200328192804-8d64ff42c90d + github.com/networkservicemesh/networkservicemesh => github.com/networkservicemesh/networkservicemesh v0.0.0-20201012191209-cb2e62e24eb3 + github.com/networkservicemesh/networkservicemesh/controlplane/api => github.com/networkservicemesh/networkservicemesh/controlplane/api v0.0.0-20201012191209-cb2e62e24eb3 + github.com/networkservicemesh/networkservicemesh/pkg => github.com/networkservicemesh/networkservicemesh/pkg v0.0.0-20201012191209-cb2e62e24eb3 + github.com/networkservicemesh/networkservicemesh/sdk => github.com/networkservicemesh/networkservicemesh/sdk v0.0.0-20201012191209-cb2e62e24eb3 + github.com/networkservicemesh/networkservicemesh/utils => github.com/networkservicemesh/networkservicemesh/utils v0.0.0-20201012191209-cb2e62e24eb3 ) diff --git a/go.sum b/go.sum index a0eca6b..5d8c5d5 100644 --- a/go.sum +++ b/go.sum @@ -291,12 +291,20 @@ github.com/naoina/toml v0.1.1/go.mod h1:NBIhNtsFMo3G2szEBne+bO4gS192HuIYRqfvOWb4 github.com/nbio/st v0.0.0-20140626010706-e9e8d9816f32/go.mod h1:9wM+0iRr9ahx58uYLpLIr5fm8diHn0JbqRycJi6w0Ms= github.com/networkservicemesh/networkservicemesh/controlplane/api v0.0.0-20200328192804-8d64ff42c90d h1:7enEgSWLXnpSlRMCS9/OJVXsoM6/TCbIfhvTxDmNqT4= github.com/networkservicemesh/networkservicemesh/controlplane/api v0.0.0-20200328192804-8d64ff42c90d/go.mod h1:0jdEdOt2NntftpnBwlTR5aJ69w7aDnu6CEwwNNmZGVA= +github.com/networkservicemesh/networkservicemesh/controlplane/api v0.0.0-20201012191209-cb2e62e24eb3 h1:7zxPX/3/Tptai2bdg7zPCG13fxSQuiYuDTseS9LVM5o= +github.com/networkservicemesh/networkservicemesh/controlplane/api v0.0.0-20201012191209-cb2e62e24eb3/go.mod h1:0jdEdOt2NntftpnBwlTR5aJ69w7aDnu6CEwwNNmZGVA= github.com/networkservicemesh/networkservicemesh/pkg v0.0.0-20200328192804-8d64ff42c90d h1:XdDVbMHv0XOdjESS/mBy9LNMJL0nz9Nwo+d2frI5Cdg= github.com/networkservicemesh/networkservicemesh/pkg v0.0.0-20200328192804-8d64ff42c90d/go.mod h1:6qYBSBbEDWVL2XlKmeh9YUOKVw+n1qWnWZXzpGo9yEo= +github.com/networkservicemesh/networkservicemesh/pkg v0.0.0-20201012191209-cb2e62e24eb3 h1:Cn/Ooaj1xMyqj3bV4tyi75O6/Cm2804aEz/p5eHQJL8= +github.com/networkservicemesh/networkservicemesh/pkg v0.0.0-20201012191209-cb2e62e24eb3/go.mod h1:6qYBSBbEDWVL2XlKmeh9YUOKVw+n1qWnWZXzpGo9yEo= github.com/networkservicemesh/networkservicemesh/sdk v0.0.0-20200328192804-8d64ff42c90d h1:nCj/iEa1RF6LE9flainBzCuhykk9+LlrMtkm4IPDbm8= github.com/networkservicemesh/networkservicemesh/sdk v0.0.0-20200328192804-8d64ff42c90d/go.mod h1:2Z2ha3eeCPYtGmvwyfta8REREo1OPYuXJwqC8RN1qcI= +github.com/networkservicemesh/networkservicemesh/sdk v0.0.0-20201012191209-cb2e62e24eb3 h1:Mw1uaPK06iOFNcL5G7rYSnvTYL518dy9B3I2qRQ+ERg= +github.com/networkservicemesh/networkservicemesh/sdk v0.0.0-20201012191209-cb2e62e24eb3/go.mod h1:2Z2ha3eeCPYtGmvwyfta8REREo1OPYuXJwqC8RN1qcI= github.com/networkservicemesh/networkservicemesh/utils v0.0.0-20200328192804-8d64ff42c90d h1:/pzIzF5B1zlZhUvfmnBXo7zi7gXd1D3zZ98xosfN2v4= github.com/networkservicemesh/networkservicemesh/utils v0.0.0-20200328192804-8d64ff42c90d/go.mod h1:VvA+zSIv3iE7FixaPc2NTMcwL/u13pWG/3DLsLpYKXc= +github.com/networkservicemesh/networkservicemesh/utils v0.0.0-20201012191209-cb2e62e24eb3 h1:jN3MeD2h2G9gaKnyebYeHjMWLJon6fDQvlgrs53M/rM= +github.com/networkservicemesh/networkservicemesh/utils v0.0.0-20201012191209-cb2e62e24eb3/go.mod h1:VvA+zSIv3iE7FixaPc2NTMcwL/u13pWG/3DLsLpYKXc= github.com/nrdcg/auroradns v1.0.0/go.mod h1:6JPXKzIRzZzMqtTDgueIhTi6rFf1QvYE/HzqidhOhjw= github.com/nrdcg/goinwx v0.6.1/go.mod h1:XPiut7enlbEdntAqalBIqcYcTEVhpv/dKWgDCX2SwKQ= github.com/nrdcg/namesilo v0.2.1/go.mod h1:lwMvfQTyYq+BbjJd30ylEG4GPSS6PII0Tia4rRpRiyw=