diff --git a/app/api/api.go b/app/api/api.go index 69db9088..442e86fe 100644 --- a/app/api/api.go +++ b/app/api/api.go @@ -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 { diff --git a/cluster/api.go b/cluster/api.go index e7c7d4ca..23704ef8 100644 --- a/cluster/api.go +++ b/cluster/api.go @@ -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 diff --git a/cluster/client/client.go b/cluster/client/client.go index b2f9bffe..3eed947b 100644 --- a/cluster/client/client.go +++ b/cluster/client/client.go @@ -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 { diff --git a/cluster/cluster.go b/cluster/cluster.go index 7c0d21b1..6968bd9c 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -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") } diff --git a/cluster/docs/ClusterAPI_docs.go b/cluster/docs/ClusterAPI_docs.go index 420267fe..be033cca 100644 --- a/cluster/docs/ClusterAPI_docs.go +++ b/cluster/docs/ClusterAPI_docs.go @@ -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": { diff --git a/cluster/docs/ClusterAPI_swagger.json b/cluster/docs/ClusterAPI_swagger.json index 4c356bd1..42812c7b 100644 --- a/cluster/docs/ClusterAPI_swagger.json +++ b/cluster/docs/ClusterAPI_swagger.json @@ -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": { diff --git a/cluster/docs/ClusterAPI_swagger.yaml b/cluster/docs/ClusterAPI_swagger.yaml index d9078dfa..2c9de87f 100644 --- a/cluster/docs/ClusterAPI_swagger.yaml +++ b/cluster/docs/ClusterAPI_swagger.yaml @@ -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" diff --git a/cluster/node/node.go b/cluster/node/node.go index 2621c0fc..9579f910 100644 --- a/cluster/node/node.go +++ b/cluster/node/node.go @@ -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() } diff --git a/go.mod b/go.mod index d9a0b015..49528742 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index 64d270aa..2909adb8 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/vendor/github.com/datarhei/core-client-go/v16/api/cluster.go b/vendor/github.com/datarhei/core-client-go/v16/api/cluster.go new file mode 100644 index 00000000..bc81e680 --- /dev/null +++ b/vendor/github.com/datarhei/core-client-go/v16/api/cluster.go @@ -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 diff --git a/vendor/github.com/datarhei/core-client-go/v16/api/config.go b/vendor/github.com/datarhei/core-client-go/v16/api/config.go index 400a419e..0c359301 100644 --- a/vendor/github.com/datarhei/core-client-go/v16/api/config.go +++ b/vendor/github.com/datarhei/core-client-go/v16/api/config.go @@ -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"` @@ -418,10 +420,11 @@ type ConfigV3 struct { MaxPort int `json:"max_port" format:"int"` } `json:"playout"` Debug struct { - Profiling bool `json:"profiling"` - 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"` + Profiling bool `json:"profiling"` + 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 diff --git a/vendor/github.com/datarhei/core-client-go/v16/api/iam.go b/vendor/github.com/datarhei/core-client-go/v16/api/iam.go new file mode 100644 index 00000000..f1f78479 --- /dev/null +++ b/vendor/github.com/datarhei/core-client-go/v16/api/iam.go @@ -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"` +} diff --git a/vendor/github.com/datarhei/core-client-go/v16/api/session.go b/vendor/github.com/datarhei/core-client-go/v16/api/session.go index f5a18591..a3ca19b9 100644 --- a/vendor/github.com/datarhei/core-client-go/v16/api/session.go +++ b/vendor/github.com/datarhei/core-client-go/v16/api/session.go @@ -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"` +} diff --git a/vendor/github.com/datarhei/core-client-go/v16/client.go b/vendor/github.com/datarhei/core-client-go/v16/client.go index d0c60757..aa3b70c5 100644 --- a/vendor/github.com/datarhei/core-client-go/v16/client.go +++ b/vendor/github.com/datarhei/core-client-go/v16/client.go @@ -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 + 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 { diff --git a/vendor/github.com/datarhei/core-client-go/v16/cluster.go b/vendor/github.com/datarhei/core-client-go/v16/cluster.go new file mode 100644 index 00000000..8f61964f --- /dev/null +++ b/vendor/github.com/datarhei/core-client-go/v16/cluster.go @@ -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 +} diff --git a/vendor/github.com/datarhei/core-client-go/v16/cluster_db.go b/vendor/github.com/datarhei/core-client-go/v16/cluster_db.go new file mode 100644 index 00000000..6239c6d7 --- /dev/null +++ b/vendor/github.com/datarhei/core-client-go/v16/cluster_db.go @@ -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 +} diff --git a/vendor/github.com/datarhei/core-client-go/v16/cluster_iam.go b/vendor/github.com/datarhei/core-client-go/v16/cluster_iam.go new file mode 100644 index 00000000..9ad42131 --- /dev/null +++ b/vendor/github.com/datarhei/core-client-go/v16/cluster_iam.go @@ -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 +} diff --git a/vendor/github.com/datarhei/core-client-go/v16/cluster_process.go b/vendor/github.com/datarhei/core-client-go/v16/cluster_process.go new file mode 100644 index 00000000..b84ab1ed --- /dev/null +++ b/vendor/github.com/datarhei/core-client-go/v16/cluster_process.go @@ -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) +} diff --git a/vendor/github.com/datarhei/core-client-go/v16/iam.go b/vendor/github.com/datarhei/core-client-go/v16/iam.go new file mode 100644 index 00000000..2408b922 --- /dev/null +++ b/vendor/github.com/datarhei/core-client-go/v16/iam.go @@ -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) +} diff --git a/vendor/github.com/datarhei/core-client-go/v16/process.go b/vendor/github.com/datarhei/core-client-go/v16/process.go index 6576a82c..e8bacce9 100644 --- a/vendor/github.com/datarhei/core-client-go/v16/process.go +++ b/vendor/github.com/datarhei/core-client-go/v16/process.go @@ -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 -} diff --git a/vendor/github.com/datarhei/core-client-go/v16/session.go b/vendor/github.com/datarhei/core-client-go/v16/session.go index 4f0c99f0..11c40d46 100644 --- a/vendor/github.com/datarhei/core-client-go/v16/session.go +++ b/vendor/github.com/datarhei/core-client-go/v16/session.go @@ -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 +} diff --git a/vendor/modules.txt b/vendor/modules.txt index 86dc43be..f3e59435 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -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