Skip to content

Commit 82e3505

Browse files
authored
feat: replace config file with command args for network discovery (#33)
1 parent 61a8df7 commit 82e3505

File tree

11 files changed

+151
-184
lines changed

11 files changed

+151
-184
lines changed

network-discovery/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ COMMIT_SHA := $(shell git rev-parse --short HEAD)
88

99
.PHONY: build
1010
build:
11-
CGO_ENABLED=$(CGO_ENABLED) GOOS=linux GOARCH=$(GOARCH) GOARM=$(GOARM) go build -mod=mod -o ${BUILD_DIR}/network-discovery cmd/main.go
11+
CGO_ENABLED=$(CGO_ENABLED) GOOS=$(GOOS) GOARCH=$(GOARCH) GOARM=$(GOARM) go build -mod=mod -o ${BUILD_DIR}/network-discovery cmd/main.go
1212

1313
.PHONY: lint
1414
lint:

network-discovery/README.md

Lines changed: 34 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -1,51 +1,58 @@
11
# network-discovery
22
Orb network discovery backend
33

4-
### Config RFC
5-
```yaml
6-
diode:
7-
config:
8-
target: grpc://localhost:8080/diode
9-
api_key: ${DIODE_API_KEY}
10-
network_discovery:
11-
config:
12-
host: 0.0.0.0
13-
port: 8072
14-
log_level: info
15-
log_format: json
4+
### Usage
5+
```sh
6+
Usage of network-discovery:
7+
-diode-api-key string
8+
diode api key (REQUIRED). Environment variables can be used by wrapping them in ${} (e.g. ${MY_API_KEY})
9+
-diode-target string
10+
diode target (REQUIRED)
11+
-help
12+
show this help
13+
-host string
14+
server host (default "0.0.0.0")
15+
-log-format string
16+
log format (default "TEXT")
17+
-log-level string
18+
log level (default "INFO")
19+
-port int
20+
server port (default 8073)
1621
```
1722

1823
### Policy RFC
1924
```yaml
20-
network_discovery:
21-
policies:
22-
network_1:
23-
config:
24-
schedule: "* * * * *" #Cron expression
25-
timeout: 5 #default 2 minutes
26-
scope:
27-
targets: [192.168.1.0/24]
28-
discover_once: # will run only once
29-
scope:
30-
targets:
31-
- 192.168.0.34/24
32-
- google.com
25+
policies:
26+
network_1:
27+
config:
28+
schedule: "* * * * *" #Cron expression
29+
timeout: 5 #default 2 minutes
30+
scope:
31+
targets: [192.168.1.0/24]
32+
discover_once: # will run only once
33+
scope:
34+
targets:
35+
- 192.168.0.34/24
36+
- google.com
3337
```
3438
## Run device-discovery
3539
device-discovery can be run by installing it with pip
3640
```sh
3741
git clone https://github.com/netboxlabs/orb-discovery.git
3842
cd network-discovery/
3943
make bin
40-
build/network-discovery -c config.yaml
44+
build/network-discovery --diode-target grpc://192.168.31.114:8080/diode --diode-api-key '${DIODE_API_KEY}'
4145
```
4246

4347
## Docker Image
4448
device-discovery can be build and run using docker:
4549
```sh
4650
cd network-discovery/
4751
docker build --no-cache -t network-discovery:develop -f docker/Dockerfile .
48-
docker run -v /local/orb:/usr/local/orb/ --net=host device-discovery:develop network-discovery -c /usr/local/orb/config.yaml
52+
docker run --net=host -e DIODE_API_KEY={YOUR_API_KEY} \
53+
network-discovery:develop network-discovery \
54+
--diode-target grpc://192.168.31.114:8080/diode \
55+
--diode-api-key '${DIODE_API_KEY}'
4956
```
5057

5158
### Routes (v1)

network-discovery/cmd/main.go

Lines changed: 34 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -6,11 +6,10 @@ import (
66
"fmt"
77
"os"
88
"os/signal"
9+
"strings"
910
"syscall"
1011

11-
"github.com/a8m/envsubst"
1212
"github.com/netboxlabs/diode-sdk-go/diode"
13-
"gopkg.in/yaml.v3"
1413

1514
"github.com/netboxlabs/orb-discovery/network-discovery/config"
1615
"github.com/netboxlabs/orb-discovery/network-discovery/policy"
@@ -21,59 +20,60 @@ import (
2120
// AppName is the application name
2221
const AppName = "network-discovery"
2322

23+
func resolveEnv(value string) string {
24+
// Check if the value starts with ${ and ends with }
25+
if strings.HasPrefix(value, "${") && strings.HasSuffix(value, "}") {
26+
// Extract the environment variable name
27+
envVar := value[2 : len(value)-1]
28+
// Get the value of the environment variable
29+
envValue := os.Getenv(envVar)
30+
if envValue != "" {
31+
return envValue
32+
}
33+
fmt.Printf("error: environment variable %s is not set\n", envVar)
34+
os.Exit(1)
35+
}
36+
// Return the original value if no substitution occurs
37+
return value
38+
}
39+
2440
func main() {
25-
configPath := flag.String("config", "", "path to the configuration file (required)")
41+
host := flag.String("host", "0.0.0.0", "server host")
42+
port := flag.Int("port", 8073, "server port")
43+
diodeTarget := flag.String("diode-target", "", "diode target (REQUIRED)")
44+
diodeAPIKey := flag.String("diode-api-key", "", "diode api key (REQUIRED)."+
45+
" Environment variables can be used by wrapping them in ${} (e.g. ${MY_API_KEY})")
46+
logLevel := flag.String("log-level", "INFO", "log level")
47+
logFormat := flag.String("log-format", "TEXT", "log format")
48+
help := flag.Bool("help", false, "show this help")
2649

2750
flag.Parse()
2851

29-
if *configPath == "" {
52+
if *help || *diodeTarget == "" || *diodeAPIKey == "" {
3053
fmt.Fprintf(os.Stderr, "Usage of network-discovery:\n")
3154
flag.PrintDefaults()
32-
os.Exit(1)
33-
34-
}
35-
if _, err := os.Stat(*configPath); os.IsNotExist(err) {
36-
fmt.Printf("configuration file '%s' does not exist", *configPath)
37-
os.Exit(1)
38-
}
39-
fileData, err := envsubst.ReadFile(*configPath)
40-
if err != nil {
41-
fmt.Printf("error reading configuration file: %v", err)
42-
os.Exit(1)
43-
}
44-
45-
cfg := config.Config{
46-
Network: config.Network{
47-
Config: config.StartupConfig{
48-
Host: "0.0.0.0",
49-
Port: 8073,
50-
LogLevel: "INFO",
51-
LogFormat: "TEXT",
52-
}},
53-
}
54-
55-
if err = yaml.Unmarshal(fileData, &cfg); err != nil {
56-
fmt.Printf("error parsing configuration file: %v\n", err)
55+
if *help {
56+
os.Exit(0)
57+
}
5758
os.Exit(1)
5859
}
5960

6061
client, err := diode.NewClient(
61-
cfg.Diode.Config.Target,
62+
*diodeTarget,
6263
AppName,
6364
version.GetBuildVersion(),
64-
diode.WithAPIKey(cfg.Diode.Config.APIKey),
65+
diode.WithAPIKey(resolveEnv(*diodeAPIKey)),
6566
)
6667
if err != nil {
6768
fmt.Printf("error creating diode client: %v\n", err)
6869
os.Exit(1)
6970
}
7071

7172
ctx := context.Background()
72-
logger := config.NewLogger(cfg.Network.Config.LogLevel, cfg.Network.Config.LogFormat)
73+
logger := config.NewLogger(*logLevel, *logFormat)
7374

7475
policyManager := policy.NewManager(ctx, logger, client)
75-
server := server.Server{}
76-
server.Configure(logger, policyManager, version.GetBuildVersion(), cfg.Network.Config)
76+
server := server.NewServer(*host, *port, logger, policyManager, version.GetBuildVersion())
7777

7878
// handle signals
7979
done := make(chan bool, 1)

network-discovery/config/config.go

Lines changed: 2 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -27,33 +27,7 @@ type Policy struct {
2727
Scope Scope `yaml:"scope"`
2828
}
2929

30-
// StartupConfig represents the configuration of the network-discovery service
31-
type StartupConfig struct {
32-
Host string `yaml:"host"`
33-
Port int32 `yaml:"port"`
34-
LogLevel string `yaml:"log_level"`
35-
LogFormat string `yaml:"log_format"`
36-
}
37-
38-
// Network represents the network-discovery configuration
39-
type Network struct {
40-
Config StartupConfig `yaml:"config"`
30+
// Policies represents a collection of network-discovery policies
31+
type Policies struct {
4132
Policies map[string]Policy `mapstructure:"policies"`
4233
}
43-
44-
// DiodeConfig represents the configuration of diode service
45-
type DiodeConfig struct {
46-
Target string `yaml:"target"`
47-
APIKey string `yaml:"api_key"`
48-
}
49-
50-
// Diode represents the root configuration of diode service
51-
type Diode struct {
52-
Config DiodeConfig `yaml:"config"`
53-
}
54-
55-
// Config represents the root configuration of the network-discovery service
56-
type Config struct {
57-
Diode Diode `yaml:"diode"`
58-
Network Network `yaml:"network_discovery"`
59-
}

network-discovery/go.mod

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ go 1.23.3
44

55
require (
66
github.com/Ullaakut/nmap/v3 v3.0.4
7-
github.com/a8m/envsubst v1.4.2
87
github.com/gin-gonic/gin v1.10.0
98
github.com/go-co-op/gocron/v2 v2.12.4
109
github.com/netboxlabs/diode-sdk-go v0.2.0

network-discovery/go.sum

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
github.com/Ullaakut/nmap/v3 v3.0.4 h1:ZwRM4gbE0gtoXipC7uTHs2wvf3r4S8d9mtqfTabXE34=
22
github.com/Ullaakut/nmap/v3 v3.0.4/go.mod h1:dd5K68P7LHc5nKrFwQx6EdTt61O9UN5x3zn1R4SLcco=
3-
github.com/a8m/envsubst v1.4.2 h1:4yWIHXOLEJHQEFd4UjrWDrYeYlV7ncFWJOCBRLOZHQg=
4-
github.com/a8m/envsubst v1.4.2/go.mod h1:MVUTQNGQ3tsjOOtKCNd+fl8RzhsXcDvvAEzkhGtlsbY=
53
github.com/bytedance/sonic v1.11.6 h1:oUp34TzMlL+OY1OUWxHqsdkgC/Zfc85zGqw9siXjrc0=
64
github.com/bytedance/sonic v1.11.6/go.mod h1:LysEHSvpvDySVdC2f87zGWf6CIKJcAvqab1ZaiQtds4=
75
github.com/bytedance/sonic/loader v0.1.1 h1:c+e5Pt1k/cy5wMveRDyk2X4B9hF4g7an8N3zCYjJFNM=

network-discovery/policy/manager.go

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,16 +32,16 @@ func NewManager(ctx context.Context, logger *slog.Logger, client diode.Client) *
3232

3333
// ParsePolicies parses the policies from the request
3434
func (m *Manager) ParsePolicies(data []byte) (map[string]config.Policy, error) {
35-
var payload config.Config
35+
var payload config.Policies
3636
if err := yaml.Unmarshal(data, &payload); err != nil {
3737
return nil, err
3838
}
3939

40-
if payload.Network.Policies == nil {
40+
if len(payload.Policies) == 0 {
4141
return nil, errors.New("no policies found in the request")
4242
}
4343

44-
return payload.Network.Policies, nil
44+
return payload.Policies, nil
4545
}
4646

4747
// HasPolicy checks if the policy exists

network-discovery/policy/manager_test.go

Lines changed: 20 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -38,15 +38,14 @@ func TestManagerParsePolicies(t *testing.T) {
3838

3939
t.Run("Valid Policies", func(t *testing.T) {
4040
yamlData := []byte(`
41-
network_discovery:
42-
policies:
43-
policy1:
44-
config:
45-
defaults:
46-
site: New York NY
47-
scope:
48-
targets:
49-
- 192.168.1.1/24
41+
policies:
42+
policy1:
43+
config:
44+
defaults:
45+
site: New York NY
46+
scope:
47+
targets:
48+
- 192.168.1.1/24
5049
`)
5150

5251
policies, err := manager.ParsePolicies(yamlData)
@@ -67,19 +66,18 @@ func TestManagerPolicyLifecycle(t *testing.T) {
6766
logger := slog.New(slog.NewJSONHandler(os.Stdout, &slog.HandlerOptions{Level: slog.LevelDebug, AddSource: false}))
6867
manager := policy.NewManager(context.Background(), logger, nil)
6968
yamlData := []byte(`
70-
network_discovery:
71-
policies:
72-
policy1:
73-
scope:
74-
targets:
75-
- 192.168.1.1/24
76-
policy2:
77-
scope:
78-
targets:
79-
- 192.168.2.1/24
80-
policy3:
81-
scope:
82-
targets: []
69+
policies:
70+
policy1:
71+
scope:
72+
targets:
73+
- 192.168.1.1/24
74+
policy2:
75+
scope:
76+
targets:
77+
- 192.168.2.1/24
78+
policy3:
79+
scope:
80+
targets: []
8381
`)
8482

8583
policies, err := manager.ParsePolicies(yamlData)

network-discovery/policy/runner.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,7 @@ func (r *Runner) run() {
8181
r.logger.Error("error creating scanner", slog.Any("error", err), slog.Any("policy", r.ctx.Value(policyKey)))
8282
return
8383
}
84-
84+
r.logger.Info("running scanner", slog.Any("targets", r.scope.Targets), slog.Any("policy", r.ctx.Value(policyKey)))
8585
result, warnings, err := scanner.Run()
8686
if len(*warnings) > 0 {
8787
r.logger.Warn("run finished with warnings", slog.String("warnings", fmt.Sprintf("%v", *warnings)))

network-discovery/server/server.go

Lines changed: 23 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -31,30 +31,37 @@ type Server struct {
3131
manager *policy.Manager
3232
stat config.Status
3333
logger *slog.Logger
34-
config config.StartupConfig
34+
host string
35+
port int
3536
}
3637

3738
func init() {
3839
gin.SetMode(gin.ReleaseMode)
3940
}
4041

41-
// Configure configures the network-discovery server
42-
func (s *Server) Configure(logger *slog.Logger, manager *policy.Manager, version string, config config.StartupConfig) {
43-
s.stat.Version = version
44-
s.stat.StartTime = time.Now()
45-
s.manager = manager
46-
s.logger = logger
47-
s.config = config
48-
49-
s.router = gin.New()
42+
// NewServer returns a new network-discovery server
43+
func NewServer(host string, port int, logger *slog.Logger, manager *policy.Manager, version string) *Server {
44+
server := &Server{
45+
router: gin.New(),
46+
manager: manager,
47+
stat: config.Status{
48+
Version: version,
49+
StartTime: time.Now(),
50+
},
51+
logger: logger,
52+
host: host,
53+
port: port,
54+
}
5055

51-
v1 := s.router.Group("/api/v1")
56+
v1 := server.router.Group("/api/v1")
5257
{
53-
v1.GET("/status", s.getStatus)
54-
v1.GET("/capabilities", s.getCapabilities)
55-
v1.POST("/policies", s.createPolicy)
56-
v1.DELETE("/policies/:policy", s.deletePolicy)
58+
v1.GET("/status", server.getStatus)
59+
v1.GET("/capabilities", server.getCapabilities)
60+
v1.POST("/policies", server.createPolicy)
61+
v1.DELETE("/policies/:policy", server.deletePolicy)
5762
}
63+
64+
return server
5865
}
5966

6067
// Router returns the router
@@ -65,7 +72,7 @@ func (s *Server) Router() *gin.Engine {
6572
// Start starts the network-discovery server
6673
func (s *Server) Start() {
6774
go func() {
68-
serv := fmt.Sprintf("%s:%d", s.config.Host, s.config.Port)
75+
serv := fmt.Sprintf("%s:%d", s.host, s.port)
6976
s.logger.Info("starting network-discovery server at: " + serv)
7077
if err := s.router.Run(serv); err != nil {
7178
s.logger.Error("shutting down the server", "error", err)

0 commit comments

Comments
 (0)