Skip to content

Commit db8e676

Browse files
committed
Fix generation output configuration if used receiver input for ViaQ data model
1 parent 080f848 commit db8e676

File tree

5 files changed

+323
-31
lines changed

5 files changed

+323
-31
lines changed

internal/generator/vector/output/lokistack/init_lokiStack.go

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,9 +33,13 @@ func GenerateOutput(outSpec obs.OutputSpec, tenant string) obs.OutputSpec {
3333

3434
// GenerateLokiSpec generates and returns a Loki spec for the defined lokistack output
3535
func GenerateLokiSpec(ls *obs.LokiStack, tenant string) *obs.Loki {
36+
url := lokiStackURL(ls, tenant, false)
37+
if url == "" {
38+
panic("LokiStack output has no valid URL")
39+
}
3640
return &obs.Loki{
3741
URLSpec: obs.URLSpec{
38-
URL: lokiStackURL(ls, tenant, false),
42+
URL: url,
3943
},
4044
Authentication: &obs.HTTPAuthentication{
4145
Token: ls.Authentication.Token,

internal/generator/vector/output/lokistack/init_lokiStack_test.go

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -294,3 +294,49 @@ var _ = Describe("#GenerateOutput", func() {
294294
),
295295
)
296296
})
297+
298+
var _ = Describe("#GenerateLokiSpec", func() {
299+
var (
300+
lokiStack *obs.LokiStack
301+
)
302+
303+
BeforeEach(func() {
304+
lokiStack = &obs.LokiStack{
305+
Target: obs.LokiStackTarget{
306+
Name: "test-lokistack",
307+
Namespace: constants.OpenshiftNS,
308+
},
309+
Authentication: &obs.LokiStackAuthentication{
310+
Token: &obs.BearerToken{
311+
From: obs.BearerTokenFromServiceAccount,
312+
},
313+
},
314+
}
315+
})
316+
317+
Context("when given a valid tenant", func() {
318+
It("should return a valid Loki spec", func() {
319+
var spec *obs.Loki
320+
Expect(func() {
321+
spec = GenerateLokiSpec(lokiStack, string(obs.InputTypeApplication))
322+
}).ToNot(Panic())
323+
324+
Expect(spec).ToNot(BeNil())
325+
Expect(spec.URL).To(Equal("https://test-lokistack-gateway-http.openshift-logging.svc:8080/api/logs/v1/application"))
326+
Expect(spec.Authentication.Token).To(Equal(lokiStack.Authentication.Token))
327+
})
328+
})
329+
330+
Context("when given an invalid tenant", func() {
331+
DescribeTable("should panic with a specific message",
332+
func(tenant string) {
333+
Expect(func() {
334+
GenerateLokiSpec(lokiStack, tenant)
335+
}).To(PanicWith("LokiStack output has no valid URL"))
336+
},
337+
Entry("with an empty tenant", ""),
338+
Entry("with a non-reserved tenant name", "my-custom-logs"),
339+
Entry("with the 'receiver' tenant name", "receiver"),
340+
)
341+
})
342+
})

internal/generator/vector/output/lokistack/lokistack.go

Lines changed: 95 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -19,47 +19,112 @@ import (
1919

2020
// New creates generate elements that represent configuration to forward logs to Loki using OpenShift Logging tenancy model
2121
func New(id string, o obs.OutputSpec, inputs []string, secrets observability.Secrets, strategy common.ConfigStrategy, op utils.Options) []framework.Element {
22-
routeID := vectorhelpers.MakeID(id, "route")
23-
routes := map[string]string{}
24-
var clfSpec, _ = utils.GetOption(op, vectorhelpers.CLFSpec, observability.ClusterLogForwarderSpec{})
22+
clfSpec, _ := utils.GetOption(op, vectorhelpers.CLFSpec, observability.ClusterLogForwarderSpec{})
2523
if len(clfSpec.Inputs) == 0 || len(clfSpec.Pipelines) == 0 || len(clfSpec.Outputs) == 0 {
2624
panic("ClusterLogForwarderSpec not found while generating LokiStack config")
2725
}
2826

2927
inputSpecs := clfSpec.InputSpecsTo(o)
30-
inputTypes := sets.NewString()
31-
for _, inputSpec := range inputSpecs {
32-
inputType := strings.ToLower(inputSpec.Type.String())
33-
inputTypes.Insert(inputType)
34-
routes[inputType] = fmt.Sprintf("'.log_type == \"%s\"'", inputType)
35-
if inputSpec.Type == obs.InputTypeApplication && observability.IncludesInfraNamespace(inputSpec.Application) {
36-
inputType = strings.ToLower(obs.InputTypeInfrastructure.String())
37-
routes[inputType] = fmt.Sprintf("'.log_type == \"%s\"'", inputType)
38-
inputTypes.Insert(inputType)
39-
}
40-
}
28+
tenants := determineTenants(inputSpecs)
29+
30+
routeID := vectorhelpers.MakeID(id, "route")
4131
confs := []framework.Element{
4232
elements.Route{
4333
ComponentID: routeID,
4434
Inputs: vectorhelpers.MakeInputs(inputs...),
45-
Routes: routes,
35+
Routes: buildRoutes(tenants),
4636
},
4737
}
48-
confs = append(confs, elements.NewUnmatched(routeID, op, map[string]string{"output_type": strings.ToLower(obs.OutputTypeLokiStack.String())}))
49-
for _, inputType := range inputTypes.List() {
50-
outputID := vectorhelpers.MakeID(id, inputType)
51-
migratedOutput := GenerateOutput(o, inputType)
52-
log.V(4).Info("migrated lokistack output", "spec", migratedOutput)
53-
factory := loki.New
54-
if migratedOutput.Type == obs.OutputTypeOTLP {
55-
factory = otlp.New
56-
}
57-
inputSources := observability.Inputs(inputSpecs).InputSources(obs.InputType(inputType))
58-
if len(inputSources) == 0 && obs.InputType(inputType) == obs.InputTypeInfrastructure {
59-
inputSources = append(inputSources, observability.ReservedInfrastructureSources.List()...)
60-
}
61-
op[otlp.OtlpLogSourcesOption] = inputSources
62-
confs = append(confs, factory(outputID, migratedOutput, []string{vectorhelpers.MakeRouteInputID(routeID, inputType)}, secrets, strategy, op)...)
38+
39+
confs = append(confs, elements.NewUnmatched(routeID, op, map[string]string{
40+
"output_type": strings.ToLower(obs.OutputTypeLokiStack.String()),
41+
}))
42+
43+
for _, inputType := range tenants.List() {
44+
confs = append(confs, generateSinkForTenant(id, routeID, inputType, o, inputSpecs, secrets, strategy, op)...)
6345
}
46+
6447
return confs
6548
}
49+
50+
func determineTenants(inputSpecs []obs.InputSpec) *sets.String {
51+
tenants := sets.NewString()
52+
53+
for _, inputSpec := range inputSpecs {
54+
switch inputSpec.Type {
55+
case obs.InputTypeApplication:
56+
tenants.Insert(string(obs.InputTypeApplication))
57+
if observability.IncludesInfraNamespace(inputSpec.Application) {
58+
tenants.Insert(string(obs.InputTypeInfrastructure))
59+
}
60+
case obs.InputTypeAudit:
61+
tenants.Insert(string(obs.InputTypeAudit))
62+
case obs.InputTypeInfrastructure:
63+
tenants.Insert(string(obs.InputTypeInfrastructure))
64+
case obs.InputTypeReceiver:
65+
tenants.Insert(getTenantForReceiver(inputSpec.Receiver.Type))
66+
}
67+
}
68+
69+
return tenants
70+
}
71+
72+
func getTenantForReceiver(receiverType obs.ReceiverType) string {
73+
if receiverType == obs.ReceiverTypeHTTP {
74+
return string(obs.InputTypeAudit)
75+
}
76+
return string(obs.InputTypeInfrastructure)
77+
}
78+
79+
func buildRoutes(tenants *sets.String) map[string]string {
80+
routes := make(map[string]string, tenants.Len())
81+
for _, tenant := range tenants.List() {
82+
routes[tenant] = fmt.Sprintf("'.log_type == \"%s\"'", tenant)
83+
}
84+
return routes
85+
}
86+
87+
func generateSinkForTenant(id, routeID, inputType string, o obs.OutputSpec, inputSpecs []obs.InputSpec,
88+
secrets observability.Secrets, strategy common.ConfigStrategy, op utils.Options) []framework.Element {
89+
90+
outputID := vectorhelpers.MakeID(id, inputType)
91+
migratedOutput := GenerateOutput(o, inputType)
92+
log.V(4).Info("migrated lokistack output", "spec", migratedOutput)
93+
94+
factoryInput := vectorhelpers.MakeRouteInputID(routeID, inputType)
95+
96+
if migratedOutput.Type == obs.OutputTypeOTLP {
97+
op[otlp.OtlpLogSourcesOption] = getInputSources(inputSpecs, obs.InputType(inputType))
98+
return otlp.New(outputID, migratedOutput, []string{factoryInput}, secrets, strategy, op)
99+
}
100+
101+
return loki.New(outputID, migratedOutput, []string{factoryInput}, secrets, strategy, op)
102+
}
103+
104+
func getInputSources(inputSpecs []obs.InputSpec, inputType obs.InputType) []string {
105+
inputSources := observability.Inputs(inputSpecs).InputSources(inputType)
106+
107+
if len(inputSources) == 0 && inputType == obs.InputTypeInfrastructure {
108+
inputSources = append(inputSources, observability.ReservedInfrastructureSources.List()...)
109+
}
110+
111+
addReceiverSources(&inputSources, inputSpecs, inputType)
112+
113+
return inputSources
114+
}
115+
116+
func addReceiverSources(inputSources *[]string, inputSpecs []obs.InputSpec, inputType obs.InputType) {
117+
for _, is := range inputSpecs {
118+
if is.Type != obs.InputTypeReceiver {
119+
continue
120+
}
121+
122+
if inputType == obs.InputTypeAudit && is.Receiver.Type == obs.ReceiverTypeHTTP {
123+
*inputSources = append(*inputSources, "receiver.http")
124+
}
125+
126+
if inputType == obs.InputTypeInfrastructure && is.Receiver.Type == obs.ReceiverTypeSyslog {
127+
*inputSources = append(*inputSources, "receiver.syslog")
128+
}
129+
}
130+
}

internal/generator/vector/output/lokistack/lokistack_test.go

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -98,6 +98,39 @@ var _ = Describe("Generate vector config", func() {
9898
}),
9999
}
100100
}
101+
102+
initReceiverOptions = func() utils.Options {
103+
output := initOutput()
104+
return utils.Options{
105+
framework.OptionServiceAccountTokenSecretName: saTokenSecretName,
106+
helpers.CLFSpec: observability.ClusterLogForwarderSpec(obs.ClusterLogForwarderSpec{
107+
Outputs: []obs.OutputSpec{output},
108+
Pipelines: []obs.PipelineSpec{
109+
{
110+
Name: "lokistack-receivers",
111+
InputRefs: []string{"http-receiver", "syslog-receiver"},
112+
OutputRefs: []string{output.Name},
113+
},
114+
},
115+
Inputs: []obs.InputSpec{
116+
{
117+
Name: "http-receiver",
118+
Type: obs.InputTypeReceiver,
119+
Receiver: &obs.ReceiverSpec{
120+
Type: obs.ReceiverTypeHTTP,
121+
},
122+
},
123+
{
124+
Name: "syslog-receiver",
125+
Type: obs.InputTypeReceiver,
126+
Receiver: &obs.ReceiverSpec{
127+
Type: obs.ReceiverTypeSyslog,
128+
},
129+
},
130+
},
131+
}),
132+
}
133+
}
101134
)
102135
DescribeTable("for LokiStack output", func(expFile string, op framework.Options, tune bool, visit func(spec *obs.OutputSpec)) {
103136
exp, err := tomlContent.ReadFile(expFile)
@@ -118,5 +151,6 @@ var _ = Describe("Generate vector config", func() {
118151
Entry("with Otel datamodel", "lokistack_otel.toml", initOptions(), false, func(spec *obs.OutputSpec) {
119152
spec.LokiStack.DataModel = obs.LokiStackDataModelOpenTelemetry
120153
}),
154+
Entry("with ViaQ datamodel with receiver", "lokistack_viaq_receiver.toml", initReceiverOptions(), false, func(spec *obs.OutputSpec) {}),
121155
)
122156
})

0 commit comments

Comments
 (0)