Add DiskCustomization and all its children to the internal blueprint. Add the conversion to the images counterpart to the Convert() function.
409 lines
12 KiB
Go
409 lines
12 KiB
Go
package blueprint
|
|
|
|
import (
|
|
"fmt"
|
|
"reflect"
|
|
"strings"
|
|
|
|
"github.com/osbuild/images/pkg/disk"
|
|
)
|
|
|
|
type Customizations struct {
|
|
Hostname *string `json:"hostname,omitempty" toml:"hostname,omitempty"`
|
|
Kernel *KernelCustomization `json:"kernel,omitempty" toml:"kernel,omitempty"`
|
|
SSHKey []SSHKeyCustomization `json:"sshkey,omitempty" toml:"sshkey,omitempty"`
|
|
User []UserCustomization `json:"user,omitempty" toml:"user,omitempty"`
|
|
Group []GroupCustomization `json:"group,omitempty" toml:"group,omitempty"`
|
|
Timezone *TimezoneCustomization `json:"timezone,omitempty" toml:"timezone,omitempty"`
|
|
Locale *LocaleCustomization `json:"locale,omitempty" toml:"locale,omitempty"`
|
|
Firewall *FirewallCustomization `json:"firewall,omitempty" toml:"firewall,omitempty"`
|
|
Services *ServicesCustomization `json:"services,omitempty" toml:"services,omitempty"`
|
|
Filesystem []FilesystemCustomization `json:"filesystem,omitempty" toml:"filesystem,omitempty"`
|
|
Disk *DiskCustomization `json:"disk,omitempty" toml:"disk,omitempty"`
|
|
InstallationDevice string `json:"installation_device,omitempty" toml:"installation_device,omitempty"`
|
|
PartitioningMode string `json:"partitioning_mode,omitempty" toml:"partitioning_mode,omitempty"`
|
|
FDO *FDOCustomization `json:"fdo,omitempty" toml:"fdo,omitempty"`
|
|
OpenSCAP *OpenSCAPCustomization `json:"openscap,omitempty" toml:"openscap,omitempty"`
|
|
Ignition *IgnitionCustomization `json:"ignition,omitempty" toml:"ignition,omitempty"`
|
|
Directories []DirectoryCustomization `json:"directories,omitempty" toml:"directories,omitempty"`
|
|
Files []FileCustomization `json:"files,omitempty" toml:"files,omitempty"`
|
|
Repositories []RepositoryCustomization `json:"repositories,omitempty" toml:"repositories,omitempty"`
|
|
FIPS *bool `json:"fips,omitempty" toml:"fips,omitempty"`
|
|
Installer *InstallerCustomization `json:"installer,omitempty" toml:"installer,omitempty"`
|
|
RPM *RPMCustomization `json:"rpm,omitempty" toml:"rpm,omitempty"`
|
|
RHSM *RHSMCustomization `json:"rhsm,omitempty" toml:"rhsm,omitempty"`
|
|
CACerts *CACustomization `json:"cacerts,omitempty" toml:"cacerts,omitempty"`
|
|
}
|
|
|
|
type IgnitionCustomization struct {
|
|
Embedded *EmbeddedIgnitionCustomization `json:"embedded,omitempty" toml:"embedded,omitempty"`
|
|
FirstBoot *FirstBootIgnitionCustomization `json:"firstboot,omitempty" toml:"firstboot,omitempty"`
|
|
}
|
|
|
|
type EmbeddedIgnitionCustomization struct {
|
|
Config string `json:"config,omitempty" toml:"config,omitempty"`
|
|
}
|
|
|
|
type FirstBootIgnitionCustomization struct {
|
|
ProvisioningURL string `json:"url,omitempty" toml:"url,omitempty"`
|
|
}
|
|
|
|
type FDOCustomization struct {
|
|
ManufacturingServerURL string `json:"manufacturing_server_url,omitempty" toml:"manufacturing_server_url,omitempty"`
|
|
DiunPubKeyInsecure string `json:"diun_pub_key_insecure,omitempty" toml:"diun_pub_key_insecure,omitempty"`
|
|
// This is the output of:
|
|
// echo "sha256:$(openssl x509 -fingerprint -sha256 -noout -in diun_cert.pem | cut -d"=" -f2 | sed 's/://g')"
|
|
DiunPubKeyHash string `json:"diun_pub_key_hash,omitempty" toml:"diun_pub_key_hash,omitempty"`
|
|
DiunPubKeyRootCerts string `json:"diun_pub_key_root_certs,omitempty" toml:"diun_pub_key_root_certs,omitempty"`
|
|
DiMfgStringTypeMacIface string `json:"di_mfg_string_type_mac_iface,omitempty" toml:"di_mfg_string_type_mac_iface,omitempty"`
|
|
}
|
|
|
|
type KernelCustomization struct {
|
|
Name string `json:"name,omitempty" toml:"name,omitempty"`
|
|
Append string `json:"append" toml:"append"`
|
|
}
|
|
|
|
type SSHKeyCustomization struct {
|
|
User string `json:"user" toml:"user"`
|
|
Key string `json:"key" toml:"key"`
|
|
}
|
|
|
|
type UserCustomization struct {
|
|
Name string `json:"name" toml:"name"`
|
|
Description *string `json:"description,omitempty" toml:"description,omitempty"`
|
|
Password *string `json:"password,omitempty" toml:"password,omitempty"`
|
|
Key *string `json:"key,omitempty" toml:"key,omitempty"`
|
|
Home *string `json:"home,omitempty" toml:"home,omitempty"`
|
|
Shell *string `json:"shell,omitempty" toml:"shell,omitempty"`
|
|
Groups []string `json:"groups,omitempty" toml:"groups,omitempty"`
|
|
UID *int `json:"uid,omitempty" toml:"uid,omitempty"`
|
|
GID *int `json:"gid,omitempty" toml:"gid,omitempty"`
|
|
ExpireDate *int `json:"expiredate,omitempty" toml:"expiredate,omitempty"`
|
|
ForcePasswordReset *bool `json:"force_password_reset,omitempty" toml:"force_password_reset,omitempty"`
|
|
}
|
|
|
|
type GroupCustomization struct {
|
|
Name string `json:"name" toml:"name"`
|
|
GID *int `json:"gid,omitempty" toml:"gid,omitempty"`
|
|
}
|
|
|
|
type TimezoneCustomization struct {
|
|
Timezone *string `json:"timezone,omitempty" toml:"timezone,omitempty"`
|
|
NTPServers []string `json:"ntpservers,omitempty" toml:"ntpservers,omitempty"`
|
|
}
|
|
|
|
type LocaleCustomization struct {
|
|
Languages []string `json:"languages,omitempty" toml:"languages,omitempty"`
|
|
Keyboard *string `json:"keyboard,omitempty" toml:"keyboard,omitempty"`
|
|
}
|
|
|
|
type FirewallCustomization struct {
|
|
Ports []string `json:"ports,omitempty" toml:"ports,omitempty"`
|
|
Services *FirewallServicesCustomization `json:"services,omitempty" toml:"services,omitempty"`
|
|
Zones []FirewallZoneCustomization `json:"zones,omitempty" toml:"zones,omitempty"`
|
|
}
|
|
|
|
type FirewallZoneCustomization struct {
|
|
Name *string `json:"name,omitempty" toml:"name,omitempty"`
|
|
Sources []string `json:"sources,omitempty" toml:"sources,omitempty"`
|
|
}
|
|
|
|
type FirewallServicesCustomization struct {
|
|
Enabled []string `json:"enabled,omitempty" toml:"enabled,omitempty"`
|
|
Disabled []string `json:"disabled,omitempty" toml:"disabled,omitempty"`
|
|
}
|
|
|
|
type ServicesCustomization struct {
|
|
Enabled []string `json:"enabled,omitempty" toml:"enabled,omitempty"`
|
|
Disabled []string `json:"disabled,omitempty" toml:"disabled,omitempty"`
|
|
Masked []string `json:"masked,omitempty" toml:"masked,omitempty"`
|
|
}
|
|
|
|
type OpenSCAPCustomization struct {
|
|
DataStream string `json:"datastream,omitempty" toml:"datastream,omitempty"`
|
|
ProfileID string `json:"profile_id,omitempty" toml:"profile_id,omitempty"`
|
|
Tailoring *OpenSCAPTailoringCustomizations `json:"tailoring,omitempty" toml:"tailoring,omitempty"`
|
|
JSONTailoring *OpenSCAPJSONTailoringCustomizations `json:"json_tailoring,omitempty" toml:"json_tailoring,omitempty"`
|
|
PolicyID string `json:"policy_id,omitempty" toml:"policy_id,omitempty"`
|
|
}
|
|
|
|
type OpenSCAPTailoringCustomizations struct {
|
|
Selected []string `json:"selected,omitempty" toml:"selected,omitempty"`
|
|
Unselected []string `json:"unselected,omitempty" toml:"unselected,omitempty"`
|
|
}
|
|
|
|
type OpenSCAPJSONTailoringCustomizations struct {
|
|
ProfileID string `json:"profile_id,omitempty" toml:"profile_id,omitempty"`
|
|
Filepath string `json:"filepath,omitempty" toml:"filepath,omitempty"`
|
|
}
|
|
|
|
type CACustomization struct {
|
|
PEMCerts []string `json:"pem_certs,omitempty" toml:"pem_certs,omitempty"`
|
|
}
|
|
|
|
type CustomizationError struct {
|
|
Message string
|
|
}
|
|
|
|
func (e *CustomizationError) Error() string {
|
|
return e.Message
|
|
}
|
|
|
|
// CheckCustomizations returns an error of type `CustomizationError`
|
|
// if `c` has any customizations not specified in `allowed`
|
|
func (c *Customizations) CheckAllowed(allowed ...string) error {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
|
|
allowMap := make(map[string]bool)
|
|
|
|
for _, a := range allowed {
|
|
allowMap[a] = true
|
|
}
|
|
|
|
t := reflect.TypeOf(*c)
|
|
v := reflect.ValueOf(*c)
|
|
|
|
for i := 0; i < t.NumField(); i++ {
|
|
|
|
empty := false
|
|
field := v.Field(i)
|
|
|
|
switch field.Kind() {
|
|
case reflect.String:
|
|
if field.String() == "" {
|
|
empty = true
|
|
}
|
|
case reflect.Array, reflect.Slice:
|
|
if field.Len() == 0 {
|
|
empty = true
|
|
}
|
|
case reflect.Ptr:
|
|
if field.IsNil() {
|
|
empty = true
|
|
}
|
|
default:
|
|
panic(fmt.Sprintf("unhandled customization field type %s, %s", v.Kind(), t.Field(i).Name))
|
|
|
|
}
|
|
|
|
if !empty && !allowMap[t.Field(i).Name] {
|
|
return &CustomizationError{fmt.Sprintf("'%s' is not allowed", t.Field(i).Name)}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func (c *Customizations) GetHostname() *string {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
return c.Hostname
|
|
}
|
|
|
|
func (c *Customizations) GetPrimaryLocale() (*string, *string) {
|
|
if c == nil {
|
|
return nil, nil
|
|
}
|
|
if c.Locale == nil {
|
|
return nil, nil
|
|
}
|
|
if len(c.Locale.Languages) == 0 {
|
|
return nil, c.Locale.Keyboard
|
|
}
|
|
return &c.Locale.Languages[0], c.Locale.Keyboard
|
|
}
|
|
|
|
func (c *Customizations) GetTimezoneSettings() (*string, []string) {
|
|
if c == nil {
|
|
return nil, nil
|
|
}
|
|
if c.Timezone == nil {
|
|
return nil, nil
|
|
}
|
|
return c.Timezone.Timezone, c.Timezone.NTPServers
|
|
}
|
|
|
|
func (c *Customizations) GetUsers() []UserCustomization {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
|
|
users := []UserCustomization{}
|
|
|
|
// prepend sshkey for backwards compat (overridden by users)
|
|
if len(c.SSHKey) > 0 {
|
|
for _, k := range c.SSHKey {
|
|
key := k.Key
|
|
users = append(users, UserCustomization{
|
|
Name: k.User,
|
|
Key: &key,
|
|
})
|
|
}
|
|
}
|
|
|
|
users = append(users, c.User...)
|
|
|
|
// sanitize user home directory in blueprint: if it has a trailing slash,
|
|
// it might lead to the directory not getting the correct selinux labels
|
|
for idx := range users {
|
|
u := users[idx]
|
|
if u.Home != nil {
|
|
homedir := strings.TrimRight(*u.Home, "/")
|
|
u.Home = &homedir
|
|
users[idx] = u
|
|
}
|
|
}
|
|
return users
|
|
}
|
|
|
|
func (c *Customizations) GetGroups() []GroupCustomization {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
|
|
return c.Group
|
|
}
|
|
|
|
func (c *Customizations) GetKernel() *KernelCustomization {
|
|
var name string
|
|
var append string
|
|
if c != nil && c.Kernel != nil {
|
|
name = c.Kernel.Name
|
|
append = c.Kernel.Append
|
|
}
|
|
|
|
if name == "" {
|
|
name = "kernel"
|
|
}
|
|
|
|
return &KernelCustomization{
|
|
Name: name,
|
|
Append: append,
|
|
}
|
|
}
|
|
|
|
func (c *Customizations) GetFirewall() *FirewallCustomization {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
|
|
return c.Firewall
|
|
}
|
|
|
|
func (c *Customizations) GetServices() *ServicesCustomization {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
|
|
return c.Services
|
|
}
|
|
|
|
func (c *Customizations) GetFilesystems() []FilesystemCustomization {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
return c.Filesystem
|
|
}
|
|
|
|
func (c *Customizations) GetFilesystemsMinSize() uint64 {
|
|
if c == nil {
|
|
return 0
|
|
}
|
|
var agg uint64
|
|
for _, m := range c.Filesystem {
|
|
agg += m.MinSize
|
|
}
|
|
// This ensures that file system customization `size` is a multiple of
|
|
// sector size (512)
|
|
if agg%512 != 0 {
|
|
agg = (agg/512 + 1) * 512
|
|
}
|
|
return agg
|
|
}
|
|
|
|
// GetPartitioningMode converts the string to a disk.PartitioningMode type
|
|
func (c *Customizations) GetPartitioningMode() (disk.PartitioningMode, error) {
|
|
if c == nil {
|
|
return disk.DefaultPartitioningMode, nil
|
|
}
|
|
|
|
switch c.PartitioningMode {
|
|
case "raw":
|
|
return disk.RawPartitioningMode, nil
|
|
case "lvm":
|
|
return disk.LVMPartitioningMode, nil
|
|
case "auto-lvm":
|
|
return disk.AutoLVMPartitioningMode, nil
|
|
case "":
|
|
return disk.DefaultPartitioningMode, nil
|
|
default:
|
|
return disk.DefaultPartitioningMode, fmt.Errorf("invalid partitioning mode '%s'", c.PartitioningMode)
|
|
}
|
|
}
|
|
|
|
func (c *Customizations) GetInstallationDevice() string {
|
|
if c == nil || c.InstallationDevice == "" {
|
|
return ""
|
|
}
|
|
return c.InstallationDevice
|
|
}
|
|
|
|
func (c *Customizations) GetFDO() *FDOCustomization {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
return c.FDO
|
|
}
|
|
|
|
func (c *Customizations) GetOpenSCAP() *OpenSCAPCustomization {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
return c.OpenSCAP
|
|
}
|
|
|
|
func (c *Customizations) GetIgnition() *IgnitionCustomization {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
return c.Ignition
|
|
}
|
|
|
|
func (c *Customizations) GetDirectories() []DirectoryCustomization {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
return c.Directories
|
|
}
|
|
|
|
func (c *Customizations) GetFiles() []FileCustomization {
|
|
if c == nil {
|
|
return nil
|
|
}
|
|
return c.Files
|
|
}
|
|
|
|
func (c *Customizations) GetRepositories() ([]RepositoryCustomization, error) {
|
|
if c == nil {
|
|
return nil, nil
|
|
}
|
|
|
|
for idx := range c.Repositories {
|
|
err := validateCustomRepository(&c.Repositories[idx])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
}
|
|
|
|
return c.Repositories, nil
|
|
}
|
|
|
|
func (c *Customizations) GetFIPS() bool {
|
|
if c == nil || c.FIPS == nil {
|
|
return false
|
|
}
|
|
return *c.FIPS
|
|
}
|