Check for equal FFmpeg skills, remove /v1/version

This commit is contained in:
Ingo Oppermann
2023-07-06 16:12:06 +02:00
parent 6c2e8b0ec3
commit 9905ac70f0
23 changed files with 1856 additions and 186 deletions

View File

@@ -441,6 +441,42 @@ func (a *api) start(ctx context.Context) error {
a.sessions = sessions
}
var portrange net.Portranger
if cfg.Playout.Enable {
portrange, err = net.NewPortrange(cfg.Playout.MinPort, cfg.Playout.MaxPort)
if err != nil {
return fmt.Errorf("playout port range: %w", err)
}
}
validatorIn, err := ffmpeg.NewValidator(cfg.FFmpeg.Access.Input.Allow, cfg.FFmpeg.Access.Input.Block)
if err != nil {
return fmt.Errorf("input address validator: %w", err)
}
validatorOut, err := ffmpeg.NewValidator(cfg.FFmpeg.Access.Output.Allow, cfg.FFmpeg.Access.Output.Block)
if err != nil {
return fmt.Errorf("output address validator: %w", err)
}
ffmpeg, err := ffmpeg.New(ffmpeg.Config{
Binary: cfg.FFmpeg.Binary,
MaxProc: cfg.FFmpeg.MaxProcesses,
MaxLogLines: cfg.FFmpeg.Log.MaxLines,
LogHistoryLength: cfg.FFmpeg.Log.MaxHistory,
LogMinimalHistoryLength: cfg.FFmpeg.Log.MaxMinimalHistory,
ValidatorInput: validatorIn,
ValidatorOutput: validatorOut,
Portrange: portrange,
Collector: a.sessions.Collector("ffmpeg"),
})
if err != nil {
return fmt.Errorf("unable to create ffmpeg: %w", err)
}
a.ffmpeg = ffmpeg
if cfg.Cluster.Enable {
peers := []cluster.Peer{}
@@ -469,6 +505,7 @@ func (a *api) start(ctx context.Context) error {
NodeRecoverTimeout: time.Duration(cfg.Cluster.NodeRecoverTimeout) * time.Second,
EmergencyLeaderTimeout: time.Duration(cfg.Cluster.EmergencyLeaderTimeout) * time.Second,
CoreConfig: cfg.Clone(),
CoreSkills: a.ffmpeg.Skills(),
IPLimiter: a.sessionsLimiter,
Logger: a.log.logger.core.WithComponent("Cluster"),
})
@@ -821,42 +858,6 @@ func (a *api) start(ctx context.Context) error {
a.s3fs[s3.Name] = s3fs
}
var portrange net.Portranger
if cfg.Playout.Enable {
portrange, err = net.NewPortrange(cfg.Playout.MinPort, cfg.Playout.MaxPort)
if err != nil {
return fmt.Errorf("playout port range: %w", err)
}
}
validatorIn, err := ffmpeg.NewValidator(cfg.FFmpeg.Access.Input.Allow, cfg.FFmpeg.Access.Input.Block)
if err != nil {
return fmt.Errorf("input address validator: %w", err)
}
validatorOut, err := ffmpeg.NewValidator(cfg.FFmpeg.Access.Output.Allow, cfg.FFmpeg.Access.Output.Block)
if err != nil {
return fmt.Errorf("output address validator: %w", err)
}
ffmpeg, err := ffmpeg.New(ffmpeg.Config{
Binary: cfg.FFmpeg.Binary,
MaxProc: cfg.FFmpeg.MaxProcesses,
MaxLogLines: cfg.FFmpeg.Log.MaxLines,
LogHistoryLength: cfg.FFmpeg.Log.MaxHistory,
LogMinimalHistoryLength: cfg.FFmpeg.Log.MaxMinimalHistory,
ValidatorInput: validatorIn,
ValidatorOutput: validatorOut,
Portrange: portrange,
Collector: a.sessions.Collector("ffmpeg"),
})
if err != nil {
return fmt.Errorf("unable to create ffmpeg: %w", err)
}
a.ffmpeg = ffmpeg
var rw rewrite.Rewriter
{

View File

@@ -96,7 +96,7 @@ func NewAPI(config APIConfig) (API, error) {
doc.GET("", echoSwagger.EchoWrapHandler(echoSwagger.InstanceName("ClusterAPI")))
a.router.GET("/", a.Version)
a.router.GET("/v1/about", a.Version)
a.router.GET("/v1/about", a.About)
a.router.GET("/v1/barrier/:name", a.Barrier)
@@ -125,6 +125,7 @@ func NewAPI(config APIConfig) (API, error) {
a.router.GET("/v1/core", a.CoreAPIAddress)
a.router.GET("/v1/core/config", a.CoreConfig)
a.router.GET("/v1/core/skills", a.CoreSkills)
return a, nil
}
@@ -147,11 +148,29 @@ func (a *api) Shutdown(ctx context.Context) error {
// @Produce json
// @Success 200 {string} string
// @Success 500 {object} Error
// @Router /v1/version [get]
// @Router / [get]
func (a *api) Version(c echo.Context) error {
return c.JSON(http.StatusOK, Version.String())
}
// About returns info about the cluster
// @Summary Cluster info
// @Description Cluster info
// @Tags v1.0.0
// @ID cluster-1-about
// @Produce json
// @Success 200 {string} string
// @Success 500 {object} Error
// @Router /v1/about [get]
func (a *api) About(c echo.Context) error {
about, err := a.cluster.About()
if err != nil {
return Err(http.StatusInternalServerError, "", "%s", err.Error())
}
return c.JSON(http.StatusOK, about)
}
// Barrier returns if the barrier already has been passed
// @Summary Has the barrier already has been passed
// @Description Has the barrier already has been passed
@@ -651,6 +670,19 @@ func (a *api) CoreConfig(c echo.Context) error {
return c.JSON(http.StatusOK, config)
}
// CoreSkills returns the Core skills of this node
// @Summary Core skills
// @Description Core skills of this node
// @Tags v1.0.0
// @ID cluster-1-core-skills
// @Produce json
// @Success 200 {object} skills.Skills
// @Router /v1/core/skills [get]
func (a *api) CoreSkills(c echo.Context) error {
skills := a.cluster.CoreSkills()
return c.JSON(http.StatusOK, skills)
}
// Lock tries to acquire a named lock
// @Summary Acquire a named lock
// @Description Acquire a named lock

View File

@@ -11,6 +11,7 @@ import (
"time"
"github.com/datarhei/core/v16/config"
"github.com/datarhei/core/v16/ffmpeg/skills"
httpapi "github.com/datarhei/core/v16/http/api"
iamaccess "github.com/datarhei/core/v16/iam/access"
iamidentity "github.com/datarhei/core/v16/iam/identity"
@@ -130,6 +131,21 @@ func (c *APIClient) CoreConfig() (*config.Config, error) {
return cfg, nil
}
func (c *APIClient) CoreSkills() (skills.Skills, error) {
data, err := c.call(http.MethodGet, "/v1/core/skills", "", nil, "")
if err != nil {
return skills.Skills{}, err
}
s := skills.Skills{}
err = json.Unmarshal(data, &s)
if err != nil {
return skills.Skills{}, err
}
return s, nil
}
func (c *APIClient) Join(origin string, r JoinRequest) error {
data, err := json.Marshal(&r)
if err != nil {

View File

@@ -23,6 +23,7 @@ import (
"github.com/datarhei/core/v16/cluster/raft"
"github.com/datarhei/core/v16/cluster/store"
"github.com/datarhei/core/v16/config"
"github.com/datarhei/core/v16/ffmpeg/skills"
"github.com/datarhei/core/v16/iam"
iamaccess "github.com/datarhei/core/v16/iam/access"
iamidentity "github.com/datarhei/core/v16/iam/identity"
@@ -43,6 +44,7 @@ type Cluster interface {
// the given raft address.
CoreAPIAddress(raftAddress string) (string, error)
CoreConfig() *config.Config
CoreSkills() skills.Skills
About() (ClusterAbout, error)
IsClusterDegraded() (bool, error)
@@ -103,6 +105,7 @@ type Config struct {
EmergencyLeaderTimeout time.Duration // Timeout for establishing the emergency leadership after lost contact to raft leader
CoreConfig *config.Config
CoreSkills skills.Skills
IPLimiter net.IPLimiter
Logger log.Logger
@@ -139,6 +142,7 @@ type cluster struct {
proxy proxy.Proxy
config *config.Config
skills skills.Skills
coreAddress string
isDegraded bool
@@ -189,6 +193,7 @@ func New(ctx context.Context, config Config) (Cluster, error) {
isCoreDegradedErr: fmt.Errorf("cluster not yet started"),
config: config.CoreConfig,
skills: config.CoreSkills,
nodes: map[string]clusternode.Node{},
barrier: map[string]bool{},
@@ -580,6 +585,10 @@ func (c *cluster) CoreConfig() *config.Config {
return c.config.Clone()
}
func (c *cluster) CoreSkills() skills.Skills {
return c.skills
}
func (c *cluster) CertManager() autocert.Manager {
return c.certManager
}
@@ -1003,6 +1012,14 @@ func (c *cluster) checkClusterNodes() ([]string, error) {
return nil, fmt.Errorf("node %s has a different configuration: %w", id, err)
}
skills, err := node.CoreSkills()
if err != nil {
return nil, fmt.Errorf("node %s has no FFmpeg skills available: %w", id, err)
}
if !c.skills.Equal(skills) {
return nil, fmt.Errorf("node %s has mismatching FFmpeg skills", id)
}
for _, name := range config.Host.Name {
hostnames[name] = struct{}{}
}
@@ -1111,6 +1128,26 @@ func verifyClusterConfig(local, remote *config.Config) error {
return fmt.Errorf("cluster.emergency_leader_timeout_sec is different")
}
if !local.API.Auth.Enable {
return fmt.Errorf("api.auth.enable must be true")
}
if local.API.Auth.Enable != remote.API.Auth.Enable {
return fmt.Errorf("api.auth.enable is different")
}
if local.API.Auth.Username != remote.API.Auth.Username {
return fmt.Errorf("api.auth.username is different")
}
if local.API.Auth.Password != remote.API.Auth.Password {
return fmt.Errorf("api.auth.password is different")
}
if local.API.Auth.JWT.Secret != remote.API.Auth.JWT.Secret {
return fmt.Errorf("api.auth.jwt.secret is different")
}
if local.RTMP.Enable != remote.RTMP.Enable {
return fmt.Errorf("rtmp.enable is different")
}

View File

@@ -24,6 +24,60 @@ const docTemplateClusterAPI = `{
"host": "{{.Host}}",
"basePath": "{{.BasePath}}",
"paths": {
"/": {
"get": {
"description": "The cluster version",
"produces": [
"application/json"
],
"tags": [
"v1.0.0"
],
"summary": "The cluster version",
"operationId": "cluster-1-version",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/cluster.Error"
}
}
}
}
},
"/v1/about": {
"get": {
"description": "Cluster info",
"produces": [
"application/json"
],
"tags": [
"v1.0.0"
],
"summary": "Cluster info",
"operationId": "cluster-1-about",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/cluster.Error"
}
}
}
}
},
"/v1/barrier/{name}": {
"get": {
"description": "Has the barrier already has been passed",
@@ -102,6 +156,27 @@ const docTemplateClusterAPI = `{
}
}
},
"/v1/core/skills": {
"get": {
"description": "Core skills of this node",
"produces": [
"application/json"
],
"tags": [
"v1.0.0"
],
"summary": "Core skills",
"operationId": "cluster-1-core-skills",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/skills.Skills"
}
}
}
}
},
"/v1/iam/user": {
"post": {
"description": "Add an identity to the cluster DB",
@@ -1003,33 +1078,6 @@ const docTemplateClusterAPI = `{
}
}
}
},
"/v1/version": {
"get": {
"description": "The cluster version",
"produces": [
"application/json"
],
"tags": [
"v1.0.0"
],
"summary": "The cluster version",
"operationId": "cluster-1-version",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/cluster.Error"
}
}
}
}
}
},
"definitions": {
@@ -1970,6 +2018,247 @@ const docTemplateClusterAPI = `{
}
}
},
"skills.Codec": {
"type": "object",
"properties": {
"decoders": {
"type": "array",
"items": {
"type": "string"
}
},
"encoders": {
"type": "array",
"items": {
"type": "string"
}
},
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.Device": {
"type": "object",
"properties": {
"devices": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.HWDevice"
}
},
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.Filter": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.Format": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.HWAccel": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.HWDevice": {
"type": "object",
"properties": {
"extra": {
"type": "string"
},
"id": {
"type": "string"
},
"media": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.Library": {
"type": "object",
"properties": {
"compiled": {
"type": "string"
},
"linked": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.Protocol": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.Skills": {
"type": "object",
"properties": {
"codecs": {
"$ref": "#/definitions/skills.ffCodecs"
},
"devices": {
"$ref": "#/definitions/skills.ffDevices"
},
"ffmpeg": {
"$ref": "#/definitions/skills.ffmpeg"
},
"filters": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Filter"
}
},
"formats": {
"$ref": "#/definitions/skills.ffFormats"
},
"hwaccels": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.HWAccel"
}
},
"protocols": {
"$ref": "#/definitions/skills.ffProtocols"
}
}
},
"skills.ffCodecs": {
"type": "object",
"properties": {
"audio": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Codec"
}
},
"subtitle": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Codec"
}
},
"video": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Codec"
}
}
}
},
"skills.ffDevices": {
"type": "object",
"properties": {
"demuxers": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Device"
}
},
"muxers": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Device"
}
}
}
},
"skills.ffFormats": {
"type": "object",
"properties": {
"demuxers": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Format"
}
},
"muxers": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Format"
}
}
}
},
"skills.ffProtocols": {
"type": "object",
"properties": {
"input": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Protocol"
}
},
"output": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Protocol"
}
}
}
},
"skills.ffmpeg": {
"type": "object",
"properties": {
"compiler": {
"type": "string"
},
"configuration": {
"type": "string"
},
"libraries": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Library"
}
},
"version": {
"type": "string"
}
}
},
"value.Auth0Tenant": {
"type": "object",
"properties": {

View File

@@ -16,6 +16,60 @@
},
"basePath": "/",
"paths": {
"/": {
"get": {
"description": "The cluster version",
"produces": [
"application/json"
],
"tags": [
"v1.0.0"
],
"summary": "The cluster version",
"operationId": "cluster-1-version",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/cluster.Error"
}
}
}
}
},
"/v1/about": {
"get": {
"description": "Cluster info",
"produces": [
"application/json"
],
"tags": [
"v1.0.0"
],
"summary": "Cluster info",
"operationId": "cluster-1-about",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/cluster.Error"
}
}
}
}
},
"/v1/barrier/{name}": {
"get": {
"description": "Has the barrier already has been passed",
@@ -94,6 +148,27 @@
}
}
},
"/v1/core/skills": {
"get": {
"description": "Core skills of this node",
"produces": [
"application/json"
],
"tags": [
"v1.0.0"
],
"summary": "Core skills",
"operationId": "cluster-1-core-skills",
"responses": {
"200": {
"description": "OK",
"schema": {
"$ref": "#/definitions/skills.Skills"
}
}
}
}
},
"/v1/iam/user": {
"post": {
"description": "Add an identity to the cluster DB",
@@ -995,33 +1070,6 @@
}
}
}
},
"/v1/version": {
"get": {
"description": "The cluster version",
"produces": [
"application/json"
],
"tags": [
"v1.0.0"
],
"summary": "The cluster version",
"operationId": "cluster-1-version",
"responses": {
"200": {
"description": "OK",
"schema": {
"type": "string"
}
},
"500": {
"description": "Internal Server Error",
"schema": {
"$ref": "#/definitions/cluster.Error"
}
}
}
}
}
},
"definitions": {
@@ -1962,6 +2010,247 @@
}
}
},
"skills.Codec": {
"type": "object",
"properties": {
"decoders": {
"type": "array",
"items": {
"type": "string"
}
},
"encoders": {
"type": "array",
"items": {
"type": "string"
}
},
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.Device": {
"type": "object",
"properties": {
"devices": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.HWDevice"
}
},
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.Filter": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.Format": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.HWAccel": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.HWDevice": {
"type": "object",
"properties": {
"extra": {
"type": "string"
},
"id": {
"type": "string"
},
"media": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.Library": {
"type": "object",
"properties": {
"compiled": {
"type": "string"
},
"linked": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.Protocol": {
"type": "object",
"properties": {
"id": {
"type": "string"
},
"name": {
"type": "string"
}
}
},
"skills.Skills": {
"type": "object",
"properties": {
"codecs": {
"$ref": "#/definitions/skills.ffCodecs"
},
"devices": {
"$ref": "#/definitions/skills.ffDevices"
},
"ffmpeg": {
"$ref": "#/definitions/skills.ffmpeg"
},
"filters": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Filter"
}
},
"formats": {
"$ref": "#/definitions/skills.ffFormats"
},
"hwaccels": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.HWAccel"
}
},
"protocols": {
"$ref": "#/definitions/skills.ffProtocols"
}
}
},
"skills.ffCodecs": {
"type": "object",
"properties": {
"audio": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Codec"
}
},
"subtitle": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Codec"
}
},
"video": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Codec"
}
}
}
},
"skills.ffDevices": {
"type": "object",
"properties": {
"demuxers": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Device"
}
},
"muxers": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Device"
}
}
}
},
"skills.ffFormats": {
"type": "object",
"properties": {
"demuxers": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Format"
}
},
"muxers": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Format"
}
}
}
},
"skills.ffProtocols": {
"type": "object",
"properties": {
"input": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Protocol"
}
},
"output": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Protocol"
}
}
}
},
"skills.ffmpeg": {
"type": "object",
"properties": {
"compiler": {
"type": "string"
},
"configuration": {
"type": "string"
},
"libraries": {
"type": "array",
"items": {
"$ref": "#/definitions/skills.Library"
}
},
"version": {
"type": "string"
}
}
},
"value.Auth0Tenant": {
"type": "object",
"properties": {

View File

@@ -626,6 +626,162 @@ definitions:
type: string
type: array
type: object
skills.Codec:
properties:
decoders:
items:
type: string
type: array
encoders:
items:
type: string
type: array
id:
type: string
name:
type: string
type: object
skills.Device:
properties:
devices:
items:
$ref: '#/definitions/skills.HWDevice'
type: array
id:
type: string
name:
type: string
type: object
skills.Filter:
properties:
id:
type: string
name:
type: string
type: object
skills.Format:
properties:
id:
type: string
name:
type: string
type: object
skills.HWAccel:
properties:
id:
type: string
name:
type: string
type: object
skills.HWDevice:
properties:
extra:
type: string
id:
type: string
media:
type: string
name:
type: string
type: object
skills.Library:
properties:
compiled:
type: string
linked:
type: string
name:
type: string
type: object
skills.Protocol:
properties:
id:
type: string
name:
type: string
type: object
skills.Skills:
properties:
codecs:
$ref: '#/definitions/skills.ffCodecs'
devices:
$ref: '#/definitions/skills.ffDevices'
ffmpeg:
$ref: '#/definitions/skills.ffmpeg'
filters:
items:
$ref: '#/definitions/skills.Filter'
type: array
formats:
$ref: '#/definitions/skills.ffFormats'
hwaccels:
items:
$ref: '#/definitions/skills.HWAccel'
type: array
protocols:
$ref: '#/definitions/skills.ffProtocols'
type: object
skills.ffCodecs:
properties:
audio:
items:
$ref: '#/definitions/skills.Codec'
type: array
subtitle:
items:
$ref: '#/definitions/skills.Codec'
type: array
video:
items:
$ref: '#/definitions/skills.Codec'
type: array
type: object
skills.ffDevices:
properties:
demuxers:
items:
$ref: '#/definitions/skills.Device'
type: array
muxers:
items:
$ref: '#/definitions/skills.Device'
type: array
type: object
skills.ffFormats:
properties:
demuxers:
items:
$ref: '#/definitions/skills.Format'
type: array
muxers:
items:
$ref: '#/definitions/skills.Format'
type: array
type: object
skills.ffProtocols:
properties:
input:
items:
$ref: '#/definitions/skills.Protocol'
type: array
output:
items:
$ref: '#/definitions/skills.Protocol'
type: array
type: object
skills.ffmpeg:
properties:
compiler:
type: string
configuration:
type: string
libraries:
items:
$ref: '#/definitions/skills.Library'
type: array
version:
type: string
type: object
value.Auth0Tenant:
properties:
audience:
@@ -686,6 +842,42 @@ info:
title: datarhei Core Cluster API
version: "1.0"
paths:
/:
get:
description: The cluster version
operationId: cluster-1-version
produces:
- application/json
responses:
"200":
description: OK
schema:
type: string
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/cluster.Error'
summary: The cluster version
tags:
- v1.0.0
/v1/about:
get:
description: Cluster info
operationId: cluster-1-about
produces:
- application/json
responses:
"200":
description: OK
schema:
type: string
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/cluster.Error'
summary: Cluster info
tags:
- v1.0.0
/v1/barrier/{name}:
get:
description: Has the barrier already has been passed
@@ -738,6 +930,20 @@ paths:
summary: Core config
tags:
- v1.0.0
/v1/core/skills:
get:
description: Core skills of this node
operationId: cluster-1-core-skills
produces:
- application/json
responses:
"200":
description: OK
schema:
$ref: '#/definitions/skills.Skills'
summary: Core skills
tags:
- v1.0.0
/v1/iam/user:
post:
consumes:
@@ -1343,22 +1549,4 @@ paths:
summary: Cluster DB snapshot
tags:
- v1.0.0
/v1/version:
get:
description: The cluster version
operationId: cluster-1-version
produces:
- application/json
responses:
"200":
description: OK
schema:
type: string
"500":
description: Internal Server Error
schema:
$ref: '#/definitions/cluster.Error'
summary: The cluster version
tags:
- v1.0.0
swagger: "2.0"

View File

@@ -11,6 +11,7 @@ import (
"github.com/datarhei/core/v16/cluster/client"
"github.com/datarhei/core/v16/cluster/proxy"
"github.com/datarhei/core/v16/config"
"github.com/datarhei/core/v16/ffmpeg/skills"
)
type Node interface {
@@ -24,6 +25,7 @@ type Node interface {
CoreStatus() (string, error)
CoreEssentials() (string, *config.Config, error)
CoreConfig() (*config.Config, error)
CoreSkills() (skills.Skills, error)
CoreAPIAddress() (string, error)
Proxy() proxy.Node
@@ -205,6 +207,10 @@ func (n *node) CoreConfig() (*config.Config, error) {
return n.client.CoreConfig()
}
func (n *node) CoreSkills() (skills.Skills, error) {
return n.client.CoreSkills()
}
func (n *node) CoreAPIAddress() (string, error) {
return n.client.CoreAPIAddress()
}

2
go.mod
View File

@@ -9,7 +9,7 @@ require (
github.com/atrox/haikunatorgo/v2 v2.0.1
github.com/caddyserver/certmagic v0.18.2
github.com/casbin/casbin/v2 v2.71.1
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230627120001-16d06aa77802
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230706135951-8384538979ca
github.com/datarhei/gosrt v0.5.2
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a
github.com/fujiwara/shapeio v1.0.0

9
go.sum
View File

@@ -8,6 +8,8 @@ github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc
github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE=
github.com/Masterminds/semver/v3 v3.2.1 h1:RN9w6+7QoMeJVGyfmbcgs28Br8cvmnucEXnY0rYXWg0=
github.com/Masterminds/semver/v3 v3.2.1/go.mod h1:qvl/7zhW3nngYb5+80sSMF+FG2BjYrf8m9wsX0PNOMQ=
github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0=
github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578/go.mod h1:uGdkoq3SwY9Y+13GIhn11/XLaGBb4BfwItxLd5jeuXE=
github.com/adhocore/gronx v1.6.3 h1:bnm5vieTrY3QQPpsfB0hrAaeaHDpuZTUC2LLCVMLe9c=
github.com/adhocore/gronx v1.6.3/go.mod h1:7oUY1WAU8rEJWmAxXR2DN0JaO4gi9khSgKjiRypqteg=
github.com/agnivade/levenshtein v1.1.1 h1:QY8M92nrzkmr798gCo3kmMyqXFzdQVpxLlGPRBij0P8=
@@ -48,6 +50,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46t
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230627120001-16d06aa77802 h1:F4ILviOV6brxg25hMyzzyI0K/C9yLzYHgecPD1PaokQ=
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230627120001-16d06aa77802/go.mod h1:6L0zr/NUwvaPsCTK/IL17m8JUEtgLp3BDtlsBREwacg=
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230706135951-8384538979ca h1:8GDG2CTReQ8CeWdfIaLSsOaGUe3QdLgxhEZWEim8pdQ=
github.com/datarhei/core-client-go/v16 v16.11.1-0.20230706135951-8384538979ca/go.mod h1:dc1NKgwGuqK4PgRxM5vN5rUIhCEQLXBeIkkRJnuRGU0=
github.com/datarhei/gosrt v0.5.2 h1:eagqZwEIiGPNJW0rLep3gwceObyaZ17+iKRc+l4VEpc=
github.com/datarhei/gosrt v0.5.2/go.mod h1:0308GQhAu5hxe2KYdbss901aKceSSKXnwCr8Vs++eiw=
github.com/datarhei/joy4 v0.0.0-20230505074825-fde05957445a h1:Tf4DSHY1xruBglr+yYP5Wct7czM86GKMYgbXH8a7OFo=
@@ -71,6 +75,7 @@ github.com/go-kit/kit v0.8.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2
github.com/go-kit/kit v0.9.0/go.mod h1:xBxKIO96dXMWWy0MnWVtmwkA9/13aqxPnvrjFYMA2as=
github.com/go-logfmt/logfmt v0.3.0/go.mod h1:Qt1PoO58o5twSAckw1HlFXLmHsOX5/0LbT9GBnD5lWE=
github.com/go-logfmt/logfmt v0.4.0/go.mod h1:3RMwSq7FuexP4Kalkev3ejPJsZTpXXBr9+V4qmtdjCk=
github.com/go-logfmt/logfmt v0.5.1/go.mod h1:WYhtIu8zTZfxdn5+rREduYbwxfcBr/Vr6KEVveWlfTs=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-openapi/jsonpointer v0.19.3/go.mod h1:Pl9vOtqEWErmShwVjC8pYs9cog34VGT37dQOVbmoatg=
@@ -156,11 +161,13 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
github.com/josharian/intern v1.0.0 h1:vlS4z54oSdjm0bgjRigI+G1HpF+tI+9rE5LLzOg8HmY=
github.com/josharian/intern v1.0.0/go.mod h1:5DoeVV0s6jJacbCEi61lwdGj/aVlrQvzHFFd8Hwg//Y=
github.com/jpillora/backoff v1.0.0/go.mod h1:J/6gKK9jxlEcS3zixgDgUAsiuZ7yrSoa/FX5e0EB2j4=
github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
github.com/json-iterator/go v1.1.9/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4=
github.com/json-iterator/go v1.1.12 h1:PV8peI4a0ysnczrg+LtxykD8LfKY9ML6u2jnxaEnrnM=
github.com/json-iterator/go v1.1.12/go.mod h1:e30LSqwooZae/UwlEbR2852Gd8hjQvJoHmT4TnhNGBo=
github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w=
github.com/julienschmidt/httprouter v1.3.0/go.mod h1:JR6WtHb+2LUe8TCKY3cZOxFyyO8IZAc4RVcycCCAKdM=
github.com/klauspost/compress v1.16.7 h1:2mk3MPGNzKyxErAw8YaohYh69+pa4sIQSC0fPGCFR9I=
github.com/klauspost/compress v1.16.7/go.mod h1:ntbaceVETuRiXiv4DpjP66DpAtAGkEQskQzEyD//IeE=
github.com/klauspost/cpuid/v2 v2.0.1/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
@@ -230,6 +237,7 @@ github.com/modern-go/reflect2 v1.0.1/go.mod h1:bx2lNnkwVCuqBIxFjflWJWanXIb3Rllmb
github.com/modern-go/reflect2 v1.0.2 h1:xBagoLtFs94CBntxluKeaWgTMpvLxC4ur3nMaC9Gz0M=
github.com/modern-go/reflect2 v1.0.2/go.mod h1:yWuevngMOJpCy52FWWMvUC8ws7m/LJsjYzDa0/r8luk=
github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/mwitkow/go-conntrack v0.0.0-20190716064945-2f068394615f/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/pascaldekloe/goe v0.1.0 h1:cBOtyMzM9HTpWjXfbbunk26uA6nG3a8n06Wieeh0MwY=
github.com/pascaldekloe/goe v0.1.0/go.mod h1:lzWF7FIEvWOWxwDKqyGYQf6ZUaNfKdP144TG7ZOy1lc=
@@ -281,6 +289,7 @@ github.com/shoenig/go-m1cpu v0.1.6 h1:nxdKQNcEB6vzgA2E2bvzKIYRuNj7XNJ4S/aRSwKzFt
github.com/shoenig/go-m1cpu v0.1.6/go.mod h1:1JJMcUBvfNwpq05QDQVAnx3gUHr9IYF7GNg9SUEw2VQ=
github.com/shoenig/test v0.6.4 h1:kVTaSd7WLz5WZ2IaoM0RSzRsUD+m8wRR+5qvntpn4LU=
github.com/shoenig/test v0.6.4/go.mod h1:byHiCGXqrVaflBLAMq/srcZIHynQPQgeyvkvXnjqq0k=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/sirupsen/logrus v1.2.0/go.mod h1:LxeOpSwHxABJmUn/MG1IvRgCAasNZTLOkJPxbbu5VWo=
github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE=
github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ=

View File

@@ -0,0 +1,86 @@
package api
import (
"time"
)
type ClusterNode struct {
ID string `json:"id"`
Name string `json:"name"`
Address string `json:"address"`
CreatedAt string `json:"created_at"`
Uptime int64 `json:"uptime_seconds"`
LastContact int64 `json:"last_contact"` // unix timestamp
Latency float64 `json:"latency_ms"` // milliseconds
State string `json:"state"`
Resources ClusterNodeResources `json:"resources"`
}
type ClusterNodeResources struct {
IsThrottling bool `json:"is_throttling"`
NCPU float64 `json:"ncpu"`
CPU float64 `json:"cpu_used"` // percent 0-100*npcu
CPULimit float64 `json:"cpu_limit"` // percent 0-100*npcu
Mem uint64 `json:"memory_used_bytes"` // bytes
MemLimit uint64 `json:"memory_limit_bytes"` // bytes
}
type ClusterNodeFiles struct {
LastUpdate int64 `json:"last_update"` // unix timestamp
Files map[string][]string `json:"files"`
}
type ClusterRaftServer struct {
ID string `json:"id"`
Address string `json:"address"` // raft address
Voter bool `json:"voter"`
Leader bool `json:"leader"`
}
type ClusterRaftStats struct {
State string `json:"state"`
LastContact float64 `json:"last_contact_ms"`
NumPeers uint64 `json:"num_peers"`
}
type ClusterRaft struct {
Server []ClusterRaftServer `json:"server"`
Stats ClusterRaftStats `json:"stats"`
}
type ClusterAbout struct {
ID string `json:"id"`
Address string `json:"address"`
ClusterAPIAddress string `json:"cluster_api_address"`
CoreAPIAddress string `json:"core_api_address"`
Raft ClusterRaft `json:"raft"`
Nodes []ClusterNode `json:"nodes"`
Version string `json:"version"`
Degraded bool `json:"degraded"`
DegradedErr string `json:"degraded_error"`
}
type ClusterProcess struct {
ID string `json:"id"`
Owner string `json:"owner"`
Domain string `json:"domain"`
NodeID string `json:"node_id"`
Reference string `json:"reference"`
Order string `json:"order"`
State string `json:"state"`
CPU float64 `json:"cpu" swaggertype:"number" jsonschema:"type=number"` // percent 0-100*ncpu
Memory uint64 `json:"memory_bytes"` // bytes
Runtime int64 `json:"runtime_seconds"` // seconds
}
type ClusterLock struct {
Name string `json:"name"`
ValidUntil time.Time `json:"valid_until"`
}
type ClusterKVSValue struct {
Value string `json:"value"`
UpdatedAt time.Time `json:"updated_at"`
}
type ClusterKVS map[string]ClusterKVSValue

View File

@@ -342,6 +342,8 @@ type ConfigV3 struct {
Enable bool `json:"enable"`
Auto bool `json:"auto"`
Email string `json:"email"`
Staging bool `json:"staging"`
Secret string `json:"secret"`
CertFile string `json:"cert_file"`
KeyFile string `json:"key_file"`
} `json:"tls"`
@@ -362,10 +364,10 @@ type ConfigV3 struct {
} `json:"disk"`
Memory struct {
Auth struct {
Enable bool `json:"enable"`
Username string `json:"username"`
Password string `json:"password"`
} `json:"auth"`
Enable bool `json:"enable"` // Deprecated, use IAM
Username string `json:"username"` // Deprecated, use IAM
Password string `json:"password"` // Deprecated, use IAM
} `json:"auth"` // Deprecated, use IAM
Size int64 `json:"max_size_mbytes" format:"int64"`
Purge bool `json:"purge"`
} `json:"memory"`
@@ -381,13 +383,13 @@ type ConfigV3 struct {
Address string `json:"address"`
AddressTLS string `json:"address_tls"`
App string `json:"app"`
Token string `json:"token"`
Token string `json:"token"` // Deprecated, use IAM
} `json:"rtmp"`
SRT struct {
Enable bool `json:"enable"`
Address string `json:"address"`
Passphrase string `json:"passphrase"`
Token string `json:"token"`
Token string `json:"token"` // Deprecated, use IAM
Log struct {
Enable bool `json:"enable"`
Topics []string `json:"topics"`
@@ -422,6 +424,7 @@ type ConfigV3 struct {
ForceGC int `json:"force_gc" format:"int"` // deprecated, use MemoryLimit instead
MemoryLimit int64 `json:"memory_limit_mbytes" format:"int64"`
AutoMaxProcs bool `json:"auto_max_procs"`
AgentAddress string `json:"agent_address"`
} `json:"debug"`
Metrics struct {
Enable bool `json:"enable"`
@@ -459,6 +462,7 @@ type ConfigV3 struct {
Debug bool `json:"debug"`
Address string `json:"address"` // ip:port
Peers []string `json:"peers"`
StartupTimeout int64 `json:"startup_timeout_sec" format:"int64"` // seconds
SyncInterval int64 `json:"sync_interval_sec" format:"int64"` // seconds
NodeRecoverTimeout int64 `json:"node_recover_timeout_sec" format:"int64"` // seconds
EmergencyLeaderTimeout int64 `json:"emergency_leader_timeout_sec" format:"int64"` // seconds

View File

@@ -0,0 +1,42 @@
package api
type IAMUser struct {
Name string `json:"name"`
Superuser bool `json:"superuser"`
Auth IAMUserAuth `json:"auth"`
Policies []IAMPolicy `json:"policies"`
}
type IAMUserAuth struct {
API IAMUserAuthAPI `json:"api"`
Services IAMUserAuthServices `json:"services"`
}
type IAMUserAuthAPI struct {
Password string `json:"userpass"`
Auth0 IAMUserAuthAPIAuth0 `json:"auth0"`
}
type IAMUserAuthAPIAuth0 struct {
User string `json:"user"`
Tenant IAMAuth0Tenant `json:"tenant"`
}
type IAMUserAuthServices struct {
Basic []string `json:"basic"`
Token []string `json:"token"`
Session []string `json:"session"`
}
type IAMAuth0Tenant struct {
Domain string `json:"domain"`
Audience string `json:"audience"`
ClientID string `json:"client_id"`
}
type IAMPolicy struct {
Name string `json:"name,omitempty"`
Domain string `json:"domain"`
Resource string `json:"resource"`
Actions []string `json:"actions"`
}

View File

@@ -58,3 +58,10 @@ type SessionsSummary map[string]SessionSummary
// SessionsActive is the API representation of all active sessions
type SessionsActive map[string][]Session
type SessionTokenRequest struct {
Match string `json:"match"`
Remote []string `json:"remote"`
Extra map[string]interface{} `json:"extra"`
Token string `json:"token,omitempty"`
}

View File

@@ -14,6 +14,7 @@ import (
"github.com/datarhei/core-client-go/v16/api"
"github.com/Masterminds/semver/v3"
"github.com/gobwas/glob"
)
const (
@@ -89,11 +90,50 @@ type RestClient interface {
ProcessMetadata(id ProcessID, key string) (api.Metadata, error) // GET /v3/process/{id}/metadata/{key}
ProcessMetadataSet(id ProcessID, key string, metadata api.Metadata) error // PUT /v3/process/{id}/metadata/{key}
IdentitiesList() ([]api.IAMUser, error) // GET /v3/iam/user
Identity(name string) (api.IAMUser, error) // GET /v3/iam/user/{name}
IdentityAdd(u api.IAMUser) error // POST /v3/iam/user
IdentityUpdate(name string, u api.IAMUser) error // PUT /v3/iam/user/{name}
IdentitySetPolicies(name string, p []api.IAMPolicy) error // PUT /v3/iam/user/{name}/policy
IdentityDelete(name string) error // DELETE /v3/iam/user/{name}
Cluster() (api.ClusterAbout, error) // GET /v3/cluster
ClusterHealthy() (bool, error) // GET /v3/cluster/healthy
ClusterSnapshot() (io.ReadCloser, error) // GET /v3/cluster/snapshot
ClusterLeave() error // PUT /v3/cluster/leave
ClusterDBProcessList() ([]api.Process, error) // GET /v3/cluster/db/process
ClusterDBProcess(id ProcessID) (api.Process, error) // GET /v3/cluster/db/process/{id}
ClusterDBUserList() ([]api.IAMUser, error) // GET /v3/cluster/db/user
ClusterDBUser(name string) (api.IAMUser, error) // GET /v3/cluster/db/user/{name}
ClusterDBPolicies() ([]api.IAMPolicy, error) // GET /v3/cluster/db/policies
ClusterDBLocks() ([]api.ClusterLock, error) // GET /v3/cluster/db/locks
ClusterDBKeyValues() (api.ClusterKVS, error) // GET /v3/cluster/db/kv
ClusterProcessList(opts ProcessListOptions) ([]api.Process, error) // GET /v3/cluster/process
ClusterProcess(id ProcessID, filter []string) (api.Process, error) // POST /v3/cluster/process
ClusterProcessAdd(p api.ProcessConfig) error // GET /v3/cluster/process/{id}
ClusterProcessUpdate(id ProcessID, p api.ProcessConfig) error // PUT /v3/cluster/process/{id}
ClusterProcessDelete(id ProcessID) error // DELETE /v3/cluster/process/{id}
ClusterProcessCommand(id ProcessID, command string) error // PUT /v3/cluster/process/{id}/command
ClusterProcessMetadata(id ProcessID, key string) (api.Metadata, error) // GET /v3/cluster/process/{id}/metadata/{key}
ClusterProcessMetadataSet(id ProcessID, key string, metadata api.Metadata) error // PUT /v3/cluster/process/{id}/metadata/{key}
ClusterProcessProbe(id ProcessID) (api.Probe, error) // GET /v3/cluster/process/{id}/probe
ClusterIdentitiesList() ([]api.IAMUser, error) // GET /v3/cluster/iam/user
ClusterIdentity(name string) (api.IAMUser, error) // GET /v3/cluster/iam/user/{name}
ClusterIdentityAdd(u api.IAMUser) error // POST /v3/cluster/iam/user
ClusterIdentityUpdate(name string, u api.IAMUser) error // PUT /v3/cluster/iam/user/{name}
ClusterIdentitySetPolicies(name string, p []api.IAMPolicy) error // PUT /v3/cluster/iam/user/{name}/policy
ClusterIdentityDelete(name string) error // DELETE /v3/cluster/iam/user/{name}
ClusterIAMReload() error // PUT /v3/cluster/iam/reload
RTMPChannels() ([]api.RTMPChannel, error) // GET /v3/rtmp
SRTChannels() ([]api.SRTChannel, error) // GET /v3/srt
Sessions(collectors []string) (api.SessionsSummary, error) // GET /v3/session
SessionsActive(collectors []string) (api.SessionsActive, error) // GET /v3/session/active
SessionToken(name string, req []api.SessionTokenRequest) ([]api.SessionTokenRequest, error) // PUT /v3/session/token/{username}
Skills() (api.Skills, error) // GET /v3/skills
SkillsReload() error // GET /v3/skills/reload
@@ -121,6 +161,11 @@ type Config struct {
Client HTTPClient
}
type apiconstraint struct {
path glob.Glob
constraint *semver.Constraints
}
// restclient implements the RestClient interface.
type restclient struct {
address string
@@ -136,7 +181,7 @@ type restclient struct {
version struct {
connectedCore *semver.Version
methods map[string]*semver.Constraints
methods map[string][]apiconstraint
}
}
@@ -185,9 +230,159 @@ func New(config Config) (RestClient, error) {
return v
}
r.version.methods = map[string]*semver.Constraints{
"GET/api/v3/srt": mustNewConstraint("^16.9.0"),
"GET/api/v3/metrics": mustNewConstraint("^16.10.0"),
mustNewGlob := func(pattern string) glob.Glob {
return glob.MustCompile(pattern, '/')
}
r.version.methods = map[string][]apiconstraint{
"GET": {
{
path: mustNewGlob("/api/v3/srt"),
constraint: mustNewConstraint("^16.9.0"),
},
{
path: mustNewGlob("/api/v3/metrics"),
constraint: mustNewConstraint("^16.10.0"),
},
{
path: mustNewGlob("/api/v3/iam/user"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/healthy"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/snapshot"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/db/process"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/db/process/*"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/db/user"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/db/user/*"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/db/policies"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/db/locks"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/db/kv"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/process"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/process/*"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/process/*/metadata/**"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/iam/user"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/iam/user/*"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/process/*/probe"),
constraint: mustNewConstraint("^16.14.0"),
},
},
"POST": {
{
path: mustNewGlob("/api/v3/iam/user"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/process"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/iam/user"),
constraint: mustNewConstraint("^16.14.0"),
},
},
"PUT": {
{
path: mustNewGlob("/api/v3/iam/user/*"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/api/v3/iam/user/*/policy"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/leave"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/process/*"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/process/*/command"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/process/*/metadata/**"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/iam/user/*"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/iam/user/*/policy"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/iam/reload"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/session/token/*"),
constraint: mustNewConstraint("^16.14.0"),
},
},
"DELETE": {
{
path: mustNewGlob("/api/v3/iam/user/*"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/process/{id}"),
constraint: mustNewConstraint("^16.14.0"),
},
{
path: mustNewGlob("/v3/cluster/iam/user/{name}"),
constraint: mustNewConstraint("^16.14.0"),
},
},
}
about, err := r.info()
@@ -398,7 +593,22 @@ func (r *restclient) login() error {
}
func (r *restclient) checkVersion(method, path string) error {
c := r.version.methods[method+path]
constraints := r.version.methods[method]
if constraints == nil {
return nil
}
var c *semver.Constraints = nil
for _, constraint := range constraints {
if !constraint.path.Match(path) {
continue
}
c = constraint.constraint
break
}
if c == nil {
return nil
}
@@ -407,7 +617,7 @@ func (r *restclient) checkVersion(method, path string) error {
defer r.aboutLock.RUnlock()
if !c.Check(r.version.connectedCore) {
return fmt.Errorf("this method is only available in version %s of the core", c.String())
return fmt.Errorf("this method is only available as of version %s of the core", c.String())
}
return nil
@@ -459,9 +669,10 @@ func (r *restclient) info() (api.About, error) {
req.Header.Add("Authorization", "Bearer "+r.accessToken)
}
status, body, err := r.request(req)
if err != nil {
return api.About{}, err
status, body, _ := r.request(req)
if status == http.StatusUnauthorized {
req.Header.Del("Authorization")
status, body, _ = r.request(req)
}
if status != 200 {

View File

@@ -0,0 +1,44 @@
package coreclient
import (
"encoding/json"
"io"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) Cluster() (api.ClusterAbout, error) {
var about api.ClusterAbout
data, err := r.call("GET", "/v3/cluster", nil, nil, "", nil)
if err != nil {
return about, err
}
err = json.Unmarshal(data, &about)
return about, err
}
func (r *restclient) ClusterHealthy() (bool, error) {
var healthy bool
data, err := r.call("GET", "/v3/cluster/healthy", nil, nil, "", nil)
if err != nil {
return healthy, err
}
err = json.Unmarshal(data, &healthy)
return healthy, err
}
func (r *restclient) ClusterSnapshot() (io.ReadCloser, error) {
return r.stream("GET", "/v3/cluster/snapshot", nil, nil, "", nil)
}
func (r *restclient) ClusterLeave() error {
_, err := r.call("PUT", "/v3/cluster/leave", nil, nil, "", nil)
return err
}

View File

@@ -0,0 +1,102 @@
package coreclient
import (
"encoding/json"
"net/url"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) ClusterDBProcessList() ([]api.Process, error) {
var processes []api.Process
data, err := r.call("GET", "/v3/cluster/db/process", nil, nil, "", nil)
if err != nil {
return processes, err
}
err = json.Unmarshal(data, &processes)
return processes, err
}
func (r *restclient) ClusterDBProcess(id ProcessID) (api.Process, error) {
var info api.Process
values := &url.Values{}
values.Set("domain", id.Domain)
data, err := r.call("GET", "/v3/cluster/db/process/"+url.PathEscape(id.ID), values, nil, "", nil)
if err != nil {
return info, err
}
err = json.Unmarshal(data, &info)
return info, err
}
func (r *restclient) ClusterDBUserList() ([]api.IAMUser, error) {
var users []api.IAMUser
data, err := r.call("GET", "/v3/cluster/db/user", nil, nil, "", nil)
if err != nil {
return users, err
}
err = json.Unmarshal(data, &users)
return users, err
}
func (r *restclient) ClusterDBUser(name string) (api.IAMUser, error) {
var user api.IAMUser
data, err := r.call("GET", "/v3/cluster/db/user/"+url.PathEscape(name), nil, nil, "", nil)
if err != nil {
return user, err
}
err = json.Unmarshal(data, &user)
return user, err
}
func (r *restclient) ClusterDBPolicies() ([]api.IAMPolicy, error) {
var policies []api.IAMPolicy
data, err := r.call("GET", "/v3/cluster/db/policies", nil, nil, "", nil)
if err != nil {
return policies, err
}
err = json.Unmarshal(data, &policies)
return policies, err
}
func (r *restclient) ClusterDBLocks() ([]api.ClusterLock, error) {
var locks []api.ClusterLock
data, err := r.call("GET", "/v3/cluster/db/locks", nil, nil, "", nil)
if err != nil {
return locks, err
}
err = json.Unmarshal(data, &locks)
return locks, err
}
func (r *restclient) ClusterDBKeyValues() (api.ClusterKVS, error) {
var kvs api.ClusterKVS
data, err := r.call("GET", "/v3/cluster/db/kv", nil, nil, "", nil)
if err != nil {
return kvs, err
}
err = json.Unmarshal(data, &kvs)
return kvs, err
}

View File

@@ -0,0 +1,33 @@
package coreclient
import "github.com/datarhei/core-client-go/v16/api"
func (r *restclient) ClusterIdentitiesList() ([]api.IAMUser, error) {
return r.identitiesList("cluster")
}
func (r *restclient) ClusterIdentity(name string) (api.IAMUser, error) {
return r.identity("cluster", name)
}
func (r *restclient) ClusterIdentityAdd(u api.IAMUser) error {
return r.identityAdd("cluster", u)
}
func (r *restclient) ClusterIdentityUpdate(name string, u api.IAMUser) error {
return r.identityUpdate("cluster", name, u)
}
func (r *restclient) ClusterIdentitySetPolicies(name string, p []api.IAMPolicy) error {
return r.identitySetPolicies("cluster", name, p)
}
func (r *restclient) ClusterIdentityDelete(name string) error {
return r.identityDelete("cluster", name)
}
func (r *restclient) ClusterIAMReload() error {
_, err := r.call("PUT", "/v3/cluster/iam/reload", nil, nil, "", nil)
return err
}

View File

@@ -0,0 +1,39 @@
package coreclient
import "github.com/datarhei/core-client-go/v16/api"
func (r *restclient) ClusterProcessList(opts ProcessListOptions) ([]api.Process, error) {
return r.processList("cluster", opts)
}
func (r *restclient) ClusterProcess(id ProcessID, filter []string) (api.Process, error) {
return r.process("cluster", id, filter)
}
func (r *restclient) ClusterProcessAdd(p api.ProcessConfig) error {
return r.processAdd("cluster", p)
}
func (r *restclient) ClusterProcessUpdate(id ProcessID, p api.ProcessConfig) error {
return r.processUpdate("cluster", id, p)
}
func (r *restclient) ClusterProcessDelete(id ProcessID) error {
return r.processDelete("cluster", id)
}
func (r *restclient) ClusterProcessCommand(id ProcessID, command string) error {
return r.processCommand("cluster", id, command)
}
func (r *restclient) ClusterProcessMetadata(id ProcessID, key string) (api.Metadata, error) {
return r.processMetadata("cluster", id, key)
}
func (r *restclient) ClusterProcessMetadataSet(id ProcessID, key string, metadata api.Metadata) error {
return r.processMetadataSet("cluster", id, key, metadata)
}
func (r *restclient) ClusterProcessProbe(id ProcessID) (api.Probe, error) {
return r.processProbe("cluster", id)
}

137
vendor/github.com/datarhei/core-client-go/v16/iam.go generated vendored Normal file
View File

@@ -0,0 +1,137 @@
package coreclient
import (
"bytes"
"encoding/json"
"net/url"
"github.com/datarhei/core-client-go/v16/api"
)
func (r *restclient) identitiesList(where string) ([]api.IAMUser, error) {
var users []api.IAMUser
path := "/v3/iam/user"
if where == "cluster" {
path = "/v3/cluster/iam/user"
}
data, err := r.call("GET", path, nil, nil, "", nil)
if err != nil {
return users, err
}
err = json.Unmarshal(data, &users)
return users, err
}
func (r *restclient) identity(where, name string) (api.IAMUser, error) {
var user api.IAMUser
path := "/v3/iam/user/" + url.PathEscape(name)
if where == "cluster" {
path = "/v3/cluster/iam/user/" + url.PathEscape(name)
}
data, err := r.call("GET", path, nil, nil, "", nil)
if err != nil {
return user, err
}
err = json.Unmarshal(data, &user)
return user, err
}
func (r *restclient) identityAdd(where string, u api.IAMUser) error {
var buf bytes.Buffer
path := "/v3/iam/user"
if where == "cluster" {
path = "/v3/cluster/iam/user"
}
e := json.NewEncoder(&buf)
e.Encode(u)
_, err := r.call("POST", path, nil, nil, "application/json", &buf)
if err != nil {
return err
}
return nil
}
func (r *restclient) identityUpdate(where, name string, u api.IAMUser) error {
var buf bytes.Buffer
path := "/v3/iam/user/" + url.PathEscape(name)
if where == "cluster" {
path = "/v3/cluster/iam/user/" + url.PathEscape(name)
}
e := json.NewEncoder(&buf)
e.Encode(u)
_, err := r.call("PUT", path, nil, nil, "application/json", &buf)
if err != nil {
return err
}
return nil
}
func (r *restclient) identitySetPolicies(where, name string, p []api.IAMPolicy) error {
var buf bytes.Buffer
path := "/v3/iam/user/" + url.PathEscape(name) + "/policy"
if where == "cluster" {
path = "/v3/cluster/iam/user/" + url.PathEscape(name) + "/policy"
}
e := json.NewEncoder(&buf)
e.Encode(p)
_, err := r.call("PUT", path, nil, nil, "application/json", &buf)
if err != nil {
return err
}
return nil
}
func (r *restclient) identityDelete(where, name string) error {
path := "/v3/iam/user" + url.PathEscape(name)
if where == "cluster" {
path = "/v3/cluster/iam/user" + url.PathEscape(name)
}
_, err := r.call("DELETE", path, nil, nil, "", nil)
return err
}
func (r *restclient) IdentitiesList() ([]api.IAMUser, error) {
return r.identitiesList("")
}
func (r *restclient) Identity(name string) (api.IAMUser, error) {
return r.identity("", name)
}
func (r *restclient) IdentityAdd(u api.IAMUser) error {
return r.identityAdd("", u)
}
func (r *restclient) IdentityUpdate(name string, u api.IAMUser) error {
return r.identityUpdate("", name, u)
}
func (r *restclient) IdentitySetPolicies(name string, p []api.IAMPolicy) error {
return r.identitySetPolicies("", name, p)
}
func (r *restclient) IdentityDelete(name string) error {
return r.identityDelete("", name)
}

View File

@@ -63,10 +63,15 @@ func (p *ProcessListOptions) Query() *url.Values {
return values
}
func (r *restclient) ProcessList(opts ProcessListOptions) ([]api.Process, error) {
func (r *restclient) processList(where string, opts ProcessListOptions) ([]api.Process, error) {
var processes []api.Process
data, err := r.call("GET", "/v3/process", opts.Query(), nil, "", nil)
path := "/v3/process"
if where == "cluster" {
path = "/v3/cluster/process"
}
data, err := r.call("GET", path, opts.Query(), nil, "", nil)
if err != nil {
return processes, err
}
@@ -76,14 +81,19 @@ func (r *restclient) ProcessList(opts ProcessListOptions) ([]api.Process, error)
return processes, err
}
func (r *restclient) Process(id ProcessID, filter []string) (api.Process, error) {
func (r *restclient) process(where string, id ProcessID, filter []string) (api.Process, error) {
var info api.Process
path := "/v3/process/" + url.PathEscape(id.ID)
if where == "cluster" {
path = "/v3/cluster/process/" + url.PathEscape(id.ID)
}
values := &url.Values{}
values.Set("filter", strings.Join(filter, ","))
values.Set("domain", id.Domain)
data, err := r.call("GET", "/v3/process/"+url.PathEscape(id.ID), values, nil, "", nil)
data, err := r.call("GET", path, values, nil, "", nil)
if err != nil {
return info, err
}
@@ -93,13 +103,18 @@ func (r *restclient) Process(id ProcessID, filter []string) (api.Process, error)
return info, err
}
func (r *restclient) ProcessAdd(p api.ProcessConfig) error {
func (r *restclient) processAdd(where string, p api.ProcessConfig) error {
var buf bytes.Buffer
path := "/v3/process"
if where == "cluster" {
path = "/v3/cluster/process"
}
e := json.NewEncoder(&buf)
e.Encode(p)
_, err := r.call("POST", "/v3/process", nil, nil, "application/json", &buf)
_, err := r.call("POST", path, nil, nil, "application/json", &buf)
if err != nil {
return err
}
@@ -107,16 +122,21 @@ func (r *restclient) ProcessAdd(p api.ProcessConfig) error {
return nil
}
func (r *restclient) ProcessUpdate(id ProcessID, p api.ProcessConfig) error {
func (r *restclient) processUpdate(where string, id ProcessID, p api.ProcessConfig) error {
var buf bytes.Buffer
path := "/v3/process/" + url.PathEscape(id.ID)
if where == "cluster" {
path = "/v3/cluster/process/" + url.PathEscape(id.ID)
}
e := json.NewEncoder(&buf)
e.Encode(p)
query := &url.Values{}
query.Set("domain", id.Domain)
_, err := r.call("PUT", "/v3/process/"+url.PathEscape(id.ID), query, nil, "application/json", &buf)
_, err := r.call("PUT", path, query, nil, "application/json", &buf)
if err != nil {
return err
}
@@ -124,18 +144,28 @@ func (r *restclient) ProcessUpdate(id ProcessID, p api.ProcessConfig) error {
return nil
}
func (r *restclient) ProcessDelete(id ProcessID) error {
func (r *restclient) processDelete(where string, id ProcessID) error {
path := "/v3/process/" + url.PathEscape(id.ID)
if where == "cluster" {
path = "/v3/cluster/process/" + url.PathEscape(id.ID)
}
query := &url.Values{}
query.Set("domain", id.Domain)
r.call("DELETE", "/v3/process/"+url.PathEscape(id.ID), query, nil, "", nil)
r.call("DELETE", path, query, nil, "", nil)
return nil
}
func (r *restclient) ProcessCommand(id ProcessID, command string) error {
func (r *restclient) processCommand(where string, id ProcessID, command string) error {
var buf bytes.Buffer
path := "/v3/process/" + url.PathEscape(id.ID) + "/command"
if where == "cluster" {
path = "/v3/cluster/process/" + url.PathEscape(id.ID) + "/command"
}
e := json.NewEncoder(&buf)
e.Encode(api.Command{
Command: command,
@@ -144,7 +174,7 @@ func (r *restclient) ProcessCommand(id ProcessID, command string) error {
query := &url.Values{}
query.Set("domain", id.Domain)
_, err := r.call("PUT", "/v3/process/"+url.PathEscape(id.ID)+"/command", query, nil, "application/json", &buf)
_, err := r.call("PUT", path, query, nil, "application/json", &buf)
if err != nil {
return err
}
@@ -152,13 +182,65 @@ func (r *restclient) ProcessCommand(id ProcessID, command string) error {
return nil
}
func (r *restclient) ProcessProbe(id ProcessID) (api.Probe, error) {
var p api.Probe
func (r *restclient) processMetadata(where string, id ProcessID, key string) (api.Metadata, error) {
var m api.Metadata
path := "/v3/process/" + url.PathEscape(id.ID) + "/metadata"
if where == "cluster" {
path = "/v3/cluster/process/" + url.PathEscape(id.ID) + "/metadata"
}
if len(key) != 0 {
path += "/" + url.PathEscape(key)
}
query := &url.Values{}
query.Set("domain", id.Domain)
data, err := r.call("GET", "/v3/process/"+url.PathEscape(id.ID)+"/probe", query, nil, "", nil)
data, err := r.call("GET", path, query, nil, "", nil)
if err != nil {
return m, err
}
err = json.Unmarshal(data, &m)
return m, err
}
func (r *restclient) processMetadataSet(where string, id ProcessID, key string, metadata api.Metadata) error {
var buf bytes.Buffer
path := "/v3/process/" + url.PathEscape(id.ID) + "/metadata/" + url.PathEscape(key)
if where == "cluster" {
path = "/v3/cluster/process/" + url.PathEscape(id.ID) + "/metadata/" + url.PathEscape(key)
}
e := json.NewEncoder(&buf)
e.Encode(metadata)
query := &url.Values{}
query.Set("domain", id.Domain)
_, err := r.call("PUT", path, query, nil, "application/json", &buf)
if err != nil {
return err
}
return nil
}
func (r *restclient) processProbe(where string, id ProcessID) (api.Probe, error) {
var p api.Probe
path := "/v3/process/" + url.PathEscape(id.ID) + "/probe"
if where == "cluster" {
path = "/v3/cluster/process/" + url.PathEscape(id.ID) + "/probe"
}
query := &url.Values{}
query.Set("domain", id.Domain)
data, err := r.call("GET", path, query, nil, "", nil)
if err != nil {
return p, err
}
@@ -168,6 +250,42 @@ func (r *restclient) ProcessProbe(id ProcessID) (api.Probe, error) {
return p, err
}
func (r *restclient) ProcessList(opts ProcessListOptions) ([]api.Process, error) {
return r.processList("", opts)
}
func (r *restclient) Process(id ProcessID, filter []string) (api.Process, error) {
return r.process("", id, filter)
}
func (r *restclient) ProcessAdd(p api.ProcessConfig) error {
return r.processAdd("", p)
}
func (r *restclient) ProcessUpdate(id ProcessID, p api.ProcessConfig) error {
return r.processUpdate("", id, p)
}
func (r *restclient) ProcessDelete(id ProcessID) error {
return r.processDelete("", id)
}
func (r *restclient) ProcessCommand(id ProcessID, command string) error {
return r.processCommand("", id, command)
}
func (r *restclient) ProcessMetadata(id ProcessID, key string) (api.Metadata, error) {
return r.processMetadata("", id, key)
}
func (r *restclient) ProcessMetadataSet(id ProcessID, key string, metadata api.Metadata) error {
return r.processMetadataSet("", id, key, metadata)
}
func (r *restclient) ProcessProbe(id ProcessID) (api.Probe, error) {
return r.processProbe("", id)
}
func (r *restclient) ProcessConfig(id ProcessID) (api.ProcessConfig, error) {
var p api.ProcessConfig
@@ -215,41 +333,3 @@ func (r *restclient) ProcessState(id ProcessID) (api.ProcessState, error) {
return p, err
}
func (r *restclient) ProcessMetadata(id ProcessID, key string) (api.Metadata, error) {
var m api.Metadata
query := &url.Values{}
query.Set("domain", id.Domain)
path := "/v3/process/" + url.PathEscape(id.ID) + "/metadata"
if len(key) != 0 {
path += "/" + url.PathEscape(key)
}
data, err := r.call("GET", path, query, nil, "", nil)
if err != nil {
return m, err
}
err = json.Unmarshal(data, &m)
return m, err
}
func (r *restclient) ProcessMetadataSet(id ProcessID, key string, metadata api.Metadata) error {
var buf bytes.Buffer
e := json.NewEncoder(&buf)
e.Encode(metadata)
query := &url.Values{}
query.Set("domain", id.Domain)
_, err := r.call("PUT", "/v3/process/"+url.PathEscape(id.ID)+"/metadata/"+url.PathEscape(key), query, nil, "application/json", &buf)
if err != nil {
return err
}
return nil
}

View File

@@ -1,6 +1,7 @@
package coreclient
import (
"bytes"
"encoding/json"
"net/url"
"strings"
@@ -39,3 +40,20 @@ func (r *restclient) SessionsActive(collectors []string) (api.SessionsActive, er
return sessions, err
}
func (r *restclient) SessionToken(name string, req []api.SessionTokenRequest) ([]api.SessionTokenRequest, error) {
var tokens []api.SessionTokenRequest
var buf bytes.Buffer
e := json.NewEncoder(&buf)
e.Encode(req)
data, err := r.call("PUT", "/session/token/"+url.PathEscape(name), nil, nil, "application/json", &buf)
if err != nil {
return tokens, err
}
err = json.Unmarshal(data, &tokens)
return tokens, nil
}

2
vendor/modules.txt vendored
View File

@@ -78,7 +78,7 @@ github.com/cespare/xxhash/v2
# github.com/cpuguy83/go-md2man/v2 v2.0.2
## explicit; go 1.11
github.com/cpuguy83/go-md2man/v2/md2man
# github.com/datarhei/core-client-go/v16 v16.11.1-0.20230627120001-16d06aa77802
# github.com/datarhei/core-client-go/v16 v16.11.1-0.20230706135951-8384538979ca
## explicit; go 1.18
github.com/datarhei/core-client-go/v16
github.com/datarhei/core-client-go/v16/api