Skip to content

Commit 37f7967

Browse files
committed
Improve Ignition conversions handling
Maintaining Ignition conversions is starting to be more and more complicated since each new version requires not only to code the conversion logic (by calling the underlaying Ignition converter) but adding the version to the MCS logic, update each comment that points to the old version or the supported version list, etc. This change aims to simplify the new Ignition bumps by providing a recursive converter that handles conversion between known versions transparently and exposes methods to retrieve the supported versions and to perform a coversion between known source and target versions. With this change, the dev in future Ignition bumps, will only need to bump the Ignition dependency and add two tiny functions that converts to and from the previous ignition version.
1 parent e0ea23d commit 37f7967

File tree

4 files changed

+459
-396
lines changed

4 files changed

+459
-396
lines changed

pkg/controller/common/helpers.go

+24-280
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import (
99
"encoding/base64"
1010
"errors"
1111
"fmt"
12+
"github.com/coreos/go-semver/semver"
1213
"io"
1314
"io/fs"
1415
"net/url"
@@ -22,30 +23,13 @@ import (
2223

2324
"github.com/clarketm/json"
2425
fcctbase "github.com/coreos/fcct/base/v0_1"
25-
"github.com/coreos/ign-converter/translate/v23tov30"
26-
"github.com/coreos/ign-converter/translate/v32tov22"
27-
"github.com/coreos/ign-converter/translate/v32tov31"
28-
"github.com/coreos/ign-converter/translate/v33tov32"
29-
"github.com/coreos/ign-converter/translate/v34tov33"
30-
"github.com/coreos/ign-converter/translate/v35tov34"
3126
ign2error "github.com/coreos/ignition/config/shared/errors"
3227
ign2 "github.com/coreos/ignition/config/v2_2"
3328
ign2types "github.com/coreos/ignition/config/v2_2/types"
34-
ign2_3 "github.com/coreos/ignition/config/v2_3"
3529
validate2 "github.com/coreos/ignition/config/validate"
3630
ign3error "github.com/coreos/ignition/v2/config/shared/errors"
37-
translate3_1 "github.com/coreos/ignition/v2/config/v3_1/translate"
38-
ign3_1types "github.com/coreos/ignition/v2/config/v3_1/types"
39-
translate3_2 "github.com/coreos/ignition/v2/config/v3_2/translate"
40-
ign3_2types "github.com/coreos/ignition/v2/config/v3_2/types"
41-
translate3_3 "github.com/coreos/ignition/v2/config/v3_3/translate"
42-
ign3_3types "github.com/coreos/ignition/v2/config/v3_3/types"
43-
translate3_4 "github.com/coreos/ignition/v2/config/v3_4/translate"
44-
ign3_4types "github.com/coreos/ignition/v2/config/v3_4/types"
4531

4632
ign3 "github.com/coreos/ignition/v2/config/v3_5"
47-
ign3_5 "github.com/coreos/ignition/v2/config/v3_5"
48-
translate3 "github.com/coreos/ignition/v2/config/v3_5/translate"
4933
ign3types "github.com/coreos/ignition/v2/config/v3_5/types"
5034
validate3 "github.com/coreos/ignition/v2/config/validate"
5135
"github.com/ghodss/yaml"
@@ -291,9 +275,7 @@ func ConvertRawExtIgnitionToV3_5(inRawExtIgn *runtime.RawExtension) (runtime.Raw
291275
return outRawExt, nil
292276
}
293277

294-
// ConvertRawExtIgnitionToV3_4 ensures that the Ignition config in
295-
// the RawExtension is spec v3.4, or translates to it.
296-
func ConvertRawExtIgnitionToV3_4(inRawExtIgn *runtime.RawExtension) (runtime.RawExtension, error) {
278+
func ConvertRawExtIgnitionToVersion(inRawExtIgn *runtime.RawExtension, targetVersion semver.Version) (runtime.RawExtension, error) {
297279
rawExt, err := ConvertRawExtIgnitionToV3_5(inRawExtIgn)
298280
if err != nil {
299281
return runtime.RawExtension{}, err
@@ -304,262 +286,17 @@ func ConvertRawExtIgnitionToV3_4(inRawExtIgn *runtime.RawExtension) (runtime.Raw
304286
return runtime.RawExtension{}, fmt.Errorf("parsing Ignition config failed with error: %w\nReport: %v", errV3, rptV3)
305287
}
306288

307-
// TODO(jkyros): someday we should write a recursive chain-downconverter, but until then,
308-
// we're going to do it the hard way
309-
ignCfgV33, err := convertIgnition35to34(ignCfgV3)
289+
conversion, err := ignitionConverter.Convert(ignCfgV3, ign3types.MaxVersion, targetVersion)
310290
if err != nil {
311291
return runtime.RawExtension{}, err
312292
}
313293

314-
outIgnV33, err := json.Marshal(ignCfgV33)
294+
out, err := json.Marshal(conversion)
315295
if err != nil {
316296
return runtime.RawExtension{}, fmt.Errorf("failed to marshal converted config: %w", err)
317297
}
318298

319-
outRawExt := runtime.RawExtension{}
320-
outRawExt.Raw = outIgnV33
321-
322-
return outRawExt, nil
323-
}
324-
325-
// ConvertRawExtIgnitionToV3_3 ensures that the Ignition config in
326-
// the RawExtension is spec v3.3, or translates to it.
327-
func ConvertRawExtIgnitionToV3_3(inRawExtIgn *runtime.RawExtension) (runtime.RawExtension, error) {
328-
rawExt, err := ConvertRawExtIgnitionToV3_5(inRawExtIgn)
329-
if err != nil {
330-
return runtime.RawExtension{}, err
331-
}
332-
333-
ignCfgV3, rptV3, errV3 := ign3.Parse(rawExt.Raw)
334-
if errV3 != nil || rptV3.IsFatal() {
335-
return runtime.RawExtension{}, fmt.Errorf("parsing Ignition config failed with error: %w\nReport: %v", errV3, rptV3)
336-
}
337-
338-
// TODO(jkyros): someday we should write a recursive chain-downconverter, but until then,
339-
// we're going to do it the hard way
340-
ignCfgV34, err := convertIgnition35to34(ignCfgV3)
341-
if err != nil {
342-
return runtime.RawExtension{}, err
343-
}
344-
345-
ignCfgV33, err := convertIgnition34to33(ignCfgV34)
346-
if err != nil {
347-
return runtime.RawExtension{}, err
348-
}
349-
350-
outIgnV33, err := json.Marshal(ignCfgV33)
351-
if err != nil {
352-
return runtime.RawExtension{}, fmt.Errorf("failed to marshal converted config: %w", err)
353-
}
354-
355-
outRawExt := runtime.RawExtension{}
356-
outRawExt.Raw = outIgnV33
357-
358-
return outRawExt, nil
359-
}
360-
361-
// ConvertRawExtIgnitionToV3_2 ensures that the Ignition config in
362-
// the RawExtension is spec v3.2, or translates to it.
363-
func ConvertRawExtIgnitionToV3_2(inRawExtIgn *runtime.RawExtension) (runtime.RawExtension, error) {
364-
rawExt, err := ConvertRawExtIgnitionToV3_5(inRawExtIgn)
365-
if err != nil {
366-
return runtime.RawExtension{}, err
367-
}
368-
369-
ignCfgV3, rptV3, errV3 := ign3.Parse(rawExt.Raw)
370-
if errV3 != nil || rptV3.IsFatal() {
371-
return runtime.RawExtension{}, fmt.Errorf("parsing Ignition config failed with error: %w\nReport: %v", errV3, rptV3)
372-
}
373-
374-
// TODO(jkyros): someday we should write a recursive chain-downconverter, but until then,
375-
// we're going to do it the hard way
376-
ignCfgV34, err := convertIgnition35to34(ignCfgV3)
377-
if err != nil {
378-
return runtime.RawExtension{}, err
379-
}
380-
381-
ignCfgV33, err := convertIgnition34to33(ignCfgV34)
382-
if err != nil {
383-
return runtime.RawExtension{}, err
384-
}
385-
386-
ignCfgV32, err := convertIgnition33to32(ignCfgV33)
387-
if err != nil {
388-
return runtime.RawExtension{}, err
389-
}
390-
391-
outIgnV32, err := json.Marshal(ignCfgV32)
392-
if err != nil {
393-
return runtime.RawExtension{}, fmt.Errorf("failed to marshal converted config: %w", err)
394-
}
395-
396-
outRawExt := runtime.RawExtension{}
397-
outRawExt.Raw = outIgnV32
398-
399-
return outRawExt, nil
400-
}
401-
402-
// ConvertRawExtIgnitionToV3_1 ensures that the Ignition config in
403-
// the RawExtension is spec v3.1, or translates to it.
404-
func ConvertRawExtIgnitionToV3_1(inRawExtIgn *runtime.RawExtension) (runtime.RawExtension, error) {
405-
rawExt, err := ConvertRawExtIgnitionToV3_5(inRawExtIgn)
406-
if err != nil {
407-
return runtime.RawExtension{}, err
408-
}
409-
410-
ignCfgV3, rptV3, errV3 := ign3.Parse(rawExt.Raw)
411-
if errV3 != nil || rptV3.IsFatal() {
412-
return runtime.RawExtension{}, fmt.Errorf("parsing Ignition config failed with error: %w\nReport: %v", errV3, rptV3)
413-
}
414-
415-
// TODO(jkyros): someday we should write a recursive chain-downconverter, but until then,
416-
// we're going to do it the hard way
417-
ignCfgV34, err := convertIgnition35to34(ignCfgV3)
418-
if err != nil {
419-
return runtime.RawExtension{}, err
420-
}
421-
422-
ignCfgV33, err := convertIgnition34to33(ignCfgV34)
423-
if err != nil {
424-
return runtime.RawExtension{}, err
425-
}
426-
427-
ignCfgV32, err := convertIgnition33to32(ignCfgV33)
428-
if err != nil {
429-
return runtime.RawExtension{}, err
430-
}
431-
432-
ignCfgV31, err := convertIgnition32to31(ignCfgV32)
433-
if err != nil {
434-
return runtime.RawExtension{}, err
435-
}
436-
437-
outIgnV31, err := json.Marshal(ignCfgV31)
438-
if err != nil {
439-
return runtime.RawExtension{}, fmt.Errorf("failed to marshal converted config: %w", err)
440-
}
441-
442-
outRawExt := runtime.RawExtension{}
443-
outRawExt.Raw = outIgnV31
444-
445-
return outRawExt, nil
446-
}
447-
448-
// ConvertRawExtIgnitionToV2 ensures that the Ignition config in
449-
// the RawExtension is spec v2.2, or translates to it.
450-
func ConvertRawExtIgnitionToV2_2(inRawExtIgn *runtime.RawExtension) (runtime.RawExtension, error) {
451-
ignCfg, rpt, err := ign3.Parse(inRawExtIgn.Raw)
452-
if err != nil || rpt.IsFatal() {
453-
return runtime.RawExtension{}, fmt.Errorf("parsing Ignition config spec v3.2 failed with error: %w\nReport: %v", err, rpt)
454-
}
455-
456-
converted2, err := convertIgnition35to22(ignCfg)
457-
if err != nil {
458-
return runtime.RawExtension{}, fmt.Errorf("failed to convert config from spec v3.2 to v2.2: %w", err)
459-
}
460-
461-
outIgnV2, err := json.Marshal(converted2)
462-
if err != nil {
463-
return runtime.RawExtension{}, fmt.Errorf("failed to marshal converted config: %w", err)
464-
}
465-
466-
outRawExt := runtime.RawExtension{}
467-
outRawExt.Raw = outIgnV2
468-
469-
return outRawExt, nil
470-
}
471-
472-
// convertIgnition2to3 takes an ignition spec v2.2 config and returns a v3.5 config
473-
func convertIgnition22to35(ign2config ign2types.Config) (ign3types.Config, error) {
474-
// only support writing to root file system
475-
fsMap := map[string]string{
476-
"root": "/",
477-
}
478-
479-
// Workaround to get v2.3 as input for converter
480-
ign2_3config := ign2_3.Translate(ign2config)
481-
ign3_0config, err := v23tov30.Translate(ign2_3config, fsMap)
482-
if err != nil {
483-
return ign3types.Config{}, fmt.Errorf("unable to convert Ignition spec v2 config to v3: %w", err)
484-
}
485-
// Workaround to get a v3.5 config as output
486-
converted3 := translate3.Translate(translate3_4.Translate(translate3_3.Translate(translate3_2.Translate(translate3_1.Translate(ign3_0config)))))
487-
488-
klog.V(4).Infof("Successfully translated Ignition spec v2 config to Ignition spec v3 config: %v", converted3)
489-
return converted3, nil
490-
}
491-
492-
// convertIgnition3to2 takes an ignition spec v3.5 config and returns a v2.2 config
493-
func convertIgnition35to22(ign3config ign3types.Config) (ign2types.Config, error) {
494-
495-
// TODO(jkyros): that recursive down-converter is looking like a better idea all the time
496-
497-
converted34, err := convertIgnition35to34(ign3config)
498-
if err != nil {
499-
return ign2types.Config{}, fmt.Errorf("unable to convert Ignition spec v3 config to v2: %w", err)
500-
}
501-
502-
converted33, err := convertIgnition34to33(converted34)
503-
if err != nil {
504-
return ign2types.Config{}, fmt.Errorf("unable to convert Ignition spec v3 config to v2: %w", err)
505-
}
506-
507-
converted32, err := convertIgnition33to32(converted33)
508-
if err != nil {
509-
return ign2types.Config{}, fmt.Errorf("unable to convert Ignition spec v3 config to v2: %w", err)
510-
}
511-
512-
converted2, err := v32tov22.Translate(converted32)
513-
if err != nil {
514-
return ign2types.Config{}, fmt.Errorf("unable to convert Ignition spec v3 config to v2: %w", err)
515-
}
516-
klog.V(4).Infof("Successfully translated Ignition spec v3 config to Ignition spec v2 config: %v", converted2)
517-
518-
return converted2, nil
519-
}
520-
521-
// convertIgnition35to34 takes an ignition spec v3.5 config and returns a v3.4 config
522-
func convertIgnition35to34(ign3config ign3types.Config) (ign3_4types.Config, error) {
523-
converted34, err := v35tov34.Translate(ign3config)
524-
if err != nil {
525-
return ign3_4types.Config{}, fmt.Errorf("unable to convert Ignition spec v3.5 config to v3.4: %w", err)
526-
}
527-
klog.V(4).Infof("Successfully translated Ignition spec v3.5 config to Ignition spec v3.4 config: %v", converted34)
528-
529-
return converted34, nil
530-
}
531-
532-
// convertIgnition34to33 takes an ignition spec v3.4 config and returns a v3.3 config
533-
func convertIgnition34to33(ign3config ign3_4types.Config) (ign3_3types.Config, error) {
534-
converted33, err := v34tov33.Translate(ign3config)
535-
if err != nil {
536-
return ign3_3types.Config{}, fmt.Errorf("unable to convert Ignition spec v3.2 config to v3.1: %w", err)
537-
}
538-
klog.V(4).Infof("Successfully translated Ignition spec v3.2 config to Ignition spec v3.1 config: %v", converted33)
539-
540-
return converted33, nil
541-
}
542-
543-
// convertIgnition33to32 takes an ignition spec v3.3 config and returns a v3.2 config
544-
func convertIgnition33to32(ign3config ign3_3types.Config) (ign3_2types.Config, error) {
545-
converted32, err := v33tov32.Translate(ign3config)
546-
if err != nil {
547-
return ign3_2types.Config{}, fmt.Errorf("unable to convert Ignition spec v3.3 config to v3.2: %w", err)
548-
}
549-
klog.V(4).Infof("Successfully translated Ignition spec v3.3 config to Ignition spec v3.2 config: %v", converted32)
550-
551-
return converted32, nil
552-
}
553-
554-
// convertIgnition32to31 takes an ignition spec v3.2 config and returns a v3.1 config
555-
func convertIgnition32to31(ign3config ign3_2types.Config) (ign3_1types.Config, error) {
556-
converted31, err := v32tov31.Translate(ign3config)
557-
if err != nil {
558-
return ign3_1types.Config{}, fmt.Errorf("unable to convert Ignition spec v3.2 config to v3.1: %w", err)
559-
}
560-
klog.V(4).Infof("Successfully translated Ignition spec v3.2 config to Ignition spec v3.1 config: %v", converted31)
561-
562-
return converted31, nil
299+
return runtime.RawExtension{Raw: out}, nil
563300
}
564301

565302
// ValidateIgnition wraps the underlying Ignition V2/V3 validation, but explicitly supports
@@ -746,7 +483,7 @@ func SupportedExtensions() map[string][]string {
746483
// a V2 or V3 Config or an error. This wrapper is necessary since V2 and V3 use different parsers.
747484
func IgnParseWrapper(rawIgn []byte) (interface{}, error) {
748485
// ParseCompatibleVersion will parse any config <= N to version N
749-
ignCfgV3, rptV3, errV3 := ign3_5.ParseCompatibleVersion(rawIgn)
486+
ignCfgV3, rptV3, errV3 := ign3.ParseCompatibleVersion(rawIgn)
750487
if errV3 == nil && !rptV3.IsFatal() {
751488
return ignCfgV3, nil
752489
}
@@ -755,7 +492,8 @@ func IgnParseWrapper(rawIgn []byte) (interface{}, error) {
755492
// ErrInvalidVersion ("I can't parse it to find out what it is"), but our old 3.2 logic didn't, so this is here to make sure
756493
// our error message for invalid version is still helpful.
757494
if errV3.Error() == ign3error.ErrInvalidVersion.Error() {
758-
return ign3types.Config{}, fmt.Errorf("parsing Ignition config failed: invalid version. Supported spec versions: 2.2, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5")
495+
versions := strings.TrimSuffix(strings.Join(IgnitionConverterSingleton().GetSupportedMinorVersions(), ","), ",")
496+
return ign3types.Config{}, fmt.Errorf("parsing Ignition config failed: invalid version. Supported spec versions: %s", versions)
759497
}
760498

761499
if errV3.Error() == ign3error.ErrUnknownVersion.Error() {
@@ -766,7 +504,8 @@ func IgnParseWrapper(rawIgn []byte) (interface{}, error) {
766504

767505
// If the error is still UnknownVersion it's not a 3.3/3.2/3.1/3.0 or 2.x config, thus unsupported
768506
if errV2.Error() == ign2error.ErrUnknownVersion.Error() {
769-
return ign3types.Config{}, fmt.Errorf("parsing Ignition config failed: unknown version. Supported spec versions: 2.2, 3.0, 3.1, 3.2, 3.3, 3.4, 3.5")
507+
versions := strings.TrimSuffix(strings.Join(IgnitionConverterSingleton().GetSupportedMinorVersions(), ","), ",")
508+
return ign3types.Config{}, fmt.Errorf("parsing Ignition config failed: unknown version. Supported spec versions: %s", versions)
770509
}
771510
return ign3types.Config{}, fmt.Errorf("parsing Ignition spec v2 failed with error: %v\nReport: %v", errV2, rptV2)
772511
}
@@ -790,11 +529,11 @@ func ParseAndConvertConfig(rawIgn []byte) (ign3types.Config, error) {
790529
if err != nil {
791530
return ign3types.Config{}, err
792531
}
793-
convertedIgnV3, err := convertIgnition22to35(ignconfv2)
532+
convertedIgnV3, err := ignitionConverter.Convert(ignconfv2, *semver.New(ignconfv2.Ignition.Version), ign3types.MaxVersion)
794533
if err != nil {
795534
return ign3types.Config{}, fmt.Errorf("failed to convert Ignition config spec v2 to v3: %w", err)
796535
}
797-
return convertedIgnV3, nil
536+
return convertedIgnV3.(ign3types.Config), nil
798537
default:
799538
return ign3types.Config{}, fmt.Errorf("unexpected type for ignition config: %v", typedConfig)
800539
}
@@ -990,13 +729,15 @@ func TranspileCoreOSConfigToIgn(files, units []string) (*ign3types.Config, error
990729
// Add the file to the config
991730
var ctCfg fcctbase.Config
992731
ctCfg.Storage.Files = append(ctCfg.Storage.Files, *f)
993-
ign3_0config, tSet, err := ctCfg.ToIgn3_0()
732+
ign30Config, tSet, err := ctCfg.ToIgn3_0()
994733
if err != nil {
995734
return nil, fmt.Errorf("failed to transpile config to Ignition config %w\nTranslation set: %v", err, tSet)
996735
}
997-
// TODO(jkyros): do we keep just...adding translations forever as we add more versions? :)
998-
ign3Config := translate3.Translate(translate3_4.Translate(translate3_3.Translate(translate3_2.Translate(translate3_1.Translate(ign3_0config)))))
999-
outConfig = ign3.Merge(outConfig, ign3Config)
736+
ign3Config, err := ignitionConverter.Convert(ign30Config, *semver.New("3.0.0"), ign3types.MaxVersion)
737+
if err != nil {
738+
return nil, fmt.Errorf("failed to convert config from 3.0 to %v. %w", ign3types.MaxVersion, err)
739+
}
740+
outConfig = ign3.Merge(outConfig, ign3Config.(ign3types.Config))
1000741
}
1001742

1002743
for _, contents := range units {
@@ -1008,12 +749,15 @@ func TranspileCoreOSConfigToIgn(files, units []string) (*ign3types.Config, error
1008749
// Add the unit to the config
1009750
var ctCfg fcctbase.Config
1010751
ctCfg.Systemd.Units = append(ctCfg.Systemd.Units, *u)
1011-
ign3_0config, tSet, err := ctCfg.ToIgn3_0()
752+
ign30Config, tSet, err := ctCfg.ToIgn3_0()
1012753
if err != nil {
1013754
return nil, fmt.Errorf("failed to transpile config to Ignition config %w\nTranslation set: %v", err, tSet)
1014755
}
1015-
ign3Config := translate3.Translate(translate3_4.Translate(translate3_3.Translate(translate3_2.Translate(translate3_1.Translate(ign3_0config)))))
1016-
outConfig = ign3.Merge(outConfig, ign3Config)
756+
ign3Config, err := ignitionConverter.Convert(ign30Config, *semver.New("3.0.0"), ign3types.MaxVersion)
757+
if err != nil {
758+
return nil, fmt.Errorf("failed to convert config from 3.0 to %v. %w", ign3types.MaxVersion, err)
759+
}
760+
outConfig = ign3.Merge(outConfig, ign3Config.(ign3types.Config))
1017761
}
1018762

1019763
return &outConfig, nil

0 commit comments

Comments
 (0)