Skip to content

Commit 0b25484

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 743dc39 commit 0b25484

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
@@ -151,7 +151,7 @@ spec:
151151
properties:
152152
configuration:
153153
description: |-
154-
Projected volumes containing custom pgBackRest configuration. These files are mounted
154+
Projected volumes containing custom pgBackRest configuration. These files are mounted
155155
under "/etc/pgbackrest/conf.d" alongside any pgBackRest configuration generated by the
156156
PostgreSQL Operator:
157157
https://pgbackrest.org/configuration.html
@@ -1583,6 +1583,13 @@ spec:
15831583
x-kubernetes-list-type: map
15841584
type: object
15851585
type: object
1586+
log:
1587+
description: Logging configuration for pgbackrest processes
1588+
running in postgres instance pods.
1589+
properties:
1590+
path:
1591+
type: string
1592+
type: object
15861593
manual:
15871594
description: Defines details for manual pgBackRest backup
15881595
Jobs
@@ -2551,6 +2558,13 @@ spec:
25512558
x-kubernetes-list-type: atomic
25522559
type: object
25532560
type: object
2561+
log:
2562+
description: Logging configuration for pgbackrest processes
2563+
running in the repo host pod.
2564+
properties:
2565+
path:
2566+
type: string
2567+
type: object
25542568
priorityClassName:
25552569
description: |-
25562570
Priority class name for the pgBackRest repo host pod. Changing this value
@@ -4562,6 +4576,10 @@ spec:
45624576
required:
45634577
- repos
45644578
type: object
4579+
x-kubernetes-validations:
4580+
- message: log path is restricted to an existing additional volume
4581+
rule: '!has(self.repoHost) || !has(self.repoHost.log) || !has(self.repoHost.log.path)
4582+
|| self.repoHost.volumes.additional.exists(x, self.repoHost.log.path.startsWith("/volumes/"+x.name))'
45654583
snapshots:
45664584
description: VolumeSnapshot configuration
45674585
properties:
@@ -18948,7 +18966,7 @@ spec:
1894818966
properties:
1894918967
configuration:
1895018968
description: |-
18951-
Projected volumes containing custom pgBackRest configuration. These files are mounted
18969+
Projected volumes containing custom pgBackRest configuration. These files are mounted
1895218970
under "/etc/pgbackrest/conf.d" alongside any pgBackRest configuration generated by the
1895318971
PostgreSQL Operator:
1895418972
https://pgbackrest.org/configuration.html
@@ -20380,6 +20398,13 @@ spec:
2038020398
x-kubernetes-list-type: map
2038120399
type: object
2038220400
type: object
20401+
log:
20402+
description: Logging configuration for pgbackrest processes
20403+
running in postgres instance pods.
20404+
properties:
20405+
path:
20406+
type: string
20407+
type: object
2038320408
manual:
2038420409
description: Defines details for manual pgBackRest backup
2038520410
Jobs
@@ -21348,6 +21373,13 @@ spec:
2134821373
x-kubernetes-list-type: atomic
2134921374
type: object
2135021375
type: object
21376+
log:
21377+
description: Logging configuration for pgbackrest processes
21378+
running in the repo host pod.
21379+
properties:
21380+
path:
21381+
type: string
21382+
type: object
2135121383
priorityClassName:
2135221384
description: |-
2135321385
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)