Skip to content

Commit 57bdaf6

Browse files
vparfonovopenshift-merge-bot[bot]
authored andcommitted
Add functional tests for forwarding logs to GCP Cloud Logging
Tests application, infrastructure container, and k8s audit log forwarding to GCP Cloud Logging using a Mockoon mock server with network-level redirect (host alias + runAsUser 0) since Vector's gcp_stackdriver_logs sink hardcodes the endpoint. Extract shared Mockoon utilities into test/helpers/mockoon package Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
1 parent f70d377 commit 57bdaf6

16 files changed

Lines changed: 722 additions & 51 deletions

File tree

internal/generator/vector/output/gcl/gcl.go

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ func New(id string, o *adapters.Output, inputs []string, secrets observability.S
3636
LogDestination(s, o.GoogleCloudLogging)
3737
s.LogId = fmt.Sprintf("{{ _internal.%s }}", componentID)
3838
s.SeverityKey = DefaultSeverityKey
39-
s.CredentialsPath = auth(g.Authentication, secrets)
39+
s.CredentialsPath = auth(g.Authentication)
4040
s.Encoding = common.NewApiEncoding("")
4141
s.Batch = common.NewApiBatch(o)
4242
s.Buffer = common.NewApiBuffer(o)
@@ -50,11 +50,11 @@ func New(id string, o *adapters.Output, inputs []string, secrets observability.S
5050
return id, sink, tfs
5151
}
5252

53-
func auth(spec *obs.GoogleCloudLoggingAuthentication, secrets observability.Secrets) string {
54-
if spec == nil {
53+
func auth(spec *obs.GoogleCloudLoggingAuthentication) string {
54+
if spec == nil || spec.Credentials == nil {
5555
return ""
5656
}
57-
return secrets.Path(spec.Credentials, "%s")
57+
return helpers.SecretPath(spec.Credentials.SecretName, spec.Credentials.Key, "%s")
5858
}
5959

6060
// LogDestination is one of BillingAccountID, OrganizationID, FolderID, or ProjectID in that order

test/functional/filters/prune/prune_filter_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,6 @@ import (
1111
"github.com/openshift/cluster-logging-operator/internal/constants"
1212
"github.com/openshift/cluster-logging-operator/internal/runtime"
1313
"github.com/openshift/cluster-logging-operator/test/framework/functional"
14-
"github.com/openshift/cluster-logging-operator/test/helpers/azure"
1514
"github.com/openshift/cluster-logging-operator/test/helpers/azure/datacollector"
1615
"github.com/openshift/cluster-logging-operator/test/helpers/kafka"
1716
"github.com/openshift/cluster-logging-operator/test/helpers/loki"
@@ -488,7 +487,7 @@ var _ = Describe("[Functional][Filters][Prune] Prune filter", func() {
488487
f.Secrets = append(f.Secrets, secret)
489488

490489
Expect(f.DeployWithVisitor(func(b *runtime.PodBuilder) error {
491-
altHost := fmt.Sprintf("%s.%s", customerId, azure.AzureDomain)
490+
altHost := fmt.Sprintf("%s.%s", customerId, "acme.com")
492491
return datacollector.NewMockoonVisitor(b, altHost, f)
493492
})).To(BeNil())
494493

test/functional/outputs/azuremonitor/forward_to_azuremonitor_test.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,6 @@ import (
1313
"github.com/openshift/cluster-logging-operator/internal/constants"
1414
"github.com/openshift/cluster-logging-operator/internal/runtime"
1515
"github.com/openshift/cluster-logging-operator/test/framework/functional"
16-
"github.com/openshift/cluster-logging-operator/test/helpers/azure"
1716
"github.com/openshift/cluster-logging-operator/test/helpers/azure/datacollector"
1817
"github.com/openshift/cluster-logging-operator/test/helpers/rand"
1918
"github.com/openshift/cluster-logging-operator/test/matchers"
@@ -46,7 +45,7 @@ var _ = Describe("Forwarding to Azure Monitor Log ", func() {
4645
output.AzureMonitor.CustomerId = customerId
4746
})
4847
Expect(framework.DeployWithVisitor(func(b *runtime.PodBuilder) error {
49-
altHost := fmt.Sprintf("%s.%s", customerId, azure.AzureDomain)
48+
altHost := fmt.Sprintf("%s.%s", customerId, "acme.com")
5049
return datacollector.NewMockoonVisitor(b, altHost, framework)
5150
})).To(BeNil())
5251
})
Lines changed: 141 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,141 @@
1+
package gcl
2+
3+
import (
4+
"fmt"
5+
"strings"
6+
"time"
7+
8+
obstestruntime "github.com/openshift/cluster-logging-operator/test/runtime/observability"
9+
10+
. "github.com/onsi/ginkgo/v2"
11+
. "github.com/onsi/gomega"
12+
obs "github.com/openshift/cluster-logging-operator/api/observability/v1"
13+
"github.com/openshift/cluster-logging-operator/internal/constants"
14+
internalgcl "github.com/openshift/cluster-logging-operator/internal/generator/vector/output/gcl"
15+
"github.com/openshift/cluster-logging-operator/internal/runtime"
16+
"github.com/openshift/cluster-logging-operator/test/client"
17+
"github.com/openshift/cluster-logging-operator/test/framework/functional"
18+
gclhelper "github.com/openshift/cluster-logging-operator/test/helpers/gcl"
19+
"github.com/openshift/cluster-logging-operator/test/helpers/types"
20+
"github.com/openshift/cluster-logging-operator/test/matchers"
21+
)
22+
23+
var _ = Describe("Forwarding to GCP Cloud Logging", func() {
24+
var (
25+
framework *functional.CollectorFunctionalFramework
26+
)
27+
28+
setup := func(inputType obs.InputType, testOptions ...client.TestOption) {
29+
framework = functional.NewCollectorFunctionalFramework(testOptions...)
30+
31+
saJSON, err := gclhelper.GenerateFakeServiceAccountJSON(
32+
fmt.Sprintf("https://%s/token", gclhelper.GCLDomain))
33+
Expect(err).To(BeNil())
34+
35+
secret := runtime.NewSecret(framework.Namespace, gclhelper.SecretName,
36+
map[string][]byte{
37+
internalgcl.GoogleApplicationCredentialsKey: saJSON,
38+
},
39+
)
40+
framework.Secrets = append(framework.Secrets, secret)
41+
42+
obstestruntime.NewClusterLogForwarderBuilder(framework.Forwarder).
43+
FromInput(inputType).
44+
ToGoogleCloudLoggingOutput()
45+
Expect(framework.DeployWithVisitor(
46+
gclhelper.NewMockoonVisitor(framework),
47+
)).To(BeNil())
48+
}
49+
50+
AfterEach(func() {
51+
framework.Cleanup()
52+
})
53+
54+
Context("application logs", func() {
55+
BeforeEach(func() {
56+
setup(obs.InputTypeApplication)
57+
})
58+
59+
It("should accept application logs", func() {
60+
timestamp := "2020-11-04T18:13:59.061892+00:00"
61+
nanoTime, _ := time.Parse(time.RFC3339Nano, timestamp)
62+
message := "This is my new test message"
63+
appLogTemplate := functional.NewApplicationLogTemplate()
64+
appLogTemplate.TimestampLegacy = nanoTime
65+
appLogTemplate.Message = message
66+
appLogTemplate.Level = "**optional**"
67+
appLogTemplate.Kubernetes.PodName = framework.Pod.Name
68+
appLogTemplate.Kubernetes.ContainerName = constants.CollectorName
69+
appLogTemplate.Kubernetes.NamespaceName = framework.Namespace
70+
71+
applicationLogLine := functional.NewCRIOLogMessage(timestamp, message, false)
72+
Expect(framework.WriteMessagesToApplicationLog(applicationLogLine, 3)).To(BeNil())
73+
time.Sleep(30 * time.Second)
74+
75+
collectorLog, err := framework.ReadCollectorLogs()
76+
Expect(err).To(BeNil())
77+
Expect(collectorLog).ToNot(ContainSubstring("error sending request"))
78+
79+
appLogs, err := gclhelper.ReadApplicationLog(framework.Namespace, framework.Name)
80+
Expect(err).To(BeNil())
81+
Expect(appLogs).ToNot(BeNil())
82+
Expect(appLogs).To(HaveLen(3))
83+
for i := range 3 {
84+
Expect(appLogs[i]).To(matchers.FitLogFormatTemplate(appLogTemplate))
85+
}
86+
})
87+
})
88+
89+
Context("infrastructure logs", func() {
90+
BeforeEach(func() {
91+
setup(obs.InputTypeInfrastructure, client.UseInfraNamespaceTestOption)
92+
})
93+
94+
It("should accept infrastructure container logs", func() {
95+
infraLogTemplate := functional.NewContainerInfrastructureLogTemplate()
96+
infraLogTemplate.Level = "**optional**"
97+
infraLogTemplate.Kubernetes.ContainerStream = "**optional**"
98+
99+
Expect(framework.WritesInfraContainerLogs(3)).To(BeNil())
100+
time.Sleep(30 * time.Second)
101+
102+
collectorLog, err := framework.ReadCollectorLogs()
103+
Expect(err).To(BeNil())
104+
Expect(collectorLog).ToNot(ContainSubstring("error sending request"))
105+
106+
infraLogs, err := gclhelper.ReadApplicationLog(framework.Namespace, framework.Name)
107+
Expect(err).To(BeNil())
108+
Expect(infraLogs).ToNot(BeNil())
109+
Expect(infraLogs).To(HaveLen(3))
110+
for i := range 3 {
111+
Expect(infraLogs[i]).To(matchers.FitLogFormatTemplate(infraLogTemplate))
112+
}
113+
})
114+
})
115+
116+
Context("audit logs", func() {
117+
BeforeEach(func() {
118+
setup(obs.InputTypeAudit)
119+
})
120+
121+
It("should accept audit logs", func() {
122+
Expect(framework.WriteK8sAuditLog(1)).To(BeNil())
123+
time.Sleep(30 * time.Second)
124+
125+
collectorLog, err := framework.ReadCollectorLogs()
126+
Expect(err).To(BeNil())
127+
Expect(collectorLog).ToNot(ContainSubstring("error sending request"))
128+
129+
rawEntries, err := gclhelper.ReadRawLogEntries(framework.Namespace, framework.Name)
130+
Expect(err).To(BeNil())
131+
Expect(rawEntries).ToNot(BeEmpty())
132+
133+
var auditLogs []types.AuditLogCommon
134+
jsonString := fmt.Sprintf("[%s]", strings.Join(rawEntries, ","))
135+
Expect(types.ParseLogsFrom(jsonString, &auditLogs, false)).To(Succeed())
136+
Expect(auditLogs).To(HaveLen(1))
137+
Expect(auditLogs[0].LogType).To(Equal(string(obs.InputTypeAudit)))
138+
Expect(auditLogs[0].Timestamp).ToNot(BeZero())
139+
})
140+
})
141+
})
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package gcl
2+
3+
import (
4+
"testing"
5+
6+
. "github.com/onsi/ginkgo/v2"
7+
. "github.com/onsi/gomega"
8+
)
9+
10+
func TestSuite(t *testing.T) {
11+
RegisterFailHandler(Fail)
12+
RunSpecs(t, "[functional][outputs][gcl] Suite")
13+
}

test/helpers/azure/datacollector/data_collector.go

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import (
77
"github.com/openshift/cluster-logging-operator/internal/runtime"
88
"github.com/openshift/cluster-logging-operator/internal/utils"
99
"github.com/openshift/cluster-logging-operator/test/framework/functional"
10-
"github.com/openshift/cluster-logging-operator/test/helpers/azure"
10+
"github.com/openshift/cluster-logging-operator/test/helpers/mockoon"
1111
"github.com/openshift/cluster-logging-operator/test/helpers/oc"
1212
"github.com/openshift/cluster-logging-operator/test/helpers/types"
1313
v1 "k8s.io/api/core/v1"
@@ -22,7 +22,7 @@ const (
2222
)
2323

2424
func NewMockoonVisitor(pb *runtime.PodBuilder, azureAltHost string, framework *functional.CollectorFunctionalFramework) error {
25-
configMap := runtime.NewConfigMap(framework.Namespace, azure.Mockoon, map[string]string{})
25+
configMap := runtime.NewConfigMap(framework.Namespace, mockoon.ContainerName, map[string]string{})
2626
runtime.NewConfigMapBuilder(configMap).Add(apiJsonFile, apiConfig)
2727
if err := framework.Test.Create(configMap); err != nil {
2828
return err
@@ -34,10 +34,10 @@ func NewMockoonVisitor(pb *runtime.PodBuilder, azureAltHost string, framework *f
3434
}
3535

3636
mountPath := "/data"
37-
pb.AddConfigMapVolume("data", azure.Mockoon).
37+
pb.AddConfigMapVolume("data", mockoon.ContainerName).
3838
AddHostAlias(hostAlias).
39-
AddContainer(azure.Mockoon, azure.Image).
40-
AddContainerPort(azure.Mockoon, azure.Port).
39+
AddContainer(mockoon.ContainerName, mockoon.Image).
40+
AddContainerPort(mockoon.ContainerName, mockoon.Port).
4141
WithCmdArgs([]string{
4242
fmt.Sprintf("--data=%s/%s", mountPath, apiJsonFile),
4343
"--log-transaction",
@@ -47,15 +47,15 @@ func NewMockoonVisitor(pb *runtime.PodBuilder, azureAltHost string, framework *f
4747
}
4848

4949
func ReadApplicationLog(framework *functional.CollectorFunctionalFramework) ([]types.ApplicationLog, error) {
50-
output, err := oc.Literal().From("oc logs -n %s pod/%s -c %s", framework.Test.NS.Name, framework.Name, azure.Mockoon).Run()
50+
output, err := oc.Literal().From("oc logs -n %s pod/%s -c %s", framework.Test.NS.Name, framework.Name, mockoon.ContainerName).Run()
5151
if err != nil {
5252
return nil, err
5353
}
5454
return extractStructuredLogs(output, "application")
5555
}
5656

5757
func extractStructuredLogs(output, logType string) ([]types.ApplicationLog, error) {
58-
logs, err := azure.DecodeMockoonLogs(output)
58+
logs, err := mockoon.DecodeLogs(output)
5959
if err != nil {
6060
return nil, err
6161
}

test/helpers/azure/datacollector/data_collector_test.go

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import (
66

77
. "github.com/onsi/ginkgo/v2"
88
. "github.com/onsi/gomega"
9-
"github.com/openshift/cluster-logging-operator/test/helpers/azure"
9+
"github.com/openshift/cluster-logging-operator/test/helpers/mockoon"
1010
)
1111

1212
//go:embed test_mockoon.log
@@ -21,14 +21,14 @@ var _ = Describe("Parsing Mockoon logs for Azure Monitor (Data Collector API)",
2121
})
2222

2323
It("should ignore non-POST requests", func() {
24-
input := azure.MockoonLine("GET", "/api/logs", 200, `[{"log_type":"application","message":"should be ignored"}]`)
24+
input := mockoon.NewLogLine("GET", "/api/logs", 200, `[{"log_type":"application","message":"should be ignored"}]`)
2525
logs, err := extractStructuredLogs(input, "application")
2626
Expect(err).ToNot(HaveOccurred())
2727
Expect(logs).To(BeEmpty())
2828
})
2929

3030
It("should ignore non-200 responses", func() {
31-
input := azure.MockoonLine("POST", "/api/logs", 500, `[{"log_type":"application","message":"should be ignored"}]`)
31+
input := mockoon.NewLogLine("POST", "/api/logs", 500, `[{"log_type":"application","message":"should be ignored"}]`)
3232
logs, err := extractStructuredLogs(input, "application")
3333
Expect(err).ToNot(HaveOccurred())
3434
Expect(logs).To(BeEmpty())
@@ -42,16 +42,16 @@ var _ = Describe("Parsing Mockoon logs for Azure Monitor (Data Collector API)",
4242
})
4343

4444
It("should skip malformed body without error", func() {
45-
input := azure.MockoonLine("POST", "/api/logs", 200, `not-valid-json`)
45+
input := mockoon.NewLogLine("POST", "/api/logs", 200, `not-valid-json`)
4646
logs, err := extractStructuredLogs(input, "application")
4747
Expect(err).ToNot(HaveOccurred())
4848
Expect(logs).To(BeEmpty())
4949
})
5050

5151
It("should aggregate logs across multiple batches", func() {
52-
line1 := azure.MockoonLine("POST", "/api/logs", 200,
52+
line1 := mockoon.NewLogLine("POST", "/api/logs", 200,
5353
`[{"log_type":"application","message":"batch1-msg1"}]`)
54-
line2 := azure.MockoonLine("POST", "/api/logs", 200,
54+
line2 := mockoon.NewLogLine("POST", "/api/logs", 200,
5555
`[{"log_type":"application","message":"batch2-msg1"},{"log_type":"infrastructure","message":"infra-msg"}]`)
5656
input := strings.Join([]string{line1, line2}, "\n")
5757

test/helpers/azure/logsingestion/log_ingestion.go

Lines changed: 11 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ import (
99
"github.com/openshift/cluster-logging-operator/internal/runtime"
1010
"github.com/openshift/cluster-logging-operator/internal/utils"
1111
"github.com/openshift/cluster-logging-operator/test/framework/functional"
12-
"github.com/openshift/cluster-logging-operator/test/helpers/azure"
1312
"github.com/openshift/cluster-logging-operator/test/helpers/certificate"
13+
"github.com/openshift/cluster-logging-operator/test/helpers/mockoon"
1414
"github.com/openshift/cluster-logging-operator/test/helpers/oc"
1515
"github.com/openshift/cluster-logging-operator/test/helpers/types"
1616
v1 "k8s.io/api/core/v1"
@@ -23,6 +23,7 @@ const (
2323
apiJsonFile = "azure-log-ingestion-api.json"
2424
SecretName = "azure-client-secret"
2525
ClientSecretKeyName = "client_secret"
26+
acme = "acme.com"
2627
)
2728

2829
// NewMockoonVisitor sets up a Mockoon mock server for the Azure Log Ingestion API.
@@ -33,9 +34,9 @@ func NewMockoonVisitor(pb *runtime.PodBuilder, framework *functional.CollectorFu
3334
// The CA and server cert must have different Organization names so that OpenSSL
3435
// can distinguish the issuer from the subject when building the certificate chain.
3536
ca := certificate.NewCA(nil, "Test CA")
36-
serverCert := certificate.NewCert(ca, "test", azure.AzureDomain)
37+
serverCert := certificate.NewCert(ca, "test", acme)
3738

38-
configMap := runtime.NewConfigMap(framework.Namespace, azure.Mockoon, map[string]string{})
39+
configMap := runtime.NewConfigMap(framework.Namespace, mockoon.ContainerName, map[string]string{})
3940
runtime.NewConfigMapBuilder(configMap).
4041
Add(apiJsonFile, apiConfig).
4142
Add("tls.crt", string(serverCert.CertificatePEM())).
@@ -47,16 +48,16 @@ func NewMockoonVisitor(pb *runtime.PodBuilder, framework *functional.CollectorFu
4748

4849
hostAlias := v1.HostAlias{
4950
IP: "127.0.0.1",
50-
Hostnames: []string{azure.AzureDomain},
51+
Hostnames: []string{acme},
5152
}
5253

5354
mountPath := "/data"
5455
mockoonDataVolume := "mockoon-data"
5556

56-
pb.AddConfigMapVolume(mockoonDataVolume, azure.Mockoon).
57+
pb.AddConfigMapVolume(mockoonDataVolume, mockoon.ContainerName).
5758
AddHostAlias(hostAlias).
58-
AddContainer(azure.Mockoon, azure.Image).
59-
AddContainerPort(azure.Mockoon, azure.Port).
59+
AddContainer(mockoon.ContainerName, mockoon.Image).
60+
AddContainerPort(mockoon.ContainerName, mockoon.Port).
6061
WithCmdArgs([]string{
6162
fmt.Sprintf("--data=%s/%s", mountPath, apiJsonFile),
6263
"--log-transaction",
@@ -67,7 +68,7 @@ func NewMockoonVisitor(pb *runtime.PodBuilder, framework *functional.CollectorFu
6768
// trust the Mockoon TLS certificate.
6869
combinedBundle := "/tmp/ca-bundle.crt"
6970
pb.GetContainer(constants.CollectorName).
70-
AddEnvVar("AZURE_AUTHORITY_HOST", fmt.Sprintf("https://%s:%d", azure.AzureDomain, azure.Port)).
71+
AddEnvVar("AZURE_AUTHORITY_HOST", fmt.Sprintf("https://%s:%d", acme, mockoon.Port)).
7172
AddEnvVar("SSL_CERT_FILE", combinedBundle).
7273
WithCmd([]string{"sh", "-c", fmt.Sprintf(
7374
"cat /etc/pki/tls/certs/ca-bundle.crt %s/ca.crt > %s && exec /opt/app-root/src/run.sh",
@@ -81,15 +82,15 @@ func NewMockoonVisitor(pb *runtime.PodBuilder, framework *functional.CollectorFu
8182
// ReadApplicationLog reads and parses application logs from the
8283
// Mockoon container used for the Azure Log Ingestion API mock.
8384
func ReadApplicationLog(framework *functional.CollectorFunctionalFramework) ([]types.ApplicationLog, error) {
84-
output, err := oc.Literal().From("oc logs -n %s pod/%s -c %s", framework.Test.NS.Name, framework.Name, azure.Mockoon).Run()
85+
output, err := oc.Literal().From("oc logs -n %s pod/%s -c %s", framework.Test.NS.Name, framework.Name, mockoon.ContainerName).Run()
8586
if err != nil {
8687
return nil, err
8788
}
8889
return extractLogs(output)
8990
}
9091

9192
func extractLogs(output string) ([]types.ApplicationLog, error) {
92-
logs, err := azure.DecodeMockoonLogs(output)
93+
logs, err := mockoon.DecodeLogs(output)
9394
if err != nil {
9495
return nil, err
9596
}

0 commit comments

Comments
 (0)