diff --git a/go.mod b/go.mod index 8f0fb5d807..871b8d8198 100644 --- a/go.mod +++ b/go.mod @@ -15,7 +15,7 @@ require ( github.com/aliyun/alibaba-cloud-sdk-go v1.61.1442 github.com/aliyun/aliyun-oss-go-sdk v2.0.3+incompatible github.com/aws/aws-sdk-go v1.44.204 - github.com/coreos/butane v0.17.1-0.20230309182032-fac76883d698 + github.com/coreos/butane v0.18.0 github.com/coreos/go-semver v0.3.1 github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e github.com/coreos/go-systemd/v22 v22.5.0 diff --git a/go.sum b/go.sum index e953dc4c1b..a5315c8341 100644 --- a/go.sum +++ b/go.sum @@ -45,8 +45,8 @@ github.com/clarketm/json v1.17.1 h1:U1IxjqJkJ7bRK4L6dyphmoO840P6bdhPdbbLySourqI= github.com/clarketm/json v1.17.1/go.mod h1:ynr2LRfb0fQU34l07csRNBTcivjySLLiY1YzQqKVfdo= github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= github.com/cncf/udpa/go v0.0.0-20191209042840-269d4d468f6f/go.mod h1:M8M6+tZqaGXZJjfX53e64911xZQV5JYwmTeXPW+k8Sc= -github.com/coreos/butane v0.17.1-0.20230309182032-fac76883d698 h1:Tmjg01aPzkOXgbMbDUA/sAKEZL2y4skcjrkkzIY/KRg= -github.com/coreos/butane v0.17.1-0.20230309182032-fac76883d698/go.mod h1:oLR7xKzJB3o1WIdLtfHlPwrSwvMmeV/zknkwOznRu88= +github.com/coreos/butane v0.18.0 h1:WDeUC/dX1MUUVPwiqsQetQZsShNKk+2lrRXlC4ZhnZA= +github.com/coreos/butane v0.18.0/go.mod h1:oLR7xKzJB3o1WIdLtfHlPwrSwvMmeV/zknkwOznRu88= github.com/coreos/go-json v0.0.0-20230131223807-18775e0fb4fb h1:rmqyI19j3Z/74bIRhuC59RB442rXUazKNueVpfJPxg4= github.com/coreos/go-json v0.0.0-20230131223807-18775e0fb4fb/go.mod h1:rcFZM3uxVvdyNmsAV2jopgPD1cs5SPWJWU5dOz2LUnw= github.com/coreos/go-semver v0.3.1 h1:yi21YpKnrx1gt5R+la8n5WgS0kCrsPp33dmEyHReZr4= diff --git a/vendor/github.com/coreos/butane/base/util/file.go b/vendor/github.com/coreos/butane/base/util/file.go index 4336ff2c6b..ecc124060b 100644 --- a/vendor/github.com/coreos/butane/base/util/file.go +++ b/vendor/github.com/coreos/butane/base/util/file.go @@ -15,6 +15,7 @@ package util import ( + "os" "path/filepath" "strings" @@ -36,6 +37,19 @@ func EnsurePathWithinFilesDir(path, filesDir string) error { return nil } +func ReadLocalFile(configPath, filesDir string) ([]byte, error) { + if filesDir == "" { + // a files dir isn't configured; refuse to read anything + return nil, common.ErrNoFilesDir + } + // calculate file path within FilesDir and check for path traversal + filePath := filepath.Join(filesDir, filepath.FromSlash(configPath)) + if err := EnsurePathWithinFilesDir(filePath, filesDir); err != nil { + return nil, err + } + return os.ReadFile(filePath) +} + // CheckForDecimalMode fails if the specified mode appears to have been // incorrectly specified in decimal instead of octal. func CheckForDecimalMode(mode int, directory bool) error { diff --git a/vendor/github.com/coreos/butane/base/v0_2/translate.go b/vendor/github.com/coreos/butane/base/v0_2/translate.go index dbe4df5f74..ded46dbcf5 100644 --- a/vendor/github.com/coreos/butane/base/v0_2/translate.go +++ b/vendor/github.com/coreos/butane/base/v0_2/translate.go @@ -118,26 +118,11 @@ func translateResource(from Resource, options common.TranslateOptions) (to types if from.Local != nil { c := path.New("yaml", "local") - - if options.FilesDir == "" { - r.AddOnError(c, common.ErrNoFilesDir) - return - } - - // calculate file path within FilesDir and check for - // path traversal - filePath := filepath.Join(options.FilesDir, filepath.FromSlash(*from.Local)) - if err := baseutil.EnsurePathWithinFilesDir(filePath, options.FilesDir); err != nil { - r.AddOnError(c, err) - return - } - - contents, err := os.ReadFile(filePath) + contents, err := baseutil.ReadLocalFile(*from.Local, options.FilesDir) if err != nil { r.AddOnError(c, err) return } - src, compression, err := baseutil.MakeDataURL(contents, to.Compression, !options.NoResourceAutoCompression) if err != nil { r.AddOnError(c, err) diff --git a/vendor/github.com/coreos/butane/base/v0_3/translate.go b/vendor/github.com/coreos/butane/base/v0_3/translate.go index 9d1d5c8efa..a539b51883 100644 --- a/vendor/github.com/coreos/butane/base/v0_3/translate.go +++ b/vendor/github.com/coreos/butane/base/v0_3/translate.go @@ -125,26 +125,11 @@ func translateResource(from Resource, options common.TranslateOptions) (to types if from.Local != nil { c := path.New("yaml", "local") - - if options.FilesDir == "" { - r.AddOnError(c, common.ErrNoFilesDir) - return - } - - // calculate file path within FilesDir and check for - // path traversal - filePath := filepath.Join(options.FilesDir, filepath.FromSlash(*from.Local)) - if err := baseutil.EnsurePathWithinFilesDir(filePath, options.FilesDir); err != nil { - r.AddOnError(c, err) - return - } - - contents, err := os.ReadFile(filePath) + contents, err := baseutil.ReadLocalFile(*from.Local, options.FilesDir) if err != nil { r.AddOnError(c, err) return } - src, compression, err := baseutil.MakeDataURL(contents, to.Compression, !options.NoResourceAutoCompression) if err != nil { r.AddOnError(c, err) diff --git a/vendor/github.com/coreos/butane/base/v0_4/translate.go b/vendor/github.com/coreos/butane/base/v0_4/translate.go index a8c8b0b6f7..74c0c187d4 100644 --- a/vendor/github.com/coreos/butane/base/v0_4/translate.go +++ b/vendor/github.com/coreos/butane/base/v0_4/translate.go @@ -140,26 +140,11 @@ func translateResource(from Resource, options common.TranslateOptions) (to types if from.Local != nil { c := path.New("yaml", "local") - - if options.FilesDir == "" { - r.AddOnError(c, common.ErrNoFilesDir) - return - } - - // calculate file path within FilesDir and check for - // path traversal - filePath := filepath.Join(options.FilesDir, filepath.FromSlash(*from.Local)) - if err := baseutil.EnsurePathWithinFilesDir(filePath, options.FilesDir); err != nil { - r.AddOnError(c, err) - return - } - - contents, err := os.ReadFile(filePath) + contents, err := baseutil.ReadLocalFile(*from.Local, options.FilesDir) if err != nil { r.AddOnError(c, err) return } - src, compression, err := baseutil.MakeDataURL(contents, to.Compression, !options.NoResourceAutoCompression) if err != nil { r.AddOnError(c, err) diff --git a/vendor/github.com/coreos/butane/base/v0_5_exp/schema.go b/vendor/github.com/coreos/butane/base/v0_5/schema.go similarity index 82% rename from vendor/github.com/coreos/butane/base/v0_5_exp/schema.go rename to vendor/github.com/coreos/butane/base/v0_5/schema.go index 524b49bb4b..8804bf7bab 100644 --- a/vendor/github.com/coreos/butane/base/v0_5_exp/schema.go +++ b/vendor/github.com/coreos/butane/base/v0_5/schema.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v0_5_exp +package v0_5 type Clevis struct { Custom ClevisCustom `yaml:"custom"` @@ -54,8 +54,9 @@ type Disk struct { } type Dropin struct { - Contents *string `yaml:"contents"` - Name string `yaml:"name"` + Contents *string `yaml:"contents"` + ContentsLocal *string `yaml:"contents_local"` + Name string `yaml:"name"` } type File struct { @@ -166,20 +167,21 @@ type PasswdGroup struct { } type PasswdUser struct { - Gecos *string `yaml:"gecos"` - Groups []Group `yaml:"groups"` - HomeDir *string `yaml:"home_dir"` - Name string `yaml:"name"` - NoCreateHome *bool `yaml:"no_create_home"` - NoLogInit *bool `yaml:"no_log_init"` - NoUserGroup *bool `yaml:"no_user_group"` - PasswordHash *string `yaml:"password_hash"` - PrimaryGroup *string `yaml:"primary_group"` - ShouldExist *bool `yaml:"should_exist"` - SSHAuthorizedKeys []SSHAuthorizedKey `yaml:"ssh_authorized_keys"` - Shell *string `yaml:"shell"` - System *bool `yaml:"system"` - UID *int `yaml:"uid"` + Gecos *string `yaml:"gecos"` + Groups []Group `yaml:"groups"` + HomeDir *string `yaml:"home_dir"` + Name string `yaml:"name"` + NoCreateHome *bool `yaml:"no_create_home"` + NoLogInit *bool `yaml:"no_log_init"` + NoUserGroup *bool `yaml:"no_user_group"` + PasswordHash *string `yaml:"password_hash"` + PrimaryGroup *string `yaml:"primary_group"` + ShouldExist *bool `yaml:"should_exist"` + SSHAuthorizedKeys []SSHAuthorizedKey `yaml:"ssh_authorized_keys"` + SSHAuthorizedKeysLocal []string `yaml:"ssh_authorized_keys_local"` + Shell *string `yaml:"shell"` + System *bool `yaml:"system"` + UID *int `yaml:"uid"` } type Proxy struct { @@ -247,11 +249,12 @@ type Tree struct { } type Unit struct { - Contents *string `yaml:"contents"` - Dropins []Dropin `yaml:"dropins"` - Enabled *bool `yaml:"enabled"` - Mask *bool `yaml:"mask"` - Name string `yaml:"name"` + Contents *string `yaml:"contents"` + ContentsLocal *string `yaml:"contents_local"` + Dropins []Dropin `yaml:"dropins"` + Enabled *bool `yaml:"enabled"` + Mask *bool `yaml:"mask"` + Name string `yaml:"name"` } type Verification struct { diff --git a/vendor/github.com/coreos/butane/base/v0_5_exp/translate.go b/vendor/github.com/coreos/butane/base/v0_5/translate.go similarity index 78% rename from vendor/github.com/coreos/butane/base/v0_5_exp/translate.go rename to vendor/github.com/coreos/butane/base/v0_5/translate.go index b063b9065e..4cb00f295c 100644 --- a/vendor/github.com/coreos/butane/base/v0_5_exp/translate.go +++ b/vendor/github.com/coreos/butane/base/v0_5/translate.go @@ -12,13 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v0_5_exp +package v0_5 import ( "fmt" "os" slashpath "path" "path/filepath" + "regexp" "strings" "text/template" @@ -86,6 +87,8 @@ func (c Config) ToIgn3_4Unvalidated(options common.TranslateOptions) (types.Conf tr.AddCustomTranslator(translateDirectory) tr.AddCustomTranslator(translateLink) tr.AddCustomTranslator(translateResource) + tr.AddCustomTranslator(translatePasswdUser) + tr.AddCustomTranslator(translateUnit) tm, r := translate.Prefixed(tr, "ignition", &c.Ignition, &ret.Ignition) tm.AddTranslation(path.New("yaml", "version"), path.New("json", "ignition", "version")) @@ -140,26 +143,11 @@ func translateResource(from Resource, options common.TranslateOptions) (to types if from.Local != nil { c := path.New("yaml", "local") - - if options.FilesDir == "" { - r.AddOnError(c, common.ErrNoFilesDir) - return - } - - // calculate file path within FilesDir and check for - // path traversal - filePath := filepath.Join(options.FilesDir, filepath.FromSlash(*from.Local)) - if err := baseutil.EnsurePathWithinFilesDir(filePath, options.FilesDir); err != nil { - r.AddOnError(c, err) - return - } - - contents, err := os.ReadFile(filePath) + contents, err := baseutil.ReadLocalFile(*from.Local, options.FilesDir) if err != nil { r.AddOnError(c, err) return } - src, compression, err := baseutil.MakeDataURL(contents, to.Compression, !options.NoResourceAutoCompression) if err != nil { r.AddOnError(c, err) @@ -212,6 +200,93 @@ func translateLink(from Link, options common.TranslateOptions) (to types.Link, t return } +func translatePasswdUser(from PasswdUser, options common.TranslateOptions) (to types.PasswdUser, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tm, r = translate.Prefixed(tr, "gecos", &from.Gecos, &to.Gecos) + translate.MergeP(tr, tm, &r, "groups", &from.Groups, &to.Groups) + translate.MergeP2(tr, tm, &r, "home_dir", &from.HomeDir, "homeDir", &to.HomeDir) + translate.MergeP(tr, tm, &r, "name", &from.Name, &to.Name) + translate.MergeP2(tr, tm, &r, "no_create_home", &from.NoCreateHome, "noCreateHome", &to.NoCreateHome) + translate.MergeP2(tr, tm, &r, "no_log_init", &from.NoLogInit, "noLogInit", &to.NoLogInit) + translate.MergeP2(tr, tm, &r, "no_user_group", &from.NoUserGroup, "noUserGroup", &to.NoUserGroup) + translate.MergeP2(tr, tm, &r, "password_hash", &from.PasswordHash, "passwordHash", &to.PasswordHash) + translate.MergeP2(tr, tm, &r, "primary_group", &from.PrimaryGroup, "primaryGroup", &to.PrimaryGroup) + translate.MergeP(tr, tm, &r, "shell", &from.Shell, &to.Shell) + translate.MergeP2(tr, tm, &r, "should_exist", &from.ShouldExist, "shouldExist", &to.ShouldExist) + translate.MergeP2(tr, tm, &r, "ssh_authorized_keys", &from.SSHAuthorizedKeys, "sshAuthorizedKeys", &to.SSHAuthorizedKeys) + translate.MergeP(tr, tm, &r, "system", &from.System, &to.System) + translate.MergeP(tr, tm, &r, "uid", &from.UID, &to.UID) + + if len(from.SSHAuthorizedKeysLocal) > 0 { + c := path.New("yaml", "ssh_authorized_keys_local") + tm.AddTranslation(c, path.New("json", "sshAuthorizedKeys")) + + if options.FilesDir == "" { + r.AddOnError(c, common.ErrNoFilesDir) + return + } + + for keyFileIndex, sshKeyFile := range from.SSHAuthorizedKeysLocal { + sshKeys, err := baseutil.ReadLocalFile(sshKeyFile, options.FilesDir) + if err != nil { + r.AddOnError(c.Append(keyFileIndex), err) + continue + } + for _, line := range regexp.MustCompile("\r?\n").Split(string(sshKeys), -1) { + if line == "" { + continue + } + tm.AddTranslation(c.Append(keyFileIndex), path.New("json", "sshAuthorizedKeys", len(to.SSHAuthorizedKeys))) + to.SSHAuthorizedKeys = append(to.SSHAuthorizedKeys, types.SSHAuthorizedKey(line)) + } + } + } + + return +} + +func translateUnit(from Unit, options common.TranslateOptions) (to types.Unit, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tr.AddCustomTranslator(translateDropin) + tm, r = translate.Prefixed(tr, "contents", &from.Contents, &to.Contents) + translate.MergeP(tr, tm, &r, "dropins", &from.Dropins, &to.Dropins) + translate.MergeP(tr, tm, &r, "enabled", &from.Enabled, &to.Enabled) + translate.MergeP(tr, tm, &r, "mask", &from.Mask, &to.Mask) + translate.MergeP(tr, tm, &r, "name", &from.Name, &to.Name) + + if util.NotEmpty(from.ContentsLocal) { + c := path.New("yaml", "contents_local") + contents, err := baseutil.ReadLocalFile(*from.ContentsLocal, options.FilesDir) + if err != nil { + r.AddOnError(c, err) + return + } + tm.AddTranslation(c, path.New("json", "contents")) + to.Contents = util.StrToPtr(string(contents)) + } + + return +} + +func translateDropin(from Dropin, options common.TranslateOptions) (to types.Dropin, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tm, r = translate.Prefixed(tr, "contents", &from.Contents, &to.Contents) + translate.MergeP(tr, tm, &r, "name", &from.Name, &to.Name) + + if util.NotEmpty(from.ContentsLocal) { + c := path.New("yaml", "contents_local") + contents, err := baseutil.ReadLocalFile(*from.ContentsLocal, options.FilesDir) + if err != nil { + r.AddOnError(c, err) + return + } + tm.AddTranslation(c, path.New("json", "contents")) + to.Contents = util.StrToPtr(string(contents)) + } + + return +} + func (c Config) processTrees(ret *types.Config, options common.TranslateOptions) (translate.TranslationSet, report.Report) { ts := translate.NewTranslationSet("yaml", "json") var r report.Report diff --git a/vendor/github.com/coreos/butane/base/v0_5_exp/util.go b/vendor/github.com/coreos/butane/base/v0_5/util.go similarity index 99% rename from vendor/github.com/coreos/butane/base/v0_5_exp/util.go rename to vendor/github.com/coreos/butane/base/v0_5/util.go index 0219ed03c0..048d7c8263 100644 --- a/vendor/github.com/coreos/butane/base/v0_5_exp/util.go +++ b/vendor/github.com/coreos/butane/base/v0_5/util.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v0_5_exp +package v0_5 import ( "github.com/coreos/ignition/v2/config/v3_4/types" diff --git a/vendor/github.com/coreos/butane/base/v0_5_exp/validate.go b/vendor/github.com/coreos/butane/base/v0_5/validate.go similarity index 82% rename from vendor/github.com/coreos/butane/base/v0_5_exp/validate.go rename to vendor/github.com/coreos/butane/base/v0_5/validate.go index 715a1bb423..32572e644b 100644 --- a/vendor/github.com/coreos/butane/base/v0_5_exp/validate.go +++ b/vendor/github.com/coreos/butane/base/v0_5/validate.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v0_5_exp +package v0_5 import ( baseutil "github.com/coreos/butane/base/util" @@ -76,3 +76,17 @@ func (t Tree) Validate(c path.ContextPath) (r report.Report) { } return } + +func (rs Unit) Validate(c path.ContextPath) (r report.Report) { + if rs.ContentsLocal != nil && rs.Contents != nil { + r.AddOnError(c.Append("contents_local"), common.ErrTooManySystemdSources) + } + return +} + +func (rs Dropin) Validate(c path.ContextPath) (r report.Report) { + if rs.ContentsLocal != nil && rs.Contents != nil { + r.AddOnError(c.Append("contents_local"), common.ErrTooManySystemdSources) + } + return +} diff --git a/vendor/github.com/coreos/butane/base/v0_6_exp/schema.go b/vendor/github.com/coreos/butane/base/v0_6_exp/schema.go new file mode 100644 index 0000000000..67e7b95c4e --- /dev/null +++ b/vendor/github.com/coreos/butane/base/v0_6_exp/schema.go @@ -0,0 +1,262 @@ +// Copyright 2020 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package v0_6_exp + +type Clevis struct { + Custom ClevisCustom `yaml:"custom"` + Tang []Tang `yaml:"tang"` + Threshold *int `yaml:"threshold"` + Tpm2 *bool `yaml:"tpm2"` +} + +type ClevisCustom struct { + Config *string `yaml:"config"` + NeedsNetwork *bool `yaml:"needs_network"` + Pin *string `yaml:"pin"` +} + +type Config struct { + Version string `yaml:"version"` + Variant string `yaml:"variant"` + Ignition Ignition `yaml:"ignition"` + KernelArguments KernelArguments `yaml:"kernel_arguments"` + Passwd Passwd `yaml:"passwd"` + Storage Storage `yaml:"storage"` + Systemd Systemd `yaml:"systemd"` +} + +type Device string + +type Directory struct { + Group NodeGroup `yaml:"group"` + Overwrite *bool `yaml:"overwrite"` + Path string `yaml:"path"` + User NodeUser `yaml:"user"` + Mode *int `yaml:"mode"` +} + +type Disk struct { + Device string `yaml:"device"` + Partitions []Partition `yaml:"partitions"` + WipeTable *bool `yaml:"wipe_table"` +} + +type Dropin struct { + Contents *string `yaml:"contents"` + ContentsLocal *string `yaml:"contents_local"` + Name string `yaml:"name"` +} + +type File struct { + Group NodeGroup `yaml:"group"` + Overwrite *bool `yaml:"overwrite"` + Path string `yaml:"path"` + User NodeUser `yaml:"user"` + Append []Resource `yaml:"append"` + Contents Resource `yaml:"contents"` + Mode *int `yaml:"mode"` +} + +type Filesystem struct { + Device string `yaml:"device"` + Format *string `yaml:"format"` + Label *string `yaml:"label"` + MountOptions []string `yaml:"mount_options"` + Options []string `yaml:"options"` + Path *string `yaml:"path"` + UUID *string `yaml:"uuid"` + WipeFilesystem *bool `yaml:"wipe_filesystem"` + WithMountUnit *bool `yaml:"with_mount_unit" butane:"auto_skip"` // Added, not in Ignition spec +} + +type Group string + +type HTTPHeader struct { + Name string `yaml:"name"` + Value *string `yaml:"value"` +} + +type HTTPHeaders []HTTPHeader + +type Ignition struct { + Config IgnitionConfig `yaml:"config"` + Proxy Proxy `yaml:"proxy"` + Security Security `yaml:"security"` + Timeouts Timeouts `yaml:"timeouts"` +} + +type IgnitionConfig struct { + Merge []Resource `yaml:"merge"` + Replace Resource `yaml:"replace"` +} + +type KernelArgument string + +type KernelArguments struct { + ShouldExist []KernelArgument `yaml:"should_exist"` + ShouldNotExist []KernelArgument `yaml:"should_not_exist"` +} + +type Link struct { + Group NodeGroup `yaml:"group"` + Overwrite *bool `yaml:"overwrite"` + Path string `yaml:"path"` + User NodeUser `yaml:"user"` + Hard *bool `yaml:"hard"` + Target *string `yaml:"target"` +} + +type Luks struct { + Clevis Clevis `yaml:"clevis"` + Device *string `yaml:"device"` + Discard *bool `yaml:"discard"` + KeyFile Resource `yaml:"key_file"` + Label *string `yaml:"label"` + Name string `yaml:"name"` + OpenOptions []string `yaml:"open_options"` + Options []string `yaml:"options"` + UUID *string `yaml:"uuid"` + WipeVolume *bool `yaml:"wipe_volume"` +} + +type NodeGroup struct { + ID *int `yaml:"id"` + Name *string `yaml:"name"` +} + +type NodeUser struct { + ID *int `yaml:"id"` + Name *string `yaml:"name"` +} + +type Partition struct { + GUID *string `yaml:"guid"` + Label *string `yaml:"label"` + Number int `yaml:"number"` + Resize *bool `yaml:"resize"` + ShouldExist *bool `yaml:"should_exist"` + SizeMiB *int `yaml:"size_mib"` + StartMiB *int `yaml:"start_mib"` + TypeGUID *string `yaml:"type_guid"` + WipePartitionEntry *bool `yaml:"wipe_partition_entry"` +} + +type Passwd struct { + Groups []PasswdGroup `yaml:"groups"` + Users []PasswdUser `yaml:"users"` +} + +type PasswdGroup struct { + Gid *int `yaml:"gid"` + Name string `yaml:"name"` + PasswordHash *string `yaml:"password_hash"` + ShouldExist *bool `yaml:"should_exist"` + System *bool `yaml:"system"` +} + +type PasswdUser struct { + Gecos *string `yaml:"gecos"` + Groups []Group `yaml:"groups"` + HomeDir *string `yaml:"home_dir"` + Name string `yaml:"name"` + NoCreateHome *bool `yaml:"no_create_home"` + NoLogInit *bool `yaml:"no_log_init"` + NoUserGroup *bool `yaml:"no_user_group"` + PasswordHash *string `yaml:"password_hash"` + PrimaryGroup *string `yaml:"primary_group"` + ShouldExist *bool `yaml:"should_exist"` + SSHAuthorizedKeys []SSHAuthorizedKey `yaml:"ssh_authorized_keys"` + SSHAuthorizedKeysLocal []string `yaml:"ssh_authorized_keys_local"` + Shell *string `yaml:"shell"` + System *bool `yaml:"system"` + UID *int `yaml:"uid"` +} + +type Proxy struct { + HTTPProxy *string `yaml:"http_proxy"` + HTTPSProxy *string `yaml:"https_proxy"` + NoProxy []string `yaml:"no_proxy"` +} + +type Raid struct { + Devices []Device `yaml:"devices"` + Level *string `yaml:"level"` + Name string `yaml:"name"` + Options []string `yaml:"options"` + Spares *int `yaml:"spares"` +} + +type Resource struct { + Compression *string `yaml:"compression"` + HTTPHeaders HTTPHeaders `yaml:"http_headers"` + Source *string `yaml:"source"` + Inline *string `yaml:"inline"` // Added, not in ignition spec + Local *string `yaml:"local"` // Added, not in ignition spec + Verification Verification `yaml:"verification"` +} + +type SSHAuthorizedKey string + +type Security struct { + TLS TLS `yaml:"tls"` +} + +type Storage struct { + Directories []Directory `yaml:"directories"` + Disks []Disk `yaml:"disks"` + Files []File `yaml:"files"` + Filesystems []Filesystem `yaml:"filesystems"` + Links []Link `yaml:"links"` + Luks []Luks `yaml:"luks"` + Raid []Raid `yaml:"raid"` + Trees []Tree `yaml:"trees" butane:"auto_skip"` // Added, not in ignition spec +} + +type Systemd struct { + Units []Unit `yaml:"units"` +} + +type Tang struct { + Thumbprint *string `yaml:"thumbprint"` + URL string `yaml:"url"` + Advertisement *string `yaml:"advertisement"` +} + +type TLS struct { + CertificateAuthorities []Resource `yaml:"certificate_authorities"` +} + +type Timeouts struct { + HTTPResponseHeaders *int `yaml:"http_response_headers"` + HTTPTotal *int `yaml:"http_total"` +} + +type Tree struct { + Local string `yaml:"local"` + Path *string `yaml:"path"` +} + +type Unit struct { + Contents *string `yaml:"contents"` + ContentsLocal *string `yaml:"contents_local"` + Dropins []Dropin `yaml:"dropins"` + Enabled *bool `yaml:"enabled"` + Mask *bool `yaml:"mask"` + Name string `yaml:"name"` +} + +type Verification struct { + Hash *string `yaml:"hash"` +} diff --git a/vendor/github.com/coreos/butane/base/v0_6_exp/translate.go b/vendor/github.com/coreos/butane/base/v0_6_exp/translate.go new file mode 100644 index 0000000000..587e0139ce --- /dev/null +++ b/vendor/github.com/coreos/butane/base/v0_6_exp/translate.go @@ -0,0 +1,501 @@ +// Copyright 2020 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package v0_6_exp + +import ( + "fmt" + "os" + slashpath "path" + "path/filepath" + "regexp" + "strings" + "text/template" + + baseutil "github.com/coreos/butane/base/util" + "github.com/coreos/butane/config/common" + "github.com/coreos/butane/translate" + + "github.com/coreos/go-systemd/v22/unit" + "github.com/coreos/ignition/v2/config/util" + "github.com/coreos/ignition/v2/config/v3_5_experimental/types" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +var ( + mountUnitTemplate = template.Must(template.New("unit").Parse(` +{{- define "options" }} + {{- if or .MountOptions .Remote }} +Options= + {{- range $i, $opt := .MountOptions }} + {{- if $i }},{{ end }} + {{- $opt }} + {{- end }} + {{- if .Remote }}{{ if .MountOptions }},{{ end }}_netdev{{ end }} + {{- end }} +{{- end -}} + +# Generated by Butane +{{- if .Swap }} +[Swap] +What={{.Device}} +{{- template "options" . }} + +[Install] +RequiredBy=swap.target +{{- else }} +[Unit] +Requires=systemd-fsck@{{.EscapedDevice}}.service +After=systemd-fsck@{{.EscapedDevice}}.service + +[Mount] +Where={{.Path}} +What={{.Device}} +Type={{.Format}} +{{- template "options" . }} + +[Install] +{{- if .Remote }} +RequiredBy=remote-fs.target +{{- else }} +RequiredBy=local-fs.target +{{- end }} +{{- end }}`)) +) + +// ToIgn3_5Unvalidated translates the config to an Ignition config. It also returns the set of translations +// it did so paths in the resultant config can be tracked back to their source in the source config. +// No config validation is performed on input or output. +func (c Config) ToIgn3_5Unvalidated(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { + ret := types.Config{} + + tr := translate.NewTranslator("yaml", "json", options) + tr.AddCustomTranslator(translateIgnition) + tr.AddCustomTranslator(translateFile) + tr.AddCustomTranslator(translateDirectory) + tr.AddCustomTranslator(translateLink) + tr.AddCustomTranslator(translateResource) + tr.AddCustomTranslator(translatePasswdUser) + tr.AddCustomTranslator(translateUnit) + + tm, r := translate.Prefixed(tr, "ignition", &c.Ignition, &ret.Ignition) + tm.AddTranslation(path.New("yaml", "version"), path.New("json", "ignition", "version")) + tm.AddTranslation(path.New("yaml", "ignition"), path.New("json", "ignition")) + translate.MergeP2(tr, tm, &r, "kernel_arguments", &c.KernelArguments, "kernelArguments", &ret.KernelArguments) + translate.MergeP(tr, tm, &r, "passwd", &c.Passwd, &ret.Passwd) + translate.MergeP(tr, tm, &r, "storage", &c.Storage, &ret.Storage) + translate.MergeP(tr, tm, &r, "systemd", &c.Systemd, &ret.Systemd) + + c.addMountUnits(&ret, &tm) + + tm2, r2 := c.processTrees(&ret, options) + tm.Merge(tm2) + r.Merge(r2) + + if r.IsFatal() { + return types.Config{}, translate.TranslationSet{}, r + } + return ret, tm, r +} + +func translateIgnition(from Ignition, options common.TranslateOptions) (to types.Ignition, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tr.AddCustomTranslator(translateResource) + to.Version = types.MaxVersion.String() + tm, r = translate.Prefixed(tr, "config", &from.Config, &to.Config) + translate.MergeP(tr, tm, &r, "proxy", &from.Proxy, &to.Proxy) + translate.MergeP(tr, tm, &r, "security", &from.Security, &to.Security) + translate.MergeP(tr, tm, &r, "timeouts", &from.Timeouts, &to.Timeouts) + return +} + +func translateFile(from File, options common.TranslateOptions) (to types.File, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tr.AddCustomTranslator(translateResource) + tm, r = translate.Prefixed(tr, "group", &from.Group, &to.Group) + translate.MergeP(tr, tm, &r, "user", &from.User, &to.User) + translate.MergeP(tr, tm, &r, "append", &from.Append, &to.Append) + translate.MergeP(tr, tm, &r, "contents", &from.Contents, &to.Contents) + translate.MergeP(tr, tm, &r, "overwrite", &from.Overwrite, &to.Overwrite) + translate.MergeP(tr, tm, &r, "path", &from.Path, &to.Path) + translate.MergeP(tr, tm, &r, "mode", &from.Mode, &to.Mode) + return +} + +func translateResource(from Resource, options common.TranslateOptions) (to types.Resource, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tm, r = translate.Prefixed(tr, "verification", &from.Verification, &to.Verification) + translate.MergeP(tr, tm, &r, "httpHeaders", &from.HTTPHeaders, &to.HTTPHeaders) + translate.MergeP(tr, tm, &r, "source", &from.Source, &to.Source) + translate.MergeP(tr, tm, &r, "compression", &from.Compression, &to.Compression) + + if from.Local != nil { + c := path.New("yaml", "local") + contents, err := baseutil.ReadLocalFile(*from.Local, options.FilesDir) + if err != nil { + r.AddOnError(c, err) + return + } + src, compression, err := baseutil.MakeDataURL(contents, to.Compression, !options.NoResourceAutoCompression) + if err != nil { + r.AddOnError(c, err) + return + } + to.Source = &src + tm.AddTranslation(c, path.New("json", "source")) + if compression != nil { + to.Compression = compression + tm.AddTranslation(c, path.New("json", "compression")) + } + } + + if from.Inline != nil { + c := path.New("yaml", "inline") + + src, compression, err := baseutil.MakeDataURL([]byte(*from.Inline), to.Compression, !options.NoResourceAutoCompression) + if err != nil { + r.AddOnError(c, err) + return + } + to.Source = &src + tm.AddTranslation(c, path.New("json", "source")) + if compression != nil { + to.Compression = compression + tm.AddTranslation(c, path.New("json", "compression")) + } + } + return +} + +func translateDirectory(from Directory, options common.TranslateOptions) (to types.Directory, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tm, r = translate.Prefixed(tr, "group", &from.Group, &to.Group) + translate.MergeP(tr, tm, &r, "user", &from.User, &to.User) + translate.MergeP(tr, tm, &r, "overwrite", &from.Overwrite, &to.Overwrite) + translate.MergeP(tr, tm, &r, "path", &from.Path, &to.Path) + translate.MergeP(tr, tm, &r, "mode", &from.Mode, &to.Mode) + return +} + +func translateLink(from Link, options common.TranslateOptions) (to types.Link, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tm, r = translate.Prefixed(tr, "group", &from.Group, &to.Group) + translate.MergeP(tr, tm, &r, "user", &from.User, &to.User) + translate.MergeP(tr, tm, &r, "target", &from.Target, &to.Target) + translate.MergeP(tr, tm, &r, "hard", &from.Hard, &to.Hard) + translate.MergeP(tr, tm, &r, "overwrite", &from.Overwrite, &to.Overwrite) + translate.MergeP(tr, tm, &r, "path", &from.Path, &to.Path) + return +} + +func translatePasswdUser(from PasswdUser, options common.TranslateOptions) (to types.PasswdUser, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tm, r = translate.Prefixed(tr, "gecos", &from.Gecos, &to.Gecos) + translate.MergeP(tr, tm, &r, "groups", &from.Groups, &to.Groups) + translate.MergeP2(tr, tm, &r, "home_dir", &from.HomeDir, "homeDir", &to.HomeDir) + translate.MergeP(tr, tm, &r, "name", &from.Name, &to.Name) + translate.MergeP2(tr, tm, &r, "no_create_home", &from.NoCreateHome, "noCreateHome", &to.NoCreateHome) + translate.MergeP2(tr, tm, &r, "no_log_init", &from.NoLogInit, "noLogInit", &to.NoLogInit) + translate.MergeP2(tr, tm, &r, "no_user_group", &from.NoUserGroup, "noUserGroup", &to.NoUserGroup) + translate.MergeP2(tr, tm, &r, "password_hash", &from.PasswordHash, "passwordHash", &to.PasswordHash) + translate.MergeP2(tr, tm, &r, "primary_group", &from.PrimaryGroup, "primaryGroup", &to.PrimaryGroup) + translate.MergeP(tr, tm, &r, "shell", &from.Shell, &to.Shell) + translate.MergeP2(tr, tm, &r, "should_exist", &from.ShouldExist, "shouldExist", &to.ShouldExist) + translate.MergeP2(tr, tm, &r, "ssh_authorized_keys", &from.SSHAuthorizedKeys, "sshAuthorizedKeys", &to.SSHAuthorizedKeys) + translate.MergeP(tr, tm, &r, "system", &from.System, &to.System) + translate.MergeP(tr, tm, &r, "uid", &from.UID, &to.UID) + + if len(from.SSHAuthorizedKeysLocal) > 0 { + c := path.New("yaml", "ssh_authorized_keys_local") + tm.AddTranslation(c, path.New("json", "sshAuthorizedKeys")) + + if options.FilesDir == "" { + r.AddOnError(c, common.ErrNoFilesDir) + return + } + + for keyFileIndex, sshKeyFile := range from.SSHAuthorizedKeysLocal { + sshKeys, err := baseutil.ReadLocalFile(sshKeyFile, options.FilesDir) + if err != nil { + r.AddOnError(c.Append(keyFileIndex), err) + continue + } + for _, line := range regexp.MustCompile("\r?\n").Split(string(sshKeys), -1) { + if line == "" { + continue + } + tm.AddTranslation(c.Append(keyFileIndex), path.New("json", "sshAuthorizedKeys", len(to.SSHAuthorizedKeys))) + to.SSHAuthorizedKeys = append(to.SSHAuthorizedKeys, types.SSHAuthorizedKey(line)) + } + } + } + + return +} + +func translateUnit(from Unit, options common.TranslateOptions) (to types.Unit, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tr.AddCustomTranslator(translateDropin) + tm, r = translate.Prefixed(tr, "contents", &from.Contents, &to.Contents) + translate.MergeP(tr, tm, &r, "dropins", &from.Dropins, &to.Dropins) + translate.MergeP(tr, tm, &r, "enabled", &from.Enabled, &to.Enabled) + translate.MergeP(tr, tm, &r, "mask", &from.Mask, &to.Mask) + translate.MergeP(tr, tm, &r, "name", &from.Name, &to.Name) + + if util.NotEmpty(from.ContentsLocal) { + c := path.New("yaml", "contents_local") + contents, err := baseutil.ReadLocalFile(*from.ContentsLocal, options.FilesDir) + if err != nil { + r.AddOnError(c, err) + return + } + tm.AddTranslation(c, path.New("json", "contents")) + to.Contents = util.StrToPtr(string(contents)) + } + + return +} + +func translateDropin(from Dropin, options common.TranslateOptions) (to types.Dropin, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + tm, r = translate.Prefixed(tr, "contents", &from.Contents, &to.Contents) + translate.MergeP(tr, tm, &r, "name", &from.Name, &to.Name) + + if util.NotEmpty(from.ContentsLocal) { + c := path.New("yaml", "contents_local") + contents, err := baseutil.ReadLocalFile(*from.ContentsLocal, options.FilesDir) + if err != nil { + r.AddOnError(c, err) + return + } + tm.AddTranslation(c, path.New("json", "contents")) + to.Contents = util.StrToPtr(string(contents)) + } + + return +} + +func (c Config) processTrees(ret *types.Config, options common.TranslateOptions) (translate.TranslationSet, report.Report) { + ts := translate.NewTranslationSet("yaml", "json") + var r report.Report + if len(c.Storage.Trees) == 0 { + return ts, r + } + t := newNodeTracker(ret) + + for i, tree := range c.Storage.Trees { + yamlPath := path.New("yaml", "storage", "trees", i) + if options.FilesDir == "" { + r.AddOnError(yamlPath, common.ErrNoFilesDir) + return ts, r + } + + // calculate base path within FilesDir and check for + // path traversal + srcBaseDir := filepath.Join(options.FilesDir, filepath.FromSlash(tree.Local)) + if err := baseutil.EnsurePathWithinFilesDir(srcBaseDir, options.FilesDir); err != nil { + r.AddOnError(yamlPath, err) + continue + } + info, err := os.Stat(srcBaseDir) + if err != nil { + r.AddOnError(yamlPath, err) + continue + } + if !info.IsDir() { + r.AddOnError(yamlPath, common.ErrTreeNotDirectory) + continue + } + destBaseDir := "/" + if util.NotEmpty(tree.Path) { + destBaseDir = *tree.Path + } + + walkTree(yamlPath, &ts, &r, t, srcBaseDir, destBaseDir, options) + } + return ts, r +} + +func walkTree(yamlPath path.ContextPath, ts *translate.TranslationSet, r *report.Report, t *nodeTracker, srcBaseDir, destBaseDir string, options common.TranslateOptions) { + // The strategy for errors within WalkFunc is to add an error to + // the report and return nil, so walking continues but translation + // will fail afterward. + err := filepath.Walk(srcBaseDir, func(srcPath string, info os.FileInfo, err error) error { + if err != nil { + r.AddOnError(yamlPath, err) + return nil + } + relPath, err := filepath.Rel(srcBaseDir, srcPath) + if err != nil { + r.AddOnError(yamlPath, err) + return nil + } + destPath := slashpath.Join(destBaseDir, filepath.ToSlash(relPath)) + + if info.Mode().IsDir() { + return nil + } else if info.Mode().IsRegular() { + i, file := t.GetFile(destPath) + if file != nil { + if util.NotEmpty(file.Contents.Source) { + r.AddOnError(yamlPath, common.ErrNodeExists) + return nil + } + } else { + if t.Exists(destPath) { + r.AddOnError(yamlPath, common.ErrNodeExists) + return nil + } + i, file = t.AddFile(types.File{ + Node: types.Node{ + Path: destPath, + }, + }) + ts.AddFromCommonSource(yamlPath, path.New("json", "storage", "files", i), file) + if i == 0 { + ts.AddTranslation(yamlPath, path.New("json", "storage", "files")) + } + } + contents, err := os.ReadFile(srcPath) + if err != nil { + r.AddOnError(yamlPath, err) + return nil + } + url, compression, err := baseutil.MakeDataURL(contents, file.Contents.Compression, !options.NoResourceAutoCompression) + if err != nil { + r.AddOnError(yamlPath, err) + return nil + } + file.Contents.Source = &url + ts.AddTranslation(yamlPath, path.New("json", "storage", "files", i, "contents", "source")) + if compression != nil { + file.Contents.Compression = compression + ts.AddTranslation(yamlPath, path.New("json", "storage", "files", i, "contents", "compression")) + } + ts.AddTranslation(yamlPath, path.New("json", "storage", "files", i, "contents")) + if file.Mode == nil { + mode := 0644 + if info.Mode()&0111 != 0 { + mode = 0755 + } + file.Mode = &mode + ts.AddTranslation(yamlPath, path.New("json", "storage", "files", i, "mode")) + } + } else if info.Mode()&os.ModeType == os.ModeSymlink { + i, link := t.GetLink(destPath) + if link != nil { + if util.NotEmpty(link.Target) { + r.AddOnError(yamlPath, common.ErrNodeExists) + return nil + } + } else { + if t.Exists(destPath) { + r.AddOnError(yamlPath, common.ErrNodeExists) + return nil + } + i, link = t.AddLink(types.Link{ + Node: types.Node{ + Path: destPath, + }, + }) + ts.AddFromCommonSource(yamlPath, path.New("json", "storage", "links", i), link) + if i == 0 { + ts.AddTranslation(yamlPath, path.New("json", "storage", "links")) + } + } + target, err := os.Readlink(srcPath) + if err != nil { + r.AddOnError(yamlPath, err) + return nil + } + link.Target = util.StrToPtr(filepath.ToSlash(target)) + ts.AddTranslation(yamlPath, path.New("json", "storage", "links", i, "target")) + } else { + r.AddOnError(yamlPath, common.ErrFileType) + return nil + } + return nil + }) + r.AddOnError(yamlPath, err) +} + +func (c Config) addMountUnits(config *types.Config, ts *translate.TranslationSet) { + if len(c.Storage.Filesystems) == 0 { + return + } + var rendered types.Config + renderedTranslations := translate.NewTranslationSet("yaml", "json") + renderedTranslations.AddTranslation(path.New("yaml", "storage", "filesystems"), path.New("json", "systemd")) + renderedTranslations.AddTranslation(path.New("yaml", "storage", "filesystems"), path.New("json", "systemd", "units")) + for i, fs := range c.Storage.Filesystems { + if !util.IsTrue(fs.WithMountUnit) { + continue + } + fromPath := path.New("yaml", "storage", "filesystems", i, "with_mount_unit") + remote := false + // check filesystems targeting /dev/mapper devices against LUKS to determine if a + // remote mount is needed + if strings.HasPrefix(fs.Device, "/dev/mapper/") || strings.HasPrefix(fs.Device, "/dev/disk/by-id/dm-name-") { + for _, luks := range c.Storage.Luks { + // LUKS devices are opened with their name specified + if fs.Device == fmt.Sprintf("/dev/mapper/%s", luks.Name) || fs.Device == fmt.Sprintf("/dev/disk/by-id/dm-name-%s", luks.Name) { + if len(luks.Clevis.Tang) > 0 { + remote = true + break + } + } + } + } + newUnit := mountUnitFromFS(fs, remote) + unitPath := path.New("json", "systemd", "units", len(rendered.Systemd.Units)) + rendered.Systemd.Units = append(rendered.Systemd.Units, newUnit) + renderedTranslations.AddFromCommonSource(fromPath, unitPath, newUnit) + } + retConfig, retTranslations := baseutil.MergeTranslatedConfigs(rendered, renderedTranslations, *config, *ts) + *config = retConfig.(types.Config) + *ts = retTranslations +} + +func mountUnitFromFS(fs Filesystem, remote bool) types.Unit { + context := struct { + *Filesystem + EscapedDevice string + Remote bool + Swap bool + }{ + Filesystem: &fs, + EscapedDevice: unit.UnitNamePathEscape(fs.Device), + Remote: remote, + // unchecked deref of format ok, fs would fail validation otherwise + Swap: *fs.Format == "swap", + } + contents := strings.Builder{} + err := mountUnitTemplate.Execute(&contents, context) + if err != nil { + panic(err) + } + var unitName string + if context.Swap { + unitName = unit.UnitNamePathEscape(fs.Device) + ".swap" + } else { + // unchecked deref of path ok, fs would fail validation otherwise + unitName = unit.UnitNamePathEscape(*fs.Path) + ".mount" + } + return types.Unit{ + Name: unitName, + Enabled: util.BoolToPtr(true), + Contents: util.StrToPtr(contents.String()), + } +} diff --git a/vendor/github.com/coreos/butane/base/v0_6_exp/util.go b/vendor/github.com/coreos/butane/base/v0_6_exp/util.go new file mode 100644 index 0000000000..bdabebd54b --- /dev/null +++ b/vendor/github.com/coreos/butane/base/v0_6_exp/util.go @@ -0,0 +1,125 @@ +// Copyright 2020 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package v0_6_exp + +import ( + "github.com/coreos/ignition/v2/config/v3_5_experimental/types" +) + +type nodeTracker struct { + files *[]types.File + fileMap map[string]int + + dirs *[]types.Directory + dirMap map[string]int + + links *[]types.Link + linkMap map[string]int +} + +func newNodeTracker(c *types.Config) *nodeTracker { + t := nodeTracker{ + files: &c.Storage.Files, + fileMap: make(map[string]int, len(c.Storage.Files)), + + dirs: &c.Storage.Directories, + dirMap: make(map[string]int, len(c.Storage.Directories)), + + links: &c.Storage.Links, + linkMap: make(map[string]int, len(c.Storage.Links)), + } + for i, n := range *t.files { + t.fileMap[n.Path] = i + } + for i, n := range *t.dirs { + t.dirMap[n.Path] = i + } + for i, n := range *t.links { + t.linkMap[n.Path] = i + } + return &t +} + +func (t *nodeTracker) Exists(path string) bool { + for _, m := range []map[string]int{t.fileMap, t.dirMap, t.linkMap} { + if _, ok := m[path]; ok { + return true + } + } + return false +} + +func (t *nodeTracker) GetFile(path string) (int, *types.File) { + if i, ok := t.fileMap[path]; ok { + return i, &(*t.files)[i] + } else { + return 0, nil + } +} + +func (t *nodeTracker) AddFile(f types.File) (int, *types.File) { + if f.Path == "" { + panic("File path missing") + } + if _, ok := t.fileMap[f.Path]; ok { + panic("Adding already existing file") + } + i := len(*t.files) + *t.files = append(*t.files, f) + t.fileMap[f.Path] = i + return i, &(*t.files)[i] +} + +func (t *nodeTracker) GetDir(path string) (int, *types.Directory) { + if i, ok := t.dirMap[path]; ok { + return i, &(*t.dirs)[i] + } else { + return 0, nil + } +} + +func (t *nodeTracker) AddDir(d types.Directory) (int, *types.Directory) { + if d.Path == "" { + panic("Directory path missing") + } + if _, ok := t.dirMap[d.Path]; ok { + panic("Adding already existing directory") + } + i := len(*t.dirs) + *t.dirs = append(*t.dirs, d) + t.dirMap[d.Path] = i + return i, &(*t.dirs)[i] +} + +func (t *nodeTracker) GetLink(path string) (int, *types.Link) { + if i, ok := t.linkMap[path]; ok { + return i, &(*t.links)[i] + } else { + return 0, nil + } +} + +func (t *nodeTracker) AddLink(l types.Link) (int, *types.Link) { + if l.Path == "" { + panic("Link path missing") + } + if _, ok := t.linkMap[l.Path]; ok { + panic("Adding already existing link") + } + i := len(*t.links) + *t.links = append(*t.links, l) + t.linkMap[l.Path] = i + return i, &(*t.links)[i] +} diff --git a/vendor/github.com/coreos/butane/base/v0_6_exp/validate.go b/vendor/github.com/coreos/butane/base/v0_6_exp/validate.go new file mode 100644 index 0000000000..ad3917be2a --- /dev/null +++ b/vendor/github.com/coreos/butane/base/v0_6_exp/validate.go @@ -0,0 +1,92 @@ +// Copyright 2020 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package v0_6_exp + +import ( + baseutil "github.com/coreos/butane/base/util" + "github.com/coreos/butane/config/common" + + "github.com/coreos/ignition/v2/config/util" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (rs Resource) Validate(c path.ContextPath) (r report.Report) { + var field string + sources := 0 + if rs.Local != nil { + sources++ + field = "local" + } + if rs.Inline != nil { + sources++ + field = "inline" + } + if rs.Source != nil { + sources++ + field = "source" + } + if sources > 1 { + r.AddOnError(c.Append(field), common.ErrTooManyResourceSources) + } + return +} + +func (fs Filesystem) Validate(c path.ContextPath) (r report.Report) { + if !util.IsTrue(fs.WithMountUnit) { + return + } + if util.NilOrEmpty(fs.Format) { + r.AddOnError(c.Append("format"), common.ErrMountUnitNoFormat) + } else if *fs.Format != "swap" && util.NilOrEmpty(fs.Path) { + r.AddOnError(c.Append("path"), common.ErrMountUnitNoPath) + } + return +} + +func (d Directory) Validate(c path.ContextPath) (r report.Report) { + if d.Mode != nil { + r.AddOnWarn(c.Append("mode"), baseutil.CheckForDecimalMode(*d.Mode, true)) + } + return +} + +func (f File) Validate(c path.ContextPath) (r report.Report) { + if f.Mode != nil { + r.AddOnWarn(c.Append("mode"), baseutil.CheckForDecimalMode(*f.Mode, false)) + } + return +} + +func (t Tree) Validate(c path.ContextPath) (r report.Report) { + if t.Local == "" { + r.AddOnError(c, common.ErrTreeNoLocal) + } + return +} + +func (rs Unit) Validate(c path.ContextPath) (r report.Report) { + if rs.ContentsLocal != nil && rs.Contents != nil { + r.AddOnError(c.Append("contents_local"), common.ErrTooManySystemdSources) + } + return +} + +func (rs Dropin) Validate(c path.ContextPath) (r report.Report) { + if rs.ContentsLocal != nil && rs.Contents != nil { + r.AddOnError(c.Append("contents_local"), common.ErrTooManySystemdSources) + } + return +} diff --git a/vendor/github.com/coreos/butane/config/common/errors.go b/vendor/github.com/coreos/butane/config/common/errors.go index 5e4982352e..bf1e3c2cd8 100644 --- a/vendor/github.com/coreos/butane/config/common/errors.go +++ b/vendor/github.com/coreos/butane/config/common/errors.go @@ -42,6 +42,9 @@ var ( // filesystem nodes ErrDecimalMode = errors.New("unreasonable mode would be reasonable if specified in octal; remember to add a leading zero") + // systemd + ErrTooManySystemdSources = errors.New("only one of the following can be set: contents, contents_local") + // mount units ErrMountUnitNoPath = errors.New("path is required if with_mount_unit is true and format is not swap") ErrMountUnitNoFormat = errors.New("format is required if with_mount_unit is true") diff --git a/vendor/github.com/coreos/butane/config/config.go b/vendor/github.com/coreos/butane/config/config.go index 54ea1f2ce5..4d095b2fb3 100644 --- a/vendor/github.com/coreos/butane/config/config.go +++ b/vendor/github.com/coreos/butane/config/config.go @@ -23,17 +23,21 @@ import ( fcos1_2 "github.com/coreos/butane/config/fcos/v1_2" fcos1_3 "github.com/coreos/butane/config/fcos/v1_3" fcos1_4 "github.com/coreos/butane/config/fcos/v1_4" - fcos1_5_exp "github.com/coreos/butane/config/fcos/v1_5_exp" + fcos1_5 "github.com/coreos/butane/config/fcos/v1_5" + fcos1_6_exp "github.com/coreos/butane/config/fcos/v1_6_exp" flatcar1_0 "github.com/coreos/butane/config/flatcar/v1_0" - flatcar1_1_exp "github.com/coreos/butane/config/flatcar/v1_1_exp" + flatcar1_1 "github.com/coreos/butane/config/flatcar/v1_1" + flatcar1_2_exp "github.com/coreos/butane/config/flatcar/v1_2_exp" openshift4_10 "github.com/coreos/butane/config/openshift/v4_10" openshift4_11 "github.com/coreos/butane/config/openshift/v4_11" openshift4_12 "github.com/coreos/butane/config/openshift/v4_12" - openshift4_13_exp "github.com/coreos/butane/config/openshift/v4_13_exp" + openshift4_13 "github.com/coreos/butane/config/openshift/v4_13" + openshift4_14_exp "github.com/coreos/butane/config/openshift/v4_14_exp" openshift4_8 "github.com/coreos/butane/config/openshift/v4_8" openshift4_9 "github.com/coreos/butane/config/openshift/v4_9" r4e1_0 "github.com/coreos/butane/config/r4e/v1_0" - r4e1_1_exp "github.com/coreos/butane/config/r4e/v1_1_exp" + r4e1_1 "github.com/coreos/butane/config/r4e/v1_1" + r4e1_2_exp "github.com/coreos/butane/config/r4e/v1_2_exp" "github.com/coreos/go-semver/semver" "github.com/coreos/vcontext/report" @@ -56,17 +60,21 @@ func init() { RegisterTranslator("fcos", "1.2.0", fcos1_2.ToIgn3_2Bytes) RegisterTranslator("fcos", "1.3.0", fcos1_3.ToIgn3_2Bytes) RegisterTranslator("fcos", "1.4.0", fcos1_4.ToIgn3_3Bytes) - RegisterTranslator("fcos", "1.5.0-experimental", fcos1_5_exp.ToIgn3_4Bytes) + RegisterTranslator("fcos", "1.5.0", fcos1_5.ToIgn3_4Bytes) + RegisterTranslator("fcos", "1.6.0-experimental", fcos1_6_exp.ToIgn3_5Bytes) RegisterTranslator("flatcar", "1.0.0", flatcar1_0.ToIgn3_3Bytes) - RegisterTranslator("flatcar", "1.1.0-experimental", flatcar1_1_exp.ToIgn3_4Bytes) + RegisterTranslator("flatcar", "1.1.0", flatcar1_1.ToIgn3_4Bytes) + RegisterTranslator("flatcar", "1.2.0-experimental", flatcar1_2_exp.ToIgn3_5Bytes) RegisterTranslator("openshift", "4.8.0", openshift4_8.ToConfigBytes) RegisterTranslator("openshift", "4.9.0", openshift4_9.ToConfigBytes) RegisterTranslator("openshift", "4.10.0", openshift4_10.ToConfigBytes) RegisterTranslator("openshift", "4.11.0", openshift4_11.ToConfigBytes) RegisterTranslator("openshift", "4.12.0", openshift4_12.ToConfigBytes) - RegisterTranslator("openshift", "4.13.0-experimental", openshift4_13_exp.ToConfigBytes) + RegisterTranslator("openshift", "4.13.0", openshift4_13.ToConfigBytes) + RegisterTranslator("openshift", "4.14.0-experimental", openshift4_14_exp.ToConfigBytes) RegisterTranslator("r4e", "1.0.0", r4e1_0.ToIgn3_3Bytes) - RegisterTranslator("r4e", "1.1.0-experimental", r4e1_1_exp.ToIgn3_4Bytes) + RegisterTranslator("r4e", "1.1.0", r4e1_1.ToIgn3_4Bytes) + RegisterTranslator("r4e", "1.2.0-experimental", r4e1_2_exp.ToIgn3_5Bytes) RegisterTranslator("rhcos", "0.1.0", unsupportedRhcosVariant) } diff --git a/vendor/github.com/coreos/butane/config/fcos/v1_5_exp/schema.go b/vendor/github.com/coreos/butane/config/fcos/v1_5/schema.go similarity index 95% rename from vendor/github.com/coreos/butane/config/fcos/v1_5_exp/schema.go rename to vendor/github.com/coreos/butane/config/fcos/v1_5/schema.go index 7730cd0886..f00e53c779 100644 --- a/vendor/github.com/coreos/butane/config/fcos/v1_5_exp/schema.go +++ b/vendor/github.com/coreos/butane/config/fcos/v1_5/schema.go @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v1_5_exp +package v1_5 import ( - base "github.com/coreos/butane/base/v0_5_exp" + base "github.com/coreos/butane/base/v0_5" ) type Config struct { diff --git a/vendor/github.com/coreos/butane/config/fcos/v1_5_exp/translate.go b/vendor/github.com/coreos/butane/config/fcos/v1_5/translate.go similarity index 99% rename from vendor/github.com/coreos/butane/config/fcos/v1_5_exp/translate.go rename to vendor/github.com/coreos/butane/config/fcos/v1_5/translate.go index ef744f083b..576c534fe7 100644 --- a/vendor/github.com/coreos/butane/config/fcos/v1_5_exp/translate.go +++ b/vendor/github.com/coreos/butane/config/fcos/v1_5/translate.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v1_5_exp +package v1_5 import ( "fmt" diff --git a/vendor/github.com/coreos/butane/config/fcos/v1_5_exp/validate.go b/vendor/github.com/coreos/butane/config/fcos/v1_5/validate.go similarity index 98% rename from vendor/github.com/coreos/butane/config/fcos/v1_5_exp/validate.go rename to vendor/github.com/coreos/butane/config/fcos/v1_5/validate.go index 3b3d22faf6..368cc1b528 100644 --- a/vendor/github.com/coreos/butane/config/fcos/v1_5_exp/validate.go +++ b/vendor/github.com/coreos/butane/config/fcos/v1_5/validate.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v1_5_exp +package v1_5 import ( "github.com/coreos/butane/config/common" diff --git a/vendor/github.com/coreos/butane/config/fcos/v1_6_exp/schema.go b/vendor/github.com/coreos/butane/config/fcos/v1_6_exp/schema.go new file mode 100644 index 0000000000..140cd31a5d --- /dev/null +++ b/vendor/github.com/coreos/butane/config/fcos/v1_6_exp/schema.go @@ -0,0 +1,51 @@ +// Copyright 2020 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package v1_6_exp + +import ( + base "github.com/coreos/butane/base/v0_6_exp" +) + +type Config struct { + base.Config `yaml:",inline"` + BootDevice BootDevice `yaml:"boot_device"` + Grub Grub `yaml:"grub"` +} + +type BootDevice struct { + Layout *string `yaml:"layout"` + Luks BootDeviceLuks `yaml:"luks"` + Mirror BootDeviceMirror `yaml:"mirror"` +} + +type BootDeviceLuks struct { + Discard *bool `yaml:"discard"` + Tang []base.Tang `yaml:"tang"` + Threshold *int `yaml:"threshold"` + Tpm2 *bool `yaml:"tpm2"` +} + +type BootDeviceMirror struct { + Devices []string `yaml:"devices"` +} + +type Grub struct { + Users []GrubUser `yaml:"users"` +} + +type GrubUser struct { + Name string `yaml:"name"` + PasswordHash *string `yaml:"password_hash"` +} diff --git a/vendor/github.com/coreos/butane/config/fcos/v1_6_exp/translate.go b/vendor/github.com/coreos/butane/config/fcos/v1_6_exp/translate.go new file mode 100644 index 0000000000..5a5d02ca0e --- /dev/null +++ b/vendor/github.com/coreos/butane/config/fcos/v1_6_exp/translate.go @@ -0,0 +1,364 @@ +// Copyright 2020 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package v1_6_exp + +import ( + "fmt" + "strings" + + baseutil "github.com/coreos/butane/base/util" + "github.com/coreos/butane/config/common" + cutil "github.com/coreos/butane/config/util" + "github.com/coreos/butane/translate" + + "github.com/coreos/ignition/v2/config/util" + "github.com/coreos/ignition/v2/config/v3_5_experimental/types" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +const ( + reservedTypeGuid = "8DA63339-0007-60C0-C436-083AC8230908" + biosTypeGuid = "21686148-6449-6E6F-744E-656564454649" + prepTypeGuid = "9E1A2D38-C612-4316-AA26-8B49521E5A8B" + espTypeGuid = "C12A7328-F81F-11D2-BA4B-00A0C93EC93B" + + // The partition layout implemented in this file replicates + // the layout of the OS image defined in: + // https://github.com/coreos/coreos-assembler/blob/main/src/create_disk.sh + // + // It's not critical that we match that layout exactly; the hard + // constraints are: + // - The desugared partition cannot be smaller than the one it + // replicates + // - The new BIOS-BOOT partition (and maybe the PReP one?) must be + // at the same offset as the original + // + // Do not change these constants! New partition layouts must be + // encoded into new layout templates. + reservedV1SizeMiB = 1 + biosV1SizeMiB = 1 + prepV1SizeMiB = 4 + espV1SizeMiB = 127 + bootV1SizeMiB = 384 +) + +// ToIgn3_5Unvalidated translates the config to an Ignition config. It also +// returns the set of translations it did so paths in the resultant config +// can be tracked back to their source in the source config. No config +// validation is performed on input or output. +func (c Config) ToIgn3_5Unvalidated(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { + ret, ts, r := c.Config.ToIgn3_5Unvalidated(options) + if r.IsFatal() { + return types.Config{}, translate.TranslationSet{}, r + } + r.Merge(c.processBootDevice(&ret, &ts, options)) + for i, disk := range ret.Storage.Disks { + // In the boot_device.mirror case, nothing specifies partition numbers + // so match existing partitions only when `wipeTable` is false + if !util.IsTrue(disk.WipeTable) { + for j, partition := range disk.Partitions { + // check for reserved partlabels + if partition.Label != nil { + if (*partition.Label == "BIOS-BOOT" && partition.Number != 1) || (*partition.Label == "PowerPC-PReP-boot" && partition.Number != 1) || (*partition.Label == "EFI-SYSTEM" && partition.Number != 2) || (*partition.Label == "boot" && partition.Number != 3) || (*partition.Label == "root" && partition.Number != 4) { + r.AddOnWarn(path.New("json", "storage", "disks", i, "partitions", j, "label"), common.ErrWrongPartitionNumber) + } + } + } + } + } + + retp, tsp, rp := c.handleUserGrubCfg(options) + retConfig, ts := baseutil.MergeTranslatedConfigs(retp, tsp, ret, ts) + ret = retConfig.(types.Config) + r.Merge(rp) + return ret, ts, r +} + +// ToIgn3_5 translates the config to an Ignition config. It returns a +// report of any errors or warnings in the source and resultant config. If +// the report has fatal errors or it encounters other problems translating, +// an error is returned. +func (c Config) ToIgn3_5(options common.TranslateOptions) (types.Config, report.Report, error) { + cfg, r, err := cutil.Translate(c, "ToIgn3_5Unvalidated", options) + return cfg.(types.Config), r, err +} + +// ToIgn3_5Bytes translates from a v1.6 Butane config to a v3.5.0 Ignition config. It returns a report of any errors or +// warnings in the source and resultant config. If the report has fatal errors or it encounters other problems +// translating, an error is returned. +func ToIgn3_5Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { + return cutil.TranslateBytes(input, &Config{}, "ToIgn3_5", options) +} + +func (c Config) processBootDevice(config *types.Config, ts *translate.TranslationSet, options common.TranslateOptions) report.Report { + var rendered types.Config + renderedTranslations := translate.NewTranslationSet("yaml", "json") + var r report.Report + + // check for high-level features + wantLuks := util.IsTrue(c.BootDevice.Luks.Tpm2) || len(c.BootDevice.Luks.Tang) > 0 + wantMirror := len(c.BootDevice.Mirror.Devices) > 0 + if !wantLuks && !wantMirror { + return r + } + + // compute layout rendering options + var wantBIOSPart bool + var wantEFIPart bool + var wantPRePPart bool + layout := c.BootDevice.Layout + switch { + case layout == nil || *layout == "x86_64": + wantBIOSPart = true + wantEFIPart = true + case *layout == "aarch64": + wantEFIPart = true + case *layout == "ppc64le": + wantPRePPart = true + default: + // should have failed validation + panic("unknown layout") + } + + // mirrored root disk + if wantMirror { + // partition disks + for i, device := range c.BootDevice.Mirror.Devices { + labelIndex := len(rendered.Storage.Disks) + 1 + disk := types.Disk{ + Device: device, + WipeTable: util.BoolToPtr(true), + } + if wantBIOSPart { + disk.Partitions = append(disk.Partitions, types.Partition{ + Label: util.StrToPtr(fmt.Sprintf("bios-%d", labelIndex)), + SizeMiB: util.IntToPtr(biosV1SizeMiB), + TypeGUID: util.StrToPtr(biosTypeGuid), + }) + } else if wantPRePPart { + disk.Partitions = append(disk.Partitions, types.Partition{ + Label: util.StrToPtr(fmt.Sprintf("prep-%d", labelIndex)), + SizeMiB: util.IntToPtr(prepV1SizeMiB), + TypeGUID: util.StrToPtr(prepTypeGuid), + }) + } else { + disk.Partitions = append(disk.Partitions, types.Partition{ + Label: util.StrToPtr(fmt.Sprintf("reserved-%d", labelIndex)), + SizeMiB: util.IntToPtr(reservedV1SizeMiB), + TypeGUID: util.StrToPtr(reservedTypeGuid), + }) + } + if wantEFIPart { + disk.Partitions = append(disk.Partitions, types.Partition{ + Label: util.StrToPtr(fmt.Sprintf("esp-%d", labelIndex)), + SizeMiB: util.IntToPtr(espV1SizeMiB), + TypeGUID: util.StrToPtr(espTypeGuid), + }) + } else { + disk.Partitions = append(disk.Partitions, types.Partition{ + Label: util.StrToPtr(fmt.Sprintf("reserved-%d", labelIndex)), + SizeMiB: util.IntToPtr(reservedV1SizeMiB), + TypeGUID: util.StrToPtr(reservedTypeGuid), + }) + } + disk.Partitions = append(disk.Partitions, types.Partition{ + Label: util.StrToPtr(fmt.Sprintf("boot-%d", labelIndex)), + SizeMiB: util.IntToPtr(bootV1SizeMiB), + }, types.Partition{ + Label: util.StrToPtr(fmt.Sprintf("root-%d", labelIndex)), + }) + renderedTranslations.AddFromCommonSource(path.New("yaml", "boot_device", "mirror", "devices", i), path.New("json", "storage", "disks", len(rendered.Storage.Disks)), disk) + rendered.Storage.Disks = append(rendered.Storage.Disks, disk) + + if wantEFIPart { + // add ESP filesystem + espFilesystem := types.Filesystem{ + Device: fmt.Sprintf("/dev/disk/by-partlabel/esp-%d", labelIndex), + Format: util.StrToPtr("vfat"), + Label: util.StrToPtr(fmt.Sprintf("esp-%d", labelIndex)), + WipeFilesystem: util.BoolToPtr(true), + } + renderedTranslations.AddFromCommonSource(path.New("yaml", "boot_device", "mirror", "devices", i), path.New("json", "storage", "filesystems", len(rendered.Storage.Filesystems)), espFilesystem) + rendered.Storage.Filesystems = append(rendered.Storage.Filesystems, espFilesystem) + } + } + renderedTranslations.AddTranslation(path.New("yaml", "boot_device", "mirror", "devices"), path.New("json", "storage", "disks")) + + // create RAIDs + raidDevices := func(labelPrefix string) []types.Device { + count := len(rendered.Storage.Disks) + ret := make([]types.Device, count) + for i := 0; i < count; i++ { + ret[i] = types.Device(fmt.Sprintf("/dev/disk/by-partlabel/%s-%d", labelPrefix, i+1)) + } + return ret + } + rendered.Storage.Raid = []types.Raid{{ + Devices: raidDevices("boot"), + Level: util.StrToPtr("raid1"), + Name: "md-boot", + // put the RAID superblock at the end of the + // partition so BIOS GRUB doesn't need to + // understand RAID + Options: []types.RaidOption{"--metadata=1.0"}, + }, { + Devices: raidDevices("root"), + Level: util.StrToPtr("raid1"), + Name: "md-root", + }} + renderedTranslations.AddFromCommonSource(path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "raid"), rendered.Storage.Raid) + + // create boot filesystem + bootFilesystem := types.Filesystem{ + Device: "/dev/md/md-boot", + Format: util.StrToPtr("ext4"), + Label: util.StrToPtr("boot"), + WipeFilesystem: util.BoolToPtr(true), + } + renderedTranslations.AddFromCommonSource(path.New("yaml", "boot_device", "mirror"), path.New("json", "storage", "filesystems", len(rendered.Storage.Filesystems)), bootFilesystem) + rendered.Storage.Filesystems = append(rendered.Storage.Filesystems, bootFilesystem) + } + + // encrypted root partition + if wantLuks { + luksDevice := "/dev/disk/by-partlabel/root" + if wantMirror { + luksDevice = "/dev/md/md-root" + } + clevis, ts2, r2 := translateBootDeviceLuks(c.BootDevice.Luks, options) + rendered.Storage.Luks = []types.Luks{{ + Clevis: clevis, + Device: &luksDevice, + Discard: c.BootDevice.Luks.Discard, + Label: util.StrToPtr("luks-root"), + Name: "root", + WipeVolume: util.BoolToPtr(true), + }} + lpath := path.New("yaml", "boot_device", "luks") + rpath := path.New("json", "storage", "luks", 0) + renderedTranslations.Merge(ts2.PrefixPaths(lpath, rpath.Append("clevis"))) + renderedTranslations.AddTranslation(lpath.Append("discard"), rpath.Append("discard")) + for _, f := range []string{"device", "label", "name", "wipeVolume"} { + renderedTranslations.AddTranslation(lpath, rpath.Append(f)) + } + renderedTranslations.AddTranslation(lpath, rpath) + renderedTranslations.AddTranslation(lpath, path.New("json", "storage", "luks")) + r.Merge(r2) + } + + // create root filesystem + var rootDevice string + switch { + case wantLuks: + // LUKS, or LUKS on RAID + rootDevice = "/dev/mapper/root" + case wantMirror: + // RAID without LUKS + rootDevice = "/dev/md/md-root" + default: + panic("can't happen") + } + rootFilesystem := types.Filesystem{ + Device: rootDevice, + Format: util.StrToPtr("xfs"), + Label: util.StrToPtr("root"), + WipeFilesystem: util.BoolToPtr(true), + } + renderedTranslations.AddFromCommonSource(path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems", len(rendered.Storage.Filesystems)), rootFilesystem) + renderedTranslations.AddTranslation(path.New("yaml", "boot_device"), path.New("json", "storage", "filesystems")) + rendered.Storage.Filesystems = append(rendered.Storage.Filesystems, rootFilesystem) + + // merge with translated config + renderedTranslations.AddTranslation(path.New("yaml", "boot_device"), path.New("json", "storage")) + retConfig, retTranslations := baseutil.MergeTranslatedConfigs(rendered, renderedTranslations, *config, *ts) + *config = retConfig.(types.Config) + *ts = retTranslations + return r +} + +func translateBootDeviceLuks(from BootDeviceLuks, options common.TranslateOptions) (to types.Clevis, tm translate.TranslationSet, r report.Report) { + tr := translate.NewTranslator("yaml", "json", options) + // Discard field is handled by the caller because it doesn't go + // into types.Clevis + tm, r = translate.Prefixed(tr, "tang", &from.Tang, &to.Tang) + translate.MergeP(tr, tm, &r, "threshold", &from.Threshold, &to.Threshold) + translate.MergeP(tr, tm, &r, "tpm2", &from.Tpm2, &to.Tpm2) + // we're being called manually, not via the translate package's + // custom translator mechanism, so we have to add the base + // translation ourselves + tm.AddTranslation(path.New("yaml"), path.New("json")) + return +} + +func (c Config) handleUserGrubCfg(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { + rendered := types.Config{} + ts := translate.NewTranslationSet("yaml", "json") + var r report.Report + yamlPath := path.New("yaml", "grub", "users") + if len(c.Grub.Users) == 0 { + // No users + return rendered, ts, r + } + + // create boot filesystem + rendered.Storage.Filesystems = append(rendered.Storage.Filesystems, + types.Filesystem{ + Device: "/dev/disk/by-label/boot", + Format: util.StrToPtr("ext4"), + Path: util.StrToPtr("/boot"), + }) + + userCfgContent := []byte(buildGrubConfig(c.Grub)) + src, compression, err := baseutil.MakeDataURL(userCfgContent, nil, !options.NoResourceAutoCompression) + if err != nil { + r.AddOnError(yamlPath, err) + return rendered, ts, r + } + + // Create user.cfg file and add it to rendered config + rendered.Storage.Files = append(rendered.Storage.Files, + types.File{ + Node: types.Node{ + Path: "/boot/grub2/user.cfg", + }, + FileEmbedded1: types.FileEmbedded1{ + Append: []types.Resource{ + { + Source: util.StrToPtr(src), + Compression: compression, + }, + }, + }, + }) + + ts.AddFromCommonSource(yamlPath, path.New("json", "storage"), rendered.Storage) + return rendered, ts, r +} + +func buildGrubConfig(gb Grub) string { + // Process super users and corresponding passwords + allUsers := []string{} + cmds := []string{} + + for _, user := range gb.Users { + // We have already validated that user.Name and user.PasswordHash are non-empty + allUsers = append(allUsers, user.Name) + // Command for setting users password + cmds = append(cmds, fmt.Sprintf("password_pbkdf2 %s %s", user.Name, *user.PasswordHash)) + } + superUserCmd := fmt.Sprintf("set superusers=\"%s\"\n", strings.Join(allUsers, " ")) + return "# Generated by Butane\n\n" + superUserCmd + strings.Join(cmds, "\n") + "\n" +} diff --git a/vendor/github.com/coreos/butane/config/fcos/v1_6_exp/validate.go b/vendor/github.com/coreos/butane/config/fcos/v1_6_exp/validate.go new file mode 100644 index 0000000000..b37585c465 --- /dev/null +++ b/vendor/github.com/coreos/butane/config/fcos/v1_6_exp/validate.go @@ -0,0 +1,53 @@ +// Copyright 2020 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package v1_6_exp + +import ( + "github.com/coreos/butane/config/common" + "github.com/coreos/ignition/v2/config/util" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (d BootDevice) Validate(c path.ContextPath) (r report.Report) { + if d.Layout != nil { + switch *d.Layout { + case "aarch64", "ppc64le", "x86_64": + default: + r.AddOnError(c.Append("layout"), common.ErrUnknownBootDeviceLayout) + } + } + r.Merge(d.Mirror.Validate(c.Append("mirror"))) + return +} + +func (m BootDeviceMirror) Validate(c path.ContextPath) (r report.Report) { + if len(m.Devices) == 1 { + r.AddOnError(c.Append("devices"), common.ErrTooFewMirrorDevices) + } + return +} + +func (user GrubUser) Validate(c path.ContextPath) (r report.Report) { + if user.Name == "" { + r.AddOnError(c.Append("name"), common.ErrGrubUserNameNotSpecified) + } + + if !util.NotEmpty(user.PasswordHash) { + r.AddOnError(c.Append("password_hash"), common.ErrGrubPasswordNotSpecified) + } + return +} diff --git a/vendor/github.com/coreos/butane/config/flatcar/v1_0/translate.go b/vendor/github.com/coreos/butane/config/flatcar/v1_0/translate.go index 498c329f5c..f5cd555f6f 100644 --- a/vendor/github.com/coreos/butane/config/flatcar/v1_0/translate.go +++ b/vendor/github.com/coreos/butane/config/flatcar/v1_0/translate.go @@ -52,7 +52,7 @@ func (c Config) ToIgn3_3(options common.TranslateOptions) (types.Config, report. return cfg.(types.Config), r, err } -// ToIgn3_3Bytes translates from a v1.4 Butane config to a v3.3.0 Ignition config. It returns a report of any errors or +// ToIgn3_3Bytes translates from a v1.0 Butane config to a v3.3.0 Ignition config. It returns a report of any errors or // warnings in the source and resultant config. If the report has fatal errors or it encounters other problems // translating, an error is returned. func ToIgn3_3Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { diff --git a/vendor/github.com/coreos/butane/config/flatcar/v1_1_exp/schema.go b/vendor/github.com/coreos/butane/config/flatcar/v1_1/schema.go similarity index 91% rename from vendor/github.com/coreos/butane/config/flatcar/v1_1_exp/schema.go rename to vendor/github.com/coreos/butane/config/flatcar/v1_1/schema.go index 72f1984fa9..f27027370a 100644 --- a/vendor/github.com/coreos/butane/config/flatcar/v1_1_exp/schema.go +++ b/vendor/github.com/coreos/butane/config/flatcar/v1_1/schema.go @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v1_1_exp +package v1_1 import ( - base "github.com/coreos/butane/base/v0_5_exp" + base "github.com/coreos/butane/base/v0_5" ) type Config struct { diff --git a/vendor/github.com/coreos/butane/config/flatcar/v1_1_exp/translate.go b/vendor/github.com/coreos/butane/config/flatcar/v1_1/translate.go similarity index 96% rename from vendor/github.com/coreos/butane/config/flatcar/v1_1_exp/translate.go rename to vendor/github.com/coreos/butane/config/flatcar/v1_1/translate.go index 6ac5a38986..4a8b071eec 100644 --- a/vendor/github.com/coreos/butane/config/flatcar/v1_1_exp/translate.go +++ b/vendor/github.com/coreos/butane/config/flatcar/v1_1/translate.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v1_1_exp +package v1_1 import ( "github.com/coreos/butane/config/common" @@ -52,7 +52,7 @@ func (c Config) ToIgn3_4(options common.TranslateOptions) (types.Config, report. return cfg.(types.Config), r, err } -// ToIgn3_4Bytes translates from a v1.4 Butane config to a v3.3.0 Ignition config. It returns a report of any errors or +// ToIgn3_4Bytes translates from a v1.1 Butane config to a v3.4.0 Ignition config. It returns a report of any errors or // warnings in the source and resultant config. If the report has fatal errors or it encounters other problems // translating, an error is returned. func ToIgn3_4Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { diff --git a/vendor/github.com/coreos/butane/config/flatcar/v1_2_exp/schema.go b/vendor/github.com/coreos/butane/config/flatcar/v1_2_exp/schema.go new file mode 100644 index 0000000000..3719293dbb --- /dev/null +++ b/vendor/github.com/coreos/butane/config/flatcar/v1_2_exp/schema.go @@ -0,0 +1,23 @@ +// Copyright 2020 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package v1_2_exp + +import ( + base "github.com/coreos/butane/base/v0_6_exp" +) + +type Config struct { + base.Config `yaml:",inline"` +} diff --git a/vendor/github.com/coreos/butane/config/flatcar/v1_2_exp/translate.go b/vendor/github.com/coreos/butane/config/flatcar/v1_2_exp/translate.go new file mode 100644 index 0000000000..bb8dafcc00 --- /dev/null +++ b/vendor/github.com/coreos/butane/config/flatcar/v1_2_exp/translate.go @@ -0,0 +1,60 @@ +// Copyright 2020 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package v1_2_exp + +import ( + "github.com/coreos/butane/config/common" + cutil "github.com/coreos/butane/config/util" + "github.com/coreos/butane/translate" + + "github.com/coreos/ignition/v2/config/v3_5_experimental/types" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +// ToIgn3_5Unvalidated translates the config to an Ignition config. It also +// returns the set of translations it did so paths in the resultant config +// can be tracked back to their source in the source config. No config +// validation is performed on input or output. +func (c Config) ToIgn3_5Unvalidated(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { + ret, ts, r := c.Config.ToIgn3_5Unvalidated(options) + if r.IsFatal() { + return types.Config{}, translate.TranslationSet{}, r + } + + for i, luks := range ret.Storage.Luks { + if luks.Clevis.IsPresent() { + r.AddOnError(path.New("json", "storage", "luks", i, "clevis"), common.ErrClevisSupport) + } + } + + return ret, ts, r +} + +// ToIgn3_5 translates the config to an Ignition config. It returns a +// report of any errors or warnings in the source and resultant config. If +// the report has fatal errors or it encounters other problems translating, +// an error is returned. +func (c Config) ToIgn3_5(options common.TranslateOptions) (types.Config, report.Report, error) { + cfg, r, err := cutil.Translate(c, "ToIgn3_5Unvalidated", options) + return cfg.(types.Config), r, err +} + +// ToIgn3_5Bytes translates from a v1.2 Butane config to a v3.5.0 Ignition config. It returns a report of any errors or +// warnings in the source and resultant config. If the report has fatal errors or it encounters other problems +// translating, an error is returned. +func ToIgn3_5Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { + return cutil.TranslateBytes(input, &Config{}, "ToIgn3_5", options) +} diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_13_exp/result/schema.go b/vendor/github.com/coreos/butane/config/openshift/v4_13/result/schema.go similarity index 96% rename from vendor/github.com/coreos/butane/config/openshift/v4_13_exp/result/schema.go rename to vendor/github.com/coreos/butane/config/openshift/v4_13/result/schema.go index ad5abd8ee2..37e49f3028 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_13_exp/result/schema.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_13/result/schema.go @@ -15,7 +15,7 @@ package result import ( - "github.com/coreos/ignition/v2/config/v3_4/types" + "github.com/coreos/ignition/v2/config/v3_2/types" ) const ( diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_13_exp/schema.go b/vendor/github.com/coreos/butane/config/openshift/v4_13/schema.go similarity index 94% rename from vendor/github.com/coreos/butane/config/openshift/v4_13_exp/schema.go rename to vendor/github.com/coreos/butane/config/openshift/v4_13/schema.go index ab204d39dd..54e6bfda09 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_13_exp/schema.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_13/schema.go @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v4_13_exp +package v4_13 import ( - fcos "github.com/coreos/butane/config/fcos/v1_5_exp" + fcos "github.com/coreos/butane/config/fcos/v1_3" ) const ROLE_LABEL_KEY = "machineconfiguration.openshift.io/role" diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_13/translate.go b/vendor/github.com/coreos/butane/config/openshift/v4_13/translate.go new file mode 100644 index 0000000000..9f1404f6da --- /dev/null +++ b/vendor/github.com/coreos/butane/config/openshift/v4_13/translate.go @@ -0,0 +1,279 @@ +// Copyright 2020 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package v4_13 + +import ( + "net/url" + "reflect" + "strings" + + "github.com/coreos/butane/config/common" + "github.com/coreos/butane/config/openshift/v4_13/result" + cutil "github.com/coreos/butane/config/util" + "github.com/coreos/butane/translate" + + "github.com/coreos/ignition/v2/config/util" + "github.com/coreos/ignition/v2/config/v3_2/types" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +const ( + // FIPS 140-2 doesn't allow the default XTS mode + fipsCipherOption = types.LuksOption("--cipher") + fipsCipherShortOption = types.LuksOption("-c") + fipsCipherArgument = types.LuksOption("aes-cbc-essiv:sha256") +) + +// ToMachineConfig4_13Unvalidated translates the config to a MachineConfig. It also +// returns the set of translations it did so paths in the resultant config +// can be tracked back to their source in the source config. No config +// validation is performed on input or output. +func (c Config) ToMachineConfig4_13Unvalidated(options common.TranslateOptions) (result.MachineConfig, translate.TranslationSet, report.Report) { + cfg, ts, r := c.Config.ToIgn3_2Unvalidated(options) + if r.IsFatal() { + return result.MachineConfig{}, ts, r + } + + // wrap + ts = ts.PrefixPaths(path.New("yaml"), path.New("json", "spec", "config")) + mc := result.MachineConfig{ + ApiVersion: result.MC_API_VERSION, + Kind: result.MC_KIND, + Metadata: result.Metadata{ + Name: c.Metadata.Name, + Labels: make(map[string]string), + }, + Spec: result.Spec{ + Config: cfg, + }, + } + ts.AddTranslation(path.New("yaml", "version"), path.New("json", "apiVersion")) + ts.AddTranslation(path.New("yaml", "version"), path.New("json", "kind")) + ts.AddTranslation(path.New("yaml", "metadata"), path.New("json", "metadata")) + ts.AddTranslation(path.New("yaml", "metadata", "name"), path.New("json", "metadata", "name")) + ts.AddTranslation(path.New("yaml", "metadata", "labels"), path.New("json", "metadata", "labels")) + ts.AddTranslation(path.New("yaml", "version"), path.New("json", "spec")) + ts.AddTranslation(path.New("yaml"), path.New("json", "spec", "config")) + for k, v := range c.Metadata.Labels { + mc.Metadata.Labels[k] = v + ts.AddTranslation(path.New("yaml", "metadata", "labels", k), path.New("json", "metadata", "labels", k)) + } + + // translate OpenShift fields + tr := translate.NewTranslator("yaml", "json", options) + from := &c.OpenShift + to := &mc.Spec + ts2, r2 := translate.Prefixed(tr, "extensions", &from.Extensions, &to.Extensions) + translate.MergeP(tr, ts2, &r2, "fips", &from.FIPS, &to.FIPS) + translate.MergeP2(tr, ts2, &r2, "kernel_arguments", &from.KernelArguments, "kernelArguments", &to.KernelArguments) + translate.MergeP2(tr, ts2, &r2, "kernel_type", &from.KernelType, "kernelType", &to.KernelType) + ts.MergeP2("openshift", "spec", ts2) + r.Merge(r2) + + // apply FIPS options to LUKS volumes + ts.Merge(addLuksFipsOptions(&mc)) + + // finally, check the fully desugared config for RHCOS and MCO support + r.Merge(validateRHCOSSupport(mc, ts)) + r.Merge(validateMCOSupport(mc, ts)) + + return mc, ts, r +} + +// ToMachineConfig4_13 translates the config to a MachineConfig. It returns a +// report of any errors or warnings in the source and resultant config. If +// the report has fatal errors or it encounters other problems translating, +// an error is returned. +func (c Config) ToMachineConfig4_13(options common.TranslateOptions) (result.MachineConfig, report.Report, error) { + cfg, r, err := cutil.Translate(c, "ToMachineConfig4_13Unvalidated", options) + return cfg.(result.MachineConfig), r, err +} + +// ToIgn3_2Unvalidated translates the config to an Ignition config. It also +// returns the set of translations it did so paths in the resultant config +// can be tracked back to their source in the source config. No config +// validation is performed on input or output. +func (c Config) ToIgn3_2Unvalidated(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { + mc, ts, r := c.ToMachineConfig4_13Unvalidated(options) + cfg := mc.Spec.Config + + // report warnings if there are any non-empty fields in Spec (other + // than the Ignition config itself) that we're ignoring + mc.Spec.Config = types.Config{} + warnings := translate.PrefixReport(cutil.CheckForElidedFields(mc.Spec), "spec") + // translate from json space into yaml space + r.Merge(cutil.TranslateReportPaths(warnings, ts)) + + ts = ts.Descend(path.New("json", "spec", "config")) + return cfg, ts, r +} + +// ToIgn3_2 translates the config to an Ignition config. It returns a +// report of any errors or warnings in the source and resultant config. If +// the report has fatal errors or it encounters other problems translating, +// an error is returned. +func (c Config) ToIgn3_2(options common.TranslateOptions) (types.Config, report.Report, error) { + cfg, r, err := cutil.Translate(c, "ToIgn3_2Unvalidated", options) + return cfg.(types.Config), r, err +} + +// ToConfigBytes translates from a v4.13 Butane config to a v4.13 MachineConfig or a v3.2.0 Ignition config. It returns a report of any errors or +// warnings in the source and resultant config. If the report has fatal errors or it encounters other problems +// translating, an error is returned. +func ToConfigBytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { + if options.Raw { + return cutil.TranslateBytes(input, &Config{}, "ToIgn3_2", options) + } else { + return cutil.TranslateBytesYAML(input, &Config{}, "ToMachineConfig4_13", options) + } +} + +func addLuksFipsOptions(mc *result.MachineConfig) translate.TranslationSet { + ts := translate.NewTranslationSet("yaml", "json") + if !util.IsTrue(mc.Spec.FIPS) { + return ts + } + +OUTER: + for i := range mc.Spec.Config.Storage.Luks { + luks := &mc.Spec.Config.Storage.Luks[i] + // Only add options if the user hasn't already specified + // a cipher option. Do this in-place, since config merging + // doesn't support conditional logic. + for _, option := range luks.Options { + if option == fipsCipherOption || + strings.HasPrefix(string(option), string(fipsCipherOption)+"=") || + option == fipsCipherShortOption { + continue OUTER + } + } + for j := 0; j < 2; j++ { + ts.AddTranslation(path.New("yaml", "openshift", "fips"), path.New("json", "spec", "config", "storage", "luks", i, "options", len(luks.Options)+j)) + } + if len(luks.Options) == 0 { + ts.AddTranslation(path.New("yaml", "openshift", "fips"), path.New("json", "spec", "config", "storage", "luks", i, "options")) + } + luks.Options = append(luks.Options, fipsCipherOption, fipsCipherArgument) + } + return ts +} + +// Error on fields that are rejected by RHCOS. +// +// Some of these fields may have been generated by sugar (e.g. +// boot_device.luks), so we work in JSON (output) space and then translate +// paths back to YAML (input) space. That's also the reason we do these +// checks after translation, rather than during validation. +func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) report.Report { + var r report.Report + for i, fs := range mc.Spec.Config.Storage.Filesystems { + if fs.Format != nil && *fs.Format == "btrfs" { + // we don't ship mkfs.btrfs + r.AddOnError(path.New("json", "spec", "config", "storage", "filesystems", i, "format"), common.ErrBtrfsSupport) + } + } + return cutil.TranslateReportPaths(r, ts) +} + +// Error on fields that are rejected outright by the MCO, or that are +// unsupported by the MCO and we want to discourage. +// +// https://github.com/openshift/machine-config-operator/blob/d6dabadeca05/MachineConfigDaemon.md#supported-vs-unsupported-ignition-config-changes +// +// Some of these fields may have been generated by sugar (e.g. storage.trees), +// so we work in JSON (output) space and then translate paths back to YAML +// (input) space. That's also the reason we do these checks after +// translation, rather than during validation. +func validateMCOSupport(mc result.MachineConfig, ts translate.TranslationSet) report.Report { + // Error classes for the purposes of this function: + // + // UNPARSABLE - Cannot be rendered into a config by the MCC. If + // present in MC, MCC will mark the pool degraded. We reject these. + // + // FORBIDDEN - Not supported by the MCD. If present in MC, MCD will + // mark the node degraded. We reject these. + // + // IMMUTABLE - Permitted in MC, passed through to Ignition, but not + // supported by the MCD. MCD will mark the node degraded if the + // field changes after the node is provisioned. We reject these + // outright to discourage their use. + // + // TRIPWIRE - A subset of fields in the containing struct are + // supported by the MCD. If the struct contents change after the node + // is provisioned, and the struct contains unsupported fields, MCD + // will mark the node degraded, even if the change only affects + // supported fields. We reject these. + + var r report.Report + for i := range mc.Spec.Config.Storage.Directories { + // IMMUTABLE + r.AddOnError(path.New("json", "spec", "config", "storage", "directories", i), common.ErrDirectorySupport) + } + for i, file := range mc.Spec.Config.Storage.Files { + if len(file.Append) > 0 { + // FORBIDDEN + r.AddOnError(path.New("json", "spec", "config", "storage", "files", i, "append"), common.ErrFileAppendSupport) + } + if file.Contents.Source != nil { + fileSource, err := url.Parse(*file.Contents.Source) + // parse errors will be caught by normal config validation + if err == nil && fileSource.Scheme != "data" { + // FORBIDDEN + r.AddOnError(path.New("json", "spec", "config", "storage", "files", i, "contents", "source"), common.ErrFileSchemeSupport) + } + } + if file.Mode != nil && *file.Mode & ^0777 != 0 { + // UNPARSABLE + r.AddOnError(path.New("json", "spec", "config", "storage", "files", i, "mode"), common.ErrFileSpecialModeSupport) + } + } + for i := range mc.Spec.Config.Storage.Links { + // IMMUTABLE + // If you change this to be less restrictive without adding + // link support in the MCO, consider what should happen if + // the user specifies a storage.tree that includes symlinks. + r.AddOnError(path.New("json", "spec", "config", "storage", "links", i), common.ErrLinkSupport) + } + for i := range mc.Spec.Config.Passwd.Groups { + // IMMUTABLE + r.AddOnError(path.New("json", "spec", "config", "passwd", "groups", i), common.ErrGroupSupport) + } + for i, user := range mc.Spec.Config.Passwd.Users { + if user.Name == "core" { + // PasswordHash and SSHAuthorizedKeys are managed; other fields are not + v := reflect.ValueOf(user) + t := v.Type() + for j := 0; j < v.NumField(); j++ { + fv := v.Field(j) + ft := t.Field(j) + switch ft.Name { + case "Name", "PasswordHash", "SSHAuthorizedKeys": + continue + default: + if fv.IsValid() && !fv.IsZero() { + tag := strings.Split(ft.Tag.Get("json"), ",")[0] + // TRIPWIRE + r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i, tag), common.ErrUserFieldSupport) + } + } + } + } else { + // TRIPWIRE + r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i), common.ErrUserNameSupport) + } + } + return cutil.TranslateReportPaths(r, ts) +} diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_13_exp/validate.go b/vendor/github.com/coreos/butane/config/openshift/v4_13/validate.go similarity index 98% rename from vendor/github.com/coreos/butane/config/openshift/v4_13_exp/validate.go rename to vendor/github.com/coreos/butane/config/openshift/v4_13/validate.go index 74f0955d15..d1e932d6f8 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_13_exp/validate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_13/validate.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v4_13_exp +package v4_13 import ( "github.com/coreos/butane/config/common" diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/result/schema.go b/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/result/schema.go new file mode 100644 index 0000000000..c8d45b7944 --- /dev/null +++ b/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/result/schema.go @@ -0,0 +1,48 @@ +// Copyright 2021 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package result + +import ( + "github.com/coreos/ignition/v2/config/v3_5_experimental/types" +) + +const ( + MC_API_VERSION = "machineconfiguration.openshift.io/v1" + MC_KIND = "MachineConfig" +) + +// We round-trip through JSON because Ignition uses `json` struct tags, +// so all struct tags need to be `json` even though we're ultimately +// writing YAML. + +type MachineConfig struct { + ApiVersion string `json:"apiVersion"` + Kind string `json:"kind"` + Metadata Metadata `json:"metadata"` + Spec Spec `json:"spec"` +} + +type Metadata struct { + Name string `json:"name"` + Labels map[string]string `json:"labels,omitempty"` +} + +type Spec struct { + Config types.Config `json:"config"` + KernelArguments []string `json:"kernelArguments,omitempty"` + Extensions []string `json:"extensions,omitempty"` + FIPS *bool `json:"fips,omitempty"` + KernelType *string `json:"kernelType,omitempty"` +} diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/schema.go b/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/schema.go new file mode 100644 index 0000000000..16d45ab4c5 --- /dev/null +++ b/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/schema.go @@ -0,0 +1,39 @@ +// Copyright 2020 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package v4_14_exp + +import ( + fcos "github.com/coreos/butane/config/fcos/v1_6_exp" +) + +const ROLE_LABEL_KEY = "machineconfiguration.openshift.io/role" + +type Config struct { + fcos.Config `yaml:",inline"` + Metadata Metadata `yaml:"metadata"` + OpenShift OpenShift `yaml:"openshift"` +} + +type Metadata struct { + Name string `yaml:"name"` + Labels map[string]string `yaml:"labels,omitempty"` +} + +type OpenShift struct { + KernelArguments []string `yaml:"kernel_arguments"` + Extensions []string `yaml:"extensions"` + FIPS *bool `yaml:"fips"` + KernelType *string `yaml:"kernel_type"` +} diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_13_exp/translate.go b/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/translate.go similarity index 91% rename from vendor/github.com/coreos/butane/config/openshift/v4_13_exp/translate.go rename to vendor/github.com/coreos/butane/config/openshift/v4_14_exp/translate.go index 4f951b4935..b8780ddca6 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_13_exp/translate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/translate.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v4_13_exp +package v4_14_exp import ( "net/url" @@ -20,12 +20,12 @@ import ( "strings" "github.com/coreos/butane/config/common" - "github.com/coreos/butane/config/openshift/v4_13_exp/result" + "github.com/coreos/butane/config/openshift/v4_14_exp/result" cutil "github.com/coreos/butane/config/util" "github.com/coreos/butane/translate" "github.com/coreos/ignition/v2/config/util" - "github.com/coreos/ignition/v2/config/v3_4/types" + "github.com/coreos/ignition/v2/config/v3_5_experimental/types" "github.com/coreos/vcontext/path" "github.com/coreos/vcontext/report" ) @@ -37,12 +37,12 @@ const ( fipsCipherArgument = types.LuksOption("aes-cbc-essiv:sha256") ) -// ToMachineConfig4_13Unvalidated translates the config to a MachineConfig. It also +// ToMachineConfig4_14Unvalidated translates the config to a MachineConfig. It also // returns the set of translations it did so paths in the resultant config // can be tracked back to their source in the source config. No config // validation is performed on input or output. -func (c Config) ToMachineConfig4_13Unvalidated(options common.TranslateOptions) (result.MachineConfig, translate.TranslationSet, report.Report) { - cfg, ts, r := c.Config.ToIgn3_4Unvalidated(options) +func (c Config) ToMachineConfig4_14Unvalidated(options common.TranslateOptions) (result.MachineConfig, translate.TranslationSet, report.Report) { + cfg, ts, r := c.Config.ToIgn3_5Unvalidated(options) if r.IsFatal() { return result.MachineConfig{}, ts, r } @@ -94,21 +94,21 @@ func (c Config) ToMachineConfig4_13Unvalidated(options common.TranslateOptions) return mc, ts, r } -// ToMachineConfig4_13 translates the config to a MachineConfig. It returns a +// ToMachineConfig4_14 translates the config to a MachineConfig. It returns a // report of any errors or warnings in the source and resultant config. If // the report has fatal errors or it encounters other problems translating, // an error is returned. -func (c Config) ToMachineConfig4_13(options common.TranslateOptions) (result.MachineConfig, report.Report, error) { - cfg, r, err := cutil.Translate(c, "ToMachineConfig4_13Unvalidated", options) +func (c Config) ToMachineConfig4_14(options common.TranslateOptions) (result.MachineConfig, report.Report, error) { + cfg, r, err := cutil.Translate(c, "ToMachineConfig4_14Unvalidated", options) return cfg.(result.MachineConfig), r, err } -// ToIgn3_4Unvalidated translates the config to an Ignition config. It also +// ToIgn3_5Unvalidated translates the config to an Ignition config. It also // returns the set of translations it did so paths in the resultant config // can be tracked back to their source in the source config. No config // validation is performed on input or output. -func (c Config) ToIgn3_4Unvalidated(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { - mc, ts, r := c.ToMachineConfig4_13Unvalidated(options) +func (c Config) ToIgn3_5Unvalidated(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { + mc, ts, r := c.ToMachineConfig4_14Unvalidated(options) cfg := mc.Spec.Config // report warnings if there are any non-empty fields in Spec (other @@ -122,23 +122,23 @@ func (c Config) ToIgn3_4Unvalidated(options common.TranslateOptions) (types.Conf return cfg, ts, r } -// ToIgn3_4 translates the config to an Ignition config. It returns a +// ToIgn3_5 translates the config to an Ignition config. It returns a // report of any errors or warnings in the source and resultant config. If // the report has fatal errors or it encounters other problems translating, // an error is returned. -func (c Config) ToIgn3_4(options common.TranslateOptions) (types.Config, report.Report, error) { - cfg, r, err := cutil.Translate(c, "ToIgn3_4Unvalidated", options) +func (c Config) ToIgn3_5(options common.TranslateOptions) (types.Config, report.Report, error) { + cfg, r, err := cutil.Translate(c, "ToIgn3_5Unvalidated", options) return cfg.(types.Config), r, err } -// ToConfigBytes translates from a v4.13 Butane config to a v4.13 MachineConfig or a v3.4.0 Ignition config. It returns a report of any errors or +// ToConfigBytes translates from a v4.14 Butane config to a v4.14 MachineConfig or a v3.4.0 Ignition config. It returns a report of any errors or // warnings in the source and resultant config. If the report has fatal errors or it encounters other problems // translating, an error is returned. func ToConfigBytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { if options.Raw { - return cutil.TranslateBytes(input, &Config{}, "ToIgn3_4", options) + return cutil.TranslateBytes(input, &Config{}, "ToIgn3_5", options) } else { - return cutil.TranslateBytesYAML(input, &Config{}, "ToMachineConfig4_13", options) + return cutil.TranslateBytesYAML(input, &Config{}, "ToMachineConfig4_14", options) } } diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/validate.go b/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/validate.go new file mode 100644 index 0000000000..09cc167335 --- /dev/null +++ b/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/validate.go @@ -0,0 +1,43 @@ +// Copyright 2021 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package v4_14_exp + +import ( + "github.com/coreos/butane/config/common" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +func (m Metadata) Validate(c path.ContextPath) (r report.Report) { + if m.Name == "" { + r.AddOnError(c.Append("name"), common.ErrNameRequired) + } + if m.Labels[ROLE_LABEL_KEY] == "" { + r.AddOnError(c.Append("labels", ROLE_LABEL_KEY), common.ErrRoleRequired) + } + return +} + +func (os OpenShift) Validate(c path.ContextPath) (r report.Report) { + if os.KernelType != nil { + switch *os.KernelType { + case "", "default", "realtime": + default: + r.AddOnError(c.Append("kernel_type"), common.ErrInvalidKernelType) + } + } + return +} diff --git a/vendor/github.com/coreos/butane/config/r4e/v1_1_exp/schema.go b/vendor/github.com/coreos/butane/config/r4e/v1_1/schema.go similarity index 91% rename from vendor/github.com/coreos/butane/config/r4e/v1_1_exp/schema.go rename to vendor/github.com/coreos/butane/config/r4e/v1_1/schema.go index 56deb4c938..5305452b03 100644 --- a/vendor/github.com/coreos/butane/config/r4e/v1_1_exp/schema.go +++ b/vendor/github.com/coreos/butane/config/r4e/v1_1/schema.go @@ -12,10 +12,10 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v1_1_exp +package v1_1 import ( - base "github.com/coreos/butane/base/v0_5_exp" + base "github.com/coreos/butane/base/v0_5" ) type Config struct { diff --git a/vendor/github.com/coreos/butane/config/r4e/v1_1_exp/translate.go b/vendor/github.com/coreos/butane/config/r4e/v1_1/translate.go similarity index 99% rename from vendor/github.com/coreos/butane/config/r4e/v1_1_exp/translate.go rename to vendor/github.com/coreos/butane/config/r4e/v1_1/translate.go index fa0ab657c7..fb855e15e9 100644 --- a/vendor/github.com/coreos/butane/config/r4e/v1_1_exp/translate.go +++ b/vendor/github.com/coreos/butane/config/r4e/v1_1/translate.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v1_1_exp +package v1_1 import ( "github.com/coreos/butane/config/common" diff --git a/vendor/github.com/coreos/butane/config/r4e/v1_2_exp/schema.go b/vendor/github.com/coreos/butane/config/r4e/v1_2_exp/schema.go new file mode 100644 index 0000000000..ffece594a5 --- /dev/null +++ b/vendor/github.com/coreos/butane/config/r4e/v1_2_exp/schema.go @@ -0,0 +1,23 @@ +// Copyright 2022 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package v1_2_exp + +import ( + base "github.com/coreos/butane/base/v0_6_exp" +) + +type Config struct { + base.Config `yaml:",inline"` +} diff --git a/vendor/github.com/coreos/butane/config/r4e/v1_2_exp/translate.go b/vendor/github.com/coreos/butane/config/r4e/v1_2_exp/translate.go new file mode 100644 index 0000000000..228150c349 --- /dev/null +++ b/vendor/github.com/coreos/butane/config/r4e/v1_2_exp/translate.go @@ -0,0 +1,78 @@ +// Copyright 2022 Red Hat, Inc +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License.) + +package v1_2_exp + +import ( + "github.com/coreos/butane/config/common" + cutil "github.com/coreos/butane/config/util" + "github.com/coreos/butane/translate" + "github.com/coreos/ignition/v2/config/v3_5_experimental/types" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +// ToIgn3_5Unvalidated translates the config to an Ignition config. It also +// returns the set of translations it did so paths in the resultant config +// can be tracked back to their source in the source config. No config +// validation is performed on input or output. +func (c Config) ToIgn3_5Unvalidated(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { + ret, ts, r := c.Config.ToIgn3_5Unvalidated(options) + if r.IsFatal() { + return types.Config{}, translate.TranslationSet{}, r + } + + checkForForbiddenFields(ret, &r) + + return ret, ts, r +} + +// Checks and adds the appropiate errors when unsupported fields on r4e are +// provided +func checkForForbiddenFields(t types.Config, r *report.Report) { + for i := range t.KernelArguments.ShouldExist { + r.AddOnError(path.New("path", "json", "kernel_arguments", "should_exist", i), common.ErrGeneralKernelArgumentSupport) + } + for i := range t.KernelArguments.ShouldNotExist { + r.AddOnError(path.New("path", "json", "kernel_arguments", "should_not_exist", i), common.ErrGeneralKernelArgumentSupport) + } + for i := range t.Storage.Disks { + r.AddOnError(path.New("path", "json", "storage", "disks", i), common.ErrDiskSupport) + } + for i := range t.Storage.Filesystems { + r.AddOnError(path.New("path", "json", "storage", "filesystems", i), common.ErrFilesystemSupport) + } + for i := range t.Storage.Luks { + r.AddOnError(path.New("path", "json", "storage", "luks", i), common.ErrLuksSupport) + } + for i := range t.Storage.Raid { + r.AddOnError(path.New("path", "json", "storage", "raid", i), common.ErrRaidSupport) + } +} + +// ToIgn3_5 translates the config to an Ignition config. It returns a +// report of any errors or warnings in the source and resultant config. If +// the report has fatal errors or it encounters other problems translating, +// an error is returned. +func (c Config) ToIgn3_5(options common.TranslateOptions) (types.Config, report.Report, error) { + cfg, r, err := cutil.Translate(c, "ToIgn3_5Unvalidated", options) + return cfg.(types.Config), r, err +} + +// ToIgn3_5Bytes translates from a v1.2 Butane config to a v3.5.0 Ignition config. It returns a report of any errors or +// warnings in the source and resultant config. If the report has fatal errors or it encounters other problems +// translating, an error is returned. +func ToIgn3_5Bytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { + return cutil.TranslateBytes(input, &Config{}, "ToIgn3_5", options) +} diff --git a/vendor/modules.txt b/vendor/modules.txt index de63fc0d10..5398df11a1 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -223,14 +223,15 @@ github.com/aws/aws-sdk-go/service/sts/stsiface # github.com/clarketm/json v1.17.1 ## explicit github.com/clarketm/json -# github.com/coreos/butane v0.17.1-0.20230309182032-fac76883d698 +# github.com/coreos/butane v0.18.0 ## explicit; go 1.18 github.com/coreos/butane/base/util github.com/coreos/butane/base/v0_1 github.com/coreos/butane/base/v0_2 github.com/coreos/butane/base/v0_3 github.com/coreos/butane/base/v0_4 -github.com/coreos/butane/base/v0_5_exp +github.com/coreos/butane/base/v0_5 +github.com/coreos/butane/base/v0_6_exp github.com/coreos/butane/config github.com/coreos/butane/config/common github.com/coreos/butane/config/fcos/v1_0 @@ -238,23 +239,28 @@ github.com/coreos/butane/config/fcos/v1_1 github.com/coreos/butane/config/fcos/v1_2 github.com/coreos/butane/config/fcos/v1_3 github.com/coreos/butane/config/fcos/v1_4 -github.com/coreos/butane/config/fcos/v1_5_exp +github.com/coreos/butane/config/fcos/v1_5 +github.com/coreos/butane/config/fcos/v1_6_exp github.com/coreos/butane/config/flatcar/v1_0 -github.com/coreos/butane/config/flatcar/v1_1_exp +github.com/coreos/butane/config/flatcar/v1_1 +github.com/coreos/butane/config/flatcar/v1_2_exp github.com/coreos/butane/config/openshift/v4_10 github.com/coreos/butane/config/openshift/v4_10/result github.com/coreos/butane/config/openshift/v4_11 github.com/coreos/butane/config/openshift/v4_11/result github.com/coreos/butane/config/openshift/v4_12 github.com/coreos/butane/config/openshift/v4_12/result -github.com/coreos/butane/config/openshift/v4_13_exp -github.com/coreos/butane/config/openshift/v4_13_exp/result +github.com/coreos/butane/config/openshift/v4_13 +github.com/coreos/butane/config/openshift/v4_13/result +github.com/coreos/butane/config/openshift/v4_14_exp +github.com/coreos/butane/config/openshift/v4_14_exp/result github.com/coreos/butane/config/openshift/v4_8 github.com/coreos/butane/config/openshift/v4_8/result github.com/coreos/butane/config/openshift/v4_9 github.com/coreos/butane/config/openshift/v4_9/result github.com/coreos/butane/config/r4e/v1_0 -github.com/coreos/butane/config/r4e/v1_1_exp +github.com/coreos/butane/config/r4e/v1_1 +github.com/coreos/butane/config/r4e/v1_2_exp github.com/coreos/butane/config/util github.com/coreos/butane/translate # github.com/coreos/go-json v0.0.0-20230131223807-18775e0fb4fb