osbuild2: add support for org.osbuild.pam.limits.conf stage
Add support for a new osbuild stage `org.osbuild.pam.limits.conf`, for creating pam_limits module configuration files. Add unit tests for the new stage. Related to: - https://github.com/osbuild/osbuild/pull/802 - https://github.com/osbuild/osbuild/pull/807 Signed-off-by: Tomas Hozza <thozza@redhat.com>
This commit is contained in:
parent
3f52af2adb
commit
a5097b2141
4 changed files with 249 additions and 0 deletions
155
internal/osbuild2/pam_limits_conf_stage.go
Normal file
155
internal/osbuild2/pam_limits_conf_stage.go
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// PamLimitsConfStageOptions represents a single pam_limits module configuration file.
|
||||
type PamLimitsConfStageOptions struct {
|
||||
// Filename of the configuration file to be created. Must end with '.conf'.
|
||||
Filename string `json:"filename"`
|
||||
// List of configuration directives. The list must contain at least one item.
|
||||
Config []PamLimitsConfigLine `json:"config"`
|
||||
}
|
||||
|
||||
func (PamLimitsConfStageOptions) isStageOptions() {}
|
||||
|
||||
// NewPamLimitsConfStageOptions creates a new PamLimitsConf Stage options object.
|
||||
func NewPamLimitsConfStageOptions(filename string, config []PamLimitsConfigLine) *PamLimitsConfStageOptions {
|
||||
return &PamLimitsConfStageOptions{
|
||||
Filename: filename,
|
||||
Config: config,
|
||||
}
|
||||
}
|
||||
|
||||
// Unexported alias for use in PamLimitsConfStageOptions's MarshalJSON() to prevent recursion
|
||||
type pamLimitsConfStageOptions PamLimitsConfStageOptions
|
||||
|
||||
func (o PamLimitsConfStageOptions) MarshalJSON() ([]byte, error) {
|
||||
if len(o.Config) == 0 {
|
||||
return nil, fmt.Errorf("the 'Config' list must contain at least one item")
|
||||
}
|
||||
options := pamLimitsConfStageOptions(o)
|
||||
return json.Marshal(options)
|
||||
}
|
||||
|
||||
// NewPamLimitsConfStage creates a new PamLimitsConf Stage object.
|
||||
func NewPamLimitsConfStage(options *PamLimitsConfStageOptions) *Stage {
|
||||
return &Stage{
|
||||
Type: "org.osbuild.pam.limits.conf",
|
||||
Options: options,
|
||||
}
|
||||
}
|
||||
|
||||
type PamLimitsType string
|
||||
|
||||
// Valid 'Type' values for the use with the PamLimitsConfigLine structure.
|
||||
const (
|
||||
PamLimitsTypeHard PamLimitsType = "hard"
|
||||
PamLimitsTypeSoft PamLimitsType = "soft"
|
||||
PamLimitsTypeBoth PamLimitsType = "-"
|
||||
)
|
||||
|
||||
type PamLimitsItem string
|
||||
|
||||
// Valid 'Item' values for the use with the PamLimitsConfigLine structure.
|
||||
const (
|
||||
PamLimitsItemCore PamLimitsItem = "core"
|
||||
PamLimitsItemData PamLimitsItem = "data"
|
||||
PamLimitsItemFsize PamLimitsItem = "fsize"
|
||||
PamLimitsItemMemlock PamLimitsItem = "memlock"
|
||||
PamLimitsItemNofile PamLimitsItem = "nofile"
|
||||
PamLimitsItemRss PamLimitsItem = "rss"
|
||||
PamLimitsItemStack PamLimitsItem = "stack"
|
||||
PamLimitsItemCpu PamLimitsItem = "cpu"
|
||||
PamLimitsItemNproc PamLimitsItem = "nproc"
|
||||
PamLimitsItemAs PamLimitsItem = "as"
|
||||
PamLimitsItemMaxlogins PamLimitsItem = "maxlogins"
|
||||
PamLimitsItemMaxsyslogins PamLimitsItem = "maxsyslogins"
|
||||
PamLimitsItemNonewprivs PamLimitsItem = "nonewprivs"
|
||||
PamLimitsItemPriority PamLimitsItem = "priority"
|
||||
PamLimitsItemLocks PamLimitsItem = "locks"
|
||||
PamLimitsItemSigpending PamLimitsItem = "sigpending"
|
||||
PamLimitsItemMsgqueue PamLimitsItem = "msgqueue"
|
||||
PamLimitsItemNice PamLimitsItem = "nice"
|
||||
PamLimitsItemRtprio PamLimitsItem = "rtprio"
|
||||
)
|
||||
|
||||
// PamLimitsValue is defined to represent all valid types of the 'Value'
|
||||
// item in the PamLimitsConfigLine structure.
|
||||
type PamLimitsValue interface {
|
||||
isPamLimitsValue()
|
||||
}
|
||||
|
||||
// PamLimitsValueStr represents a string type of the 'Value' item in
|
||||
// the PamLimitsConfigLine structure.
|
||||
type PamLimitsValueStr string
|
||||
|
||||
func (v PamLimitsValueStr) isPamLimitsValue() {}
|
||||
|
||||
// Valid string values which can be used for the 'Value' item in
|
||||
// the PamLimitsConfigLine structure.
|
||||
const (
|
||||
PamLimitsValueUnlimited PamLimitsValueStr = "unlimited"
|
||||
PamLimitsValueInfinity PamLimitsValueStr = "infinity"
|
||||
)
|
||||
|
||||
// PamLimitsValueInt represents an integer type of the 'Value' item in
|
||||
// the PamLimitsConfigLine structure.
|
||||
type PamLimitsValueInt int
|
||||
|
||||
func (v PamLimitsValueInt) isPamLimitsValue() {}
|
||||
|
||||
// PamLimitsConfigLine represents a single line in a pam_limits module configuration.
|
||||
type PamLimitsConfigLine struct {
|
||||
// Domain to which the limit applies. E.g. username, groupname, etc.
|
||||
Domain string `json:"domain"`
|
||||
// Type of the limit.
|
||||
Type PamLimitsType `json:"type"`
|
||||
// The resource type, which is being limited.
|
||||
Item PamLimitsItem `json:"item"`
|
||||
// The limit value.
|
||||
Value PamLimitsValue `json:"value"`
|
||||
}
|
||||
|
||||
// Unexported struct used for Unmarshalling of PamLimitsConfigLine due to
|
||||
// 'value' being an integer or a string.
|
||||
type rawPamLimitsConfigLine struct {
|
||||
// Domain to which the limit applies. E.g. username, groupname, etc.
|
||||
Domain string `json:"domain"`
|
||||
// Type of the limit.
|
||||
Type PamLimitsType `json:"type"`
|
||||
// The resource type, which is being limited.
|
||||
Item PamLimitsItem `json:"item"`
|
||||
// The limit value.
|
||||
Value interface{} `json:"value"`
|
||||
}
|
||||
|
||||
func (l *PamLimitsConfigLine) UnmarshalJSON(data []byte) error {
|
||||
var rawLine rawPamLimitsConfigLine
|
||||
if err := json.Unmarshal(data, &rawLine); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var value PamLimitsValue
|
||||
switch valueType := reflect.TypeOf(rawLine.Value); valueType.Kind() {
|
||||
// json.Unmarshal() uses float64 for JSON numbers
|
||||
// https://pkg.go.dev/encoding/json#Unmarshal
|
||||
// However the expected value is only integer.
|
||||
case reflect.Float64:
|
||||
value = PamLimitsValueInt(rawLine.Value.(float64))
|
||||
case reflect.String:
|
||||
value = PamLimitsValueStr(rawLine.Value.(string))
|
||||
default:
|
||||
return fmt.Errorf("the 'value' item has unsupported type %q", valueType)
|
||||
}
|
||||
|
||||
l.Domain = rawLine.Domain
|
||||
l.Type = rawLine.Type
|
||||
l.Item = rawLine.Item
|
||||
l.Value = value
|
||||
|
||||
return nil
|
||||
}
|
||||
52
internal/osbuild2/pam_limits_conf_stage_test.go
Normal file
52
internal/osbuild2/pam_limits_conf_stage_test.go
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
package osbuild2
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/assert"
|
||||
)
|
||||
|
||||
func TestNewPamLimitsConfStageOptions(t *testing.T) {
|
||||
filename := "example.conf"
|
||||
config := []PamLimitsConfigLine{{
|
||||
Domain: "user1",
|
||||
Type: PamLimitsTypeHard,
|
||||
Item: PamLimitsItemCpu,
|
||||
Value: PamLimitsValueInt(123),
|
||||
}}
|
||||
|
||||
expectedOptions := &PamLimitsConfStageOptions{
|
||||
Filename: filename,
|
||||
Config: config,
|
||||
}
|
||||
actualOptions := NewPamLimitsConfStageOptions(filename, config)
|
||||
assert.Equal(t, expectedOptions, actualOptions)
|
||||
}
|
||||
|
||||
func TestNewPamLimitsConfStage(t *testing.T) {
|
||||
expectedStage := &Stage{
|
||||
Type: "org.osbuild.pam.limits.conf",
|
||||
Options: &PamLimitsConfStageOptions{},
|
||||
}
|
||||
actualStage := NewPamLimitsConfStage(&PamLimitsConfStageOptions{})
|
||||
assert.Equal(t, expectedStage, actualStage)
|
||||
}
|
||||
|
||||
func TestPamLimitsConfStageOptions_MarshalJSON_Invalid(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
options PamLimitsConfStageOptions
|
||||
}{
|
||||
{
|
||||
name: "empty-options",
|
||||
options: PamLimitsConfStageOptions{},
|
||||
},
|
||||
}
|
||||
for idx, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotBytes, err := json.Marshal(tt.options)
|
||||
assert.NotNilf(t, err, "json.Marshal() didn't return an error, but: %s [idx: %d]", string(gotBytes), idx)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -114,6 +114,8 @@ func (stage *Stage) UnmarshalJSON(data []byte) error {
|
|||
options = new(OSTreeInitStageOptions)
|
||||
case "org.osbuild.ostree.preptree":
|
||||
options = new(OSTreePrepTreeStageOptions)
|
||||
case "org.osbuild.pam.limits.conf":
|
||||
options = new(PamLimitsConfStageOptions)
|
||||
case "org.osbuild.truncate":
|
||||
options = new(TruncateStageOptions)
|
||||
case "org.osbuild.sfdisk":
|
||||
|
|
|
|||
|
|
@ -328,6 +328,46 @@ func TestStage_UnmarshalJSON(t *testing.T) {
|
|||
data: []byte(`{"type":"org.osbuild.locale","options":{"language":""}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "pam-limits-conf-str",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.pam.limits.conf",
|
||||
Options: &PamLimitsConfStageOptions{
|
||||
Filename: "example.conf",
|
||||
Config: []PamLimitsConfigLine{
|
||||
{
|
||||
Domain: "user1",
|
||||
Type: PamLimitsTypeHard,
|
||||
Item: PamLimitsItemNofile,
|
||||
Value: PamLimitsValueUnlimited,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.pam.limits.conf","options":{"filename":"example.conf","config":[{"domain":"user1","type":"hard","item":"nofile","value":"unlimited"}]}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "pam-limits-conf-int",
|
||||
fields: fields{
|
||||
Type: "org.osbuild.pam.limits.conf",
|
||||
Options: &PamLimitsConfStageOptions{
|
||||
Filename: "example.conf",
|
||||
Config: []PamLimitsConfigLine{
|
||||
{
|
||||
Domain: "user1",
|
||||
Type: PamLimitsTypeHard,
|
||||
Item: PamLimitsItemNofile,
|
||||
Value: PamLimitsValueInt(-1),
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
args: args{
|
||||
data: []byte(`{"type":"org.osbuild.pam.limits.conf","options":{"filename":"example.conf","config":[{"domain":"user1","type":"hard","item":"nofile","value":-1}]}}`),
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "rhsm-empty",
|
||||
fields: fields{
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue