Compare commits

...

4 Commits

Author SHA1 Message Date
Karan Balani
78f75ac8cc chore: handle 409 conflict also 2026-02-18 12:20:43 +05:30
Karan Balani
002bdab078 fix: host update endpoint 2026-02-18 11:41:48 +05:30
Karan Balani
7d3b21463f chore: update open api specs 2026-02-18 10:59:42 +05:30
Karan Balani
bcfb986127 feat: add zeus handler with profile and host apis 2026-02-18 01:26:29 +05:30
13 changed files with 333 additions and 11 deletions

View File

@@ -3526,6 +3526,108 @@ paths:
summary: Rotate session
tags:
- sessions
/api/v2/zeus/deployments/host:
put:
deprecated: false
description: This endpoint saves the host of a deployment to zeus.
operationId: PutHostInZeus
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ZeustypesPostableHost'
responses:
"204":
description: No Content
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- ADMIN
- tokenizer:
- ADMIN
summary: Put host in Zeus for a deployment.
tags:
- zeus
/api/v2/zeus/deployments/profile:
put:
deprecated: false
description: This endpoint saves the profile of a deployment to zeus.
operationId: PutProfileInZeus
requestBody:
content:
application/json:
schema:
$ref: '#/components/schemas/ZeustypesPostableProfile'
responses:
"204":
description: No Content
"400":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Bad Request
"401":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Unauthorized
"403":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Forbidden
"404":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Not Found
"500":
content:
application/json:
schema:
$ref: '#/components/schemas/RenderErrorResponse'
description: Internal Server Error
security:
- api_key:
- ADMIN
- tokenizer:
- ADMIN
summary: Put profile in Zeus for a deployment.
tags:
- zeus
components:
schemas:
AuthtypesAttributeMapping:
@@ -4688,6 +4790,37 @@ components:
format: date-time
type: string
type: object
ZeustypesPostableHost:
properties:
name:
type: string
required:
- name
type: object
ZeustypesPostableProfile:
properties:
existing_observability_tool:
type: string
has_existing_observability_tool:
type: boolean
logs_scale_per_day_in_gb:
type: integer
number_of_hosts:
type: integer
number_of_services:
type: integer
reasons_for_interest_in_signoz:
items:
type: string
nullable: true
type: array
timeline_for_migrating_to_signoz:
type: string
uses_otel:
type: boolean
where_did_you_discover_signoz:
type: string
type: object
securitySchemes:
api_key:
description: API Keys

View File

