build:create systemd_journald stage

Creates new stage to configure
journald to persist the journal.
Instead of creating the `/var/log/journal`
directory we explicitly configure journald
via the new stage.This is done in
according to the FCOS norms.
Unit tests also added for functionality
check.

Co-authored-by: Irene Diez <idiez@redhat.com>
Signed-off-by: Sayan Paul <saypaul@redhat.com>
This commit is contained in:
Sayan Paul 2022-11-07 10:29:28 +00:00 committed by Achilleas Koutsou
parent 8c75975917
commit 5ce3de214d
2 changed files with 162 additions and 0 deletions

View file

@ -0,0 +1,92 @@
package osbuild
import (
"fmt"
"regexp"
)
const configFilenameRegex = "^[a-zA-Z0-9_\\.-]{1,250}\\.conf$"
type SystemdJournaldStageOptions struct {
Filename string `json:"filename"`
Config SystemdJournaldConfigDropin `json:"config"`
}
func (SystemdJournaldStageOptions) isStageOptions() {}
func (o SystemdJournaldStageOptions) validate() error {
filenameRegex := regexp.MustCompile(configFilenameRegex)
if !filenameRegex.MatchString(o.Filename) {
return fmt.Errorf("filename %q doesn't conform to schema (%s)", o.Filename, repoFilenameRegex)
}
if o.Config.Journal == (SystemdJournaldConfigJournalSection{}) {
return fmt.Errorf("the 'Journal' section is required")
}
return nil
}
func NewSystemdJournaldStage(options *SystemdJournaldStageOptions) *Stage {
if err := options.validate(); err != nil {
panic(err)
}
return &Stage{
Type: "org.osbuild.systemd-journald",
Options: options,
}
}
type SystemdJournaldConfigDropin struct {
Journal SystemdJournaldConfigJournalSection `json:"Journal"`
}
type ConfigStorage string
const (
StorageVolatile ConfigStorage = "volatile"
StoragePresistent ConfigStorage = "persistent"
StorageAuto ConfigStorage = "auto"
StorageNone ConfigStorage = "none"
)
type ConfigSplitMode string
const (
SplitUuid ConfigSplitMode = "uuid"
SplitNone ConfigSplitMode = "none"
)
type ConfigAudit string
const (
AuditYes ConfigAudit = "yes"
AuditNo ConfigAudit = "no"
)
// 'Journal' configuration section, at least one option must be specified
type SystemdJournaldConfigJournalSection struct {
// Controls where to store journal data.
Storage ConfigStorage `json:"Storage,omitempty"`
// Sets whether the data objects stored in the journal should be
// compressed or not. Can also take threshold values.
Compress string `json:"Compress,omitempty"`
// Splits journal files per user or to a single file.
SplitMode ConfigSplitMode `json:"SplitMode,omitempty"`
// Max time to store entries in a single file. By default seconds, may be
// sufixed with units (year, month, week, day, h, m) to override this.
MaxFileSec string `json:"MaxFileSec,omitempty"`
// Maximum time to store journal entries. By default seconds, may be sufixed
// with units (year, month, week, day, h, m) to override this.
MaxRetentionSec string `json:"MaxRetentionSec,omitempty"`
// Timeout before synchronizing journal files to disk. Minimum 0.
SyncIntervalSec int `json:"SyncIntervalSec,omitempty"`
// Enables/Disables kernel auditing on start-up, leaves it as is if
// unspecified.
Audit ConfigAudit `json:"Audit,omitempty"`
}

View file

@ -0,0 +1,70 @@
package osbuild
import (
"testing"
"github.com/stretchr/testify/assert"
)
func TestNewSystemdJournalStage(t *testing.T) {
options := &SystemdJournaldStageOptions{
Filename: "journald-config.conf",
Config: SystemdJournaldConfigDropin{
Journal: SystemdJournaldConfigJournalSection{
Storage: StoragePresistent,
Compress: "yes",
MaxFileSec: "10day",
Audit: AuditYes,
},
}}
expectedStage := &Stage{
Type: "org.osbuild.systemd-journald",
Options: options,
}
actualStage := NewSystemdJournaldStage(options)
assert.Equal(t, expectedStage, actualStage)
}
func TestSystemdJournaldStage_ValidateInvalid(t *testing.T) {
tests := []struct {
name string
options SystemdJournaldStageOptions
}{
{
name: "empty-options",
options: SystemdJournaldStageOptions{},
},
{
name: "no-journal-section-options",
options: SystemdJournaldStageOptions{
Filename: "10-some-file.conf",
Config: SystemdJournaldConfigDropin{
Journal: SystemdJournaldConfigJournalSection{},
},
},
},
}
for idx, te := range tests {
t.Run(te.name, func(t *testing.T) {
assert.Errorf(t, te.options.validate(), "%q didn't return an error [idx: %d]", te.name, idx)
assert.Panics(t, func() { NewSystemdJournaldStage(&te.options) })
})
}
}
func TestInvalidFilename(t *testing.T) {
options := &SystemdJournaldStageOptions{
Filename: "invalid-filename",
Config: SystemdJournaldConfigDropin{
Journal: SystemdJournaldConfigJournalSection{
Storage: StoragePresistent,
Compress: "yes",
MaxFileSec: "10day",
Audit: AuditYes,
},
},
}
assert.Errorf(t, options.validate(), "test didn't return any error ")
assert.Panics(t, func() { NewSystemdJournaldStage(options) })
}