Skip to content

Commit

Permalink
Enable building without netsnmp shared library
Browse files Browse the repository at this point in the history
  • Loading branch information
grongor committed Jul 7, 2020
1 parent 28346a6 commit a3a5a0e
Show file tree
Hide file tree
Showing 14 changed files with 272 additions and 148 deletions.
24 changes: 23 additions & 1 deletion .github/workflows/ci.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,15 @@ jobs:
- name: Build
run: go build ./cmd/snmp-proxy

build-no-netsnmp:
name: Build without netsnmp
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
- name: Build
run: go build -tags=nonetsnmp ./cmd/snmp-proxy

test:
name: Test
runs-on: ubuntu-18.04
Expand All @@ -29,6 +38,19 @@ jobs:
- name: Run tests
run: make test

test-no-netsnmp:
name: Test without netsnmp
runs-on: ubuntu-18.04
steps:
- uses: actions/checkout@v2
- uses: actions/setup-go@v2
- name: Install dependencies
run: sudo apt update && sudo apt install python3-pip
- name: Install snmpsim
run: sudo pip3 install snmpsim
- name: Run tests
run: GO_TEST_FLAGS=-tags=nonetsnmp make test

coverage:
name: Code Coverage
runs-on: ubuntu-18.04
Expand All @@ -42,7 +64,7 @@ jobs:
- name: Download go-acc
run: GO111MODULE=off go get -u github.com/ory/go-acc
- name: Generate Code Coverage
run: $(go env GOPATH)/bin/go-acc --covermode set --output coverage.cov ./...
run: GO_ACC="$(go env GOPATH)/bin/go-acc" make coverage
- name: Send coverage
uses: shogo82148/actions-goveralls@v1
with:
Expand Down
31 changes: 28 additions & 3 deletions .goreleaser.yaml
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
archives:
- files:
- builds:
- default
files:
- LICENSE
- README.md
- config.toml.dist
- id: binary
- builds:
- default
id: binary
format: binary
name_template: snmp-proxy
- id: no-netsnmp
builds:
- no-netsnmp
files:
- LICENSE
- README.md
- config.toml.dist
name_template: "{{ .ProjectName }}-no-netsnmp_{{ .Version }}_{{ .Os }}_{{ .Arch }}"

builds:
- main: ./cmd/snmp-proxy
- id: default
main: ./cmd/snmp-proxy
binary: snmp-proxy
env:
- CGO_ENABLED=1
Expand All @@ -17,6 +30,18 @@ builds:
goarch:
- amd64

- id: no-netsnmp
main: ./cmd/snmp-proxy
binary: snmp-proxy
flags:
- -tags=nonetsnmp
goos:
- linux
- darwin
# - windows # todo need to figure out how to deal with panicwatch
goarch:
- amd64

checksum:
name_template: 'checksums.txt'

Expand Down
15 changes: 12 additions & 3 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
GO_ACC ?= go-acc
export BIN = ${PWD}/bin
export GOBIN = $(BIN)

Expand All @@ -20,9 +21,17 @@ fix: $(BIN)/golangci-lint

.PHONY: test
test:
timeout 300 go test ./...
timeout 300 go test --race ./...
timeout 300 go test --count 100 ./...
timeout 300 go test $(GO_TEST_FLAGS) ./...
timeout 300 go test $(GO_TEST_FLAGS) --race ./...
timeout 300 go test $(GO_TEST_FLAGS) --count 100 ./...

.PHONY: coverage
coverage:
$(GO_ACC) --covermode set --output coverage.cov --ignore ./snmpproxy/mib ./...
$(GO_ACC) --covermode set --output coverage-netsnmp.cov ./snmpproxy/mib
$(GO_ACC) --covermode set --output coverage-nonetsnmp.cov ./snmpproxy/mib -- -tags=nonetsnmp
cat coverage-netsnmp.cov coverage-nonetsnmp.cov | grep -v 'mode: ' >> coverage.cov
rm coverage-netsnmp.cov coverage-nonetsnmp.cov

.PHONY: clean
clean:
Expand Down
10 changes: 8 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,9 @@ so that it knows how to format them. MIB parsing was inspired by
In case that OID is of the type OctetString, and it isn't found in the MIBs, then we try to detect whether the string
is printable (utf8 valid + all characters are printable). If it isn't, it's formatted as `AB C0 D5 D6...`.

