mirror of
https://github.com/SigNoz/signoz.git
synced 2026-05-13 21:50:31 +01:00
Compare commits
5 Commits
v0.123.0
...
platform-p
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
478430f426 | ||
|
|
43ec9fd039 | ||
|
|
2768d4c9e6 | ||
|
|
6f6f6bc766 | ||
|
|
951f55b062 |
@@ -66,10 +66,9 @@ func runGenerateAuthz(_ context.Context) error {
|
||||
registry := coretypes.NewRegistry()
|
||||
|
||||
allowedResources := map[string]bool{
|
||||
coretypes.NewResourceRef(coretypes.ResourceServiceAccount).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceMetaResourcesServiceAccount).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceRole).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceMetaResourcesRole).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceServiceAccount).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceRole).String(): true,
|
||||
coretypes.NewResourceRef(coretypes.ResourceMetaResourceFactorAPIKey).String(): true,
|
||||
}
|
||||
|
||||
allowedTypes := map[string]bool{}
|
||||
|
||||
@@ -449,6 +449,7 @@ components:
|
||||
- list
|
||||
- assignee
|
||||
- attach
|
||||
- detach
|
||||
type: string
|
||||
AuthtypesRole:
|
||||
properties:
|
||||
@@ -2206,7 +2207,6 @@ components:
|
||||
- role
|
||||
- organization
|
||||
- metaresource
|
||||
- metaresources
|
||||
type: string
|
||||
DashboardtypesDashboard:
|
||||
properties:
|
||||
@@ -2579,6 +2579,76 @@ components:
|
||||
- requiredMetricsCheck
|
||||
- endTimeBeforeRetention
|
||||
type: object
|
||||
InframonitoringtypesDeploymentRecord:
|
||||
properties:
|
||||
availablePods:
|
||||
type: integer
|
||||
deploymentCPU:
|
||||
format: double
|
||||
type: number
|
||||
deploymentCPULimit:
|
||||
format: double
|
||||
type: number
|
||||
deploymentCPURequest:
|
||||
format: double
|
||||
type: number
|
||||
deploymentMemory:
|
||||
format: double
|
||||
type: number
|
||||
deploymentMemoryLimit:
|
||||
format: double
|
||||
type: number
|
||||
deploymentMemoryRequest:
|
||||
format: double
|
||||
type: number
|
||||
deploymentName:
|
||||
type: string
|
||||
desiredPods:
|
||||
type: integer
|
||||
meta:
|
||||
additionalProperties:
|
||||
type: string
|
||||
nullable: true
|
||||
type: object
|
||||
podCountsByPhase:
|
||||
$ref: '#/components/schemas/InframonitoringtypesPodCountsByPhase'
|
||||
required:
|
||||
- deploymentName
|
||||
- deploymentCPU
|
||||
- deploymentCPURequest
|
||||
- deploymentCPULimit
|
||||
- deploymentMemory
|
||||
- deploymentMemoryRequest
|
||||
- deploymentMemoryLimit
|
||||
- desiredPods
|
||||
- availablePods
|
||||
- podCountsByPhase
|
||||
- meta
|
||||
type: object
|
||||
InframonitoringtypesDeployments:
|
||||
properties:
|
||||
endTimeBeforeRetention:
|
||||
type: boolean
|
||||
records:
|
||||
items:
|
||||
$ref: '#/components/schemas/InframonitoringtypesDeploymentRecord'
|
||||
nullable: true
|
||||
type: array
|
||||
requiredMetricsCheck:
|
||||
$ref: '#/components/schemas/InframonitoringtypesRequiredMetricsCheck'
|
||||
total:
|
||||
type: integer
|
||||
type:
|
||||
$ref: '#/components/schemas/InframonitoringtypesResponseType'
|
||||
warning:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5QueryWarnData'
|
||||
required:
|
||||
- type
|
||||
- records
|
||||
- total
|
||||
- requiredMetricsCheck
|
||||
- endTimeBeforeRetention
|
||||
type: object
|
||||
InframonitoringtypesHostFilter:
|
||||
properties:
|
||||
expression:
|
||||
@@ -2909,6 +2979,32 @@ components:
|
||||
- end
|
||||
- limit
|
||||
type: object
|
||||
InframonitoringtypesPostableDeployments:
|
||||
properties:
|
||||
end:
|
||||
format: int64
|
||||
type: integer
|
||||
filter:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5Filter'
|
||||
groupBy:
|
||||
items:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5GroupByKey'
|
||||
nullable: true
|
||||
type: array
|
||||
limit:
|
||||
type: integer
|
||||
offset:
|
||||
type: integer
|
||||
orderBy:
|
||||
$ref: '#/components/schemas/Querybuildertypesv5OrderBy'
|
||||
start:
|
||||
format: int64
|
||||
type: integer
|
||||
required:
|
||||
- start
|
||||
- end
|
||||
- limit
|
||||
type: object
|
||||
InframonitoringtypesPostableHosts:
|
||||
properties:
|
||||
end:
|
||||
@@ -9002,9 +9098,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- role:list
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- role:list
|
||||
summary: List roles
|
||||
tags:
|
||||
- role
|
||||
@@ -9076,9 +9172,9 @@ paths:
|
||||
description: Not Implemented
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- role:create
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- role:create
|
||||
summary: Create role
|
||||
tags:
|
||||
- role
|
||||
@@ -9138,9 +9234,9 @@ paths:
|
||||
description: Not Implemented
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- role:delete
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- role:delete
|
||||
summary: Delete role
|
||||
tags:
|
||||
- role
|
||||
@@ -9189,9 +9285,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- role:read
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- role:read
|
||||
summary: Get role
|
||||
tags:
|
||||
- role
|
||||
@@ -9255,9 +9351,9 @@ paths:
|
||||
description: Not Implemented
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- role:update
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- role:update
|
||||
summary: Patch role
|
||||
tags:
|
||||
- role
|
||||
@@ -9333,9 +9429,9 @@ paths:
|
||||
description: Not Implemented
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- role:read
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- role:read
|
||||
summary: Get objects for a role by relation
|
||||
tags:
|
||||
- role
|
||||
@@ -9411,9 +9507,9 @@ paths:
|
||||
description: Not Implemented
|
||||
security:
|
||||
- api_key:
|
||||
- ADMIN
|
||||
- role:update
|
||||
- tokenizer:
|
||||
- ADMIN
|
||||
- role:update
|
||||
summary: Patch objects for a role by relation
|
||||
tags:
|
||||
- role
|
||||
@@ -10017,9 +10113,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- serviceaccount:read
|
||||
- factor-api-key:list
|
||||
- tokenizer:
|
||||
- serviceaccount:read
|
||||
- factor-api-key:list
|
||||
summary: List service account keys
|
||||
tags:
|
||||
- serviceaccount
|
||||
@@ -10085,9 +10181,11 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- serviceaccount:update
|
||||
- factor-api-key:create
|
||||
- serviceaccount:attach
|
||||
- tokenizer:
|
||||
- serviceaccount:update
|
||||
- factor-api-key:create
|
||||
- serviceaccount:attach
|
||||
summary: Create a service account key
|
||||
tags:
|
||||
- serviceaccount
|
||||
@@ -10140,9 +10238,11 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- serviceaccount:update
|
||||
- factor-api-key:delete
|
||||
- serviceaccount:detach
|
||||
- tokenizer:
|
||||
- serviceaccount:update
|
||||
- factor-api-key:delete
|
||||
- serviceaccount:detach
|
||||
summary: Revoke a service account key
|
||||
tags:
|
||||
- serviceaccount
|
||||
@@ -10205,9 +10305,9 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- serviceaccount:update
|
||||
- factor-api-key:update
|
||||
- tokenizer:
|
||||
- serviceaccount:update
|
||||
- factor-api-key:update
|
||||
summary: Updates a service account key
|
||||
tags:
|
||||
- serviceaccount
|
||||
@@ -10379,11 +10479,11 @@ paths:
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- serviceaccount:attach
|
||||
- role:attach
|
||||
- serviceaccount:detach
|
||||
- role:detach
|
||||
- tokenizer:
|
||||
- serviceaccount:attach
|
||||
- role:attach
|
||||
- serviceaccount:detach
|
||||
- role:detach
|
||||
summary: Delete service account role
|
||||
tags:
|
||||
- serviceaccount
|
||||
@@ -11976,6 +12076,81 @@ paths:
|
||||
summary: List Clusters for Infra Monitoring
|
||||
tags:
|
||||
- inframonitoring
|
||||
/api/v2/infra_monitoring/deployments:
|
||||
post:
|
||||
deprecated: false
|
||||
description: 'Returns a paginated list of Kubernetes Deployments with key aggregated
|
||||
pod metrics: CPU usage and memory working set summed across pods owned by
|
||||
the deployment, plus average CPU/memory request and limit utilization (deploymentCPURequest,
|
||||
deploymentCPULimit, deploymentMemoryRequest, deploymentMemoryLimit). Each
|
||||
row also reports the latest known desiredPods (k8s.deployment.desired) and
|
||||
availablePods (k8s.deployment.available) replica counts and per-group podCountsByPhase
|
||||
({ pending, running, succeeded, failed, unknown } from each pod''s latest
|
||||
k8s.pod.phase value). Each deployment includes metadata attributes (k8s.deployment.name,
|
||||
k8s.namespace.name, k8s.cluster.name). The response type is ''list'' for the
|
||||
default k8s.deployment.name grouping or ''grouped_list'' for custom groupBy
|
||||
keys; in both modes every row aggregates pods owned by deployments in the
|
||||
group. Supports filtering via a filter expression, custom groupBy, ordering
|
||||
by cpu / cpu_request / cpu_limit / memory / memory_request / memory_limit
|
||||
/ desired_pods / available_pods, and pagination via offset/limit. Also reports
|
||||
missing required metrics and whether the requested time range falls before
|
||||
the data retention boundary. Numeric metric fields (deploymentCPU, deploymentCPURequest,
|
||||
deploymentCPULimit, deploymentMemory, deploymentMemoryRequest, deploymentMemoryLimit,
|
||||
desiredPods, availablePods) return -1 as a sentinel when no data is available
|
||||
for that field.'
|
||||
operationId: ListDeployments
|
||||
requestBody:
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/InframonitoringtypesPostableDeployments'
|
||||
responses:
|
||||
"200":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
properties:
|
||||
data:
|
||||
$ref: '#/components/schemas/InframonitoringtypesDeployments'
|
||||
status:
|
||||
type: string
|
||||
required:
|
||||
- status
|
||||
- data
|
||||
type: object
|
||||
description: OK
|
||||
"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
|
||||
"500":
|
||||
content:
|
||||
application/json:
|
||||
schema:
|
||||
$ref: '#/components/schemas/RenderErrorResponse'
|
||||
description: Internal Server Error
|
||||
security:
|
||||
- api_key:
|
||||
- VIEWER
|
||||
- tokenizer:
|
||||
- VIEWER
|
||||
summary: List Deployments for Infra Monitoring
|
||||
tags:
|
||||
- inframonitoring
|
||||
/api/v2/infra_monitoring/hosts:
|
||||
post:
|
||||
deprecated: false
|
||||
|
||||
@@ -7,17 +7,27 @@ type organization
|
||||
|
||||
type user
|
||||
relations
|
||||
define create: [user, serviceaccount, role#assignee]
|
||||
define list: [user, serviceaccount, role#assignee]
|
||||
|
||||
define read: [user, serviceaccount, role#assignee]
|
||||
define update: [user, serviceaccount, role#assignee]
|
||||
define delete: [user, serviceaccount, role#assignee]
|
||||
|
||||
define attach: [user, serviceaccount, role#assignee]
|
||||
define detach: [user, serviceaccount, role#assignee]
|
||||
|
||||
type serviceaccount
|
||||
relations
|
||||
define create: [user, serviceaccount, role#assignee]
|
||||
define list: [user, serviceaccount, role#assignee]
|
||||
|
||||
define read: [user, serviceaccount, role#assignee]
|
||||
define update: [user, serviceaccount, role#assignee]
|
||||
define delete: [user, serviceaccount, role#assignee]
|
||||
|
||||
define attach: [user, serviceaccount, role#assignee]
|
||||
define detach: [user, serviceaccount, role#assignee]
|
||||
|
||||
type anonymous
|
||||
|
||||
@@ -25,25 +35,28 @@ type role
|
||||
relations
|
||||
define assignee: [user, serviceaccount, anonymous]
|
||||
|
||||
define create: [user, serviceaccount, role#assignee]
|
||||
define list: [user, serviceaccount, role#assignee]
|
||||
|
||||
define read: [user, serviceaccount, role#assignee]
|
||||
define update: [user, serviceaccount, role#assignee]
|
||||
define delete: [user, serviceaccount, role#assignee]
|
||||
define attach: [user, serviceaccount, role#assignee]
|
||||
|
||||
type metaresources
|
||||
define attach: [user, serviceaccount, role#assignee]
|
||||
define detach: [user, serviceaccount, role#assignee]
|
||||
|
||||
type metaresource
|
||||
relations
|
||||
define create: [user, serviceaccount, role#assignee]
|
||||
define list: [user, serviceaccount, role#assignee]
|
||||
|
||||
type metaresource
|
||||
relations
|
||||
define read: [user, serviceaccount, anonymous, role#assignee]
|
||||
define update: [user, serviceaccount, role#assignee]
|
||||
define delete: [user, serviceaccount, role#assignee]
|
||||
define read: [user, serviceaccount, anonymous, role#assignee]
|
||||
define update: [user, serviceaccount, role#assignee]
|
||||
define delete: [user, serviceaccount, role#assignee]
|
||||
|
||||
define block: [user, serviceaccount, role#assignee]
|
||||
define block: [user, serviceaccount, role#assignee]
|
||||
|
||||
|
||||
type telemetryresource
|
||||
relations
|
||||
define read: [user, serviceaccount, role#assignee]
|
||||
define read: [user, serviceaccount, role#assignee]
|
||||
@@ -13,12 +13,14 @@ import type {
|
||||
|
||||
import type {
|
||||
InframonitoringtypesPostableClustersDTO,
|
||||
InframonitoringtypesPostableDeploymentsDTO,
|
||||
InframonitoringtypesPostableHostsDTO,
|
||||
InframonitoringtypesPostableNamespacesDTO,
|
||||
InframonitoringtypesPostableNodesDTO,
|
||||
InframonitoringtypesPostablePodsDTO,
|
||||
InframonitoringtypesPostableVolumesDTO,
|
||||
ListClusters200,
|
||||
ListDeployments200,
|
||||
ListHosts200,
|
||||
ListNamespaces200,
|
||||
ListNodes200,
|
||||
@@ -114,6 +116,90 @@ export const useListClusters = <
|
||||
|
||||
return useMutation(mutationOptions);
|
||||
};
|
||||
/**
|
||||
* Returns a paginated list of Kubernetes Deployments with key aggregated pod metrics: CPU usage and memory working set summed across pods owned by the deployment, plus average CPU/memory request and limit utilization (deploymentCPURequest, deploymentCPULimit, deploymentMemoryRequest, deploymentMemoryLimit). Each row also reports the latest known desiredPods (k8s.deployment.desired) and availablePods (k8s.deployment.available) replica counts and per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } from each pod's latest k8s.pod.phase value). Each deployment includes metadata attributes (k8s.deployment.name, k8s.namespace.name, k8s.cluster.name). The response type is 'list' for the default k8s.deployment.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates pods owned by deployments in the group. Supports filtering via a filter expression, custom groupBy, ordering by cpu / cpu_request / cpu_limit / memory / memory_request / memory_limit / desired_pods / available_pods, and pagination via offset/limit. Also reports missing required metrics and whether the requested time range falls before the data retention boundary. Numeric metric fields (deploymentCPU, deploymentCPURequest, deploymentCPULimit, deploymentMemory, deploymentMemoryRequest, deploymentMemoryLimit, desiredPods, availablePods) return -1 as a sentinel when no data is available for that field.
|
||||
* @summary List Deployments for Infra Monitoring
|
||||
*/
|
||||
export const listDeployments = (
|
||||
inframonitoringtypesPostableDeploymentsDTO: BodyType<InframonitoringtypesPostableDeploymentsDTO>,
|
||||
signal?: AbortSignal,
|
||||
) => {
|
||||
return GeneratedAPIInstance<ListDeployments200>({
|
||||
url: `/api/v2/infra_monitoring/deployments`,
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
data: inframonitoringtypesPostableDeploymentsDTO,
|
||||
signal,
|
||||
});
|
||||
};
|
||||
|
||||
export const getListDeploymentsMutationOptions = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof listDeployments>>,
|
||||
TError,
|
||||
{ data: BodyType<InframonitoringtypesPostableDeploymentsDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationOptions<
|
||||
Awaited<ReturnType<typeof listDeployments>>,
|
||||
TError,
|
||||
{ data: BodyType<InframonitoringtypesPostableDeploymentsDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationKey = ['listDeployments'];
|
||||
const { mutation: mutationOptions } = options
|
||||
? options.mutation &&
|
||||
'mutationKey' in options.mutation &&
|
||||
options.mutation.mutationKey
|
||||
? options
|
||||
: { ...options, mutation: { ...options.mutation, mutationKey } }
|
||||
: { mutation: { mutationKey } };
|
||||
|
||||
const mutationFn: MutationFunction<
|
||||
Awaited<ReturnType<typeof listDeployments>>,
|
||||
{ data: BodyType<InframonitoringtypesPostableDeploymentsDTO> }
|
||||
> = (props) => {
|
||||
const { data } = props ?? {};
|
||||
|
||||
return listDeployments(data);
|
||||
};
|
||||
|
||||
return { mutationFn, ...mutationOptions };
|
||||
};
|
||||
|
||||
export type ListDeploymentsMutationResult = NonNullable<
|
||||
Awaited<ReturnType<typeof listDeployments>>
|
||||
>;
|
||||
export type ListDeploymentsMutationBody =
|
||||
BodyType<InframonitoringtypesPostableDeploymentsDTO>;
|
||||
export type ListDeploymentsMutationError = ErrorType<RenderErrorResponseDTO>;
|
||||
|
||||
/**
|
||||
* @summary List Deployments for Infra Monitoring
|
||||
*/
|
||||
export const useListDeployments = <
|
||||
TError = ErrorType<RenderErrorResponseDTO>,
|
||||
TContext = unknown,
|
||||
>(options?: {
|
||||
mutation?: UseMutationOptions<
|
||||
Awaited<ReturnType<typeof listDeployments>>,
|
||||
TError,
|
||||
{ data: BodyType<InframonitoringtypesPostableDeploymentsDTO> },
|
||||
TContext
|
||||
>;
|
||||
}): UseMutationResult<
|
||||
Awaited<ReturnType<typeof listDeployments>>,
|
||||
TError,
|
||||
{ data: BodyType<InframonitoringtypesPostableDeploymentsDTO> },
|
||||
TContext
|
||||
> => {
|
||||
const mutationOptions = getListDeploymentsMutationOptions(options);
|
||||
|
||||
return useMutation(mutationOptions);
|
||||
};
|
||||
/**
|
||||
* Returns a paginated list of hosts with key infrastructure metrics: CPU usage (%), memory usage (%), I/O wait (%), disk usage (%), and 15-minute load average. Each host includes its current status (active/inactive based on metrics reported in the last 10 minutes) and metadata attributes (e.g., os.type). Supports filtering via a filter expression, filtering by host status, custom groupBy to aggregate hosts by any attribute, ordering by any of the five metrics, and pagination via offset/limit. The response type is 'list' for the default host.name grouping or 'grouped_list' for custom groupBy keys. Also reports missing required metrics and whether the requested time range falls before the data retention boundary. Numeric metric fields (cpu, memory, wait, load15, diskUsage) return -1 as a sentinel when no data is available for that field.
|
||||
* @summary List Hosts for Infra Monitoring
|
||||
|
||||
@@ -1840,6 +1840,7 @@ export enum AuthtypesRelationDTO {
|
||||
list = 'list',
|
||||
assignee = 'assignee',
|
||||
attach = 'attach',
|
||||
detach = 'detach',
|
||||
}
|
||||
export interface AuthtypesRoleDTO {
|
||||
/**
|
||||
@@ -4161,7 +4162,6 @@ export enum CoretypesTypeDTO {
|
||||
role = 'role',
|
||||
organization = 'organization',
|
||||
metaresource = 'metaresource',
|
||||
metaresources = 'metaresources',
|
||||
}
|
||||
export interface DashboardtypesDashboardDTO {
|
||||
/**
|
||||
@@ -4628,6 +4628,83 @@ export interface InframonitoringtypesClustersDTO {
|
||||
warning?: Querybuildertypesv5QueryWarnDataDTO;
|
||||
}
|
||||
|
||||
/**
|
||||
* @nullable
|
||||
*/
|
||||
export type InframonitoringtypesDeploymentRecordDTOMeta = {
|
||||
[key: string]: string;
|
||||
} | null;
|
||||
|
||||
export interface InframonitoringtypesDeploymentRecordDTO {
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
availablePods: number;
|
||||
/**
|
||||
* @type number
|
||||
* @format double
|
||||
*/
|
||||
deploymentCPU: number;
|
||||
/**
|
||||
* @type number
|
||||
* @format double
|
||||
*/
|
||||
deploymentCPULimit: number;
|
||||
/**
|
||||
* @type number
|
||||
* @format double
|
||||
*/
|
||||
deploymentCPURequest: number;
|
||||
/**
|
||||
* @type number
|
||||
* @format double
|
||||
*/
|
||||
deploymentMemory: number;
|
||||
/**
|
||||
* @type number
|
||||
* @format double
|
||||
*/
|
||||
deploymentMemoryLimit: number;
|
||||
/**
|
||||
* @type number
|
||||
* @format double
|
||||
*/
|
||||
deploymentMemoryRequest: number;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
deploymentName: string;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
desiredPods: number;
|
||||
/**
|
||||
* @type object
|
||||
* @nullable true
|
||||
*/
|
||||
meta: InframonitoringtypesDeploymentRecordDTOMeta;
|
||||
podCountsByPhase: InframonitoringtypesPodCountsByPhaseDTO;
|
||||
}
|
||||
|
||||
export interface InframonitoringtypesDeploymentsDTO {
|
||||
/**
|
||||
* @type boolean
|
||||
*/
|
||||
endTimeBeforeRetention: boolean;
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
records: InframonitoringtypesDeploymentRecordDTO[] | null;
|
||||
requiredMetricsCheck: InframonitoringtypesRequiredMetricsCheckDTO;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
total: number;
|
||||
type: InframonitoringtypesResponseTypeDTO;
|
||||
warning?: Querybuildertypesv5QueryWarnDataDTO;
|
||||
}
|
||||
|
||||
export interface InframonitoringtypesHostFilterDTO {
|
||||
/**
|
||||
* @type string
|
||||
@@ -4973,6 +5050,34 @@ export interface InframonitoringtypesPostableClustersDTO {
|
||||
start: number;
|
||||
}
|
||||
|
||||
export interface InframonitoringtypesPostableDeploymentsDTO {
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
end: number;
|
||||
filter?: Querybuildertypesv5FilterDTO;
|
||||
/**
|
||||
* @type array
|
||||
* @nullable true
|
||||
*/
|
||||
groupBy?: Querybuildertypesv5GroupByKeyDTO[] | null;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
limit: number;
|
||||
/**
|
||||
* @type integer
|
||||
*/
|
||||
offset?: number;
|
||||
orderBy?: Querybuildertypesv5OrderByDTO;
|
||||
/**
|
||||
* @type integer
|
||||
* @format int64
|
||||
*/
|
||||
start: number;
|
||||
}
|
||||
|
||||
export interface InframonitoringtypesPostableHostsDTO {
|
||||
/**
|
||||
* @type integer
|
||||
@@ -9509,6 +9614,14 @@ export type ListClusters200 = {
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type ListDeployments200 = {
|
||||
data: InframonitoringtypesDeploymentsDTO;
|
||||
/**
|
||||
* @type string
|
||||
*/
|
||||
status: string;
|
||||
};
|
||||
|
||||
export type ListHosts200 = {
|
||||
data: InframonitoringtypesHostsDTO;
|
||||
/**
|
||||
|
||||
@@ -360,8 +360,7 @@ describe('createGuardedRoute', () => {
|
||||
const obj = payload[0]?.object;
|
||||
const kind = obj?.resource?.kind;
|
||||
const selector = obj?.selector ?? '*';
|
||||
const objectStr =
|
||||
obj?.resource?.type === 'metaresources' ? kind : `${kind}:${selector}`;
|
||||
const objectStr = `${kind}:${selector}`;
|
||||
requestedObjects.push(objectStr ?? '');
|
||||
|
||||
return res(ctx.status(200), ctx.json(authzMockResponse(payload, [true])));
|
||||
|
||||
@@ -4,12 +4,8 @@ export default {
|
||||
data: {
|
||||
resources: [
|
||||
{
|
||||
kind: 'role',
|
||||
type: 'metaresources',
|
||||
},
|
||||
{
|
||||
kind: 'serviceaccount',
|
||||
type: 'metaresources',
|
||||
kind: 'factor-api-key',
|
||||
type: 'metaresource',
|
||||
},
|
||||
{
|
||||
kind: 'role',
|
||||
@@ -22,12 +18,13 @@ export default {
|
||||
],
|
||||
relations: {
|
||||
assignee: ['role'],
|
||||
attach: ['role', 'serviceaccount'],
|
||||
create: ['metaresources'],
|
||||
delete: ['role', 'serviceaccount'],
|
||||
list: ['metaresources'],
|
||||
read: ['role', 'serviceaccount'],
|
||||
update: ['role', 'serviceaccount'],
|
||||
attach: ['metaresource', 'role', 'serviceaccount'],
|
||||
create: ['metaresource', 'role', 'serviceaccount'],
|
||||
delete: ['metaresource', 'role', 'serviceaccount'],
|
||||
detach: ['metaresource', 'role', 'serviceaccount'],
|
||||
list: ['metaresource', 'role', 'serviceaccount'],
|
||||
read: ['metaresource', 'role', 'serviceaccount'],
|
||||
update: ['metaresource', 'role', 'serviceaccount'],
|
||||
},
|
||||
},
|
||||
} as const;
|
||||
|
||||
@@ -80,19 +80,6 @@ export function permissionToTransactionDto(
|
||||
permission: BrandedPermission,
|
||||
): AuthtypesTransactionDTO {
|
||||
const { relation, object: objectStr } = parsePermission(permission);
|
||||
const directType = resolveType(relation, objectStr);
|
||||
if (directType === 'metaresources') {
|
||||
return {
|
||||
relation: relation as AuthtypesRelationDTO,
|
||||
object: {
|
||||
resource: {
|
||||
kind: objectStr as ResourceName,
|
||||
type: directType as CoretypesTypeDTO,
|
||||
},
|
||||
selector: '*',
|
||||
},
|
||||
};
|
||||
}
|
||||
const { resourceName, selector } = splitObjectString(objectStr);
|
||||
const type = resolveType(relation, resourceName) ?? 'metaresource';
|
||||
|
||||
@@ -117,9 +104,6 @@ export function gettableTransactionToPermission(
|
||||
} = item;
|
||||
const resourceName = String(resource.kind);
|
||||
const selectorStr = typeof selector === 'string' ? selector : '*';
|
||||
const objectStr =
|
||||
resource.type === 'metaresources'
|
||||
? resourceName
|
||||
: `${resourceName}${ObjectSeparator}${selectorStr}`;
|
||||
const objectStr = `${resourceName}${ObjectSeparator}${selectorStr}`;
|
||||
return `${relation}${PermissionSeparator}${objectStr}` as BrandedPermission;
|
||||
}
|
||||
|
||||
@@ -124,5 +124,24 @@ func (provider *provider) addInfraMonitoringRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v2/infra_monitoring/deployments", handler.New(
|
||||
provider.authzMiddleware.ViewAccess(provider.infraMonitoringHandler.ListDeployments),
|
||||
handler.OpenAPIDef{
|
||||
ID: "ListDeployments",
|
||||
Tags: []string{"inframonitoring"},
|
||||
Summary: "List Deployments for Infra Monitoring",
|
||||
Description: "Returns a paginated list of Kubernetes Deployments with key aggregated pod metrics: CPU usage and memory working set summed across pods owned by the deployment, plus average CPU/memory request and limit utilization (deploymentCPURequest, deploymentCPULimit, deploymentMemoryRequest, deploymentMemoryLimit). Each row also reports the latest known desiredPods (k8s.deployment.desired) and availablePods (k8s.deployment.available) replica counts and per-group podCountsByPhase ({ pending, running, succeeded, failed, unknown } from each pod's latest k8s.pod.phase value). Each deployment includes metadata attributes (k8s.deployment.name, k8s.namespace.name, k8s.cluster.name). The response type is 'list' for the default k8s.deployment.name grouping or 'grouped_list' for custom groupBy keys; in both modes every row aggregates pods owned by deployments in the group. Supports filtering via a filter expression, custom groupBy, ordering by cpu / cpu_request / cpu_limit / memory / memory_request / memory_limit / desired_pods / available_pods, and pagination via offset/limit. Also reports missing required metrics and whether the requested time range falls before the data retention boundary. Numeric metric fields (deploymentCPU, deploymentCPURequest, deploymentCPULimit, deploymentMemory, deploymentMemoryRequest, deploymentMemoryLimit, desiredPods, availablePods) return -1 as a sentinel when no data is available for that field.",
|
||||
Request: new(inframonitoringtypes.PostableDeployments),
|
||||
RequestContentType: "application/json",
|
||||
Response: new(inframonitoringtypes.Deployments),
|
||||
ResponseContentType: "application/json",
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusUnauthorized},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleViewer),
|
||||
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -7,11 +7,14 @@ import (
|
||||
"github.com/SigNoz/signoz/pkg/types"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/coretypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/gorilla/mux"
|
||||
)
|
||||
|
||||
func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
if err := router.Handle("/api/v1/roles", handler.New(provider.authzMiddleware.AdminAccess(provider.authzHandler.Create), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/roles", handler.New(provider.authzMiddleware.Check(provider.authzHandler.Create, authtypes.Relation{Verb: coretypes.VerbCreate}, coretypes.ResourceRole, roleCollectionSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "CreateRole",
|
||||
Tags: []string{"role"},
|
||||
Summary: "Create role",
|
||||
@@ -23,12 +26,14 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusCreated,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceRole.Scope(coretypes.VerbCreate)}),
|
||||
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/roles", handler.New(provider.authzMiddleware.AdminAccess(provider.authzHandler.List), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/roles", handler.New(provider.authzMiddleware.Check(provider.authzHandler.List, authtypes.Relation{Verb: coretypes.VerbList}, coretypes.ResourceRole, roleCollectionSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "ListRoles",
|
||||
Tags: []string{"role"},
|
||||
Summary: "List roles",
|
||||
@@ -40,12 +45,14 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceRole.Scope(coretypes.VerbList)}),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authzMiddleware.AdminAccess(provider.authzHandler.Get), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authzMiddleware.Check(provider.authzHandler.Get, authtypes.Relation{Verb: coretypes.VerbRead}, coretypes.ResourceRole, provider.roleInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "GetRole",
|
||||
Tags: []string{"role"},
|
||||
Summary: "Get role",
|
||||
@@ -57,12 +64,14 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceRole.Scope(coretypes.VerbRead)}),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/roles/{id}/relations/{relation}/objects", handler.New(provider.authzMiddleware.AdminAccess(provider.authzHandler.GetObjects), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/roles/{id}/relations/{relation}/objects", handler.New(provider.authzMiddleware.Check(provider.authzHandler.GetObjects, authtypes.Relation{Verb: coretypes.VerbRead}, coretypes.ResourceRole, provider.roleInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "GetObjects",
|
||||
Tags: []string{"role"},
|
||||
Summary: "Get objects for a role by relation",
|
||||
@@ -74,12 +83,14 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceRole.Scope(coretypes.VerbRead)}),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authzMiddleware.AdminAccess(provider.authzHandler.Patch), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authzMiddleware.Check(provider.authzHandler.Patch, authtypes.Relation{Verb: coretypes.VerbUpdate}, coretypes.ResourceRole, provider.roleInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "PatchRole",
|
||||
Tags: []string{"role"},
|
||||
Summary: "Patch role",
|
||||
@@ -91,12 +102,14 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceRole.Scope(coretypes.VerbUpdate)}),
|
||||
})).Methods(http.MethodPatch).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/roles/{id}/relations/{relation}/objects", handler.New(provider.authzMiddleware.AdminAccess(provider.authzHandler.PatchObjects), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/roles/{id}/relations/{relation}/objects", handler.New(provider.authzMiddleware.Check(provider.authzHandler.PatchObjects, authtypes.Relation{Verb: coretypes.VerbUpdate}, coretypes.ResourceRole, provider.roleInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "PatchObjects",
|
||||
Tags: []string{"role"},
|
||||
Summary: "Patch objects for a role by relation",
|
||||
@@ -108,12 +121,14 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusBadRequest, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceRole.Scope(coretypes.VerbUpdate)}),
|
||||
})).Methods(http.MethodPatch).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authzMiddleware.AdminAccess(provider.authzHandler.Delete), handler.OpenAPIDef{
|
||||
if err := router.Handle("/api/v1/roles/{id}", handler.New(provider.authzMiddleware.Check(provider.authzHandler.Delete, authtypes.Relation{Verb: coretypes.VerbDelete}, coretypes.ResourceRole, provider.roleInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "DeleteRole",
|
||||
Tags: []string{"role"},
|
||||
Summary: "Delete role",
|
||||
@@ -125,10 +140,33 @@ func (provider *provider) addRoleRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound, http.StatusNotImplemented, http.StatusUnavailableForLegalReasons},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newSecuritySchemes(types.RoleAdmin),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceRole.Scope(coretypes.VerbDelete)}),
|
||||
})).Methods(http.MethodDelete).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func roleCollectionSelectorCallback(_ *http.Request, _ authtypes.Claims) ([]coretypes.Selector, error) {
|
||||
return []coretypes.Selector{
|
||||
coretypes.TypeRole.MustSelector(coretypes.WildCardSelectorString),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func (provider *provider) roleInstanceSelectorCallback(req *http.Request, claims authtypes.Claims) ([]coretypes.Selector, error) {
|
||||
roleID, err := valuer.NewUUID(mux.Vars(req)["id"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
role, err := provider.authzService.Get(req.Context(), valuer.MustNewUUID(claims.OrgID), roleID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []coretypes.Selector{
|
||||
coretypes.TypeRole.MustSelector(role.Name),
|
||||
coretypes.TypeRole.MustSelector(coretypes.WildCardSelectorString),
|
||||
}, nil
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
if err := router.Handle("/api/v1/service_accounts", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.Create, authtypes.Relation{Verb: coretypes.VerbCreate}, coretypes.ResourceMetaResourcesServiceAccount, serviceAccountCollectionSelectorCallback, []string{
|
||||
if err := router.Handle("/api/v1/service_accounts", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.Create, authtypes.Relation{Verb: coretypes.VerbCreate}, coretypes.ResourceServiceAccount, serviceAccountCollectionSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "CreateServiceAccount",
|
||||
@@ -31,12 +31,12 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusCreated,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourcesServiceAccount.Scope(coretypes.VerbCreate)}),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbCreate)}),
|
||||
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/service_accounts", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.List, authtypes.Relation{Verb: coretypes.VerbList}, coretypes.ResourceMetaResourcesServiceAccount, serviceAccountCollectionSelectorCallback, []string{
|
||||
if err := router.Handle("/api/v1/service_accounts", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.List, authtypes.Relation{Verb: coretypes.VerbList}, coretypes.ResourceServiceAccount, serviceAccountCollectionSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "ListServiceAccounts",
|
||||
@@ -50,7 +50,7 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourcesServiceAccount.Scope(coretypes.VerbList)}),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbList)}),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -135,10 +135,10 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/roles/{rid}", handler.New(provider.authzMiddleware.CheckAll(provider.serviceAccountHandler.DeleteRole, []middleware.AuthZCheckGroup{
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbAttach}, Resource: coretypes.ResourceServiceAccount, SelectorCallback: serviceAccountInstanceSelectorCallback, Roles: []string{
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbDetach}, Resource: coretypes.ResourceServiceAccount, SelectorCallback: serviceAccountInstanceSelectorCallback, Roles: []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}}},
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbAttach}, Resource: coretypes.ResourceRole, SelectorCallback: provider.roleAttachSelectorFromPath, Roles: []string{
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbDetach}, Resource: coretypes.ResourceRole, SelectorCallback: provider.roleDetachSelectorFromPath, Roles: []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}}},
|
||||
}), handler.OpenAPIDef{
|
||||
@@ -153,7 +153,7 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbAttach), coretypes.ResourceRole.Scope(coretypes.VerbAttach)}),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbDetach), coretypes.ResourceRole.Scope(coretypes.VerbDetach)}),
|
||||
})).Methods(http.MethodDelete).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -213,8 +213,13 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.CreateFactorAPIKey, authtypes.Relation{Verb: coretypes.VerbUpdate}, coretypes.ResourceServiceAccount, serviceAccountInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys", handler.New(provider.authzMiddleware.CheckAll(provider.serviceAccountHandler.CreateFactorAPIKey, []middleware.AuthZCheckGroup{
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbCreate}, Resource: coretypes.ResourceMetaResourceFactorAPIKey, SelectorCallback: factorAPIKeyCollectionSelectorCallback, Roles: []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}}},
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbAttach}, Resource: coretypes.ResourceServiceAccount, SelectorCallback: serviceAccountInstanceSelectorCallback, Roles: []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}}},
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "CreateServiceAccountKey",
|
||||
Tags: []string{"serviceaccount"},
|
||||
@@ -227,12 +232,12 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusCreated,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusConflict},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbUpdate)}),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourceFactorAPIKey.Scope(coretypes.VerbCreate), coretypes.ResourceServiceAccount.Scope(coretypes.VerbAttach)}),
|
||||
})).Methods(http.MethodPost).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.ListFactorAPIKey, authtypes.Relation{Verb: coretypes.VerbRead}, coretypes.ResourceServiceAccount, serviceAccountInstanceSelectorCallback, []string{
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.ListFactorAPIKey, authtypes.Relation{Verb: coretypes.VerbList}, coretypes.ResourceMetaResourceFactorAPIKey, factorAPIKeyCollectionSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "ListServiceAccountKeys",
|
||||
@@ -246,12 +251,12 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusOK,
|
||||
ErrorStatusCodes: []int{},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbRead)}),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourceFactorAPIKey.Scope(coretypes.VerbList)}),
|
||||
})).Methods(http.MethodGet).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys/{fid}", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.UpdateFactorAPIKey, authtypes.Relation{Verb: coretypes.VerbUpdate}, coretypes.ResourceServiceAccount, serviceAccountInstanceSelectorCallback, []string{
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys/{fid}", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.UpdateFactorAPIKey, authtypes.Relation{Verb: coretypes.VerbUpdate}, coretypes.ResourceMetaResourceFactorAPIKey, factorAPIKeyInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "UpdateServiceAccountKey",
|
||||
@@ -265,13 +270,18 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusBadRequest, http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbUpdate)}),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourceFactorAPIKey.Scope(coretypes.VerbUpdate)}),
|
||||
})).Methods(http.MethodPut).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys/{fid}", handler.New(provider.authzMiddleware.Check(provider.serviceAccountHandler.RevokeFactorAPIKey, authtypes.Relation{Verb: coretypes.VerbUpdate}, coretypes.ResourceServiceAccount, serviceAccountInstanceSelectorCallback, []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
if err := router.Handle("/api/v1/service_accounts/{id}/keys/{fid}", handler.New(provider.authzMiddleware.CheckAll(provider.serviceAccountHandler.RevokeFactorAPIKey, []middleware.AuthZCheckGroup{
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbDelete}, Resource: coretypes.ResourceMetaResourceFactorAPIKey, SelectorCallback: factorAPIKeyInstanceSelectorCallback, Roles: []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}}},
|
||||
{{Relation: authtypes.Relation{Verb: coretypes.VerbDetach}, Resource: coretypes.ResourceServiceAccount, SelectorCallback: serviceAccountInstanceSelectorCallback, Roles: []string{
|
||||
authtypes.SigNozAdminRoleName,
|
||||
}}},
|
||||
}), handler.OpenAPIDef{
|
||||
ID: "RevokeServiceAccountKey",
|
||||
Tags: []string{"serviceaccount"},
|
||||
@@ -284,7 +294,7 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
SuccessStatusCode: http.StatusNoContent,
|
||||
ErrorStatusCodes: []int{http.StatusNotFound},
|
||||
Deprecated: false,
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceServiceAccount.Scope(coretypes.VerbUpdate)}),
|
||||
SecuritySchemes: newScopedSecuritySchemes([]string{coretypes.ResourceMetaResourceFactorAPIKey.Scope(coretypes.VerbDelete), coretypes.ResourceServiceAccount.Scope(coretypes.VerbDetach)}),
|
||||
})).Methods(http.MethodDelete).GetError(); err != nil {
|
||||
return err
|
||||
}
|
||||
@@ -292,7 +302,7 @@ func (provider *provider) addServiceAccountRoutes(router *mux.Router) error {
|
||||
return nil
|
||||
}
|
||||
|
||||
func (provider *provider) roleAttachSelectorFromPath(req *http.Request, claims authtypes.Claims) ([]coretypes.Selector, error) {
|
||||
func (provider *provider) roleDetachSelectorFromPath(req *http.Request, claims authtypes.Claims) ([]coretypes.Selector, error) {
|
||||
roleID, err := valuer.NewUUID(mux.Vars(req)["rid"])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
@@ -333,9 +343,28 @@ func (provider *provider) roleAttachSelectorFromBody(req *http.Request, claims a
|
||||
}, nil
|
||||
}
|
||||
|
||||
func factorAPIKeyCollectionSelectorCallback(_ *http.Request, _ authtypes.Claims) ([]coretypes.Selector, error) {
|
||||
return []coretypes.Selector{
|
||||
coretypes.TypeMetaResource.MustSelector(coretypes.WildCardSelectorString),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func factorAPIKeyInstanceSelectorCallback(req *http.Request, _ authtypes.Claims) ([]coretypes.Selector, error) {
|
||||
fid := mux.Vars(req)["fid"]
|
||||
fidSelector, err := coretypes.TypeMetaResource.Selector(fid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return []coretypes.Selector{
|
||||
fidSelector,
|
||||
coretypes.TypeMetaResource.MustSelector(coretypes.WildCardSelectorString),
|
||||
}, nil
|
||||
}
|
||||
|
||||
func serviceAccountCollectionSelectorCallback(_ *http.Request, _ authtypes.Claims) ([]coretypes.Selector, error) {
|
||||
return []coretypes.Selector{
|
||||
coretypes.TypeMetaResources.MustSelector(coretypes.WildCardSelectorString),
|
||||
coretypes.TypeServiceAccount.MustSelector(coretypes.WildCardSelectorString),
|
||||
}, nil
|
||||
}
|
||||
|
||||
|
||||
148
pkg/modules/inframonitoring/implinframonitoring/deployments.go
Normal file
148
pkg/modules/inframonitoring/implinframonitoring/deployments.go
Normal file
@@ -0,0 +1,148 @@
|
||||
package implinframonitoring
|
||||
|
||||
import (
|
||||
"context"
|
||||
"slices"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/types/inframonitoringtypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
// buildDeploymentRecords assembles the page records. Pod phase counts come from
|
||||
// phaseCounts in both modes; every row is a group of pods (one deployment in
|
||||
// list mode, an arbitrary roll-up in grouped_list mode), so there's no
|
||||
// per-row "current phase" concept.
|
||||
func buildDeploymentRecords(
|
||||
resp *qbtypes.QueryRangeResponse,
|
||||
pageGroups []map[string]string,
|
||||
groupBy []qbtypes.GroupByKey,
|
||||
metadataMap map[string]map[string]string,
|
||||
phaseCounts map[string]podPhaseCounts,
|
||||
) []inframonitoringtypes.DeploymentRecord {
|
||||
metricsMap := parseFullQueryResponse(resp, groupBy)
|
||||
|
||||
records := make([]inframonitoringtypes.DeploymentRecord, 0, len(pageGroups))
|
||||
for _, labels := range pageGroups {
|
||||
compositeKey := compositeKeyFromLabels(labels, groupBy)
|
||||
deploymentName := labels[deploymentNameAttrKey]
|
||||
|
||||
record := inframonitoringtypes.DeploymentRecord{ // initialize with default values
|
||||
DeploymentName: deploymentName,
|
||||
DeploymentCPU: -1,
|
||||
DeploymentCPURequest: -1,
|
||||
DeploymentCPULimit: -1,
|
||||
DeploymentMemory: -1,
|
||||
DeploymentMemoryRequest: -1,
|
||||
DeploymentMemoryLimit: -1,
|
||||
DesiredPods: -1,
|
||||
AvailablePods: -1,
|
||||
Meta: map[string]string{},
|
||||
}
|
||||
|
||||
if metrics, ok := metricsMap[compositeKey]; ok {
|
||||
if v, exists := metrics["A"]; exists {
|
||||
record.DeploymentCPU = v
|
||||
}
|
||||
if v, exists := metrics["B"]; exists {
|
||||
record.DeploymentCPURequest = v
|
||||
}
|
||||
if v, exists := metrics["C"]; exists {
|
||||
record.DeploymentCPULimit = v
|
||||
}
|
||||
if v, exists := metrics["D"]; exists {
|
||||
record.DeploymentMemory = v
|
||||
}
|
||||
if v, exists := metrics["E"]; exists {
|
||||
record.DeploymentMemoryRequest = v
|
||||
}
|
||||
if v, exists := metrics["F"]; exists {
|
||||
record.DeploymentMemoryLimit = v
|
||||
}
|
||||
if v, exists := metrics["H"]; exists {
|
||||
record.DesiredPods = int(v)
|
||||
}
|
||||
if v, exists := metrics["I"]; exists {
|
||||
record.AvailablePods = int(v)
|
||||
}
|
||||
}
|
||||
|
||||
if phaseCountsForGroup, ok := phaseCounts[compositeKey]; ok {
|
||||
record.PodCountsByPhase = inframonitoringtypes.PodCountsByPhase{
|
||||
Pending: phaseCountsForGroup.Pending,
|
||||
Running: phaseCountsForGroup.Running,
|
||||
Succeeded: phaseCountsForGroup.Succeeded,
|
||||
Failed: phaseCountsForGroup.Failed,
|
||||
Unknown: phaseCountsForGroup.Unknown,
|
||||
}
|
||||
}
|
||||
|
||||
if attrs, ok := metadataMap[compositeKey]; ok {
|
||||
for k, v := range attrs {
|
||||
record.Meta[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
records = append(records, record)
|
||||
}
|
||||
return records
|
||||
}
|
||||
|
||||
func (m *module) getTopDeploymentGroups(
|
||||
ctx context.Context,
|
||||
orgID valuer.UUID,
|
||||
req *inframonitoringtypes.PostableDeployments,
|
||||
metadataMap map[string]map[string]string,
|
||||
) ([]map[string]string, error) {
|
||||
orderByKey := req.OrderBy.Key.Name
|
||||
queryNamesForOrderBy := orderByToDeploymentsQueryNames[orderByKey]
|
||||
rankingQueryName := queryNamesForOrderBy[len(queryNamesForOrderBy)-1]
|
||||
|
||||
topReq := &qbtypes.QueryRangeRequest{
|
||||
Start: uint64(req.Start),
|
||||
End: uint64(req.End),
|
||||
RequestType: qbtypes.RequestTypeScalar,
|
||||
CompositeQuery: qbtypes.CompositeQuery{
|
||||
Queries: make([]qbtypes.QueryEnvelope, 0, len(queryNamesForOrderBy)),
|
||||
},
|
||||
}
|
||||
|
||||
for _, envelope := range m.newDeploymentsTableListQuery().CompositeQuery.Queries {
|
||||
if !slices.Contains(queryNamesForOrderBy, envelope.GetQueryName()) {
|
||||
continue
|
||||
}
|
||||
copied := envelope
|
||||
if copied.Type == qbtypes.QueryTypeBuilder {
|
||||
existingExpr := ""
|
||||
if f := copied.GetFilter(); f != nil {
|
||||
existingExpr = f.Expression
|
||||
}
|
||||
reqFilterExpr := ""
|
||||
if req.Filter != nil {
|
||||
reqFilterExpr = req.Filter.Expression
|
||||
}
|
||||
merged := mergeFilterExpressions(existingExpr, reqFilterExpr)
|
||||
copied.SetFilter(&qbtypes.Filter{Expression: merged})
|
||||
copied.SetGroupBy(req.GroupBy)
|
||||
}
|
||||
topReq.CompositeQuery.Queries = append(topReq.CompositeQuery.Queries, copied)
|
||||
}
|
||||
|
||||
resp, err := m.querier.QueryRange(ctx, orgID, topReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
allMetricGroups := parseAndSortGroups(resp, rankingQueryName, req.GroupBy, req.OrderBy.Direction)
|
||||
return paginateWithBackfill(allMetricGroups, metadataMap, req.GroupBy, req.Offset, req.Limit), nil
|
||||
}
|
||||
|
||||
func (m *module) getDeploymentsTableMetadata(ctx context.Context, req *inframonitoringtypes.PostableDeployments) (map[string]map[string]string, error) {
|
||||
var nonGroupByAttrs []string
|
||||
for _, key := range deploymentAttrKeysForMetadata {
|
||||
if !isKeyInGroupByAttrs(req.GroupBy, key) {
|
||||
nonGroupByAttrs = append(nonGroupByAttrs, key)
|
||||
}
|
||||
}
|
||||
return m.getMetadata(ctx, deploymentsTableMetricNamesList, req.GroupBy, nonGroupByAttrs, req.Filter, req.Start, req.End)
|
||||
}
|
||||
@@ -0,0 +1,254 @@
|
||||
package implinframonitoring
|
||||
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/types/inframonitoringtypes"
|
||||
"github.com/SigNoz/signoz/pkg/types/metrictypes"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
)
|
||||
|
||||
const (
|
||||
deploymentNameAttrKey = "k8s.deployment.name"
|
||||
deploymentsBaseFilterExpr = "k8s.deployment.name != ''"
|
||||
)
|
||||
|
||||
var deploymentNameGroupByKey = qbtypes.GroupByKey{
|
||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
||||
Name: deploymentNameAttrKey,
|
||||
FieldContext: telemetrytypes.FieldContextResource,
|
||||
FieldDataType: telemetrytypes.FieldDataTypeString,
|
||||
},
|
||||
}
|
||||
|
||||
// deploymentsTableMetricNamesList drives the existence/retention check.
|
||||
// Includes k8s.pod.phase even though phase isn't part of the QB composite query —
|
||||
// it is queried separately via getPerGroupPodPhaseCounts, and we want the
|
||||
// response to short-circuit cleanly when the phase metric is absent.
|
||||
var deploymentsTableMetricNamesList = []string{
|
||||
"k8s.pod.phase",
|
||||
"k8s.pod.cpu.usage",
|
||||
"k8s.pod.cpu_request_utilization",
|
||||
"k8s.pod.cpu_limit_utilization",
|
||||
"k8s.pod.memory.working_set",
|
||||
"k8s.pod.memory_request_utilization",
|
||||
"k8s.pod.memory_limit_utilization",
|
||||
"k8s.deployment.desired",
|
||||
"k8s.deployment.available",
|
||||
}
|
||||
|
||||
// Carried forward from v1 deploymentAttrsToEnrich
|
||||
// (pkg/query-service/app/inframetrics/deployments.go:29-33).
|
||||
var deploymentAttrKeysForMetadata = []string{
|
||||
"k8s.deployment.name",
|
||||
"k8s.namespace.name",
|
||||
"k8s.cluster.name",
|
||||
}
|
||||
|
||||
// orderByToDeploymentsQueryNames maps the orderBy column to the query name
|
||||
// used for ranking deployment groups. v2 B/C/E/F are direct metrics, no
|
||||
// formula deps — so unlike v1 we don't carry A/D.
|
||||
var orderByToDeploymentsQueryNames = map[string][]string{
|
||||
inframonitoringtypes.DeploymentsOrderByCPU: {"A"},
|
||||
inframonitoringtypes.DeploymentsOrderByCPURequest: {"B"},
|
||||
inframonitoringtypes.DeploymentsOrderByCPULimit: {"C"},
|
||||
inframonitoringtypes.DeploymentsOrderByMemory: {"D"},
|
||||
inframonitoringtypes.DeploymentsOrderByMemoryRequest: {"E"},
|
||||
inframonitoringtypes.DeploymentsOrderByMemoryLimit: {"F"},
|
||||
inframonitoringtypes.DeploymentsOrderByDesiredPods: {"H"},
|
||||
inframonitoringtypes.DeploymentsOrderByAvailablePods: {"I"},
|
||||
}
|
||||
|
||||
// newDeploymentsTableListQuery builds the composite QB v5 request for the deployments list.
|
||||
// Eight builder queries: A..F roll up pod-level metrics by deployment, H/I take the
|
||||
// latest deployment-level desired/available counts. Restarts (v1 query G) is intentionally
|
||||
// omitted to match the v2 pods pattern.
|
||||
//
|
||||
// Every builder query carries a base filter `k8s.deployment.name != ”`.
|
||||
// Reason: pod-level metrics (A..F) are emitted for every pod regardless of whether the
|
||||
// pod belongs to a Deployment; only Deployment-owned pods carry the
|
||||
// `k8s.deployment.name` resource attribute. Without this filter, standalone pods and
|
||||
// pods owned by other workloads (StatefulSet/DaemonSet/Job/...) collapse into a single
|
||||
// empty-string group under the default groupBy. v1's GetDeploymentList applied the same
|
||||
// filter via FilterOperatorExists; this matches v1 parity. The base filter merges
|
||||
// cleanly with user filters via mergeFilterExpressions / buildFullQueryRequest.
|
||||
func (m *module) newDeploymentsTableListQuery() *qbtypes.QueryRangeRequest {
|
||||
queries := []qbtypes.QueryEnvelope{
|
||||
// Query A: k8s.pod.cpu.usage — sum of pod CPU within the group.
|
||||
{
|
||||
Type: qbtypes.QueryTypeBuilder,
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
|
||||
Name: "A",
|
||||
Signal: telemetrytypes.SignalMetrics,
|
||||
Aggregations: []qbtypes.MetricAggregation{
|
||||
{
|
||||
MetricName: "k8s.pod.cpu.usage",
|
||||
TimeAggregation: metrictypes.TimeAggregationAvg,
|
||||
SpaceAggregation: metrictypes.SpaceAggregationSum,
|
||||
ReduceTo: qbtypes.ReduceToAvg,
|
||||
},
|
||||
},
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: deploymentsBaseFilterExpr,
|
||||
},
|
||||
GroupBy: []qbtypes.GroupByKey{deploymentNameGroupByKey},
|
||||
Disabled: false,
|
||||
},
|
||||
},
|
||||
// Query B: k8s.pod.cpu_request_utilization — avg across pods in the group.
|
||||
{
|
||||
Type: qbtypes.QueryTypeBuilder,
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
|
||||
Name: "B",
|
||||
Signal: telemetrytypes.SignalMetrics,
|
||||
Aggregations: []qbtypes.MetricAggregation{
|
||||
{
|
||||
MetricName: "k8s.pod.cpu_request_utilization",
|
||||
TimeAggregation: metrictypes.TimeAggregationAvg,
|
||||
SpaceAggregation: metrictypes.SpaceAggregationAvg,
|
||||
ReduceTo: qbtypes.ReduceToAvg,
|
||||
},
|
||||
},
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: deploymentsBaseFilterExpr,
|
||||
},
|
||||
GroupBy: []qbtypes.GroupByKey{deploymentNameGroupByKey},
|
||||
Disabled: false,
|
||||
},
|
||||
},
|
||||
// Query C: k8s.pod.cpu_limit_utilization — avg across pods in the group.
|
||||
{
|
||||
Type: qbtypes.QueryTypeBuilder,
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
|
||||
Name: "C",
|
||||
Signal: telemetrytypes.SignalMetrics,
|
||||
Aggregations: []qbtypes.MetricAggregation{
|
||||
{
|
||||
MetricName: "k8s.pod.cpu_limit_utilization",
|
||||
TimeAggregation: metrictypes.TimeAggregationAvg,
|
||||
SpaceAggregation: metrictypes.SpaceAggregationAvg,
|
||||
ReduceTo: qbtypes.ReduceToAvg,
|
||||
},
|
||||
},
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: deploymentsBaseFilterExpr,
|
||||
},
|
||||
GroupBy: []qbtypes.GroupByKey{deploymentNameGroupByKey},
|
||||
Disabled: false,
|
||||
},
|
||||
},
|
||||
// Query D: k8s.pod.memory.working_set — sum of pod memory within the group.
|
||||
{
|
||||
Type: qbtypes.QueryTypeBuilder,
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
|
||||
Name: "D",
|
||||
Signal: telemetrytypes.SignalMetrics,
|
||||
Aggregations: []qbtypes.MetricAggregation{
|
||||
{
|
||||
MetricName: "k8s.pod.memory.working_set",
|
||||
TimeAggregation: metrictypes.TimeAggregationAvg,
|
||||
SpaceAggregation: metrictypes.SpaceAggregationSum,
|
||||
ReduceTo: qbtypes.ReduceToAvg,
|
||||
},
|
||||
},
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: deploymentsBaseFilterExpr,
|
||||
},
|
||||
GroupBy: []qbtypes.GroupByKey{deploymentNameGroupByKey},
|
||||
Disabled: false,
|
||||
},
|
||||
},
|
||||
// Query E: k8s.pod.memory_request_utilization — avg across pods in the group.
|
||||
{
|
||||
Type: qbtypes.QueryTypeBuilder,
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
|
||||
Name: "E",
|
||||
Signal: telemetrytypes.SignalMetrics,
|
||||
Aggregations: []qbtypes.MetricAggregation{
|
||||
{
|
||||
MetricName: "k8s.pod.memory_request_utilization",
|
||||
TimeAggregation: metrictypes.TimeAggregationAvg,
|
||||
SpaceAggregation: metrictypes.SpaceAggregationAvg,
|
||||
ReduceTo: qbtypes.ReduceToAvg,
|
||||
},
|
||||
},
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: deploymentsBaseFilterExpr,
|
||||
},
|
||||
GroupBy: []qbtypes.GroupByKey{deploymentNameGroupByKey},
|
||||
Disabled: false,
|
||||
},
|
||||
},
|
||||
// Query F: k8s.pod.memory_limit_utilization — avg across pods in the group.
|
||||
{
|
||||
Type: qbtypes.QueryTypeBuilder,
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
|
||||
Name: "F",
|
||||
Signal: telemetrytypes.SignalMetrics,
|
||||
Aggregations: []qbtypes.MetricAggregation{
|
||||
{
|
||||
MetricName: "k8s.pod.memory_limit_utilization",
|
||||
TimeAggregation: metrictypes.TimeAggregationAvg,
|
||||
SpaceAggregation: metrictypes.SpaceAggregationAvg,
|
||||
ReduceTo: qbtypes.ReduceToAvg,
|
||||
},
|
||||
},
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: deploymentsBaseFilterExpr,
|
||||
},
|
||||
GroupBy: []qbtypes.GroupByKey{deploymentNameGroupByKey},
|
||||
Disabled: false,
|
||||
},
|
||||
},
|
||||
// Query H: k8s.deployment.desired — latest known desired replica count per group.
|
||||
// v1 used TimeAggregationAnyLast (v3) → mapped to TimeAggregationLatest in v5;
|
||||
// SpaceAggregationSum + ReduceToLast preserve v1's "latest, summed across the group".
|
||||
{
|
||||
Type: qbtypes.QueryTypeBuilder,
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
|
||||
Name: "H",
|
||||
Signal: telemetrytypes.SignalMetrics,
|
||||
Aggregations: []qbtypes.MetricAggregation{
|
||||
{
|
||||
MetricName: "k8s.deployment.desired",
|
||||
TimeAggregation: metrictypes.TimeAggregationLatest,
|
||||
SpaceAggregation: metrictypes.SpaceAggregationSum,
|
||||
ReduceTo: qbtypes.ReduceToLast,
|
||||
},
|
||||
},
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: deploymentsBaseFilterExpr,
|
||||
},
|
||||
GroupBy: []qbtypes.GroupByKey{deploymentNameGroupByKey},
|
||||
Disabled: false,
|
||||
},
|
||||
},
|
||||
// Query I: k8s.deployment.available — latest known available replica count per group.
|
||||
{
|
||||
Type: qbtypes.QueryTypeBuilder,
|
||||
Spec: qbtypes.QueryBuilderQuery[qbtypes.MetricAggregation]{
|
||||
Name: "I",
|
||||
Signal: telemetrytypes.SignalMetrics,
|
||||
Aggregations: []qbtypes.MetricAggregation{
|
||||
{
|
||||
MetricName: "k8s.deployment.available",
|
||||
TimeAggregation: metrictypes.TimeAggregationLatest,
|
||||
SpaceAggregation: metrictypes.SpaceAggregationSum,
|
||||
ReduceTo: qbtypes.ReduceToLast,
|
||||
},
|
||||
},
|
||||
Filter: &qbtypes.Filter{
|
||||
Expression: deploymentsBaseFilterExpr,
|
||||
},
|
||||
GroupBy: []qbtypes.GroupByKey{deploymentNameGroupByKey},
|
||||
Disabled: false,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
return &qbtypes.QueryRangeRequest{
|
||||
RequestType: qbtypes.RequestTypeScalar,
|
||||
CompositeQuery: qbtypes.CompositeQuery{
|
||||
Queries: queries,
|
||||
},
|
||||
}
|
||||
}
|
||||
@@ -165,3 +165,27 @@ func (h *handler) ListVolumes(rw http.ResponseWriter, req *http.Request) {
|
||||
|
||||
render.Success(rw, http.StatusOK, result)
|
||||
}
|
||||
|
||||
func (h *handler) ListDeployments(rw http.ResponseWriter, req *http.Request) {
|
||||
claims, err := authtypes.ClaimsFromContext(req.Context())
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
orgID := valuer.MustNewUUID(claims.OrgID)
|
||||
|
||||
var parsedReq inframonitoringtypes.PostableDeployments
|
||||
if err := binding.JSON.BindBody(req.Body, &parsedReq); err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
result, err := h.module.ListDeployments(req.Context(), orgID, &parsedReq)
|
||||
if err != nil {
|
||||
render.Error(rw, err)
|
||||
return
|
||||
}
|
||||
|
||||
render.Success(rw, http.StatusOK, result)
|
||||
}
|
||||
|
||||
@@ -611,3 +611,98 @@ func (m *module) ListVolumes(ctx context.Context, orgID valuer.UUID, req *infram
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
func (m *module) ListDeployments(ctx context.Context, orgID valuer.UUID, req *inframonitoringtypes.PostableDeployments) (*inframonitoringtypes.Deployments, error) {
|
||||
if err := req.Validate(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp := &inframonitoringtypes.Deployments{}
|
||||
|
||||
if req.OrderBy == nil {
|
||||
req.OrderBy = &qbtypes.OrderBy{
|
||||
Key: qbtypes.OrderByKey{
|
||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
||||
Name: inframonitoringtypes.DeploymentsOrderByCPU,
|
||||
},
|
||||
},
|
||||
Direction: qbtypes.OrderDirectionDesc,
|
||||
}
|
||||
}
|
||||
|
||||
if len(req.GroupBy) == 0 {
|
||||
req.GroupBy = []qbtypes.GroupByKey{deploymentNameGroupByKey}
|
||||
resp.Type = inframonitoringtypes.ResponseTypeList
|
||||
} else {
|
||||
resp.Type = inframonitoringtypes.ResponseTypeGroupedList
|
||||
}
|
||||
|
||||
// Bake the deployments base filter into req.Filter so all downstream helpers pick it up.
|
||||
if req.Filter == nil {
|
||||
req.Filter = &qbtypes.Filter{}
|
||||
}
|
||||
req.Filter.Expression = mergeFilterExpressions(deploymentsBaseFilterExpr, req.Filter.Expression)
|
||||
|
||||
missingMetrics, minFirstReportedUnixMilli, err := m.getMetricsExistenceAndEarliestTime(ctx, deploymentsTableMetricNamesList)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(missingMetrics) > 0 {
|
||||
resp.RequiredMetricsCheck = inframonitoringtypes.RequiredMetricsCheck{MissingMetrics: missingMetrics}
|
||||
resp.Records = []inframonitoringtypes.DeploymentRecord{}
|
||||
resp.Total = 0
|
||||
return resp, nil
|
||||
}
|
||||
if req.End < int64(minFirstReportedUnixMilli) {
|
||||
resp.EndTimeBeforeRetention = true
|
||||
resp.Records = []inframonitoringtypes.DeploymentRecord{}
|
||||
resp.Total = 0
|
||||
return resp, nil
|
||||
}
|
||||
resp.RequiredMetricsCheck = inframonitoringtypes.RequiredMetricsCheck{MissingMetrics: []string{}}
|
||||
|
||||
metadataMap, err := m.getDeploymentsTableMetadata(ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.Total = len(metadataMap)
|
||||
|
||||
pageGroups, err := m.getTopDeploymentGroups(ctx, orgID, req, metadataMap)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if len(pageGroups) == 0 {
|
||||
resp.Records = []inframonitoringtypes.DeploymentRecord{}
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
filterExpr := ""
|
||||
if req.Filter != nil {
|
||||
filterExpr = req.Filter.Expression
|
||||
}
|
||||
|
||||
fullQueryReq := buildFullQueryRequest(req.Start, req.End, filterExpr, req.GroupBy, pageGroups, m.newDeploymentsTableListQuery())
|
||||
queryResp, err := m.querier.QueryRange(ctx, orgID, fullQueryReq)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Reuse the pods phase-counts CTE function via a temp struct — it reads only
|
||||
// Start/End/Filter/GroupBy from PostablePods.
|
||||
phaseCounts, err := m.getPerGroupPodPhaseCounts(ctx, &inframonitoringtypes.PostablePods{
|
||||
Start: req.Start,
|
||||
End: req.End,
|
||||
Filter: req.Filter,
|
||||
GroupBy: req.GroupBy,
|
||||
}, pageGroups)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp.Records = buildDeploymentRecords(queryResp, pageGroups, req.GroupBy, metadataMap, phaseCounts)
|
||||
resp.Warning = queryResp.Warning
|
||||
|
||||
return resp, nil
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ type Handler interface {
|
||||
ListNamespaces(http.ResponseWriter, *http.Request)
|
||||
ListClusters(http.ResponseWriter, *http.Request)
|
||||
ListVolumes(http.ResponseWriter, *http.Request)
|
||||
ListDeployments(http.ResponseWriter, *http.Request)
|
||||
}
|
||||
|
||||
type Module interface {
|
||||
@@ -24,4 +25,5 @@ type Module interface {
|
||||
ListNamespaces(ctx context.Context, orgID valuer.UUID, req *inframonitoringtypes.PostableNamespaces) (*inframonitoringtypes.Namespaces, error)
|
||||
ListClusters(ctx context.Context, orgID valuer.UUID, req *inframonitoringtypes.PostableClusters) (*inframonitoringtypes.Clusters, error)
|
||||
ListVolumes(ctx context.Context, orgID valuer.UUID, req *inframonitoringtypes.PostableVolumes) (*inframonitoringtypes.Volumes, error)
|
||||
ListDeployments(ctx context.Context, orgID valuer.UUID, req *inframonitoringtypes.PostableDeployments) (*inframonitoringtypes.Deployments, error)
|
||||
}
|
||||
|
||||
@@ -200,6 +200,7 @@ func NewSQLMigrationProviderFactories(
|
||||
sqlmigration.NewAddServiceAccountManagedRoleTransactionsFactory(sqlstore),
|
||||
sqlmigration.NewAddSpanMapperFactory(sqlstore, sqlschema),
|
||||
sqlmigration.NewAddLLMPricingRulesFactory(sqlstore, sqlschema),
|
||||
sqlmigration.NewMigrateMetaresourcesTuplesFactory(sqlstore),
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
277
pkg/sqlmigration/081_migrate_metaresources_tuples.go
Normal file
277
pkg/sqlmigration/081_migrate_metaresources_tuples.go
Normal file
@@ -0,0 +1,277 @@
|
||||
package sqlmigration
|
||||
|
||||
import (
|
||||
"context"
|
||||
"time"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/factory"
|
||||
"github.com/SigNoz/signoz/pkg/sqlstore"
|
||||
"github.com/SigNoz/signoz/pkg/types/authtypes"
|
||||
"github.com/oklog/ulid/v2"
|
||||
"github.com/uptrace/bun"
|
||||
"github.com/uptrace/bun/dialect"
|
||||
"github.com/uptrace/bun/migrate"
|
||||
)
|
||||
|
||||
type migrateMetaresourcesTuples struct {
|
||||
sqlstore sqlstore.SQLStore
|
||||
}
|
||||
|
||||
func NewMigrateMetaresourcesTuplesFactory(sqlstore sqlstore.SQLStore) factory.ProviderFactory[SQLMigration, Config] {
|
||||
return factory.NewProviderFactory(factory.MustNewName("migrate_metaresources_tuples"), func(ctx context.Context, ps factory.ProviderSettings, c Config) (SQLMigration, error) {
|
||||
return &migrateMetaresourcesTuples{sqlstore: sqlstore}, nil
|
||||
})
|
||||
}
|
||||
|
||||
func (migration *migrateMetaresourcesTuples) Register(migrations *migrate.Migrations) error {
|
||||
return migrations.Register(migration.Up, migration.Down)
|
||||
}
|
||||
|
||||
// migrationTuple describes a single FGA tuple to insert.
|
||||
type migrationTuple struct {
|
||||
roleName string // "signoz-admin", "signoz-editor", "signoz-viewer"
|
||||
objectType string // "serviceaccount", "user", "role", "metaresource"
|
||||
objectName string // "serviceaccount", "user", "role", etc.
|
||||
relation string // "create", "list", "detach", etc.
|
||||
}
|
||||
|
||||
func (migration *migrateMetaresourcesTuples) Up(ctx context.Context, db *bun.DB) error {
|
||||
tx, err := db.BeginTx(ctx, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() { _ = tx.Rollback() }()
|
||||
|
||||
var storeID string
|
||||
err = tx.QueryRowContext(ctx, `SELECT id FROM store WHERE name = ? LIMIT 1`, "signoz").Scan(&storeID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Fetch all orgs.
|
||||
var orgIDs []string
|
||||
rows, err := tx.QueryContext(ctx, `SELECT id FROM organizations`)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer rows.Close()
|
||||
for rows.Next() {
|
||||
var orgID string
|
||||
if err := rows.Scan(&orgID); err != nil {
|
||||
return err
|
||||
}
|
||||
orgIDs = append(orgIDs, orgID)
|
||||
}
|
||||
|
||||
isPG := migration.sqlstore.BunDB().Dialect().Name() == dialect.PG
|
||||
|
||||
// Step 1: Delete all tuples with the old "metaresources" object_type.
|
||||
for _, orgID := range orgIDs {
|
||||
if isPG {
|
||||
_, err = tx.ExecContext(ctx, `DELETE FROM tuple WHERE store = ? AND object_type = ? AND object_id LIKE ?`,
|
||||
storeID, "metaresources", "organization/"+orgID+"/%")
|
||||
} else {
|
||||
_, err = tx.ExecContext(ctx, `DELETE FROM tuple WHERE store = ? AND object_type = ? AND object_id LIKE ?`,
|
||||
storeID, "metaresources", "organization/"+orgID+"/%")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Also clean up changelog entries for the old type.
|
||||
if isPG {
|
||||
_, err = tx.ExecContext(ctx, `DELETE FROM changelog WHERE store = ? AND object_type = ?`,
|
||||
storeID, "metaresources")
|
||||
} else {
|
||||
_, err = tx.ExecContext(ctx, `DELETE FROM changelog WHERE store = ? AND object_type = ?`,
|
||||
storeID, "metaresources")
|
||||
}
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
// Step 2: Insert replacement tuples.
|
||||
// For types with their own FGA type (user, serviceaccount, role), create/list
|
||||
// go on the type directly. For all other resources, create/list go on "metaresource".
|
||||
// Also add new detach tuples for role/user/serviceaccount.
|
||||
tuples := []migrationTuple{
|
||||
// New detach tuples for admin
|
||||
{authtypes.SigNozAdminRoleName, "role", "role", "detach"},
|
||||
{authtypes.SigNozAdminRoleName, "user", "user", "detach"},
|
||||
{authtypes.SigNozAdminRoleName, "serviceaccount", "serviceaccount", "detach"},
|
||||
// Replacement create/list for user/serviceaccount/role (moved from metaresources to own types)
|
||||
{authtypes.SigNozAdminRoleName, "serviceaccount", "serviceaccount", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "serviceaccount", "serviceaccount", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "user", "user", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "user", "user", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "role", "role", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "role", "role", "list"},
|
||||
// Replacement create/list for resources that move from "metaresources" to "metaresource"
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "auth-domain", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "auth-domain", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "cloud-integration", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "cloud-integration", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "cloud-integration-service", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "cloud-integration-service", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "integration", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "integration", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "factor-api-key", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "factor-api-key", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "factor-password", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "factor-password", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "license", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "license", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "subscription", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "subscription", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "organization", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "organization", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "org-preference", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "org-preference", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "public-dashboard", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "public-dashboard", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "session", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "dashboard", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "dashboard", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "pipeline", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "pipeline", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "planned-maintenance", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "planned-maintenance", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "rule", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "rule", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "saved-view", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "saved-view", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "trace-funnel", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "trace-funnel", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "ingestion-key", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "ingestion-key", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "ingestion-limit", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "ingestion-limit", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "notification-channel", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "notification-channel", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "route-policy", "create"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "route-policy", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "apdex-setting", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "quick-filter", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "ttl-setting", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "user-preference", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "logs-field", "list"},
|
||||
{authtypes.SigNozAdminRoleName, "metaresource", "traces-field", "list"},
|
||||
// Editor role: create/list replacements from metaresources → metaresource
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "dashboard", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "dashboard", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "pipeline", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "pipeline", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "planned-maintenance", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "planned-maintenance", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "rule", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "rule", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "saved-view", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "saved-view", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "trace-funnel", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "trace-funnel", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "integration", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "integration", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "ingestion-key", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "ingestion-key", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "ingestion-limit", "create"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "ingestion-limit", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "notification-channel", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "route-policy", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "apdex-setting", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "quick-filter", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "ttl-setting", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "user-preference", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "logs-field", "list"},
|
||||
{authtypes.SigNozEditorRoleName, "metaresource", "traces-field", "list"},
|
||||
// Viewer role: create/list replacements from metaresources → metaresource
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "dashboard", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "pipeline", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "planned-maintenance", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "rule", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "saved-view", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "trace-funnel", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "integration", "create"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "integration", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "notification-channel", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "route-policy", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "apdex-setting", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "quick-filter", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "ttl-setting", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "user-preference", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "logs-field", "list"},
|
||||
{authtypes.SigNozViewerRoleName, "metaresource", "traces-field", "list"},
|
||||
}
|
||||
|
||||
for _, orgID := range orgIDs {
|
||||
for _, tuple := range tuples {
|
||||
entropy := ulid.DefaultEntropy()
|
||||
now := time.Now().UTC()
|
||||
tupleID := ulid.MustNew(ulid.Timestamp(now), entropy).String()
|
||||
|
||||
objectID := "organization/" + orgID + "/" + tuple.objectName + "/*"
|
||||
roleSubject := "organization/" + orgID + "/role/" + tuple.roleName
|
||||
|
||||
if isPG {
|
||||
user := "role:" + roleSubject + "#assignee"
|
||||
result, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO tuple (store, object_type, object_id, relation, _user, user_type, ulid, inserted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (store, object_type, object_id, relation, _user) DO NOTHING`,
|
||||
storeID, tuple.objectType, objectID, tuple.relation, user, "userset", tupleID, now,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
continue
|
||||
}
|
||||
_, err = tx.ExecContext(ctx, `
|
||||
INSERT INTO changelog (store, object_type, object_id, relation, _user, operation, ulid, inserted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (store, ulid, object_type) DO NOTHING`,
|
||||
storeID, tuple.objectType, objectID, tuple.relation, user, "TUPLE_OPERATION_WRITE", tupleID, now,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
result, err := tx.ExecContext(ctx, `
|
||||
INSERT INTO tuple (store, object_type, object_id, relation, user_object_type, user_object_id, user_relation, user_type, ulid, inserted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (store, object_type, object_id, relation, user_object_type, user_object_id, user_relation) DO NOTHING`,
|
||||
storeID, tuple.objectType, objectID, tuple.relation, "role", roleSubject, "assignee", "userset", tupleID, now,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
rowsAffected, err := result.RowsAffected()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if rowsAffected == 0 {
|
||||
continue
|
||||
}
|
||||
_, err = tx.ExecContext(ctx, `
|
||||
INSERT INTO changelog (store, object_type, object_id, relation, user_object_type, user_object_id, user_relation, operation, ulid, inserted_at)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
ON CONFLICT (store, ulid, object_type) DO NOTHING`,
|
||||
storeID, tuple.objectType, objectID, tuple.relation, "role", roleSubject, "assignee", 0, tupleID, now,
|
||||
)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return tx.Commit()
|
||||
}
|
||||
|
||||
func (migration *migrateMetaresourcesTuples) Down(context.Context, *bun.DB) error {
|
||||
return nil
|
||||
}
|
||||
@@ -18,46 +18,49 @@ const (
|
||||
|
||||
var ManagedRoleToTransactions = map[string][]Transaction{
|
||||
SigNozAdminRoleName: {
|
||||
// role attach — admin can attach/detach role assignments
|
||||
// role attach/detach — admin can attach/detach role assignments
|
||||
{Verb: VerbAttach, Object: *MustNewObject(ResourceRef{Type: TypeRole, Kind: KindRole}, WildCardSelectorString)},
|
||||
// user attach — admin can attach roles to any user
|
||||
{Verb: VerbDetach, Object: *MustNewObject(ResourceRef{Type: TypeRole, Kind: KindRole}, WildCardSelectorString)},
|
||||
// user attach/detach — admin can attach/detach roles to any user
|
||||
{Verb: VerbAttach, Object: *MustNewObject(ResourceRef{Type: TypeUser, Kind: KindUser}, WildCardSelectorString)},
|
||||
// serviceaccount attach — admin can attach roles to any SA
|
||||
{Verb: VerbDetach, Object: *MustNewObject(ResourceRef{Type: TypeUser, Kind: KindUser}, WildCardSelectorString)},
|
||||
// serviceaccount attach/detach — admin can attach/detach roles to any SA
|
||||
{Verb: VerbAttach, Object: *MustNewObject(ResourceRef{Type: TypeServiceAccount, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
{Verb: VerbDetach, Object: *MustNewObject(ResourceRef{Type: TypeServiceAccount, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
// auth-domain — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindAuthDomain}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindAuthDomain}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindAuthDomain}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindAuthDomain}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindAuthDomain}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindAuthDomain}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindAuthDomain}, WildCardSelectorString)},
|
||||
// cloud-integration — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindCloudIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindCloudIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegration}, WildCardSelectorString)},
|
||||
// cloud-integration-service — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegrationService}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegrationService}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegrationService}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindCloudIntegrationService}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindCloudIntegrationService}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegrationService}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindCloudIntegrationService}, WildCardSelectorString)},
|
||||
// integration — viewer/editor/admin (install/uninstall via ViewAccess)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
// factor-api-key — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorAPIKey}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorAPIKey}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorAPIKey}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindFactorAPIKey}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindFactorAPIKey}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorAPIKey}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorAPIKey}, WildCardSelectorString)},
|
||||
// factor-password — admin can issue and inspect reset tokens; users change their own password via OpenAccess
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorPassword}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindFactorPassword}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindFactorPassword}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorPassword}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindFactorPassword}, WildCardSelectorString)},
|
||||
// license — admin only.
|
||||
// Uniform LCRUD shape; actual ee routes are POST /api/v3/licenses (create
|
||||
// = Activate), PUT /api/v3/licenses (update = Refresh), GET
|
||||
@@ -67,8 +70,8 @@ var ManagedRoleToTransactions = map[string][]Transaction{
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLicense}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLicense}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLicense}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindLicense}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindLicense}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLicense}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLicense}, WildCardSelectorString)},
|
||||
// subscription — admin only.
|
||||
// Uniform LCRUD shape; actual ee routes are POST /api/v1/checkout
|
||||
// (create), POST /api/v1/portal (update — opens Stripe portal), GET
|
||||
@@ -78,123 +81,123 @@ var ManagedRoleToTransactions = map[string][]Transaction{
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSubscription}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSubscription}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSubscription}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSubscription}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSubscription}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSubscription}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSubscription}, WildCardSelectorString)},
|
||||
// organization — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeOrganization, Kind: KindOrganization}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeOrganization, Kind: KindOrganization}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindOrganization}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindOrganization}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindOrganization}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindOrganization}, WildCardSelectorString)},
|
||||
// org-preference — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindOrgPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindOrgPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindOrgPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindOrgPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindOrgPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindOrgPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindOrgPreference}, WildCardSelectorString)},
|
||||
// public-dashboard — admin manages, anonymous reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPublicDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPublicDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPublicDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPublicDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPublicDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPublicDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPublicDashboard}, WildCardSelectorString)},
|
||||
// role — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeRole, Kind: KindRole}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeRole, Kind: KindRole}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeRole, Kind: KindRole}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRole}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRole}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeRole, Kind: KindRole}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeRole, Kind: KindRole}, WildCardSelectorString)},
|
||||
// serviceaccount — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeServiceAccount, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeServiceAccount, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeServiceAccount, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeServiceAccount, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeServiceAccount, Kind: KindServiceAccount}, WildCardSelectorString)},
|
||||
// session — admin can revoke and list
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSession}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSession}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSession}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSession}, WildCardSelectorString)},
|
||||
// user — admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeUser, Kind: KindUser}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeUser, Kind: KindUser}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeUser, Kind: KindUser}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindUser}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindUser}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeUser, Kind: KindUser}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeUser, Kind: KindUser}, WildCardSelectorString)},
|
||||
// dashboard — full CRUD (also held by editor)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
// pipeline — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
// planned-maintenance — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
// rule — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
// saved-view — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
// trace-funnel — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
// ingestion-key — editor+admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
// ingestion-limit — editor+admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
// notification-channel — admin writes, viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
// route-policy — admin writes, viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
// apdex-setting — admin updates, viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
// quick-filter — admin updates, viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
// ttl-setting — admin updates, viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
// user-preference — every authenticated user can read+update their own
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
// telemetry — read on each signal (logs/traces/metrics); schema permits read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeTelemetryResource, Kind: KindLogs}, WildCardSelectorString)},
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeTelemetryResource, Kind: KindTraces}, WildCardSelectorString)},
|
||||
@@ -207,86 +210,86 @@ var ManagedRoleToTransactions = map[string][]Transaction{
|
||||
// logs-field — editor+admin update (POST overwrites field config), viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
// traces-field — editor+admin update, viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
},
|
||||
SigNozEditorRoleName: {
|
||||
// dashboard — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
// pipeline — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
// planned-maintenance — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
// rule — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
// saved-view — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
// trace-funnel — full CRUD
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
// integration — viewer/editor/admin
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
// ingestion-key — editor+admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionKey}, WildCardSelectorString)},
|
||||
// ingestion-limit — editor+admin only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIngestionLimit}, WildCardSelectorString)},
|
||||
// notification-channel — read only (admin writes)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
// route-policy — read only (admin writes)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
// apdex-setting — read only (admin updates)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
// quick-filter — read only (admin updates)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
// ttl-setting — read only (admin updates)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
// user-preference — every authenticated user can read+update their own
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
// telemetry — read on each signal
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeTelemetryResource, Kind: KindLogs}, WildCardSelectorString)},
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeTelemetryResource, Kind: KindTraces}, WildCardSelectorString)},
|
||||
@@ -294,66 +297,66 @@ var ManagedRoleToTransactions = map[string][]Transaction{
|
||||
// logs-field — editor reads+updates
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
// traces-field — editor reads+updates
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
},
|
||||
SigNozViewerRoleName: {
|
||||
// dashboard — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindDashboard}, WildCardSelectorString)},
|
||||
// pipeline — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPipeline}, WildCardSelectorString)},
|
||||
// planned-maintenance — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindPlannedMaintenance}, WildCardSelectorString)},
|
||||
// rule — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRule}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRule}, WildCardSelectorString)},
|
||||
// saved-view — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindSavedView}, WildCardSelectorString)},
|
||||
// trace-funnel — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTraceFunnel}, WildCardSelectorString)},
|
||||
// integration — viewer/editor/admin (install/uninstall via ViewAccess)
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbDelete, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbCreate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindIntegration}, WildCardSelectorString)},
|
||||
// notification-channel — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindNotificationChannel}, WildCardSelectorString)},
|
||||
// route-policy — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindRoutePolicy}, WildCardSelectorString)},
|
||||
// apdex-setting — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindApdexSetting}, WildCardSelectorString)},
|
||||
// quick-filter — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindQuickFilter}, WildCardSelectorString)},
|
||||
// ttl-setting — read only
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTTLSetting}, WildCardSelectorString)},
|
||||
// user-preference — every authenticated user can read+update their own
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbUpdate, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindUserPreference}, WildCardSelectorString)},
|
||||
// telemetry — read on each signal
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeTelemetryResource, Kind: KindLogs}, WildCardSelectorString)},
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeTelemetryResource, Kind: KindTraces}, WildCardSelectorString)},
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeTelemetryResource, Kind: KindMetrics}, WildCardSelectorString)},
|
||||
// logs-field — viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindLogsField}, WildCardSelectorString)},
|
||||
// traces-field — viewer reads
|
||||
{Verb: VerbRead, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResources, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
{Verb: VerbList, Object: *MustNewObject(ResourceRef{Type: TypeMetaResource, Kind: KindTracesField}, WildCardSelectorString)},
|
||||
},
|
||||
SigNozAnonymousRoleName: {
|
||||
// public-dashboard — anonymous read
|
||||
|
||||
@@ -6,138 +6,76 @@ var Resources = []Resource{
|
||||
ResourceRole,
|
||||
ResourceServiceAccount,
|
||||
ResourceUser,
|
||||
ResourceMetaResourcesRole,
|
||||
ResourceMetaResourcesOrganization,
|
||||
ResourceMetaResourcesServiceAccount,
|
||||
ResourceMetaResourcesUser,
|
||||
ResourceMetaResourceNotificationChannel,
|
||||
ResourceMetaResourcesNotificationChannel,
|
||||
ResourceMetaResourceRoutePolicy,
|
||||
ResourceMetaResourcesRoutePolicy,
|
||||
ResourceMetaResourceApdexSetting,
|
||||
ResourceMetaResourcesApdexSetting,
|
||||
ResourceMetaResourceAuthDomain,
|
||||
ResourceMetaResourcesAuthDomain,
|
||||
ResourceMetaResourceSession,
|
||||
ResourceMetaResourcesSession,
|
||||
ResourceMetaResourceCloudIntegration,
|
||||
ResourceMetaResourcesCloudIntegration,
|
||||
ResourceMetaResourceCloudIntegrationService,
|
||||
ResourceMetaResourcesCloudIntegrationService,
|
||||
ResourceMetaResourceIntegration,
|
||||
ResourceMetaResourcesIntegration,
|
||||
ResourceMetaResourceDashboard,
|
||||
ResourceMetaResourcesDashboard,
|
||||
ResourceMetaResourcePublicDashboard,
|
||||
ResourceMetaResourcesPublicDashboard,
|
||||
ResourceMetaResourceIngestionKey,
|
||||
ResourceMetaResourcesIngestionKey,
|
||||
ResourceMetaResourceIngestionLimit,
|
||||
ResourceMetaResourcesIngestionLimit,
|
||||
ResourceMetaResourcePipeline,
|
||||
ResourceMetaResourcesPipeline,
|
||||
ResourceMetaResourceUserPreference,
|
||||
ResourceMetaResourcesUserPreference,
|
||||
ResourceMetaResourceOrgPreference,
|
||||
ResourceMetaResourcesOrgPreference,
|
||||
ResourceMetaResourceQuickFilter,
|
||||
ResourceMetaResourcesQuickFilter,
|
||||
ResourceMetaResourceTTLSetting,
|
||||
ResourceMetaResourcesTTLSetting,
|
||||
ResourceMetaResourceRule,
|
||||
ResourceMetaResourcesRule,
|
||||
ResourceMetaResourcePlannedMaintenance,
|
||||
ResourceMetaResourcesPlannedMaintenance,
|
||||
ResourceMetaResourceSavedView,
|
||||
ResourceMetaResourcesSavedView,
|
||||
ResourceMetaResourceTraceFunnel,
|
||||
ResourceMetaResourcesTraceFunnel,
|
||||
ResourceMetaResourceFactorPassword,
|
||||
ResourceMetaResourcesFactorPassword,
|
||||
ResourceMetaResourceFactorAPIKey,
|
||||
ResourceMetaResourcesFactorAPIKey,
|
||||
ResourceMetaResourceLicense,
|
||||
ResourceMetaResourcesLicense,
|
||||
ResourceMetaResourceSubscription,
|
||||
ResourceMetaResourcesSubscription,
|
||||
ResourceTelemetryResourceLogs,
|
||||
ResourceTelemetryResourceTraces,
|
||||
ResourceTelemetryResourceMetrics,
|
||||
ResourceTelemetryResourceAuditLogs,
|
||||
ResourceTelemetryResourceMeterMetrics,
|
||||
ResourceMetaResourceLogsField,
|
||||
ResourceMetaResourcesLogsField,
|
||||
ResourceMetaResourceTracesField,
|
||||
ResourceMetaResourcesTracesField,
|
||||
}
|
||||
|
||||
var (
|
||||
ResourceAnonymous Resource = NewResourceAnonymous()
|
||||
ResourceOrganization = NewResourceOrganization()
|
||||
ResourceRole = NewResourceRole()
|
||||
ResourceServiceAccount = NewResourceServiceAccount()
|
||||
ResourceUser = NewResourceUser()
|
||||
ResourceMetaResourcesRole = NewResourceMetaResources(KindRole)
|
||||
ResourceMetaResourcesOrganization = NewResourceMetaResources(KindOrganization)
|
||||
ResourceMetaResourcesServiceAccount = NewResourceMetaResources(KindServiceAccount)
|
||||
ResourceMetaResourcesUser = NewResourceMetaResources(KindUser)
|
||||
ResourceMetaResourceNotificationChannel = NewResourceMetaResource(KindNotificationChannel)
|
||||
ResourceMetaResourcesNotificationChannel = NewResourceMetaResources(KindNotificationChannel)
|
||||
ResourceMetaResourceRoutePolicy = NewResourceMetaResource(KindRoutePolicy)
|
||||
ResourceMetaResourcesRoutePolicy = NewResourceMetaResources(KindRoutePolicy)
|
||||
ResourceMetaResourceApdexSetting = NewResourceMetaResource(KindApdexSetting)
|
||||
ResourceMetaResourcesApdexSetting = NewResourceMetaResources(KindApdexSetting)
|
||||
ResourceMetaResourceAuthDomain = NewResourceMetaResource(KindAuthDomain)
|
||||
ResourceMetaResourcesAuthDomain = NewResourceMetaResources(KindAuthDomain)
|
||||
ResourceMetaResourceSession = NewResourceMetaResource(KindSession)
|
||||
ResourceMetaResourcesSession = NewResourceMetaResources(KindSession)
|
||||
ResourceMetaResourceCloudIntegration = NewResourceMetaResource(KindCloudIntegration)
|
||||
ResourceMetaResourcesCloudIntegration = NewResourceMetaResources(KindCloudIntegration)
|
||||
ResourceMetaResourceCloudIntegrationService = NewResourceMetaResource(KindCloudIntegrationService)
|
||||
ResourceMetaResourcesCloudIntegrationService = NewResourceMetaResources(KindCloudIntegrationService)
|
||||
ResourceMetaResourceIntegration = NewResourceMetaResource(KindIntegration)
|
||||
ResourceMetaResourcesIntegration = NewResourceMetaResources(KindIntegration)
|
||||
ResourceMetaResourceDashboard = NewResourceMetaResource(KindDashboard)
|
||||
ResourceMetaResourcesDashboard = NewResourceMetaResources(KindDashboard)
|
||||
ResourceMetaResourcePublicDashboard = NewResourceMetaResource(KindPublicDashboard)
|
||||
ResourceMetaResourcesPublicDashboard = NewResourceMetaResources(KindPublicDashboard)
|
||||
ResourceMetaResourceIngestionKey = NewResourceMetaResource(KindIngestionKey)
|
||||
ResourceMetaResourcesIngestionKey = NewResourceMetaResources(KindIngestionKey)
|
||||
ResourceMetaResourceIngestionLimit = NewResourceMetaResource(KindIngestionLimit)
|
||||
ResourceMetaResourcesIngestionLimit = NewResourceMetaResources(KindIngestionLimit)
|
||||
ResourceMetaResourcePipeline = NewResourceMetaResource(KindPipeline)
|
||||
ResourceMetaResourcesPipeline = NewResourceMetaResources(KindPipeline)
|
||||
ResourceMetaResourceUserPreference = NewResourceMetaResource(KindUserPreference)
|
||||
ResourceMetaResourcesUserPreference = NewResourceMetaResources(KindUserPreference)
|
||||
ResourceMetaResourceOrgPreference = NewResourceMetaResource(KindOrgPreference)
|
||||
ResourceMetaResourcesOrgPreference = NewResourceMetaResources(KindOrgPreference)
|
||||
ResourceMetaResourceQuickFilter = NewResourceMetaResource(KindQuickFilter)
|
||||
ResourceMetaResourcesQuickFilter = NewResourceMetaResources(KindQuickFilter)
|
||||
ResourceMetaResourceTTLSetting = NewResourceMetaResource(KindTTLSetting)
|
||||
ResourceMetaResourcesTTLSetting = NewResourceMetaResources(KindTTLSetting)
|
||||
ResourceMetaResourceRule = NewResourceMetaResource(KindRule)
|
||||
ResourceMetaResourcesRule = NewResourceMetaResources(KindRule)
|
||||
ResourceMetaResourcePlannedMaintenance = NewResourceMetaResource(KindPlannedMaintenance)
|
||||
ResourceMetaResourcesPlannedMaintenance = NewResourceMetaResources(KindPlannedMaintenance)
|
||||
ResourceMetaResourceSavedView = NewResourceMetaResource(KindSavedView)
|
||||
ResourceMetaResourcesSavedView = NewResourceMetaResources(KindSavedView)
|
||||
ResourceMetaResourceTraceFunnel = NewResourceMetaResource(KindTraceFunnel)
|
||||
ResourceMetaResourcesTraceFunnel = NewResourceMetaResources(KindTraceFunnel)
|
||||
ResourceMetaResourceFactorPassword = NewResourceMetaResource(KindFactorPassword)
|
||||
ResourceMetaResourcesFactorPassword = NewResourceMetaResources(KindFactorPassword)
|
||||
ResourceMetaResourceFactorAPIKey = NewResourceMetaResource(KindFactorAPIKey)
|
||||
ResourceMetaResourcesFactorAPIKey = NewResourceMetaResources(KindFactorAPIKey)
|
||||
ResourceMetaResourceLicense = NewResourceMetaResource(KindLicense)
|
||||
ResourceMetaResourcesLicense = NewResourceMetaResources(KindLicense)
|
||||
ResourceMetaResourceSubscription = NewResourceMetaResource(KindSubscription)
|
||||
ResourceMetaResourcesSubscription = NewResourceMetaResources(KindSubscription)
|
||||
ResourceTelemetryResourceLogs = NewResourceTelemetryResource(KindLogs)
|
||||
ResourceTelemetryResourceTraces = NewResourceTelemetryResource(KindTraces)
|
||||
ResourceTelemetryResourceMetrics = NewResourceTelemetryResource(KindMetrics)
|
||||
ResourceTelemetryResourceAuditLogs = NewResourceTelemetryResource(KindAuditLogs)
|
||||
ResourceTelemetryResourceMeterMetrics = NewResourceTelemetryResource(KindMeterMetrics)
|
||||
ResourceMetaResourceLogsField = NewResourceMetaResource(KindLogsField)
|
||||
ResourceMetaResourcesLogsField = NewResourceMetaResources(KindLogsField)
|
||||
ResourceMetaResourceTracesField = NewResourceMetaResource(KindTracesField)
|
||||
ResourceMetaResourcesTracesField = NewResourceMetaResources(KindTracesField)
|
||||
ResourceAnonymous Resource = NewResourceAnonymous()
|
||||
ResourceOrganization = NewResourceOrganization()
|
||||
ResourceRole = NewResourceRole()
|
||||
ResourceServiceAccount = NewResourceServiceAccount()
|
||||
ResourceUser = NewResourceUser()
|
||||
ResourceMetaResourceNotificationChannel = NewResourceMetaResource(KindNotificationChannel)
|
||||
ResourceMetaResourceRoutePolicy = NewResourceMetaResource(KindRoutePolicy)
|
||||
ResourceMetaResourceApdexSetting = NewResourceMetaResource(KindApdexSetting)
|
||||
ResourceMetaResourceAuthDomain = NewResourceMetaResource(KindAuthDomain)
|
||||
ResourceMetaResourceSession = NewResourceMetaResource(KindSession)
|
||||
ResourceMetaResourceCloudIntegration = NewResourceMetaResource(KindCloudIntegration)
|
||||
ResourceMetaResourceCloudIntegrationService = NewResourceMetaResource(KindCloudIntegrationService)
|
||||
ResourceMetaResourceIntegration = NewResourceMetaResource(KindIntegration)
|
||||
ResourceMetaResourceDashboard = NewResourceMetaResource(KindDashboard)
|
||||
ResourceMetaResourcePublicDashboard = NewResourceMetaResource(KindPublicDashboard)
|
||||
ResourceMetaResourceIngestionKey = NewResourceMetaResource(KindIngestionKey)
|
||||
ResourceMetaResourceIngestionLimit = NewResourceMetaResource(KindIngestionLimit)
|
||||
ResourceMetaResourcePipeline = NewResourceMetaResource(KindPipeline)
|
||||
ResourceMetaResourceUserPreference = NewResourceMetaResource(KindUserPreference)
|
||||
ResourceMetaResourceOrgPreference = NewResourceMetaResource(KindOrgPreference)
|
||||
ResourceMetaResourceQuickFilter = NewResourceMetaResource(KindQuickFilter)
|
||||
ResourceMetaResourceTTLSetting = NewResourceMetaResource(KindTTLSetting)
|
||||
ResourceMetaResourceRule = NewResourceMetaResource(KindRule)
|
||||
ResourceMetaResourcePlannedMaintenance = NewResourceMetaResource(KindPlannedMaintenance)
|
||||
ResourceMetaResourceSavedView = NewResourceMetaResource(KindSavedView)
|
||||
ResourceMetaResourceTraceFunnel = NewResourceMetaResource(KindTraceFunnel)
|
||||
ResourceMetaResourceFactorPassword = NewResourceMetaResource(KindFactorPassword)
|
||||
ResourceMetaResourceFactorAPIKey = NewResourceMetaResource(KindFactorAPIKey)
|
||||
ResourceMetaResourceLicense = NewResourceMetaResource(KindLicense)
|
||||
ResourceMetaResourceSubscription = NewResourceMetaResource(KindSubscription)
|
||||
ResourceTelemetryResourceLogs = NewResourceTelemetryResource(KindLogs)
|
||||
ResourceTelemetryResourceTraces = NewResourceTelemetryResource(KindTraces)
|
||||
ResourceTelemetryResourceMetrics = NewResourceTelemetryResource(KindMetrics)
|
||||
ResourceTelemetryResourceAuditLogs = NewResourceTelemetryResource(KindAuditLogs)
|
||||
ResourceTelemetryResourceMeterMetrics = NewResourceTelemetryResource(KindMeterMetrics)
|
||||
ResourceMetaResourceLogsField = NewResourceMetaResource(KindLogsField)
|
||||
ResourceMetaResourceTracesField = NewResourceMetaResource(KindTracesField)
|
||||
)
|
||||
|
||||
@@ -13,17 +13,15 @@ var Types = []Type{
|
||||
TypeRole,
|
||||
TypeOrganization,
|
||||
TypeMetaResource,
|
||||
TypeMetaResources,
|
||||
TypeTelemetryResource,
|
||||
}
|
||||
|
||||
var (
|
||||
TypeUser = Type{valuer.NewString("user"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbAttach, VerbRead, VerbUpdate, VerbDelete}}
|
||||
TypeServiceAccount = Type{valuer.NewString("serviceaccount"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbAttach, VerbRead, VerbUpdate, VerbDelete}}
|
||||
TypeUser = Type{valuer.NewString("user"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbCreate, VerbList, VerbRead, VerbUpdate, VerbDelete, VerbAttach, VerbDetach}}
|
||||
TypeServiceAccount = Type{valuer.NewString("serviceaccount"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbCreate, VerbList, VerbRead, VerbUpdate, VerbDelete, VerbAttach, VerbDetach}}
|
||||
TypeAnonymous = Type{valuer.NewString("anonymous"), regexp.MustCompile(`^\*$`), []Verb{}}
|
||||
TypeRole = Type{valuer.NewString("role"), regexp.MustCompile(`^([a-z-]{1,50}|\*)$`), []Verb{VerbAssignee, VerbAttach, VerbRead, VerbUpdate, VerbDelete}}
|
||||
TypeRole = Type{valuer.NewString("role"), regexp.MustCompile(`^([a-z-]{1,50}|\*)$`), []Verb{VerbAssignee, VerbCreate, VerbList, VerbRead, VerbUpdate, VerbDelete, VerbAttach, VerbDetach}}
|
||||
TypeOrganization = Type{valuer.NewString("organization"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbRead, VerbUpdate, VerbDelete}}
|
||||
TypeMetaResource = Type{valuer.NewString("metaresource"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbRead, VerbUpdate, VerbDelete}}
|
||||
TypeMetaResources = Type{valuer.NewString("metaresources"), regexp.MustCompile(`^\*$`), []Verb{VerbCreate, VerbList}}
|
||||
TypeMetaResource = Type{valuer.NewString("metaresource"), regexp.MustCompile(`^(^[0-9a-f]{8}(?:\-[0-9a-f]{4}){3}-[0-9a-f]{12}$|\*)$`), []Verb{VerbCreate, VerbList, VerbRead, VerbUpdate, VerbDelete, VerbAttach, VerbDetach}}
|
||||
TypeTelemetryResource = Type{valuer.NewString("telemetryresource"), regexp.MustCompile(`^\*$`), []Verb{VerbRead}}
|
||||
)
|
||||
|
||||
@@ -10,6 +10,7 @@ var Verbs = []Verb{
|
||||
VerbList,
|
||||
VerbAssignee,
|
||||
VerbAttach,
|
||||
VerbDetach,
|
||||
}
|
||||
|
||||
var (
|
||||
@@ -20,4 +21,5 @@ var (
|
||||
VerbList = Verb{valuer.NewString("list"), "listed"}
|
||||
VerbAssignee = Verb{valuer.NewString("assignee"), "assigned"}
|
||||
VerbAttach = Verb{valuer.NewString("attach"), "attached"}
|
||||
VerbDetach = Verb{valuer.NewString("detach"), "detached"}
|
||||
)
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package coretypes
|
||||
|
||||
import (
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
)
|
||||
|
||||
type resourceMetaResources struct {
|
||||
kind Kind
|
||||
}
|
||||
|
||||
func NewResourceMetaResources(kind Kind) Resource {
|
||||
return &resourceMetaResources{kind: kind}
|
||||
}
|
||||
|
||||
func (*resourceMetaResources) Type() Type {
|
||||
return TypeMetaResources
|
||||
}
|
||||
|
||||
func (resourceMetaResources *resourceMetaResources) Kind() Kind {
|
||||
return resourceMetaResources.kind
|
||||
}
|
||||
|
||||
// example: metaresources:organization/0199c47d-f61b-7833-bc5f-c0730f12f046/dashboards
|
||||
func (resourceMetaResources *resourceMetaResources) Prefix(orgID valuer.UUID) string {
|
||||
return resourceMetaResources.Type().StringValue() + ":" + "organization" + "/" + orgID.StringValue() + "/" + resourceMetaResources.Kind().String()
|
||||
}
|
||||
|
||||
func (resourceMetaResources *resourceMetaResources) Object(orgID valuer.UUID, selector string) string {
|
||||
return resourceMetaResources.Prefix(orgID) + "/" + selector
|
||||
}
|
||||
|
||||
func (resourceMetaResources *resourceMetaResources) Scope(verb Verb) string {
|
||||
return resourceMetaResources.Kind().String() + ":" + verb.StringValue()
|
||||
}
|
||||
@@ -33,8 +33,6 @@ func NewType(input string) (Type, error) {
|
||||
return TypeOrganization, nil
|
||||
case "metaresource":
|
||||
return TypeMetaResource, nil
|
||||
case "metaresources":
|
||||
return TypeMetaResources, nil
|
||||
default:
|
||||
return Type{}, errors.Newf(errors.TypeInvalidInput, ErrCodeInvalidType, "invalid type: %s", input)
|
||||
}
|
||||
@@ -80,7 +78,6 @@ func (typed Type) Enum() []any {
|
||||
TypeRole,
|
||||
TypeOrganization,
|
||||
TypeMetaResource,
|
||||
TypeMetaResources,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -30,6 +30,8 @@ func NewVerb(verb string) (Verb, error) {
|
||||
return VerbAssignee, nil
|
||||
case "attach":
|
||||
return VerbAttach, nil
|
||||
case "detach":
|
||||
return VerbDetach, nil
|
||||
default:
|
||||
return Verb{}, errors.Newf(errors.TypeInvalidInput, ErrCodeInvalidVerb, "verb %s is invalid, valid verbs are: %s", verb, Verb{}.Enum())
|
||||
}
|
||||
@@ -44,6 +46,7 @@ func (Verb) Enum() []any {
|
||||
VerbList,
|
||||
VerbAssignee,
|
||||
VerbAttach,
|
||||
VerbDetach,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
105
pkg/types/inframonitoringtypes/deployments.go
Normal file
105
pkg/types/inframonitoringtypes/deployments.go
Normal file
@@ -0,0 +1,105 @@
|
||||
package inframonitoringtypes
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"slices"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
)
|
||||
|
||||
type Deployments struct {
|
||||
Type ResponseType `json:"type" required:"true"`
|
||||
Records []DeploymentRecord `json:"records" required:"true"`
|
||||
Total int `json:"total" required:"true"`
|
||||
RequiredMetricsCheck RequiredMetricsCheck `json:"requiredMetricsCheck" required:"true"`
|
||||
EndTimeBeforeRetention bool `json:"endTimeBeforeRetention" required:"true"`
|
||||
Warning *qbtypes.QueryWarnData `json:"warning,omitempty"`
|
||||
}
|
||||
|
||||
type DeploymentRecord struct {
|
||||
DeploymentName string `json:"deploymentName" required:"true"`
|
||||
DeploymentCPU float64 `json:"deploymentCPU" required:"true"`
|
||||
DeploymentCPURequest float64 `json:"deploymentCPURequest" required:"true"`
|
||||
DeploymentCPULimit float64 `json:"deploymentCPULimit" required:"true"`
|
||||
DeploymentMemory float64 `json:"deploymentMemory" required:"true"`
|
||||
DeploymentMemoryRequest float64 `json:"deploymentMemoryRequest" required:"true"`
|
||||
DeploymentMemoryLimit float64 `json:"deploymentMemoryLimit" required:"true"`
|
||||
DesiredPods int `json:"desiredPods" required:"true"`
|
||||
AvailablePods int `json:"availablePods" required:"true"`
|
||||
PodCountsByPhase PodCountsByPhase `json:"podCountsByPhase" required:"true"`
|
||||
Meta map[string]string `json:"meta" required:"true"`
|
||||
}
|
||||
|
||||
// PostableDeployments is the request body for the v2 deployments list API.
|
||||
type PostableDeployments struct {
|
||||
Start int64 `json:"start" required:"true"`
|
||||
End int64 `json:"end" required:"true"`
|
||||
Filter *qbtypes.Filter `json:"filter"`
|
||||
GroupBy []qbtypes.GroupByKey `json:"groupBy"`
|
||||
OrderBy *qbtypes.OrderBy `json:"orderBy"`
|
||||
Offset int `json:"offset"`
|
||||
Limit int `json:"limit" required:"true"`
|
||||
}
|
||||
|
||||
// Validate ensures PostableDeployments contains acceptable values.
|
||||
func (req *PostableDeployments) Validate() error {
|
||||
if req == nil {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "request is nil")
|
||||
}
|
||||
|
||||
if req.Start <= 0 {
|
||||
return errors.NewInvalidInputf(
|
||||
errors.CodeInvalidInput,
|
||||
"invalid start time %d: start must be greater than 0",
|
||||
req.Start,
|
||||
)
|
||||
}
|
||||
|
||||
if req.End <= 0 {
|
||||
return errors.NewInvalidInputf(
|
||||
errors.CodeInvalidInput,
|
||||
"invalid end time %d: end must be greater than 0",
|
||||
req.End,
|
||||
)
|
||||
}
|
||||
|
||||
if req.Start >= req.End {
|
||||
return errors.NewInvalidInputf(
|
||||
errors.CodeInvalidInput,
|
||||
"invalid time range: start (%d) must be less than end (%d)",
|
||||
req.Start,
|
||||
req.End,
|
||||
)
|
||||
}
|
||||
|
||||
if req.Limit < 1 || req.Limit > 5000 {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "limit must be between 1 and 5000")
|
||||
}
|
||||
|
||||
if req.Offset < 0 {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "offset cannot be negative")
|
||||
}
|
||||
|
||||
if req.OrderBy != nil {
|
||||
if !slices.Contains(DeploymentsValidOrderByKeys, req.OrderBy.Key.Name) {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "invalid order by key: %s", req.OrderBy.Key.Name)
|
||||
}
|
||||
if req.OrderBy.Direction != qbtypes.OrderDirectionAsc && req.OrderBy.Direction != qbtypes.OrderDirectionDesc {
|
||||
return errors.NewInvalidInputf(errors.CodeInvalidInput, "invalid order by direction: %s", req.OrderBy.Direction)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// UnmarshalJSON validates input immediately after decoding.
|
||||
func (req *PostableDeployments) UnmarshalJSON(data []byte) error {
|
||||
type raw PostableDeployments
|
||||
var decoded raw
|
||||
if err := json.Unmarshal(data, &decoded); err != nil {
|
||||
return err
|
||||
}
|
||||
*req = PostableDeployments(decoded)
|
||||
return req.Validate()
|
||||
}
|
||||
23
pkg/types/inframonitoringtypes/deployments_constants.go
Normal file
23
pkg/types/inframonitoringtypes/deployments_constants.go
Normal file
@@ -0,0 +1,23 @@
|
||||
package inframonitoringtypes
|
||||
|
||||
const (
|
||||
DeploymentsOrderByCPU = "cpu"
|
||||
DeploymentsOrderByCPURequest = "cpu_request"
|
||||
DeploymentsOrderByCPULimit = "cpu_limit"
|
||||
DeploymentsOrderByMemory = "memory"
|
||||
DeploymentsOrderByMemoryRequest = "memory_request"
|
||||
DeploymentsOrderByMemoryLimit = "memory_limit"
|
||||
DeploymentsOrderByDesiredPods = "desired_pods"
|
||||
DeploymentsOrderByAvailablePods = "available_pods"
|
||||
)
|
||||
|
||||
var DeploymentsValidOrderByKeys = []string{
|
||||
DeploymentsOrderByCPU,
|
||||
DeploymentsOrderByCPURequest,
|
||||
DeploymentsOrderByCPULimit,
|
||||
DeploymentsOrderByMemory,
|
||||
DeploymentsOrderByMemoryRequest,
|
||||
DeploymentsOrderByMemoryLimit,
|
||||
DeploymentsOrderByDesiredPods,
|
||||
DeploymentsOrderByAvailablePods,
|
||||
}
|
||||
273
pkg/types/inframonitoringtypes/deployments_test.go
Normal file
273
pkg/types/inframonitoringtypes/deployments_test.go
Normal file
@@ -0,0 +1,273 @@
|
||||
package inframonitoringtypes
|
||||
|
||||
import (
|
||||
"testing"
|
||||
|
||||
"github.com/SigNoz/signoz/pkg/errors"
|
||||
qbtypes "github.com/SigNoz/signoz/pkg/types/querybuildertypes/querybuildertypesv5"
|
||||
"github.com/SigNoz/signoz/pkg/types/telemetrytypes"
|
||||
"github.com/SigNoz/signoz/pkg/valuer"
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPostableDeployments_Validate(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
req *PostableDeployments
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "valid request",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 2000,
|
||||
Limit: 100,
|
||||
Offset: 0,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "nil request",
|
||||
req: nil,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "start time zero",
|
||||
req: &PostableDeployments{
|
||||
Start: 0,
|
||||
End: 2000,
|
||||
Limit: 100,
|
||||
Offset: 0,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "start time negative",
|
||||
req: &PostableDeployments{
|
||||
Start: -1000,
|
||||
End: 2000,
|
||||
Limit: 100,
|
||||
Offset: 0,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "end time zero",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 0,
|
||||
Limit: 100,
|
||||
Offset: 0,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "start time greater than end time",
|
||||
req: &PostableDeployments{
|
||||
Start: 2000,
|
||||
End: 1000,
|
||||
Limit: 100,
|
||||
Offset: 0,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "start time equal to end time",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 1000,
|
||||
Limit: 100,
|
||||
Offset: 0,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "limit zero",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 2000,
|
||||
Limit: 0,
|
||||
Offset: 0,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "limit negative",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 2000,
|
||||
Limit: -10,
|
||||
Offset: 0,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "limit exceeds max",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 2000,
|
||||
Limit: 5001,
|
||||
Offset: 0,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "offset negative",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 2000,
|
||||
Limit: 100,
|
||||
Offset: -5,
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "orderBy nil is valid",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 2000,
|
||||
Limit: 100,
|
||||
Offset: 0,
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "orderBy with valid key cpu and direction asc",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 2000,
|
||||
Limit: 100,
|
||||
Offset: 0,
|
||||
OrderBy: &qbtypes.OrderBy{
|
||||
Key: qbtypes.OrderByKey{
|
||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
||||
Name: DeploymentsOrderByCPU,
|
||||
},
|
||||
},
|
||||
Direction: qbtypes.OrderDirectionAsc,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "orderBy with valid key memory_limit and direction desc",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 2000,
|
||||
Limit: 100,
|
||||
Offset: 0,
|
||||
OrderBy: &qbtypes.OrderBy{
|
||||
Key: qbtypes.OrderByKey{
|
||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
||||
Name: DeploymentsOrderByMemoryLimit,
|
||||
},
|
||||
},
|
||||
Direction: qbtypes.OrderDirectionDesc,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "orderBy with valid key desired_pods and direction desc",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 2000,
|
||||
Limit: 100,
|
||||
Offset: 0,
|
||||
OrderBy: &qbtypes.OrderBy{
|
||||
Key: qbtypes.OrderByKey{
|
||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
||||
Name: DeploymentsOrderByDesiredPods,
|
||||
},
|
||||
},
|
||||
Direction: qbtypes.OrderDirectionDesc,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "orderBy with valid key available_pods and direction asc",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 2000,
|
||||
Limit: 100,
|
||||
Offset: 0,
|
||||
OrderBy: &qbtypes.OrderBy{
|
||||
Key: qbtypes.OrderByKey{
|
||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
||||
Name: DeploymentsOrderByAvailablePods,
|
||||
},
|
||||
},
|
||||
Direction: qbtypes.OrderDirectionAsc,
|
||||
},
|
||||
},
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "orderBy with restarts key is rejected",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 2000,
|
||||
Limit: 100,
|
||||
Offset: 0,
|
||||
OrderBy: &qbtypes.OrderBy{
|
||||
Key: qbtypes.OrderByKey{
|
||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
||||
Name: "restarts",
|
||||
},
|
||||
},
|
||||
Direction: qbtypes.OrderDirectionDesc,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "orderBy with invalid key",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 2000,
|
||||
Limit: 100,
|
||||
Offset: 0,
|
||||
OrderBy: &qbtypes.OrderBy{
|
||||
Key: qbtypes.OrderByKey{
|
||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
||||
Name: "unknown",
|
||||
},
|
||||
},
|
||||
Direction: qbtypes.OrderDirectionDesc,
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "orderBy with valid key but invalid direction",
|
||||
req: &PostableDeployments{
|
||||
Start: 1000,
|
||||
End: 2000,
|
||||
Limit: 100,
|
||||
Offset: 0,
|
||||
OrderBy: &qbtypes.OrderBy{
|
||||
Key: qbtypes.OrderByKey{
|
||||
TelemetryFieldKey: telemetrytypes.TelemetryFieldKey{
|
||||
Name: DeploymentsOrderByCPU,
|
||||
},
|
||||
},
|
||||
Direction: qbtypes.OrderDirection{String: valuer.NewString("invalid")},
|
||||
},
|
||||
},
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
err := tt.req.Validate()
|
||||
if tt.wantErr {
|
||||
require.Error(t, err)
|
||||
require.True(t, errors.Ast(err, errors.TypeInvalidInput), "expected error to be of type InvalidInput")
|
||||
} else {
|
||||
require.NoError(t, err)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
326
tests/integration/tests/role/03_fga.py
Normal file
326
tests/integration/tests/role/03_fga.py
Normal file
@@ -0,0 +1,326 @@
|
||||
"""Tests for resource-level FGA on role endpoints.
|
||||
|
||||
Validates that a custom role with specific role permissions gets exactly
|
||||
the access it was granted — read/list allowed, create/update/delete forbidden
|
||||
until explicitly granted, and revocation removes access.
|
||||
"""
|
||||
|
||||
from collections.abc import Callable
|
||||
from http import HTTPStatus
|
||||
|
||||
import requests
|
||||
from wiremock.resources.mappings import Mapping
|
||||
|
||||
from fixtures import types
|
||||
from fixtures.auth import (
|
||||
USER_ADMIN_EMAIL,
|
||||
USER_ADMIN_PASSWORD,
|
||||
add_license,
|
||||
change_user_role,
|
||||
create_active_user,
|
||||
find_user_by_email,
|
||||
)
|
||||
from fixtures.role import (
|
||||
ROLES_BASE,
|
||||
create_custom_role,
|
||||
delete_custom_role,
|
||||
find_role_by_name,
|
||||
object_group,
|
||||
patch_role_objects,
|
||||
)
|
||||
|
||||
ROLE_FGA_CUSTOM_ROLE_NAME = "role-fga-readonly"
|
||||
ROLE_FGA_CUSTOM_USER_EMAIL = "customrole+rolefga@integration.test"
|
||||
ROLE_FGA_CUSTOM_USER_PASSWORD = "password123Z$"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 1. Apply license (required for custom role CRUD)
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_apply_license(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
make_http_mocks: Callable[[types.TestContainerDocker, list[Mapping]], None],
|
||||
get_token: Callable[[str, str], str],
|
||||
) -> None:
|
||||
add_license(signoz, make_http_mocks, get_token)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 2. Create custom role + user with read/list on roles
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_create_custom_role_for_role_fga(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
|
||||
# Create the custom role.
|
||||
role_id = create_custom_role(signoz, admin_token, ROLE_FGA_CUSTOM_ROLE_NAME)
|
||||
|
||||
# Grant read on role instances.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"read",
|
||||
additions=[
|
||||
object_group("role", "role", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
# Grant list on role collection.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"list",
|
||||
additions=[
|
||||
object_group("role", "role", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
# Create the custom-role user: invite as VIEWER, activate, change role.
|
||||
user_id = create_active_user(
|
||||
signoz,
|
||||
admin_token,
|
||||
email=ROLE_FGA_CUSTOM_USER_EMAIL,
|
||||
role="VIEWER",
|
||||
password=ROLE_FGA_CUSTOM_USER_PASSWORD,
|
||||
name="role-fga-test-user",
|
||||
)
|
||||
change_user_role(signoz, admin_token, user_id, "signoz-viewer", ROLE_FGA_CUSTOM_ROLE_NAME)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 3. Read-only access: allowed operations
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_role_readonly_allowed_operations(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
token = get_token(ROLE_FGA_CUSTOM_USER_EMAIL, ROLE_FGA_CUSTOM_USER_PASSWORD)
|
||||
target_role_id = find_role_by_name(signoz, admin_token, "signoz-viewer")
|
||||
|
||||
# List roles.
|
||||
resp = requests.get(
|
||||
signoz.self.host_configs["8080"].get(ROLES_BASE),
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.OK, f"list roles: {resp.text}"
|
||||
|
||||
# Get role.
|
||||
resp = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{target_role_id}"),
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.OK, f"get role: {resp.text}"
|
||||
|
||||
# Get objects for role.
|
||||
resp = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{target_role_id}/relations/read/objects"),
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.OK, f"get role objects: {resp.text}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 4. Read-only access: forbidden operations
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_role_readonly_forbidden_operations(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
token = get_token(ROLE_FGA_CUSTOM_USER_EMAIL, ROLE_FGA_CUSTOM_USER_PASSWORD)
|
||||
target_role_id = find_role_by_name(signoz, admin_token, "signoz-viewer")
|
||||
|
||||
# Create role — forbidden.
|
||||
resp = requests.post(
|
||||
signoz.self.host_configs["8080"].get(ROLES_BASE),
|
||||
json={"name": "role-fga-should-fail"},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"create role: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Patch role — forbidden.
|
||||
resp = requests.patch(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{target_role_id}"),
|
||||
json={"name": "role-fga-renamed"},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"patch role: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Patch objects — forbidden.
|
||||
resp = requests.patch(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{target_role_id}/relations/read/objects"),
|
||||
json={"additions": [object_group("metaresource", "dashboard", ["*"])]},
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"patch objects: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Delete role — forbidden (cannot delete managed role, but auth check comes first).
|
||||
# Use the custom role itself as target (non-managed, but user lacks delete permission).
|
||||
custom_role_id = find_role_by_name(signoz, admin_token, ROLE_FGA_CUSTOM_ROLE_NAME)
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{custom_role_id}"),
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"delete role: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 5. Grant write permissions, verify access opens up
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_role_grant_write_permissions(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
role_id = find_role_by_name(signoz, admin_token, ROLE_FGA_CUSTOM_ROLE_NAME)
|
||||
|
||||
# Grant create, update, delete on roles.
|
||||
for verb in ("create", "update", "delete"):
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
verb,
|
||||
additions=[object_group("role", "role", ["*"])],
|
||||
)
|
||||
|
||||
custom_token = get_token(ROLE_FGA_CUSTOM_USER_EMAIL, ROLE_FGA_CUSTOM_USER_PASSWORD)
|
||||
|
||||
# Create role — now allowed.
|
||||
resp = requests.post(
|
||||
signoz.self.host_configs["8080"].get(ROLES_BASE),
|
||||
json={"name": "role-fga-write-test"},
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.CREATED, f"create role: {resp.text}"
|
||||
new_role_id = resp.json()["data"]["id"]
|
||||
|
||||
# Patch role — now allowed.
|
||||
resp = requests.patch(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{new_role_id}"),
|
||||
json={"name": "role-fga-write-renamed"},
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"patch role: {resp.text}"
|
||||
|
||||
# Delete role — now allowed.
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{new_role_id}"),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"delete role: {resp.text}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 6. Revoke read/list → verify access lost
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_role_revoke_read_permissions(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
role_id = find_role_by_name(signoz, admin_token, ROLE_FGA_CUSTOM_ROLE_NAME)
|
||||
target_role_id = find_role_by_name(signoz, admin_token, "signoz-viewer")
|
||||
|
||||
# Revoke read.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"read",
|
||||
deletions=[object_group("role", "role", ["*"])],
|
||||
)
|
||||
|
||||
# Revoke list.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"list",
|
||||
deletions=[object_group("role", "role", ["*"])],
|
||||
)
|
||||
|
||||
custom_token = get_token(ROLE_FGA_CUSTOM_USER_EMAIL, ROLE_FGA_CUSTOM_USER_PASSWORD)
|
||||
|
||||
# List roles — forbidden.
|
||||
resp = requests.get(
|
||||
signoz.self.host_configs["8080"].get(ROLES_BASE),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"list roles after revoke: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Get role — forbidden.
|
||||
resp = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"{ROLES_BASE}/{target_role_id}"),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"get role after revoke: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 7. Clean up: delete custom role
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_role_fga_cleanup(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
role_id = find_role_by_name(signoz, admin_token, ROLE_FGA_CUSTOM_ROLE_NAME)
|
||||
user = find_user_by_email(signoz, admin_token, ROLE_FGA_CUSTOM_USER_EMAIL)
|
||||
|
||||
# Remove the custom role from the user first.
|
||||
resp = requests.get(
|
||||
signoz.self.host_configs["8080"].get(f"/api/v2/users/{user['id']}/roles"),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.OK, resp.text
|
||||
roles = resp.json()["data"]
|
||||
custom_entry = next((r for r in roles if r["name"] == ROLE_FGA_CUSTOM_ROLE_NAME), None)
|
||||
if custom_entry is not None:
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"/api/v2/users/{user['id']}/roles/{custom_entry['id']}"),
|
||||
headers={"Authorization": f"Bearer {admin_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"remove role from user: {resp.text}"
|
||||
|
||||
delete_custom_role(signoz, admin_token, role_id)
|
||||
@@ -1,8 +1,11 @@
|
||||
"""Tests for resource-level FGA on service account endpoints.
|
||||
|
||||
Validates that a custom role with specific SA permissions gets exactly
|
||||
the access it was granted, and that SA role assignment requires BOTH
|
||||
serviceaccount:attach AND role:attach.
|
||||
the access it was granted, and that:
|
||||
- SA role assignment requires BOTH serviceaccount:attach AND role:attach.
|
||||
- SA role removal requires BOTH serviceaccount:detach AND role:detach.
|
||||
- Factor API key creation requires factor-api-key:create AND serviceaccount:attach.
|
||||
- Factor API key revocation requires factor-api-key:delete AND serviceaccount:detach.
|
||||
"""
|
||||
|
||||
from collections.abc import Callable
|
||||
@@ -80,14 +83,36 @@ def test_create_custom_role_readonly_sa(
|
||||
],
|
||||
)
|
||||
|
||||
# Grant list on serviceaccount collection.
|
||||
# Grant list on serviceaccount (now on the serviceaccount type directly).
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"list",
|
||||
additions=[
|
||||
object_group("metaresources", "serviceaccount", ["*"]),
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
# Grant read on factor-api-key (needed for listing keys).
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"read",
|
||||
additions=[
|
||||
object_group("metaresource", "factor-api-key", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
# Grant list on factor-api-key.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"list",
|
||||
additions=[
|
||||
object_group("metaresource", "factor-api-key", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -203,7 +228,7 @@ def test_readonly_role_forbidden_operations(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"delete SA: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Assign role to SA — forbidden.
|
||||
# Assign role to SA — forbidden (needs attach on both SA and role).
|
||||
resp = requests.post(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/roles"),
|
||||
json={"id": viewer_role_id},
|
||||
@@ -212,7 +237,7 @@ def test_readonly_role_forbidden_operations(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"assign SA role: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Remove role from SA — forbidden.
|
||||
# Remove role from SA — forbidden (needs detach on both SA and role).
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/roles/{viewer_role_id}"),
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
@@ -220,7 +245,7 @@ def test_readonly_role_forbidden_operations(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"remove SA role: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Create key — forbidden (needs update).
|
||||
# Create key — forbidden (needs factor-api-key:create + serviceaccount:attach).
|
||||
resp = requests.post(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/keys"),
|
||||
json={"name": "fga-key-fail", "expiresAt": 0},
|
||||
@@ -229,7 +254,7 @@ def test_readonly_role_forbidden_operations(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"create key: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Revoke key — forbidden (needs update).
|
||||
# Revoke key — forbidden (needs factor-api-key:delete + serviceaccount:detach).
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/keys/{key_id}"),
|
||||
headers={"Authorization": f"Bearer {token}"},
|
||||
@@ -253,14 +278,14 @@ def test_patch_role_add_write_permissions(
|
||||
sa_id = find_service_account_by_name(signoz, admin_token, SA_FGA_TARGET_SA_NAME)["id"]
|
||||
viewer_role_id = find_role_by_name(signoz, admin_token, "signoz-viewer")
|
||||
|
||||
# Grant create on collection.
|
||||
# Grant create on serviceaccount (now on serviceaccount type directly).
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"create",
|
||||
additions=[
|
||||
object_group("metaresources", "serviceaccount", ["*"]),
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -286,6 +311,44 @@ def test_patch_role_add_write_permissions(
|
||||
],
|
||||
)
|
||||
|
||||
# Grant factor-api-key create/delete + serviceaccount attach/detach for key operations.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"create",
|
||||
additions=[
|
||||
object_group("metaresource", "factor-api-key", ["*"]),
|
||||
],
|
||||
)
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"delete",
|
||||
additions=[
|
||||
object_group("metaresource", "factor-api-key", ["*"]),
|
||||
],
|
||||
)
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"attach",
|
||||
additions=[
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"detach",
|
||||
additions=[
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
custom_token = get_token(SA_FGA_CUSTOM_USER_EMAIL, SA_FGA_CUSTOM_USER_PASSWORD)
|
||||
|
||||
# Create SA — now allowed.
|
||||
@@ -307,7 +370,7 @@ def test_patch_role_add_write_permissions(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"update SA: {resp.text}"
|
||||
|
||||
# Create key — now allowed (update permission covers key create).
|
||||
# Create key — now allowed (factor-api-key:create + serviceaccount:attach).
|
||||
key_resp = requests.post(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{new_sa_id}/keys"),
|
||||
json={"name": "fga-write-key", "expiresAt": 0},
|
||||
@@ -317,7 +380,7 @@ def test_patch_role_add_write_permissions(
|
||||
assert key_resp.status_code == HTTPStatus.CREATED, f"create key: {key_resp.text}"
|
||||
new_key_id = key_resp.json()["data"]["id"]
|
||||
|
||||
# Revoke key — now allowed (update permission covers key revoke).
|
||||
# Revoke key — now allowed (factor-api-key:delete + serviceaccount:detach).
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{new_sa_id}/keys/{new_key_id}"),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
@@ -333,7 +396,7 @@ def test_patch_role_add_write_permissions(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"delete SA: {resp.text}"
|
||||
|
||||
# Role assignment still forbidden (no attach).
|
||||
# Role assignment still forbidden (has attach on SA but not on role).
|
||||
resp = requests.post(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/roles"),
|
||||
json={"id": viewer_role_id},
|
||||
@@ -342,6 +405,7 @@ def test_patch_role_add_write_permissions(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"assign SA role: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Role removal still forbidden (has detach on SA but not on role).
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/roles/{viewer_role_id}"),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
@@ -351,7 +415,7 @@ def test_patch_role_add_write_permissions(
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 6. Dual-attach: SA attach only (no role attach) → forbidden
|
||||
# 6. Dual-attach: SA attach only (no role attach) → assign forbidden
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -361,21 +425,10 @@ def test_attach_with_only_sa_attach_forbidden(
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
role_id = find_role_by_name(signoz, admin_token, SA_FGA_CUSTOM_ROLE_NAME)
|
||||
sa_id = find_service_account_by_name(signoz, admin_token, SA_FGA_TARGET_SA_NAME)["id"]
|
||||
viewer_role_id = find_role_by_name(signoz, admin_token, "signoz-viewer")
|
||||
|
||||
# Grant attach on serviceaccount only.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"attach",
|
||||
additions=[
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
# SA attach already granted from previous test; role attach not yet granted.
|
||||
custom_token = get_token(SA_FGA_CUSTOM_USER_EMAIL, SA_FGA_CUSTOM_USER_PASSWORD)
|
||||
|
||||
# Assign role — forbidden (has SA attach, missing role attach).
|
||||
@@ -387,17 +440,36 @@ def test_attach_with_only_sa_attach_forbidden(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"assign with only SA attach: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Remove role — forbidden (CheckAll: role attach group fails).
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 7. Dual-detach: SA detach only (no role detach) → remove forbidden
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_detach_with_only_sa_detach_forbidden(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
sa_id = find_service_account_by_name(signoz, admin_token, SA_FGA_TARGET_SA_NAME)["id"]
|
||||
viewer_role_id = find_role_by_name(signoz, admin_token, "signoz-viewer")
|
||||
|
||||
# SA detach already granted from test_patch_role_add_write_permissions;
|
||||
# role detach not yet granted.
|
||||
custom_token = get_token(SA_FGA_CUSTOM_USER_EMAIL, SA_FGA_CUSTOM_USER_PASSWORD)
|
||||
|
||||
# Remove role — forbidden (has SA detach, missing role detach).
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/roles/{viewer_role_id}"),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"remove with only SA attach: expected 403, got {resp.status_code}: {resp.text}"
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"remove with only SA detach: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 7. Dual-attach: role attach only (no SA attach) → forbidden
|
||||
# 8. Dual-attach: role attach only (no SA attach) → assign forbidden
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -432,21 +504,49 @@ def test_attach_with_only_role_attach_forbidden(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"assign with only role attach: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
# Remove role — forbidden (CheckAll: SA attach group fails).
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 9. Dual-detach: role detach only (no SA detach) → remove forbidden
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_detach_with_only_role_detach_forbidden(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
):
|
||||
admin_token = get_token(USER_ADMIN_EMAIL, USER_ADMIN_PASSWORD)
|
||||
role_id = find_role_by_name(signoz, admin_token, SA_FGA_CUSTOM_ROLE_NAME)
|
||||
sa_id = find_service_account_by_name(signoz, admin_token, SA_FGA_TARGET_SA_NAME)["id"]
|
||||
viewer_role_id = find_role_by_name(signoz, admin_token, "signoz-viewer")
|
||||
|
||||
# Remove SA detach, grant role detach.
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"detach",
|
||||
additions=[object_group("role", "role", ["*"])],
|
||||
deletions=[object_group("serviceaccount", "serviceaccount", ["*"])],
|
||||
)
|
||||
|
||||
custom_token = get_token(SA_FGA_CUSTOM_USER_EMAIL, SA_FGA_CUSTOM_USER_PASSWORD)
|
||||
|
||||
# Remove role — forbidden (SA detach check fails).
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/roles/{viewer_role_id}"),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"remove with only role attach: expected 403, got {resp.status_code}: {resp.text}"
|
||||
assert resp.status_code == HTTPStatus.FORBIDDEN, f"remove with only role detach: expected 403, got {resp.status_code}: {resp.text}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 8. Dual-attach: both SA + role attach → succeeds
|
||||
# 10. Both attach + detach → assign and remove succeed
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
def test_attach_with_both_permissions_succeeds(
|
||||
def test_attach_detach_with_both_permissions_succeeds(
|
||||
signoz: types.SigNoz,
|
||||
create_user_admin: types.Operation, # pylint: disable=unused-argument
|
||||
get_token: Callable[[str, str], str],
|
||||
@@ -455,7 +555,7 @@ def test_attach_with_both_permissions_succeeds(
|
||||
role_id = find_role_by_name(signoz, admin_token, SA_FGA_CUSTOM_ROLE_NAME)
|
||||
sa_id = find_service_account_by_name(signoz, admin_token, SA_FGA_TARGET_SA_NAME)["id"]
|
||||
|
||||
# Add back SA attach (role attach already present from previous test).
|
||||
# Add back SA attach and SA detach (role attach/detach already present from previous tests).
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
@@ -465,6 +565,15 @@ def test_attach_with_both_permissions_succeeds(
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"detach",
|
||||
additions=[
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
custom_token = get_token(SA_FGA_CUSTOM_USER_EMAIL, SA_FGA_CUSTOM_USER_PASSWORD)
|
||||
|
||||
@@ -480,17 +589,17 @@ def test_attach_with_both_permissions_succeeds(
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"assign with both attach: {resp.text}"
|
||||
|
||||
# Remove the editor role — should succeed (CheckAll: both groups pass).
|
||||
# Remove the editor role — should succeed (both SA detach + role detach).
|
||||
resp = requests.delete(
|
||||
signoz.self.host_configs["8080"].get(f"{SERVICE_ACCOUNT_BASE}/{sa_id}/roles/{editor_role_id}"),
|
||||
headers={"Authorization": f"Bearer {custom_token}"},
|
||||
timeout=5,
|
||||
)
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"remove with both attach: {resp.text}"
|
||||
assert resp.status_code == HTTPStatus.NO_CONTENT, f"remove with both detach: {resp.text}"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 9. Revoke read/list → verify access lost
|
||||
# 11. Revoke read/list → verify access lost
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
@@ -514,14 +623,14 @@ def test_remove_read_permissions_revokes_access(
|
||||
],
|
||||
)
|
||||
|
||||
# Revoke list.
|
||||
# Revoke list (now on serviceaccount type directly).
|
||||
patch_role_objects(
|
||||
signoz,
|
||||
admin_token,
|
||||
role_id,
|
||||
"list",
|
||||
deletions=[
|
||||
object_group("metaresources", "serviceaccount", ["*"]),
|
||||
object_group("serviceaccount", "serviceaccount", ["*"]),
|
||||
],
|
||||
)
|
||||
|
||||
@@ -545,7 +654,7 @@ def test_remove_read_permissions_revokes_access(
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# 10. Clean up: delete custom role
|
||||
# 12. Clean up: delete custom role
|
||||
# ---------------------------------------------------------------------------
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user