Skip to content

Commit

Permalink
Merge pull request #49 from netboxlabs/feat/OBS-688-worker-backend
Browse files Browse the repository at this point in the history
feat: Implement orb worker backend
  • Loading branch information
leoparente authored Jan 21, 2025
2 parents 3d7a2bd + d7c354c commit 52f6a62
Show file tree
Hide file tree
Showing 8 changed files with 520 additions and 4 deletions.
49 changes: 47 additions & 2 deletions .github/workflows/tests.yaml → .github/workflows/build.yaml
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
name: Orb Agent - test
name: Orb Agent - build
on:
push:
branches:
- "!release"
- "develop"
paths:
- "agent/**"
- "cmd/**"
Expand All @@ -17,6 +18,9 @@ concurrency:
group: ${{ github.workflow }}
cancel-in-progress: false

env:
GO_VERSION: '1.23'

permissions:
contents: write
pull-requests: write
Expand Down Expand Up @@ -69,4 +73,45 @@ jobs:
Go test coverage
${{ steps.go-test.outputs.coverage-report }}
Total coverage: ${{ steps.go-test.outputs.coverage-total }}%
edit-mode: replace
edit-mode: replace

build-and-push:
needs: go-test
runs-on: ubuntu-latest
if: success() && github.ref == 'refs/heads/develop'
strategy:
fail-fast: false
steps:
- name: Checkout
uses: actions/checkout@v4

- name: Set up QEMU
uses: docker/setup-qemu-action@49b3bc8e6bdd4a60e6116a5414239cba5943d3cf #v3.2.0

- name: Set up Docker Buildx
uses: docker/setup-buildx-action@6524bf65af31da8d45b59e8c27de4bd072b392f5 #v3.8.0

- name: Login to Docker Hub
uses: docker/login-action@9780b0c442fbb1117ed29e0efdff1e18412f7567 #v3.3.0
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

- name: Set build info
run: |
echo ${GITHUB_SHA::7} > ./agent/version/BUILD_COMMIT.txt
LATEST_RELEASE=$(curl --silent "https://api.github.com/repos/${{ github.repository }}/releases/latest" | jq -r '.tag_name')
echo $LATEST_RELEASE > ./agent/version/BUILD_VERSION.txt
- name: Build image and push
uses: docker/build-push-action@48aba3b46d1b1fec4febb7c5d0c644b249a11355 #v6.10.0
with:
context: .
file: agent/docker/Dockerfile
platforms: linux/amd64, linux/arm64
push: true
cache-from: type=gha
cache-to: type=gha,mode=max
tags: netboxlabs/orb-agent:develop
build-args: |
GO_VERSION=${{ env.GO_VERSION }}
6 changes: 5 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,9 +36,10 @@ orb:
network_discovery:
...
```
Only the `network_discovery` and `device_discovery` backends are currently supported. They do not require any special configuration.
Only the `network_discovery`, `device_discovery` and `worker` 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)
- [Worker](./docs/backends/worker.md)

#### 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.
Expand Down Expand Up @@ -66,6 +67,9 @@ orb:
network_discovery:
network_policy_1:
# see docs/backends/network_discovery.md
worker:
worker_policy_1:
# see docs/backends/worker.md
```

## Running the agent
Expand Down
96 changes: 96 additions & 0 deletions agent/backend/worker/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package worker

import (
"encoding/json"
"fmt"
"io"
"net/http"
"time"

"go.uber.org/zap"

"github.com/netboxlabs/orb-agent/agent/backend"
)

func (d *workerBackend) getProcRunningStatus() (backend.RunningStatus, string, error) {
if d.proc == nil {
return backend.Unknown, "backend not started yet", nil
}
status := d.proc.Status()

if status.Error != nil {
errMsg := fmt.Sprintf("worker process error: %v", status.Error)
return backend.BackendError, errMsg, status.Error
}

if status.Complete {
err := d.proc.Stop()
return backend.Offline, "worker process ended", err
}

if status.StopTs > 0 {
return backend.Offline, "worker process ended", nil
}
return backend.Running, "", nil
}

// note this needs to be stateless because it is called for multiple go routines
func (d *workerBackend) request(url string, payload interface{}, method string, body io.Reader, contentType string, timeout int32) error {
client := http.Client{
Timeout: time.Second * time.Duration(timeout),
}

status, _, err := d.getProcRunningStatus()
if status != backend.Running {
d.logger.Warn("skipping device discovery REST API request because process is not running or is unresponsive", zap.String("url", url), zap.String("method", method), zap.Error(err))
return err
}

URL := fmt.Sprintf("%s://%s:%s/api/v1/%s", d.apiProtocol, d.apiHost, d.apiPort, url)

req, err := http.NewRequest(method, URL, body)
if err != nil {
d.logger.Error("received error from payload", zap.Error(err))
return err
}

req.Header.Add("Content-Type", contentType)
res, getErr := client.Do(req)

if getErr != nil {
d.logger.Error("received error from payload", zap.Error(getErr))
return getErr
}

defer func() {
if err := res.Body.Close(); err != nil {
d.logger.Error("failed to close response body", zap.Error(err))
}
}()

if (res.StatusCode < 200) || (res.StatusCode > 299) {
body, err := io.ReadAll(res.Body)
if err != nil {
return fmt.Errorf("non 2xx HTTP error code from worker, no or invalid body: %d", res.StatusCode)
}
if len(body) == 0 {
return fmt.Errorf("%d empty body", res.StatusCode)
} else if body[0] == '{' {
var jsonBody map[string]interface{}
err := json.Unmarshal(body, &jsonBody)
if err == nil {
if errMsg, ok := jsonBody["error"]; ok {
return fmt.Errorf("%d %s", res.StatusCode, errMsg)
}
}
}
}

if res.Body != nil {
err = json.NewDecoder(res.Body).Decode(&payload)
if err != nil {
return err
}
}
return nil
}
11 changes: 11 additions & 0 deletions agent/backend/worker/vars.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package worker

import (
"github.com/spf13/viper"
)

// RegisterBackendSpecificVariables registers the backend specific variables for the worker backend
func RegisterBackendSpecificVariables(v *viper.Viper) {
v.SetDefault("orb.backends.worker.host", defaultAPIHost)
v.SetDefault("orb.backends.worker.port", defaultAPIPort)
}
Loading

0 comments on commit 52f6a62

Please sign in to comment.