From d8b6402baa0824cf4218be821077e6e653e0afa9 Mon Sep 17 00:00:00 2001 From: Adam0Brien Date: Thu, 27 Jul 2023 13:54:41 +0100 Subject: [PATCH] base/v0_6_exp: Validate merged/replaced Ingition Configs --- base/v0_6_exp/validate.go | 15 ++ config/fcos/v1_6_exp/validate.go | 13 -- config/fcos/v1_6_exp/validate_test.go | 35 ---- .../ignition/v2/config/translate/translate.go | 187 ++++++++++++++++++ .../coreos/ignition/v2/config/v3_0/config.go | 73 +++++++ .../coreos/ignition/v2/config/v3_1/config.go | 78 ++++++++ .../v2/config/v3_1/translate/translate.go | 106 ++++++++++ .../coreos/ignition/v2/config/v3_2/config.go | 78 ++++++++ .../v2/config/v3_2/translate/translate.go | 90 +++++++++ .../coreos/ignition/v2/config/v3_3/config.go | 78 ++++++++ .../v2/config/v3_3/translate/translate.go | 95 +++++++++ .../coreos/ignition/v2/config/v3_4/config.go | 78 ++++++++ .../v2/config/v3_4/translate/translate.go | 85 ++++++++ .../v2/config/v3_5_experimental/config.go | 78 ++++++++ .../v3_5_experimental/translate/translate.go | 35 ++++ vendor/modules.txt | 12 ++ 16 files changed, 1088 insertions(+), 48 deletions(-) create mode 100644 vendor/github.com/coreos/ignition/v2/config/translate/translate.go create mode 100644 vendor/github.com/coreos/ignition/v2/config/v3_0/config.go create mode 100644 vendor/github.com/coreos/ignition/v2/config/v3_1/config.go create mode 100644 vendor/github.com/coreos/ignition/v2/config/v3_1/translate/translate.go create mode 100644 vendor/github.com/coreos/ignition/v2/config/v3_2/config.go create mode 100644 vendor/github.com/coreos/ignition/v2/config/v3_2/translate/translate.go create mode 100644 vendor/github.com/coreos/ignition/v2/config/v3_3/config.go create mode 100644 vendor/github.com/coreos/ignition/v2/config/v3_3/translate/translate.go create mode 100644 vendor/github.com/coreos/ignition/v2/config/v3_4/config.go create mode 100644 vendor/github.com/coreos/ignition/v2/config/v3_4/translate/translate.go create mode 100644 vendor/github.com/coreos/ignition/v2/config/v3_5_experimental/config.go create mode 100644 vendor/github.com/coreos/ignition/v2/config/v3_5_experimental/translate/translate.go diff --git a/base/v0_6_exp/validate.go b/base/v0_6_exp/validate.go index ad3917be..2c3b4cda 100644 --- a/base/v0_6_exp/validate.go +++ b/base/v0_6_exp/validate.go @@ -17,6 +17,7 @@ package v0_6_exp import ( baseutil "github.com/coreos/butane/base/util" "github.com/coreos/butane/config/common" + exp "github.com/coreos/ignition/v2/config/v3_5_experimental" "github.com/coreos/ignition/v2/config/util" "github.com/coreos/vcontext/path" @@ -29,10 +30,24 @@ func (rs Resource) Validate(c path.ContextPath) (r report.Report) { if rs.Local != nil { sources++ field = "local" + rsLocalBytes := []byte(*rs.Local) + _, report, err := exp.ParseCompatibleVersion(rsLocalBytes) + if err != nil { + r.AddOnError(c.Append("ignition", "config", "merge", "local"), common.ErrTooManyResourceSources) + } else { + r.Merge(report) + } } if rs.Inline != nil { sources++ field = "inline" + rsInlineBytes := []byte(*rs.Inline) + _, report, err := exp.ParseCompatibleVersion(rsInlineBytes) + if err == nil { + r.AddOnError(c.Append("ignition", "config", "merge", "inline"), common.ErrTooManyResourceSources) + } else { + r.Merge(report) + } } if rs.Source != nil { sources++ diff --git a/config/fcos/v1_6_exp/validate.go b/config/fcos/v1_6_exp/validate.go index 58df0230..4c3ae9de 100644 --- a/config/fcos/v1_6_exp/validate.go +++ b/config/fcos/v1_6_exp/validate.go @@ -27,7 +27,6 @@ import ( const rootDevice = "/dev/disk/by-id/coreos-boot-disk" var allowedMountpoints = regexp.MustCompile(`^/(etc|var)(/|$)`) -var fileExt = regexp.MustCompile("^(.ign)$") // 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. @@ -46,18 +45,6 @@ func (conf Config) Validate(c path.ContextPath) (r report.Report) { r.AddOnError(c.Append("storage", "filesystems", i, "path"), common.ErrMountPointForbidden) } } - - if conf.Ignition.Config.Merge != nil { - for i, conf := range conf.Ignition.Config.Merge { - if conf.Inline != nil && !fileExt.MatchString(string(*conf.Inline)) { - r.AddOnError(c.Append("ignition", "config", "merge", i, "inline"), common.ErrInvalidFileExtension) - } - - if conf.Local != nil && !fileExt.MatchString(string(*conf.Local)) { - r.AddOnError(c.Append("ignition", "config", "merge", i, "local"), common.ErrInvalidFileExtension) - } - } - } return } diff --git a/config/fcos/v1_6_exp/validate_test.go b/config/fcos/v1_6_exp/validate_test.go index e313e396..2c850580 100644 --- a/config/fcos/v1_6_exp/validate_test.go +++ b/config/fcos/v1_6_exp/validate_test.go @@ -479,38 +479,3 @@ func TestValidateConfig(t *testing.T) { }) } } - -func TestValidateFileExtension(t *testing.T) { - tests := []struct { - in Config - out error - errPath path.ContextPath - }{ - { - in: Config{ - Config: base.Config{ - Ignition: base.Ignition{ - Config: base.IgnitionConfig{ - Merge: []base.Resource{ - { - Inline: util.StrToPtr("config.bu"), - }, - }, - }, - }, - }, - }, - out: common.ErrInvalidFileExtension, - errPath: path.New("yaml", "ignition", "config", "merge", 0, "inline"), - }, - } - for i, test := range tests { - t.Run(fmt.Sprintf("validate %d", i), func(t *testing.T) { - actual := test.in.Validate(path.New("yaml")) - baseutil.VerifyReport(t, test.in, actual) - expected := report.Report{} - expected.AddOnError(test.errPath, test.out) - assert.Equal(t, expected, actual, "invalid report") - }) - } -} diff --git a/vendor/github.com/coreos/ignition/v2/config/translate/translate.go b/vendor/github.com/coreos/ignition/v2/config/translate/translate.go new file mode 100644 index 00000000..05d8ce2c --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/translate/translate.go @@ -0,0 +1,187 @@ +// Copyright 2019 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 translate + +import ( + "fmt" + "reflect" + + "github.com/coreos/ignition/v2/config/util" +) + +/* + * This is an automatic translator that replace boilerplate code to copy one + * struct into a nearly identical struct in another package. To use it first + * call NewTranslator() to get a translator instance. This can then have + * additional translation rules (in the form of functions) to translate from + * types in one struct to the other. Those functions are in the form: + * func(typeFromInputStruct) -> typeFromOutputStruct + * These can be closures that reference the translator as well. This allows for + * manually translating some fields but resuming automatic translation on the + * other fields through the Translator.Translate() function. + */ + +// Returns if this type can be translated without a custom translator. Children or other +// ancestors might require custom translators however +func (t translator) translatable(t1, t2 reflect.Type) bool { + k1 := t1.Kind() + k2 := t2.Kind() + if k1 != k2 { + return false + } + switch { + case util.IsPrimitive(k1): + return true + case util.IsInvalidInConfig(k1): + panic(fmt.Sprintf("Encountered invalid kind %s in config. This is a bug, please file a report", k1)) + case k1 == reflect.Ptr || k1 == reflect.Slice: + return t.translatable(t1.Elem(), t2.Elem()) || t.hasTranslator(t1.Elem(), t2.Elem()) + case k1 == reflect.Struct: + return t.translatableStruct(t1, t2) + default: + panic(fmt.Sprintf("Encountered unknown kind %s in config. This is a bug, please file a report", k1)) + } +} + +// precondition: t1, t2 are both of Kind 'struct' +func (t translator) translatableStruct(t1, t2 reflect.Type) bool { + if t1.NumField() != t2.NumField() || t1.Name() != t2.Name() { + return false + } + for i := 0; i < t1.NumField(); i++ { + t1f := t1.Field(i) + t2f, ok := t2.FieldByName(t1f.Name) + + if !ok { + return false + } + if !t.translatable(t1f.Type, t2f.Type) && !t.hasTranslator(t1f.Type, t2f.Type) { + return false + } + } + return true +} + +// checks that t could reasonably be the type of a translator function +func couldBeValidTranslator(t reflect.Type) bool { + if t.Kind() != reflect.Func { + return false + } + if t.NumIn() != 1 || t.NumOut() != 1 { + return false + } + if util.IsInvalidInConfig(t.In(0).Kind()) || util.IsInvalidInConfig(t.Out(0).Kind()) { + return false + } + return true +} + +// translate from one type to another, but deep copy all data +// precondition: vFrom and vTo are the same type as defined by translatable() +// precondition: vTo is addressable and settable +func (t translator) translateSameType(vFrom, vTo reflect.Value) { + k := vFrom.Kind() + switch { + case util.IsPrimitive(k): + // Use convert, even if not needed; type alias to primitives are not + // directly assignable and calling Convert on primitives does no harm + vTo.Set(vFrom.Convert(vTo.Type())) + case k == reflect.Ptr: + if vFrom.IsNil() { + return + } + vTo.Set(reflect.New(vTo.Type().Elem())) + t.translate(vFrom.Elem(), vTo.Elem()) + case k == reflect.Slice: + if vFrom.IsNil() { + return + } + vTo.Set(reflect.MakeSlice(vTo.Type(), vFrom.Len(), vFrom.Len())) + for i := 0; i < vFrom.Len(); i++ { + t.translate(vFrom.Index(i), vTo.Index(i)) + } + case k == reflect.Struct: + for i := 0; i < vFrom.NumField(); i++ { + t.translate(vFrom.Field(i), vTo.FieldByName(vFrom.Type().Field(i).Name)) + } + default: + panic("Encountered types that are not the same when they should be. This is a bug, please file a report") + } +} + +// helper to return if a custom translator was defined +func (t translator) hasTranslator(tFrom, tTo reflect.Type) bool { + return t.getTranslator(tFrom, tTo).IsValid() +} + +// vTo must be addressable, should be acquired by calling reflect.ValueOf() on a variable of the correct type +func (t translator) translate(vFrom, vTo reflect.Value) { + tFrom := vFrom.Type() + tTo := vTo.Type() + if fnv := t.getTranslator(tFrom, tTo); fnv.IsValid() { + vTo.Set(fnv.Call([]reflect.Value{vFrom})[0]) + return + } + if t.translatable(tFrom, tTo) { + t.translateSameType(vFrom, vTo) + return + } + + panic(fmt.Sprintf("Translator not defined for %v to %v", tFrom, tTo)) +} + +type Translator interface { + AddCustomTranslator(t interface{}) + Translate(from, to interface{}) +} + +func NewTranslator() Translator { + return &translator{} +} + +type translator struct { + // List of custom translation funcs, must pass couldBeValidTranslator + // This is only for fields that cannot or should not be trivially translated, + // All trivially translated fields use the default behavior. + translators []reflect.Value +} + +func (t *translator) AddCustomTranslator(fn interface{}) { + fnv := reflect.ValueOf(fn) + if !couldBeValidTranslator(fnv.Type()) { + panic("Tried to register invalid translator function") + } + t.translators = append(t.translators, fnv) +} + +func (t translator) getTranslator(from, to reflect.Type) reflect.Value { + for _, fn := range t.translators { + if fn.Type().In(0) == from && fn.Type().Out(0) == to { + return fn + } + } + return reflect.Value{} +} + +func (t translator) Translate(from, to interface{}) { + fv := reflect.ValueOf(from) + tv := reflect.ValueOf(to) + if fv.Kind() != reflect.Ptr || tv.Kind() != reflect.Ptr { + panic("Translate needs to be called on pointers") + } + fv = fv.Elem() + tv = tv.Elem() + t.translate(fv, tv) +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_0/config.go b/vendor/github.com/coreos/ignition/v2/config/v3_0/config.go new file mode 100644 index 00000000..ca32f7ad --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_0/config.go @@ -0,0 +1,73 @@ +// Copyright 2015 CoreOS, 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 v3_0 + +import ( + "github.com/coreos/ignition/v2/config/merge" + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + "github.com/coreos/ignition/v2/config/v3_0/types" + "github.com/coreos/ignition/v2/config/validate" + + "github.com/coreos/go-semver/semver" + "github.com/coreos/vcontext/report" +) + +func Merge(parent, child types.Config) types.Config { + res, _ := merge.MergeStructTranscribe(parent, child) + return res.(types.Config) +} + +// Parse parses the raw config into a types.Config struct and generates a report of any +// errors, warnings, info, and deprecations it encountered +func Parse(rawConfig []byte) (types.Config, report.Report, error) { + if len(rawConfig) == 0 { + return types.Config{}, report.Report{}, errors.ErrEmpty + } + + var config types.Config + if rpt, err := util.HandleParseErrors(rawConfig, &config); err != nil { + return types.Config{}, rpt, err + } + + version, err := semver.NewVersion(config.Ignition.Version) + + if err != nil || *version != types.MaxVersion { + return types.Config{}, report.Report{}, errors.ErrUnknownVersion + } + + rpt := validate.ValidateWithContext(config, rawConfig) + if rpt.IsFatal() { + return types.Config{}, rpt, errors.ErrInvalid + } + + return config, rpt, nil +} + +// ParseCompatibleVersion parses the raw config of version 3.0.0 into +// a 3.0 types.Config struct and generates a report of any errors, warnings, +// info, and deprecations it encountered +func ParseCompatibleVersion(raw []byte) (types.Config, report.Report, error) { + version, rpt, err := util.GetConfigVersion(raw) + if err != nil { + return types.Config{}, rpt, err + } + + if version == types.MaxVersion { + return Parse(raw) + } + + return types.Config{}, report.Report{}, errors.ErrUnknownVersion +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_1/config.go b/vendor/github.com/coreos/ignition/v2/config/v3_1/config.go new file mode 100644 index 00000000..a40ae56c --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_1/config.go @@ -0,0 +1,78 @@ +// Copyright 2019 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 v3_1 + +import ( + "github.com/coreos/ignition/v2/config/merge" + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + prev "github.com/coreos/ignition/v2/config/v3_0" + "github.com/coreos/ignition/v2/config/v3_1/translate" + "github.com/coreos/ignition/v2/config/v3_1/types" + "github.com/coreos/ignition/v2/config/validate" + + "github.com/coreos/go-semver/semver" + "github.com/coreos/vcontext/report" +) + +func Merge(parent, child types.Config) types.Config { + res, _ := merge.MergeStructTranscribe(parent, child) + return res.(types.Config) +} + +// Parse parses the raw config into a types.Config struct and generates a report of any +// errors, warnings, info, and deprecations it encountered +func Parse(rawConfig []byte) (types.Config, report.Report, error) { + if len(rawConfig) == 0 { + return types.Config{}, report.Report{}, errors.ErrEmpty + } + + var config types.Config + if rpt, err := util.HandleParseErrors(rawConfig, &config); err != nil { + return types.Config{}, rpt, err + } + + version, err := semver.NewVersion(config.Ignition.Version) + + if err != nil || *version != types.MaxVersion { + return types.Config{}, report.Report{}, errors.ErrUnknownVersion + } + + rpt := validate.ValidateWithContext(config, rawConfig) + if rpt.IsFatal() { + return types.Config{}, rpt, errors.ErrInvalid + } + + return config, rpt, nil +} + +// ParseCompatibleVersion parses the raw config of version 3.1.0 or lesser +// into a 3.1 types.Config struct and generates a report of any errors, warnings, +// info, and deprecations it encountered +func ParseCompatibleVersion(raw []byte) (types.Config, report.Report, error) { + version, rpt, err := util.GetConfigVersion(raw) + if err != nil { + return types.Config{}, rpt, err + } + + if version == types.MaxVersion { + return Parse(raw) + } + prevCfg, r, err := prev.ParseCompatibleVersion(raw) + if err != nil { + return types.Config{}, r, err + } + return translate.Translate(prevCfg), r, nil +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_1/translate/translate.go b/vendor/github.com/coreos/ignition/v2/config/v3_1/translate/translate.go new file mode 100644 index 00000000..5921c42c --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_1/translate/translate.go @@ -0,0 +1,106 @@ +// Copyright 2019 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 translate + +import ( + "github.com/coreos/ignition/v2/config/translate" + "github.com/coreos/ignition/v2/config/util" + old_types "github.com/coreos/ignition/v2/config/v3_0/types" + "github.com/coreos/ignition/v2/config/v3_1/types" +) + +func translateFilesystem(old old_types.Filesystem) (ret types.Filesystem) { + // use a new translator so we don't recurse infinitely + tr := translate.NewTranslator() + tr.Translate(&old.Device, &ret.Device) + tr.Translate(&old.Format, &ret.Format) + tr.Translate(&old.Label, &ret.Label) + tr.Translate(&old.Options, &ret.Options) + tr.Translate(&old.Path, &ret.Path) + tr.Translate(&old.UUID, &ret.UUID) + tr.Translate(&old.WipeFilesystem, &ret.WipeFilesystem) + return +} + +func translateConfigReference(old old_types.ConfigReference) (ret types.Resource) { + // use a new translator so we don't recurse infinitely + tr := translate.NewTranslator() + tr.Translate(&old.Source, &ret.Source) + tr.Translate(&old.Verification, &ret.Verification) + return +} + +func translateCAReference(old old_types.CaReference) (ret types.Resource) { + // use a new translator so we don't recurse infinitely + tr := translate.NewTranslator() + ret.Source = util.StrToPtr(old.Source) + tr.Translate(&old.Verification, &ret.Verification) + return +} + +func translateFileContents(old old_types.FileContents) (ret types.Resource) { + // use a new translator so we don't recurse infinitely + tr := translate.NewTranslator() + tr.Translate(&old.Compression, &ret.Compression) + tr.Translate(&old.Source, &ret.Source) + tr.Translate(&old.Verification, &ret.Verification) + return +} + +func translateIgnitionConfig(old old_types.IgnitionConfig) (ret types.IgnitionConfig) { + // use a new translator so we don't recurse infinitely + tr := translate.NewTranslator() + tr.AddCustomTranslator(translateConfigReference) + tr.Translate(&old.Merge, &ret.Merge) + tr.Translate(&old.Replace, &ret.Replace) + return +} + +func translateSecurity(old old_types.Security) (ret types.Security) { + // use a new translator so we don't recurse infinitely + tr := translate.NewTranslator() + tr.AddCustomTranslator(translateTLS) + tr.Translate(&old.TLS, &ret.TLS) + return +} + +func translateTLS(old old_types.TLS) (ret types.TLS) { + // use a new translator so we don't recurse infinitely + tr := translate.NewTranslator() + tr.AddCustomTranslator(translateCAReference) + tr.Translate(&old.CertificateAuthorities, &ret.CertificateAuthorities) + return +} + +func translateIgnition(old old_types.Ignition) (ret types.Ignition) { + // use a new translator so we don't recurse infinitely + tr := translate.NewTranslator() + tr.AddCustomTranslator(translateIgnitionConfig) + tr.AddCustomTranslator(translateSecurity) + tr.Translate(&old.Config, &ret.Config) + tr.Translate(&old.Security, &ret.Security) + tr.Translate(&old.Timeouts, &ret.Timeouts) + ret.Version = types.MaxVersion.String() + return +} + +func Translate(old old_types.Config) (ret types.Config) { + tr := translate.NewTranslator() + tr.AddCustomTranslator(translateFileContents) + tr.AddCustomTranslator(translateIgnition) + tr.AddCustomTranslator(translateFilesystem) + tr.Translate(&old, &ret) + return +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_2/config.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/config.go new file mode 100644 index 00000000..c6c7d94d --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/config.go @@ -0,0 +1,78 @@ +// 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 v3_2 + +import ( + "github.com/coreos/ignition/v2/config/merge" + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + prev "github.com/coreos/ignition/v2/config/v3_1" + "github.com/coreos/ignition/v2/config/v3_2/translate" + "github.com/coreos/ignition/v2/config/v3_2/types" + "github.com/coreos/ignition/v2/config/validate" + + "github.com/coreos/go-semver/semver" + "github.com/coreos/vcontext/report" +) + +func Merge(parent, child types.Config) types.Config { + res, _ := merge.MergeStructTranscribe(parent, child) + return res.(types.Config) +} + +// Parse parses the raw config into a types.Config struct and generates a report of any +// errors, warnings, info, and deprecations it encountered +func Parse(rawConfig []byte) (types.Config, report.Report, error) { + if len(rawConfig) == 0 { + return types.Config{}, report.Report{}, errors.ErrEmpty + } + + var config types.Config + if rpt, err := util.HandleParseErrors(rawConfig, &config); err != nil { + return types.Config{}, rpt, err + } + + version, err := semver.NewVersion(config.Ignition.Version) + + if err != nil || *version != types.MaxVersion { + return types.Config{}, report.Report{}, errors.ErrUnknownVersion + } + + rpt := validate.ValidateWithContext(config, rawConfig) + if rpt.IsFatal() { + return types.Config{}, rpt, errors.ErrInvalid + } + + return config, rpt, nil +} + +// ParseCompatibleVersion parses the raw config of version 3.2.0 or lesser +// into a 3.2 types.Config struct and generates a report of any errors, warnings, +// info, and deprecations it encountered +func ParseCompatibleVersion(raw []byte) (types.Config, report.Report, error) { + version, rpt, err := util.GetConfigVersion(raw) + if err != nil { + return types.Config{}, rpt, err + } + + if version == types.MaxVersion { + return Parse(raw) + } + prevCfg, r, err := prev.ParseCompatibleVersion(raw) + if err != nil { + return types.Config{}, r, err + } + return translate.Translate(prevCfg), r, nil +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_2/translate/translate.go b/vendor/github.com/coreos/ignition/v2/config/v3_2/translate/translate.go new file mode 100644 index 00000000..0fd4f420 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_2/translate/translate.go @@ -0,0 +1,90 @@ +// 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 translate + +import ( + "github.com/coreos/ignition/v2/config/translate" + old_types "github.com/coreos/ignition/v2/config/v3_1/types" + "github.com/coreos/ignition/v2/config/v3_2/types" +) + +func translateIgnition(old old_types.Ignition) (ret types.Ignition) { + // use a new translator so we don't recurse infinitely + translate.NewTranslator().Translate(&old, &ret) + ret.Version = types.MaxVersion.String() + return +} + +func translateStorage(old old_types.Storage) (ret types.Storage) { + tr := translate.NewTranslator() + tr.AddCustomTranslator(translatePartition) + tr.Translate(&old.Directories, &ret.Directories) + tr.Translate(&old.Disks, &ret.Disks) + tr.Translate(&old.Files, &ret.Files) + tr.Translate(&old.Filesystems, &ret.Filesystems) + tr.Translate(&old.Links, &ret.Links) + tr.Translate(&old.Raid, &ret.Raid) + return +} + +func translatePasswdUser(old old_types.PasswdUser) (ret types.PasswdUser) { + tr := translate.NewTranslator() + tr.Translate(&old.Gecos, &ret.Gecos) + tr.Translate(&old.Groups, &ret.Groups) + tr.Translate(&old.HomeDir, &ret.HomeDir) + tr.Translate(&old.Name, &ret.Name) + tr.Translate(&old.NoCreateHome, &ret.NoCreateHome) + tr.Translate(&old.NoLogInit, &ret.NoLogInit) + tr.Translate(&old.NoUserGroup, &ret.NoUserGroup) + tr.Translate(&old.PasswordHash, &ret.PasswordHash) + tr.Translate(&old.PrimaryGroup, &ret.PrimaryGroup) + tr.Translate(&old.SSHAuthorizedKeys, &ret.SSHAuthorizedKeys) + tr.Translate(&old.Shell, &ret.Shell) + tr.Translate(&old.System, &ret.System) + tr.Translate(&old.UID, &ret.UID) + return +} + +func translatePasswdGroup(old old_types.PasswdGroup) (ret types.PasswdGroup) { + tr := translate.NewTranslator() + tr.Translate(&old.Gid, &ret.Gid) + tr.Translate(&old.Name, &ret.Name) + tr.Translate(&old.PasswordHash, &ret.PasswordHash) + tr.Translate(&old.System, &ret.System) + return +} + +func translatePartition(old old_types.Partition) (ret types.Partition) { + tr := translate.NewTranslator() + tr.Translate(&old.GUID, &ret.GUID) + tr.Translate(&old.Label, &ret.Label) + tr.Translate(&old.Number, &ret.Number) + tr.Translate(&old.ShouldExist, &ret.ShouldExist) + tr.Translate(&old.SizeMiB, &ret.SizeMiB) + tr.Translate(&old.StartMiB, &ret.StartMiB) + tr.Translate(&old.TypeGUID, &ret.TypeGUID) + tr.Translate(&old.WipePartitionEntry, &ret.WipePartitionEntry) + return +} + +func Translate(old old_types.Config) (ret types.Config) { + tr := translate.NewTranslator() + tr.AddCustomTranslator(translateIgnition) + tr.AddCustomTranslator(translateStorage) + tr.AddCustomTranslator(translatePasswdUser) + tr.AddCustomTranslator(translatePasswdGroup) + tr.Translate(&old, &ret) + return +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_3/config.go b/vendor/github.com/coreos/ignition/v2/config/v3_3/config.go new file mode 100644 index 00000000..446ea67d --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3/config.go @@ -0,0 +1,78 @@ +// 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 v3_3 + +import ( + "github.com/coreos/ignition/v2/config/merge" + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + prev "github.com/coreos/ignition/v2/config/v3_2" + "github.com/coreos/ignition/v2/config/v3_3/translate" + "github.com/coreos/ignition/v2/config/v3_3/types" + "github.com/coreos/ignition/v2/config/validate" + + "github.com/coreos/go-semver/semver" + "github.com/coreos/vcontext/report" +) + +func Merge(parent, child types.Config) types.Config { + res, _ := merge.MergeStructTranscribe(parent, child) + return res.(types.Config) +} + +// Parse parses the raw config into a types.Config struct and generates a report of any +// errors, warnings, info, and deprecations it encountered +func Parse(rawConfig []byte) (types.Config, report.Report, error) { + if len(rawConfig) == 0 { + return types.Config{}, report.Report{}, errors.ErrEmpty + } + + var config types.Config + if rpt, err := util.HandleParseErrors(rawConfig, &config); err != nil { + return types.Config{}, rpt, err + } + + version, err := semver.NewVersion(config.Ignition.Version) + + if err != nil || *version != types.MaxVersion { + return types.Config{}, report.Report{}, errors.ErrUnknownVersion + } + + rpt := validate.ValidateWithContext(config, rawConfig) + if rpt.IsFatal() { + return types.Config{}, rpt, errors.ErrInvalid + } + + return config, rpt, nil +} + +// ParseCompatibleVersion parses the raw config of version 3.3.0 or +// lesser into a 3.3 types.Config struct and generates a report of any errors, +// warnings, info, and deprecations it encountered +func ParseCompatibleVersion(raw []byte) (types.Config, report.Report, error) { + version, rpt, err := util.GetConfigVersion(raw) + if err != nil { + return types.Config{}, rpt, err + } + + if version == types.MaxVersion { + return Parse(raw) + } + prevCfg, r, err := prev.ParseCompatibleVersion(raw) + if err != nil { + return types.Config{}, r, err + } + return translate.Translate(prevCfg), r, nil +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_3/translate/translate.go b/vendor/github.com/coreos/ignition/v2/config/v3_3/translate/translate.go new file mode 100644 index 00000000..656ad0a4 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_3/translate/translate.go @@ -0,0 +1,95 @@ +// 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 translate + +import ( + "github.com/coreos/ignition/v2/config/translate" + "github.com/coreos/ignition/v2/config/util" + old_types "github.com/coreos/ignition/v2/config/v3_2/types" + "github.com/coreos/ignition/v2/config/v3_3/types" +) + +func translateIgnition(old old_types.Ignition) (ret types.Ignition) { + // use a new translator so we don't recurse infinitely + translate.NewTranslator().Translate(&old, &ret) + ret.Version = types.MaxVersion.String() + return +} + +func translateRaid(old old_types.Raid) (ret types.Raid) { + tr := translate.NewTranslator() + tr.Translate(&old.Devices, &ret.Devices) + ret.Level = util.StrToPtr(old.Level) + tr.Translate(&old.Name, &ret.Name) + tr.Translate(&old.Options, &ret.Options) + tr.Translate(&old.Spares, &ret.Spares) + return +} + +func translateLuks(old old_types.Luks) (ret types.Luks) { + tr := translate.NewTranslator() + tr.AddCustomTranslator(translateClevis) + if old.Clevis != nil { + tr.Translate(old.Clevis, &ret.Clevis) + } + tr.Translate(&old.Device, &ret.Device) + tr.Translate(&old.KeyFile, &ret.KeyFile) + tr.Translate(&old.Label, &ret.Label) + tr.Translate(&old.Name, &ret.Name) + tr.Translate(&old.Options, &ret.Options) + tr.Translate(&old.UUID, &ret.UUID) + tr.Translate(&old.WipeVolume, &ret.WipeVolume) + return +} + +func translateClevis(old old_types.Clevis) (ret types.Clevis) { + tr := translate.NewTranslator() + tr.AddCustomTranslator(translateClevisCustom) + if old.Custom != nil { + tr.Translate(old.Custom, &ret.Custom) + } + tr.Translate(&old.Tang, &ret.Tang) + tr.Translate(&old.Threshold, &ret.Threshold) + tr.Translate(&old.Tpm2, &ret.Tpm2) + return +} + +func translateClevisCustom(old old_types.Custom) (ret types.ClevisCustom) { + tr := translate.NewTranslator() + ret.Config = util.StrToPtr(old.Config) + tr.Translate(&old.NeedsNetwork, &ret.NeedsNetwork) + ret.Pin = util.StrToPtr(old.Pin) + return +} + +func translateLinkEmbedded1(old old_types.LinkEmbedded1) (ret types.LinkEmbedded1) { + tr := translate.NewTranslator() + tr.Translate(&old.Hard, &ret.Hard) + ret.Target = util.StrToPtr(old.Target) + return +} + +func Translate(old old_types.Config) (ret types.Config) { + tr := translate.NewTranslator() + tr.AddCustomTranslator(translateIgnition) + tr.AddCustomTranslator(translateRaid) + tr.AddCustomTranslator(translateLuks) + tr.AddCustomTranslator(translateLinkEmbedded1) + tr.Translate(&old.Ignition, &ret.Ignition) + tr.Translate(&old.Passwd, &ret.Passwd) + tr.Translate(&old.Storage, &ret.Storage) + tr.Translate(&old.Systemd, &ret.Systemd) + return +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_4/config.go b/vendor/github.com/coreos/ignition/v2/config/v3_4/config.go new file mode 100644 index 00000000..e83abb0b --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_4/config.go @@ -0,0 +1,78 @@ +// 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 v3_4 + +import ( + "github.com/coreos/ignition/v2/config/merge" + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + prev "github.com/coreos/ignition/v2/config/v3_3" + "github.com/coreos/ignition/v2/config/v3_4/translate" + "github.com/coreos/ignition/v2/config/v3_4/types" + "github.com/coreos/ignition/v2/config/validate" + + "github.com/coreos/go-semver/semver" + "github.com/coreos/vcontext/report" +) + +func Merge(parent, child types.Config) types.Config { + res, _ := merge.MergeStructTranscribe(parent, child) + return res.(types.Config) +} + +// Parse parses the raw config into a types.Config struct and generates a report of any +// errors, warnings, info, and deprecations it encountered +func Parse(rawConfig []byte) (types.Config, report.Report, error) { + if len(rawConfig) == 0 { + return types.Config{}, report.Report{}, errors.ErrEmpty + } + + var config types.Config + if rpt, err := util.HandleParseErrors(rawConfig, &config); err != nil { + return types.Config{}, rpt, err + } + + version, err := semver.NewVersion(config.Ignition.Version) + + if err != nil || *version != types.MaxVersion { + return types.Config{}, report.Report{}, errors.ErrUnknownVersion + } + + rpt := validate.ValidateWithContext(config, rawConfig) + if rpt.IsFatal() { + return types.Config{}, rpt, errors.ErrInvalid + } + + return config, rpt, nil +} + +// ParseCompatibleVersion parses the raw config of version 3.4.0 or +// lesser into a 3.4 types.Config struct and generates a report of any errors, +// warnings, info, and deprecations it encountered +func ParseCompatibleVersion(raw []byte) (types.Config, report.Report, error) { + version, rpt, err := util.GetConfigVersion(raw) + if err != nil { + return types.Config{}, rpt, err + } + + if version == types.MaxVersion { + return Parse(raw) + } + prevCfg, r, err := prev.ParseCompatibleVersion(raw) + if err != nil { + return types.Config{}, r, err + } + return translate.Translate(prevCfg), r, nil +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_4/translate/translate.go b/vendor/github.com/coreos/ignition/v2/config/v3_4/translate/translate.go new file mode 100644 index 00000000..5d748d82 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_4/translate/translate.go @@ -0,0 +1,85 @@ +// 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 translate + +import ( + "github.com/coreos/ignition/v2/config/translate" + "github.com/coreos/ignition/v2/config/util" + old_types "github.com/coreos/ignition/v2/config/v3_3/types" + "github.com/coreos/ignition/v2/config/v3_4/types" +) + +func translateIgnition(old old_types.Ignition) (ret types.Ignition) { + // use a new translator so we don't recurse infinitely + translate.NewTranslator().Translate(&old, &ret) + ret.Version = types.MaxVersion.String() + return +} + +func translateFileEmbedded1(old old_types.FileEmbedded1) (ret types.FileEmbedded1) { + tr := translate.NewTranslator() + tr.Translate(&old.Append, &ret.Append) + tr.Translate(&old.Contents, &ret.Contents) + if old.Mode != nil { + // We support the special mode bits for specs >=3.4.0, so if + // the user provides special mode bits in an Ignition config + // with the version < 3.4.0, then we need to explicitly mask + // those bits out during translation. + ret.Mode = util.IntToPtr(*old.Mode & ^07000) + } + return +} + +func translateDirectoryEmbedded1(old old_types.DirectoryEmbedded1) (ret types.DirectoryEmbedded1) { + if old.Mode != nil { + // We support the special mode bits for specs >=3.4.0, so if + // the user provides special mode bits in an Ignition config + // with the version < 3.4.0, then we need to explicitly mask + // those bits out during translation. + ret.Mode = util.IntToPtr(*old.Mode & ^07000) + } + return +} + +func translateLuks(old old_types.Luks) (ret types.Luks) { + tr := translate.NewTranslator() + tr.AddCustomTranslator(translateTang) + tr.Translate(&old.Clevis, &ret.Clevis) + tr.Translate(&old.Device, &ret.Device) + tr.Translate(&old.KeyFile, &ret.KeyFile) + tr.Translate(&old.Label, &ret.Label) + tr.Translate(&old.Name, &ret.Name) + tr.Translate(&old.Options, &ret.Options) + tr.Translate(&old.UUID, &ret.UUID) + tr.Translate(&old.WipeVolume, &ret.WipeVolume) + return +} + +func translateTang(old old_types.Tang) (ret types.Tang) { + tr := translate.NewTranslator() + tr.Translate(&old.Thumbprint, &ret.Thumbprint) + tr.Translate(&old.URL, &ret.URL) + return +} + +func Translate(old old_types.Config) (ret types.Config) { + tr := translate.NewTranslator() + tr.AddCustomTranslator(translateIgnition) + tr.AddCustomTranslator(translateDirectoryEmbedded1) + tr.AddCustomTranslator(translateFileEmbedded1) + tr.AddCustomTranslator(translateLuks) + tr.Translate(&old, &ret) + return +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_5_experimental/config.go b/vendor/github.com/coreos/ignition/v2/config/v3_5_experimental/config.go new file mode 100644 index 00000000..9a92bf2a --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_5_experimental/config.go @@ -0,0 +1,78 @@ +// 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 v3_5_experimental + +import ( + "github.com/coreos/ignition/v2/config/merge" + "github.com/coreos/ignition/v2/config/shared/errors" + "github.com/coreos/ignition/v2/config/util" + prev "github.com/coreos/ignition/v2/config/v3_4" + "github.com/coreos/ignition/v2/config/v3_5_experimental/translate" + "github.com/coreos/ignition/v2/config/v3_5_experimental/types" + "github.com/coreos/ignition/v2/config/validate" + + "github.com/coreos/go-semver/semver" + "github.com/coreos/vcontext/report" +) + +func Merge(parent, child types.Config) types.Config { + res, _ := merge.MergeStructTranscribe(parent, child) + return res.(types.Config) +} + +// Parse parses the raw config into a types.Config struct and generates a report of any +// errors, warnings, info, and deprecations it encountered +func Parse(rawConfig []byte) (types.Config, report.Report, error) { + if len(rawConfig) == 0 { + return types.Config{}, report.Report{}, errors.ErrEmpty + } + + var config types.Config + if rpt, err := util.HandleParseErrors(rawConfig, &config); err != nil { + return types.Config{}, rpt, err + } + + version, err := semver.NewVersion(config.Ignition.Version) + + if err != nil || *version != types.MaxVersion { + return types.Config{}, report.Report{}, errors.ErrUnknownVersion + } + + rpt := validate.ValidateWithContext(config, rawConfig) + if rpt.IsFatal() { + return types.Config{}, rpt, errors.ErrInvalid + } + + return config, rpt, nil +} + +// ParseCompatibleVersion parses the raw config of version 3.5.0-experimental or +// lesser into a 3.5-exp types.Config struct and generates a report of any errors, +// warnings, info, and deprecations it encountered +func ParseCompatibleVersion(raw []byte) (types.Config, report.Report, error) { + version, rpt, err := util.GetConfigVersion(raw) + if err != nil { + return types.Config{}, rpt, err + } + + if version == types.MaxVersion { + return Parse(raw) + } + prevCfg, r, err := prev.ParseCompatibleVersion(raw) + if err != nil { + return types.Config{}, r, err + } + return translate.Translate(prevCfg), r, nil +} diff --git a/vendor/github.com/coreos/ignition/v2/config/v3_5_experimental/translate/translate.go b/vendor/github.com/coreos/ignition/v2/config/v3_5_experimental/translate/translate.go new file mode 100644 index 00000000..5647e629 --- /dev/null +++ b/vendor/github.com/coreos/ignition/v2/config/v3_5_experimental/translate/translate.go @@ -0,0 +1,35 @@ +// 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 translate + +import ( + "github.com/coreos/ignition/v2/config/translate" + old_types "github.com/coreos/ignition/v2/config/v3_4/types" + "github.com/coreos/ignition/v2/config/v3_5_experimental/types" +) + +func translateIgnition(old old_types.Ignition) (ret types.Ignition) { + // use a new translator so we don't recurse infinitely + translate.NewTranslator().Translate(&old, &ret) + ret.Version = types.MaxVersion.String() + return +} + +func Translate(old old_types.Config) (ret types.Config) { + tr := translate.NewTranslator() + tr.AddCustomTranslator(translateIgnition) + tr.Translate(&old, &ret) + return +} diff --git a/vendor/modules.txt b/vendor/modules.txt index d4ea6695..20d35e43 100644 --- a/vendor/modules.txt +++ b/vendor/modules.txt @@ -20,12 +20,24 @@ github.com/coreos/ignition/v2/config/merge github.com/coreos/ignition/v2/config/shared/errors github.com/coreos/ignition/v2/config/shared/parse github.com/coreos/ignition/v2/config/shared/validations +github.com/coreos/ignition/v2/config/translate github.com/coreos/ignition/v2/config/util +github.com/coreos/ignition/v2/config/v3_0 github.com/coreos/ignition/v2/config/v3_0/types +github.com/coreos/ignition/v2/config/v3_1 +github.com/coreos/ignition/v2/config/v3_1/translate github.com/coreos/ignition/v2/config/v3_1/types +github.com/coreos/ignition/v2/config/v3_2 +github.com/coreos/ignition/v2/config/v3_2/translate github.com/coreos/ignition/v2/config/v3_2/types +github.com/coreos/ignition/v2/config/v3_3 +github.com/coreos/ignition/v2/config/v3_3/translate github.com/coreos/ignition/v2/config/v3_3/types +github.com/coreos/ignition/v2/config/v3_4 +github.com/coreos/ignition/v2/config/v3_4/translate github.com/coreos/ignition/v2/config/v3_4/types +github.com/coreos/ignition/v2/config/v3_5_experimental +github.com/coreos/ignition/v2/config/v3_5_experimental/translate github.com/coreos/ignition/v2/config/v3_5_experimental/types github.com/coreos/ignition/v2/config/validate # github.com/coreos/vcontext v0.0.0-20230201181013-d72178a18687