Skip to content

Commit

Permalink
Merge pull request #39 from netboxlabs/develop
Browse files Browse the repository at this point in the history
release 🚚
  • Loading branch information
leoparente authored Dec 26, 2024
2 parents a825e71 + b2f3d7f commit 798482a
Show file tree
Hide file tree
Showing 38 changed files with 415 additions and 722 deletions.
40 changes: 40 additions & 0 deletions .github/golangci.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
run:
timeout: 5m
modules-download-mode: readonly

output:
formats: colored-line-number

linters:
enable:
- revive
- errcheck
- unused
- staticcheck
- ineffassign
- govet
- gosimple
- bodyclose
- gci
- gofumpt

issues:
exclude-use-default: false
exclude-rules:
- path: /*.go
text: "package-comments: should have a package comment"
linters:
- revive

severity:
default-severity: error

linters-settings:
gci:
sections:
- standard
- default
- prefix(github.com/netboxlabs/orb-agent)
custom-order: true
go-fumpt:
extra-rules: true
29 changes: 29 additions & 0 deletions .github/workflows/lint.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Orb Agent - lint
on:
push:
branches:
- "!release"
pull_request:

permissions:
contents: read

jobs:
golangci:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4
- name: Setup Go
uses: actions/setup-go@v4
with:
go-version: '1.23'
check-latest: true
- name: Lint
uses: golangci/golangci-lint-action@v3
with:
version: v1.62
working-directory: .
args: --config .github/golangci.yaml
skip-pkg-cache: true
skip-build-cache: true
8 changes: 8 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,14 @@ test-coverage:
@cat .coverage/cover.out.tmp | grep -Ev "cmd" > .coverage/cover.out
@go tool cover -func=.coverage/cover.out | grep total | awk '{print substr($$3, 1, length($$3)-1)}' > .coverage/coverage.txt

.PHONY: lint
lint:
@golangci-lint run ./... --config .github/golangci.yaml

.PHONY: fix-lint
fix-lint:
@golangci-lint run ./... --config .github/golangci.yaml --fix

agent:
docker build --no-cache \
--build-arg GOARCH=$(GOARCH) \
Expand Down
46 changes: 25 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,23 +2,20 @@
Orb agent is a component of the NetBox Discovery solution. It provides network discovery and observability capabilities and is developed by NetBox Labs.

## Project status

The Orb agent project is currently in the Public Preview stage. For details, please see [NetBox Labs Product and Feature Lifecycle](https://docs.netboxlabs.com/product_feature_lifecycle/). We actively welcome feedback to help us identify and prioritize bugs, features, and improvements.

## Getting Started
To run `orb-agent`, first pull the Docker image from [Docker Hub](https://hub.docker.com/r/netboxlabs/orb-agent):

To get started with `orb-agent`, first pull the Docker image from [Docker Hub](https://hub.docker.com/r/netboxlabs/orb-agent):

```sh
docker pull netboxlabs/orb-agent:latest
```

## Orb Agent Configuration
To run, the Orb agent requires a configuration file. This configuration file consists of three main sections: `Config Manager`, `Backends`, and `Policies`.

The Orb agent requires a configuration file. This file consists of three main sections: `config_manager`, `backends`, and `policies`.

### Config Manager
The `Config Manager` section specifies how Orb agent should retrieve it's configuration information. The configuration manager is responsible for processing the configuration to retrieve policies and pass them to the appropriate backend.
The `config_manager` section specifies how Orb agent should retrieve it's configuration information. The configuration manager is responsible for processing the configuration to retrieve policies and pass them to the appropriate backend.

```yaml
orb:
Expand All @@ -30,7 +27,7 @@ orb:
Currently, only the `local` manager is supported, which retrieves policies from the local configuration file passed to the agent.

### Backends
The `Backends` section specifies what Orb agent backends should be enabled. Each Orb agent backend offers specific discovery or observability capabilities and may require specific configuration information.
The `backends` section specifies what Orb agent backends should be enabled. Each Orb agent backend offers specific discovery or observability capabilities and may require specific configuration information.

```yaml
orb:
Expand All @@ -42,38 +39,45 @@ orb:
Only the `network_discovery` and `device_discovery` backends are currently supported. They do not require any special configuration.
- [Device Discovery](./docs/backends/device_discovery.md)
- [Network Discovery](./docs/backends/network_discovery.md)
### Commons
A special `common` subsection under `Backends` defines configuration settings that are shared with all backends. Currently, it supports passing [diode](https://github.com/netboxlabs/diode) server settings to all backends.

#### Common
A special `common` subsection under `backends` defines configuration settings that are shared with all backends. Currently, it supports passing [diode](https://github.com/netboxlabs/diode) server settings to all backends.

```yaml
backends:
...
common:
diode:
target: grpc://192.168.0.22:8080/diode
api_key: ${DIODE_API_KEY}
agent_name: agent01
diode:
target: grpc://192.168.0.22:8080/diode
api_key: ${DIODE_API_KEY}
agent_name: agent01
```
### Policies
The `Policies` section specifies what discovery policies should be passed to each backend. Policies define specific settings for discovery (such as scheduling and default properties) and the scope (targets). Backends can run multiple policies simultaneously, but for each backend all policies must have a unique name. These policies are defined in the `policies` section and are grouped under a subsection for each backend:
The `policies` section specifies what discovery policies should be passed to each backend. Policies define specific settings for discovery (such as scheduling and default properties) and the scope (targets). Backends can run multiple policies simultaneously, but for each backend all policies must have a unique name. These policies are defined in the `policies` section and are grouped under a subsection for each backend:

```yaml
orb:
...
policies:
device_discovery:
device_policy_1:
# see device_discovery section
# see docs/backends/device_discovery.md
network_discovery:
network_policy_1:
# see network_discovery section
# see docs/backends/network_discovery.md
```

## Configuration samples
You can find sample configurations [here](./docs/config_samples.md) of how to configure Orb agent to run network and device discoveries.
## Running the agent

## Required Notice
To run `orb-agent`, use the following command from the directory where your created your `agent.yaml` file:

Copyright NetBox Labs, Inc.
```sh
docker run -v $(PWD):/opt/orb/ netboxlabs/orb-agent:latest run -c /opt/orb/agent.yaml
```

### Configuration samples
You can find complete sample configurations [here](./docs/config_samples.md) of how to configure Orb agent to run network and device discoveries, as well as the relevant `docker run` commands.

## Required Notice
Copyright NetBox Labs, Inc.
57 changes: 23 additions & 34 deletions agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@ import (

mqtt "github.com/eclipse/paho.mqtt.golang"
"github.com/google/uuid"
_ "github.com/mattn/go-sqlite3"
"github.com/mitchellh/mapstructure"
"github.com/orb-community/orb/fleet"
"go.uber.org/zap"
Expand All @@ -20,10 +19,9 @@ import (
"github.com/netboxlabs/orb-agent/agent/version"
)

var (
ErrMqttConnection = errors.New("failed to connect to a broker")
)
const routineKey config.ContextKey = "routine"

// Agent is the interface that all agents must implement
type Agent interface {
Start(ctx context.Context, cancelFunc context.CancelFunc) error
Stop(ctx context.Context)
Expand All @@ -35,7 +33,7 @@ type orbAgent struct {
logger *zap.Logger
config config.Config
client mqtt.Client
agent_id string
agentID string
backends map[string]backend.Backend
backendState map[string]*backend.State
backendsCommon config.BackendCommons
Expand All @@ -49,38 +47,29 @@ type orbAgent struct {
heartbeatCancel context.CancelFunc

// Agent RPC channel, configured from command line
baseTopic string
rpcToCoreTopic string
rpcFromCoreTopic string
capabilitiesTopic string
heartbeatsTopic string
logTopic string
baseTopic string
rpcFromCoreTopic string
heartbeatsTopic string

// Retry Mechanism to ensure the Request is received
groupRequestTicker *time.Ticker
groupRequestSucceeded context.CancelFunc
policyRequestTicker *time.Ticker
policyRequestSucceeded context.CancelFunc

// AgentGroup channels sent from core
groupsInfos map[string]GroupInfo
groupsInfos map[string]groupInfo

policyManager manager.PolicyManager
configManager config.ConfigManager
configManager config.Manager
}

const retryRequestDuration = time.Second
const retryRequestFixedTime = 15
const retryDurationIncrPerAttempts = 10
const retryMaxAttempts = 4

type GroupInfo struct {
type groupInfo struct {
Name string
ChannelID string
}

var _ Agent = (*orbAgent)(nil)

// New creates a new agent
func New(logger *zap.Logger, c config.Config) (Agent, error) {
pm, err := manager.New(logger, c)
if err != nil {
Expand All @@ -93,11 +82,10 @@ func New(logger *zap.Logger, c config.Config) (Agent, error) {
}
cm := config.New(logger, c.OrbAgent.ConfigManager)

return &orbAgent{logger: logger, config: c, policyManager: pm, configManager: cm, groupsInfos: make(map[string]GroupInfo)}, nil
return &orbAgent{logger: logger, config: c, policyManager: pm, configManager: cm, groupsInfos: make(map[string]groupInfo)}, nil
}

func (a *orbAgent) managePolicies() error {

if a.config.OrbAgent.Policies == nil {
return errors.New("no policies specified")
}
Expand Down Expand Up @@ -147,7 +135,7 @@ func (a *orbAgent) startBackends(agentCtx context.Context) error {
a.logger.Info("failed to configure backend", zap.String("backend", name), zap.Error(err))
return err
}
backendCtx := context.WithValue(agentCtx, "routine", name)
backendCtx := context.WithValue(agentCtx, routineKey, name)
backendCtx = a.configManager.GetContext(backendCtx)
a.backends[name] = be
initialState := be.GetInitialState()
Expand Down Expand Up @@ -177,12 +165,12 @@ func (a *orbAgent) Start(ctx context.Context, cancelFunc context.CancelFunc) err
defer func(t time.Time) {
a.logger.Debug("Startup of agent execution duration", zap.String("Start() execution duration", time.Since(t).String()))
}(startTime)
agentCtx := context.WithValue(ctx, "routine", "agentRoutine")
asyncCtx, cancelAllAsync := context.WithCancel(context.WithValue(ctx, "routine", "asyncParent"))
agentCtx := context.WithValue(ctx, routineKey, "agentRoutine")
asyncCtx, cancelAllAsync := context.WithCancel(context.WithValue(ctx, routineKey, "asyncParent"))
a.asyncContext = asyncCtx
a.rpcFromCancelFunc = cancelAllAsync
a.cancelFunction = cancelFunc
a.logger.Info("agent started", zap.String("version", version.GetBuildVersion()), zap.Any("routine", agentCtx.Value("routine")))
a.logger.Info("agent started", zap.String("version", version.GetBuildVersion()), zap.Any("routine", agentCtx.Value(routineKey)))
mqtt.CRITICAL = &agentLoggerCritical{a: a}
mqtt.ERROR = &agentLoggerError{a: a}

Expand Down Expand Up @@ -212,7 +200,7 @@ func (a *orbAgent) logonWithHeartbeat() {
}

func (a *orbAgent) logoffWithHeartbeat(ctx context.Context) {
a.logger.Debug("stopping heartbeat, going offline status", zap.Any("routine", ctx.Value("routine")))
a.logger.Debug("stopping heartbeat, going offline status", zap.Any("routine", ctx.Value(routineKey)))
if a.heartbeatCtx != nil {
a.heartbeatCancel()
}
Expand All @@ -222,8 +210,9 @@ func (a *orbAgent) logoffWithHeartbeat(ctx context.Context) {
}
}
}

func (a *orbAgent) Stop(ctx context.Context) {
a.logger.Info("routine call for stop agent", zap.Any("routine", ctx.Value("routine")))
a.logger.Info("routine call for stop agent", zap.Any("routine", ctx.Value(routineKey)))
if a.rpcFromCancelFunc != nil {
a.rpcFromCancelFunc()
}
Expand Down Expand Up @@ -256,7 +245,7 @@ func (a *orbAgent) RestartBackend(ctx context.Context, name string, reason strin

be := a.backends[name]
a.logger.Info("restarting backend", zap.String("backend", name), zap.String("reason", reason))
a.backendState[name].RestartCount += 1
a.backendState[name].RestartCount++
a.backendState[name].LastRestartTS = time.Now()
a.backendState[name].LastRestartReason = reason
a.logger.Info("removing policies", zap.String("backend", name))
Expand All @@ -272,7 +261,7 @@ func (a *orbAgent) RestartBackend(ctx context.Context, name string, reason strin
a.backendState[name].LastError = fmt.Sprintf("failed to reset backend: %v", err)
a.logger.Error("failed to reset backend", zap.String("backend", name), zap.Error(err))
}
be.SetCommsClient(a.agent_id, &a.client, fmt.Sprintf("%s/?/%s", a.baseTopic, name))
be.SetCommsClient(a.agentID, &a.client, fmt.Sprintf("%s/?/%s", a.baseTopic, name))

return nil
}
Expand All @@ -294,7 +283,7 @@ func (a *orbAgent) RestartAll(ctx context.Context, reason string) error {
}

func (a *orbAgent) extendContext(routine string) (context.Context, context.CancelFunc) {
uuidTraceId := uuid.NewString()
a.logger.Debug("creating context for receiving message", zap.String("routine", routine), zap.String("trace-id", uuidTraceId))
return context.WithCancel(context.WithValue(context.WithValue(a.asyncContext, "routine", routine), "trace-id", uuidTraceId))
uuidTraceID := uuid.NewString()
a.logger.Debug("creating context for receiving message", zap.String("routine", routine), zap.String("trace-id", uuidTraceID))
return context.WithCancel(context.WithValue(context.WithValue(a.asyncContext, routineKey, routine), config.ContextKey("trace-id"), uuidTraceID))
}
1 change: 0 additions & 1 deletion agent/agent_prof_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import (
)

func Test_orbAgent_startBackends(t *testing.T) {

type args struct {
agentCtx context.Context
}
Expand Down
Loading

0 comments on commit 798482a

Please sign in to comment.