Skip to content

Commit 13af5ea

Browse files
committed
Add LoggingConfiguration struct for providing log path to pgbackrest processes.
Add ability to send pgbackrest logs to additional volume for repo host processes.
1 parent f49f235 commit 13af5ea

File tree

11 files changed

+339
-23
lines changed

11 files changed

+339
-23
lines changed

config/crd/bases/postgres-operator.crunchydata.com_postgresclusters.yaml

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -154,7 +154,7 @@ spec:
154154
properties:
155155
configuration:
156156
description: |-
157-
Projected volumes containing custom pgBackRest configuration. These files are mounted
157+
Projected volumes containing custom pgBackRest configuration. These files are mounted
158158
under "/etc/pgbackrest/conf.d" alongside any pgBackRest configuration generated by the
159159
PostgreSQL Operator:
160160
https://pgbackrest.org/configuration.html
@@ -1597,6 +1597,13 @@ spec:
15971597
x-kubernetes-list-type: map
15981598
type: object
15991599
type: object
1600+
log:
1601+
description: Logging configuration for pgbackrest processes
1602+
running in postgres instance pods.
1603+
properties:
1604+
path:
1605+
type: string
1606+
type: object
16001607
manual:
16011608
description: Defines details for manual pgBackRest backup
16021609
Jobs
@@ -2565,6 +2572,13 @@ spec:
25652572
x-kubernetes-list-type: atomic
25662573
type: object
25672574
type: object
2575+
log:
2576+
description: Logging configuration for pgbackrest processes
2577+
running in the repo host pod.
2578+
properties:
2579+
path:
2580+
type: string
2581+
type: object
25682582
priorityClassName:
25692583
description: |-
25702584
Priority class name for the pgBackRest repo host pod. Changing this value
@@ -4588,6 +4602,10 @@ spec:
45884602
required:
45894603
- repos
45904604
type: object
4605+
x-kubernetes-validations:
4606+
- message: log path is restricted to an existing additional volume
4607+
rule: '!has(self.repoHost) || !has(self.repoHost.log) || !has(self.repoHost.log.path)
4608+
|| self.repoHost.volumes.additional.exists(x, self.repoHost.log.path.startsWith("/volumes/"+x.name))'
45914609
snapshots:
45924610
description: VolumeSnapshot configuration
45934611
properties:
@@ -19087,7 +19105,7 @@ spec:
1908719105
properties:
1908819106
configuration:
1908919107
description: |-
19090-
Projected volumes containing custom pgBackRest configuration. These files are mounted
19108+
Projected volumes containing custom pgBackRest configuration. These files are mounted
1909119109
under "/etc/pgbackrest/conf.d" alongside any pgBackRest configuration generated by the
1909219110
PostgreSQL Operator:
1909319111
https://pgbackrest.org/configuration.html
@@ -20530,6 +20548,13 @@ spec:
2053020548
x-kubernetes-list-type: map
2053120549
type: object
2053220550
type: object
20551+
log:
20552+
description: Logging configuration for pgbackrest processes
20553+
running in postgres instance pods.
20554+
properties:
20555+
path:
20556+
type: string
20557+
type: object
2053320558
manual:
2053420559
description: Defines details for manual pgBackRest backup
2053520560
Jobs
@@ -21498,6 +21523,13 @@ spec:
2149821523
x-kubernetes-list-type: atomic
2149921524
type: object
2150021525
type: object
21526+
log:
21527+
description: Logging configuration for pgbackrest processes
21528+
running in the repo host pod.
21529+
properties:
21530+
path:
21531+
type: string
21532+
type: object
2150121533
priorityClassName:
2150221534
description: |-
2150321535
Priority class name for the pgBackRest repo host pod. Changing this value

internal/collector/helpers_test.go

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,9 +5,22 @@
55
package collector
66

77
import (
8+
corev1 "k8s.io/api/core/v1"
9+
"k8s.io/apimachinery/pkg/api/resource"
10+
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
11+
12+
"github.com/crunchydata/postgres-operator/internal/initialize"
813
"github.com/crunchydata/postgres-operator/pkg/apis/postgres-operator.crunchydata.com/v1beta1"
914
)
1015

