diff --git a/.gitignore b/.gitignore index be19042..47726a0 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,6 @@ .idea/ junit/ +nohup.out +.DS_Store +.vscode diff --git a/Dockerfile b/Dockerfile index 1a3d3ad..c3fd83b 100644 --- a/Dockerfile +++ b/Dockerfile @@ -19,4 +19,8 @@ CMD dlv -l :40000 --headless=true --api-version=2 test -test.v ./... FROM alpine as runtime COPY --from=build /bin/app /bin/app + +# Expose port for REST server +EXPOSE 3001 + CMD /bin/app \ No newline at end of file diff --git a/README.md b/README.md index 80fa61b..c1f1d37 100644 --- a/README.md +++ b/README.md @@ -1,75 +1,31 @@ -# Build +# Network Service Mesh Dashboard Backend -## Build cmd binary locally +NSM dashboard backend part providing graphical model data for [the UI part](https://github.com/networkservicemesh/cmd-dashboard-ui) through the REST API -You can build the locally by executing +Written in [Go](https://go.dev/) -```bash -go build ./... -``` +The entire NSM dashboard deployment info see [here](https://github.com/networkservicemesh/deployments-k8s/tree/main/examples/observability/dashboard) -## Build Docker container +## Dev/debug -You can build the docker container by running: +### To run dashboard backend in the cluster: -```bash -docker build . -``` +1. `git clone git@github.com:networkservicemesh/deployments-k8s.git` +2. `cd deployments-k8s/examples/observability/dashboard` +3. Edit `dashboard-pod.yaml` and remove the `dashboard-ui` container +4. `kubectl apply -f dashboard-pod.yaml` +5. `kubectl apply -f dashboard-backend-service.yaml` +6. `kubectl port-forward -n nsm-system service/dashboard-backend 3001:3001` +7. Check `http://localhost:3001/nodes` in the browser -# Testing +### To run dashboard backend with a custom container ([Docker](https://docs.docker.com/engine/install/) have to be installed) in the cluster: -## Testing Docker container - -Testing is run via a Docker container. To run testing run: - -```bash -docker run --privileged --rm $(docker build -q --target test .) -``` - -# Debugging - -## Debugging the tests -If you wish to debug the test code itself, that can be acheived by running: - -```bash -docker run --privileged --rm -p 40000:40000 $(docker build -q --target debug .) -``` - -This will result in the tests running under dlv. Connecting your debugger to localhost:40000 will allow you to debug. - -```bash --p 40000:40000 -``` -forwards port 40000 in the container to localhost:40000 where you can attach with your debugger. - -```bash ---target debug -``` - -Runs the debug target, which is just like the test target, but starts tests with dlv listening on port 40000 inside the container. - -## Debugging the cmd - -When you run 'cmd' you will see an early line of output that tells you: - -```Setting env variable DLV_LISTEN_FORWARDER to a valid dlv '--listen' value will cause the dlv debugger to execute this binary and listen as directed.``` - -If you follow those instructions when running the Docker container: -```bash -docker run --privileged -e DLV_LISTEN_FORWARDER=:50000 -p 50000:50000 --rm $(docker build -q --target test .) -``` - -```-e DLV_LISTEN_FORWARDER=:50000``` tells docker to set the environment variable DLV_LISTEN_FORWARDER to :50000 telling -dlv to listen on port 50000. - -```-p 50000:50000``` tells docker to forward port 50000 in the container to port 50000 in the host. From there, you can -just connect dlv using your favorite IDE and debug cmd. - -## Debugging the tests and the cmd - -```bash -docker run --privileged -e DLV_LISTEN_FORWARDER=:50000 -p 40000:40000 -p 50000:50000 --rm $(docker build -q --target debug .) -``` - -Please note, the tests **start** the cmd, so until you connect to port 40000 with your debugger and walk the tests -through to the point of running cmd, you will not be able to attach a debugger on port 50000 to the cmd. \ No newline at end of file +1. `git clone git@github.com:networkservicemesh/cmd-dashboard-backend.git` +2. `cd cmd-dashboard-backend` +3. Make the necessary code changes +4. Create a [Dockerhub](https://hub.docker.com/) repository +5. `docker build -t your-dh-namespace/dh-repo-name .` +6. `docker push your-dh-namespace/dh-repo-name` +7. `cd deployments-k8s/examples/observability/dashboard` +8. Edit `dashboard-pod.yaml` and set your Dockerhub image address for the `dashboard-backend` container +9. Execute the steps 3-7 from the previous section diff --git a/go.mod b/go.mod index 75a5330..e5b10f8 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,54 @@ -module github.com/networkservicemesh/cmd-template +module github.com/networkservicemesh/cmd-dashboard-backend go 1.20 + +require ( + github.com/antonfisher/nested-logrus-formatter v1.3.1 + github.com/gorilla/mux v1.8.1 + github.com/networkservicemesh/api v1.11.0 + github.com/networkservicemesh/sdk v1.11.0 + github.com/sirupsen/logrus v1.9.0 + github.com/spiffe/go-spiffe/v2 v2.0.0 + google.golang.org/grpc v1.59.0 +) + +require ( + cloud.google.com/go/compute/metadata v0.2.3 // indirect + github.com/beorn7/perks v1.0.1 // indirect + github.com/cenkalti/backoff/v4 v4.2.1 // indirect + github.com/cespare/xxhash/v2 v2.2.0 // indirect + github.com/go-logr/logr v1.2.4 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/golang-jwt/jwt/v4 v4.2.0 // indirect + github.com/golang/protobuf v1.5.3 // indirect + github.com/google/uuid v1.4.0 // indirect + github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 // indirect + github.com/matttproud/golang_protobuf_extensions v1.0.4 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/prometheus/client_golang v1.16.0 // indirect + github.com/prometheus/client_model v0.4.0 // indirect + github.com/prometheus/common v0.42.0 // indirect + github.com/prometheus/procfs v0.10.1 // indirect + github.com/zeebo/errs v1.2.2 // indirect + go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 // indirect + go.opentelemetry.io/otel v1.19.0-rc.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0-rc.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0-rc.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0-rc.1 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0-rc.1 // indirect + go.opentelemetry.io/otel/exporters/prometheus v0.42.0-rc.1 // indirect + go.opentelemetry.io/otel/metric v1.19.0-rc.1 // indirect + go.opentelemetry.io/otel/sdk v1.19.0-rc.1 // indirect + go.opentelemetry.io/otel/sdk/metric v1.19.0-rc.1 // indirect + go.opentelemetry.io/otel/trace v1.19.0-rc.1 // indirect + go.opentelemetry.io/proto/otlp v1.0.0 // indirect + go.uber.org/atomic v1.7.0 // indirect + golang.org/x/crypto v0.16.0 // indirect + golang.org/x/net v0.19.0 // indirect + golang.org/x/sys v0.15.0 // indirect + golang.org/x/text v0.14.0 // indirect + google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d // indirect + google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 // indirect + google.golang.org/protobuf v1.31.0 // indirect + gopkg.in/square/go-jose.v2 v2.5.1 // indirect +) diff --git a/go.sum b/go.sum index e69de29..3d62bca 100644 --- a/go.sum +++ b/go.sum @@ -0,0 +1,199 @@ +cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go v0.34.0 h1:eOI3/cP2VTU6uZLDYAoic+eyzzB9YyGmJ7eIjl8rOPg= +cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= +cloud.google.com/go/compute v1.23.0 h1:tP41Zoavr8ptEqaW6j+LQOnyBBhO7OkOMAGrgLopTwY= +cloud.google.com/go/compute/metadata v0.2.3 h1:mg4jlk7mCAj6xXp9UJ4fjI9VUI5rubuGBW5aJ7UnBMY= +cloud.google.com/go/compute/metadata v0.2.3/go.mod h1:VAV5nSsACxMJvgaAuX6Pk2AawlZn8kiOGuCv6gTkwuA= +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/antonfisher/nested-logrus-formatter v1.3.1 h1:NFJIr+pzwv5QLHTPyKz9UMEoHck02Q9L0FP13b/xSbQ= +github.com/antonfisher/nested-logrus-formatter v1.3.1/go.mod h1:6WTfyWFkBc9+zyBaKIqRrg/KwMqBbodBjgbHjDz7zjA= +github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= +github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/cenkalti/backoff/v4 v4.2.1 h1:y4OZtCnogmCPw98Zjyt5a6+QwPLGkiQsYW5oUqylYbM= +github.com/cenkalti/backoff/v4 v4.2.1/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= +github.com/cespare/xxhash/v2 v2.2.0 h1:DC2CZ1Ep5Y4k3ZQ899DldepgrayRUGE6BBZ/cd9Cj44= +github.com/cespare/xxhash/v2 v2.2.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= +github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= +github.com/cncf/xds/go v0.0.0-20230607035331-e9ce68804cb4 h1:/inchEIKaYC1Akx+H+gqO04wryn5h75LSazbRlnya1k= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/envoyproxy/go-control-plane v0.9.0/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.1-0.20191026205805-5f8ba28d4473/go.mod h1:YTl/9mNaCwkRvm6d1a2C3ymFceY/DCBVvsKhRF0iEA4= +github.com/envoyproxy/go-control-plane v0.9.4/go.mod h1:6rpuAdCZL397s3pYoYcLgu1mIlRU8Am5FuJP05cCM98= +github.com/envoyproxy/protoc-gen-validate v0.1.0/go.mod h1:iSmxcyjqTsJpI2R4NaDN7+kN2VEUnK/pcBlmesArF7c= +github.com/envoyproxy/protoc-gen-validate v1.0.2 h1:QkIBuU5k+x7/QXPvPPnWXWlCdaBFApVqftFV6k087DA= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.2.4 h1:g01GSCwiDw2xSZfjJ2/T9M+S6pFdcNtFYsp+Y43HYDQ= +github.com/go-logr/logr v1.2.4/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/golang-jwt/jwt/v4 v4.2.0 h1:besgBTC8w8HjP6NzQdxwKH9Z5oQMZ24ThTrHp3cZ8eU= +github.com/golang-jwt/jwt/v4 v4.2.0/go.mod h1:/xlHOz8bRuivTWchD4jCa+NbatV+wEUSzwAxVc6locg= +github.com/golang/glog v0.0.0-20160126235308-23def4e6c14b/go.mod h1:SBH7ygxi8pfUlaOkMMuAQtPIUF8ecWP5IEl/CR7VP2Q= +github.com/golang/glog v1.1.2 h1:DVjP2PbBOzHyzA+dn3WhHIq4NdVu3Q+pvivFICf/7fo= +github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A= +github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.2/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U= +github.com/golang/protobuf v1.3.3/go.mod h1:vzj43D7+SQXF/4pzW/hwtAqwc6iTitCiVSaWz5lYuqw= +github.com/golang/protobuf v1.4.0-rc.1/go.mod h1:ceaxUfeHdC40wWswd/P6IGgMaK3YpKi5j83Wpe3EHw8= +github.com/golang/protobuf v1.4.0-rc.1.0.20200221234624-67d41d38c208/go.mod h1:xKAWHe0F5eneWXFV3EuXVDTCmh+JuBKY0li0aMyXATA= +github.com/golang/protobuf v1.4.0-rc.2/go.mod h1:LlEzMj4AhA7rCAGe4KMBDvJI+AwstrUpVNzEA03Pprs= +github.com/golang/protobuf v1.4.0-rc.4.0.20200313231945-b860323f09d0/go.mod h1:WU3c8KckQ9AFe+yFwt9sWVRKCVIyN9cPHBJSNnbL67w= +github.com/golang/protobuf v1.4.0/go.mod h1:jodUvKwWbYaEsadDk5Fwe5c77LiNKVO9IDvqG2KuDX0= +github.com/golang/protobuf v1.4.1/go.mod h1:U8fpvMrcmy5pZrNK1lt4xCsGvpyWQ/VVv6QDs8UjoX8= +github.com/golang/protobuf v1.4.2/go.mod h1:oDoupMAO8OvCJWAcko0GGGIgR6R6ocIYbsSw735rRwI= +github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk= +github.com/golang/protobuf v1.5.3 h1:KhyjKVUg7Usr/dYsdSqoFveMYd5ko72D+zANwlG1mmg= +github.com/golang/protobuf v1.5.3/go.mod h1:XVQd3VNwM+JqD3oG2Ue2ip4fOMUkwXdXDdiuN0vRsmY= +github.com/google/go-cmp v0.2.0/go.mod h1:oXzfMopK8JAjlY9xF4vHSVASa0yLyX7SntLO5aqRK0M= +github.com/google/go-cmp v0.3.0/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.3.1/go.mod h1:8QqcDgzrUqlUb/G2PQTWiueGozuR1884gddMywk6iLU= +github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.5.9 h1:O2Tfq5qg4qc4AmwVlvv0oLiVAGB7enBSJ2x2DqQFi38= +github.com/google/uuid v1.1.2/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/google/uuid v1.4.0 h1:MtMxsa51/r9yyhkyLsVeVt0B+BGQZzpQiTQ4eHZ8bc4= +github.com/google/uuid v1.4.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= +github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0 h1:YBftPWNWd4WwGqtY2yeZL2ef8rHAxPBD8KFhJpmcqms= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.16.0/go.mod h1:YN5jB8ie0yfIUg6VvR9Kz84aCaG7AsGZnLjhHbUqwPg= +github.com/matttproud/golang_protobuf_extensions v1.0.4 h1:mmDVorXM7PCGKw94cs5zkfA9PSy5pEvNWRP0ET0TIVo= +github.com/matttproud/golang_protobuf_extensions v1.0.4/go.mod h1:BSXmuO+STAnVfrANrmjBb36TMTDstsz7MSK+HVaYKv4= +github.com/networkservicemesh/api v1.11.0 h1:9Hbanw+Eet3D/BF2jE75Nfhsmz0r9aStBiHEeyXXc50= +github.com/networkservicemesh/api v1.11.0/go.mod h1:DO7D13Y4x/A8tQ2cQyUWa9o5wi7XHUB8+vITWRKreCc= +github.com/networkservicemesh/sdk v1.11.0 h1:RUoMfq1/yBjWHeikEbcPGIcK7L23yim8sapBk51g4QA= +github.com/networkservicemesh/sdk v1.11.0/go.mod h1:kskx6RdU+taL14uTk/BMq7cDDPEy2s7O/ekcGxfWmBk= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/prometheus/client_golang v1.16.0 h1:yk/hx9hDbrGHovbci4BY+pRMfSuuat626eFsHb7tmT8= +github.com/prometheus/client_golang v1.16.0/go.mod h1:Zsulrv/L9oM40tJ7T815tM89lFEugiJ9HzIqaAx4LKc= +github.com/prometheus/client_model v0.0.0-20190812154241-14fe0d1b01d4/go.mod h1:xMI15A0UPsDsEKsMN9yxemIoYk6Tm2C1GtYGdfGttqA= +github.com/prometheus/client_model v0.4.0 h1:5lQXD3cAg1OXBf4Wq03gTrXHeaV0TQvGfUooCfx1yqY= +github.com/prometheus/client_model v0.4.0/go.mod h1:oMQmHW1/JoDwqLtg57MGgP/Fb1CJEYF2imWWhWtMkYU= +github.com/prometheus/common v0.42.0 h1:EKsfXEYo4JpWMHH5cg+KOUWeuJSov1Id8zGR8eeI1YM= +github.com/prometheus/common v0.42.0/go.mod h1:xBwqVerjNdUDjgODMpudtOMwlOwf2SaTr1yjz4b7Zbc= +github.com/prometheus/procfs v0.10.1 h1:kYK1Va/YMlutzCGazswoHKo//tZVlFpKYh+PymziUAg= +github.com/prometheus/procfs v0.10.1/go.mod h1:nwNm2aOCAYw8uTR/9bWRREkZFxAUcWzPHWJq+XBB/FM= +github.com/sirupsen/logrus v1.9.0 h1:trlNQbNUG3OdDrDil03MCb1H2o9nJ1x4/5LYw7byDE0= +github.com/sirupsen/logrus v1.9.0/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/spiffe/go-spiffe/v2 v2.0.0 h1:y6N7BZAxgaFZYELyrIdxSMm2e2tWpzgQewUts9h1hfM= +github.com/spiffe/go-spiffe/v2 v2.0.0/go.mod h1:TEfgrEcyFhuSuvqohJt6IxENUNeHfndWCCV1EX7UaVk= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= +github.com/zeebo/errs v1.2.2 h1:5NFypMTuSdoySVTqlNs1dEoU21QVamMQJxW/Fii5O7g= +github.com/zeebo/errs v1.2.2/go.mod h1:sgbWHsvVuTPHcqJJGQ1WhI5KbWlHYz+2+2C/LSEtCw4= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0 h1:ZOLJc06r4CB42laIXg/7udr0pbZyuAihN10A/XuiQRY= +go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.42.0/go.mod h1:5z+/ZWJQKXa9YT34fQNx5K8Hd1EoIhvtUygUQPqEOgQ= +go.opentelemetry.io/otel v1.19.0-rc.1 h1:9nKcb4AOpKFjcIWQgebt1k75VYn2ZQMvgXD+/n28OLo= +go.opentelemetry.io/otel v1.19.0-rc.1/go.mod h1:+JfHvVWJ0QCKM+TFO3gpReowE8GaFEx2Ry9calmSibE= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0-rc.1 h1:m86dgj8YG+6KfM9wrSfMMTPi822rqfLGyQ1DD257zaU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric v0.42.0-rc.1/go.mod h1:hG4Fj/y8TR/tlEDREo8tWstl9fO9gcFkn4xrx0Io8xU= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0-rc.1 h1:mgQPqQKUP0IyfrmXCdMbqAH1xsBAHVzolbJkzgAqxlc= +go.opentelemetry.io/otel/exporters/otlp/otlpmetric/otlpmetricgrpc v0.42.0-rc.1/go.mod h1:FMR+HUsfMXreSeo2juctBZN0rizwEc/c1vw7q2LY2lk= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0-rc.1 h1:8ZG2TARAaJWqm1YN2QH86h1xX4EPCA4ldZtc4N4YWgg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.19.0-rc.1/go.mod h1:jykghzDqP7m7cPJHTLdKpzYO1LSLZJM78TIhTdcWM8s= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0-rc.1 h1:oy1QPbh9KiTlBicC8xXMPoMu6sxnsyPl1mdXBUrg3QY= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracegrpc v1.19.0-rc.1/go.mod h1:MX98pqy1OH9RFoOhcK7MWP0me/bMs5m/8BLxg2Sg8oY= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0-rc.1 h1:V92ofeZM5yESfTQtNVpjEG+cDI8rp+3dmVE4OjVzcc8= +go.opentelemetry.io/otel/exporters/prometheus v0.42.0-rc.1/go.mod h1:EtVcwPt5W/MC6fZfzya4A5iE9bRz8ysu0FRrNL8+SN0= +go.opentelemetry.io/otel/metric v1.19.0-rc.1 h1:LqJlejNazmpXWlOrgYUc1xj0pZ5cqkLoDaP4hlPQR00= +go.opentelemetry.io/otel/metric v1.19.0-rc.1/go.mod h1:xzyh2gMwfLsu30VybtOXISwzy7ohXDkPiG+YFPfcROc= +go.opentelemetry.io/otel/sdk v1.19.0-rc.1 h1:dD5Y9QMAktJ0gIlZjv+fmRAyVGm5v76spKJ63Ev22WQ= +go.opentelemetry.io/otel/sdk v1.19.0-rc.1/go.mod h1:e3KNe2cNBxQH2lL5sX9p7yi/GLKZ/QZDGyoJVNvYx7A= +go.opentelemetry.io/otel/sdk/metric v1.19.0-rc.1 h1:RbGxgZUjffRVPLjX/H695ri/noDOeFtzaP6uINNFJjA= +go.opentelemetry.io/otel/sdk/metric v1.19.0-rc.1/go.mod h1:J+jlmTpSn8aR86SQSlTkhGRKCN9ktrTDRzGgcaxDKzw= +go.opentelemetry.io/otel/trace v1.19.0-rc.1 h1:VvCcp07aHaPAS3I0UwONZiY4mglW2LHXRKNazj3B0w4= +go.opentelemetry.io/otel/trace v1.19.0-rc.1/go.mod h1:UttzyZK+fFPfslCglhDGMadPXgSNaQAqLPSh2ahBA34= +go.opentelemetry.io/proto/otlp v1.0.0 h1:T0TX0tmXU8a3CbNXzEKGeU5mIVOdf0oykP+u2lIVU/I= +go.opentelemetry.io/proto/otlp v1.0.0/go.mod h1:Sy6pihPLfYHkr3NkUbEhGHFhINUSI/v80hjKIs5JXpM= +go.uber.org/atomic v1.7.0 h1:ADUqmZGgLDDfbSL9ZmPxKTybcoEYHgpYfELNoN+7hsw= +go.uber.org/atomic v1.7.0/go.mod h1:fEN4uk6kAWBTFdckzkM89CLk9XfWZrxpCo0nPH17wJc= +go.uber.org/goleak v1.2.1 h1:NBol2c7O1ZokfZ0LEU9K6Whx/KnwvepVetCUhtKja4A= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.16.0 h1:mMMrFzRSCF0GvB7Ne27XVtVAaXLrPmgPC7/v0tkwHaY= +golang.org/x/crypto v0.16.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA= +golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= +golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU= +golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= +golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= +golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= +golang.org/x/oauth2 v0.11.0 h1:vPL4xzxBM4niKCW6g9whtaWVXTJf1U5e4aZxxFx/gbU= +golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= +golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= +golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20190114222345-bf090417da8b/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190226205152-f727befe758c/go.mod h1:9Yl7xja0Znq3iFh3HoIrodX9oNMXvdceNzlUR8zjMvY= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190524140312-2c0ae7006135/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= +google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= +google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= +google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= +google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= +google.golang.org/genproto v0.0.0-20200526211855-cb27e3aa2013/go.mod h1:NbSheEEYHJ7i3ixzK3sjbqSGDJWnxyFXZblF3eUsNvo= +google.golang.org/genproto v0.0.0-20200806141610-86f49bd18e98/go.mod h1:FWY/as6DDZQgahTzZj3fqbO1CbirC29ZNUFHwi0/+no= +google.golang.org/genproto v0.0.0-20231030173426-d783a09b4405 h1:I6WNifs6pF9tNdSob2W24JtyxIYjzFB9qDlpUC76q+U= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d h1:DoPTO70H+bcDXcd39vOqb2viZxgqeBeSGtZ55yZU4/Q= +google.golang.org/genproto/googleapis/api v0.0.0-20230822172742-b8732ec3820d/go.mod h1:KjSP20unUpOx5kyQUFa7k4OJg0qeJ7DEZflGDu2p6Bk= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17 h1:Jyp0Hsi0bmHXG6k9eATXoYtjd6e2UzZ1SCn/wIupY14= +google.golang.org/genproto/googleapis/rpc v0.0.0-20231106174013-bbf56f31fb17/go.mod h1:oQ5rr10WTTMvP4A36n8JpR1OrO1BEiV4f78CneXZxkA= +google.golang.org/grpc v1.19.0/go.mod h1:mqu4LbDTu4XGKhr4mRzUsmM4RtVoemTSY81AxZiDr8c= +google.golang.org/grpc v1.23.0/go.mod h1:Y5yQAOtifL1yxbo5wqy6BxZv8vAUGQwXBOALyacEbxg= +google.golang.org/grpc v1.25.1/go.mod h1:c3i+UQWmh7LiEpx4sFZnkU36qjEYZ0imhYfXVyQciAY= +google.golang.org/grpc v1.27.0/go.mod h1:qbnxyOmOxrQa7FizSgH+ReBfzJrCY1pSN7KXBS8abTk= +google.golang.org/grpc v1.31.0/go.mod h1:N36X2cJ7JwdamYAgDz+s+rVMFjt3numwzf/HckM8pak= +google.golang.org/grpc v1.33.2/go.mod h1:JMHMWHQWaTccqQQlmk3MJZS+GWXOdAesneDmEnv2fbc= +google.golang.org/grpc v1.59.0 h1:Z5Iec2pjwb+LEOqzpB2MR12/eKFhDPhuqW91O+4bwUk= +google.golang.org/grpc v1.59.0/go.mod h1:aUPDwccQo6OTjy7Hct4AfBPD1GptF4fyUjIkQ9YtF98= +google.golang.org/grpc/examples v0.0.0-20201130180447-c456688b1860/go.mod h1:Ly7ZA/ARzg8fnPU9TyZIxoz33sEUuWX7txiqs8lPTgE= +google.golang.org/protobuf v0.0.0-20200109180630-ec00e32a8dfd/go.mod h1:DFci5gLYBciE7Vtevhsrf46CRTquxDuWsQurQQe4oz8= +google.golang.org/protobuf v0.0.0-20200221191635-4d8936d0db64/go.mod h1:kwYJMbMJ01Woi6D6+Kah6886xMZcty6N08ah7+eCXa0= +google.golang.org/protobuf v0.0.0-20200228230310-ab0ca4ff8a60/go.mod h1:cfTl7dwQJ+fmap5saPgwCLgHXTUD7jkjRqWcaiX5VyM= +google.golang.org/protobuf v1.20.1-0.20200309200217-e05f789c0967/go.mod h1:A+miEFZTKqfCUM6K7xSMQL9OKL/b6hQv+e19PK+JZNE= +google.golang.org/protobuf v1.21.0/go.mod h1:47Nbq4nVaFHyn7ilMalzfO3qCViNmqZ2kzikPIcrTAo= +google.golang.org/protobuf v1.22.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.0/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.23.1-0.20200526195155-81db48ad09cc/go.mod h1:EGpADcykh3NcUnDUJcl1+ZksZNG86OlYog2l/sGQquU= +google.golang.org/protobuf v1.24.0/go.mod h1:r/3tXBNzIEhYS9I1OUVjXDlt8tc493IdKGjtUeSXeh4= +google.golang.org/protobuf v1.25.0/go.mod h1:9JNX74DMeImyA3h4bdi1ymwjUzf21/xIlbajtzgsN7c= +google.golang.org/protobuf v1.26.0-rc.1/go.mod h1:jlhhOSvTdKEhbULTjvd4ARK9grFBp09yW+WbY/TyQbw= +google.golang.org/protobuf v1.26.0/go.mod h1:9q0QmTI4eRPtz6boOQmLYwt+qCgq0jsYwAQnmE0givc= +google.golang.org/protobuf v1.31.0 h1:g0LDEJHgrBl9N9r17Ru3sqWhkIx2NB67okBHPwC7hs8= +google.golang.org/protobuf v1.31.0/go.mod h1:HV8QOd/L58Z+nl8r43ehVNZIU/HEI6OcFqwMG9pJV4I= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/square/go-jose.v2 v2.4.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= +gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= +honnef.co/go/tools v0.0.0-20190523083050-ea95bdfd59fc/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4= diff --git a/main.go b/main.go index 82418ab..f5f8848 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,7 @@ -// Copyright (c) 2021 Doc.ai and/or its affiliates. -// // Copyright (c) 2023 Cisco and/or its affiliates. // +// Copyright (c) 2023 Doc.ai and/or its affiliates. +// // SPDX-License-Identifier: Apache-2.0 // // Licensed under the Apache License, Version 2.0 (the "License"); @@ -18,5 +18,223 @@ package main +import ( + "context" + "crypto/tls" + "encoding/json" + "fmt" + "net/http" + "os" + "os/signal" + "strings" + "syscall" + "time" + + "github.com/networkservicemesh/api/pkg/api/networkservice" + "github.com/networkservicemesh/api/pkg/api/registry" + + nested "github.com/antonfisher/nested-logrus-formatter" + "github.com/gorilla/mux" + "github.com/sirupsen/logrus" + "github.com/spiffe/go-spiffe/v2/spiffetls/tlsconfig" + "github.com/spiffe/go-spiffe/v2/svid/x509svid" + "github.com/spiffe/go-spiffe/v2/workloadapi" + "google.golang.org/grpc" + "google.golang.org/grpc/credentials" + + "github.com/networkservicemesh/sdk/pkg/tools/log" + "github.com/networkservicemesh/sdk/pkg/tools/log/logruslogger" + "github.com/networkservicemesh/sdk/pkg/tools/spiffejwt" + "github.com/networkservicemesh/sdk/pkg/tools/token" + "github.com/networkservicemesh/sdk/pkg/tools/tracing" +) + +const registryAddr = "registry:5002" + +func handleError(w http.ResponseWriter, err error) { + fmt.Println("Error: ", err) + http.Error(w, "Error encoding JSON", http.StatusInternalServerError) +} + func main() { + ctx, cancel := signal.NotifyContext( + context.Background(), + os.Interrupt, + syscall.SIGHUP, + syscall.SIGTERM, + syscall.SIGQUIT, + ) + defer cancel() + + // Setup logger + log.EnableTracing(true) + logrus.SetFormatter(&nested.Formatter{}) + ctx = log.WithLog(ctx, logruslogger.New(ctx, map[string]interface{}{"cmd": os.Args[:1]})) + logger := log.FromContext(ctx) + + // Setup Spire + source, err2 := workloadapi.NewX509Source(ctx) + if err2 != nil { + logger.Fatalf("Error getting x509 source: %v", err2.Error()) + } + var svid *x509svid.SVID + svid, err2 = source.GetX509SVID() + if err2 != nil { + logger.Fatalf("Error getting x509 svid: %v", err2.Error()) + } + logger.Infof("sVID: %q", svid.ID) + + tlsClientConfig := tlsconfig.MTLSClientConfig(source, source, tlsconfig.AuthorizeAny()) + tlsClientConfig.MinVersion = tls.VersionTLS12 + + // Configure and run REST API server + configureAndRunRESTServer(logger) + + // Create gRPC dial options + dialOptions := append(tracing.WithTracingDial(), + grpc.WithDefaultCallOptions( + grpc.WaitForReady(true), + grpc.PerRPCCredentials(token.NewPerRPCCredentials(spiffejwt.TokenGeneratorFunc(source, time.Duration(10)*time.Minute))), + ), + grpc.WithTransportCredentials(credentials.NewTLS(tlsClientConfig)), + ) + + // Monitor Connections + for ; ctx.Err() == nil; time.Sleep(time.Millisecond * 100) { + var nseChannel = getNseChannel(ctx, logger, dialOptions) + + for ctx.Err() == nil { + select { + case <-ctx.Done(): + return + case nse, ok := <-nseChannel: + if !ok { + break + } + + nsmgrAddr := strings.Join(strings.Split(strings.TrimPrefix(nse.GetNetworkServiceEndpoint().Url, "tcp://["), "]:"), ":") + + if !nsmgrs[nsmgrAddr] { + nsmgrs[nsmgrAddr] = true + logger.Infof("Extracted NSMGR addr: %q", nsmgrAddr) + + // Start a goroutine for each nsmgr + go func() { + nsmgrConn, err6 := grpc.Dial(nsmgrAddr, dialOptions...) + if err6 != nil { + logger.Errorf("Failed dial to NSMgr %q: %v", nsmgrAddr, err6.Error()) + return + } + + clientConnections := networkservice.NewMonitorConnectionClient(nsmgrConn) + + stream, err7 := clientConnections.MonitorConnections(ctx, &networkservice.MonitorScopeSelector{}) + if err7 != nil { + logger.Errorf("Error from MonitorConnectionClient: %v", err7.Error()) + return + } + + for ctx.Err() == nil { + event, err8 := stream.Recv() + if err8 != nil { + logger.Errorf("Error from %q nsmgr monitorConnection stream: %v", nsmgrAddr, err8.Error()) + time.Sleep(time.Millisecond * 500) + cleanupManager(logger, nsmgrAddr) + break + } + updateConnections(logger, nsmgrAddr, event) + } + }() + } + } + } + } +} + +func getNseChannel(ctx context.Context, logger log.Logger, dialOptions []grpc.DialOption) <-chan *registry.NetworkServiceEndpointResponse { + conn, err4 := grpc.Dial(registryAddr, dialOptions...) + if err4 != nil { + logger.Errorf("Failed dial to Registry: %v", err4.Error()) + } + + clientNse := registry.NewNetworkServiceEndpointRegistryClient(conn) + + streamNse, err5 := clientNse.Find(ctx, ®istry.NetworkServiceEndpointQuery{ + Watch: true, + NetworkServiceEndpoint: ®istry.NetworkServiceEndpoint{ + NetworkServiceNames: []string{"forwarder"}, + }, + }) + if err5 != nil { + logger.Errorf("Failed to perform Find NSE request: %v", err5) + } + + return registry.ReadNetworkServiceEndpointChannel(streamNse) +} + +func updateConnections(logger log.Logger, nsmgrAddr string, event *networkservice.ConnectionEvent) { + for _, connection := range event.Connections { + connectionID := generateConnectionID(connection) + logger.Infof("Handling %q event on %q nsmgr connection: %q", event.Type.String(), nsmgrAddr, connectionID) + switch { + case event.Type == networkservice.ConnectionEventType_DELETE: + delete(connections, connectionID) + removeManagerConnection(nsmgrAddr, connectionID) + default: // INITIAL_STATE_TRANSFER and UPDATE event types + connections[connectionID] = connection + addManagerConnection(nsmgrAddr, connectionID) + } + } + parceConnectionsToGraphicalModel() +} + +func cleanupManager(logger log.Logger, nsmgrAddr string) { + for connectionID := range managerConnections[nsmgrAddr] { + logger.Infof("Removing %q nsmgr connection: %q", nsmgrAddr, connectionID) + delete(connections, connectionID) + } + delete(managerConnections, nsmgrAddr) + delete(nsmgrs, nsmgrAddr) + parceConnectionsToGraphicalModel() +} + +func configureAndRunRESTServer(logger log.Logger) { + r := mux.NewRouter() + + r.HandleFunc("/nodes", getNodesHandler).Methods("GET") + r.HandleFunc("/edges", getEdgesHandler).Methods("GET") + + http.Handle("/", r) + go func() { + server := &http.Server{ + Addr: ":3001", + Handler: nil, + ReadTimeout: 10 * time.Second, + WriteTimeout: 10 * time.Second, + } + + if err3 := server.ListenAndServe(); err3 != nil { + logger.Fatalf("Error starting server: %v", err3) + } + }() +} + +func getNodesHandler(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000") + err := json.NewEncoder(w).Encode(storageData.Nodes) + if err != nil { + handleError(w, err) + return + } +} + +func getEdgesHandler(w http.ResponseWriter, _ *http.Request) { + w.Header().Set("Content-Type", "application/json") + w.Header().Set("Access-Control-Allow-Origin", "http://localhost:3000") + err := json.NewEncoder(w).Encode(storageData.Edges) + if err != nil { + handleError(w, err) + return + } } diff --git a/model.go b/model.go new file mode 100644 index 0000000..ec88270 --- /dev/null +++ b/model.go @@ -0,0 +1,300 @@ +// Copyright (c) 2023 Cisco and/or its affiliates. +// +// Copyright (c) 2023 Doc.ai and/or its affiliates. +// +// 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 ( + "crypto/sha256" + "encoding/hex" + "fmt" + "strings" + + "github.com/networkservicemesh/api/pkg/api/networkservice" +) + +// Node is a graphical model entity +type Node struct { + Data struct { + ID string `json:"id"` + Type NodeType `json:"type"` + Label string `json:"label"` + Parent string `json:"parent,omitempty"` + CustomData map[string]interface{} `json:"customData,omitempty"` + } `json:"data"` +} + +// Edge is a graphical model connection +type Edge struct { + Data struct { + ID string `json:"id"` + Type EdgeType `json:"type"` + Label string `json:"label,omitempty"` + Source string `json:"source"` + Target string `json:"target"` + Healthy bool `json:"healthy,omitempty"` + CustomData map[string]interface{} `json:"customData,omitempty"` + } `json:"data"` +} + +// NodeType is a Node entity type +type NodeType string + +const ( + clusterNT NodeType = "cluster" + interfaceNT NodeType = "interface" + clientNT NodeType = "client" + forwarderNT NodeType = "forwarder" + managerNT NodeType = "manager" + endpointNT NodeType = "endpoint" + serviceNT NodeType = "service" + // registryNT NodeType = "registry" +) + +// EdgeType is a connection type +type EdgeType string + +const ( + interfaceConnection EdgeType = "interfaceConnection" + interfaceCrossConnection EdgeType = "interfaceCrossConnection" + // serviceRequest EdgeType = "serviceRequest" + // registryRequest EdgeType = "registryRequest" +) + +const ( + clientInterface string = "client_interface" + serverInterface string = "server_interface" +) + +// StorageData is a current graphical model state +type StorageData struct { + Nodes []Node + Edges []Edge +} + +var storageData StorageData + +func updateStorage(nodes []Node, edges []Edge) { + storageData.Nodes = nodes + storageData.Edges = edges +} + +var connections = make(map[string]*networkservice.Connection) +var nsmgrs = make(map[string]bool) +var managerConnections = make(map[string]map[string]bool) + +func addManagerConnection(mgr, conn string) { + if _, ok := managerConnections[mgr]; !ok { + managerConnections[mgr] = make(map[string]bool) + } + managerConnections[mgr][conn] = true +} + +func removeManagerConnection(mgr, conn string) { + if conns, ok := managerConnections[mgr]; ok { + delete(conns, conn) + if len(conns) == 0 { + delete(managerConnections, mgr) + } + } +} + +func parceConnectionsToGraphicalModel() { + nodeMap := make(map[string]Node) + var edges []Edge + + // Add local cluster node + var clusterLocal Node + if len(connections) > 0 { + clusterLocal = makeLocalCluster(nodeMap) + } + + for connectionID, conn := range connections { + healthy := conn.GetState() != networkservice.State_DOWN + + // Add Network Service Node + ns := makeNetworkService(nodeMap, conn.GetNetworkService(), clusterLocal.Data.ID) + + // Create all path segment nodes, interfaces and interface connections + pathSegments := conn.GetPath().GetPathSegments() + var previousInterfaceID string + for _, segment := range pathSegments { + segmentType := getPathSegmentType(segment.GetName()) + + // Add segment node + node := makeSegmentNode(nodeMap, segment.GetName(), clusterLocal.Data.ID, ns.Data.ID, segmentType) + + switch { + case segmentType == clientNT: + interfID := fmt.Sprintf("int-c--%s--%s", connectionID, node.Data.ID) + nodeMap[interfID] = makeInterface(interfID, node.Data.ID, getInterfaceLabelFromMetrics(segment, clientInterface)) + previousInterfaceID = interfID + case segmentType == endpointNT: + interfID := fmt.Sprintf("int-s--%s--%s", connectionID, node.Data.ID) + nodeMap[interfID] = makeInterface(interfID, node.Data.ID, getInterfaceLabelFromMetrics(segment, serverInterface)) + edges = addEdge(edges, previousInterfaceID, interfID, interfaceConnection, healthy) + case segmentType == forwarderNT: + interfCID := fmt.Sprintf("int-c--%s--%s", connectionID, node.Data.ID) + nodeMap[interfCID] = makeInterface(interfCID, node.Data.ID, getInterfaceLabelFromMetrics(segment, clientInterface)) + edges = addEdge(edges, previousInterfaceID, interfCID, interfaceConnection, healthy) + interfEID := fmt.Sprintf("int-s--%s--%s", connectionID, node.Data.ID) + nodeMap[interfEID] = makeInterface(interfEID, node.Data.ID, getInterfaceLabelFromMetrics(segment, serverInterface)) + edges = addEdge(edges, interfCID, interfEID, interfaceCrossConnection, healthy) + previousInterfaceID = interfEID + // TODO Aggregate statistics for the Overview page + } + } + } + + edges = addInternalNSEInterfaceConnections(edges, nodeMap) + + updateStorage(mapToArray(nodeMap), edges) +} + +func addInternalNSEInterfaceConnections(edges []Edge, nodeMap map[string]Node) []Edge { + var endpoints []Node + for _, node := range nodeMap { + if node.Data.Type == endpointNT { + endpoints = append(endpoints, node) + } + } + for _, nse := range endpoints { + var interfaces []Node + for _, node := range nodeMap { + if node.Data.Parent == nse.Data.ID { + interfaces = append(interfaces, node) + } + } + if len(interfaces) > 1 { + for i := 0; i < len(interfaces)-1; i++ { + for j := i + 1; j < len(interfaces)-1; j++ { + edges = addEdge(edges, interfaces[i].Data.ID, interfaces[j].Data.ID, interfaceCrossConnection, true) + } + } + } + } + return edges +} + +func makeLocalCluster(nodeMap map[string]Node) Node { + clusterLocal := Node{} + clusterLocalName := "cluster-local" + clusterLocal.Data.ID = clusterLocalName + clusterLocal.Data.Type = clusterNT + clusterLocal.Data.Label = clusterLocalName + nodeMap[clusterLocal.Data.ID] = clusterLocal + return clusterLocal +} + +func makeNetworkService(nodeMap map[string]Node, id, parentID string) Node { + ns := Node{} + ns.Data.ID = id + if !duplicateNodeExists(nodeMap, ns.Data.ID) { + ns.Data.Type = serviceNT + ns.Data.Label = id + ns.Data.Parent = parentID + nodeMap[ns.Data.ID] = ns + } + return ns +} + +func makeSegmentNode(nodeMap map[string]Node, id, parentID, nsID string, segmentType NodeType) Node { + node := Node{} + node.Data.ID = id + if !duplicateNodeExists(nodeMap, node.Data.ID) { + node.Data.Type = segmentType + node.Data.Parent = parentID + if node.Data.Type == endpointNT { + node.Data.CustomData = make(map[string]interface{}) + node.Data.CustomData["networkService"] = nsID + } + node.Data.Label = id + nodeMap[node.Data.ID] = node + } + return node +} + +func makeInterface(id, parentID, label string) Node { + interf := Node{} + interf.Data.ID = id + interf.Data.Type = interfaceNT + interf.Data.Parent = parentID + interf.Data.Label = label + return interf +} + +func addEdge(edges []Edge, sourceID, targetID string, edgeType EdgeType, healthy bool) []Edge { + edge := Edge{} + edge.Data.ID = fmt.Sprintf("conn--%s--%s", sourceID, targetID) + edge.Data.Type = edgeType + edge.Data.Source = sourceID + edge.Data.Target = targetID + edge.Data.Healthy = healthy + return append(edges, edge) +} + +func getInterfaceLabelFromMetrics(segment *networkservice.PathSegment, interfaceType string) string { + metrics := segment.GetMetrics() + if metrics != nil && metrics[interfaceType] != "" { + return metrics[interfaceType] + } + return "unknown" +} + +func duplicateNodeExists(nodeMap map[string]Node, id string) bool { + if _, exists := nodeMap[id]; exists { + return true + } + return false +} + +func mapToArray(nodeMap map[string]Node) []Node { + nodes := make([]Node, 0, len(nodeMap)) + for _, node := range nodeMap { + nodes = append(nodes, node) + } + return nodes +} + +func getPathSegmentType(name string) NodeType { + switch { + case strings.HasPrefix(name, "nsmgr-"): + return managerNT + case strings.HasPrefix(name, "forwarder-") || strings.HasPrefix(name, "fwd-"): + return forwarderNT + case strings.HasPrefix(name, "nse-"): + return endpointNT + default: + return clientNT + } +} + +func generateConnectionID(connection *networkservice.Connection) string { + ids := "" + for _, segment := range connection.GetPath().GetPathSegments() { + ids += segment.GetId() + } + return generateHash(ids) +} + +func generateHash(input string) string { + hasher := sha256.New() + hasher.Write([]byte(input)) + hashBytes := hasher.Sum(nil) + hash := hex.EncodeToString(hashBytes) + return hash +}