generated from networkservicemesh/cmd-template
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: Vitaliy Guschin <[email protected]>
- Loading branch information
Vitaliy Guschin
committed
Dec 5, 2023
1 parent
1285ddf
commit b78ee65
Showing
8 changed files
with
740 additions
and
108 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -16,3 +16,6 @@ | |
|
||
.idea/ | ||
junit/ | ||
nohup.out | ||
.DS_Store | ||
.vscode |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,22 +1,14 @@ | ||
FROM golang:1.20.5-buster as go | ||
ENV GO111MODULE=on | ||
ENV CGO_ENABLED=0 | ||
ENV GOBIN=/bin | ||
RUN go install github.com/go-delve/delve/cmd/[email protected] | ||
ADD https://github.com/spiffe/spire/releases/download/v1.2.2/spire-1.2.2-linux-x86_64-glibc.tar.gz . | ||
RUN tar xzvf spire-1.2.2-linux-x86_64-glibc.tar.gz -C /bin --strip=2 spire-1.2.2/bin/spire-server spire-1.2.2/bin/spire-agent | ||
FROM golang:1.20.11 | ||
|
||
FROM go as build | ||
WORKDIR /build | ||
COPY . . | ||
RUN go build -o /bin/app . | ||
WORKDIR /backend | ||
COPY dashboard-backend.go /backend | ||
COPY model.go /backend | ||
COPY go.mod /backend | ||
COPY go.sum /backend | ||
|
||
FROM build as test | ||
CMD go test -test.v ./... | ||
RUN go build -o dashboard-backend | ||
|
||
FROM test as debug | ||
CMD dlv -l :40000 --headless=true --api-version=2 test -test.v ./... | ||
# Expose port for REST server | ||
EXPOSE 3001 | ||
|
||
FROM alpine as runtime | ||
COPY --from=build /bin/app /bin/app | ||
CMD /bin/app | ||
CMD ["./dashboard-backend"] |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 [email protected]: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. | ||
1. `git clone [email protected]: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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,210 @@ | ||
// Copyright (c) 2020 Cisco and/or its affiliates. | ||
// | ||
// Copyright (c) 2020-2021 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 ( | ||
"context" | ||
"crypto/tls" | ||
"encoding/json" | ||
"fmt" | ||
"net/http" | ||
"os" | ||
"strconv" | ||
"strings" | ||
"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/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" | ||
"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" | ||
) | ||
|
||
const registryAddr = "registry:5002" | ||
|
||
func getNodesHandler(w http.ResponseWriter, _ *http.Request) { | ||
fmt.Println("GET /nodes requested") | ||
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) { | ||
fmt.Println("GET /edges requested") | ||
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 | ||
} | ||
} | ||
|
||
func handleError(w http.ResponseWriter, err error) { | ||
fmt.Println("Error: ", err) | ||
http.Error(w, "Error encoding JSON", http.StatusInternalServerError) | ||
} | ||
|
||
func main() { | ||
ctx, cancel := context.WithCancel(context.Background()) | ||
defer cancel() | ||
|
||
// Read polling interval env variable | ||
pollingIntervalSeconds := readPollingInterval() | ||
|
||
// 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 | ||
|
||
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)), | ||
) | ||
|
||
nsmgrs := getNsmgrsInfo(logger, dialOptions) | ||
|
||
// Monitor Connections | ||
for { | ||
connections := make(map[string]*networkservice.Connection) | ||
for _, addr := range nsmgrs { | ||
grpcConn, err6 := grpc.Dial(addr, dialOptions...) | ||
if err6 != nil { | ||
logger.Fatalf("Failed dial to NSMgr %q: %v", addr, err6.Error()) | ||
} | ||
|
||
client := networkservice.NewMonitorConnectionClient(grpcConn) | ||
|
||
stream, err7 := client.MonitorConnections(context.Background(), &networkservice.MonitorScopeSelector{}) | ||
if err7 != nil { | ||
logger.Fatalf("Error from MonitorConnectionClient: %v", err7.Error()) | ||
} | ||
|
||
event, err8 := stream.Recv() | ||
if err8 != nil { | ||
logger.Errorf("Error from monitorConnection stream: %v", err8.Error()) | ||
} else { | ||
logger.Infof("Received %d connections from %q", len(event.Connections), addr) | ||
for _, connection := range event.Connections { | ||
connectionID := generateConnectionID(connection) | ||
if _, exists := connections[connectionID]; !exists { | ||
connections[connectionID] = connection | ||
} | ||
} | ||
} | ||
} | ||
logger.Infof("Parce connections. Size: %d", len(connections)) | ||
parceConnectionsToGraphicalModel(connections) | ||
time.Sleep(time.Duration(pollingIntervalSeconds) * time.Second) | ||
} | ||
} | ||
|
||
func readPollingInterval() int { | ||
pollingIntervalSecondsStr := os.Getenv("POLLING_INTERVAL_SECONDS") | ||
pollingIntervalSeconds, err1 := strconv.Atoi(pollingIntervalSecondsStr) | ||
if err1 != nil { | ||
pollingIntervalSeconds = 1 | ||
} | ||
return pollingIntervalSeconds | ||
} | ||
|
||
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 getNsmgrsInfo(logger log.Logger, dialOptions []grpc.DialOption) []string { | ||
conn, err4 := grpc.Dial(registryAddr, dialOptions...) | ||
if err4 != nil { | ||
logger.Fatalf("Failed dial to Registry: %v", err4.Error()) | ||
} | ||
|
||
clientNse := registry.NewNetworkServiceEndpointRegistryClient(conn) | ||
|
||
streamNse, err5 := clientNse.Find(context.Background(), ®istry.NetworkServiceEndpointQuery{ | ||
NetworkServiceEndpoint: ®istry.NetworkServiceEndpoint{ | ||
NetworkServiceNames: []string{"forwarder"}, | ||
}, | ||
}) | ||
if err5 != nil { | ||
logger.Fatalf("Failed to perform Find NSE request: %v", err5) | ||
} | ||
|
||
var nsmgrs []string | ||
var nseList = registry.ReadNetworkServiceEndpointList(streamNse) | ||
for _, nse := range nseList { | ||
nsmgrAddr := strings.Join(strings.Split(strings.TrimPrefix(nse.Url, "tcp://["), "]:"), ":") | ||
logger.Infof("Extracted NSMGR addr: %q", nsmgrAddr) | ||
nsmgrs = append(nsmgrs, nsmgrAddr) | ||
} | ||
return nsmgrs | ||
} |
Oops, something went wrong.