16+
var (
17+
// TODO (testing): With the new RELATED_IMAGES defaulting behavior, tests could be refactored
18+
// to reference those environment variables instead of hard coded image values
19+
CrunchyPostgresHAImage = "registry.developers.crunchydata.com/crunchydata/crunchy-postgres:ubi8-13.6-1"
20+
CrunchyPGBackRestImage = "registry.developers.crunchydata.com/crunchydata/crunchy-pgbackrest:ubi8-2.38-0"
21+
CrunchyPGBouncerImage = "registry.developers.crunchydata.com/crunchydata/crunchy-pgbouncer:ubi8-1.16-2"
22+
)
23+
1124
func testInstrumentationSpec() *v1beta1.InstrumentationSpec {
1225
spec := v1beta1.InstrumentationSpec{
1326
Config: &v1beta1.InstrumentationConfigSpec{
@@ -30,3 +43,58 @@ func testInstrumentationSpec() *v1beta1.InstrumentationSpec {
3043

3144
return spec.DeepCopy()
3245
}
46+
47+
// Copied from postgrescluster package
48+
func testVolumeClaimSpec() v1beta1.VolumeClaimSpec {
49+
// Defines a volume claim spec that can be used to create instances
50+
return v1beta1.VolumeClaimSpec{
51+
AccessModes: []corev1.PersistentVolumeAccessMode{corev1.ReadWriteOnce},
52+
Resources: corev1.VolumeResourceRequirements{
53+
Requests: map[corev1.ResourceName]resource.Quantity{
54+
corev1.ResourceStorage: resource.MustParse("1Gi"),
55+
},
56+
},
57+
}
58+
}
59+
60+
// Copied from postgrescluster package (and then edited)
61+
func testCluster() *v1beta1.PostgresCluster {
62+
// Defines a base cluster spec that can be used by tests to generate a
63+
// cluster with an expected number of instances
64+
cluster := v1beta1.PostgresCluster{
65+
ObjectMeta: metav1.ObjectMeta{
66+
Name: "hippo",
67+
},
68+
Spec: v1beta1.PostgresClusterSpec{
69+
PostgresVersion: 13,
70+
Image: CrunchyPostgresHAImage,
71+
ImagePullSecrets: []corev1.LocalObjectReference{{
72+
Name: "myImagePullSecret"},
73+
},
74+
InstanceSets: []v1beta1.PostgresInstanceSetSpec{{
75+
Name: "instance1",
76+
Replicas: initialize.Int32(1),
77+
DataVolumeClaimSpec: testVolumeClaimSpec(),
78+
}},
79+
Backups: v1beta1.Backups{
80+
PGBackRest: v1beta1.PGBackRestArchive{
81+
Image: CrunchyPGBackRestImage,
82+
Repos: []v1beta1.PGBackRestRepo{{
83+
Name: "repo1",
84+
Volume: &v1beta1.RepoPVC{
85+
VolumeClaimSpec: testVolumeClaimSpec(),
86+
},
87+
}},
88+
RepoHost: &v1beta1.PGBackRestRepoHost{},
89+
},
90+
},
91+
Proxy: &v1beta1.PostgresProxySpec{
92+
PGBouncer: &v1beta1.PGBouncerPodSpec{
93+
Image: CrunchyPGBouncerImage,
94+
},
95+
},
96+
Instrumentation: testInstrumentationSpec(),
97+
},
98+
}
99+
return cluster.DeepCopy()
100+
}

internal/collector/pgbackrest.go

Lines changed: 15 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -23,17 +23,25 @@ var pgBackRestLogsTransforms json.RawMessage
2323

2424
func NewConfigForPgBackrestRepoHostPod(
2525
ctx context.Context,
26-
spec *v1beta1.InstrumentationSpec,
26+
cluster *v1beta1.PostgresCluster,
2727
repos []v1beta1.PGBackRestRepo,
2828
) *Config {
29-
config := NewConfig(spec)
29+
config := NewConfig(cluster.Spec.Instrumentation)
3030

31-
if OpenTelemetryLogsEnabled(ctx, spec) {
31+
if OpenTelemetryLogsEnabled(ctx, cluster) {
3232

3333
var directory string
3434
for _, repo := range repos {
3535
if repo.Volume != nil {
36-
directory = fmt.Sprintf(naming.PGBackRestRepoLogPath, repo.Name)
36+
// If the user has set a log path in the spec, use it.
37+
// Otherwise, default to /pgbackrest/repo#/log
38+
if cluster.Spec.Backups.PGBackRest.RepoHost != nil &&
39+
cluster.Spec.Backups.PGBackRest.RepoHost.Log != nil &&
40+
cluster.Spec.Backups.PGBackRest.RepoHost.Log.Path != "" {
41+
directory = cluster.Spec.Backups.PGBackRest.RepoHost.Log.Path
42+
} else {
43+
directory = fmt.Sprintf(naming.PGBackRestRepoLogPath, repo.Name)
44+
}
3745
break
3846
}
3947
}
@@ -99,8 +107,9 @@ func NewConfigForPgBackrestRepoHostPod(
99107
// If there are exporters to be added to the logs pipelines defined in
100108
// the spec, add them to the pipeline. Otherwise, add the DebugExporter.
101109
exporters := []ComponentID{DebugExporter}
102-
if spec != nil && spec.Logs != nil && spec.Logs.Exporters != nil {
103-
exporters = slices.Clone(spec.Logs.Exporters)
110+
if cluster.Spec.Instrumentation != nil && cluster.Spec.Instrumentation.Logs != nil &&
111+
cluster.Spec.Instrumentation.Logs.Exporters != nil {
112+
exporters = slices.Clone(cluster.Spec.Instrumentation.Logs.Exporters)
104113
}
105114

106115
config.Pipelines["logs/pgbackrest"] = Pipeline{

internal/collector/pgbackrest_test.go

Lines changed: 118 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -30,8 +30,9 @@ func TestNewConfigForPgBackrestRepoHostPod(t *testing.T) {
3030
}
3131
var instrumentation *v1beta1.InstrumentationSpec
3232
require.UnmarshalInto(t, &instrumentation, `{}`)
33-
34-
config := NewConfigForPgBackrestRepoHostPod(ctx, instrumentation, repos)
33+
cluster := testCluster()
34+
cluster.Spec.Instrumentation = instrumentation
35+
config := NewConfigForPgBackrestRepoHostPod(ctx, cluster, repos)
3536

3637
result, err := config.ToYAML()
3738
assert.NilError(t, err)
@@ -136,8 +137,8 @@ service:
136137
Volume: new(v1beta1.RepoPVC),
137138
},
138139
}
139-
140-
config := NewConfigForPgBackrestRepoHostPod(ctx, testInstrumentationSpec(), repos)
140+
cluster := testCluster()
141+
config := NewConfigForPgBackrestRepoHostPod(ctx, cluster, repos)
141142

142143
result, err := config.ToYAML()
143144
assert.NilError(t, err)
@@ -231,6 +232,119 @@ service:
231232
- groupbyattrs/compact
232233
receivers:
233234
- filelog/pgbackrest_log
235+
`)
236+
})
237+
238+
t.Run("LogPathDefined", func(t *testing.T) {
239+
gate := feature.NewGate()
240+
assert.NilError(t, gate.SetFromMap(map[string]bool{
241+
feature.OpenTelemetryLogs: true,
242+
}))
243+
ctx := feature.NewContext(context.Background(), gate)
244+
repos := []v1beta1.PGBackRestRepo{
245+
{
246+
Name: "repo1",
247+
Volume: new(v1beta1.RepoPVC),
248+
},
249+
}
250+
cluster := testCluster()
251+
cluster.Spec.Backups.PGBackRest.RepoHost.Log = &v1beta1.LoggingConfiguration{
252+
Path: "/test/path",
253+
}
254+
config := NewConfigForPgBackrestRepoHostPod(ctx, cluster, repos)
255+
256+
result, err := config.ToYAML()
257+
assert.NilError(t, err)
258+
assert.DeepEqual(t, result, `# Generated by postgres-operator. DO NOT EDIT.
259+
# Your changes will not be saved.
260+
exporters:
261+
debug:
262+
verbosity: detailed
263+
googlecloud:
264+
log:
265+
default_log_name: opentelemetry.io/collector-exported-log
266+
project: google-project-name
267+
extensions:
268+
file_storage/pgbackrest_logs:
269+
create_directory: false
270+
directory: /test/path/receiver
271+
fsync: true
272+
processors:
273+
batch/1s:
274+
timeout: 1s
275+
batch/200ms:
276+
timeout: 200ms
277+
batch/logs:
278+
send_batch_size: 8192
279+
timeout: 200ms
280+
groupbyattrs/compact: {}
281+
resource/pgbackrest:
282+
attributes:
283+
- action: insert
284+
key: k8s.container.name
285+
value: pgbackrest
286+
- action: insert
287+
key: k8s.namespace.name
288+
value: ${env:K8S_POD_NAMESPACE}
289+
- action: insert
290+
key: k8s.pod.name
291+
value: ${env:K8S_POD_NAME}
292+
- action: insert
293+
key: process.executable.name
294+
value: pgbackrest
295+
resourcedetection:
296+
detectors: []
297+
override: false
298+
timeout: 30s
299+
transform/pgbackrest_logs:
300+
log_statements:
301+
- statements:
302+
- set(instrumentation_scope.name, "pgbackrest")
303+
- set(instrumentation_scope.schema_url, "https://opentelemetry.io/schemas/1.29.0")
304+
- 'merge_maps(log.cache, ExtractPatterns(log.body, "^(?<timestamp>\\d{4}-\\d{2}-\\d{2}
305+
\\d{2}:\\d{2}:\\d{2}\\.\\d{3}) (?<process_id>P\\d{2,3})\\s*(?<error_severity>\\S*):
306+
(?<message>(?s).*)$"), "insert") where Len(log.body) > 0'
307+
- set(log.severity_text, log.cache["error_severity"]) where IsString(log.cache["error_severity"])
308+
- set(log.severity_number, SEVERITY_NUMBER_TRACE) where log.severity_text ==
309+
"TRACE"
310+
- set(log.severity_number, SEVERITY_NUMBER_DEBUG) where log.severity_text ==
311+
"DEBUG"
312+
- set(log.severity_number, SEVERITY_NUMBER_DEBUG2) where log.severity_text ==
313+
"DETAIL"
314+
- set(log.severity_number, SEVERITY_NUMBER_INFO) where log.severity_text ==
315+
"INFO"
316+
- set(log.severity_number, SEVERITY_NUMBER_WARN) where log.severity_text ==
317+
"WARN"
318+
- set(log.severity_number, SEVERITY_NUMBER_ERROR) where log.severity_text ==
319+
"ERROR"
320+
- set(log.time, Time(log.cache["timestamp"], "%Y-%m-%d %H:%M:%S.%L")) where
321+
IsString(log.cache["timestamp"])
322+
- set(log.attributes["process.pid"], log.cache["process_id"])
323+
- set(log.attributes["log.record.original"], log.body)
324+
- set(log.body, log.cache["message"])
325+
receivers:
326+
filelog/pgbackrest_log:
327+
include:
328+
- /test/path/*.log
329+
- /test/path/*.log.1
330+
multiline:
331+
line_start_pattern: ^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}|^-{19}
332+
storage: file_storage/pgbackrest_logs
333+
service:
334+
extensions:
335+
- file_storage/pgbackrest_logs
336+
pipelines:
337+
logs/pgbackrest:
338+
exporters:
339+
- googlecloud
340+
processors:
341+
- resource/pgbackrest
342+
- transform/pgbackrest_logs
343+
- resourcedetection
344+
- batch/logs
345+
- groupbyattrs/compact
346+
receivers:
347+
- filelog/pgbackrest_log
234348
`)
235349
})
236350
}

0 commit comments

Comments
 (0)