@@ -3,6 +3,7 @@ package httpzeus
import (
"bytes"
"context"
"encoding/json"
"io"
"net/http"
"net/url"
@@ -10,6 +11,7 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/http/client"
"github.com/SigNoz/signoz/pkg/types/zeustypes"
"github.com/SigNoz/signoz/pkg/zeus"
"github.com/tidwall/gjson"
)
@@ -119,8 +121,13 @@ func (provider *Provider) PutMeters(ctx context.Context, key string, data []byte
return err
}
func (provider *Provider) PutProfile(ctx context.Context, key string, body []byte) error {
_, err := provider.do(
func (provider *Provider) PutProfile(ctx context.Context, key string, profile *zeustypes.PostableProfile) error {
body, err := json.Marshal(profile)
if err != nil {
return err
}
_, err = provider.do(
ctx,
provider.config.URL.JoinPath("/v2/profiles/me"),
http.MethodPut,
@@ -131,10 +138,15 @@ func (provider *Provider) PutProfile(ctx context.Context, key string, body []byt
return err
}
func (provider *Provider) PutHost(ctx context.Context, key string, body []byte) error {
_, err := provider.do(
func (provider *Provider) PutHost(ctx context.Context, key string, host *zeustypes.PostableHost) error {
body, err := json.Marshal(host)
if err != nil {
return err
}
_, err = provider.do(
ctx,
provider.config.URL.JoinPath("/v2/deployments/me/hosts"),
provider.config.URL.JoinPath("/v2/deployments/me/host"),
http.MethodPut,
key,
body,
@@ -183,6 +195,8 @@ func (provider *Provider) errFromStatusCode(statusCode int) error {
return errors.Newf(errors.TypeForbidden, errors.CodeForbidden, "forbidden")
case http.StatusNotFound:
return errors.Newf(errors.TypeNotFound, errors.CodeNotFound, "not found")
case http.StatusConflict:
return errors.Newf(errors.TypeAlreadyExists, errors.CodeAlreadyExists, "already exists")
}
return errors.Newf(errors.TypeInternal, errors.CodeInternal, "internal")

View File

@@ -22,6 +22,7 @@ import (
"github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/zeus"
"github.com/gorilla/mux"
)
@@ -44,6 +45,7 @@ type provider struct {
gatewayHandler gateway.Handler
fieldsHandler fields.Handler
authzHandler authz.Handler
zeusHandler zeus.Handler
}
func NewFactory(
@@ -63,6 +65,7 @@ func NewFactory(
gatewayHandler gateway.Handler,
fieldsHandler fields.Handler,
authzHandler authz.Handler,
zeusHandler zeus.Handler,
) factory.ProviderFactory[apiserver.APIServer, apiserver.Config] {
return factory.NewProviderFactory(factory.MustNewName("signoz"), func(ctx context.Context, providerSettings factory.ProviderSettings, config apiserver.Config) (apiserver.APIServer, error) {
return newProvider(
@@ -85,6 +88,7 @@ func NewFactory(
gatewayHandler,
fieldsHandler,
authzHandler,
zeusHandler,
)
})
}
@@ -109,6 +113,7 @@ func newProvider(
gatewayHandler gateway.Handler,
fieldsHandler fields.Handler,
authzHandler authz.Handler,
zeusHandler zeus.Handler,
) (apiserver.APIServer, error) {
settings := factory.NewScopedProviderSettings(providerSettings, "github.com/SigNoz/signoz/pkg/apiserver/signozapiserver")
router := mux.NewRouter().UseEncodedPath()
@@ -131,6 +136,7 @@ func newProvider(
gatewayHandler: gatewayHandler,
fieldsHandler: fieldsHandler,
authzHandler: authzHandler,
zeusHandler: zeusHandler,
}
provider.authZ = middleware.NewAuthZ(settings.Logger(), orgGetter, authz)
@@ -199,6 +205,10 @@ func (provider *provider) AddToRouter(router *mux.Router) error {
return err
}
if err := provider.addZeusRoutes(router); err != nil {
return err
}
return nil
}

View File

@@ -0,0 +1,48 @@
package signozapiserver
import (
"net/http"
"github.com/SigNoz/signoz/pkg/http/handler"
"github.com/SigNoz/signoz/pkg/types"
"github.com/SigNoz/signoz/pkg/types/zeustypes"
"github.com/gorilla/mux"
)
func (provider *provider) addZeusRoutes(router *mux.Router) error {
if err := router.Handle("/api/v2/zeus/deployments/profile", handler.New(provider.authZ.AdminAccess(provider.zeusHandler.PutProfile), handler.OpenAPIDef{
ID: "PutProfileInZeus",
Tags: []string{"zeus"},
Summary: "Put profile in Zeus for a deployment.",
Description: "This endpoint saves the profile of a deployment to zeus.",
Request: new(zeustypes.PostableProfile),
RequestContentType: "application/json",
Response: nil,
ResponseContentType: "",
SuccessStatusCode: http.StatusNoContent,
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden, http.StatusNotFound, http.StatusConflict},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
})).Methods(http.MethodPut).GetError(); err != nil {
return err
}
if err := router.Handle("/api/v2/zeus/deployments/host", handler.New(provider.authZ.AdminAccess(provider.zeusHandler.PutHost), handler.OpenAPIDef{
ID: "PutHostInZeus",
Tags: []string{"zeus"},
Summary: "Put host in Zeus for a deployment.",
Description: "This endpoint saves the host of a deployment to zeus.",
Request: new(zeustypes.PostableHost),
RequestContentType: "application/json",
Response: nil,
ResponseContentType: "",
SuccessStatusCode: http.StatusNoContent,
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized, http.StatusForbidden, http.StatusNotFound, http.StatusConflict},
Deprecated: false,
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
})).Methods(http.MethodPut).GetError(); err != nil {
return err
}
return nil
}

View File

@@ -31,6 +31,7 @@ import (
"github.com/SigNoz/signoz/pkg/modules/tracefunnel/impltracefunnel"
"github.com/SigNoz/signoz/pkg/querier"
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
"github.com/SigNoz/signoz/pkg/zeus"
)
type Handlers struct {
@@ -48,6 +49,7 @@ type Handlers struct {
GatewayHandler gateway.Handler
Fields fields.Handler
AuthzHandler authz.Handler
ZeusHandler zeus.Handler
}
func NewHandlers(
@@ -60,6 +62,7 @@ func NewHandlers(
gatewayService gateway.Gateway,
telemetryMetadataStore telemetrytypes.MetadataStore,
authz authz.AuthZ,
zeusService zeus.Zeus,
) Handlers {
return Handlers{
SavedView: implsavedview.NewHandler(modules.SavedView),
@@ -76,5 +79,6 @@ func NewHandlers(
GatewayHandler: gateway.NewHandler(gatewayService),
Fields: implfields.NewHandler(providerSettings, telemetryMetadataStore),
AuthzHandler: signozauthzapi.NewHandler(authz),
ZeusHandler: zeus.NewHandler(zeusService, licensing),
}
}

View File

@@ -42,7 +42,7 @@ func TestNewHandlers(t *testing.T) {
dashboardModule := impldashboard.NewModule(impldashboard.NewStore(sqlstore), providerSettings, nil, orgGetter, queryParser)
modules := NewModules(sqlstore, tokenizer, emailing, providerSettings, orgGetter, alertmanager, nil, nil, nil, nil, nil, nil, nil, queryParser, Config{}, dashboardModule)
handlers := NewHandlers(modules, providerSettings, nil, nil, nil, nil, nil, nil, nil)
handlers := NewHandlers(modules, providerSettings, nil, nil, nil, nil, nil, nil, nil, nil)
reflectVal := reflect.ValueOf(handlers)
for i := 0; i < reflectVal.NumField(); i++ {
f := reflectVal.Field(i)

View File

@@ -23,6 +23,7 @@ import (
"github.com/SigNoz/signoz/pkg/modules/session"
"github.com/SigNoz/signoz/pkg/modules/user"
"github.com/SigNoz/signoz/pkg/types/ctxtypes"
"github.com/SigNoz/signoz/pkg/zeus"
"github.com/swaggest/jsonschema-go"
"github.com/swaggest/openapi-go"
"github.com/swaggest/openapi-go/openapi3"
@@ -52,6 +53,7 @@ func NewOpenAPI(ctx context.Context, instrumentation instrumentation.Instrumenta
struct{ gateway.Handler }{},
struct{ fields.Handler }{},
struct{ authz.Handler }{},
struct{ zeus.Handler }{},
).New(ctx, instrumentation.ToProviderSettings(), apiserver.Config{})
if err != nil {
return nil, err

View File

@@ -252,6 +252,7 @@ func NewAPIServerProviderFactories(orgGetter organization.Getter, authz authz.Au
handlers.GatewayHandler,
handlers.Fields,
handlers.AuthzHandler,
handlers.ZeusHandler,
),
)
}

View File

@@ -392,7 +392,7 @@ func New(
userService := impluser.NewService(providerSettings, impluser.NewStore(sqlstore, providerSettings), modules.User, orgGetter, authz, config.User.Root)
// Initialize all handlers for the modules
handlers := NewHandlers(modules, providerSettings, querier, licensing, global, flagger, gateway, telemetryMetadataStore, authz)
handlers := NewHandlers(modules, providerSettings, querier, licensing, global, flagger, gateway, telemetryMetadataStore, authz, zeus)
// Initialize the API server
apiserver, err := factory.NewProviderFromNamedMap(

View File

@@ -0,0 +1,17 @@
package zeustypes
type PostableHost struct {
Name string `json:"name" required:"true"`
}
type PostableProfile struct {
UsesOtel bool `json:"uses_otel"`
HasExistingObservabilityTool bool `json:"has_existing_observability_tool"`
ExistingObservabilityTool string `json:"existing_observability_tool"`
WhereDidYouDiscoverSignoz string `json:"where_did_you_discover_signoz"`
TimelineForMigratingToSignoz string `json:"timeline_for_migrating_to_signoz"`
ReasonsForInterestInSignoz []string `json:"reasons_for_interest_in_signoz"`
LogsScalePerDayInGB int `json:"logs_scale_per_day_in_gb"`
NumberOfHosts int `json:"number_of_hosts"`
NumberOfServices int `json:"number_of_services"`
}

82
pkg/zeus/handler.go Normal file
View File

@@ -0,0 +1,82 @@
package zeus
import (
"encoding/json"
"net/http"
"github.com/SigNoz/signoz/pkg/http/render"
"github.com/SigNoz/signoz/pkg/licensing"
"github.com/SigNoz/signoz/pkg/types/authtypes"
"github.com/SigNoz/signoz/pkg/types/zeustypes"
"github.com/SigNoz/signoz/pkg/valuer"
)
type handler struct {
zeus Zeus
licensing licensing.Licensing
}
func NewHandler(zeus Zeus, licensing licensing.Licensing) Handler {
return &handler{
zeus: zeus,
licensing: licensing,
}
}
func (h *handler) PutProfile(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
license, err := h.licensing.GetActive(ctx, valuer.MustNewUUID(claims.OrgID))
if err != nil {
render.Error(rw, err)
return
}
var req zeustypes.PostableProfile
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
render.Error(rw, err)
return
}
if err := h.zeus.PutProfile(ctx, license.Key, &req); err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusNoContent, nil)
}
func (h *handler) PutHost(rw http.ResponseWriter, r *http.Request) {
ctx := r.Context()
claims, err := authtypes.ClaimsFromContext(ctx)
if err != nil {
render.Error(rw, err)
return
}
license, err := h.licensing.GetActive(ctx, valuer.MustNewUUID(claims.OrgID))
if err != nil {
render.Error(rw, err)
return
}
var req zeustypes.PostableHost
if err := json.NewDecoder(r.Body).Decode(&req); err != nil {
render.Error(rw, err)
return
}
if err := h.zeus.PutHost(ctx, license.Key, &req); err != nil {
render.Error(rw, err)
return
}
render.Success(rw, http.StatusNoContent, nil)
}

View File

@@ -5,6 +5,7 @@ import (
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/factory"
"github.com/SigNoz/signoz/pkg/types/zeustypes"
"github.com/SigNoz/signoz/pkg/zeus"
)
@@ -40,10 +41,10 @@ func (provider *provider) PutMeters(_ context.Context, _ string, _ []byte) error
return errors.New(errors.TypeUnsupported, zeus.ErrCodeUnsupported, "putting meters is not supported")
}
func (provider *provider) PutProfile(_ context.Context, _ string, _ []byte) error {
func (provider *provider) PutProfile(_ context.Context, _ string, _ *zeustypes.PostableProfile) error {
return errors.New(errors.TypeUnsupported, zeus.ErrCodeUnsupported, "putting profile is not supported")
}
func (provider *provider) PutHost(_ context.Context, _ string, _ []byte) error {
func (provider *provider) PutHost(_ context.Context, _ string, _ *zeustypes.PostableHost) error {
return errors.New(errors.TypeUnsupported, zeus.ErrCodeUnsupported, "putting host is not supported")
}

View File

@@ -2,8 +2,10 @@ package zeus
import (
"context"
"net/http"
"github.com/SigNoz/signoz/pkg/errors"
"github.com/SigNoz/signoz/pkg/types/zeustypes"
)
var (
@@ -28,8 +30,16 @@ type Zeus interface {
PutMeters(context.Context, string, []byte) error
// Put profile for the given license key.
PutProfile(context.Context, string, []byte) error
PutProfile(context.Context, string, *zeustypes.PostableProfile) error
// Put host for the given license key.
PutHost(context.Context, string, []byte) error
PutHost(context.Context, string, *zeustypes.PostableHost) error
}
type Handler interface {
// API level handler for PutProfile
PutProfile(http.ResponseWriter, *http.Request)
// API level handler for PutHost
PutHost(http.ResponseWriter, *http.Request)
}