diff --git a/Makefile b/Makefile index a93319c0..06df56cf 100644 --- a/Makefile +++ b/Makefile @@ -23,7 +23,8 @@ build_linux: ## swagger: Update swagger API documentation (requires github.com/swaggo/swag) swagger: - swag init -g http/server.go + swag init -g http/server.go -o ./docs --exclude ./cluster + swag init -g cluster/api.go -o ./cluster/docs --exclude ./http --instanceName ClusterAPI ## gqlgen: Regenerate GraphQL server from schema gqlgen: diff --git a/cluster/api.go b/cluster/api.go index 367906b7..e3cf588e 100644 --- a/cluster/api.go +++ b/cluster/api.go @@ -1,12 +1,25 @@ +// @title datarhei Core Cluster API +// @version 1.0 +// @description Internal REST API for the datarhei Core cluster + +// @contact.name datarhei Core Support +// @contact.url https://www.datarhei.com +// @contact.email hello@datarhei.com + +// @license.name Apache 2.0 +// @license.url https://github.com/datarhei/core/v16/blob/main/LICENSE + +// @BasePath / + package cluster import ( "context" + "fmt" "net/http" "strings" "github.com/datarhei/core/v16/cluster/client" - httpapi "github.com/datarhei/core/v16/http/api" "github.com/datarhei/core/v16/http/errorhandler" "github.com/datarhei/core/v16/http/handler/util" httplog "github.com/datarhei/core/v16/http/log" @@ -17,6 +30,9 @@ import ( "github.com/labstack/echo/v4" "github.com/labstack/echo/v4/middleware" + echoSwagger "github.com/swaggo/echo-swagger" // echo-swagger middleware + + _ "github.com/datarhei/core/v16/cluster/docs" ) type api struct { @@ -75,284 +91,28 @@ func NewAPI(config APIConfig) (API, error) { })) a.router.Logger.SetOutput(httplog.NewWrapper(a.logger)) - a.router.POST("/v1/server", func(c echo.Context) error { - r := client.JoinRequest{} + swagHandler := echoSwagger.EchoWrapHandler(echoSwagger.InstanceName("ClusterAPI")) - if err := util.ShouldBindJSON(c, &r); err != nil { - return httpapi.Err(http.StatusBadRequest, "Invalid JSON", "%s", err) - } + // Swagger API documentation router group + doc := a.router.Group("/v1/swagger/*") + doc.GET("", swagHandler) - a.logger.Debug().WithFields(log.Fields{ - "id": r.ID, - "request": r, - }).Log("Join request: %+v", r) + a.router.POST("/v1/server", a.AddServer) + a.router.DELETE("/v1/server/:id", a.RemoveServer) - origin := c.Request().Header.Get("X-Cluster-Origin") + a.router.GET("/v1/snaphot", a.Snapshot) - if origin == a.id { - return httpapi.Err(http.StatusLoopDetected, "", "breaking circuit") - } + a.router.POST("/v1/process", a.AddProcess) + a.router.DELETE("/v1/process/:id", a.RemoveProcess) + a.router.PUT("/v1/process/:id", a.UpdateProcess) + a.router.PUT("/v1/process/:id/metadata/:key", a.SetProcessMetadata) - err := a.cluster.Join(origin, r.ID, r.RaftAddress, "") - if err != nil { - a.logger.Debug().WithError(err).WithField("id", r.ID).Log("Unable to join cluster") - return httpapi.Err(http.StatusInternalServerError, "unable to join cluster", "%s", err) - } + a.router.POST("/v1/iam/user", a.AddIdentity) + a.router.PUT("/v1/iam/user/:name", a.UpdateIdentity) + a.router.PUT("/v1/iam/user/:name/policies", a.SetIdentityPolicies) + a.router.DELETE("/v1/iam/user/:name", a.RemoveIdentity) - return c.JSON(http.StatusOK, "OK") - }) - - a.router.DELETE("/v1/server/:id", func(c echo.Context) error { - id := util.PathParam(c, "id") - - a.logger.Debug().WithFields(log.Fields{ - "id": id, - }).Log("Leave request") - - origin := c.Request().Header.Get("X-Cluster-Origin") - - if origin == a.id { - return httpapi.Err(http.StatusLoopDetected, "", "breaking circuit") - } - - err := a.cluster.Leave(origin, id) - if err != nil { - a.logger.Debug().WithError(err).WithField("id", id).Log("Unable to leave cluster") - return httpapi.Err(http.StatusInternalServerError, "unable to leave cluster", "%s", err) - } - - return c.JSON(http.StatusOK, "OK") - }) - - a.router.GET("/v1/snaphot", func(c echo.Context) error { - data, err := a.cluster.Snapshot() - if err != nil { - a.logger.Debug().WithError(err).Log("Unable to create snaphot") - return httpapi.Err(http.StatusInternalServerError, "unable to create snapshot", "%s", err) - } - - defer data.Close() - - return c.Stream(http.StatusOK, "application/octet-stream", data) - }) - - a.router.POST("/v1/process", func(c echo.Context) error { - r := client.AddProcessRequest{} - - if err := util.ShouldBindJSON(c, &r); err != nil { - return httpapi.Err(http.StatusBadRequest, "Invalid JSON", "%s", err) - } - - origin := c.Request().Header.Get("X-Cluster-Origin") - - if origin == a.id { - return httpapi.Err(http.StatusLoopDetected, "", "breaking circuit") - } - - a.logger.Debug().WithField("id", r.Config.ID).Log("Add process request") - - err := a.cluster.AddProcess(origin, &r.Config) - if err != nil { - a.logger.Debug().WithError(err).WithField("id", r.Config.ID).Log("Unable to add process") - return httpapi.Err(http.StatusInternalServerError, "unable to add process", "%s", err) - } - - return c.JSON(http.StatusOK, "OK") - }) - - a.router.DELETE("/v1/process/:id", func(c echo.Context) error { - id := util.PathParam(c, "id") - domain := util.DefaultQuery(c, "domain", "") - - origin := c.Request().Header.Get("X-Cluster-Origin") - - if origin == a.id { - return httpapi.Err(http.StatusLoopDetected, "", "breaking circuit") - } - - pid := app.ProcessID{ID: id, Domain: domain} - - a.logger.Debug().WithField("id", pid).Log("Remove process request") - - err := a.cluster.RemoveProcess(origin, pid) - if err != nil { - a.logger.Debug().WithError(err).WithField("id", pid).Log("Unable to remove process") - return httpapi.Err(http.StatusInternalServerError, "unable to remove process", "%s", err) - } - - return c.JSON(http.StatusOK, "OK") - }) - - a.router.PUT("/v1/process/:id", func(c echo.Context) error { - id := util.PathParam(c, "id") - domain := util.DefaultQuery(c, "domain", "") - - r := client.UpdateProcessRequest{} - - if err := util.ShouldBindJSON(c, &r); err != nil { - return httpapi.Err(http.StatusBadRequest, "Invalid JSON", "%s", err) - } - - origin := c.Request().Header.Get("X-Cluster-Origin") - - if origin == a.id { - return httpapi.Err(http.StatusLoopDetected, "", "breaking circuit") - } - - pid := app.ProcessID{ID: id, Domain: domain} - - if !pid.Equals(r.ID) { - return httpapi.Err(http.StatusBadRequest, "Invalid data", "the ID in the path and the request do not match") - } - - a.logger.Debug().WithFields(log.Fields{ - "old_id": r.ID, - "new_id": r.Config.ProcessID(), - }).Log("Update process request") - - err := a.cluster.UpdateProcess(origin, r.ID, &r.Config) - if err != nil { - a.logger.Debug().WithError(err).WithField("id", r.ID).Log("Unable to update process") - return httpapi.Err(http.StatusInternalServerError, "unable to update process", "%s", err) - } - - return c.JSON(http.StatusOK, "OK") - }) - - a.router.PUT("/v1/process/:id/metadata/:key", func(c echo.Context) error { - id := util.PathParam(c, "id") - key := util.PathParam(c, "key") - domain := util.DefaultQuery(c, "domain", "") - - r := client.SetProcessMetadataRequest{} - - if err := util.ShouldBindJSON(c, &r); err != nil { - return httpapi.Err(http.StatusBadRequest, "Invalid JSON", "%s", err) - } - - origin := c.Request().Header.Get("X-Cluster-Origin") - - if origin == a.id { - return httpapi.Err(http.StatusLoopDetected, "", "breaking circuit") - } - - pid := app.ProcessID{ID: id, Domain: domain} - - err := a.cluster.SetProcessMetadata(origin, pid, key, r.Metadata) - if err != nil { - a.logger.Debug().WithError(err).WithField("id", r.ID).Log("Unable to update metadata") - return httpapi.Err(http.StatusInternalServerError, "unable to update metadata", "%s", err) - } - - return c.JSON(http.StatusOK, "OK") - }) - - a.router.POST("/v1/iam/user", func(c echo.Context) error { - r := client.AddIdentityRequest{} - - if err := util.ShouldBindJSON(c, &r); err != nil { - return httpapi.Err(http.StatusBadRequest, "Invalid JSON", "%s", err) - } - - origin := c.Request().Header.Get("X-Cluster-Origin") - - if origin == a.id { - return httpapi.Err(http.StatusLoopDetected, "", "breaking circuit") - } - - a.logger.Debug().WithField("identity", r.Identity).Log("Add identity request") - - err := a.cluster.AddIdentity(origin, r.Identity) - if err != nil { - a.logger.Debug().WithError(err).WithField("identity", r.Identity).Log("Unable to add identity") - return httpapi.Err(http.StatusInternalServerError, "unable to add identity", "%s", err) - } - - return c.JSON(http.StatusOK, "OK") - }) - - a.router.PUT("/v1/iam/user/:name", func(c echo.Context) error { - name := util.PathParam(c, "name") - - r := client.UpdateIdentityRequest{} - - if err := util.ShouldBindJSON(c, &r); err != nil { - return httpapi.Err(http.StatusBadRequest, "Invalid JSON", "%s", err) - } - - origin := c.Request().Header.Get("X-Cluster-Origin") - - if origin == a.id { - return httpapi.Err(http.StatusLoopDetected, "", "breaking circuit") - } - - a.logger.Debug().WithFields(log.Fields{ - "name": name, - "identity": r.Identity, - }).Log("Update identity request") - - err := a.cluster.UpdateIdentity(origin, name, r.Identity) - if err != nil { - a.logger.Debug().WithError(err).WithFields(log.Fields{ - "name": name, - "identity": r.Identity, - }).Log("Unable to add identity") - return httpapi.Err(http.StatusInternalServerError, "unable to update identity", "%s", err) - } - - return c.JSON(http.StatusOK, "OK") - }) - - a.router.PUT("/v1/iam/user/:name/policies", func(c echo.Context) error { - name := util.PathParam(c, "name") - - r := client.SetPoliciesRequest{} - - if err := util.ShouldBindJSON(c, &r); err != nil { - return httpapi.Err(http.StatusBadRequest, "Invalid JSON", "%s", err) - } - - origin := c.Request().Header.Get("X-Cluster-Origin") - - if origin == a.id { - return httpapi.Err(http.StatusLoopDetected, "", "breaking circuit") - } - - a.logger.Debug().WithField("policies", r.Policies).Log("Set policiesrequest") - - err = a.cluster.SetPolicies(origin, name, r.Policies) - if err != nil { - a.logger.Debug().WithError(err).WithField("policies", r.Policies).Log("Unable to set policies") - return httpapi.Err(http.StatusInternalServerError, "unable to add identity", "%s", err) - } - - return c.JSON(http.StatusOK, "OK") - }) - - a.router.DELETE("/v1/iam/user/:name", func(c echo.Context) error { - name := util.PathParam(c, "name") - - origin := c.Request().Header.Get("X-Cluster-Origin") - - if origin == a.id { - return httpapi.Err(http.StatusLoopDetected, "", "breaking circuit") - } - - a.logger.Debug().WithField("identity", name).Log("Remove identity request") - - err := a.cluster.RemoveIdentity(origin, name) - if err != nil { - a.logger.Debug().WithError(err).WithField("identity", name).Log("Unable to remove identity") - return httpapi.Err(http.StatusInternalServerError, "unable to remove identity", "%s", err) - } - - return c.JSON(http.StatusOK, "OK") - }) - - a.router.GET("/v1/core", func(c echo.Context) error { - address, _ := a.cluster.CoreAPIAddress("") - return c.JSON(http.StatusOK, address) - }) + a.router.GET("/v1/core", a.CoreAPIAddress) return a, nil } @@ -366,3 +126,464 @@ func (a *api) Shutdown(ctx context.Context) error { a.logger.Debug().WithField("address", a.address).Log("Shutting down api") return a.router.Shutdown(ctx) } + +// AddServer adds a new server to the cluster +// @Summary Add a new server +// @Description Add a new server to the cluster +// @Tags v1.0.0 +// @ID cluster-1-add-server +// @Accept json +// @Produce json +// @Param config body client.JoinRequest true "Server ID and address" +// @Param X-Cluster-Origin header string false "Origin ID of request" +// @Success 200 {string} string +// @Failure 400 {object} Error +// @Failure 500 {object} Error +// @Failure 508 {object} Error +// @Router /v1/server [post] +func (a *api) AddServer(c echo.Context) error { + r := client.JoinRequest{} + + if err := util.ShouldBindJSON(c, &r); err != nil { + return Err(http.StatusBadRequest, "Invalid JSON", "%s", err) + } + + a.logger.Debug().WithFields(log.Fields{ + "id": r.ID, + "request": r, + }).Log("Join request: %+v", r) + + origin := c.Request().Header.Get("X-Cluster-Origin") + + if origin == a.id { + return Err(http.StatusLoopDetected, "", "breaking circuit") + } + + err := a.cluster.Join(origin, r.ID, r.RaftAddress, "") + if err != nil { + a.logger.Debug().WithError(err).WithField("id", r.ID).Log("Unable to join cluster") + return Err(http.StatusInternalServerError, "unable to join cluster", "%s", err) + } + + return c.JSON(http.StatusOK, "OK") +} + +// RemoveServer removes a server from the cluster +// @Summary Remove a server +// @Description Remove a server from the cluster +// @Tags v1.0.0 +// @ID cluster-1-remove-server +// @Produce json +// @Param id path string true "Server ID" +// @Param X-Cluster-Origin header string false "Origin ID of request" +// @Success 200 {string} string +// @Failure 500 {object} Error +// @Failure 508 {object} Error +// @Router /v1/server/{id} [delete] +func (a *api) RemoveServer(c echo.Context) error { + id := util.PathParam(c, "id") + + a.logger.Debug().WithFields(log.Fields{ + "id": id, + }).Log("Leave request") + + origin := c.Request().Header.Get("X-Cluster-Origin") + + if origin == a.id { + return Err(http.StatusLoopDetected, "", "breaking circuit") + } + + err := a.cluster.Leave(origin, id) + if err != nil { + a.logger.Debug().WithError(err).WithField("id", id).Log("Unable to leave cluster") + return Err(http.StatusInternalServerError, "unable to leave cluster", "%s", err) + } + + return c.JSON(http.StatusOK, "OK") +} + +// Snapshot returns a current snapshot of the cluster DB +// @Summary Cluster DB snapshot +// @Description Current snapshot of the clusterDB +// @Tags v1.0.0 +// @ID cluster-1-snapshot +// @Produce application/octet-stream +// @Success 200 {file} byte +// @Success 500 {array} Error +// @Router /v1/snapshot [get] +func (a *api) Snapshot(c echo.Context) error { + data, err := a.cluster.Snapshot() + if err != nil { + a.logger.Debug().WithError(err).Log("Unable to create snaphot") + return Err(http.StatusInternalServerError, "unable to create snapshot", "%s", err) + } + + defer data.Close() + + return c.Stream(http.StatusOK, "application/octet-stream", data) +} + +// AddProcess adds a process to the cluster DB +// @Summary Add a process +// @Description Add a process to the cluster DB +// @Tags v1.0.0 +// @ID cluster-1-add-process +// @Accept json +// @Produce json +// @Param config body client.AddProcessRequest true "Process config" +// @Param X-Cluster-Origin header string false "Origin ID of request" +// @Success 200 {string} string +// @Failure 400 {object} Error +// @Failure 500 {object} Error +// @Failure 508 {object} Error +// @Router /v1/process [post] +func (a *api) AddProcess(c echo.Context) error { + r := client.AddProcessRequest{} + + if err := util.ShouldBindJSON(c, &r); err != nil { + return Err(http.StatusBadRequest, "Invalid JSON", "%s", err) + } + + origin := c.Request().Header.Get("X-Cluster-Origin") + + if origin == a.id { + return Err(http.StatusLoopDetected, "", "breaking circuit") + } + + a.logger.Debug().WithField("id", r.Config.ID).Log("Add process request") + + err := a.cluster.AddProcess(origin, &r.Config) + if err != nil { + a.logger.Debug().WithError(err).WithField("id", r.Config.ID).Log("Unable to add process") + return Err(http.StatusInternalServerError, "unable to add process", "%s", err) + } + + return c.JSON(http.StatusOK, "OK") +} + +// RemoveProcess removes a process from the cluster DB +// @Summary Remove a process +// @Description Remove a process from the cluster DB +// @Tags v1.0.0 +// @ID cluster-1-remove-process +// @Produce json +// @Param id path string true "Process ID" +// @Param domain query string false "Domain to act on" +// @Param X-Cluster-Origin header string false "Origin ID of request" +// @Success 200 {string} string +// @Failure 500 {object} Error +// @Failure 508 {object} Error +// @Router /v1/process/{id} [delete] +func (a *api) RemoveProcess(c echo.Context) error { + id := util.PathParam(c, "id") + domain := util.DefaultQuery(c, "domain", "") + + origin := c.Request().Header.Get("X-Cluster-Origin") + + if origin == a.id { + return Err(http.StatusLoopDetected, "", "breaking circuit") + } + + pid := app.ProcessID{ID: id, Domain: domain} + + a.logger.Debug().WithField("id", pid).Log("Remove process request") + + err := a.cluster.RemoveProcess(origin, pid) + if err != nil { + a.logger.Debug().WithError(err).WithField("id", pid).Log("Unable to remove process") + return Err(http.StatusInternalServerError, "unable to remove process", "%s", err) + } + + return c.JSON(http.StatusOK, "OK") +} + +// UpdateProcess replaces an existing process in the cluster DB +// @Summary Replace an existing process +// @Description Replace an existing process in the cluster DB +// @Tags v1.0.0 +// @ID cluster-1-update-process +// @Accept json +// @Produce json +// @Param id path string true "Process ID" +// @Param domain query string false "Domain to act on" +// @Param config body client.UpdateProcessRequest true "Process config" +// @Success 200 {string} string +// @Failure 500 {object} Error +// @Failure 508 {object} Error +// @Router /v1/process/{id} [put] +func (a *api) UpdateProcess(c echo.Context) error { + id := util.PathParam(c, "id") + domain := util.DefaultQuery(c, "domain", "") + + r := client.UpdateProcessRequest{} + + if err := util.ShouldBindJSON(c, &r); err != nil { + return Err(http.StatusBadRequest, "Invalid JSON", "%s", err) + } + + origin := c.Request().Header.Get("X-Cluster-Origin") + + if origin == a.id { + return Err(http.StatusLoopDetected, "", "breaking circuit") + } + + pid := app.ProcessID{ID: id, Domain: domain} + + a.logger.Debug().WithFields(log.Fields{ + "old_id": pid, + "new_id": r.Config.ProcessID(), + }).Log("Update process request") + + err := a.cluster.UpdateProcess(origin, pid, &r.Config) + if err != nil { + a.logger.Debug().WithError(err).WithField("id", pid).Log("Unable to update process") + return Err(http.StatusInternalServerError, "unable to update process", "%s", err) + } + + return c.JSON(http.StatusOK, "OK") +} + +// SetProcessMetadata stores metadata with a process +// @Summary Add JSON metadata with a process under the given key +// @Description Add arbitrary JSON metadata under the given key. If the key exists, all already stored metadata with this key will be overwritten. If the key doesn't exist, it will be created. +// @Tags v1.0.0 +// @ID cluster-3-set-process-metadata +// @Produce json +// @Param id path string true "Process ID" +// @Param key path string true "Key for data store" +// @Param domain query string false "Domain to act on" +// @Param data body client.SetProcessMetadataRequest true "Arbitrary JSON data. The null value will remove the key and its contents" +// @Success 200 {string} string +// @Failure 500 {object} Error +// @Failure 508 {object} Error +// @Router /v1/process/{id}/metadata/{key} [put] +func (a *api) SetProcessMetadata(c echo.Context) error { + id := util.PathParam(c, "id") + key := util.PathParam(c, "key") + domain := util.DefaultQuery(c, "domain", "") + + r := client.SetProcessMetadataRequest{} + + if err := util.ShouldBindJSON(c, &r); err != nil { + return Err(http.StatusBadRequest, "Invalid JSON", "%s", err) + } + + origin := c.Request().Header.Get("X-Cluster-Origin") + + if origin == a.id { + return Err(http.StatusLoopDetected, "", "breaking circuit") + } + + pid := app.ProcessID{ID: id, Domain: domain} + + err := a.cluster.SetProcessMetadata(origin, pid, key, r.Metadata) + if err != nil { + a.logger.Debug().WithError(err).WithField("id", pid).Log("Unable to update metadata") + return Err(http.StatusInternalServerError, "unable to update metadata", "%s", err) + } + + return c.JSON(http.StatusOK, "OK") +} + +// AddIdentity adds an identity to the cluster DB +// @Summary Add an identity +// @Description Add an identity to the cluster DB +// @Tags v1.0.0 +// @ID cluster-1-add-identity +// @Accept json +// @Produce json +// @Param config body client.AddIdentityRequest true "Identity config" +// @Param X-Cluster-Origin header string false "Origin ID of request" +// @Success 200 {string} string +// @Failure 400 {object} Error +// @Failure 500 {object} Error +// @Failure 508 {object} Error +// @Router /v1/iam/user [post] +func (a *api) AddIdentity(c echo.Context) error { + r := client.AddIdentityRequest{} + + if err := util.ShouldBindJSON(c, &r); err != nil { + return Err(http.StatusBadRequest, "Invalid JSON", "%s", err) + } + + origin := c.Request().Header.Get("X-Cluster-Origin") + + if origin == a.id { + return Err(http.StatusLoopDetected, "", "breaking circuit") + } + + a.logger.Debug().WithField("identity", r.Identity).Log("Add identity request") + + err := a.cluster.AddIdentity(origin, r.Identity) + if err != nil { + a.logger.Debug().WithError(err).WithField("identity", r.Identity).Log("Unable to add identity") + return Err(http.StatusInternalServerError, "unable to add identity", "%s", err) + } + + return c.JSON(http.StatusOK, "OK") +} + +// UpdateIdentity replaces an existing identity in the cluster DB +// @Summary Replace an existing identity +// @Description Replace an existing identity in the cluster DB +// @Tags v1.0.0 +// @ID cluster-1-update-identity +// @Accept json +// @Produce json +// @Param name path string true "Process ID" +// @Param config body client.UpdateIdentityRequest true "Identity config" +// @Success 200 {string} string +// @Failure 500 {object} Error +// @Failure 508 {object} Error +// @Router /v1/iam/user/{name} [put] +func (a *api) UpdateIdentity(c echo.Context) error { + name := util.PathParam(c, "name") + + r := client.UpdateIdentityRequest{} + + if err := util.ShouldBindJSON(c, &r); err != nil { + return Err(http.StatusBadRequest, "Invalid JSON", "%s", err) + } + + origin := c.Request().Header.Get("X-Cluster-Origin") + + if origin == a.id { + return Err(http.StatusLoopDetected, "", "breaking circuit") + } + + a.logger.Debug().WithFields(log.Fields{ + "name": name, + "identity": r.Identity, + }).Log("Update identity request") + + err := a.cluster.UpdateIdentity(origin, name, r.Identity) + if err != nil { + a.logger.Debug().WithError(err).WithFields(log.Fields{ + "name": name, + "identity": r.Identity, + }).Log("Unable to add identity") + return Err(http.StatusInternalServerError, "unable to update identity", "%s", err) + } + + return c.JSON(http.StatusOK, "OK") +} + +// SetIdentityPolicies set policies for an identity in the cluster DB +// @Summary Set identity policies +// @Description Set policies for an identity in the cluster DB. Any existing policies will be replaced. +// @Tags v1.0.0 +// @ID cluster-3-set-identity-policies +// @Produce json +// @Param id path string true "Process ID"SetPoliciesRequest +// @Param data body client.SetPoliciesRequest true "Policies for that user" +// @Success 200 {string} string +// @Failure 400 {object} Error +// @Failure 500 {object} Error +// @Failure 508 {object} Error +// @Router /v1/iam/user/{name}/policies [put] +func (a *api) SetIdentityPolicies(c echo.Context) error { + name := util.PathParam(c, "name") + + r := client.SetPoliciesRequest{} + + if err := util.ShouldBindJSON(c, &r); err != nil { + return Err(http.StatusBadRequest, "Invalid JSON", "%s", err) + } + + origin := c.Request().Header.Get("X-Cluster-Origin") + + if origin == a.id { + return Err(http.StatusLoopDetected, "", "breaking circuit") + } + + a.logger.Debug().WithField("policies", r.Policies).Log("Set policiesrequest") + + err := a.cluster.SetPolicies(origin, name, r.Policies) + if err != nil { + a.logger.Debug().WithError(err).WithField("policies", r.Policies).Log("Unable to set policies") + return Err(http.StatusInternalServerError, "unable to add identity", "%s", err) + } + + return c.JSON(http.StatusOK, "OK") +} + +// RemoveIdentity removes an identity from the cluster DB +// @Summary Remove an identity +// @Description Remove an identity from the cluster DB +// @Tags v1.0.0 +// @ID cluster-1-remove-identity +// @Produce json +// @Param name path string true "Identity name" +// @Param X-Cluster-Origin header string false "Origin ID of request" +// @Success 200 {string} string +// @Failure 500 {object} Error +// @Failure 508 {object} Error +// @Router /v1/iam/user/{name} [delete] +func (a *api) RemoveIdentity(c echo.Context) error { + name := util.PathParam(c, "name") + + origin := c.Request().Header.Get("X-Cluster-Origin") + + if origin == a.id { + return Err(http.StatusLoopDetected, "", "breaking circuit") + } + + a.logger.Debug().WithField("identity", name).Log("Remove identity request") + + err := a.cluster.RemoveIdentity(origin, name) + if err != nil { + a.logger.Debug().WithError(err).WithField("identity", name).Log("Unable to remove identity") + return Err(http.StatusInternalServerError, "unable to remove identity", "%s", err) + } + + return c.JSON(http.StatusOK, "OK") +} + +// CoreAPIAddress returns the Core API address and login of this node +// @Summary Core API address and login +// @Description Core API address and login of this node +// @Tags v1.0.0 +// @ID cluster-1-core-api-address +// @Produce json +// @Success 200 {string} string +// @Success 500 {array} Error +// @Router /v1/core [get] +func (a *api) CoreAPIAddress(c echo.Context) error { + address, _ := a.cluster.CoreAPIAddress("") + return c.JSON(http.StatusOK, address) +} + +// Error represents an error response of the API +type Error struct { + Code int `json:"code" jsonschema:"required" format:"int"` + Message string `json:"message" jsonschema:""` + Details []string `json:"details" jsonschema:""` +} + +// Error returns the string representation of the error +func (e Error) Error() string { + return fmt.Sprintf("code=%d, message=%s, details=%s", e.Code, e.Message, strings.Join(e.Details, " ")) +} + +// Err creates a new API error with the given HTTP status code. If message is empty, the default message +// for the given code is used. If the first entry in args is a string, it is interpreted as a format string +// for the remaining entries in args, that is used for fmt.Sprintf. Otherwise the args are ignored. +func Err(code int, message string, args ...interface{}) Error { + if len(message) == 0 { + message = http.StatusText(code) + } + + e := Error{ + Code: code, + Message: message, + Details: []string{}, + } + + if len(args) >= 1 { + if format, ok := args[0].(string); ok { + e.Details = strings.Split(fmt.Sprintf(format, args[1:]...), "\n") + } + } + + return e +} diff --git a/cluster/client/client.go b/cluster/client/client.go index ac7a8a8b..4d15a1e1 100644 --- a/cluster/client/client.go +++ b/cluster/client/client.go @@ -19,23 +19,16 @@ type JoinRequest struct { RaftAddress string `json:"raft_address"` } -type LeaveRequest struct { - ID string `json:"id"` -} - type AddProcessRequest struct { Config app.Config `json:"config"` } type UpdateProcessRequest struct { - ID app.ProcessID `json:"id"` - Config app.Config `json:"config"` + Config app.Config `json:"config"` } type SetProcessMetadataRequest struct { - ID app.ProcessID `json:"id"` - Key string `json:"key"` - Metadata interface{} `json:"metadata"` + Metadata interface{} `json:"metadata"` } type AddIdentityRequest struct { @@ -43,12 +36,10 @@ type AddIdentityRequest struct { } type UpdateIdentityRequest struct { - Name string `json:"name"` Identity iamidentity.User `json:"identity"` } type SetPoliciesRequest struct { - Name string `json:"name"` Policies []iamaccess.Policy `json:"policies"` } @@ -106,24 +97,24 @@ func (c *APIClient) RemoveProcess(origin string, id app.ProcessID) error { return err } -func (c *APIClient) UpdateProcess(origin string, r UpdateProcessRequest) error { +func (c *APIClient) UpdateProcess(origin string, id app.ProcessID, r UpdateProcessRequest) error { data, err := json.Marshal(r) if err != nil { return err } - _, err = c.call(http.MethodPut, "/process/"+r.ID.ID+"?domain="+r.ID.Domain, "application/json", bytes.NewReader(data), origin) + _, err = c.call(http.MethodPut, "/process/"+id.ID+"?domain="+id.Domain, "application/json", bytes.NewReader(data), origin) return err } -func (c *APIClient) SetProcessMetadata(origin string, r SetProcessMetadataRequest) error { +func (c *APIClient) SetProcessMetadata(origin string, id app.ProcessID, key string, r SetProcessMetadataRequest) error { data, err := json.Marshal(r) if err != nil { return err } - _, err = c.call(http.MethodPut, "/process/"+r.ID.ID+"/metadata/"+r.Key+"?domain="+r.ID.Domain, "application/json", bytes.NewReader(data), origin) + _, err = c.call(http.MethodPut, "/process/"+id.ID+"/metadata/"+key+"?domain="+id.Domain, "application/json", bytes.NewReader(data), origin) return err } diff --git a/cluster/cluster.go b/cluster/cluster.go index c9bee849..b89deee9 100644 --- a/cluster/cluster.go +++ b/cluster/cluster.go @@ -25,26 +25,6 @@ import ( "github.com/datarhei/core/v16/restream/app" ) -/* - /api/v3: - GET /cluster/store/node - list all nodes that are stored in the FSM - Cluster.Store.ListNodes() - POST /cluster/store/node - add a node to the FSM - Cluster.Store.AddNode() - DELETE /cluster/store/node/:id - remove a node from the FSM - Cluster.Store.RemoveNode() - - GET /cluster/store/process - list all process configs that are stored in the FSM - Cluster.Store.ListProcesses() - POST /cluster/store/process - add a process config to the FSM - Cluster.Store.AddProcess() - PUT /cluster/store/process/:id - update a process config in the FSM - Cluster.Store.UpdateProcess() - DELETE /cluster/store/process/:id - remove a process config from the FSM - Cluster.Store.RemoveProcess() - - ** for the processes, the leader will decide where to actually run them. the process configs will - also be added to the regular process DB of each core. - - POST /cluster/join - join the cluster - Cluster.Join() - DELETE /cluster/:id - leave the cluster - Cluster.Leave() - - ** all these endpoints will forward the request to the leader. -*/ - type Cluster interface { // Address returns the raft address of this node Address() string diff --git a/cluster/docs/ClusterAPI_docs.go b/cluster/docs/ClusterAPI_docs.go new file mode 100644 index 00000000..e631c090 --- /dev/null +++ b/cluster/docs/ClusterAPI_docs.go @@ -0,0 +1,961 @@ +// Code generated by swaggo/swag. DO NOT EDIT. + +package docs + +import "github.com/swaggo/swag" + +const docTemplateClusterAPI = `{ + "schemes": {{ marshal .Schemes }}, + "swagger": "2.0", + "info": { + "description": "{{escape .Description}}", + "title": "{{.Title}}", + "contact": { + "name": "datarhei Core Support", + "url": "https://www.datarhei.com", + "email": "hello@datarhei.com" + }, + "license": { + "name": "Apache 2.0", + "url": "https://github.com/datarhei/core/v16/blob/main/LICENSE" + }, + "version": "{{.Version}}" + }, + "host": "{{.Host}}", + "basePath": "{{.BasePath}}", + "paths": { + "/v1/core": { + "get": { + "description": "Core API address and login of this node", + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Core API address and login", + "operationId": "cluster-1-core-api-address", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + } + }, + "/v1/iam/user": { + "post": { + "description": "Add an identity to the cluster DB", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Add an identity", + "operationId": "cluster-1-add-identity", + "parameters": [ + { + "description": "Identity config", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/client.AddIdentityRequest" + } + }, + { + "type": "string", + "description": "Origin ID of request", + "name": "X-Cluster-Origin", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/iam/user/{name}": { + "put": { + "description": "Replace an existing identity in the cluster DB", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Replace an existing identity", + "operationId": "cluster-1-update-identity", + "parameters": [ + { + "type": "string", + "description": "Process ID", + "name": "name", + "in": "path", + "required": true + }, + { + "description": "Identity config", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/client.UpdateIdentityRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + }, + "delete": { + "description": "Remove an identity from the cluster DB", + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Remove an identity", + "operationId": "cluster-1-remove-identity", + "parameters": [ + { + "type": "string", + "description": "Identity name", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Origin ID of request", + "name": "X-Cluster-Origin", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/iam/user/{name}/policies": { + "put": { + "description": "Set policies for an identity in the cluster DB. Any existing policies will be replaced.", + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Set identity policies", + "operationId": "cluster-3-set-identity-policies", + "parameters": [ + { + "type": "string", + "description": "Process ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Policies for that user", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/client.SetPoliciesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/process": { + "post": { + "description": "Add a process to the cluster DB", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Add a process", + "operationId": "cluster-1-add-process", + "parameters": [ + { + "description": "Process config", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/client.AddProcessRequest" + } + }, + { + "type": "string", + "description": "Origin ID of request", + "name": "X-Cluster-Origin", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/process/{id}": { + "put": { + "description": "Replace an existing process in the cluster DB", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Replace an existing process", + "operationId": "cluster-1-update-process", + "parameters": [ + { + "type": "string", + "description": "Process ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Domain to act on", + "name": "domain", + "in": "query" + }, + { + "description": "Process config", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/client.UpdateProcessRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + }, + "delete": { + "description": "Remove a process from the cluster DB", + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Remove a process", + "operationId": "cluster-1-remove-process", + "parameters": [ + { + "type": "string", + "description": "Process ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Domain to act on", + "name": "domain", + "in": "query" + }, + { + "type": "string", + "description": "Origin ID of request", + "name": "X-Cluster-Origin", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/process/{id}/metadata/{key}": { + "put": { + "description": "Add arbitrary JSON metadata under the given key. If the key exists, all already stored metadata with this key will be overwritten. If the key doesn't exist, it will be created.", + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Add JSON metadata with a process under the given key", + "operationId": "cluster-3-set-process-metadata", + "parameters": [ + { + "type": "string", + "description": "Process ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Key for data store", + "name": "key", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Domain to act on", + "name": "domain", + "in": "query" + }, + { + "description": "Arbitrary JSON data. The null value will remove the key and its contents", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/client.SetProcessMetadataRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/server": { + "post": { + "description": "Add a new server to the cluster", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Add a new server", + "operationId": "cluster-1-add-server", + "parameters": [ + { + "description": "Server ID and address", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/client.JoinRequest" + } + }, + { + "type": "string", + "description": "Origin ID of request", + "name": "X-Cluster-Origin", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/server/{id}": { + "delete": { + "description": "Remove a server from the cluster", + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Remove a server", + "operationId": "cluster-1-remove-server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Origin ID of request", + "name": "X-Cluster-Origin", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/snapshot": { + "get": { + "description": "Current snapshot of the clusterDB", + "produces": [ + "application/octet-stream" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Cluster DB snapshot", + "operationId": "cluster-1-snapshot", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "file" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + } + } + }, + "definitions": { + "access.Policy": { + "type": "object", + "properties": { + "actions": { + "type": "array", + "items": { + "type": "string" + } + }, + "domain": { + "type": "string" + }, + "name": { + "type": "string" + }, + "resource": { + "type": "string" + } + } + }, + "app.Config": { + "type": "object", + "properties": { + "autostart": { + "type": "boolean" + }, + "domain": { + "type": "string" + }, + "ffversion": { + "type": "string" + }, + "id": { + "type": "string" + }, + "input": { + "type": "array", + "items": { + "$ref": "#/definitions/app.ConfigIO" + } + }, + "limitCPU": { + "description": "percent", + "type": "number" + }, + "limitMemory": { + "description": "bytes", + "type": "integer" + }, + "limitWaitFor": { + "description": "seconds", + "type": "integer" + }, + "logPatterns": { + "description": "will we interpreted as regualr expressions", + "type": "array", + "items": { + "type": "string" + } + }, + "options": { + "type": "array", + "items": { + "type": "string" + } + }, + "output": { + "type": "array", + "items": { + "$ref": "#/definitions/app.ConfigIO" + } + }, + "owner": { + "type": "string" + }, + "reconnect": { + "type": "boolean" + }, + "reconnectDelay": { + "description": "seconds", + "type": "integer" + }, + "reference": { + "type": "string" + }, + "scheduler": { + "description": "crontab pattern or RFC3339 timestamp", + "type": "string" + }, + "staleTimeout": { + "description": "seconds", + "type": "integer" + }, + "timeout": { + "description": "seconds", + "type": "integer" + } + } + }, + "app.ConfigIO": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "cleanup": { + "type": "array", + "items": { + "$ref": "#/definitions/app.ConfigIOCleanup" + } + }, + "id": { + "type": "string" + }, + "options": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "app.ConfigIOCleanup": { + "type": "object", + "properties": { + "maxFileAge": { + "type": "integer" + }, + "maxFiles": { + "type": "integer" + }, + "pattern": { + "type": "string" + }, + "purgeOnDelete": { + "type": "boolean" + } + } + }, + "client.AddIdentityRequest": { + "type": "object", + "properties": { + "identity": { + "$ref": "#/definitions/identity.User" + } + } + }, + "client.AddProcessRequest": { + "type": "object", + "properties": { + "config": { + "$ref": "#/definitions/app.Config" + } + } + }, + "client.JoinRequest": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "raft_address": { + "type": "string" + } + } + }, + "client.SetPoliciesRequest": { + "type": "object", + "properties": { + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/access.Policy" + } + } + } + }, + "client.SetProcessMetadataRequest": { + "type": "object", + "properties": { + "metadata": {} + } + }, + "client.UpdateIdentityRequest": { + "type": "object", + "properties": { + "identity": { + "$ref": "#/definitions/identity.User" + } + } + }, + "client.UpdateProcessRequest": { + "type": "object", + "properties": { + "config": { + "$ref": "#/definitions/app.Config" + } + } + }, + "cluster.Error": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int" + }, + "details": { + "type": "array", + "items": { + "type": "string" + } + }, + "message": { + "type": "string" + } + } + }, + "identity.Auth0Tenant": { + "type": "object", + "properties": { + "audience": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "domain": { + "type": "string" + } + } + }, + "identity.User": { + "type": "object", + "properties": { + "auth": { + "$ref": "#/definitions/identity.UserAuth" + }, + "name": { + "type": "string" + }, + "superuser": { + "type": "boolean" + } + } + }, + "identity.UserAuth": { + "type": "object", + "properties": { + "api": { + "$ref": "#/definitions/identity.UserAuthAPI" + }, + "services": { + "$ref": "#/definitions/identity.UserAuthServices" + } + } + }, + "identity.UserAuthAPI": { + "type": "object", + "properties": { + "auth0": { + "$ref": "#/definitions/identity.UserAuthAPIAuth0" + }, + "password": { + "type": "string" + } + } + }, + "identity.UserAuthAPIAuth0": { + "type": "object", + "properties": { + "tenant": { + "$ref": "#/definitions/identity.Auth0Tenant" + }, + "user": { + "type": "string" + } + } + }, + "identity.UserAuthServices": { + "type": "object", + "properties": { + "basic": { + "type": "array", + "items": { + "type": "string" + } + }, + "token": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } +}` + +// SwaggerInfoClusterAPI holds exported Swagger Info so clients can modify it +var SwaggerInfoClusterAPI = &swag.Spec{ + Version: "1.0", + Host: "", + BasePath: "/", + Schemes: []string{}, + Title: "datarhei Core Cluster API", + Description: "Internal REST API for the datarhei Core cluster", + InfoInstanceName: "ClusterAPI", + SwaggerTemplate: docTemplateClusterAPI, + LeftDelim: "{{", + RightDelim: "}}", +} + +func init() { + swag.Register(SwaggerInfoClusterAPI.InstanceName(), SwaggerInfoClusterAPI) +} diff --git a/cluster/docs/ClusterAPI_swagger.json b/cluster/docs/ClusterAPI_swagger.json new file mode 100644 index 00000000..c023685b --- /dev/null +++ b/cluster/docs/ClusterAPI_swagger.json @@ -0,0 +1,935 @@ +{ + "swagger": "2.0", + "info": { + "description": "Internal REST API for the datarhei Core cluster", + "title": "datarhei Core Cluster API", + "contact": { + "name": "datarhei Core Support", + "url": "https://www.datarhei.com", + "email": "hello@datarhei.com" + }, + "license": { + "name": "Apache 2.0", + "url": "https://github.com/datarhei/core/v16/blob/main/LICENSE" + }, + "version": "1.0" + }, + "basePath": "/", + "paths": { + "/v1/core": { + "get": { + "description": "Core API address and login of this node", + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Core API address and login", + "operationId": "cluster-1-core-api-address", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + } + }, + "/v1/iam/user": { + "post": { + "description": "Add an identity to the cluster DB", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Add an identity", + "operationId": "cluster-1-add-identity", + "parameters": [ + { + "description": "Identity config", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/client.AddIdentityRequest" + } + }, + { + "type": "string", + "description": "Origin ID of request", + "name": "X-Cluster-Origin", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/iam/user/{name}": { + "put": { + "description": "Replace an existing identity in the cluster DB", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Replace an existing identity", + "operationId": "cluster-1-update-identity", + "parameters": [ + { + "type": "string", + "description": "Process ID", + "name": "name", + "in": "path", + "required": true + }, + { + "description": "Identity config", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/client.UpdateIdentityRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + }, + "delete": { + "description": "Remove an identity from the cluster DB", + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Remove an identity", + "operationId": "cluster-1-remove-identity", + "parameters": [ + { + "type": "string", + "description": "Identity name", + "name": "name", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Origin ID of request", + "name": "X-Cluster-Origin", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/iam/user/{name}/policies": { + "put": { + "description": "Set policies for an identity in the cluster DB. Any existing policies will be replaced.", + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Set identity policies", + "operationId": "cluster-3-set-identity-policies", + "parameters": [ + { + "type": "string", + "description": "Process ID", + "name": "id", + "in": "path", + "required": true + }, + { + "description": "Policies for that user", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/client.SetPoliciesRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/process": { + "post": { + "description": "Add a process to the cluster DB", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Add a process", + "operationId": "cluster-1-add-process", + "parameters": [ + { + "description": "Process config", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/client.AddProcessRequest" + } + }, + { + "type": "string", + "description": "Origin ID of request", + "name": "X-Cluster-Origin", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/process/{id}": { + "put": { + "description": "Replace an existing process in the cluster DB", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Replace an existing process", + "operationId": "cluster-1-update-process", + "parameters": [ + { + "type": "string", + "description": "Process ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Domain to act on", + "name": "domain", + "in": "query" + }, + { + "description": "Process config", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/client.UpdateProcessRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + }, + "delete": { + "description": "Remove a process from the cluster DB", + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Remove a process", + "operationId": "cluster-1-remove-process", + "parameters": [ + { + "type": "string", + "description": "Process ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Domain to act on", + "name": "domain", + "in": "query" + }, + { + "type": "string", + "description": "Origin ID of request", + "name": "X-Cluster-Origin", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/process/{id}/metadata/{key}": { + "put": { + "description": "Add arbitrary JSON metadata under the given key. If the key exists, all already stored metadata with this key will be overwritten. If the key doesn't exist, it will be created.", + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Add JSON metadata with a process under the given key", + "operationId": "cluster-3-set-process-metadata", + "parameters": [ + { + "type": "string", + "description": "Process ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Key for data store", + "name": "key", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Domain to act on", + "name": "domain", + "in": "query" + }, + { + "description": "Arbitrary JSON data. The null value will remove the key and its contents", + "name": "data", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/client.SetProcessMetadataRequest" + } + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/server": { + "post": { + "description": "Add a new server to the cluster", + "consumes": [ + "application/json" + ], + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Add a new server", + "operationId": "cluster-1-add-server", + "parameters": [ + { + "description": "Server ID and address", + "name": "config", + "in": "body", + "required": true, + "schema": { + "$ref": "#/definitions/client.JoinRequest" + } + }, + { + "type": "string", + "description": "Origin ID of request", + "name": "X-Cluster-Origin", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/server/{id}": { + "delete": { + "description": "Remove a server from the cluster", + "produces": [ + "application/json" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Remove a server", + "operationId": "cluster-1-remove-server", + "parameters": [ + { + "type": "string", + "description": "Server ID", + "name": "id", + "in": "path", + "required": true + }, + { + "type": "string", + "description": "Origin ID of request", + "name": "X-Cluster-Origin", + "in": "header" + } + ], + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "string" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + }, + "508": { + "description": "Loop Detected", + "schema": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + }, + "/v1/snapshot": { + "get": { + "description": "Current snapshot of the clusterDB", + "produces": [ + "application/octet-stream" + ], + "tags": [ + "v1.0.0" + ], + "summary": "Cluster DB snapshot", + "operationId": "cluster-1-snapshot", + "responses": { + "200": { + "description": "OK", + "schema": { + "type": "file" + } + }, + "500": { + "description": "Internal Server Error", + "schema": { + "type": "array", + "items": { + "$ref": "#/definitions/cluster.Error" + } + } + } + } + } + } + }, + "definitions": { + "access.Policy": { + "type": "object", + "properties": { + "actions": { + "type": "array", + "items": { + "type": "string" + } + }, + "domain": { + "type": "string" + }, + "name": { + "type": "string" + }, + "resource": { + "type": "string" + } + } + }, + "app.Config": { + "type": "object", + "properties": { + "autostart": { + "type": "boolean" + }, + "domain": { + "type": "string" + }, + "ffversion": { + "type": "string" + }, + "id": { + "type": "string" + }, + "input": { + "type": "array", + "items": { + "$ref": "#/definitions/app.ConfigIO" + } + }, + "limitCPU": { + "description": "percent", + "type": "number" + }, + "limitMemory": { + "description": "bytes", + "type": "integer" + }, + "limitWaitFor": { + "description": "seconds", + "type": "integer" + }, + "logPatterns": { + "description": "will we interpreted as regualr expressions", + "type": "array", + "items": { + "type": "string" + } + }, + "options": { + "type": "array", + "items": { + "type": "string" + } + }, + "output": { + "type": "array", + "items": { + "$ref": "#/definitions/app.ConfigIO" + } + }, + "owner": { + "type": "string" + }, + "reconnect": { + "type": "boolean" + }, + "reconnectDelay": { + "description": "seconds", + "type": "integer" + }, + "reference": { + "type": "string" + }, + "scheduler": { + "description": "crontab pattern or RFC3339 timestamp", + "type": "string" + }, + "staleTimeout": { + "description": "seconds", + "type": "integer" + }, + "timeout": { + "description": "seconds", + "type": "integer" + } + } + }, + "app.ConfigIO": { + "type": "object", + "properties": { + "address": { + "type": "string" + }, + "cleanup": { + "type": "array", + "items": { + "$ref": "#/definitions/app.ConfigIOCleanup" + } + }, + "id": { + "type": "string" + }, + "options": { + "type": "array", + "items": { + "type": "string" + } + } + } + }, + "app.ConfigIOCleanup": { + "type": "object", + "properties": { + "maxFileAge": { + "type": "integer" + }, + "maxFiles": { + "type": "integer" + }, + "pattern": { + "type": "string" + }, + "purgeOnDelete": { + "type": "boolean" + } + } + }, + "client.AddIdentityRequest": { + "type": "object", + "properties": { + "identity": { + "$ref": "#/definitions/identity.User" + } + } + }, + "client.AddProcessRequest": { + "type": "object", + "properties": { + "config": { + "$ref": "#/definitions/app.Config" + } + } + }, + "client.JoinRequest": { + "type": "object", + "properties": { + "id": { + "type": "string" + }, + "raft_address": { + "type": "string" + } + } + }, + "client.SetPoliciesRequest": { + "type": "object", + "properties": { + "policies": { + "type": "array", + "items": { + "$ref": "#/definitions/access.Policy" + } + } + } + }, + "client.SetProcessMetadataRequest": { + "type": "object", + "properties": { + "metadata": {} + } + }, + "client.UpdateIdentityRequest": { + "type": "object", + "properties": { + "identity": { + "$ref": "#/definitions/identity.User" + } + } + }, + "client.UpdateProcessRequest": { + "type": "object", + "properties": { + "config": { + "$ref": "#/definitions/app.Config" + } + } + }, + "cluster.Error": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "format": "int" + }, + "details": { + "type": "array", + "items": { + "type": "string" + } + }, + "message": { + "type": "string" + } + } + }, + "identity.Auth0Tenant": { + "type": "object", + "properties": { + "audience": { + "type": "string" + }, + "client_id": { + "type": "string" + }, + "domain": { + "type": "string" + } + } + }, + "identity.User": { + "type": "object", + "properties": { + "auth": { + "$ref": "#/definitions/identity.UserAuth" + }, + "name": { + "type": "string" + }, + "superuser": { + "type": "boolean" + } + } + }, + "identity.UserAuth": { + "type": "object", + "properties": { + "api": { + "$ref": "#/definitions/identity.UserAuthAPI" + }, + "services": { + "$ref": "#/definitions/identity.UserAuthServices" + } + } + }, + "identity.UserAuthAPI": { + "type": "object", + "properties": { + "auth0": { + "$ref": "#/definitions/identity.UserAuthAPIAuth0" + }, + "password": { + "type": "string" + } + } + }, + "identity.UserAuthAPIAuth0": { + "type": "object", + "properties": { + "tenant": { + "$ref": "#/definitions/identity.Auth0Tenant" + }, + "user": { + "type": "string" + } + } + }, + "identity.UserAuthServices": { + "type": "object", + "properties": { + "basic": { + "type": "array", + "items": { + "type": "string" + } + }, + "token": { + "type": "array", + "items": { + "type": "string" + } + } + } + } + } +} \ No newline at end of file diff --git a/cluster/docs/ClusterAPI_swagger.yaml b/cluster/docs/ClusterAPI_swagger.yaml new file mode 100644 index 00000000..462367b3 --- /dev/null +++ b/cluster/docs/ClusterAPI_swagger.yaml @@ -0,0 +1,625 @@ +basePath: / +definitions: + access.Policy: + properties: + actions: + items: + type: string + type: array + domain: + type: string + name: + type: string + resource: + type: string + type: object + app.Config: + properties: + autostart: + type: boolean + domain: + type: string + ffversion: + type: string + id: + type: string + input: + items: + $ref: '#/definitions/app.ConfigIO' + type: array + limitCPU: + description: percent + type: number + limitMemory: + description: bytes + type: integer + limitWaitFor: + description: seconds + type: integer + logPatterns: + description: will we interpreted as regualr expressions + items: + type: string + type: array + options: + items: + type: string + type: array + output: + items: + $ref: '#/definitions/app.ConfigIO' + type: array + owner: + type: string + reconnect: + type: boolean + reconnectDelay: + description: seconds + type: integer + reference: + type: string + scheduler: + description: crontab pattern or RFC3339 timestamp + type: string + staleTimeout: + description: seconds + type: integer + timeout: + description: seconds + type: integer + type: object + app.ConfigIO: + properties: + address: + type: string + cleanup: + items: + $ref: '#/definitions/app.ConfigIOCleanup' + type: array + id: + type: string + options: + items: + type: string + type: array + type: object + app.ConfigIOCleanup: + properties: + maxFileAge: + type: integer + maxFiles: + type: integer + pattern: + type: string + purgeOnDelete: + type: boolean + type: object + client.AddIdentityRequest: + properties: + identity: + $ref: '#/definitions/identity.User' + type: object + client.AddProcessRequest: + properties: + config: + $ref: '#/definitions/app.Config' + type: object + client.JoinRequest: + properties: + id: + type: string + raft_address: + type: string + type: object + client.SetPoliciesRequest: + properties: + policies: + items: + $ref: '#/definitions/access.Policy' + type: array + type: object + client.SetProcessMetadataRequest: + properties: + metadata: {} + type: object + client.UpdateIdentityRequest: + properties: + identity: + $ref: '#/definitions/identity.User' + type: object + client.UpdateProcessRequest: + properties: + config: + $ref: '#/definitions/app.Config' + type: object + cluster.Error: + properties: + code: + format: int + type: integer + details: + items: + type: string + type: array + message: + type: string + type: object + identity.Auth0Tenant: + properties: + audience: + type: string + client_id: + type: string + domain: + type: string + type: object + identity.User: + properties: + auth: + $ref: '#/definitions/identity.UserAuth' + name: + type: string + superuser: + type: boolean + type: object + identity.UserAuth: + properties: + api: + $ref: '#/definitions/identity.UserAuthAPI' + services: + $ref: '#/definitions/identity.UserAuthServices' + type: object + identity.UserAuthAPI: + properties: + auth0: + $ref: '#/definitions/identity.UserAuthAPIAuth0' + password: + type: string + type: object + identity.UserAuthAPIAuth0: + properties: + tenant: + $ref: '#/definitions/identity.Auth0Tenant' + user: + type: string + type: object + identity.UserAuthServices: + properties: + basic: + items: + type: string + type: array + token: + items: + type: string + type: array + type: object +info: + contact: + email: hello@datarhei.com + name: datarhei Core Support + url: https://www.datarhei.com + description: Internal REST API for the datarhei Core cluster + license: + name: Apache 2.0 + url: https://github.com/datarhei/core/v16/blob/main/LICENSE + title: datarhei Core Cluster API + version: "1.0" +paths: + /v1/core: + get: + description: Core API address and login of this node + operationId: cluster-1-core-api-address + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "500": + description: Internal Server Error + schema: + items: + $ref: '#/definitions/cluster.Error' + type: array + summary: Core API address and login + tags: + - v1.0.0 + /v1/iam/user: + post: + consumes: + - application/json + description: Add an identity to the cluster DB + operationId: cluster-1-add-identity + parameters: + - description: Identity config + in: body + name: config + required: true + schema: + $ref: '#/definitions/client.AddIdentityRequest' + - description: Origin ID of request + in: header + name: X-Cluster-Origin + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/cluster.Error' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/cluster.Error' + "508": + description: Loop Detected + schema: + $ref: '#/definitions/cluster.Error' + summary: Add an identity + tags: + - v1.0.0 + /v1/iam/user/{name}: + delete: + description: Remove an identity from the cluster DB + operationId: cluster-1-remove-identity + parameters: + - description: Identity name + in: path + name: name + required: true + type: string + - description: Origin ID of request + in: header + name: X-Cluster-Origin + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/cluster.Error' + "508": + description: Loop Detected + schema: + $ref: '#/definitions/cluster.Error' + summary: Remove an identity + tags: + - v1.0.0 + put: + consumes: + - application/json + description: Replace an existing identity in the cluster DB + operationId: cluster-1-update-identity + parameters: + - description: Process ID + in: path + name: name + required: true + type: string + - description: Identity config + in: body + name: config + required: true + schema: + $ref: '#/definitions/client.UpdateIdentityRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/cluster.Error' + "508": + description: Loop Detected + schema: + $ref: '#/definitions/cluster.Error' + summary: Replace an existing identity + tags: + - v1.0.0 + /v1/iam/user/{name}/policies: + put: + description: Set policies for an identity in the cluster DB. Any existing policies + will be replaced. + operationId: cluster-3-set-identity-policies + parameters: + - description: Process ID + in: path + name: id + required: true + type: string + - description: Policies for that user + in: body + name: data + required: true + schema: + $ref: '#/definitions/client.SetPoliciesRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/cluster.Error' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/cluster.Error' + "508": + description: Loop Detected + schema: + $ref: '#/definitions/cluster.Error' + summary: Set identity policies + tags: + - v1.0.0 + /v1/process: + post: + consumes: + - application/json + description: Add a process to the cluster DB + operationId: cluster-1-add-process + parameters: + - description: Process config + in: body + name: config + required: true + schema: + $ref: '#/definitions/client.AddProcessRequest' + - description: Origin ID of request + in: header + name: X-Cluster-Origin + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/cluster.Error' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/cluster.Error' + "508": + description: Loop Detected + schema: + $ref: '#/definitions/cluster.Error' + summary: Add a process + tags: + - v1.0.0 + /v1/process/{id}: + delete: + description: Remove a process from the cluster DB + operationId: cluster-1-remove-process + parameters: + - description: Process ID + in: path + name: id + required: true + type: string + - description: Domain to act on + in: query + name: domain + type: string + - description: Origin ID of request + in: header + name: X-Cluster-Origin + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/cluster.Error' + "508": + description: Loop Detected + schema: + $ref: '#/definitions/cluster.Error' + summary: Remove a process + tags: + - v1.0.0 + put: + consumes: + - application/json + description: Replace an existing process in the cluster DB + operationId: cluster-1-update-process + parameters: + - description: Process ID + in: path + name: id + required: true + type: string + - description: Domain to act on + in: query + name: domain + type: string + - description: Process config + in: body + name: config + required: true + schema: + $ref: '#/definitions/client.UpdateProcessRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/cluster.Error' + "508": + description: Loop Detected + schema: + $ref: '#/definitions/cluster.Error' + summary: Replace an existing process + tags: + - v1.0.0 + /v1/process/{id}/metadata/{key}: + put: + description: Add arbitrary JSON metadata under the given key. If the key exists, + all already stored metadata with this key will be overwritten. If the key + doesn't exist, it will be created. + operationId: cluster-3-set-process-metadata + parameters: + - description: Process ID + in: path + name: id + required: true + type: string + - description: Key for data store + in: path + name: key + required: true + type: string + - description: Domain to act on + in: query + name: domain + type: string + - description: Arbitrary JSON data. The null value will remove the key and its + contents + in: body + name: data + required: true + schema: + $ref: '#/definitions/client.SetProcessMetadataRequest' + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/cluster.Error' + "508": + description: Loop Detected + schema: + $ref: '#/definitions/cluster.Error' + summary: Add JSON metadata with a process under the given key + tags: + - v1.0.0 + /v1/server: + post: + consumes: + - application/json + description: Add a new server to the cluster + operationId: cluster-1-add-server + parameters: + - description: Server ID and address + in: body + name: config + required: true + schema: + $ref: '#/definitions/client.JoinRequest' + - description: Origin ID of request + in: header + name: X-Cluster-Origin + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "400": + description: Bad Request + schema: + $ref: '#/definitions/cluster.Error' + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/cluster.Error' + "508": + description: Loop Detected + schema: + $ref: '#/definitions/cluster.Error' + summary: Add a new server + tags: + - v1.0.0 + /v1/server/{id}: + delete: + description: Remove a server from the cluster + operationId: cluster-1-remove-server + parameters: + - description: Server ID + in: path + name: id + required: true + type: string + - description: Origin ID of request + in: header + name: X-Cluster-Origin + type: string + produces: + - application/json + responses: + "200": + description: OK + schema: + type: string + "500": + description: Internal Server Error + schema: + $ref: '#/definitions/cluster.Error' + "508": + description: Loop Detected + schema: + $ref: '#/definitions/cluster.Error' + summary: Remove a server + tags: + - v1.0.0 + /v1/snapshot: + get: + description: Current snapshot of the clusterDB + operationId: cluster-1-snapshot + produces: + - application/octet-stream + responses: + "200": + description: OK + schema: + type: file + "500": + description: Internal Server Error + schema: + items: + $ref: '#/definitions/cluster.Error' + type: array + summary: Cluster DB snapshot + tags: + - v1.0.0 +swagger: "2.0" diff --git a/cluster/forwarder/forwarder.go b/cluster/forwarder/forwarder.go index cd503f35..f9953a0e 100644 --- a/cluster/forwarder/forwarder.go +++ b/cluster/forwarder/forwarder.go @@ -161,7 +161,6 @@ func (f *forwarder) UpdateProcess(origin string, id app.ProcessID, config *app.C } r := apiclient.UpdateProcessRequest{ - ID: id, Config: *config, } @@ -169,7 +168,7 @@ func (f *forwarder) UpdateProcess(origin string, id app.ProcessID, config *app.C client := f.client f.lock.RUnlock() - return client.UpdateProcess(origin, r) + return client.UpdateProcess(origin, id, r) } func (f *forwarder) SetProcessMetadata(origin string, id app.ProcessID, key string, data interface{}) error { @@ -178,8 +177,6 @@ func (f *forwarder) SetProcessMetadata(origin string, id app.ProcessID, key stri } r := apiclient.SetProcessMetadataRequest{ - ID: id, - Key: key, Metadata: data, } @@ -187,7 +184,7 @@ func (f *forwarder) SetProcessMetadata(origin string, id app.ProcessID, key stri client := f.client f.lock.RUnlock() - return client.SetProcessMetadata(origin, r) + return client.SetProcessMetadata(origin, id, key, r) } func (f *forwarder) RemoveProcess(origin string, id app.ProcessID) error { @@ -224,7 +221,6 @@ func (f *forwarder) UpdateIdentity(origin, name string, identity iamidentity.Use } r := apiclient.UpdateIdentityRequest{ - Name: name, Identity: identity, } @@ -241,7 +237,6 @@ func (f *forwarder) SetPolicies(origin, name string, policies []iamaccess.Policy } r := apiclient.SetPoliciesRequest{ - Name: name, Policies: policies, }