MIBs parsing can be skipped by using a binary built with `-tags=nonetsnmp`.
These binaries are also available in the [Releases](https://github.com/grongor/go-snmp-proxy/releases).

Metrics
-------

Expand All @@ -117,11 +120,14 @@ of `POST /snmp-proxy` requests (count, durations).
Shared libraries
----------------

You will have to install a shared library for the `snmp-proxy`: `libsnmp` (contained in `libsnmp-dev`).
Unless you use binary built with `-tags=nonetsnmp`, you will have to install a shared library for the `snmp-proxy`:
`libsnmp` (contained in `libsnmp-dev`).

Binaries available in the [Releases](https://github.com/grongor/go-snmp-proxy/releases) will be built on the Ubuntu LTS
and thus compatible with the version of this library available in the Ubuntu LTS (and stable Debian).
If you that doesn't match your target system, you can:
If that doesn't match your target system, you can:
- install the expected version of `libsnmp` (you will usually have the newer version, but it should be possible
to install older one)
- build the `snmp-proxy` yourself in the same environment as you expected the `snmp-proxy` to run
- use binary built with `-tags=nonetsnmp`, also available in the
[Releases](https://github.com/grongor/go-snmp-proxy/releases)
5 changes: 3 additions & 2 deletions cmd/snmp-proxy/snmp-proxy.go
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import (

"github.com/grongor/go-snmp-proxy/metrics"
"github.com/grongor/go-snmp-proxy/snmpproxy"
"github.com/grongor/go-snmp-proxy/snmpproxy/mib"
)

func main() {
Expand All @@ -25,14 +26,14 @@ func main() {

validator := snmpproxy.NewRequestValidator(config.Snmp.MaxTimeoutSeconds, config.Snmp.MaxRetries)

mibParser := snmpproxy.NewNetsnmpMibParser(config.Logger, config.Snmp.StrictMibParsing)
mibParser := mib.NewNetsnmpMibParser(config.Logger, config.Snmp.StrictMibParsing)

displayHints, err := mibParser.Parse()
if err != nil {
config.Logger.Fatalw("mib parser error: ", zap.Error(err))
}

mibDataProvider := snmpproxy.NewMibDataProvider(displayHints)
mibDataProvider := mib.NewDataProvider(displayHints)
requester := snmpproxy.NewGosnmpRequester(mibDataProvider)

apiListener := snmpproxy.NewApiListener(validator, requester, config.Logger, config.Api.Listen)
Expand Down
37 changes: 37 additions & 0 deletions snmpproxy/mib/mib.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package mib

import (
"strings"
)

type DisplayHint uint8

const (
DisplayHintUnknown = DisplayHint(iota)
DisplayHintString
DisplayHintHexadecimal
)

type DisplayHints map[string]DisplayHint

type Parser interface {
Parse() (DisplayHints, error)
}

type DataProvider struct {
displayHints DisplayHints
}

func (p *DataProvider) GetDisplayHint(oid string) DisplayHint {
for length := len(oid); length > 7; length = strings.LastIndex(oid[:length], ".") {
if displayHint, ok := p.displayHints[oid[:length]]; ok {
return displayHint
}
}

return DisplayHintUnknown
}

func NewDataProvider(displayHints DisplayHints) *DataProvider {
return &DataProvider{displayHints: displayHints}
}
60 changes: 60 additions & 0 deletions snmpproxy/mib/mib_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
package mib_test

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/grongor/go-snmp-proxy/snmpproxy/mib"
)

func TestMibDataProvider_GetStringType(t *testing.T) {
tests := []struct {
name string
stringTypes mib.DisplayHints
oid string
expected mib.DisplayHint
}{
{
name: "exact match",
stringTypes: mib.DisplayHints{
".1.2.3.1.1.5.15.2": mib.DisplayHintString,
".1.2.3.1.1.5.15": mib.DisplayHintHexadecimal,
},
oid: ".1.2.3.1.1.5.15.2",
expected: mib.DisplayHintString,
},
{
name: "parent matched",
stringTypes: mib.DisplayHints{
".1.2.3.1.1.5.15.22": mib.DisplayHintHexadecimal,
".1.2.3.1.1.5.15": mib.DisplayHintString,
},
oid: ".1.2.3.1.1.5.15.2",
expected: mib.DisplayHintString,
},
{
name: "parent matched (multiple steps back)",
stringTypes: mib.DisplayHints{
".1.2.3.1.1.5.15.22": mib.DisplayHintString,
".1.2.3.1.1.5.1": mib.DisplayHintString,
".1.2.3.1.1.1": mib.DisplayHintString,
".1.2.3.1.11": mib.DisplayHintString,
".1.2.3.1": mib.DisplayHintHexadecimal,
},
oid: ".1.2.3.1.1.5.15.2",
expected: mib.DisplayHintHexadecimal,
},
{
name: "OID is too short to consider -> unknown type",
stringTypes: mib.DisplayHints{".1.2.3": mib.DisplayHintHexadecimal},
oid: ".1.2.3",
expected: mib.DisplayHintUnknown,
},
}
for _, test := range tests {
t.Run(test.name, func(t *testing.T) {
require.Equal(t, test.expected, mib.NewDataProvider(test.stringTypes).GetDisplayHint(test.oid))
})
}
}
36 changes: 3 additions & 33 deletions snmpproxy/mib.go → snmpproxy/mib/netsnmp.go
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
package snmpproxy
// +build !nonetsnmp

package mib

/*
#cgo LDFLAGS: -lnetsnmp -L/usr/local/lib
Expand All @@ -19,20 +21,6 @@ import (
"go.uber.org/zap"
)

type DisplayHint uint8

const (
DisplayHintUnknown = DisplayHint(iota)
DisplayHintString
DisplayHintHexadecimal
)

type DisplayHints map[string]DisplayHint

type MibParser interface {
Parse() (DisplayHints, error)
}

// This parser was inspired by https://github.com/prometheus/snmp_exporter/tree/master/generator
type NetsnmpMibParser struct {
logger *zap.SugaredLogger
Expand Down Expand Up @@ -140,21 +128,3 @@ func (p *NetsnmpMibParser) findStringTypesDisplayHints(displayHints DisplayHints
func NewNetsnmpMibParser(logger *zap.SugaredLogger, strictParsing bool) *NetsnmpMibParser {
return &NetsnmpMibParser{logger: logger, strictParsing: strictParsing}
}

type MibDataProvider struct {
displayHints DisplayHints
}

func (p *MibDataProvider) GetDisplayHint(oid string) DisplayHint {
for length := len(oid); length > 7; length = strings.LastIndex(oid[:length], ".") {
if displayHint, ok := p.displayHints[oid[:length]]; ok {
return displayHint
}
}

return DisplayHintUnknown
}

func NewMibDataProvider(displayHints DisplayHints) *MibDataProvider {
return &MibDataProvider{displayHints: displayHints}
}
24 changes: 24 additions & 0 deletions snmpproxy/mib/netsnmp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
// +build !nonetsnmp

package mib_test

import (
"testing"

"github.com/stretchr/testify/require"
"go.uber.org/zap"

"github.com/grongor/go-snmp-proxy/snmpproxy/mib"
)

func TestNetsnmpMibParser_Parse(t *testing.T) {
require := require.New(t)

mibParser := mib.NewNetsnmpMibParser(zap.NewNop().Sugar(), false)
displayHints, err := mibParser.Parse()

require.NoError(err)
require.NotEmpty(displayHints)
require.Equal(mib.DisplayHintString, displayHints[".1.3.6.1.2.1.2.2.1.2"])
require.Equal(mib.DisplayHintHexadecimal, displayHints[".1.3.6.1.2.1.4.22.1.2"])
}
22 changes: 22 additions & 0 deletions snmpproxy/mib/nonetsnmp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// +build nonetsnmp

package mib

import (
"go.uber.org/zap"
)

type NopMibParser struct {
}

func (n NopMibParser) Parse() (DisplayHints, error) {
return nil, nil
}

func NewNopMibParser() *NopMibParser {
return &NopMibParser{}
}

func NewNetsnmpMibParser(*zap.SugaredLogger, bool) Parser {
return NewNopMibParser()
}
18 changes: 18 additions & 0 deletions snmpproxy/mib/nonetsnmp_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// +build nonetsnmp

package mib_test

import (
"testing"

"github.com/stretchr/testify/require"

"github.com/grongor/go-snmp-proxy/snmpproxy/mib"
)

func TestNopMibParser_Parse(t *testing.T) {
parser := mib.NewNetsnmpMibParser(nil, false)
hints, err := parser.Parse()
require.Nil(t, hints)
require.Nil(t, err)
}
Loading

0 comments on commit a3a5a0e

Please sign in to comment.