audit-go/pkg/audit/api/api_telemetry_hub_test.go
Christian Schaible (EXT) f8f6b4e72a Merged PR 946513: feat: Implement functionality to send logs to the telemetry hub
Security-concept-update-needed: false.

JIRA Work Item: [STACKITRMA-931](https://jira.schwarz/browse/STACKITRMA-931)
2026-03-12 12:48:45 +00:00

1507 lines
71 KiB
Go

package api
import (
"context"
"fmt"
"net/url"
"testing"
"time"
"buf.build/go/protovalidate"
auditV1 "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/gen/go/audit/v1"
internalAuditApi "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/internal/audit/api"
pkgAuditCommon "dev.azure.com/schwarzit/schwarzit.stackit-public/audit-go.git/pkg/audit/common"
"github.com/google/uuid"
"github.com/stretchr/testify/assert"
otelLog "go.opentelemetry.io/otel/log"
"go.opentelemetry.io/otel/log/global"
otelSdkLog "go.opentelemetry.io/otel/sdk/log"
"go.opentelemetry.io/otel/trace"
otlpCommonV1 "go.opentelemetry.io/proto/otlp/common/v1"
otlpLogsV1 "go.opentelemetry.io/proto/otlp/logs/v1"
"google.golang.org/protobuf/types/known/structpb"
"google.golang.org/protobuf/types/known/wrapperspb"
)
func Test_TelemetryHubAuditApi(t *testing.T) {
validator, err := protovalidate.New()
assert.NoError(t, err)
api, err := NewTelemetryHubAuditApi(validator, "workerId", "eu01")
assert.NoError(t, err)
t.Run("organization event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
cloudEvent, err := api.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
assert.NotNil(t, cloudEvent)
})
t.Run("folder event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewFolderAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
cloudEvent, err := api.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
assert.NotNil(t, cloudEvent)
})
t.Run("project event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
cloudEvent, err := api.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
assert.NotNil(t, cloudEvent)
})
t.Run("system event", func(t *testing.T) {
event := internalAuditApi.NewSystemAuditEvent(nil)
routableIdentifier := pkgAuditCommon.RoutableSystemIdentifier
cloudEvent, err := api.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PRIVATE, routableIdentifier)
assert.NoError(t, err)
assert.NotNil(t, cloudEvent)
})
t.Run("invalid event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
apiWithInvalidRegion, err := NewTelemetryHubAuditApi(validator, "workerId", "invalid")
assert.NoError(t, err)
cloudEvent, err := apiWithInvalidRegion.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.EqualError(t, err,
"telemetry hub record validation: validation error: cloud.region attribute is required and should be global or eu01, eu02, ...")
assert.Nil(t, cloudEvent)
})
}
func Test_TelemetryHubAuditSendApi(t *testing.T) {
validator, err := protovalidate.New()
assert.NoError(t, err)
// audit api
auditApi, err := NewTelemetryHubAuditApi(validator, "workerId", "eu01")
assert.NoError(t, err)
// telemetry hub logger pool
loggerPool, err := NewTelemetryHubLoggerPool(1, NewMockTelemetryHubLoggerOption())
assert.NoError(t, err)
logger := loggerPool.Get().(*MockTelemetryHubLogger)
// audit send api
auditSendApi, err := NewTelemetryAuditSendApi(loggerPool, "workerId", "eu01")
assert.NoError(t, err)
t.Run("organization event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
ctx := context.Background()
cloudEvent, err := auditApi.ValidateAndSerialize(ctx, event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
assert.NoError(t, auditSendApi.Send(ctx, routableIdentifier, cloudEvent))
record := logger.GetRecord()
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "ORGANIZATION"),
newConstraint("stackit.resource.id", objectIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.organization.created"),
newConstraint("stackit.visibility", "PUBLIC"),
)
record.WalkAttributes(attributeConstraints.assertAttributesIfSpecifiedFunc(t))
assert.Empty(t, attributeConstraints.constraintMap)
})
t.Run("folder event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewFolderAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
cloudEvent, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
assert.NoError(t, auditSendApi.Send(context.Background(), routableIdentifier, cloudEvent))
record := logger.GetRecord()
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "FOLDER"),
newConstraint("stackit.resource.id", objectIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.folder.created"),
newConstraint("stackit.visibility", "PUBLIC"),
)
record.WalkAttributes(attributeConstraints.assertAttributesIfSpecifiedFunc(t))
assert.Empty(t, attributeConstraints.constraintMap)
})
t.Run("project event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
cloudEvent, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
assert.NoError(t, auditSendApi.Send(context.Background(), routableIdentifier, cloudEvent))
record := logger.GetRecord()
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "PROJECT"),
newConstraint("stackit.resource.id", objectIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.project.created"),
newConstraint("stackit.visibility", "PUBLIC"),
)
record.WalkAttributes(attributeConstraints.assertAttributesIfSpecifiedFunc(t))
assert.Empty(t, attributeConstraints.constraintMap)
})
t.Run("system event", func(t *testing.T) {
event := internalAuditApi.NewSystemAuditEvent(nil)
routableIdentifier := pkgAuditCommon.RoutableSystemIdentifier
cloudEvent, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PRIVATE, routableIdentifier)
assert.NoError(t, err)
assert.NoError(t, auditSendApi.Send(context.Background(), routableIdentifier, cloudEvent))
record := logger.GetRecord()
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "SYSTEM"),
newConstraint("stackit.resource.id", routableIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.system.changed"),
newConstraint("stackit.visibility", "INTERNAL"),
)
record.WalkAttributes(attributeConstraints.assertAttributesIfSpecifiedFunc(t))
assert.Empty(t, attributeConstraints.constraintMap)
})
t.Run("invalid event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
cloudEvent, err := auditApi.ValidateAndSerialize(context.Background(), event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
sendApiWithInvalidRegion, err := NewTelemetryAuditSendApi(loggerPool, "workerId", "invalid")
assert.NoError(t, err)
assert.EqualError(t, sendApiWithInvalidRegion.Send(context.Background(), routableIdentifier, cloudEvent),
"send: validation error: cloud.region attribute is required and should be global or eu01, eu02, ...")
})
t.Run("cloud event nil", func(t *testing.T) {
assert.ErrorIs(t,
auditSendApi.Send(context.Background(), pkgAuditCommon.RoutableSystemIdentifier, nil),
pkgAuditCommon.ErrCloudEventNil)
})
t.Run("object identifier nil", func(t *testing.T) {
assert.ErrorIs(t,
auditSendApi.Send(context.Background(), nil, &pkgAuditCommon.CloudEvent{}),
pkgAuditCommon.ErrObjectIdentifierNil)
})
t.Run("unsupported routable type", func(t *testing.T) {
assert.ErrorIs(t,
auditSendApi.Send(context.Background(), &pkgAuditCommon.RoutableIdentifier{Identifier: uuid.NewString(), Type: "INVALID"}, &pkgAuditCommon.CloudEvent{}),
pkgAuditCommon.ErrUnsupportedRoutableType)
})
t.Run("unsupported data type type", func(t *testing.T) {
assert.NoError(t, auditSendApi.Send(
context.Background(),
pkgAuditCommon.RoutableSystemIdentifier,
&pkgAuditCommon.CloudEvent{DataType: pkgAuditCommon.DataTypeLegacyAuditEventV1}),
)
})
}
func Test_InitLoggerProvider(t *testing.T) {
loggerProvider := global.GetLoggerProvider()
defer global.SetLoggerProvider(loggerProvider)
validator, err := protovalidate.New()
assert.NoError(t, err)
t.Run("valid", func(t *testing.T) {
assert.NoError(t, InitLoggerProvider(context.Background(), "localhost:8080", "token", "resource-manager", validator))
})
t.Run("invalid hub url", func(t *testing.T) {
assert.EqualError(t,
InitLoggerProvider(context.Background(), "otlp://localhost:8080", "token", "resource-manager", validator),
"failed to create OTLP log exporter: parse \"https://otlp:%2F%2Flocalhost:8080/v1/logs\": invalid URL escape \"%2F\"",
)
})
}
func Test_convertVisibility(t *testing.T) {
t.Run("visibility public", func(t *testing.T) {
event := auditV1.RoutableAuditEvent{Visibility: auditV1.Visibility_VISIBILITY_PUBLIC}
assert.Equal(t, visibilityPublic, convertVisibility(&event))
})
t.Run("visibility private", func(t *testing.T) {
event := auditV1.RoutableAuditEvent{Visibility: auditV1.Visibility_VISIBILITY_PRIVATE}
assert.Equal(t, visibilityInternal, convertVisibility(&event))
})
t.Run("visibility unspecified", func(t *testing.T) {
event := auditV1.RoutableAuditEvent{Visibility: auditV1.Visibility_VISIBILITY_UNSPECIFIED}
assert.Equal(t, visibilityInternal, convertVisibility(&event))
})
}
func Test_convertSeverity(t *testing.T) {
t.Run("severity debug", func(t *testing.T) {
event := auditV1.AuditLogEntry{Severity: auditV1.LogSeverity_LOG_SEVERITY_DEBUG}
severity, severityText, err := convertSeverity(&event)
assert.NoError(t, err)
assert.Equal(t, otelLog.SeverityDebug, severity)
assert.Equal(t, severityTextDebug, severityText)
})
t.Run("severity info", func(t *testing.T) {
event := auditV1.AuditLogEntry{Severity: auditV1.LogSeverity_LOG_SEVERITY_INFO}
severity, severityText, err := convertSeverity(&event)
assert.NoError(t, err)
assert.Equal(t, otelLog.SeverityInfo, severity)
assert.Equal(t, severityTextInfo, severityText)
})
t.Run("severity notice", func(t *testing.T) {
event := auditV1.AuditLogEntry{Severity: auditV1.LogSeverity_LOG_SEVERITY_NOTICE}
severity, severityText, err := convertSeverity(&event)
assert.NoError(t, err)
assert.Equal(t, otelLog.SeverityInfo, severity)
assert.Equal(t, severityTextInfo, severityText)
})
t.Run("severity default", func(t *testing.T) {
event := auditV1.AuditLogEntry{Severity: auditV1.LogSeverity_LOG_SEVERITY_DEFAULT}
severity, severityText, err := convertSeverity(&event)
assert.NoError(t, err)
assert.Equal(t, otelLog.SeverityInfo, severity)
assert.Equal(t, severityTextInfo, severityText)
})
t.Run("severity warn", func(t *testing.T) {
event := auditV1.AuditLogEntry{Severity: auditV1.LogSeverity_LOG_SEVERITY_WARNING}
severity, severityText, err := convertSeverity(&event)
assert.NoError(t, err)
assert.Equal(t, otelLog.SeverityWarn, severity)
assert.Equal(t, severityTextWarn, severityText)
})
t.Run("severity error", func(t *testing.T) {
event := auditV1.AuditLogEntry{Severity: auditV1.LogSeverity_LOG_SEVERITY_ERROR}
severity, severityText, err := convertSeverity(&event)
assert.NoError(t, err)
assert.Equal(t, otelLog.SeverityError, severity)
assert.Equal(t, severityTextError, severityText)
})
t.Run("severity critical", func(t *testing.T) {
event := auditV1.AuditLogEntry{Severity: auditV1.LogSeverity_LOG_SEVERITY_CRITICAL}
severity, severityText, err := convertSeverity(&event)
assert.NoError(t, err)
assert.Equal(t, otelLog.SeverityFatal, severity)
assert.Equal(t, severityTextFatal, severityText)
})
t.Run("severity alert", func(t *testing.T) {
event := auditV1.AuditLogEntry{Severity: auditV1.LogSeverity_LOG_SEVERITY_ALERT}
severity, severityText, err := convertSeverity(&event)
assert.NoError(t, err)
assert.Equal(t, otelLog.SeverityFatal, severity)
assert.Equal(t, severityTextFatal, severityText)
})
t.Run("severity emergency", func(t *testing.T) {
event := auditV1.AuditLogEntry{Severity: auditV1.LogSeverity_LOG_SEVERITY_EMERGENCY}
severity, severityText, err := convertSeverity(&event)
assert.NoError(t, err)
assert.Equal(t, otelLog.SeverityFatal, severity)
assert.Equal(t, severityTextFatal, severityText)
})
t.Run("severity unknown", func(t *testing.T) {
event := auditV1.AuditLogEntry{Severity: auditV1.LogSeverity(10000)}
severity, severityText, err := convertSeverity(&event)
assert.EqualError(t, err, "unsupported audit log entry severity: 10000")
assert.Equal(t, otelLog.Severity(0), severity)
assert.Equal(t, "", severityText)
})
}
func Test_convertRequestMethod(t *testing.T) {
newEvent := func(method auditV1.AttributeContext_HttpMethod) auditV1.AuditLogEntry {
return auditV1.AuditLogEntry{
ProtoPayload: &auditV1.AuditLog{
RequestMetadata: &auditV1.RequestMetadata{
RequestAttributes: &auditV1.AttributeContext_Request{
Method: method,
}}}}
}
t.Run("request method get", func(t *testing.T) {
event := newEvent(auditV1.AttributeContext_HTTP_METHOD_GET)
assert.Equal(t, requestMethodGet, convertRequestMethod(&event))
})
t.Run("request method head", func(t *testing.T) {
event := newEvent(auditV1.AttributeContext_HTTP_METHOD_HEAD)
assert.Equal(t, requestMethodHead, convertRequestMethod(&event))
})
t.Run("request method post", func(t *testing.T) {
event := newEvent(auditV1.AttributeContext_HTTP_METHOD_POST)
assert.Equal(t, requestMethodPost, convertRequestMethod(&event))
})
t.Run("request method put", func(t *testing.T) {
event := newEvent(auditV1.AttributeContext_HTTP_METHOD_PUT)
assert.Equal(t, requestMethodPut, convertRequestMethod(&event))
})
t.Run("request method delete", func(t *testing.T) {
event := newEvent(auditV1.AttributeContext_HTTP_METHOD_DELETE)
assert.Equal(t, requestMethodDelete, convertRequestMethod(&event))
})
t.Run("request method connect", func(t *testing.T) {
event := newEvent(auditV1.AttributeContext_HTTP_METHOD_CONNECT)
assert.Equal(t, requestMethodConnect, convertRequestMethod(&event))
})
t.Run("request method options", func(t *testing.T) {
event := newEvent(auditV1.AttributeContext_HTTP_METHOD_OPTIONS)
assert.Equal(t, requestMethodOptions, convertRequestMethod(&event))
})
t.Run("request method trace", func(t *testing.T) {
event := newEvent(auditV1.AttributeContext_HTTP_METHOD_TRACE)
assert.Equal(t, requestMethodTrace, convertRequestMethod(&event))
})
t.Run("request method patch", func(t *testing.T) {
event := newEvent(auditV1.AttributeContext_HTTP_METHOD_PATCH)
assert.Equal(t, requestMethodPatch, convertRequestMethod(&event))
})
t.Run("request method other", func(t *testing.T) {
event := newEvent(auditV1.AttributeContext_HTTP_METHOD_OTHER)
assert.Equal(t, requestMethodOther, convertRequestMethod(&event))
})
t.Run("request method unspecified", func(t *testing.T) {
event := newEvent(auditV1.AttributeContext_HTTP_METHOD_UNSPECIFIED)
assert.Equal(t, requestMethodUnspecified, convertRequestMethod(&event))
})
}
func Test_IdFromRequestAttributes(t *testing.T) {
t.Run("id from value", func(t *testing.T) {
rawId := "d2bcd43e-8418-462b-89a5-64db1af4aca1/1"
result := IdFromRequestAttributes(&auditV1.AuditLogEntry{ProtoPayload: &auditV1.AuditLog{
RequestMetadata: &auditV1.RequestMetadata{
RequestAttributes: &auditV1.AttributeContext_Request{
Id: &rawId,
},
},
}})
assert.Equal(t, "d2bcd43e-8418-462b-89a5-64db1af4aca1", result)
_, err := uuid.Parse(result)
assert.NoError(t, err)
})
t.Run("invalid format", func(t *testing.T) {
rawId := "d2bcd43e-8418-462b-89a5-64db1af4aca1:1"
result := IdFromRequestAttributes(&auditV1.AuditLogEntry{ProtoPayload: &auditV1.AuditLog{
RequestMetadata: &auditV1.RequestMetadata{
RequestAttributes: &auditV1.AttributeContext_Request{
Id: &rawId,
},
},
}})
assert.NotEqual(t, "d2bcd43e-8418-462b-89a5-64db1af4aca1", result)
_, err := uuid.Parse(result)
assert.NoError(t, err)
})
t.Run("empty", func(t *testing.T) {
rawId := ""
result := IdFromRequestAttributes(&auditV1.AuditLogEntry{ProtoPayload: &auditV1.AuditLog{
RequestMetadata: &auditV1.RequestMetadata{
RequestAttributes: &auditV1.AttributeContext_Request{
Id: &rawId,
},
},
}})
assert.NotEqual(t, "", result)
_, err := uuid.Parse(result)
assert.NoError(t, err)
})
t.Run("not set", func(t *testing.T) {
result := IdFromRequestAttributes(&auditV1.AuditLogEntry{ProtoPayload: &auditV1.AuditLog{
RequestMetadata: &auditV1.RequestMetadata{
RequestAttributes: &auditV1.AttributeContext_Request{},
},
}})
assert.NotEqual(t, "", result)
_, err := uuid.Parse(result)
assert.NoError(t, err)
})
}
func Test_convertToOpenTelemetryLogFormat(t *testing.T) {
validator, err := protovalidate.New()
assert.NoError(t, err)
validationProcessor := NewValidationProcessor(validator)
t.Run("public organization event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
assert.Equal(t, event.Timestamp.AsTime(), record.Timestamp())
assert.Equal(t, event.Timestamp.AsTime(), record.ObservedTimestamp())
assert.Equal(t, otelLog.StringValue(event.ProtoPayload.OperationName), record.Body())
assert.Equal(t, otelLog.SeverityInfo, record.Severity())
assert.Equal(t, severityTextInfo, record.SeverityText())
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "ORGANIZATION"),
newConstraint("stackit.resource.id", objectIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.organization.created"),
newConstraint("stackit.visibility", "PUBLIC"),
newConstraint("stackit.initiator", event.ProtoPayload.AuthenticationInfo.PrincipalId),
newConstraint("user_agent.original", event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent),
newConstraint("http.request.method", convertRequestMethod(event)),
newConstraint("client.address", event.ProtoPayload.RequestMetadata.CallerIp),
newConstraint("server.address", event.ProtoPayload.RequestMetadata.RequestAttributes.Host),
newConstraint("url.path", event.ProtoPayload.RequestMetadata.RequestAttributes.Path),
newConstraint("stackit.response.status", "200"),
newConstraint("logname", event.LogName),
newConstraint("insertid", event.InsertId),
newConstraint("correlationid", *event.CorrelationId),
newConstraint("stackit.event.time", event.Timestamp.AsTime().Format(time.RFC3339)),
newConstraint("stackit.request.time", event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.response.time", event.ProtoPayload.ResponseMetadata.ResponseAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.initiator.email", *event.ProtoPayload.AuthenticationInfo.PrincipalEmail),
newConstraint("stackit.request.body", "{}"),
).WithSliceConstraints(labelsAsConstraints(event.Labels)...)
record.WalkAttributes(attributeConstraints.assertAttributesFunc(t))
attributeConstraints.assertAllAttributesMatched(t)
assert.NoError(t, validationProcessor.OnEmit(context.Background(), convertLogRecordToSdkRecord(record, trace.SpanFromContext(context.Background()))))
})
t.Run("private organization event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
assert.Equal(t, event.Timestamp.AsTime(), record.Timestamp())
assert.Equal(t, event.Timestamp.AsTime(), record.ObservedTimestamp())
assert.Equal(t, otelLog.StringValue(event.ProtoPayload.OperationName), record.Body())
assert.Equal(t, otelLog.SeverityInfo, record.Severity())
assert.Equal(t, severityTextInfo, record.SeverityText())
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "ORGANIZATION"),
newConstraint("stackit.resource.id", objectIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.organization.created"),
newConstraint("stackit.visibility", "INTERNAL"),
newConstraint("stackit.initiator", event.ProtoPayload.AuthenticationInfo.PrincipalId),
newConstraint("user_agent.original", event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent),
newConstraint("http.request.method", convertRequestMethod(event)),
newConstraint("client.address", event.ProtoPayload.RequestMetadata.CallerIp),
newConstraint("server.address", event.ProtoPayload.RequestMetadata.RequestAttributes.Host),
newConstraint("url.path", event.ProtoPayload.RequestMetadata.RequestAttributes.Path),
newConstraint("stackit.response.status", "200"),
newConstraint("logname", event.LogName),
newConstraint("insertid", event.InsertId),
newConstraint("correlationid", *event.CorrelationId),
newConstraint("stackit.event.time", event.Timestamp.AsTime().Format(time.RFC3339)),
newConstraint("stackit.request.time", event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.response.time", event.ProtoPayload.ResponseMetadata.ResponseAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.initiator.email", *event.ProtoPayload.AuthenticationInfo.PrincipalEmail),
newConstraint("stackit.request.body", "{}"),
).WithSliceConstraints(labelsAsConstraints(event.Labels)...)
record.WalkAttributes(attributeConstraints.assertAttributesFunc(t))
attributeConstraints.assertAllAttributesMatched(t)
assert.NoError(t, validationProcessor.OnEmit(context.Background(), convertLogRecordToSdkRecord(record, trace.SpanFromContext(context.Background()))))
})
t.Run("public folder event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewFolderAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
assert.Equal(t, event.Timestamp.AsTime(), record.Timestamp())
assert.Equal(t, event.Timestamp.AsTime(), record.ObservedTimestamp())
assert.Equal(t, otelLog.StringValue(event.ProtoPayload.OperationName), record.Body())
assert.Equal(t, otelLog.SeverityInfo, record.Severity())
assert.Equal(t, severityTextInfo, record.SeverityText())
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "FOLDER"),
newConstraint("stackit.resource.id", objectIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.folder.created"),
newConstraint("stackit.visibility", "PUBLIC"),
newConstraint("stackit.initiator", event.ProtoPayload.AuthenticationInfo.PrincipalId),
newConstraint("user_agent.original", event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent),
newConstraint("http.request.method", convertRequestMethod(event)),
newConstraint("client.address", event.ProtoPayload.RequestMetadata.CallerIp),
newConstraint("server.address", event.ProtoPayload.RequestMetadata.RequestAttributes.Host),
newConstraint("url.path", event.ProtoPayload.RequestMetadata.RequestAttributes.Path),
newConstraint("stackit.response.status", "200"),
newConstraint("logname", event.LogName),
newConstraint("insertid", event.InsertId),
newConstraint("correlationid", *event.CorrelationId),
newConstraint("stackit.event.time", event.Timestamp.AsTime().Format(time.RFC3339)),
newConstraint("stackit.request.time", event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.response.time", event.ProtoPayload.ResponseMetadata.ResponseAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.initiator.email", *event.ProtoPayload.AuthenticationInfo.PrincipalEmail),
newConstraint("stackit.request.body", "{}"),
).WithSliceConstraints(labelsAsConstraints(event.Labels)...)
record.WalkAttributes(attributeConstraints.assertAttributesFunc(t))
attributeConstraints.assertAllAttributesMatched(t)
assert.NoError(t, validationProcessor.OnEmit(context.Background(), convertLogRecordToSdkRecord(record, trace.SpanFromContext(context.Background()))))
})
t.Run("private folder event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewFolderAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
assert.Equal(t, event.Timestamp.AsTime(), record.Timestamp())
assert.Equal(t, event.Timestamp.AsTime(), record.ObservedTimestamp())
assert.Equal(t, otelLog.StringValue(event.ProtoPayload.OperationName), record.Body())
assert.Equal(t, otelLog.SeverityInfo, record.Severity())
assert.Equal(t, severityTextInfo, record.SeverityText())
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "FOLDER"),
newConstraint("stackit.resource.id", objectIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.folder.created"),
newConstraint("stackit.visibility", "INTERNAL"),
newConstraint("stackit.initiator", event.ProtoPayload.AuthenticationInfo.PrincipalId),
newConstraint("user_agent.original", event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent),
newConstraint("http.request.method", convertRequestMethod(event)),
newConstraint("client.address", event.ProtoPayload.RequestMetadata.CallerIp),
newConstraint("server.address", event.ProtoPayload.RequestMetadata.RequestAttributes.Host),
newConstraint("url.path", event.ProtoPayload.RequestMetadata.RequestAttributes.Path),
newConstraint("stackit.response.status", "200"),
newConstraint("logname", event.LogName),
newConstraint("insertid", event.InsertId),
newConstraint("correlationid", *event.CorrelationId),
newConstraint("stackit.event.time", event.Timestamp.AsTime().Format(time.RFC3339)),
newConstraint("stackit.request.time", event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.response.time", event.ProtoPayload.ResponseMetadata.ResponseAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.initiator.email", *event.ProtoPayload.AuthenticationInfo.PrincipalEmail),
newConstraint("stackit.request.body", "{}"),
).WithSliceConstraints(labelsAsConstraints(event.Labels)...)
record.WalkAttributes(attributeConstraints.assertAttributesFunc(t))
attributeConstraints.assertAllAttributesMatched(t)
assert.NoError(t, validationProcessor.OnEmit(context.Background(), convertLogRecordToSdkRecord(record, trace.SpanFromContext(context.Background()))))
})
t.Run("public project event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
assert.Equal(t, event.Timestamp.AsTime(), record.Timestamp())
assert.Equal(t, event.Timestamp.AsTime(), record.ObservedTimestamp())
assert.Equal(t, otelLog.StringValue(event.ProtoPayload.OperationName), record.Body())
assert.Equal(t, otelLog.SeverityInfo, record.Severity())
assert.Equal(t, severityTextInfo, record.SeverityText())
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "PROJECT"),
newConstraint("stackit.resource.id", objectIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.project.created"),
newConstraint("stackit.visibility", "PUBLIC"),
newConstraint("stackit.initiator", event.ProtoPayload.AuthenticationInfo.PrincipalId),
newConstraint("user_agent.original", event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent),
newConstraint("http.request.method", convertRequestMethod(event)),
newConstraint("client.address", event.ProtoPayload.RequestMetadata.CallerIp),
newConstraint("server.address", event.ProtoPayload.RequestMetadata.RequestAttributes.Host),
newConstraint("url.path", event.ProtoPayload.RequestMetadata.RequestAttributes.Path),
newConstraint("stackit.response.status", "200"),
newConstraint("logname", event.LogName),
newConstraint("insertid", event.InsertId),
newConstraint("correlationid", *event.CorrelationId),
newConstraint("stackit.event.time", event.Timestamp.AsTime().Format(time.RFC3339)),
newConstraint("stackit.request.time", event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.response.time", event.ProtoPayload.ResponseMetadata.ResponseAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.initiator.email", *event.ProtoPayload.AuthenticationInfo.PrincipalEmail),
newConstraint("stackit.request.body", "{}"),
).WithSliceConstraints(labelsAsConstraints(event.Labels)...)
record.WalkAttributes(attributeConstraints.assertAttributesFunc(t))
attributeConstraints.assertAllAttributesMatched(t)
assert.NoError(t, validationProcessor.OnEmit(context.Background(), convertLogRecordToSdkRecord(record, trace.SpanFromContext(context.Background()))))
})
t.Run("private project event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
assert.Equal(t, event.Timestamp.AsTime(), record.Timestamp())
assert.Equal(t, event.Timestamp.AsTime(), record.ObservedTimestamp())
assert.Equal(t, otelLog.StringValue(event.ProtoPayload.OperationName), record.Body())
assert.Equal(t, otelLog.SeverityInfo, record.Severity())
assert.Equal(t, severityTextInfo, record.SeverityText())
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "PROJECT"),
newConstraint("stackit.resource.id", objectIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.project.created"),
newConstraint("stackit.visibility", "INTERNAL"),
newConstraint("stackit.initiator", event.ProtoPayload.AuthenticationInfo.PrincipalId),
newConstraint("user_agent.original", event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent),
newConstraint("http.request.method", convertRequestMethod(event)),
newConstraint("client.address", event.ProtoPayload.RequestMetadata.CallerIp),
newConstraint("server.address", event.ProtoPayload.RequestMetadata.RequestAttributes.Host),
newConstraint("url.path", event.ProtoPayload.RequestMetadata.RequestAttributes.Path),
newConstraint("stackit.response.status", "200"),
newConstraint("logname", event.LogName),
newConstraint("insertid", event.InsertId),
newConstraint("correlationid", *event.CorrelationId),
newConstraint("stackit.event.time", event.Timestamp.AsTime().Format(time.RFC3339)),
newConstraint("stackit.request.time", event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.response.time", event.ProtoPayload.ResponseMetadata.ResponseAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.initiator.email", *event.ProtoPayload.AuthenticationInfo.PrincipalEmail),
newConstraint("stackit.request.body", "{}"),
).WithSliceConstraints(labelsAsConstraints(event.Labels)...)
record.WalkAttributes(attributeConstraints.assertAttributesFunc(t))
attributeConstraints.assertAllAttributesMatched(t)
assert.NoError(t, validationProcessor.OnEmit(context.Background(), convertLogRecordToSdkRecord(record, trace.SpanFromContext(context.Background()))))
})
t.Run("private project system event", func(t *testing.T) {
event := internalAuditApi.NewProjectSystemAuditEvent(nil)
routableIdentifier := pkgAuditCommon.RoutableSystemIdentifier
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
assert.Equal(t, event.Timestamp.AsTime(), record.Timestamp())
assert.Equal(t, event.Timestamp.AsTime(), record.ObservedTimestamp())
assert.Equal(t, otelLog.StringValue(event.ProtoPayload.OperationName), record.Body())
assert.Equal(t, otelLog.SeverityInfo, record.Severity())
assert.Equal(t, severityTextInfo, record.SeverityText())
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "SYSTEM"),
newConstraint("stackit.resource.id", routableIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.system.changed"),
newConstraint("stackit.visibility", "INTERNAL"),
newConstraint("stackit.initiator", event.ProtoPayload.AuthenticationInfo.PrincipalId),
newConstraint("user_agent.original", event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent),
newConstraint("http.request.method", convertRequestMethod(event)),
newConstraint("client.address", event.ProtoPayload.RequestMetadata.CallerIp),
newConstraint("server.address", event.ProtoPayload.RequestMetadata.RequestAttributes.Host),
newConstraint("url.path", event.ProtoPayload.RequestMetadata.RequestAttributes.Path),
newConstraint("stackit.response.status", "200"),
newConstraint("logname", event.LogName),
newConstraint("insertid", event.InsertId),
newConstraint("correlationid", *event.CorrelationId),
newConstraint("stackit.event.time", event.Timestamp.AsTime().Format(time.RFC3339)),
newConstraint("stackit.request.time", event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.response.time", event.ProtoPayload.ResponseMetadata.ResponseAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.initiator.email", *event.ProtoPayload.AuthenticationInfo.PrincipalEmail),
newConstraint("stackit.initiator.serviceaccountname", *event.ProtoPayload.AuthenticationInfo.ServiceAccountName),
newConstraint("stackit.initiator.serviceaccount0.id", "system"),
newConstraint("stackit.request.body", "{}"),
).WithSliceConstraints(labelsAsConstraints(event.Labels)...)
record.WalkAttributes(attributeConstraints.assertAttributesFunc(t))
attributeConstraints.assertAllAttributesMatched(t)
assert.NoError(t, validationProcessor.OnEmit(context.Background(), convertLogRecordToSdkRecord(record, trace.SpanFromContext(context.Background()))))
})
t.Run("private system event", func(t *testing.T) {
event := internalAuditApi.NewSystemAuditEvent(nil)
routableIdentifier := pkgAuditCommon.RoutableSystemIdentifier
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
assert.Equal(t, event.Timestamp.AsTime(), record.Timestamp())
assert.Equal(t, event.Timestamp.AsTime(), record.ObservedTimestamp())
assert.Equal(t, otelLog.StringValue(event.ProtoPayload.OperationName), record.Body())
assert.Equal(t, otelLog.SeverityInfo, record.Severity())
assert.Equal(t, severityTextInfo, record.SeverityText())
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "SYSTEM"),
newConstraint("stackit.resource.id", routableIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.system.changed"),
newConstraint("stackit.visibility", "INTERNAL"),
newConstraint("stackit.initiator", event.ProtoPayload.AuthenticationInfo.PrincipalId),
newConstraint("user_agent.original", event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent),
newConstraint("http.request.method", convertRequestMethod(event)),
newConstraint("client.address", event.ProtoPayload.RequestMetadata.CallerIp),
newConstraint("server.address", event.ProtoPayload.RequestMetadata.RequestAttributes.Host),
newConstraint("url.path", event.ProtoPayload.RequestMetadata.RequestAttributes.Path),
newConstraint("stackit.response.status", "200"),
newConstraint("logname", event.LogName),
newConstraint("insertid", event.InsertId),
newConstraint("correlationid", *event.CorrelationId),
newConstraint("stackit.event.time", event.Timestamp.AsTime().Format(time.RFC3339)),
newConstraint("stackit.request.time", event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.response.time", event.ProtoPayload.ResponseMetadata.ResponseAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.initiator.email", *event.ProtoPayload.AuthenticationInfo.PrincipalEmail),
newConstraint("stackit.initiator.serviceaccountname", *event.ProtoPayload.AuthenticationInfo.ServiceAccountName),
newConstraint("stackit.initiator.serviceaccount0.id", "system"),
newConstraint("stackit.request.body", "{}"),
).WithSliceConstraints(labelsAsConstraints(event.Labels)...)
record.WalkAttributes(attributeConstraints.assertAttributesFunc(t))
attributeConstraints.assertAllAttributesMatched(t)
assert.NoError(t, validationProcessor.OnEmit(context.Background(), convertLogRecordToSdkRecord(record, trace.SpanFromContext(context.Background()))))
})
t.Run("private system event with service account delegation", func(t *testing.T) {
serviceAccountPrincipalId := uuid.NewString()
serviceAccountPrincipalEmail := "test@example.com"
updateServiceAccount := func(e *auditV1.AuditLogEntry) {
e.ProtoPayload.AuthenticationInfo.ServiceAccountDelegationInfo[0].Authority =
&auditV1.ServiceAccountDelegationInfo_IdpPrincipal_{
IdpPrincipal: &auditV1.ServiceAccountDelegationInfo_IdpPrincipal{
PrincipalId: serviceAccountPrincipalId, PrincipalEmail: serviceAccountPrincipalEmail,
},
}
}
event := internalAuditApi.NewSystemAuditEvent(&updateServiceAccount)
routableIdentifier := pkgAuditCommon.RoutableSystemIdentifier
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PRIVATE, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
assert.Equal(t, event.Timestamp.AsTime(), record.Timestamp())
assert.Equal(t, event.Timestamp.AsTime(), record.ObservedTimestamp())
assert.Equal(t, otelLog.StringValue(event.ProtoPayload.OperationName), record.Body())
assert.Equal(t, otelLog.SeverityInfo, record.Severity())
assert.Equal(t, severityTextInfo, record.SeverityText())
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "SYSTEM"),
newConstraint("stackit.resource.id", routableIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.system.changed"),
newConstraint("stackit.visibility", "INTERNAL"),
newConstraint("stackit.initiator", event.ProtoPayload.AuthenticationInfo.PrincipalId),
newConstraint("user_agent.original", event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent),
newConstraint("http.request.method", convertRequestMethod(event)),
newConstraint("client.address", event.ProtoPayload.RequestMetadata.CallerIp),
newConstraint("server.address", event.ProtoPayload.RequestMetadata.RequestAttributes.Host),
newConstraint("url.path", event.ProtoPayload.RequestMetadata.RequestAttributes.Path),
newConstraint("stackit.response.status", "200"),
newConstraint("logname", event.LogName),
newConstraint("insertid", event.InsertId),
newConstraint("correlationid", *event.CorrelationId),
newConstraint("stackit.event.time", event.Timestamp.AsTime().Format(time.RFC3339)),
newConstraint("stackit.request.time", event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.response.time", event.ProtoPayload.ResponseMetadata.ResponseAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.initiator.email", *event.ProtoPayload.AuthenticationInfo.PrincipalEmail),
newConstraint("stackit.initiator.serviceaccountname", *event.ProtoPayload.AuthenticationInfo.ServiceAccountName),
newConstraint("stackit.initiator.serviceaccount0.id", serviceAccountPrincipalId),
newConstraint("stackit.initiator.serviceaccount0.email", serviceAccountPrincipalEmail),
newConstraint("stackit.request.body", "{}"),
).WithSliceConstraints(labelsAsConstraints(event.Labels)...)
record.WalkAttributes(attributeConstraints.assertAttributesFunc(t))
attributeConstraints.assertAllAttributesMatched(t)
assert.NoError(t, validationProcessor.OnEmit(context.Background(), convertLogRecordToSdkRecord(record, trace.SpanFromContext(context.Background()))))
})
t.Run("with query params", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil)
escapedQuery := url.QueryEscape("param=value")
event.ProtoPayload.RequestMetadata.RequestAttributes.Query = &escapedQuery
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
assert.Equal(t, event.Timestamp.AsTime(), record.Timestamp())
assert.Equal(t, event.Timestamp.AsTime(), record.ObservedTimestamp())
assert.Equal(t, otelLog.StringValue(event.ProtoPayload.OperationName), record.Body())
assert.Equal(t, otelLog.SeverityInfo, record.Severity())
assert.Equal(t, severityTextInfo, record.SeverityText())
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "ORGANIZATION"),
newConstraint("stackit.resource.id", objectIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.organization.created"),
newConstraint("stackit.visibility", "PUBLIC"),
newConstraint("stackit.initiator", event.ProtoPayload.AuthenticationInfo.PrincipalId),
newConstraint("user_agent.original", event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent),
newConstraint("http.request.method", convertRequestMethod(event)),
newConstraint("client.address", event.ProtoPayload.RequestMetadata.CallerIp),
newConstraint("server.address", event.ProtoPayload.RequestMetadata.RequestAttributes.Host),
newConstraint("url.path", event.ProtoPayload.RequestMetadata.RequestAttributes.Path),
newConstraint("url.query", "param%3Dvalue"),
newConstraint("stackit.response.status", "200"),
newConstraint("logname", event.LogName),
newConstraint("insertid", event.InsertId),
newConstraint("correlationid", *event.CorrelationId),
newConstraint("stackit.event.time", event.Timestamp.AsTime().Format(time.RFC3339)),
newConstraint("stackit.request.time", event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.response.time", event.ProtoPayload.ResponseMetadata.ResponseAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.initiator.email", *event.ProtoPayload.AuthenticationInfo.PrincipalEmail),
newConstraint("stackit.request.body", "{}"),
).WithSliceConstraints(labelsAsConstraints(event.Labels)...)
record.WalkAttributes(attributeConstraints.assertAttributesFunc(t))
attributeConstraints.assertAllAttributesMatched(t)
assert.NoError(t, validationProcessor.OnEmit(context.Background(), convertLogRecordToSdkRecord(record, trace.SpanFromContext(context.Background()))))
})
t.Run("with request body", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
request := map[string]any{"key": "value"}
requestStruct, err := structpb.NewStruct(request)
assert.NoError(t, err)
event.ProtoPayload.Request = requestStruct
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
assert.Equal(t, event.Timestamp.AsTime(), record.Timestamp())
assert.Equal(t, event.Timestamp.AsTime(), record.ObservedTimestamp())
assert.Equal(t, otelLog.StringValue(event.ProtoPayload.OperationName), record.Body())
assert.Equal(t, otelLog.SeverityInfo, record.Severity())
assert.Equal(t, severityTextInfo, record.SeverityText())
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "ORGANIZATION"),
newConstraint("stackit.resource.id", objectIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.organization.created"),
newConstraint("stackit.visibility", "PUBLIC"),
newConstraint("stackit.initiator", event.ProtoPayload.AuthenticationInfo.PrincipalId),
newConstraint("user_agent.original", event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent),
newConstraint("http.request.method", convertRequestMethod(event)),
newConstraint("client.address", event.ProtoPayload.RequestMetadata.CallerIp),
newConstraint("server.address", event.ProtoPayload.RequestMetadata.RequestAttributes.Host),
newConstraint("url.path", event.ProtoPayload.RequestMetadata.RequestAttributes.Path),
newConstraint("stackit.response.status", "200"),
newConstraint("logname", event.LogName),
newConstraint("insertid", event.InsertId),
newConstraint("correlationid", *event.CorrelationId),
newConstraint("stackit.event.time", event.Timestamp.AsTime().Format(time.RFC3339)),
newConstraint("stackit.request.time", event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.response.time", event.ProtoPayload.ResponseMetadata.ResponseAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.initiator.email", *event.ProtoPayload.AuthenticationInfo.PrincipalEmail),
newConstraint("stackit.request.body", "{\"key\":\"value\"}"),
).WithSliceConstraints(labelsAsConstraints(event.Labels)...)
record.WalkAttributes(attributeConstraints.assertAttributesFunc(t))
attributeConstraints.assertAllAttributesMatched(t)
assert.NoError(t, validationProcessor.OnEmit(context.Background(), convertLogRecordToSdkRecord(record, trace.SpanFromContext(context.Background()))))
})
t.Run("with response body", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil)
event.ProtoPayload.ResponseMetadata.ResponseAttributes.Size = wrapperspb.Int64(1000)
event.ProtoPayload.ResponseMetadata.ResponseAttributes.NumResponseItems = wrapperspb.Int64(1)
response := map[string]any{"key": "value"}
responseStruct, err := structpb.NewStruct(response)
assert.NoError(t, err)
event.ProtoPayload.Response = responseStruct
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
assert.Equal(t, event.Timestamp.AsTime(), record.Timestamp())
assert.Equal(t, event.Timestamp.AsTime(), record.ObservedTimestamp())
assert.Equal(t, otelLog.StringValue(event.ProtoPayload.OperationName), record.Body())
assert.Equal(t, otelLog.SeverityInfo, record.Severity())
assert.Equal(t, severityTextInfo, record.SeverityText())
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "ORGANIZATION"),
newConstraint("stackit.resource.id", objectIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.organization.created"),
newConstraint("stackit.visibility", "PUBLIC"),
newConstraint("stackit.initiator", event.ProtoPayload.AuthenticationInfo.PrincipalId),
newConstraint("user_agent.original", event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent),
newConstraint("http.request.method", convertRequestMethod(event)),
newConstraint("client.address", event.ProtoPayload.RequestMetadata.CallerIp),
newConstraint("server.address", event.ProtoPayload.RequestMetadata.RequestAttributes.Host),
newConstraint("url.path", event.ProtoPayload.RequestMetadata.RequestAttributes.Path),
newConstraint("stackit.response.status", "200"),
newConstraint("logname", event.LogName),
newConstraint("insertid", event.InsertId),
newConstraint("correlationid", *event.CorrelationId),
newConstraint("stackit.event.time", event.Timestamp.AsTime().Format(time.RFC3339)),
newConstraint("stackit.request.time", event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.response.time", event.ProtoPayload.ResponseMetadata.ResponseAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.initiator.email", *event.ProtoPayload.AuthenticationInfo.PrincipalEmail),
newConstraint("stackit.request.body", "{}"),
newConstraint("stackit.response.size", "1000"),
newConstraint("stackit.response.items.count", "1"),
newConstraint("stackit.response.body", "{\"key\":\"value\"}"),
).WithSliceConstraints(labelsAsConstraints(event.Labels)...)
record.WalkAttributes(attributeConstraints.assertAttributesFunc(t))
attributeConstraints.assertAllAttributesMatched(t)
assert.NoError(t, validationProcessor.OnEmit(context.Background(), convertLogRecordToSdkRecord(record, trace.SpanFromContext(context.Background()))))
})
t.Run("with error response", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
responseErrorMessage := "error message"
event.ProtoPayload.ResponseMetadata.ErrorMessage = &responseErrorMessage
errorMap := map[string]any{"key": "value1"}
errorStruct, err := structpb.NewStruct(errorMap)
assert.NoError(t, err)
event.ProtoPayload.ResponseMetadata.ErrorDetails = []*structpb.Struct{errorStruct}
event.ProtoPayload.ResponseMetadata.StatusCode = wrapperspb.Int32(int32(400))
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
assert.Equal(t, event.Timestamp.AsTime(), record.Timestamp())
assert.Equal(t, event.Timestamp.AsTime(), record.ObservedTimestamp())
assert.Equal(t, otelLog.StringValue(event.ProtoPayload.OperationName), record.Body())
assert.Equal(t, otelLog.SeverityInfo, record.Severity())
assert.Equal(t, severityTextInfo, record.SeverityText())
attributeConstraints := NewAttributesConstraints(
newConstraint("service.name", "resource-manager"),
newConstraint("service.instance.id", "workerId"),
newConstraint("cloud.region", "eu01"),
newConstraint("stackit.resource.type", "ORGANIZATION"),
newConstraint("stackit.resource.id", objectIdentifier.Identifier),
newConstraint("stackit.log.id", IdFromRequestAttributes(event)),
newConstraint("stackit.log.type", "AUDIT"),
newConstraint("stackit.action", "resourcemanager.organization.created"),
newConstraint("stackit.visibility", "PUBLIC"),
newConstraint("stackit.initiator", event.ProtoPayload.AuthenticationInfo.PrincipalId),
newConstraint("user_agent.original", event.ProtoPayload.RequestMetadata.CallerSuppliedUserAgent),
newConstraint("http.request.method", convertRequestMethod(event)),
newConstraint("client.address", event.ProtoPayload.RequestMetadata.CallerIp),
newConstraint("server.address", event.ProtoPayload.RequestMetadata.RequestAttributes.Host),
newConstraint("url.path", event.ProtoPayload.RequestMetadata.RequestAttributes.Path),
newConstraint("stackit.response.status", "400"),
newConstraint("logname", event.LogName),
newConstraint("insertid", event.InsertId),
newConstraint("correlationid", *event.CorrelationId),
newConstraint("stackit.event.time", event.Timestamp.AsTime().Format(time.RFC3339)),
newConstraint("stackit.request.time", event.ProtoPayload.RequestMetadata.RequestAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.response.time", event.ProtoPayload.ResponseMetadata.ResponseAttributes.Time.AsTime().Format(time.RFC3339)),
newConstraint("stackit.initiator.email", *event.ProtoPayload.AuthenticationInfo.PrincipalEmail),
newConstraint("stackit.request.body", "{}"),
newConstraint("stackit.response.error.message", *event.ProtoPayload.ResponseMetadata.ErrorMessage),
newConstraint("stackit.response.error.details0", "{\"key\":\"value1\"}"),
).WithSliceConstraints(labelsAsConstraints(event.Labels)...)
record.WalkAttributes(attributeConstraints.assertAttributesFunc(t))
attributeConstraints.assertAllAttributesMatched(t)
assert.NoError(t, validationProcessor.OnEmit(context.Background(), convertLogRecordToSdkRecord(record, trace.SpanFromContext(context.Background()))))
})
}
//nolint:gosec // SeverityNumber is not that large
func Test_convertSdkRecordToLogRecord(t *testing.T) {
// prepare record
record := otelSdkLog.Record{}
initRequiredPrivateRecordFields(&record)
span := trace.SpanFromContext(context.Background())
eventTime := time.Now()
eventObservedTime := time.Now()
record.SetTimestamp(eventTime)
record.SetObservedTimestamp(eventObservedTime)
record.SetSeverity(otelLog.SeverityError)
record.SetSeverityText("ERROR")
record.SetBody(otelLog.StringValue("body"))
record.SetEventName("eventName")
record.SetTraceID(span.SpanContext().TraceID())
record.SetSpanID(span.SpanContext().SpanID())
record.SetTraceFlags(span.SpanContext().TraceFlags())
otlpAttrs := make([]otelLog.KeyValue, 6)
otlpAttrs[0] = otelLog.KeyValue{Key: "key1", Value: otelLog.StringValue("value1")}
otlpAttrs[1] = otelLog.KeyValue{Key: "key2", Value: otelLog.BoolValue(true)}
otlpAttrs[2] = otelLog.KeyValue{Key: "key3", Value: otelLog.IntValue(1000)}
otlpAttrs[3] = otelLog.KeyValue{Key: "key4", Value: otelLog.Float64Value(1.0)}
otlpAttrs[4] = otelLog.KeyValue{Key: "key5", Value: otelLog.BytesValue([]byte("bytes"))}
otlpAttrs[5] = otelLog.KeyValue{Key: "key6", Value: otelLog.SliceValue(
otelLog.StringValue("value1"),
otelLog.StringValue("value2"),
)}
record.AddAttributes(otlpAttrs...)
// convert
convertedRecord := convertSdkRecordToLogRecord(&record, span)
assert.Equal(t, uint64(eventTime.UnixNano()), convertedRecord.TimeUnixNano)
assert.Equal(t, uint64(eventObservedTime.UnixNano()), convertedRecord.ObservedTimeUnixNano)
assert.Equal(t, otlpLogsV1.SeverityNumber(record.Severity()), convertedRecord.SeverityNumber)
assert.Equal(t, record.SeverityText(), convertedRecord.SeverityText)
assert.Equal(t, &otlpCommonV1.AnyValue_StringValue{StringValue: otelLog.StringValue("body").AsString()}, convertedRecord.Body.Value)
assert.Equal(t, record.EventName(), convertedRecord.EventName)
assert.Equal(t, []byte(record.TraceID().String()), convertedRecord.TraceId)
assert.Equal(t, []byte(record.SpanID().String()), convertedRecord.SpanId)
assert.Equal(t, uint32(record.TraceFlags()), convertedRecord.Flags)
assert.Len(t, convertedRecord.Attributes, 6)
assert.Equal(t, otlpAttrs[0].Key, convertedRecord.Attributes[0].Key)
assert.Equal(t, otlpAttrs[0].Value.AsString(), convertedRecord.Attributes[0].Value.GetStringValue())
assert.Equal(t, otlpAttrs[1].Key, convertedRecord.Attributes[1].Key)
assert.Equal(t, otlpAttrs[1].Value.AsBool(), convertedRecord.Attributes[1].Value.GetBoolValue())
assert.Equal(t, otlpAttrs[2].Key, convertedRecord.Attributes[2].Key)
assert.Equal(t, otlpAttrs[2].Value.AsInt64(), convertedRecord.Attributes[2].Value.GetIntValue())
assert.Equal(t, otlpAttrs[3].Key, convertedRecord.Attributes[3].Key)
assert.Equal(t, otlpAttrs[3].Value.AsFloat64(), convertedRecord.Attributes[3].Value.GetDoubleValue())
assert.Equal(t, otlpAttrs[4].Key, convertedRecord.Attributes[4].Key)
assert.Equal(t, otlpAttrs[4].Value.AsBytes(), convertedRecord.Attributes[4].Value.GetBytesValue())
assert.Equal(t, otlpAttrs[5].Key, convertedRecord.Attributes[5].Key)
assert.Len(t, otlpAttrs[5].Value.AsSlice(), 2)
assert.Len(t, (*convertedRecord.Attributes[5].Value.GetArrayValue()).Values, 2)
assert.Equal(t, otlpAttrs[5].Value.AsSlice()[0].AsString(), convertedRecord.Attributes[5].Value.GetArrayValue().Values[0].GetStringValue())
assert.Equal(t, otlpAttrs[5].Value.AsSlice()[1].AsString(), convertedRecord.Attributes[5].Value.GetArrayValue().Values[1].GetStringValue())
}
func Test_ValidationProcessor(t *testing.T) {
validator, err := protovalidate.New()
assert.NoError(t, err)
validationProcessor := NewValidationProcessor(validator)
t.Run("enabled", func(t *testing.T) {
assert.True(t, validationProcessor.Enabled(context.Background(), otelSdkLog.EnabledParameters{}))
})
t.Run("shutdown", func(t *testing.T) {
assert.NoError(t, validationProcessor.Shutdown(context.Background()))
})
t.Run("force flush", func(t *testing.T) {
assert.NoError(t, validationProcessor.ForceFlush(context.Background()))
})
t.Run("on emit", func(t *testing.T) {
validateEvent := func(event *auditV1.AuditLogEntry, objectIdentifier *auditV1.ObjectIdentifier, visibility auditV1.Visibility) {
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, visibility, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
span := trace.SpanFromContext(context.Background())
sdkRecord := convertLogRecordToSdkRecord(record, span)
assert.NoError(t, validationProcessor.OnEmit(context.Background(), sdkRecord))
}
t.Run("public organization event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil)
validateEvent(event, objectIdentifier, auditV1.Visibility_VISIBILITY_PUBLIC)
})
t.Run("private organization event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil)
validateEvent(event, objectIdentifier, auditV1.Visibility_VISIBILITY_PRIVATE)
})
t.Run("public folder event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewFolderAuditEvent(nil)
validateEvent(event, objectIdentifier, auditV1.Visibility_VISIBILITY_PUBLIC)
})
t.Run("private folder event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewFolderAuditEvent(nil)
validateEvent(event, objectIdentifier, auditV1.Visibility_VISIBILITY_PRIVATE)
})
t.Run("public project event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil)
validateEvent(event, objectIdentifier, auditV1.Visibility_VISIBILITY_PUBLIC)
})
t.Run("private project event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil)
validateEvent(event, objectIdentifier, auditV1.Visibility_VISIBILITY_PRIVATE)
})
t.Run("system event", func(t *testing.T) {
event := internalAuditApi.NewSystemAuditEvent(nil)
validateEvent(event, pkgAuditCommon.SystemIdentifier, auditV1.Visibility_VISIBILITY_PRIVATE)
})
t.Run("invalid event", func(t *testing.T) {
event, objectIdentifier := internalAuditApi.NewProjectAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
record, err := convertToOpenTelemetryFormat("workerId", "invalid", event, routableEvent)
assert.NoError(t, err)
span := trace.SpanFromContext(context.Background())
sdkRecord := convertLogRecordToSdkRecord(record, span)
assert.EqualError(t, validationProcessor.OnEmit(context.Background(), sdkRecord),
"validation error: cloud.region attribute is required and should be global or eu01, eu02, ...")
})
})
}
func Test_TelemetryHubLoggerPool(t *testing.T) {
pool, err := NewTelemetryHubLoggerPool(2, NewMockTelemetryHubLoggerOption())
assert.NoError(t, err)
logger := pool.Get()
assert.NotNil(t, logger)
logger2 := pool.Get()
assert.NotNil(t, logger2)
assert.NotEqual(t, logger, logger2)
logger3 := pool.Get()
assert.NotNil(t, logger3)
assert.Equal(t, logger, logger3)
logger4 := pool.Get()
assert.NotNil(t, logger4)
assert.Equal(t, logger2, logger4)
}
func Test_synchronousExporter(t *testing.T) {
validator, err := protovalidate.New()
assert.NoError(t, err)
// prepare exporter
innerExporter := newMockExporter()
exporter := NewSynchronousExporter(innerExporter)
t.Run("export", func(t *testing.T) {
innerExporter.Reset()
// prepare event
event, objectIdentifier := internalAuditApi.NewOrganizationAuditEvent(nil)
routableIdentifier := pkgAuditCommon.NewRoutableIdentifier(objectIdentifier)
routableEvent, err := internalAuditApi.ValidateAndSerializePartially(validator, event, auditV1.Visibility_VISIBILITY_PUBLIC, routableIdentifier)
assert.NoError(t, err)
// convert to record
record, err := convertToOpenTelemetryFormat("workerId", "eu01", event, routableEvent)
assert.NoError(t, err)
span := trace.SpanFromContext(context.Background())
sdkRecord := convertLogRecordToSdkRecord(record, span)
// export
assert.NoError(t, exporter.Export(context.Background(), []otelSdkLog.Record{*sdkRecord}))
// check result
assert.False(t, innerExporter.forceFlushInvoked)
assert.False(t, innerExporter.shutdownInvoked)
records := innerExporter.records
assert.Len(t, records, 1)
assert.Equal(t, *sdkRecord, records[0])
})
t.Run("shutdown", func(t *testing.T) {
innerExporter.Reset()
// invoke
assert.NoError(t, exporter.Shutdown(context.Background()))
// assert
assert.True(t, innerExporter.shutdownInvoked)
assert.False(t, innerExporter.forceFlushInvoked)
})
t.Run("force flush", func(t *testing.T) {
innerExporter.Reset()
// invoke
assert.NoError(t, exporter.ForceFlush(context.Background()))
// assert
assert.True(t, innerExporter.forceFlushInvoked)
assert.False(t, innerExporter.shutdownInvoked)
})
}
type mockExporter struct {
records []otelSdkLog.Record
shutdownInvoked bool
forceFlushInvoked bool
}
var _ otelSdkLog.Exporter = (*mockExporter)(nil)
func newMockExporter() *mockExporter {
return &mockExporter{}
}
func (e *mockExporter) Export(_ context.Context, records []otelSdkLog.Record) error {
e.records = records
return nil
}
func (e *mockExporter) Shutdown(_ context.Context) error {
e.shutdownInvoked = true
return nil
}
func (e *mockExporter) ForceFlush(_ context.Context) error {
e.forceFlushInvoked = true
return nil
}
func (e *mockExporter) Reset() {
e.records = nil
e.shutdownInvoked = false
e.forceFlushInvoked = false
}
type AttributesConstraints struct {
constraintMap map[string]string
}
func NewAttributesConstraints(constraints ...constraint) *AttributesConstraints {
constraintMap := make(map[string]string)
for _, constraint := range constraints {
constraintMap[constraint.key] = constraint.value
}
return &AttributesConstraints{
constraintMap: constraintMap,
}
}
func (c *AttributesConstraints) WithSliceConstraints(constraints ...constraint) *AttributesConstraints {
for _, constraint := range constraints {
c.constraintMap[constraint.key] = constraint.value
}
return c
}
func (c *AttributesConstraints) assertAttributesIfSpecifiedFunc(t *testing.T) func(value otelLog.KeyValue) bool {
return func(value otelLog.KeyValue) bool {
expectedValue, hasKey := c.constraintMap[value.Key]
if hasKey {
assert.Equal(t, expectedValue, value.Value.String())
delete(c.constraintMap, value.Key)
}
return true
}
}
func (c *AttributesConstraints) assertAttributesFunc(t *testing.T) func(value otelLog.KeyValue) bool {
return func(value otelLog.KeyValue) bool {
expectedValue, hasKey := c.constraintMap[value.Key]
assert.Truef(t, hasKey, "unexpected attribute '%s': %s", value.Key, value.Value)
assert.Equal(t, expectedValue, value.Value.String())
delete(c.constraintMap, value.Key)
return true
}
}
func (c *AttributesConstraints) assertAllAttributesMatched(t *testing.T) {
if len(c.constraintMap) == 0 {
return
}
for k, v := range c.constraintMap {
println(fmt.Sprintf("Error: missing attribute %s: %s", k, v))
}
assert.FailNow(t, "number of constraints larger than given validated list of attributes")
}
func labelsAsConstraints(labels map[string]string) []constraint {
constraints := make([]constraint, 0, 0)
for k, v := range labels {
constraints = append(constraints, newConstraint(fmt.Sprintf("labels.%s", k), v))
}
return constraints
}
type constraint struct {
key, value string
}
func newConstraint(key, value string) constraint {
return constraint{key, value}
}