Compare commits

...

8 Commits
v0.0.2 ... main

12 changed files with 1860 additions and 446 deletions

View File

@ -1,7 +1,7 @@
## ##
# Compile application # Compile application
## ##
FROM golang:latest AS build-env FROM docker.io/library/golang:alpine AS build-env
WORKDIR /app WORKDIR /app
COPY . . COPY . .
# ge dependencies # ge dependencies
@ -14,8 +14,7 @@ RUN CGO_ENABLED=0 go build -ldflags="-w -s" -o oven-exporter
# Build Image # Build Image
## ##
FROM scratch FROM scratch
COPY --from=build-env ["/etc/ssl/cert.pem", "/etc/ssl/certs/ca-certificates.crt"]
COPY --from=build-env /app/oven-exporter /oven-exporter COPY --from=build-env /app/oven-exporter /oven-exporter
COPY --from=build-env /app/config_example.toml /config.toml
WORKDIR / WORKDIR /
ENTRYPOINT ["/oven-exporter"] ENTRYPOINT ["/oven-exporter", "-c", ""]

59
README.adoc Normal file
View File

@ -0,0 +1,59 @@
== Oven-Exporter
An Prometheus Exporter for OvenMediaEngine
(it provides also a small API-Client for OvenMediaEngine) Be welcome to improve it.
=== Configure OvenMediaEngine
This Exporter use the REST-API of OvenMediaEngine, to setting it up, that a look in there Documentation https://airensoft.gitbook.io/ovenmediaengine/rest-api[OvenMediaEngine REST-API].
=== Setup Exporter
==== Compile
Install https://golang.org/doc/install[golang].
Run: `go install -v codeberg.org/Mediathek/oven-exporter@latest`
==== Configuration
Read comments in config_example.toml for more information.
Maybe a good place to store this file is: `/etc/ovenmediaengine/exporter.conf`
OR use env variables:
....
OVEN_E_LISTEN=:8080
OVEN_E_API__URL=http://1.2.3.4:8081
OVEN_E_API__TOKEN=ome-access-token
OVEN_E_API__DEFAULT_VHOST=
OVEN_E_API__DEFAULT_APP=
....
(File read could be disabled by call `oven-exporter -c ''`
==== Startup
Create a systemd.service file e.g. under `/etc/systemd/system/oven-exporter.service` with maybe a content like this:
[source,ini]
----
[Unit]
Description = Prometheus exporter for OvenMediaEngine
[Service]
Type=simple
ExecStart=/usr/local/bin/oven-exporter -c /etc/ovenmediaengine/exporter.conf
Restart=always
RestartSec=5s
Environment=PATH=/usr/bin:/usr/local/bin
[Install]
WantedBy=multi-user.target
----
PS: maybe you need to adjust the binary path and configuration path.
Start and enable on boot: `systemctl enable --now oven-exporter.service`

View File

@ -1,47 +0,0 @@
# Oven-Exporter
An Prometheus Exporter for OvenMediaEngine
(it provides also a small API-Client for OvenMediaEngine)
Be welcome to improve it.
## Configure OvenMediaEngine
This Exporter use the REST-API of OvenMediaEngine,
to setting it up, that a look in there Documentation [OvenMediaEngine REST-API](
https://airensoft.gitbook.io/ovenmediaengine/rest-api).
## Setup Exporter
### Compile
Install [golang](https://golang.org/doc/install).
Run:
`go install -v dev.sum7.eu/genofire/oven-exporter@latest`
### Configuration
Read comments in [config_example.toml](config_example.toml) for more information.
Maybe a good place to store this file is: `/etc/ovenmediaengine/exporter.conf`
### Startup
Create a systemd.service file e.g. under `/etc/systemd/system/oven-exporter.service` with maybe a content like this:
```ini
[Unit]
Description = Prometheus exporter for OvenMediaEngine
[Service]
Type=simple
ExecStart=/usr/local/bin/oven-exporter -c /etc/ovenmediaengine/exporter.conf
Restart=always
RestartSec=5s
Environment=PATH=/usr/bin:/usr/local/bin
[Install]
WantedBy=multi-user.target
```
PS: maybe you need to adjust the binary path and configuration path.
Start and enable on boot:
`systemctl enable --now oven-exporter.service`

View File

@ -4,10 +4,10 @@ import "encoding/base64"
// A Client for the API // A Client for the API
type Client struct { type Client struct {
Token string `toml:"token"` Token string `config:"token"`
URL string `toml:"url"` URL string `config:"url"`
DefaultVHost string `toml:"default_vhost"` DefaultVHost string `config:"default_vhost"`
DefaultApp string `toml:"default_app"` DefaultApp string `config:"default_app"`
} }
// New Client from host and token // New Client from host and token

9
docs/antora.yml Normal file
View File

@ -0,0 +1,9 @@
name: oven-exporter
title: Oven Exporter
# use from git tag
version:
v(?<version>+({0..9}).+({0..9}).+({0..9})): $<version>
main: latest
nav:
- "modules/ROOT/nav.adoc"

View File

@ -0,0 +1 @@
../../../../README.adoc

16
go.mod
View File

@ -1,10 +1,16 @@
module dev.sum7.eu/genofire/oven-exporter module codeberg.org/Mediathek/oven-exporter
go 1.16 go 1.16
require ( require (
dev.sum7.eu/genofire/golang-lib v0.0.0-20210719163544-fb766ca32b7c github.com/fsnotify/fsnotify v1.7.0 // indirect
github.com/prometheus/client_golang v1.10.0 github.com/knadh/koanf v1.5.0
go.uber.org/zap v1.13.0 github.com/pelletier/go-toml v1.9.5 // indirect
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c // indirect github.com/prometheus/client_golang v1.17.0
github.com/prometheus/client_model v0.5.0 // indirect
github.com/prometheus/common v0.45.0 // indirect
github.com/prometheus/procfs v0.12.0 // indirect
github.com/stretchr/testify v1.8.4
go.uber.org/multierr v1.11.0 // indirect
go.uber.org/zap v1.26.0
) )

1984
go.sum

File diff suppressed because it is too large Load Diff

63
helper/policy.go Normal file
View File

@ -0,0 +1,63 @@
package helper
import (
"crypto/hmac"
"crypto/sha1"
"encoding/base64"
"encoding/json"
"net/url"
)
type Policy struct {
URLExpire int64 `json:"url_expire"`
URLActivate int64 `json:"url_activate,omitempty"`
StreamExpire int64 `json:"stream_expire,omitempty"`
AllowIP string `json:"allow_ip,omitempty"`
}
func (p Policy) Encode() (string, error) {
str, err := json.Marshal(p)
if err != nil {
return "", err
}
return base64.RawStdEncoding.EncodeToString(str), nil
}
func SignEncodedPolicy(u *url.URL, secretKey string) string {
hasher := hmac.New(sha1.New, []byte(secretKey))
hasher.Write([]byte(u.String()))
return base64.RawURLEncoding.EncodeToString(hasher.Sum(nil))
}
func (p Policy) SignWithQuery(u *url.URL, secretKey, encodeQuery string) (string, error) {
encode, err := p.Encode()
if err != nil {
return "", nil
}
query := u.Query()
query.Add(encodeQuery, encode)
u.RawQuery = query.Encode()
return SignEncodedPolicy(u, secretKey), nil
}
func (p Policy) Sign(u *url.URL, secretKey string) (string, error) {
return p.SignWithQuery(u, secretKey, "policy")
}
func (p Policy) SignURLWithQuery(u *url.URL, secretKey, encodeQuery, signatureQuery string) error {
encode, err := p.Encode()
if err != nil {
return err
}
query := u.Query()
query.Add(encodeQuery, encode)
u.RawQuery = query.Encode()
signature := SignEncodedPolicy(u, secretKey)
query.Add(signatureQuery, signature)
u.RawQuery = query.Encode()
return nil
}
func (p Policy) SignURL(u *url.URL, secretKey string) error {
return p.SignURLWithQuery(u, secretKey, "policy", "signature")
}

57
helper/policy_test.go Normal file
View File

@ -0,0 +1,57 @@
// https://airensoft.gitbook.io/ovenmediaengine/access-control/signedpolicy
package helper
import (
"net/url"
"testing"
"github.com/stretchr/testify/assert"
)
const (
examplePolicyEncode = "eyJ1cmxfZXhwaXJlIjoxMzk5NzIxNTgxfQ"
exampleSecretKey = "1kU^b6"
exampleURL = "ws://192.168.0.100:3333/app/stream"
exampleSignature = "dvVdBpoxAeCPl94Kt5RoiqLI0YE"
exampleURLWithSignatureAndPolicy = "ws://192.168.0.100/app/stream?policy=eyJ1cmxfZXhwaXJlIjoxMzk5NzIxNTgxfQ&signature=dvVdBpoxAeCPl94Kt5RoiqLI0YE"
)
var (
examplePolicy = Policy{
URLExpire: 1399721581,
}
)
func TestPolicyEncode(t *testing.T) {
assert := assert.New(t)
encode, err := examplePolicy.Encode()
assert.NoError(err)
assert.Equal(examplePolicyEncode, encode)
}
func TestPolicySign(t *testing.T) {
assert := assert.New(t)
u, err := url.Parse(exampleURL)
assert.NoError(err)
sign, err := examplePolicy.Sign(u, exampleSecretKey)
assert.NoError(err)
assert.Equal(exampleSignature, sign)
}
func TestPolicySignURL(t *testing.T) {
assert := assert.New(t)
u, err := url.Parse(exampleURL)
assert.NoError(err)
err = examplePolicy.SignURL(u, exampleSecretKey)
assert.NoError(err)
// drop port -> is not part of example
u.Host = u.Hostname()
assert.Equal(exampleURLWithSignatureAndPolicy, u.String())
}

53
main.go
View File

@ -3,34 +3,71 @@ package main
import ( import (
"flag" "flag"
"net/http" "net/http"
"path/filepath"
"strings"
"dev.sum7.eu/genofire/golang-lib/file" "github.com/knadh/koanf"
"github.com/knadh/koanf/parsers/json"
"github.com/knadh/koanf/parsers/toml"
"github.com/knadh/koanf/parsers/yaml"
"github.com/knadh/koanf/providers/env"
"github.com/knadh/koanf/providers/file"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"github.com/prometheus/client_golang/prometheus/promhttp" "github.com/prometheus/client_golang/prometheus/promhttp"
"go.uber.org/zap" "go.uber.org/zap"
"dev.sum7.eu/genofire/oven-exporter/api" "codeberg.org/Mediathek/oven-exporter/api"
) )
var configExtParser = map[string]koanf.Parser{
".json": json.Parser(),
".toml": toml.Parser(),
".yaml": yaml.Parser(),
".yml": yaml.Parser(),
}
type configData struct { type configData struct {
log *zap.Logger log *zap.Logger
Log *zap.Config `toml:"log"` Log *zap.Config `config:"log"`
API *api.Client `toml:"api"` API api.Client `config:"api"`
Listen string `toml:"listen"` Listen string `config:"listen"`
} }
func main() { func main() {
configPath := "config.toml" configPath := "config.toml"
log, _ := zap.NewProduction() log, _ := zap.NewProduction()
flag.StringVar(&configPath, "c", configPath, "path to configuration file") flag.StringVar(&configPath, "c", configPath, "path to configuration file")
flag.Parse() flag.Parse()
k := koanf.New("/")
if configPath != "" {
fileExt := filepath.Ext(configPath)
parser, ok := configExtParser[fileExt]
if !ok {
log.Panic("unsupported file extension:",
zap.String("config-path", configPath),
zap.String("file-ext", fileExt),
)
}
if err := k.Load(file.Provider(configPath), parser); err != nil {
log.Panic("load file config:", zap.Error(err))
}
}
if err := k.Load(env.Provider("OVEN_E_", "/", func(s string) string {
return strings.Replace(strings.ToLower(
strings.TrimPrefix(s, "OVEN_E_")), "__", "/", -1)
}), nil); err != nil {
log.Panic("load env:", zap.Error(err))
}
config := &configData{} config := &configData{}
if err := file.ReadTOML(configPath, config); err != nil { if err := k.UnmarshalWithConf("", &config, koanf.UnmarshalConf{Tag: "config"}); err != nil {
log.Panic("open config file", zap.Error(err)) log.Panic("reading config", zap.Error(err))
} }
if config.Log != nil { if config.Log != nil {
l, err := config.Log.Build() l, err := config.Log.Build()

View File

@ -1,7 +1,7 @@
package main package main
import ( import (
"dev.sum7.eu/genofire/oven-exporter/api" "codeberg.org/Mediathek/oven-exporter/api"
"github.com/prometheus/client_golang/prometheus" "github.com/prometheus/client_golang/prometheus"
"go.uber.org/zap" "go.uber.org/zap"
) )