diff --git a/go.mod b/go.mod index 610622610b..1585b0d9f4 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.47.9 - github.com/coreos/butane v0.18.1-0.20230412230143-79c207705ee4 + github.com/coreos/butane v0.19.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 883ab3e9e1..b7d4e4798a 100644 --- a/go.sum +++ b/go.sum @@ -47,8 +47,8 @@ github.com/containers/image/v5 v5.28.0 h1:H4cWbdI88UA/mDb6SxMo3IxpmS1BSs/Kifvhwt github.com/containers/image/v5 v5.28.0/go.mod h1:9aPnNkwHNHgGl9VlQxXEshvmOJRbdRAc1rNDD6sP2eU= github.com/containers/storage v1.50.1 h1:1r5k4N2BNa94WZZFw116tozj08zJg7SxihQZ3iccyCs= github.com/containers/storage v1.50.1/go.mod h1:dpspZsUrcKD8SpTofvKWhwPDHD0MkO4Q7VE+oYdWkiA= -github.com/coreos/butane v0.18.1-0.20230412230143-79c207705ee4 h1:YB2AgNmzotg/PwcMqy3sqej32rDkZ7OVQZtzI3Rv3ck= -github.com/coreos/butane v0.18.1-0.20230412230143-79c207705ee4/go.mod h1:oLR7xKzJB3o1WIdLtfHlPwrSwvMmeV/zknkwOznRu88= +github.com/coreos/butane v0.19.0 h1:F4uuWwIaOCA6YrBOKoVU1cb25SMIkuValW9p1/PXyO8= +github.com/coreos/butane v0.19.0/go.mod h1:dfa3/aWa58qfWMK/CGm3OR3T328x6x2nm66MgZURCTs= 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/test.go b/vendor/github.com/coreos/butane/base/util/test.go index f4d8ca83f5..61b5180e6b 100644 --- a/vendor/github.com/coreos/butane/base/util/test.go +++ b/vendor/github.com/coreos/butane/base/util/test.go @@ -15,17 +15,29 @@ package util import ( + "reflect" + "regexp" + "strings" "testing" "github.com/coreos/butane/translate" + + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" "github.com/stretchr/testify/assert" ) // helper functions for writing tests -// VerifyTranslations ensures all the translations are identity, unless they -// match a listed one, and verifies that all the listed ones exist. +// VerifyTranslations validates a TranslationSet from 'yaml' to 'json'. It +// expects all translations to be identity, unless they match a listed one, +// and all the listed ones to exist. func VerifyTranslations(t *testing.T, set translate.TranslationSet, exceptions []translate.Translation) { + // check tags + assert.Equal(t, set.FromTag, "yaml") + assert.Equal(t, set.ToTag, "json") + + // build up exceptions and check them exceptionSet := translate.NewTranslationSet(set.FromTag, set.ToTag) for _, ex := range exceptions { exceptionSet.AddTranslation(ex.From, ex.To) @@ -35,9 +47,90 @@ func VerifyTranslations(t *testing.T, set translate.TranslationSet, exceptions [ t.Errorf("missing non-identity translation %v", ex) } } + + // walk translations for key, translation := range set.Set { + // unexpected non-identity? if _, ok := exceptionSet.Set[key]; !ok { assert.Equal(t, translation.From.Path, translation.To.Path, "translation is not identity") } + // camel case on left? + assert.NotRegexp(t, regexp.MustCompile("[A-Z]"), translation.From.String(), "from path in camelCase") + // snake case on right? + assert.NotContains(t, translation.To.String(), "_", "to path in snake_case") + } +} + +// VerifyReport verifies that every path in a report corresponds to a valid +// field in the object. +func VerifyReport(t *testing.T, obj interface{}, r report.Report) { + v := reflect.ValueOf(obj) + for _, entry := range r.Entries { + verifyPath(t, v, entry.Context) + } +} + +func verifyPath(t *testing.T, v reflect.Value, p path.ContextPath) { + if len(p.Path) == 0 { + return + } + switch v.Kind() { + case reflect.Map: + value := v.MapIndex(reflect.ValueOf(p.Path[0])) + if v.IsZero() { + t.Errorf("%s: path component %q is nonexistent map key", p, p.Path[0]) + return + } + verifyPath(t, value, p.Tail()) + case reflect.Pointer: + if !v.IsValid() || v.IsNil() { + t.Errorf("%s: path component %q points through a nil pointer", p, p.Path[0]) + return + } + verifyPath(t, v.Elem(), p) + case reflect.Slice: + index, ok := p.Path[0].(int) + if !ok { + t.Errorf("%s: path component %q is not a valid slice index", p, p.Path[0]) + return + } + if index >= v.Len() { + t.Errorf("%s: path index %d out of bounds for slice of length %d", p, index, v.Len()) + return + } + verifyPath(t, v.Index(index), p.Tail()) + case reflect.Struct: + fieldName, ok := p.Path[0].(string) + if !ok { + t.Errorf("%s: path component %q is not a valid struct field name", p, p.Path[0]) + return + } + if !verifyStruct(t, v, p, fieldName) { + t.Errorf("%s: path component %q refers to nonexistent field", p, p.Path[0]) + } + default: + t.Errorf("%s: path component %q points through kind %s", p, p.Path[0], v.Kind()) + } +} + +func verifyStruct(t *testing.T, v reflect.Value, p path.ContextPath, fieldName string) bool { + if v.Kind() != reflect.Struct { + panic("verifyStruct called on non-struct") + } + for i := 0; i < v.NumField(); i++ { + fieldType := v.Type().Field(i) + if fieldType.Anonymous { + if verifyStruct(t, v.Field(i), p, fieldName) { + return true + } + } else { + tag := strings.Split(fieldType.Tag.Get("yaml"), ",")[0] + if tag == fieldName { + verifyPath(t, v.Field(i), p.Tail()) + return true + } + } } + // didn't find field + return false } 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 ded46dbcf5..0433092f76 100644 --- a/vendor/github.com/coreos/butane/base/v0_2/translate.go +++ b/vendor/github.com/coreos/butane/base/v0_2/translate.go @@ -112,7 +112,7 @@ func translateFile(from File, options common.TranslateOptions) (to types.File, t 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.MergeP2(tr, tm, &r, "http_headers", &from.HTTPHeaders, "httpHeaders", &to.HTTPHeaders) translate.MergeP(tr, tm, &r, "source", &from.Source, &to.Source) translate.MergeP(tr, tm, &r, "compression", &from.Compression, &to.Compression) 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 a539b51883..66469a6a9c 100644 --- a/vendor/github.com/coreos/butane/base/v0_3/translate.go +++ b/vendor/github.com/coreos/butane/base/v0_3/translate.go @@ -119,7 +119,7 @@ func translateFile(from File, options common.TranslateOptions) (to types.File, t 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.MergeP2(tr, tm, &r, "http_headers", &from.HTTPHeaders, "httpHeaders", &to.HTTPHeaders) translate.MergeP(tr, tm, &r, "source", &from.Source, &to.Source) translate.MergeP(tr, tm, &r, "compression", &from.Compression, &to.Compression) 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 74c0c187d4..dabb5546be 100644 --- a/vendor/github.com/coreos/butane/base/v0_4/translate.go +++ b/vendor/github.com/coreos/butane/base/v0_4/translate.go @@ -134,7 +134,7 @@ func translateFile(from File, options common.TranslateOptions) (to types.File, t 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.MergeP2(tr, tm, &r, "http_headers", &from.HTTPHeaders, "httpHeaders", &to.HTTPHeaders) translate.MergeP(tr, tm, &r, "source", &from.Source, &to.Source) translate.MergeP(tr, tm, &r, "compression", &from.Compression, &to.Compression) diff --git a/vendor/github.com/coreos/butane/base/v0_5/translate.go b/vendor/github.com/coreos/butane/base/v0_5/translate.go index 4cb00f295c..00fa581b01 100644 --- a/vendor/github.com/coreos/butane/base/v0_5/translate.go +++ b/vendor/github.com/coreos/butane/base/v0_5/translate.go @@ -137,7 +137,7 @@ func translateFile(from File, options common.TranslateOptions) (to types.File, t 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.MergeP2(tr, tm, &r, "http_headers", &from.HTTPHeaders, "httpHeaders", &to.HTTPHeaders) translate.MergeP(tr, tm, &r, "source", &from.Source, &to.Source) translate.MergeP(tr, tm, &r, "compression", &from.Compression, &to.Compression) 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 index 587e0139ce..8cf2395ff5 100644 --- a/vendor/github.com/coreos/butane/base/v0_6_exp/translate.go +++ b/vendor/github.com/coreos/butane/base/v0_6_exp/translate.go @@ -137,7 +137,7 @@ func translateFile(from File, options common.TranslateOptions) (to types.File, t 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.MergeP2(tr, tm, &r, "http_headers", &from.HTTPHeaders, "httpHeaders", &to.HTTPHeaders) translate.MergeP(tr, tm, &r, "source", &from.Source, &to.Source) translate.MergeP(tr, tm, &r, "compression", &from.Compression, &to.Compression) diff --git a/vendor/github.com/coreos/butane/config/common/errors.go b/vendor/github.com/coreos/butane/config/common/errors.go index 1c6e1a91f6..a5f8d5b2bc 100644 --- a/vendor/github.com/coreos/butane/config/common/errors.go +++ b/vendor/github.com/coreos/butane/config/common/errors.go @@ -49,14 +49,16 @@ var ( 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") + 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") + ErrMountPointForbidden = errors.New("path must be under /etc or /var if with_mount_unit is true") // boot device ErrUnknownBootDeviceLayout = errors.New("layout must be one of: aarch64, ppc64le, x86_64") ErrTooFewMirrorDevices = errors.New("mirroring requires at least two devices") // partition + ErrReuseByLabel = errors.New("partitions cannot be reused by label; number must be specified except on boot disk (/dev/disk/by-id/coreos-boot-disk) or when wipe_table is true") ErrWrongPartitionNumber = errors.New("incorrect partition number; a new partition will be created using reserved label") // MachineConfigs @@ -69,11 +71,12 @@ var ( ErrFileSchemeSupport = errors.New("file contents source must be data URL in this spec version") ErrFileAppendSupport = errors.New("appending to files is not supported in this spec version") ErrFileCompressionSupport = errors.New("file compression is not supported in this spec version") + ErrFileHeaderSupport = errors.New("file HTTP headers are not supported in this spec version") ErrFileSpecialModeSupport = errors.New("special mode bits are not supported in this spec version") ErrGroupSupport = errors.New("groups are not supported in this spec version") ErrUserFieldSupport = errors.New("fields other than \"name\", \"ssh_authorized_keys\", and \"password_hash\" (4.13.0+) are not supported in this spec version") ErrUserNameSupport = errors.New("users other than \"core\" are not supported in this spec version") - ErrKernelArgumentSupport = errors.New("this field cannot be used for kernel arguments in this spec version; use openshift.kernel_arguments instead") + ErrKernelArgumentSupport = errors.New("this section cannot be used for kernel arguments in this spec version; use openshift.kernel_arguments instead") // Storage ErrClevisSupport = errors.New("clevis is not supported in this spec version") diff --git a/vendor/github.com/coreos/butane/config/config.go b/vendor/github.com/coreos/butane/config/config.go index 7d1c02a87a..0686758593 100644 --- a/vendor/github.com/coreos/butane/config/config.go +++ b/vendor/github.com/coreos/butane/config/config.go @@ -32,7 +32,8 @@ import ( openshift4_11 "github.com/coreos/butane/config/openshift/v4_11" openshift4_12 "github.com/coreos/butane/config/openshift/v4_12" openshift4_13 "github.com/coreos/butane/config/openshift/v4_13" - openshift4_14_exp "github.com/coreos/butane/config/openshift/v4_14_exp" + openshift4_14 "github.com/coreos/butane/config/openshift/v4_14" + openshift4_15_exp "github.com/coreos/butane/config/openshift/v4_15_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" @@ -71,7 +72,8 @@ func init() { RegisterTranslator("openshift", "4.11.0", openshift4_11.ToConfigBytes) RegisterTranslator("openshift", "4.12.0", openshift4_12.ToConfigBytes) RegisterTranslator("openshift", "4.13.0", openshift4_13.ToConfigBytes) - RegisterTranslator("openshift", "4.14.0-experimental", openshift4_14_exp.ToConfigBytes) + RegisterTranslator("openshift", "4.14.0", openshift4_14.ToConfigBytes) + RegisterTranslator("openshift", "4.15.0-experimental", openshift4_15_exp.ToConfigBytes) RegisterTranslator("r4e", "1.0.0", r4e1_0.ToIgn3_3Bytes) RegisterTranslator("r4e", "1.1.0", r4e1_1.ToIgn3_4Bytes) RegisterTranslator("r4e", "1.2.0-experimental", r4e1_2_exp.ToIgn3_5Bytes) diff --git a/vendor/github.com/coreos/butane/config/fcos/v1_0/translate.go b/vendor/github.com/coreos/butane/config/fcos/v1_0/translate.go index 200e3c3461..d8e0ebf62f 100644 --- a/vendor/github.com/coreos/butane/config/fcos/v1_0/translate.go +++ b/vendor/github.com/coreos/butane/config/fcos/v1_0/translate.go @@ -25,6 +25,11 @@ import ( "github.com/coreos/vcontext/report" ) +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return nil +} + // ToIgn3_0Unvalidated 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 diff --git a/vendor/github.com/coreos/butane/config/fcos/v1_1/translate.go b/vendor/github.com/coreos/butane/config/fcos/v1_1/translate.go index 6e1c88800c..1a38c3ed9e 100644 --- a/vendor/github.com/coreos/butane/config/fcos/v1_1/translate.go +++ b/vendor/github.com/coreos/butane/config/fcos/v1_1/translate.go @@ -25,6 +25,11 @@ import ( "github.com/coreos/vcontext/report" ) +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return nil +} + // ToIgn3_1Unvalidated 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 diff --git a/vendor/github.com/coreos/butane/config/fcos/v1_2/translate.go b/vendor/github.com/coreos/butane/config/fcos/v1_2/translate.go index cff6eb1dc1..9399728c02 100644 --- a/vendor/github.com/coreos/butane/config/fcos/v1_2/translate.go +++ b/vendor/github.com/coreos/butane/config/fcos/v1_2/translate.go @@ -25,6 +25,11 @@ import ( "github.com/coreos/vcontext/report" ) +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return nil +} + // 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 diff --git a/vendor/github.com/coreos/butane/config/fcos/v1_3/translate.go b/vendor/github.com/coreos/butane/config/fcos/v1_3/translate.go index 77af078429..748fb5e881 100644 --- a/vendor/github.com/coreos/butane/config/fcos/v1_3/translate.go +++ b/vendor/github.com/coreos/butane/config/fcos/v1_3/translate.go @@ -54,6 +54,11 @@ const ( bootV1SizeMiB = 384 ) +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return nil +} + // 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 diff --git a/vendor/github.com/coreos/butane/config/fcos/v1_4/translate.go b/vendor/github.com/coreos/butane/config/fcos/v1_4/translate.go index 964926b2e5..9723edaa0a 100644 --- a/vendor/github.com/coreos/butane/config/fcos/v1_4/translate.go +++ b/vendor/github.com/coreos/butane/config/fcos/v1_4/translate.go @@ -54,6 +54,11 @@ const ( bootV1SizeMiB = 384 ) +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return nil +} + // ToIgn3_3Unvalidated 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 diff --git a/vendor/github.com/coreos/butane/config/fcos/v1_5/translate.go b/vendor/github.com/coreos/butane/config/fcos/v1_5/translate.go index 576c534fe7..473a0db307 100644 --- a/vendor/github.com/coreos/butane/config/fcos/v1_5/translate.go +++ b/vendor/github.com/coreos/butane/config/fcos/v1_5/translate.go @@ -55,6 +55,11 @@ const ( bootV1SizeMiB = 384 ) +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return nil +} + // ToIgn3_4Unvalidated 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 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 index 5a5d02ca0e..2a45287b88 100644 --- 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 @@ -55,6 +55,11 @@ const ( bootV1SizeMiB = 384 ) +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return nil +} + // 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 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 index b37585c465..4c3ae9de45 100644 --- 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 @@ -15,6 +15,8 @@ package v1_6_exp import ( + "regexp" + "github.com/coreos/butane/config/common" "github.com/coreos/ignition/v2/config/util" @@ -22,6 +24,30 @@ import ( "github.com/coreos/vcontext/report" ) +const rootDevice = "/dev/disk/by-id/coreos-boot-disk" + +var allowedMountpoints = regexp.MustCompile(`^/(etc|var)(/|$)`) + +// We can't define a Validate function directly on Disk because that's defined in base, +// so we use a Validate function on the top-level Config instead. +func (conf Config) Validate(c path.ContextPath) (r report.Report) { + for i, disk := range conf.Storage.Disks { + if disk.Device != rootDevice && !util.IsTrue(disk.WipeTable) { + for p, partition := range disk.Partitions { + if partition.Number == 0 && partition.Label != nil { + r.AddOnWarn(c.Append("storage", "disks", i, "partitions", p, "number"), common.ErrReuseByLabel) + } + } + } + } + for i, fs := range conf.Storage.Filesystems { + if fs.Path != nil && !allowedMountpoints.MatchString(*fs.Path) && util.IsTrue(fs.WithMountUnit) { + r.AddOnError(c.Append("storage", "filesystems", i, "path"), common.ErrMountPointForbidden) + } + } + return +} + func (d BootDevice) Validate(c path.ContextPath) (r report.Report) { if d.Layout != nil { switch *d.Layout { 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 f5cd555f6f..a6a7821ec3 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 @@ -17,30 +17,20 @@ package v1_0 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_3/types" - "github.com/coreos/vcontext/path" "github.com/coreos/vcontext/report" ) -// ToIgn3_3Unvalidated 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_3Unvalidated(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { - ret, ts, r := c.Config.ToIgn3_3Unvalidated(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) - } - } +var ( + fieldFilters = cutil.NewFilters(types.Config{}, cutil.FilterMap{ + "storage.luks.clevis": common.ErrClevisSupport, + }) +) - return ret, ts, r +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return &fieldFilters } // ToIgn3_3 translates the config to an Ignition config. It returns a diff --git a/vendor/github.com/coreos/butane/config/flatcar/v1_1/translate.go b/vendor/github.com/coreos/butane/config/flatcar/v1_1/translate.go index 4a8b071eec..8f6f28a039 100644 --- a/vendor/github.com/coreos/butane/config/flatcar/v1_1/translate.go +++ b/vendor/github.com/coreos/butane/config/flatcar/v1_1/translate.go @@ -17,30 +17,20 @@ package v1_1 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_4/types" - "github.com/coreos/vcontext/path" "github.com/coreos/vcontext/report" ) -// ToIgn3_4Unvalidated 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) { - ret, ts, r := c.Config.ToIgn3_4Unvalidated(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) - } - } +var ( + fieldFilters = cutil.NewFilters(types.Config{}, cutil.FilterMap{ + "storage.luks.clevis": common.ErrClevisSupport, + }) +) - return ret, ts, r +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return &fieldFilters } // ToIgn3_4 translates the config to an Ignition config. It returns a 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 index bb8dafcc00..cf95d7241b 100644 --- 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 @@ -17,30 +17,20 @@ 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) - } - } +var ( + fieldFilters = cutil.NewFilters(types.Config{}, cutil.FilterMap{ + "storage.luks.clevis": common.ErrClevisSupport, + }) +) - return ret, ts, r +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return &fieldFilters } // ToIgn3_5 translates the config to an Ignition config. It returns a diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_10/translate.go b/vendor/github.com/coreos/butane/config/openshift/v4_10/translate.go index 722162cbae..5ab6a7e1c7 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_10/translate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_10/translate.go @@ -16,7 +16,6 @@ package v4_10 import ( "net/url" - "reflect" "strings" "github.com/coreos/butane/config/common" @@ -30,6 +29,25 @@ import ( "github.com/coreos/vcontext/report" ) +// Error classes: +// +// 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. + const ( // FIPS 140-2 doesn't allow the default XTS mode fipsCipherOption = types.LuksOption("--cipher") @@ -37,6 +55,55 @@ const ( fipsCipherArgument = types.LuksOption("aes-cbc-essiv:sha256") ) +var ( + // See also validateRHCOSSupport() and validateMCOSupport() + fieldFilters = cutil.NewFilters(result.MachineConfig{}, cutil.FilterMap{ + // IMMUTABLE + "spec.config.passwd.groups": common.ErrGroupSupport, + // TRIPWIRE + "spec.config.passwd.users.gecos": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.groups": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.homeDir": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noCreateHome": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noLogInit": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noUserGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.passwordHash": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.primaryGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shell": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shouldExist": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.system": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.uid": common.ErrUserFieldSupport, + // IMMUTABLE + "spec.config.storage.directories": common.ErrDirectorySupport, + // FORBIDDEN + "spec.config.storage.files.append": common.ErrFileAppendSupport, + // redundant with a check from Ignition validation, but ensures we + // exclude the section from docs + "spec.config.storage.files.contents.httpHeaders": common.ErrFileHeaderSupport, + // 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. + "spec.config.storage.links": common.ErrLinkSupport, + }) +) + +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return &fieldFilters +} + // ToMachineConfig4_10Unvalidated 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 @@ -87,8 +154,8 @@ func (c Config) ToMachineConfig4_10Unvalidated(options common.TranslateOptions) 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)) + r.Merge(validateRHCOSSupport(mc)) + r.Merge(validateMCOSupport(mc)) return mc, ts, r } @@ -114,7 +181,8 @@ func (c Config) ToIgn3_2Unvalidated(options common.TranslateOptions) (types.Conf // 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 + // translate from json space into yaml space, since the caller won't + // have enough info to do it r.Merge(cutil.TranslateReportPaths(warnings, ts)) ts = ts.Descend(path.New("json", "spec", "config")) @@ -177,7 +245,7 @@ OUTER: // 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 { +func validateRHCOSSupport(mc result.MachineConfig) report.Report { var r report.Report for i, fs := range mc.Spec.Config.Storage.Filesystems { if fs.Format != nil && *fs.Format == "btrfs" { @@ -185,7 +253,7 @@ func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) r.AddOnError(path.New("json", "spec", "config", "storage", "filesystems", i, "format"), common.ErrBtrfsSupport) } } - return cutil.TranslateReportPaths(r, ts) + return r } // Error on fields that are rejected outright by the MCO, or that are @@ -197,36 +265,11 @@ func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) // 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. +func validateMCOSupport(mc result.MachineConfig) report.Report { + // See also fieldFilters at the top of this file. 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 @@ -240,40 +283,11 @@ func validateMCOSupport(mc result.MachineConfig, ts translate.TranslationSet) re 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" { - // SSHAuthorizedKeys is 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", "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 { + if user.Name != "core" { // TRIPWIRE - r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i), common.ErrUserNameSupport) + r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i, "name"), common.ErrUserNameSupport) } } - return cutil.TranslateReportPaths(r, ts) + return r } diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_10/validate.go b/vendor/github.com/coreos/butane/config/openshift/v4_10/validate.go index 7fd870e73e..e145103d08 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_10/validate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_10/validate.go @@ -26,7 +26,7 @@ func (m Metadata) Validate(c path.ContextPath) (r report.Report) { r.AddOnError(c.Append("name"), common.ErrNameRequired) } if m.Labels[ROLE_LABEL_KEY] == "" { - r.AddOnError(c.Append("labels", ROLE_LABEL_KEY), common.ErrRoleRequired) + r.AddOnError(c.Append("labels"), common.ErrRoleRequired) } return } diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_11/translate.go b/vendor/github.com/coreos/butane/config/openshift/v4_11/translate.go index b19ad93370..9382e41e88 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_11/translate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_11/translate.go @@ -16,7 +16,6 @@ package v4_11 import ( "net/url" - "reflect" "strings" "github.com/coreos/butane/config/common" @@ -30,6 +29,25 @@ import ( "github.com/coreos/vcontext/report" ) +// Error classes: +// +// 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. + const ( // FIPS 140-2 doesn't allow the default XTS mode fipsCipherOption = types.LuksOption("--cipher") @@ -37,6 +55,55 @@ const ( fipsCipherArgument = types.LuksOption("aes-cbc-essiv:sha256") ) +var ( + // See also validateRHCOSSupport() and validateMCOSupport() + fieldFilters = cutil.NewFilters(result.MachineConfig{}, cutil.FilterMap{ + // IMMUTABLE + "spec.config.passwd.groups": common.ErrGroupSupport, + // TRIPWIRE + "spec.config.passwd.users.gecos": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.groups": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.homeDir": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noCreateHome": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noLogInit": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noUserGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.passwordHash": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.primaryGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shell": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shouldExist": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.system": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.uid": common.ErrUserFieldSupport, + // IMMUTABLE + "spec.config.storage.directories": common.ErrDirectorySupport, + // FORBIDDEN + "spec.config.storage.files.append": common.ErrFileAppendSupport, + // redundant with a check from Ignition validation, but ensures we + // exclude the section from docs + "spec.config.storage.files.contents.httpHeaders": common.ErrFileHeaderSupport, + // 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. + "spec.config.storage.links": common.ErrLinkSupport, + }) +) + +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return &fieldFilters +} + // ToMachineConfig4_11Unvalidated 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 @@ -87,8 +154,8 @@ func (c Config) ToMachineConfig4_11Unvalidated(options common.TranslateOptions) 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)) + r.Merge(validateRHCOSSupport(mc)) + r.Merge(validateMCOSupport(mc)) return mc, ts, r } @@ -114,7 +181,8 @@ func (c Config) ToIgn3_2Unvalidated(options common.TranslateOptions) (types.Conf // 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 + // translate from json space into yaml space, since the caller won't + // have enough info to do it r.Merge(cutil.TranslateReportPaths(warnings, ts)) ts = ts.Descend(path.New("json", "spec", "config")) @@ -177,7 +245,7 @@ OUTER: // 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 { +func validateRHCOSSupport(mc result.MachineConfig) report.Report { var r report.Report for i, fs := range mc.Spec.Config.Storage.Filesystems { if fs.Format != nil && *fs.Format == "btrfs" { @@ -185,7 +253,7 @@ func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) r.AddOnError(path.New("json", "spec", "config", "storage", "filesystems", i, "format"), common.ErrBtrfsSupport) } } - return cutil.TranslateReportPaths(r, ts) + return r } // Error on fields that are rejected outright by the MCO, or that are @@ -197,36 +265,11 @@ func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) // 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. +func validateMCOSupport(mc result.MachineConfig) report.Report { + // See also fieldFilters at the top of this file. 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 @@ -240,40 +283,11 @@ func validateMCOSupport(mc result.MachineConfig, ts translate.TranslationSet) re 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" { - // SSHAuthorizedKeys is 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", "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 { + if user.Name != "core" { // TRIPWIRE - r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i), common.ErrUserNameSupport) + r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i, "name"), common.ErrUserNameSupport) } } - return cutil.TranslateReportPaths(r, ts) + return r } diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_11/validate.go b/vendor/github.com/coreos/butane/config/openshift/v4_11/validate.go index dd827c2cda..3e78041dbc 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_11/validate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_11/validate.go @@ -26,7 +26,7 @@ func (m Metadata) Validate(c path.ContextPath) (r report.Report) { r.AddOnError(c.Append("name"), common.ErrNameRequired) } if m.Labels[ROLE_LABEL_KEY] == "" { - r.AddOnError(c.Append("labels", ROLE_LABEL_KEY), common.ErrRoleRequired) + r.AddOnError(c.Append("labels"), common.ErrRoleRequired) } return } diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_12/translate.go b/vendor/github.com/coreos/butane/config/openshift/v4_12/translate.go index 4cb433fa77..2687288d80 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_12/translate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_12/translate.go @@ -16,7 +16,6 @@ package v4_12 import ( "net/url" - "reflect" "strings" "github.com/coreos/butane/config/common" @@ -30,6 +29,25 @@ import ( "github.com/coreos/vcontext/report" ) +// Error classes: +// +// 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. + const ( // FIPS 140-2 doesn't allow the default XTS mode fipsCipherOption = types.LuksOption("--cipher") @@ -37,6 +55,55 @@ const ( fipsCipherArgument = types.LuksOption("aes-cbc-essiv:sha256") ) +var ( + // See also validateRHCOSSupport() and validateMCOSupport() + fieldFilters = cutil.NewFilters(result.MachineConfig{}, cutil.FilterMap{ + // IMMUTABLE + "spec.config.passwd.groups": common.ErrGroupSupport, + // TRIPWIRE + "spec.config.passwd.users.gecos": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.groups": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.homeDir": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noCreateHome": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noLogInit": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noUserGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.passwordHash": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.primaryGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shell": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shouldExist": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.system": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.uid": common.ErrUserFieldSupport, + // IMMUTABLE + "spec.config.storage.directories": common.ErrDirectorySupport, + // FORBIDDEN + "spec.config.storage.files.append": common.ErrFileAppendSupport, + // redundant with a check from Ignition validation, but ensures we + // exclude the section from docs + "spec.config.storage.files.contents.httpHeaders": common.ErrFileHeaderSupport, + // 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. + "spec.config.storage.links": common.ErrLinkSupport, + }) +) + +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return &fieldFilters +} + // ToMachineConfig4_12Unvalidated 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 @@ -87,8 +154,8 @@ func (c Config) ToMachineConfig4_12Unvalidated(options common.TranslateOptions) 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)) + r.Merge(validateRHCOSSupport(mc)) + r.Merge(validateMCOSupport(mc)) return mc, ts, r } @@ -114,7 +181,8 @@ func (c Config) ToIgn3_2Unvalidated(options common.TranslateOptions) (types.Conf // 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 + // translate from json space into yaml space, since the caller won't + // have enough info to do it r.Merge(cutil.TranslateReportPaths(warnings, ts)) ts = ts.Descend(path.New("json", "spec", "config")) @@ -177,7 +245,7 @@ OUTER: // 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 { +func validateRHCOSSupport(mc result.MachineConfig) report.Report { var r report.Report for i, fs := range mc.Spec.Config.Storage.Filesystems { if fs.Format != nil && *fs.Format == "btrfs" { @@ -185,7 +253,7 @@ func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) r.AddOnError(path.New("json", "spec", "config", "storage", "filesystems", i, "format"), common.ErrBtrfsSupport) } } - return cutil.TranslateReportPaths(r, ts) + return r } // Error on fields that are rejected outright by the MCO, or that are @@ -197,36 +265,11 @@ func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) // 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. +func validateMCOSupport(mc result.MachineConfig) report.Report { + // See also fieldFilters at the top of this file. 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 @@ -240,40 +283,11 @@ func validateMCOSupport(mc result.MachineConfig, ts translate.TranslationSet) re 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" { - // SSHAuthorizedKeys is 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", "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 { + if user.Name != "core" { // TRIPWIRE - r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i), common.ErrUserNameSupport) + r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i, "name"), common.ErrUserNameSupport) } } - return cutil.TranslateReportPaths(r, ts) + return r } diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_12/validate.go b/vendor/github.com/coreos/butane/config/openshift/v4_12/validate.go index ff1403cb4a..48205bb96d 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_12/validate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_12/validate.go @@ -26,7 +26,7 @@ func (m Metadata) Validate(c path.ContextPath) (r report.Report) { r.AddOnError(c.Append("name"), common.ErrNameRequired) } if m.Labels[ROLE_LABEL_KEY] == "" { - r.AddOnError(c.Append("labels", ROLE_LABEL_KEY), common.ErrRoleRequired) + r.AddOnError(c.Append("labels"), common.ErrRoleRequired) } return } 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 index 9f1404f6da..482ea0d733 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_13/translate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_13/translate.go @@ -16,7 +16,6 @@ package v4_13 import ( "net/url" - "reflect" "strings" "github.com/coreos/butane/config/common" @@ -30,6 +29,25 @@ import ( "github.com/coreos/vcontext/report" ) +// Error classes: +// +// 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. + const ( // FIPS 140-2 doesn't allow the default XTS mode fipsCipherOption = types.LuksOption("--cipher") @@ -37,6 +55,53 @@ const ( fipsCipherArgument = types.LuksOption("aes-cbc-essiv:sha256") ) +var ( + // See also validateRHCOSSupport() and validateMCOSupport() + fieldFilters = cutil.NewFilters(result.MachineConfig{}, cutil.FilterMap{ + // IMMUTABLE + "spec.config.passwd.groups": common.ErrGroupSupport, + // TRIPWIRE + "spec.config.passwd.users.gecos": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.groups": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.homeDir": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noCreateHome": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noLogInit": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noUserGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.primaryGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shell": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shouldExist": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.system": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.uid": common.ErrUserFieldSupport, + // IMMUTABLE + "spec.config.storage.directories": common.ErrDirectorySupport, + // FORBIDDEN + "spec.config.storage.files.append": common.ErrFileAppendSupport, + // redundant with a check from Ignition validation, but ensures we + // exclude the section from docs + "spec.config.storage.files.contents.httpHeaders": common.ErrFileHeaderSupport, + // 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. + "spec.config.storage.links": common.ErrLinkSupport, + }) +) + +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return &fieldFilters +} + // 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 @@ -87,8 +152,8 @@ func (c Config) ToMachineConfig4_13Unvalidated(options common.TranslateOptions) 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)) + r.Merge(validateRHCOSSupport(mc)) + r.Merge(validateMCOSupport(mc)) return mc, ts, r } @@ -114,7 +179,8 @@ func (c Config) ToIgn3_2Unvalidated(options common.TranslateOptions) (types.Conf // 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 + // translate from json space into yaml space, since the caller won't + // have enough info to do it r.Merge(cutil.TranslateReportPaths(warnings, ts)) ts = ts.Descend(path.New("json", "spec", "config")) @@ -177,7 +243,7 @@ OUTER: // 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 { +func validateRHCOSSupport(mc result.MachineConfig) report.Report { var r report.Report for i, fs := range mc.Spec.Config.Storage.Filesystems { if fs.Format != nil && *fs.Format == "btrfs" { @@ -185,7 +251,7 @@ func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) r.AddOnError(path.New("json", "spec", "config", "storage", "filesystems", i, "format"), common.ErrBtrfsSupport) } } - return cutil.TranslateReportPaths(r, ts) + return r } // Error on fields that are rejected outright by the MCO, or that are @@ -197,36 +263,11 @@ func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) // 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. +func validateMCOSupport(mc result.MachineConfig) report.Report { + // See also fieldFilters at the top of this file. 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 @@ -240,40 +281,11 @@ func validateMCOSupport(mc result.MachineConfig, ts translate.TranslationSet) re 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 { + if user.Name != "core" { // TRIPWIRE - r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i), common.ErrUserNameSupport) + r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i, "name"), common.ErrUserNameSupport) } } - return cutil.TranslateReportPaths(r, ts) + return r } diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_13/validate.go b/vendor/github.com/coreos/butane/config/openshift/v4_13/validate.go index d1e932d6f8..ba41834f39 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_13/validate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_13/validate.go @@ -26,7 +26,7 @@ func (m Metadata) Validate(c path.ContextPath) (r report.Report) { r.AddOnError(c.Append("name"), common.ErrNameRequired) } if m.Labels[ROLE_LABEL_KEY] == "" { - r.AddOnError(c.Append("labels", ROLE_LABEL_KEY), common.ErrRoleRequired) + r.AddOnError(c.Append("labels"), common.ErrRoleRequired) } return } diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_14/result/schema.go b/vendor/github.com/coreos/butane/config/openshift/v4_14/result/schema.go new file mode 100644 index 0000000000..ad5abd8ee2 --- /dev/null +++ b/vendor/github.com/coreos/butane/config/openshift/v4_14/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_4/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/schema.go b/vendor/github.com/coreos/butane/config/openshift/v4_14/schema.go new file mode 100644 index 0000000000..4ef6cf2047 --- /dev/null +++ b/vendor/github.com/coreos/butane/config/openshift/v4_14/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 + +import ( + fcos "github.com/coreos/butane/config/fcos/v1_5" +) + +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_14/translate.go b/vendor/github.com/coreos/butane/config/openshift/v4_14/translate.go new file mode 100644 index 0000000000..ae7e36f435 --- /dev/null +++ b/vendor/github.com/coreos/butane/config/openshift/v4_14/translate.go @@ -0,0 +1,303 @@ +// 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 + +import ( + "net/url" + "strings" + + "github.com/coreos/butane/config/common" + "github.com/coreos/butane/config/openshift/v4_14/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/vcontext/path" + "github.com/coreos/vcontext/report" +) + +// Error classes: +// +// 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. +// +// REDUNDANT - Feature is also provided by a MachineConfig-specific field +// with different semantics. To reduce confusion, disable this +// implementation. +// +// 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. + +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") +) + +var ( + // See also validateRHCOSSupport() and validateMCOSupport() + fieldFilters = cutil.NewFilters(result.MachineConfig{}, cutil.FilterMap{ + // UNPARSABLE, REDUNDANT + "spec.config.kernelArguments": common.ErrKernelArgumentSupport, + // IMMUTABLE + "spec.config.passwd.groups": common.ErrGroupSupport, + // TRIPWIRE + "spec.config.passwd.users.gecos": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.groups": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.homeDir": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noCreateHome": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noLogInit": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noUserGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.primaryGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shell": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shouldExist": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.system": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.uid": common.ErrUserFieldSupport, + // IMMUTABLE + "spec.config.storage.directories": common.ErrDirectorySupport, + // FORBIDDEN + "spec.config.storage.files.append": common.ErrFileAppendSupport, + // redundant with a check from Ignition validation, but ensures we + // exclude the section from docs + "spec.config.storage.files.contents.httpHeaders": common.ErrFileHeaderSupport, + // 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. + "spec.config.storage.links": common.ErrLinkSupport, + }) +) + +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return &fieldFilters +} + +// 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_14Unvalidated(options common.TranslateOptions) (result.MachineConfig, translate.TranslationSet, report.Report) { + cfg, ts, r := c.Config.ToIgn3_4Unvalidated(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)) + r.Merge(validateMCOSupport(mc)) + + return mc, ts, r +} + +// 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_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 +// 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_14Unvalidated(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, since the caller won't + // have enough info to do it + r.Merge(cutil.TranslateReportPaths(warnings, ts)) + + ts = ts.Descend(path.New("json", "spec", "config")) + return cfg, ts, r +} + +// ToIgn3_4 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) + return cfg.(types.Config), r, err +} + +// 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) + } else { + return cutil.TranslateBytesYAML(input, &Config{}, "ToMachineConfig4_14", 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) 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 r +} + +// 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) report.Report { + // See also fieldFilters at the top of this file. + + var r report.Report + for i, fs := range mc.Spec.Config.Storage.Filesystems { + if fs.Format != nil && *fs.Format == "none" { + // UNPARSABLE + r.AddOnError(path.New("json", "spec", "config", "storage", "filesystems", i, "format"), common.ErrFilesystemNoneSupport) + } + } + for i, file := range mc.Spec.Config.Storage.Files { + 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, user := range mc.Spec.Config.Passwd.Users { + if user.Name != "core" { + // TRIPWIRE + r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i, "name"), common.ErrUserNameSupport) + } + } + return r +} 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/validate.go similarity index 92% rename from vendor/github.com/coreos/butane/config/openshift/v4_14_exp/validate.go rename to vendor/github.com/coreos/butane/config/openshift/v4_14/validate.go index 09cc167335..d1becc02f1 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/validate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_14/validate.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v4_14_exp +package v4_14 import ( "github.com/coreos/butane/config/common" @@ -26,7 +26,7 @@ func (m Metadata) Validate(c path.ContextPath) (r report.Report) { r.AddOnError(c.Append("name"), common.ErrNameRequired) } if m.Labels[ROLE_LABEL_KEY] == "" { - r.AddOnError(c.Append("labels", ROLE_LABEL_KEY), common.ErrRoleRequired) + r.AddOnError(c.Append("labels"), common.ErrRoleRequired) } return } 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_15_exp/result/schema.go similarity index 100% rename from vendor/github.com/coreos/butane/config/openshift/v4_14_exp/result/schema.go rename to vendor/github.com/coreos/butane/config/openshift/v4_15_exp/result/schema.go diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/schema.go b/vendor/github.com/coreos/butane/config/openshift/v4_15_exp/schema.go similarity index 98% rename from vendor/github.com/coreos/butane/config/openshift/v4_14_exp/schema.go rename to vendor/github.com/coreos/butane/config/openshift/v4_15_exp/schema.go index 16d45ab4c5..5ed101e3b0 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/schema.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_15_exp/schema.go @@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v4_14_exp +package v4_15_exp import ( fcos "github.com/coreos/butane/config/fcos/v1_6_exp" diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/translate.go b/vendor/github.com/coreos/butane/config/openshift/v4_15_exp/translate.go similarity index 72% rename from vendor/github.com/coreos/butane/config/openshift/v4_14_exp/translate.go rename to vendor/github.com/coreos/butane/config/openshift/v4_15_exp/translate.go index b8780ddca6..8ba7be9d9e 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_14_exp/translate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_15_exp/translate.go @@ -12,15 +12,14 @@ // See the License for the specific language governing permissions and // limitations under the License.) -package v4_14_exp +package v4_15_exp import ( "net/url" - "reflect" "strings" "github.com/coreos/butane/config/common" - "github.com/coreos/butane/config/openshift/v4_14_exp/result" + "github.com/coreos/butane/config/openshift/v4_15_exp/result" cutil "github.com/coreos/butane/config/util" "github.com/coreos/butane/translate" @@ -30,6 +29,29 @@ import ( "github.com/coreos/vcontext/report" ) +// Error classes: +// +// 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. +// +// REDUNDANT - Feature is also provided by a MachineConfig-specific field +// with different semantics. To reduce confusion, disable this +// implementation. +// +// 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. + const ( // FIPS 140-2 doesn't allow the default XTS mode fipsCipherOption = types.LuksOption("--cipher") @@ -37,11 +59,60 @@ const ( fipsCipherArgument = types.LuksOption("aes-cbc-essiv:sha256") ) -// ToMachineConfig4_14Unvalidated translates the config to a MachineConfig. It also +var ( + // See also validateRHCOSSupport() and validateMCOSupport() + fieldFilters = cutil.NewFilters(result.MachineConfig{}, cutil.FilterMap{ + // UNPARSABLE, REDUNDANT + "spec.config.kernelArguments": common.ErrKernelArgumentSupport, + // IMMUTABLE + "spec.config.passwd.groups": common.ErrGroupSupport, + // TRIPWIRE + "spec.config.passwd.users.gecos": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.groups": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.homeDir": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noCreateHome": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noLogInit": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noUserGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.primaryGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shell": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shouldExist": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.system": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.uid": common.ErrUserFieldSupport, + // IMMUTABLE + "spec.config.storage.directories": common.ErrDirectorySupport, + // FORBIDDEN + "spec.config.storage.files.append": common.ErrFileAppendSupport, + // redundant with a check from Ignition validation, but ensures we + // exclude the section from docs + "spec.config.storage.files.contents.httpHeaders": common.ErrFileHeaderSupport, + // 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. + "spec.config.storage.links": common.ErrLinkSupport, + }) +) + +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return &fieldFilters +} + +// ToMachineConfig4_15Unvalidated 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_14Unvalidated(options common.TranslateOptions) (result.MachineConfig, translate.TranslationSet, report.Report) { +func (c Config) ToMachineConfig4_15Unvalidated(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 @@ -88,18 +159,18 @@ func (c Config) ToMachineConfig4_14Unvalidated(options common.TranslateOptions) 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)) + r.Merge(validateRHCOSSupport(mc)) + r.Merge(validateMCOSupport(mc)) return mc, ts, r } -// ToMachineConfig4_14 translates the config to a MachineConfig. It returns a +// ToMachineConfig4_15 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_14(options common.TranslateOptions) (result.MachineConfig, report.Report, error) { - cfg, r, err := cutil.Translate(c, "ToMachineConfig4_14Unvalidated", options) +func (c Config) ToMachineConfig4_15(options common.TranslateOptions) (result.MachineConfig, report.Report, error) { + cfg, r, err := cutil.Translate(c, "ToMachineConfig4_15Unvalidated", options) return cfg.(result.MachineConfig), r, err } @@ -108,14 +179,15 @@ func (c Config) ToMachineConfig4_14(options common.TranslateOptions) (result.Mac // 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) { - mc, ts, r := c.ToMachineConfig4_14Unvalidated(options) + mc, ts, r := c.ToMachineConfig4_15Unvalidated(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 + // translate from json space into yaml space, since the caller won't + // have enough info to do it r.Merge(cutil.TranslateReportPaths(warnings, ts)) ts = ts.Descend(path.New("json", "spec", "config")) @@ -131,14 +203,14 @@ func (c Config) ToIgn3_5(options common.TranslateOptions) (types.Config, report. return cfg.(types.Config), r, err } -// 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 +// ToConfigBytes translates from a v4.15 Butane config to a v4.15 MachineConfig or 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 ToConfigBytes(input []byte, options common.TranslateBytesOptions) ([]byte, report.Report, error) { if options.Raw { return cutil.TranslateBytes(input, &Config{}, "ToIgn3_5", options) } else { - return cutil.TranslateBytesYAML(input, &Config{}, "ToMachineConfig4_14", options) + return cutil.TranslateBytesYAML(input, &Config{}, "ToMachineConfig4_15", options) } } @@ -178,7 +250,7 @@ OUTER: // 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 { +func validateRHCOSSupport(mc result.MachineConfig) report.Report { var r report.Report for i, fs := range mc.Spec.Config.Storage.Filesystems { if fs.Format != nil && *fs.Format == "btrfs" { @@ -186,7 +258,7 @@ func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) r.AddOnError(path.New("json", "spec", "config", "storage", "filesystems", i, "format"), common.ErrBtrfsSupport) } } - return cutil.TranslateReportPaths(r, ts) + return r } // Error on fields that are rejected outright by the MCO, or that are @@ -198,29 +270,8 @@ func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) // 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. - // - // REDUNDANT - Feature is also provided by a MachineConfig-specific - // field with different semantics. To reduce confusion, disable - // this implementation. - // - // 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. +func validateMCOSupport(mc result.MachineConfig) report.Report { + // See also fieldFilters at the top of this file. var r report.Report for i, fs := range mc.Spec.Config.Storage.Filesystems { @@ -229,15 +280,7 @@ func validateMCOSupport(mc result.MachineConfig, ts translate.TranslationSet) re r.AddOnError(path.New("json", "spec", "config", "storage", "filesystems", i, "format"), common.ErrFilesystemNoneSupport) } } - 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 @@ -251,50 +294,13 @@ func validateMCOSupport(mc result.MachineConfig, ts translate.TranslationSet) re 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 { + if user.Name != "core" { // TRIPWIRE - r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i), common.ErrUserNameSupport) + r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i, "name"), common.ErrUserNameSupport) } } - for i := range mc.Spec.Config.KernelArguments.ShouldExist { - // UNPARSABLE, REDUNDANT - r.AddOnError(path.New("json", "spec", "config", "kernelArguments", "shouldExist", i), common.ErrKernelArgumentSupport) - } - for i := range mc.Spec.Config.KernelArguments.ShouldNotExist { - // UNPARSABLE, REDUNDANT - r.AddOnError(path.New("json", "spec", "config", "kernelArguments", "shouldNotExist", i), common.ErrKernelArgumentSupport) - } - return cutil.TranslateReportPaths(r, ts) + return r } // fcos config generates a user.cfg file using append; however, OpenShift config diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_15_exp/validate.go b/vendor/github.com/coreos/butane/config/openshift/v4_15_exp/validate.go new file mode 100644 index 0000000000..715797d3e6 --- /dev/null +++ b/vendor/github.com/coreos/butane/config/openshift/v4_15_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_15_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"), 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/openshift/v4_8/translate.go b/vendor/github.com/coreos/butane/config/openshift/v4_8/translate.go index ac2a2c4cd5..6739fe94bf 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_8/translate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_8/translate.go @@ -16,7 +16,6 @@ package v4_8 import ( "net/url" - "reflect" "strings" "github.com/coreos/butane/config/common" @@ -30,6 +29,29 @@ import ( "github.com/coreos/vcontext/report" ) +// Error classes: +// +// 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. +// +// BUGGED - Ignored by the MCD but not by Ignition. Ignition correctly +// applies the setting, but the MCD doesn't, and writes incorrect state to +// the node. + const ( // FIPS 140-2 doesn't allow the default XTS mode fipsCipherOption = types.LuksOption("--cipher") @@ -37,6 +59,60 @@ const ( fipsCipherArgument = types.LuksOption("aes-cbc-essiv:sha256") ) +var ( + // See also validateRHCOSSupport() and validateMCOSupport() + fieldFilters = cutil.NewFiltersIgnoreZero(result.MachineConfig{}, cutil.FilterMap{ + // IMMUTABLE + "spec.config.passwd.groups": common.ErrGroupSupport, + // TRIPWIRE + "spec.config.passwd.users.gecos": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.groups": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.homeDir": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noCreateHome": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noLogInit": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noUserGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.passwordHash": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.primaryGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shell": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shouldExist": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.system": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.uid": common.ErrUserFieldSupport, + // IMMUTABLE + "spec.config.storage.directories": common.ErrDirectorySupport, + // FORBIDDEN + "spec.config.storage.files.append": common.ErrFileAppendSupport, + // BUGGED + // https://bugzilla.redhat.com/show_bug.cgi?id=1970218 + // We ignoreZero because desugaring inline/local can + // produce StrToPtr("") which is harmless. + "spec.config.storage.files.contents.compression": common.ErrFileCompressionSupport, + // redundant with a check from Ignition validation, but ensures we + // exclude the section from docs + "spec.config.storage.files.contents.httpHeaders": common.ErrFileHeaderSupport, + // 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. + "spec.config.storage.links": common.ErrLinkSupport, + }, []string{"spec.config.storage.files.contents.compression"}) +) + +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return &fieldFilters +} + // ToMachineConfig4_8Unvalidated 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 @@ -91,8 +167,8 @@ func (c Config) ToMachineConfig4_8Unvalidated(options common.TranslateOptions) ( 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)) + r.Merge(validateRHCOSSupport(mc)) + r.Merge(validateMCOSupport(mc)) return mc, ts, r } @@ -118,7 +194,8 @@ func (c Config) ToIgn3_2Unvalidated(options common.TranslateOptions) (types.Conf // 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 + // translate from json space into yaml space, since the caller won't + // have enough info to do it r.Merge(cutil.TranslateReportPaths(warnings, ts)) ts = ts.Descend(path.New("json", "spec", "config")) @@ -181,7 +258,7 @@ OUTER: // 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 { +func validateRHCOSSupport(mc result.MachineConfig) report.Report { var r report.Report for i, fs := range mc.Spec.Config.Storage.Filesystems { if fs.Format != nil && *fs.Format == "btrfs" { @@ -189,7 +266,7 @@ func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) r.AddOnError(path.New("json", "spec", "config", "storage", "filesystems", i, "format"), common.ErrBtrfsSupport) } } - return cutil.TranslateReportPaths(r, ts) + return r } // Error on fields that are rejected outright by the MCO, or that are @@ -201,42 +278,11 @@ func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) // 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: - // - // 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. - // - // BUGGED - Ignored by the MCD but not by Ignition. Ignition - // correctly applies the setting, but the MCD doesn't, and writes - // incorrect state to the node. +func validateMCOSupport(mc result.MachineConfig) report.Report { + // See also fieldFilters at the top of this file. 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 util.NotEmpty(file.Contents.Compression) { - // BUGGED - // https://bugzilla.redhat.com/show_bug.cgi?id=1970218 - r.AddOnError(path.New("json", "spec", "config", "storage", "files", i, "contents", "compression"), common.ErrFileCompressionSupport) - } if file.Contents.Source != nil { fileSource, err := url.Parse(*file.Contents.Source) // parse errors will be caught by normal config validation @@ -246,40 +292,11 @@ func validateMCOSupport(mc result.MachineConfig, ts translate.TranslationSet) re } } } - 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" { - // SSHAuthorizedKeys is 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", "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 { + if user.Name != "core" { // TRIPWIRE - r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i), common.ErrUserNameSupport) + r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i, "name"), common.ErrUserNameSupport) } } - return cutil.TranslateReportPaths(r, ts) + return r } diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_8/validate.go b/vendor/github.com/coreos/butane/config/openshift/v4_8/validate.go index 3e04825402..bd6f890cba 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_8/validate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_8/validate.go @@ -26,7 +26,7 @@ func (m Metadata) Validate(c path.ContextPath) (r report.Report) { r.AddOnError(c.Append("name"), common.ErrNameRequired) } if m.Labels[ROLE_LABEL_KEY] == "" { - r.AddOnError(c.Append("labels", ROLE_LABEL_KEY), common.ErrRoleRequired) + r.AddOnError(c.Append("labels"), common.ErrRoleRequired) } return } diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_9/translate.go b/vendor/github.com/coreos/butane/config/openshift/v4_9/translate.go index 8dd1cac055..9f2a8eb785 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_9/translate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_9/translate.go @@ -16,7 +16,6 @@ package v4_9 import ( "net/url" - "reflect" "strings" "github.com/coreos/butane/config/common" @@ -30,6 +29,29 @@ import ( "github.com/coreos/vcontext/report" ) +// Error classes: +// +// 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. +// +// BUGGED - Ignored by the MCD but not by Ignition. Ignition correctly +// applies the setting, but the MCD doesn't, and writes incorrect state to +// the node. + const ( // FIPS 140-2 doesn't allow the default XTS mode fipsCipherOption = types.LuksOption("--cipher") @@ -37,6 +59,60 @@ const ( fipsCipherArgument = types.LuksOption("aes-cbc-essiv:sha256") ) +var ( + // See also validateRHCOSSupport() and validateMCOSupport() + fieldFilters = cutil.NewFiltersIgnoreZero(result.MachineConfig{}, cutil.FilterMap{ + // IMMUTABLE + "spec.config.passwd.groups": common.ErrGroupSupport, + // TRIPWIRE + "spec.config.passwd.users.gecos": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.groups": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.homeDir": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noCreateHome": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noLogInit": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.noUserGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.passwordHash": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.primaryGroup": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shell": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.shouldExist": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.system": common.ErrUserFieldSupport, + // TRIPWIRE + "spec.config.passwd.users.uid": common.ErrUserFieldSupport, + // IMMUTABLE + "spec.config.storage.directories": common.ErrDirectorySupport, + // FORBIDDEN + "spec.config.storage.files.append": common.ErrFileAppendSupport, + // BUGGED + // https://bugzilla.redhat.com/show_bug.cgi?id=1970218 + // We ignoreZero because desugaring inline/local can + // produce StrToPtr("") which is harmless. + "spec.config.storage.files.contents.compression": common.ErrFileCompressionSupport, + // redundant with a check from Ignition validation, but ensures we + // exclude the section from docs + "spec.config.storage.files.contents.httpHeaders": common.ErrFileHeaderSupport, + // 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. + "spec.config.storage.links": common.ErrLinkSupport, + }, []string{"spec.config.storage.files.contents.compression"}) +) + +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return &fieldFilters +} + // ToMachineConfig4_9Unvalidated 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 @@ -91,8 +167,8 @@ func (c Config) ToMachineConfig4_9Unvalidated(options common.TranslateOptions) ( 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)) + r.Merge(validateRHCOSSupport(mc)) + r.Merge(validateMCOSupport(mc)) return mc, ts, r } @@ -118,7 +194,8 @@ func (c Config) ToIgn3_2Unvalidated(options common.TranslateOptions) (types.Conf // 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 + // translate from json space into yaml space, since the caller won't + // have enough info to do it r.Merge(cutil.TranslateReportPaths(warnings, ts)) ts = ts.Descend(path.New("json", "spec", "config")) @@ -181,7 +258,7 @@ OUTER: // 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 { +func validateRHCOSSupport(mc result.MachineConfig) report.Report { var r report.Report for i, fs := range mc.Spec.Config.Storage.Filesystems { if fs.Format != nil && *fs.Format == "btrfs" { @@ -189,7 +266,7 @@ func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) r.AddOnError(path.New("json", "spec", "config", "storage", "filesystems", i, "format"), common.ErrBtrfsSupport) } } - return cutil.TranslateReportPaths(r, ts) + return r } // Error on fields that are rejected outright by the MCO, or that are @@ -201,42 +278,11 @@ func validateRHCOSSupport(mc result.MachineConfig, ts translate.TranslationSet) // 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: - // - // 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. - // - // BUGGED - Ignored by the MCD but not by Ignition. Ignition - // correctly applies the setting, but the MCD doesn't, and writes - // incorrect state to the node. +func validateMCOSupport(mc result.MachineConfig) report.Report { + // See also fieldFilters at the top of this file. 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 util.NotEmpty(file.Contents.Compression) { - // BUGGED - // https://bugzilla.redhat.com/show_bug.cgi?id=1970218 - r.AddOnError(path.New("json", "spec", "config", "storage", "files", i, "contents", "compression"), common.ErrFileCompressionSupport) - } if file.Contents.Source != nil { fileSource, err := url.Parse(*file.Contents.Source) // parse errors will be caught by normal config validation @@ -246,40 +292,11 @@ func validateMCOSupport(mc result.MachineConfig, ts translate.TranslationSet) re } } } - 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" { - // SSHAuthorizedKeys is 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", "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 { + if user.Name != "core" { // TRIPWIRE - r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i), common.ErrUserNameSupport) + r.AddOnError(path.New("json", "spec", "config", "passwd", "users", i, "name"), common.ErrUserNameSupport) } } - return cutil.TranslateReportPaths(r, ts) + return r } diff --git a/vendor/github.com/coreos/butane/config/openshift/v4_9/validate.go b/vendor/github.com/coreos/butane/config/openshift/v4_9/validate.go index 8322e0542a..8d3059b1b0 100644 --- a/vendor/github.com/coreos/butane/config/openshift/v4_9/validate.go +++ b/vendor/github.com/coreos/butane/config/openshift/v4_9/validate.go @@ -26,7 +26,7 @@ func (m Metadata) Validate(c path.ContextPath) (r report.Report) { r.AddOnError(c.Append("name"), common.ErrNameRequired) } if m.Labels[ROLE_LABEL_KEY] == "" { - r.AddOnError(c.Append("labels", ROLE_LABEL_KEY), common.ErrRoleRequired) + r.AddOnError(c.Append("labels"), common.ErrRoleRequired) } return } diff --git a/vendor/github.com/coreos/butane/config/r4e/v1_0/translate.go b/vendor/github.com/coreos/butane/config/r4e/v1_0/translate.go index c5475ecc98..374ec88c61 100644 --- a/vendor/github.com/coreos/butane/config/r4e/v1_0/translate.go +++ b/vendor/github.com/coreos/butane/config/r4e/v1_0/translate.go @@ -17,49 +17,24 @@ package v1_0 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_3/types" - "github.com/coreos/vcontext/path" "github.com/coreos/vcontext/report" ) -// ToIgn3_3Unvalidated 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_3Unvalidated(options common.TranslateOptions) (types.Config, translate.TranslationSet, report.Report) { - ret, ts, r := c.Config.ToIgn3_3Unvalidated(options) - if r.IsFatal() { - return types.Config{}, translate.TranslationSet{}, r - } - - checkForForbiddenFields(ret, &r) - - return ret, ts, r -} +var ( + fieldFilters = cutil.NewFilters(types.Config{}, cutil.FilterMap{ + "kernelArguments": common.ErrGeneralKernelArgumentSupport, + "storage.disks": common.ErrDiskSupport, + "storage.filesystems": common.ErrFilesystemSupport, + "storage.luks": common.ErrLuksSupport, + "storage.raid": common.ErrRaidSupport, + }) +) -// 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) - } +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return &fieldFilters } // ToIgn3_3 translates the config to an Ignition config. It returns a diff --git a/vendor/github.com/coreos/butane/config/r4e/v1_1/translate.go b/vendor/github.com/coreos/butane/config/r4e/v1_1/translate.go index fb855e15e9..68e46113f7 100644 --- a/vendor/github.com/coreos/butane/config/r4e/v1_1/translate.go +++ b/vendor/github.com/coreos/butane/config/r4e/v1_1/translate.go @@ -17,48 +17,24 @@ package v1_1 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_4/types" - "github.com/coreos/vcontext/path" "github.com/coreos/vcontext/report" ) -// ToIgn3_4Unvalidated 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) { - ret, ts, r := c.Config.ToIgn3_4Unvalidated(options) - if r.IsFatal() { - return types.Config{}, translate.TranslationSet{}, r - } - - checkForForbiddenFields(ret, &r) - - return ret, ts, r -} +var ( + fieldFilters = cutil.NewFilters(types.Config{}, cutil.FilterMap{ + "kernelArguments": common.ErrGeneralKernelArgumentSupport, + "storage.disks": common.ErrDiskSupport, + "storage.filesystems": common.ErrFilesystemSupport, + "storage.luks": common.ErrLuksSupport, + "storage.raid": common.ErrRaidSupport, + }) +) -// 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) - } +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return &fieldFilters } // ToIgn3_4 translates the config to an Ignition config. It returns a 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 index 228150c349..1ca3ca7978 100644 --- 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 @@ -17,48 +17,24 @@ 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 -} +var ( + fieldFilters = cutil.NewFilters(types.Config{}, cutil.FilterMap{ + "kernelArguments": common.ErrGeneralKernelArgumentSupport, + "storage.disks": common.ErrDiskSupport, + "storage.filesystems": common.ErrFilesystemSupport, + "storage.luks": common.ErrLuksSupport, + "storage.raid": common.ErrRaidSupport, + }) +) -// 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) - } +// Return FieldFilters for this spec. +func (c Config) FieldFilters() *cutil.FieldFilters { + return &fieldFilters } // ToIgn3_5 translates the config to an Ignition config. It returns a diff --git a/vendor/github.com/coreos/butane/config/util/filter.go b/vendor/github.com/coreos/butane/config/util/filter.go new file mode 100644 index 0000000000..fe3ced7812 --- /dev/null +++ b/vendor/github.com/coreos/butane/config/util/filter.go @@ -0,0 +1,186 @@ +// Copyright 2023 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 util + +import ( + "fmt" + "reflect" + "strings" + + "github.com/coreos/ignition/v2/config/util" + "github.com/coreos/vcontext/path" + "github.com/coreos/vcontext/report" +) + +type FilterMap map[string]error + +type FieldFilters struct { + filters FilterMap + // openshift 4.8 and 4.9 specs want to filter out the compression + // field but ignore a pointer to a zero value (StrToPtr("")) + // because those are generated automatically by desugaring. Provide + // a way to do that. + ignoreZero map[string]struct{} +} + +func NewFilters(v any, filters FilterMap) FieldFilters { + return NewFiltersIgnoreZero(v, filters, []string{}) +} + +func NewFiltersIgnoreZero(v any, filters FilterMap, ignoreZero []string) FieldFilters { + for filter := range filters { + if !isValidFilter(reflect.TypeOf(v), filter) { + panic(fmt.Errorf("invalid filter path: %s", filter)) + } + } + ignore := make(map[string]struct{}) + for _, value := range ignoreZero { + ignore[value] = struct{}{} + } + return FieldFilters{ + filters: filters, + ignoreZero: ignore, + } +} + +func isValidFilter(typ reflect.Type, filter string) bool { + if filter == "" { + return true + } + kind := typ.Kind() + switch { + case util.IsPrimitive(kind): + // can't descend further + return false + case kind == reflect.Struct: + element, rest, _ := strings.Cut(filter, ".") + for i := 0; i < typ.NumField(); i++ { + field := typ.Field(i) + if field.Anonymous { + if isValidFilter(field.Type, filter) { + return true + } + } else { + if getTag(field) == element { + return isValidFilter(field.Type, rest) + } + } + } + return false + case kind == reflect.Slice, kind == reflect.Ptr: + return isValidFilter(typ.Elem(), filter) + default: + panic(fmt.Errorf("%v has kind %v", typ.Name(), kind)) + } +} + +func (ff FieldFilters) Verify(v any) report.Report { + return ff.verify(reflect.ValueOf(v), "", path.New("json")) +} + +func (ff FieldFilters) verify(v reflect.Value, filter string, p path.ContextPath) (r report.Report) { + if err := ff.Lookup(filter); err != nil { + // This object is filtered. Add an error if it's non-empty, + // but don't descend further in any case. + if !ff.isEmpty(v, filter) { + r.AddOnError(p, err) + } + return + } + + typ := v.Type() + kind := typ.Kind() + switch { + case util.IsPrimitive(kind): + case kind == reflect.Struct: + for i := 0; i < v.NumField(); i++ { + field := v.Field(i) + if typ.Field(i).Anonymous { + r.Merge(ff.verify(field, filter, p)) + } else { + tag := getTag(typ.Field(i)) + r.Merge(ff.verify(field, fmt.Sprintf("%s.%s", filter, tag), p.Append(tag))) + } + } + case kind == reflect.Slice: + for i := 0; i < v.Len(); i++ { + r.Merge(ff.verify(v.Index(i), filter, p.Append(i))) + } + case kind == reflect.Ptr: + if !v.IsNil() { + r.Merge(ff.verify(v.Elem(), filter, p)) + } + case kind == reflect.Map: + // not supported in filters; ignore + default: + panic(fmt.Errorf("%v has kind %v", typ.Name(), kind)) + } + return +} + +func (ff FieldFilters) isEmpty(v reflect.Value, filter string) bool { + typ := v.Type() + kind := typ.Kind() + switch { + case util.IsPrimitive(kind): + return v.IsZero() + case kind == reflect.Struct: + for i := 0; i < v.NumField(); i++ { + childFilter := filter + if !typ.Field(i).Anonymous { + childFilter = fmt.Sprintf("%s.%s", filter, getTag(typ.Field(i))) + } + if !ff.isEmpty(v.Field(i), childFilter) { + return false + } + } + return true + case kind == reflect.Slice: + // different from v.IsZero(): we treat a non-nil zero-length + // slice as empty + return v.Len() == 0 + case kind == reflect.Ptr: + if v.IsNil() { + return true + } + // special case: if pointing to a primitive, and the primitive + // is the zero value, and the filter is listed in ff.ignoreZero, + // treat as empty + if util.IsPrimitive(typ.Elem().Kind()) && v.Elem().IsZero() { + _, ignoreZero := ff.ignoreZero[strings.TrimPrefix(filter, ".")] + if ignoreZero { + return true + } + } + return false + case kind == reflect.Map: + // not supported in filters; prune + return true + default: + panic(fmt.Errorf("%v has kind %v", typ.Name(), kind)) + } +} + +func (ff FieldFilters) Lookup(filter string) error { + return ff.filters[strings.TrimPrefix(filter, ".")] +} + +func getTag(field reflect.StructField) string { + tag, ok := field.Tag.Lookup("json") + if !ok { + panic(fmt.Errorf("struct field %q has no JSON tag", field.Name)) + } + return strings.Split(tag, ",")[0] +} diff --git a/vendor/github.com/coreos/butane/config/util/util.go b/vendor/github.com/coreos/butane/config/util/util.go index 64599c4099..997b2fb1fc 100644 --- a/vendor/github.com/coreos/butane/config/util/util.go +++ b/vendor/github.com/coreos/butane/config/util/util.go @@ -21,6 +21,7 @@ import ( "reflect" "regexp" "strings" + "unicode" "github.com/coreos/butane/config/common" "github.com/coreos/butane/translate" @@ -36,17 +37,21 @@ import ( ) var ( - snakeRe = regexp.MustCompile("([A-Z])") + snakeRe = regexp.MustCompile("(MiB|[A-Z])") ) // Misc helpers +type Config interface { + FieldFilters() *FieldFilters +} + // Translate translates cfg to the corresponding Ignition config version // using the named translation method on cfg, and returns the marshaled // 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 Translate(cfg interface{}, translateMethod string, options common.TranslateOptions) (interface{}, report.Report, error) { +func Translate(cfg Config, translateMethod string, options common.TranslateOptions) (interface{}, report.Report, error) { // Get method, and zero return value for error returns. method := reflect.ValueOf(cfg).MethodByName(translateMethod) zeroValue := reflect.Zero(method.Type().Out(0)).Interface() @@ -62,7 +67,7 @@ func Translate(cfg interface{}, translateMethod string, options common.Translate final := translateRet[0].Interface() translations := translateRet[1].Interface().(translate.TranslationSet) translateReport := translateRet[2].Interface().(report.Report) - r.Merge(translateReport) + r.Merge(TranslateReportPaths(translateReport, translations)) if r.IsFatal() { return zeroValue, r, common.ErrInvalidSourceConfig } @@ -73,6 +78,16 @@ func Translate(cfg interface{}, translateMethod string, options common.Translate } } + // Check for fields forbidden by this spec. + filters := cfg.FieldFilters() + if filters != nil { + filterReport := filters.Verify(final) + r.Merge(TranslateReportPaths(filterReport, translations)) + if r.IsFatal() { + return zeroValue, r, common.ErrInvalidSourceConfig + } + } + // Check for invalid duplicated keys. dupsReport := validate.ValidateCustom(final, "json", ignvalidate.ValidateDups) r.Merge(TranslateReportPaths(dupsReport, translations)) @@ -204,7 +219,7 @@ func snakePath(p path.ContextPath) path.ContextPath { ret := path.New(p.Tag) for _, part := range p.Path { if str, ok := part.(string); ok { - ret = ret.Append(snake(str)) + ret = ret.Append(Snake(str)) } else { ret = ret.Append(part) } @@ -212,18 +227,36 @@ func snakePath(p path.ContextPath) path.ContextPath { return ret } -// snake converts from camelCase (not CamelCase) to snake_case -func snake(in string) string { +// Snake converts from camelCase (not CamelCase) to snake_case +func Snake(in string) string { return strings.ToLower(snakeRe.ReplaceAllString(in, "_$1")) } -// TranslateReportPaths takes a report from a camelCase json document and a set of translations rules, -// applies those rules and converts all camelCase to snake_case. +// Camel converts from snake_case to camelCase +func Camel(in string) string { + if strings.HasSuffix(in, "_mib") { + in = strings.TrimSuffix(in, "_mib") + "MiB" + } + arr := []rune(in) + for i := range arr { + if i > 0 && arr[i-1] == '_' { + arr[i] = unicode.ToUpper(arr[i]) + } + } + return strings.ReplaceAll(string(arr), "_", "") +} + +// TranslateReportPaths takes a report with a mix of json (camelCase) and +// yaml (snake_case) paths, and a set of translation rules. It applies +// those rules and converts all json paths to snake-cased yaml. func TranslateReportPaths(r report.Report, ts translate.TranslationSet) report.Report { var ret report.Report ret.Merge(r) for i, ent := range ret.Entries { context := ent.Context + if context.Tag == "yaml" { + continue + } if t, ok := ts.Set[context.String()]; ok { context = t.From } else { diff --git a/vendor/modules.txt b/vendor/modules.txt index 0f68d1e8c0..f425692100 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -231,7 +231,7 @@ github.com/containers/image/v5/docker/reference # github.com/containers/storage v1.50.1 ## explicit; go 1.19 github.com/containers/storage/pkg/regexp -# github.com/coreos/butane v0.18.1-0.20230412230143-79c207705ee4 +# github.com/coreos/butane v0.19.0 ## explicit; go 1.18 github.com/coreos/butane/base/util github.com/coreos/butane/base/v0_1 @@ -260,8 +260,10 @@ 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 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_14 +github.com/coreos/butane/config/openshift/v4_14/result +github.com/coreos/butane/config/openshift/v4_15_exp +github.com/coreos/butane/config/openshift/v4_15_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