diff --git a/go.sum b/go.sum index 3ac8378612..ff55767d6b 100644 --- a/go.sum +++ b/go.sum @@ -131,6 +131,7 @@ github.com/coreos/vcontext v0.0.0-20190529201340-22b159166068 h1:y2aHj7QqyAJ6YBB github.com/coreos/vcontext v0.0.0-20190529201340-22b159166068/go.mod h1:E+6hug9bFSe0KZ2ZAzr8M9F5JlArJjv5D1JS7KSkPKE= github.com/coreos/vcontext v0.0.0-20191017033345-260217907eb5 h1:DjoHHi6+9J7DGYPvBdmszKZLY+ucx2bnA77jf8KIk9M= github.com/coreos/vcontext v0.0.0-20191017033345-260217907eb5/go.mod h1:E+6hug9bFSe0KZ2ZAzr8M9F5JlArJjv5D1JS7KSkPKE= +github.com/coreos/vcontext v0.0.0-20200225161404-ee043618d38d h1:Nu473BdYOxcnhFfPrl1ihpCtxI/VZr2IfhVIHDGP43Y= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= diff --git a/lib/resourceapply/machineconfig_test.go b/lib/resourceapply/machineconfig_test.go index 9feb0b59f8..a9fd2fa88c 100644 --- a/lib/resourceapply/machineconfig_test.go +++ b/lib/resourceapply/machineconfig_test.go @@ -4,7 +4,7 @@ import ( "fmt" "testing" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" "github.com/davecgh/go-spew/spew" mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" "github.com/openshift/machine-config-operator/pkg/generated/clientset/versioned/fake" @@ -177,10 +177,10 @@ func TestApplyMachineConfig(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: mcfgv1.MachineConfigSpec{ Config: runtime.RawExtension{ - Raw: helpers.MarshalOrDie(&igntypes.Config{ - Passwd: igntypes.Passwd{ - Users: []igntypes.PasswdUser{{ - HomeDir: "/home/dummy", + Raw: helpers.MarshalOrDie(&ign3types.Config{ + Passwd: ign3types.Passwd{ + Users: []ign3types.PasswdUser{{ + HomeDir: helpers.StrToPtr("/home/dummy"), }}, }, }), @@ -203,10 +203,10 @@ func TestApplyMachineConfig(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{"extra": "leave-alone"}}, Spec: mcfgv1.MachineConfigSpec{ Config: runtime.RawExtension{ - Raw: helpers.MarshalOrDie(&igntypes.Config{ - Passwd: igntypes.Passwd{ - Users: []igntypes.PasswdUser{{ - HomeDir: "/home/dummy", + Raw: helpers.MarshalOrDie(&ign3types.Config{ + Passwd: ign3types.Passwd{ + Users: []ign3types.PasswdUser{{ + HomeDir: helpers.StrToPtr("/home/dummy"), }}, }, }), @@ -224,10 +224,10 @@ func TestApplyMachineConfig(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{"extra": "leave-alone"}}, Spec: mcfgv1.MachineConfigSpec{ Config: runtime.RawExtension{ - Raw: helpers.MarshalOrDie(&igntypes.Config{ - Passwd: igntypes.Passwd{ - Users: []igntypes.PasswdUser{{ - HomeDir: "/home/dummy-prev", + Raw: helpers.MarshalOrDie(&ign3types.Config{ + Passwd: ign3types.Passwd{ + Users: []ign3types.PasswdUser{{ + HomeDir: helpers.StrToPtr("/home/dummy-prev"), }}, }, }), @@ -239,10 +239,10 @@ func TestApplyMachineConfig(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "foo"}, Spec: mcfgv1.MachineConfigSpec{ Config: runtime.RawExtension{ - Raw: helpers.MarshalOrDie(&igntypes.Config{ - Passwd: igntypes.Passwd{ - Users: []igntypes.PasswdUser{{ - HomeDir: "/home/dummy", + Raw: helpers.MarshalOrDie(&ign3types.Config{ + Passwd: ign3types.Passwd{ + Users: []ign3types.PasswdUser{{ + HomeDir: helpers.StrToPtr("/home/dummy"), }}, }, }), @@ -265,10 +265,10 @@ func TestApplyMachineConfig(t *testing.T) { ObjectMeta: metav1.ObjectMeta{Name: "foo", Labels: map[string]string{"extra": "leave-alone"}}, Spec: mcfgv1.MachineConfigSpec{ Config: runtime.RawExtension{ - Raw: helpers.MarshalOrDie(&igntypes.Config{ - Passwd: igntypes.Passwd{ - Users: []igntypes.PasswdUser{{ - HomeDir: "/home/dummy", + Raw: helpers.MarshalOrDie(&ign3types.Config{ + Passwd: ign3types.Passwd{ + Users: []ign3types.PasswdUser{{ + HomeDir: helpers.StrToPtr("/home/dummy"), }}, }, }), diff --git a/pkg/controller/bootstrap/bootstrap_test.go b/pkg/controller/bootstrap/bootstrap_test.go index 8102a104a3..f1fa71747e 100644 --- a/pkg/controller/bootstrap/bootstrap_test.go +++ b/pkg/controller/bootstrap/bootstrap_test.go @@ -9,7 +9,7 @@ import ( "strings" "testing" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/vincent-petithory/dataurl" @@ -149,8 +149,8 @@ func TestBootstrapRun(t *testing.T) { require.NoError(t, err) // Ensure that generated registries.conf corresponds to the testdata ImageContentSourcePolicy - var registriesConfig *igntypes.File - ignCfg, err := ctrlcommon.IgnParseWrapper(mc.Spec.Config.Raw) + var registriesConfig *ign3types.File + ignCfg, err := ctrlcommon.ParseAndConvertConfig(mc.Spec.Config.Raw) require.NoError(t, err) for i := range ignCfg.Storage.Files { f := &ignCfg.Storage.Files[i] @@ -159,7 +159,7 @@ func TestBootstrapRun(t *testing.T) { } } require.NotNil(t, registriesConfig) - dataURL, err := dataurl.DecodeString(registriesConfig.Contents.Source) + dataURL, err := dataurl.DecodeString(*registriesConfig.Contents.Source) require.NoError(t, err) // Only a minimal presence check; more comprehensive tests that the contents correspond to the ICSP semantics are // maintained in pkg/controller/continer-runtime-config. diff --git a/pkg/controller/bootstrap/testdata/bootstrap/99_openshift-machineconfig_master.yaml b/pkg/controller/bootstrap/testdata/bootstrap/99_openshift-machineconfig_master.yaml index d0940ccf0d..25b00ef261 100644 --- a/pkg/controller/bootstrap/testdata/bootstrap/99_openshift-machineconfig_master.yaml +++ b/pkg/controller/bootstrap/testdata/bootstrap/99_openshift-machineconfig_master.yaml @@ -13,8 +13,7 @@ spec: security: tls: {} timeouts: {} - version: 2.2.0 - networkd: {} + version: 3.1.0 passwd: users: - name: core diff --git a/pkg/controller/bootstrap/testdata/bootstrap/99_openshift-machineconfig_worker.yaml b/pkg/controller/bootstrap/testdata/bootstrap/99_openshift-machineconfig_worker.yaml index 2580116d5a..1797965959 100644 --- a/pkg/controller/bootstrap/testdata/bootstrap/99_openshift-machineconfig_worker.yaml +++ b/pkg/controller/bootstrap/testdata/bootstrap/99_openshift-machineconfig_worker.yaml @@ -13,8 +13,7 @@ spec: security: tls: {} timeouts: {} - version: 2.2.0 - networkd: {} + version: 3.1.0 passwd: users: - name: core diff --git a/pkg/controller/common/helpers.go b/pkg/controller/common/helpers.go index ad72991433..316e8e47a4 100644 --- a/pkg/controller/common/helpers.go +++ b/pkg/controller/common/helpers.go @@ -48,13 +48,13 @@ func MergeMachineConfigs(configs []*mcfgv1.MachineConfig, osImageURL string) (*m var fips bool var kernelType string - var outIgn ign2types.Config + var outIgn ign3types.Config var err error if configs[0].Spec.Config.Raw == nil { - outIgn = ign2types.Config{} + outIgn = ign3types.Config{} } else { - outIgn, err = IgnParseWrapper(configs[0].Spec.Config.Raw) + outIgn, err = ParseAndConvertConfig(configs[0].Spec.Config.Raw) if err != nil { return nil, err } @@ -66,16 +66,16 @@ func MergeMachineConfigs(configs []*mcfgv1.MachineConfig, osImageURL string) (*m fips = true } - var appendIgn ign2types.Config + var mergedIgn ign3types.Config if configs[idx].Spec.Config.Raw == nil { - appendIgn = ign2types.Config{} + mergedIgn = ign3types.Config{} } else { - appendIgn, err = IgnParseWrapper(configs[idx].Spec.Config.Raw) + mergedIgn, err = ParseAndConvertConfig(configs[idx].Spec.Config.Raw) if err != nil { return nil, err } } - outIgn = ign2.Append(outIgn, appendIgn) + outIgn = ign3.Merge(outIgn, mergedIgn) } rawOutIgn, err := json.Marshal(outIgn) if err != nil { @@ -119,7 +119,7 @@ func MergeMachineConfigs(configs []*mcfgv1.MachineConfig, osImageURL string) (*m // PointerConfig generates the stub ignition for the machine to boot properly // NOTE: If you change this, you also need to change the pointer configuration in openshift/installer, see // https://github.com/openshift/installer/blob/master/pkg/asset/ignition/machine/node.go#L20 -func PointerConfig(ignitionHost string, rootCA []byte) (ign2types.Config, error) { +func PointerConfig(ignitionHost string, rootCA []byte) (ign3types.Config, error) { configSourceURL := &url.URL{ Scheme: "https", Host: ignitionHost, @@ -128,20 +128,21 @@ func PointerConfig(ignitionHost string, rootCA []byte) (ign2types.Config, error) // we do decoding here as curly brackets are escaped to %7B and breaks golang's templates ignitionHostTmpl, err := url.QueryUnescape(configSourceURL.String()) if err != nil { - return ign2types.Config{}, err - } - return ign2types.Config{ - Ignition: ign2types.Ignition{ - Version: ign2types.MaxVersion.String(), - Config: ign2types.IgnitionConfig{ - Append: []ign2types.ConfigReference{{ - Source: ignitionHostTmpl, + return ign3types.Config{}, err + } + CASource := dataurl.EncodeBytes(rootCA) + return ign3types.Config{ + Ignition: ign3types.Ignition{ + Version: ign3types.MaxVersion.String(), + Config: ign3types.IgnitionConfig{ + Merge: []ign3types.Resource{{ + Source: &ignitionHostTmpl, }}, }, - Security: ign2types.Security{ - TLS: ign2types.TLS{ - CertificateAuthorities: []ign2types.CaReference{{ - Source: dataurl.EncodeBytes(rootCA), + Security: ign3types.Security{ + TLS: ign3types.TLS{ + CertificateAuthorities: []ign3types.Resource{{ + Source: &CASource, }}, }, }, @@ -150,10 +151,10 @@ func PointerConfig(ignitionHost string, rootCA []byte) (ign2types.Config, error) } // NewIgnConfig returns an empty ignition config with version set as latest version -func NewIgnConfig() ign2types.Config { - return ign2types.Config{ - Ignition: ign2types.Ignition{ - Version: ign2types.MaxVersion.String(), +func NewIgnConfig() ign3types.Config { + return ign3types.Config{ + Ignition: ign3types.Ignition{ + Version: ign3types.MaxVersion.String(), }, } } @@ -165,10 +166,17 @@ func WriteTerminationError(err error) { glog.Fatal(msg) } -// ConvertRawExtIgnition2to3 converts a RawExtension containing Ignition spec v2.2 config -// into a RawExtension containing Ignition spec v3.1 config -func ConvertRawExtIgnition2to3(inRawExtIgnV2 *runtime.RawExtension) (runtime.RawExtension, error) { - ignCfg, rpt, err := ign2.Parse(inRawExtIgnV2.Raw) +// ConvertRawExtIgnitionToV3 ensures that the Ignition config in +// the RawExtension is spec v3.1, or translates to it. +func ConvertRawExtIgnitionToV3(inRawExtIgn *runtime.RawExtension) (runtime.RawExtension, error) { + // This function is only used by the MCServer so we don't need to consider v3.0 + _, rptV3, errV3 := ign3.Parse(inRawExtIgn.Raw) + if errV3 == nil && !rptV3.IsFatal() { + // The rawExt is already on V3.1, no need to translate + return *inRawExtIgn, nil + } + + ignCfg, rpt, err := ign2.Parse(inRawExtIgn.Raw) if err != nil || rpt.IsFatal() { return runtime.RawExtension{}, errors.Errorf("parsing Ignition config spec v2.2 failed with error: %v\nReport: %v", err, rpt) } @@ -188,6 +196,36 @@ func ConvertRawExtIgnition2to3(inRawExtIgnV2 *runtime.RawExtension) (runtime.Raw return outRawExt, nil } +// ConvertRawExtIgnitionToV2 ensures that the Ignition config in +// the RawExtension is spec v2.2, or translates to it. +func ConvertRawExtIgnitionToV2(inRawExtIgn *runtime.RawExtension) (runtime.RawExtension, error) { + _, rptV2, errV2 := ign2.Parse(inRawExtIgn.Raw) + if errV2 == nil && !rptV2.IsFatal() { + // The rawExt is already on V2.2, no need to translate + return *inRawExtIgn, nil + } + + ignCfg, rpt, err := ign3.Parse(inRawExtIgn.Raw) + if err != nil || rpt.IsFatal() { + return runtime.RawExtension{}, errors.Errorf("parsing Ignition config spec v3.1 failed with error: %v\nReport: %v", err, rpt) + } + + converted2, err := convertIgnition3to2(ignCfg) + if err != nil { + return runtime.RawExtension{}, errors.Errorf("failed to convert config from spec v3.1 to v2.2: %v", err) + } + + outIgnV2, err := json.Marshal(converted2) + if err != nil { + return runtime.RawExtension{}, errors.Errorf("failed to marshal converted config: %v", err) + } + + outRawExt := runtime.RawExtension{} + outRawExt.Raw = outIgnV2 + + return outRawExt, nil +} + // convertIgnition2to3 takes an ignition spec v2.2 config and returns a v3.1 config func convertIgnition2to3(ign2config ign2types.Config) (ign3types.Config, error) { // only support writing to root file system @@ -265,36 +303,24 @@ func ValidateMachineConfig(cfg mcfgv1.MachineConfigSpec) error { return nil } -// IgnParseWrapper parses rawIgn for v2.2, v3.1 and v3.0 Ignition configs and returns -// a spec v2.2 or an error -// This wrapper is necessary since each version uses a different parser. -func IgnParseWrapper(rawIgn []byte) (ign2types.Config, error) { +// IgnParseWrapper parses rawIgn for both V2 and V3 ignition configs and returns +// a V2 or V3 Config or an error. This wrapper is necessary since V2 and V3 use different parsers. +func IgnParseWrapper(rawIgn []byte) (interface{}, error) { ignCfg, rpt, err := ign2.Parse(rawIgn) if err == nil && !rpt.IsFatal() { - // this is an ign spec v2.2 config that was successfully parsed + // this is an ignv2cfg that was successfully parsed return ignCfg, nil } if err.Error() == ign2error.ErrUnknownVersion.Error() { - // check to see if this is ign config spec v3.1 ignCfgV3, rptV3, errV3 := ign3.Parse(rawIgn) if errV3 == nil && !rptV3.IsFatal() { - convertedIgnV2, err := convertIgnition3to2(ignCfgV3) - if err != nil { - return ign2types.Config{}, errors.Errorf("failed to convert Ignition config spec v3 to v2: %v", err) - } - return convertedIgnV2, nil - - } else if errV3.Error() == ign3error.ErrUnknownVersion.Error() { - // unlike spec v2.x parsers, v3.x parsers aren't chained by default, - // so try with spec v3.0 parser as well + return ignCfgV3, nil + } + // unlike spec v2 parsers, v3 parsers aren't chained by default so we need to try parsing as spec v3.0 as well + if errV3.Error() == ign3error.ErrUnknownVersion.Error() { ignCfgV3_0, rptV3_0, errV3_0 := ign3_0.Parse(rawIgn) if errV3_0 == nil && !rptV3_0.IsFatal() { - convertedIgnV2, err := convertIgnition3to2(translate3.Translate(ignCfgV3_0)) - if err != nil { - return ign2types.Config{}, errors.Errorf("failed to convert Ignition config spec v3 to v2: %v", err) - } - - return convertedIgnV2, nil + return translate3.Translate(ignCfgV3_0), nil } return ign2types.Config{}, errors.Errorf("parsing Ignition config spec v3.0 failed with error: %v\nReport: %v", errV3_0, rptV3_0) @@ -306,11 +332,102 @@ func IgnParseWrapper(rawIgn []byte) (ign2types.Config, error) { return ign2types.Config{}, errors.Errorf("parsing Ignition config spec v2 failed with error: %v\nReport: %v", err, rpt) } +// ParseAndConvertConfig parses rawIgn for both V2 and V3 ignition configs and returns +// a V3 or an error. +func ParseAndConvertConfig(rawIgn []byte) (ign3types.Config, error) { + ignconfigi, err := IgnParseWrapper(rawIgn) + if err != nil { + return ign3types.Config{}, errors.Wrapf(err, "failed to parse Ignition config") + } + + switch typedConfig := ignconfigi.(type) { + case ign3types.Config: + return ignconfigi.(ign3types.Config), nil + case ign2types.Config: + ignconfv2 := removeIgnDuplicateFilesAndUnits(ignconfigi.(ign2types.Config)) + convertedIgnV3, err := convertIgnition2to3(ignconfv2) + if err != nil { + return ign3types.Config{}, errors.Wrapf(err, "failed to convert Ignition config spec v2 to v3") + } + return convertedIgnV3, nil + default: + return ign3types.Config{}, errors.Errorf("unexpected type for ignition config: %v", typedConfig) + } +} + +// Function to remove duplicated files/units from a V2 MC, since the translator +// (and ignition spec V3) does not allow for duplicated entries in one MC. +// This should really not change the actual final behaviour, since it keeps +// ordering into consideration and has contents from the highest alphanumeric +// MC's final version of a file. +// Note: +// Append is not considered since we do not allow for appending +// Units have one exception: dropins are concat'ed + +func removeIgnDuplicateFilesAndUnits(ignConfig ign2types.Config) ign2types.Config { + + files := ignConfig.Storage.Files + units := ignConfig.Systemd.Units + + filePathMap := map[string]bool{} + var outFiles []ign2types.File + for i := len(files) - 1; i >= 0; i-- { + // We do not actually support to other filesystems so we make the assumption that there is only 1 here + path := files[i].Path + if _, isDup := filePathMap[path]; isDup { + continue + } + outFiles = append(outFiles, files[i]) + filePathMap[path] = true + } + + unitNameMap := map[string]bool{} + var outUnits []ign2types.Unit + for i := len(units) - 1; i >= 0; i-- { + unitName := units[i].Name + if _, isDup := unitNameMap[unitName]; isDup { + // this is a duplicated unit by name, so let's check for the dropins and append them + if len(units[i].Dropins) > 0 { + for j := range outUnits { + if outUnits[j].Name == unitName { + // outUnits[j] is the highest priority entry with this unit name + // now loop over the new unit's dropins and append it if the name + // isn't duplicated in the existing unit's dropins + for _, newDropin := range units[i].Dropins { + hasExistingDropin := false + for _, existingDropins := range outUnits[j].Dropins { + if existingDropins.Name == newDropin.Name { + hasExistingDropin = true + break + } + } + if !hasExistingDropin { + outUnits[j].Dropins = append(outUnits[j].Dropins, newDropin) + } + } + continue + } + } + glog.V(2).Infof("Found duplicate unit %v, appending dropin section", unitName) + } + continue + } + outUnits = append(outUnits, units[i]) + unitNameMap[unitName] = true + } + + // outFiles and outUnits should now have all duplication removed + ignConfig.Storage.Files = outFiles + ignConfig.Systemd.Units = outUnits + + return ignConfig +} + // TranspileCoreOSConfigToIgn transpiles Fedora CoreOS config to ignition -// internally it transpiles to Ign spec v3 config and translates to spec v2 -func TranspileCoreOSConfigToIgn(files, units []string) (*ign2types.Config, error) { - var ctCfg fcctbase.Config +// internally it transpiles to Ign spec v3 config +func TranspileCoreOSConfigToIgn(files, units []string) (*ign3types.Config, error) { overwrite := true + outConfig := ign3types.Config{} // Convert data to Ignition resources for _, d := range files { f := new(fcctbase.File) @@ -320,7 +437,14 @@ func TranspileCoreOSConfigToIgn(files, units []string) (*ign2types.Config, error f.Overwrite = &overwrite // Add the file to the config + var ctCfg fcctbase.Config ctCfg.Storage.Files = append(ctCfg.Storage.Files, *f) + ign3_0config, tSet, err := ctCfg.ToIgn3_0() + if err != nil { + return nil, fmt.Errorf("failed to transpile config to Ignition config %s\nTranslation set: %v", err, tSet) + } + ign3_1config := translate3.Translate(ign3_0config) + outConfig = ign3.Merge(outConfig, ign3_1config) } for _, d := range units { @@ -330,23 +454,17 @@ func TranspileCoreOSConfigToIgn(files, units []string) (*ign2types.Config, error } // Add the unit to the config + var ctCfg fcctbase.Config ctCfg.Systemd.Units = append(ctCfg.Systemd.Units, *u) + ign3_0config, tSet, err := ctCfg.ToIgn3_0() + if err != nil { + return nil, fmt.Errorf("failed to transpile config to Ignition config %s\nTranslation set: %v", err, tSet) + } + ign3_1config := translate3.Translate(ign3_0config) + outConfig = ign3.Merge(outConfig, ign3_1config) } - ign3_0config, tSet, err := ctCfg.ToIgn3_0() - if err != nil { - return nil, fmt.Errorf("failed to transpile config to Ignition config %s\nTranslation set: %v", err, tSet) - } - - // Workaround to get a v3.1 config - ign3config := translate3.Translate(ign3_0config) - - converted2, errV3 := convertIgnition3to2(ign3config) - if errV3 != nil { - return nil, errors.Errorf("converting Ignition spec v3 config to v2 failed with error: %v", errV3) - } - - return &converted2, nil + return &outConfig, nil } // MachineConfigFromIgnConfig creates a MachineConfig with the provided Ignition config diff --git a/pkg/controller/common/helpers_test.go b/pkg/controller/common/helpers_test.go index 5d657a38e2..4524483b10 100644 --- a/pkg/controller/common/helpers_test.go +++ b/pkg/controller/common/helpers_test.go @@ -57,14 +57,14 @@ func TestValidateIgnition(t *testing.T) { func TestConvertIgnition2to3(t *testing.T) { // Make a new Ign spec v2 config - testIgnCfgV2 := ign2types.Config{} + testIgn2Config := ign2types.Config{} tempUser := ign2types.PasswdUser{Name: "core", SSHAuthorizedKeys: []ign2types.SSHAuthorizedKey{"5678", "abc"}} - testIgnCfgV2.Passwd.Users = []ign2types.PasswdUser{tempUser} - testIgnCfgV2.Ignition.Version = "2.2.0" - isValid := ValidateIgnition(testIgnCfgV2) + testIgn2Config.Passwd.Users = []ign2types.PasswdUser{tempUser} + testIgn2Config.Ignition.Version = "2.2.0" + isValid := ValidateIgnition(testIgn2Config) require.Nil(t, isValid) - convertedIgn, err := convertIgnition2to3(testIgnCfgV2) + convertedIgn, err := convertIgnition2to3(testIgn2Config) require.Nil(t, err) assert.IsType(t, ign3types.Config{}, convertedIgn) isValid3 := ValidateIgnition(convertedIgn) @@ -88,7 +88,7 @@ func TestConvertIgnition3to2(t *testing.T) { require.Nil(t, isValid2) } -func TestIgnParseWrapper(t *testing.T) { +func TestParseAndConvert(t *testing.T) { // Make a new Ign3.1 config testIgn3Config := ign3types.Config{} tempUser := ign3types.PasswdUser{Name: "core", SSHAuthorizedKeys: []ign3types.SSHAuthorizedKey{"5678", "abc"}} @@ -96,38 +96,157 @@ func TestIgnParseWrapper(t *testing.T) { testIgn3Config.Ignition.Version = "3.1.0" // Make a Ign2 comp config - testIgn2Config := NewIgnConfig() + testIgn2Config := ign2types.Config{} tempUser2 := ign2types.PasswdUser{Name: "core", SSHAuthorizedKeys: []ign2types.SSHAuthorizedKey{"5678", "abc"}} testIgn2Config.Passwd.Users = []ign2types.PasswdUser{tempUser2} + testIgn2Config.Ignition.Version = "2.2.0" // turn v2.2 config into a raw []byte rawIgn := helpers.MarshalOrDie(testIgn2Config) // check that it was parsed successfully - convertedIgn, err := IgnParseWrapper(rawIgn) + convertedIgn, err := ParseAndConvertConfig(rawIgn) require.Nil(t, err) - assert.Equal(t, testIgn2Config, convertedIgn) + assert.Equal(t, testIgn3Config, convertedIgn) // turn v3.1 config into a raw []byte rawIgn = helpers.MarshalOrDie(testIgn3Config) // check that it was parsed successfully - convertedIgn, err = IgnParseWrapper(rawIgn) + convertedIgn, err = ParseAndConvertConfig(rawIgn) + require.Nil(t, err) + assert.Equal(t, testIgn3Config, convertedIgn) + + // Make a valid Ign 3.1 cfg + testIgn3Config.Ignition.Version = "3.1.0" + // turn it into a raw []byte + rawIgn = helpers.MarshalOrDie(testIgn3Config) + // check that it was parsed successfully + convertedIgn, err = ParseAndConvertConfig(rawIgn) require.Nil(t, err) - assert.Equal(t, testIgn2Config, convertedIgn) + assert.Equal(t, testIgn3Config, convertedIgn) // Make a valid Ign 3.0 cfg testIgn3Config.Ignition.Version = "3.0.0" // turn it into a raw []byte rawIgn = helpers.MarshalOrDie(testIgn3Config) - // check that it was parsed successfully - convertedIgn, err = IgnParseWrapper(rawIgn) + // check that it was parsed successfully back to 3.1 + convertedIgn, err = ParseAndConvertConfig(rawIgn) require.Nil(t, err) - assert.Equal(t, testIgn2Config, convertedIgn) + testIgn3Config.Ignition.Version = "3.1.0" + assert.Equal(t, testIgn3Config, convertedIgn) // Make a bad Ign3 cfg testIgn3Config.Ignition.Version = "21.0.0" rawIgn = helpers.MarshalOrDie(testIgn3Config) // check that it failed since this is an invalid cfg - convertedIgn, err = IgnParseWrapper(rawIgn) + convertedIgn, err = ParseAndConvertConfig(rawIgn) require.NotNil(t, err) - assert.Equal(t, ign2types.Config{}, convertedIgn) + assert.Equal(t, ign3types.Config{}, convertedIgn) +} + +func TestRemoveIgnDuplicateFilesAndUnits(t *testing.T) { + mode := 420 + testDataOld := "data:,old" + testDataNew := "data:,new" + testIgn2Config := ign2types.Config{} + + // file test, add a duplicate file and see if the newest one is preserved + fileOld := ign2types.File{ + Node: ign2types.Node{ + Filesystem: "root", Path: "/etc/testfileconfig", + }, + FileEmbedded1: ign2types.FileEmbedded1{ + Contents: ign2types.FileContents{ + Source: testDataOld, + }, + Mode: &mode, + }, + } + testIgn2Config.Storage.Files = append(testIgn2Config.Storage.Files, fileOld) + + fileNew := ign2types.File{ + Node: ign2types.Node{ + Filesystem: "root", Path: "/etc/testfileconfig", + }, + FileEmbedded1: ign2types.FileEmbedded1{ + Contents: ign2types.FileContents{ + Source: testDataNew, + }, + Mode: &mode, + }, + } + testIgn2Config.Storage.Files = append(testIgn2Config.Storage.Files, fileNew) + + // unit test, add three units and three dropins with the same name as follows: + // unitOne: + // contents: old + // dropin: + // name: one + // contents: old + // unitTwo: + // dropin: + // name: one + // contents: new + // unitThree: + // contents: new + // dropin: + // name: two + // contents: new + // Which should result in: + // unitFinal: + // contents: new + // dropin: + // - name: one + // contents: new + // - name: two + // contents: new + // + unitName := "testUnit" + dropinNameOne := "one" + dropinNameTwo := "two" + dropinOne := ign2types.SystemdDropin{ + Contents: testDataOld, + Name: dropinNameOne, + } + dropinTwo := ign2types.SystemdDropin{ + Contents: testDataNew, + Name: dropinNameOne, + } + dropinThree := ign2types.SystemdDropin{ + Contents: testDataNew, + Name: dropinNameTwo, + } + + unitOne := ign2types.Unit{ + Contents: testDataOld, + Name: unitName, + } + unitOne.Dropins = append(unitOne.Dropins, dropinOne) + testIgn2Config.Systemd.Units = append(testIgn2Config.Systemd.Units, unitOne) + + unitTwo := ign2types.Unit{ + Name: unitName, + } + unitTwo.Dropins = append(unitTwo.Dropins, dropinTwo) + testIgn2Config.Systemd.Units = append(testIgn2Config.Systemd.Units, unitTwo) + + unitThree := ign2types.Unit{ + Contents: testDataNew, + Name: unitName, + } + unitThree.Dropins = append(unitThree.Dropins, dropinThree) + testIgn2Config.Systemd.Units = append(testIgn2Config.Systemd.Units, unitThree) + + convertedIgn2Config := removeIgnDuplicateFilesAndUnits(testIgn2Config) + + expectedIgn2Config := ign2types.Config{} + expectedIgn2Config.Storage.Files = append(expectedIgn2Config.Storage.Files, fileNew) + unitExpected := ign2types.Unit{ + Contents: testDataNew, + Name: unitName, + } + unitExpected.Dropins = append(unitExpected.Dropins, dropinThree) + unitExpected.Dropins = append(unitExpected.Dropins, dropinTwo) + expectedIgn2Config.Systemd.Units = append(expectedIgn2Config.Systemd.Units, unitExpected) + + assert.Equal(t, expectedIgn2Config, convertedIgn2Config) } diff --git a/pkg/controller/container-runtime-config/container_runtime_config_controller.go b/pkg/controller/container-runtime-config/container_runtime_config_controller.go index c0e51f6a70..dddda3eaad 100644 --- a/pkg/controller/container-runtime-config/container_runtime_config_controller.go +++ b/pkg/controller/container-runtime-config/container_runtime_config_controller.go @@ -7,7 +7,7 @@ import ( "time" "github.com/clarketm/json" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" "github.com/golang/glog" apicfgv1 "github.com/openshift/api/config/v1" apioperatorsv1alpha1 "github.com/openshift/api/operator/v1alpha1" @@ -378,7 +378,7 @@ func (ctrl *Controller) handleImgErr(err error, key interface{}) { } // generateOriginalContainerRuntimeConfigs returns rendered default storage, registries and policy config files -func generateOriginalContainerRuntimeConfigs(templateDir string, cc *mcfgv1.ControllerConfig, role string) (*igntypes.File, *igntypes.File, *igntypes.File, error) { +func generateOriginalContainerRuntimeConfigs(templateDir string, cc *mcfgv1.ControllerConfig, role string) (*ign3types.File, *ign3types.File, *ign3types.File, error) { // Render the default templates rc := &mtmpl.RenderConfig{ControllerConfigSpec: &cc.Spec} generatedConfigs, err := mtmpl.GenerateMachineConfigsForRole(rc, role, templateDir) @@ -387,7 +387,7 @@ func generateOriginalContainerRuntimeConfigs(templateDir string, cc *mcfgv1.Cont } // Find generated storage.conf, registries.conf, and policy.json var ( - config, gmcStorageConfig, gmcRegistriesConfig, gmcPolicyJSON *igntypes.File + config, gmcStorageConfig, gmcRegistriesConfig, gmcPolicyJSON *ign3types.File errStorage, errRegistries, errPolicy error ) // Find storage config @@ -596,8 +596,8 @@ func (ctrl *Controller) syncContainerRuntimeConfig(key string) error { // mergeConfigChanges retrieves the original/default config data from the templates, decodes it and merges in the changes given by the Custom Resource. // It then encodes the new data and returns it. -func (ctrl *Controller) mergeConfigChanges(origFile *igntypes.File, cfg *mcfgv1.ContainerRuntimeConfig, update updateConfigFunc) ([]byte, error) { - dataURL, err := dataurl.DecodeString(origFile.Contents.Source) +func (ctrl *Controller) mergeConfigChanges(origFile *ign3types.File, cfg *mcfgv1.ContainerRuntimeConfig, update updateConfigFunc) ([]byte, error) { + dataURL, err := dataurl.DecodeString(*origFile.Contents.Source) if err != nil { return nil, ctrl.syncStatusOnly(cfg, err, "could not decode original Container Runtime config: %v", err) } @@ -741,7 +741,7 @@ func (ctrl *Controller) syncImageConfig(key string) error { } func registriesConfigIgnition(templateDir string, controllerConfig *mcfgv1.ControllerConfig, role string, - insecureRegs, blockedRegs, allowedRegs []string, icspRules []*apioperatorsv1alpha1.ImageContentSourcePolicy) (*igntypes.Config, error) { + insecureRegs, blockedRegs, allowedRegs []string, icspRules []*apioperatorsv1alpha1.ImageContentSourcePolicy) (*ign3types.Config, error) { var ( registriesTOML []byte @@ -755,7 +755,7 @@ func registriesConfigIgnition(templateDir string, controllerConfig *mcfgv1.Contr } if insecureRegs != nil || blockedRegs != nil || len(icspRules) != 0 { - dataURL, err := dataurl.DecodeString(originalRegistriesIgn.Contents.Source) + dataURL, err := dataurl.DecodeString(*originalRegistriesIgn.Contents.Source) if err != nil { return nil, fmt.Errorf("could not decode original registries config: %v", err) } @@ -765,7 +765,7 @@ func registriesConfigIgnition(templateDir string, controllerConfig *mcfgv1.Contr } } if blockedRegs != nil || allowedRegs != nil { - dataURL, err := dataurl.DecodeString(originalPolicyIgn.Contents.Source) + dataURL, err := dataurl.DecodeString(*originalPolicyIgn.Contents.Source) if err != nil { return nil, fmt.Errorf("could not decode original policy json: %v", err) } @@ -934,7 +934,7 @@ func (ctrl *Controller) isUpdatingFromOldCRIOConf(cfg *mcfgv1.ContainerRuntimeCo return false, fmt.Errorf("could not get mc with name %q: %v", managedKey, err) } if mc.Spec.Config.Raw != nil { - conf, err := ctrlcommon.IgnParseWrapper(mc.Spec.Config.Raw) + conf, err := ctrlcommon.ParseAndConvertConfig(mc.Spec.Config.Raw) if err != nil { return false, fmt.Errorf("error parsing ignition: %v", err) } diff --git a/pkg/controller/container-runtime-config/container_runtime_config_controller_test.go b/pkg/controller/container-runtime-config/container_runtime_config_controller_test.go index d763747c35..5cbc3d8dc9 100644 --- a/pkg/controller/container-runtime-config/container_runtime_config_controller_test.go +++ b/pkg/controller/container-runtime-config/container_runtime_config_controller_test.go @@ -26,7 +26,7 @@ import ( "k8s.io/client-go/tools/cache" "k8s.io/client-go/tools/record" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" apicfgv1 "github.com/openshift/api/config/v1" apioperatorsv1alpha1 "github.com/openshift/api/operator/v1alpha1" fakeconfigv1client "github.com/openshift/client-go/config/clientset/versioned/fake" @@ -358,7 +358,7 @@ func verifyRegistriesConfigAndPolicyJSONContents(t *testing.T, mc *mcfgv1.Machin require.NoError(t, err) assert.Equal(t, mcName, mc.ObjectMeta.Name) - ignCfg, err := ctrlcommon.IgnParseWrapper(mc.Spec.Config.Raw) + ignCfg, err := ctrlcommon.ParseAndConvertConfig(mc.Spec.Config.Raw) require.NoError(t, err) if verifyPolicyJSON { // If there is a change to the policy.json file then there will be 2 files @@ -371,7 +371,7 @@ func verifyRegistriesConfigAndPolicyJSONContents(t *testing.T, mc *mcfgv1.Machin regfile = ignCfg.Storage.Files[1] } assert.Equal(t, registriesConfigPath, regfile.Node.Path) - registriesConf, err := dataurl.DecodeString(regfile.Contents.Source) + registriesConf, err := dataurl.DecodeString(*regfile.Contents.Source) require.NoError(t, err) assert.Equal(t, string(expectedRegistriesConf), string(registriesConf.Data)) @@ -386,7 +386,7 @@ func verifyRegistriesConfigAndPolicyJSONContents(t *testing.T, mc *mcfgv1.Machin policyfile = ignCfg.Storage.Files[0] } assert.Equal(t, policyConfigPath, policyfile.Node.Path) - policyJSON, err := dataurl.DecodeString(policyfile.Contents.Source) + policyJSON, err := dataurl.DecodeString(*policyfile.Contents.Source) require.NoError(t, err) assert.Equal(t, string(expectedPolicyJSON), string(policyJSON.Data)) } @@ -409,7 +409,7 @@ func TestContainerRuntimeConfigCreate(t *testing.T) { mcp2.ObjectMeta.Labels["custom-crio"] = "storage-config" ctrcfg1 := newContainerRuntimeConfig("set-log-level", &mcfgv1.ContainerRuntimeConfiguration{LogLevel: "debug", LogSizeMax: resource.MustParse("9k"), OverlaySize: resource.MustParse("3G")}, metav1.AddLabelToSelector(&metav1.LabelSelector{}, "custom-crio", "my-config")) ctrCfgKey, _ := getManagedKeyCtrCfg(mcp, nil) - mcs1 := helpers.NewMachineConfig(getManagedKeyCtrCfgDeprecated(mcp), map[string]string{"node-role": "master"}, "dummy://", []igntypes.File{{}}) + mcs1 := helpers.NewMachineConfig(getManagedKeyCtrCfgDeprecated(mcp), map[string]string{"node-role": "master"}, "dummy://", []ign3types.File{{}}) mcs2 := mcs1.DeepCopy() mcs2.Name = ctrCfgKey @@ -446,7 +446,7 @@ func TestContainerRuntimeConfigUpdate(t *testing.T) { mcp2.ObjectMeta.Labels["custom-crio"] = "storage-config" ctrcfg1 := newContainerRuntimeConfig("set-log-level", &mcfgv1.ContainerRuntimeConfiguration{LogLevel: "debug", LogSizeMax: resource.MustParse("9k"), OverlaySize: resource.MustParse("3G")}, metav1.AddLabelToSelector(&metav1.LabelSelector{}, "custom-crio", "my-config")) keyCtrCfg, _ := getManagedKeyCtrCfg(mcp, nil) - mcs := helpers.NewMachineConfig(getManagedKeyCtrCfgDeprecated(mcp), map[string]string{"node-role": "master"}, "dummy://", []igntypes.File{{}}) + mcs := helpers.NewMachineConfig(getManagedKeyCtrCfgDeprecated(mcp), map[string]string{"node-role": "master"}, "dummy://", []ign3types.File{{}}) mcsUpdate := mcs.DeepCopy() mcsUpdate.Name = keyCtrCfg @@ -527,8 +527,8 @@ func TestImageConfigCreate(t *testing.T) { cvcfg1 := newClusterVersionConfig("version", "test.io/myuser/myimage:test") keyReg1, _ := getManagedKeyReg(mcp, nil) keyReg2, _ := getManagedKeyReg(mcp2, nil) - mcs1 := helpers.NewMachineConfig(keyReg1, map[string]string{"node-role": "master"}, "dummy://", []igntypes.File{{}}) - mcs2 := helpers.NewMachineConfig(keyReg2, map[string]string{"node-role": "worker"}, "dummy://", []igntypes.File{{}}) + mcs1 := helpers.NewMachineConfig(keyReg1, map[string]string{"node-role": "master"}, "dummy://", []ign3types.File{{}}) + mcs2 := helpers.NewMachineConfig(keyReg2, map[string]string{"node-role": "worker"}, "dummy://", []ign3types.File{{}}) f.ccLister = append(f.ccLister, cc) f.mcpLister = append(f.mcpLister, mcp) @@ -569,8 +569,8 @@ func TestImageConfigUpdate(t *testing.T) { cvcfg1 := newClusterVersionConfig("version", "test.io/myuser/myimage:test") keyReg1, _ := getManagedKeyReg(mcp, nil) keyReg2, _ := getManagedKeyReg(mcp2, nil) - mcs1 := helpers.NewMachineConfig(getManagedKeyRegDeprecated(mcp), map[string]string{"node-role": "master"}, "dummy://", []igntypes.File{{}}) - mcs2 := helpers.NewMachineConfig(getManagedKeyRegDeprecated(mcp2), map[string]string{"node-role": "worker"}, "dummy://", []igntypes.File{{}}) + mcs1 := helpers.NewMachineConfig(getManagedKeyRegDeprecated(mcp), map[string]string{"node-role": "master"}, "dummy://", []ign3types.File{{}}) + mcs2 := helpers.NewMachineConfig(getManagedKeyRegDeprecated(mcp2), map[string]string{"node-role": "worker"}, "dummy://", []ign3types.File{{}}) mcs1Update := mcs1.DeepCopy() mcs2Update := mcs2.DeepCopy() mcs1Update.Name = keyReg1 @@ -665,8 +665,8 @@ func TestICSPUpdate(t *testing.T) { cvcfg1 := newClusterVersionConfig("version", "test.io/myuser/myimage:test") keyReg1, _ := getManagedKeyReg(mcp, nil) keyReg2, _ := getManagedKeyReg(mcp2, nil) - mcs1 := helpers.NewMachineConfig(getManagedKeyRegDeprecated(mcp), map[string]string{"node-role": "master"}, "dummy://", []igntypes.File{{}}) - mcs2 := helpers.NewMachineConfig(getManagedKeyRegDeprecated(mcp2), map[string]string{"node-role": "worker"}, "dummy://", []igntypes.File{{}}) + mcs1 := helpers.NewMachineConfig(getManagedKeyRegDeprecated(mcp), map[string]string{"node-role": "master"}, "dummy://", []ign3types.File{{}}) + mcs2 := helpers.NewMachineConfig(getManagedKeyRegDeprecated(mcp2), map[string]string{"node-role": "worker"}, "dummy://", []ign3types.File{{}}) icsp := newICSP("built-in", []apioperatorsv1alpha1.RepositoryDigestMirrors{ {Source: "built-in-source.example.com", Mirrors: []string{"built-in-mirror.example.com"}}, }) diff --git a/pkg/controller/container-runtime-config/helpers.go b/pkg/controller/container-runtime-config/helpers.go index 8625e02573..ba86d82542 100644 --- a/pkg/controller/container-runtime-config/helpers.go +++ b/pkg/controller/container-runtime-config/helpers.go @@ -12,7 +12,7 @@ import ( "github.com/containers/image/pkg/sysregistriesv2" signature "github.com/containers/image/signature" storageconfig "github.com/containers/storage/pkg/config" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" "github.com/golang/glog" apicfgv1 "github.com/openshift/api/config/v1" apioperatorsv1alpha1 "github.com/openshift/api/operator/v1alpha1" @@ -100,9 +100,10 @@ type updateConfigFunc func(data []byte, internal *mcfgv1.ContainerRuntimeConfigu // createNewIgnition takes a map where the key is the path of the file, and the value is the // new data in the form of a byte array. The function returns the ignition config with the // updated data. -func createNewIgnition(configs []generatedConfigFile) igntypes.Config { +func createNewIgnition(configs []generatedConfigFile) ign3types.Config { tempIgnConfig := ctrlcommon.NewIgnConfig() mode := 0644 + overwrite := true // Create ignitions for _, ignConf := range configs { // If the file is not included, the data will be nil so skip over @@ -111,15 +112,16 @@ func createNewIgnition(configs []generatedConfigFile) igntypes.Config { } configdu := dataurl.New(ignConf.data, "text/plain") configdu.Encoding = dataurl.EncodingASCII - configTempFile := igntypes.File{ - Node: igntypes.Node{ - Filesystem: "root", - Path: ignConf.filePath, + configduStr := configdu.String() + configTempFile := ign3types.File{ + Node: ign3types.Node{ + Path: ignConf.filePath, + Overwrite: &overwrite, }, - FileEmbedded1: igntypes.FileEmbedded1{ + FileEmbedded1: ign3types.FileEmbedded1{ Mode: &mode, - Contents: igntypes.FileContents{ - Source: configdu.String(), + Contents: ign3types.Resource{ + Source: &(configduStr), }, }, } @@ -129,8 +131,8 @@ func createNewIgnition(configs []generatedConfigFile) igntypes.Config { return tempIgnConfig } -func findStorageConfig(mc *mcfgv1.MachineConfig) (*igntypes.File, error) { - ignCfg, err := ctrlcommon.IgnParseWrapper(mc.Spec.Config.Raw) +func findStorageConfig(mc *mcfgv1.MachineConfig) (*ign3types.File, error) { + ignCfg, err := ctrlcommon.ParseAndConvertConfig(mc.Spec.Config.Raw) if err != nil { return nil, fmt.Errorf("parsing Storage Ignition config failed with error: %v", err) } @@ -143,8 +145,8 @@ func findStorageConfig(mc *mcfgv1.MachineConfig) (*igntypes.File, error) { return nil, fmt.Errorf("could not find Storage Config") } -func findRegistriesConfig(mc *mcfgv1.MachineConfig) (*igntypes.File, error) { - ignCfg, err := ctrlcommon.IgnParseWrapper(mc.Spec.Config.Raw) +func findRegistriesConfig(mc *mcfgv1.MachineConfig) (*ign3types.File, error) { + ignCfg, err := ctrlcommon.ParseAndConvertConfig(mc.Spec.Config.Raw) if err != nil { return nil, fmt.Errorf("parsing Registries Ignition config failed with error: %v", err) } @@ -156,8 +158,8 @@ func findRegistriesConfig(mc *mcfgv1.MachineConfig) (*igntypes.File, error) { return nil, fmt.Errorf("could not find Registries Config") } -func findPolicyJSON(mc *mcfgv1.MachineConfig) (*igntypes.File, error) { - ignCfg, err := ctrlcommon.IgnParseWrapper(mc.Spec.Config.Raw) +func findPolicyJSON(mc *mcfgv1.MachineConfig) (*ign3types.File, error) { + ignCfg, err := ctrlcommon.ParseAndConvertConfig(mc.Spec.Config.Raw) if err != nil { return nil, fmt.Errorf("parsing Policy JSON Ignition config failed with error: %v", err) } diff --git a/pkg/controller/kubelet-config/helpers.go b/pkg/controller/kubelet-config/helpers.go index cfa4a8b28f..a9f768bb85 100644 --- a/pkg/controller/kubelet-config/helpers.go +++ b/pkg/controller/kubelet-config/helpers.go @@ -5,7 +5,7 @@ import ( "encoding/json" "fmt" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" osev1 "github.com/openshift/api/config/v1" "github.com/vincent-petithory/dataurl" corev1 "k8s.io/api/core/v1" @@ -22,23 +22,25 @@ import ( mcfgclientset "github.com/openshift/machine-config-operator/pkg/generated/clientset/versioned" ) -func createNewKubeletIgnition(jsonConfig []byte) igntypes.Config { +func createNewKubeletIgnition(jsonConfig []byte) ign3types.Config { // Want the kubelet.conf file to have the pretty JSON formatting buf := new(bytes.Buffer) json.Indent(buf, jsonConfig, "", " ") mode := 0644 + overwrite := true du := dataurl.New(buf.Bytes(), "text/plain") du.Encoding = dataurl.EncodingASCII - tempFile := igntypes.File{ - Node: igntypes.Node{ - Filesystem: "root", - Path: "/etc/kubernetes/kubelet.conf", + duStr := du.String() + tempFile := ign3types.File{ + Node: ign3types.Node{ + Path: "/etc/kubernetes/kubelet.conf", + Overwrite: &overwrite, }, - FileEmbedded1: igntypes.FileEmbedded1{ + FileEmbedded1: ign3types.FileEmbedded1{ Mode: &mode, - Contents: igntypes.FileContents{ - Source: du.String(), + Contents: ign3types.Resource{ + Source: &(duStr), }, }, } @@ -57,8 +59,8 @@ func createNewDefaultFeatureGate() *osev1.FeatureGate { } } -func findKubeletConfig(mc *mcfgv1.MachineConfig) (*igntypes.File, error) { - ignCfg, err := ctrlcommon.IgnParseWrapper(mc.Spec.Config.Raw) +func findKubeletConfig(mc *mcfgv1.MachineConfig) (*ign3types.File, error) { + ignCfg, err := ctrlcommon.ParseAndConvertConfig(mc.Spec.Config.Raw) if err != nil { return nil, fmt.Errorf("parsing Kubelet Ignition config failed with error: %v", err) } diff --git a/pkg/controller/kubelet-config/kubelet_config_controller.go b/pkg/controller/kubelet-config/kubelet_config_controller.go index e495d0b7f4..f2d6862464 100644 --- a/pkg/controller/kubelet-config/kubelet_config_controller.go +++ b/pkg/controller/kubelet-config/kubelet_config_controller.go @@ -8,7 +8,7 @@ import ( "time" "github.com/clarketm/json" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" "github.com/golang/glog" "github.com/imdario/mergo" "github.com/vincent-petithory/dataurl" @@ -326,7 +326,7 @@ func (ctrl *Controller) handleFeatureErr(err error, key interface{}) { ctrl.featureQueue.AddAfter(key, 1*time.Minute) } -func (ctrl *Controller) generateOriginalKubeletConfig(role string) (*igntypes.File, error) { +func (ctrl *Controller) generateOriginalKubeletConfig(role string) (*ign3types.File, error) { cc, err := ctrl.ccLister.Get(ctrlcommon.ControllerConfigName) if err != nil { return nil, fmt.Errorf("could not get ControllerConfig %v", err) @@ -453,7 +453,7 @@ func (ctrl *Controller) syncKubeletConfig(key string) error { if err != nil { return ctrl.syncStatusOnly(cfg, err, "could not generate the original Kubelet config: %v", err) } - dataURL, err := dataurl.DecodeString(originalKubeletIgn.Contents.Source) + dataURL, err := dataurl.DecodeString(*originalKubeletIgn.Contents.Source) if err != nil { return ctrl.syncStatusOnly(cfg, err, "could not decode the original Kubelet source string: %v", err) } diff --git a/pkg/controller/kubelet-config/kubelet_config_controller_test.go b/pkg/controller/kubelet-config/kubelet_config_controller_test.go index f234ba3e43..a1cde34f99 100644 --- a/pkg/controller/kubelet-config/kubelet_config_controller_test.go +++ b/pkg/controller/kubelet-config/kubelet_config_controller_test.go @@ -25,7 +25,7 @@ import ( osev1 "github.com/openshift/api/config/v1" oseinformersv1 "github.com/openshift/client-go/config/informers/externalversions" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" oseconfigfake "github.com/openshift/client-go/config/clientset/versioned/fake" mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" ctrlcommon "github.com/openshift/machine-config-operator/pkg/controller/common" @@ -329,7 +329,7 @@ func TestKubeletConfigCreate(t *testing.T) { mcp2 := helpers.NewMachineConfigPool("worker", nil, helpers.WorkerSelector, "v0") kc1 := newKubeletConfig("smaller-max-pods", &kubeletconfigv1beta1.KubeletConfiguration{MaxPods: 100}, metav1.AddLabelToSelector(&metav1.LabelSelector{}, "kubeletType", "small-pods")) kubeletConfigKey, _ := getManagedKubeletConfigKey(mcp, nil) - mcs := helpers.NewMachineConfig(kubeletConfigKey, map[string]string{"node-role/master": ""}, "dummy://", []igntypes.File{{}}) + mcs := helpers.NewMachineConfig(kubeletConfigKey, map[string]string{"node-role/master": ""}, "dummy://", []ign3types.File{{}}) mcsDeprecated := mcs.DeepCopy() mcsDeprecated.Name = getManagedKubeletConfigKeyDeprecated(mcp) @@ -362,7 +362,7 @@ func TestKubeletConfigUpdates(t *testing.T) { mcp2 := helpers.NewMachineConfigPool("worker", nil, helpers.WorkerSelector, "v0") kc1 := newKubeletConfig("smaller-max-pods", &kubeletconfigv1beta1.KubeletConfiguration{MaxPods: 100}, metav1.AddLabelToSelector(&metav1.LabelSelector{}, "kubeletType", "small-pods")) kubeletConfigKey, _ := getManagedKubeletConfigKey(mcp, nil) - mcs := helpers.NewMachineConfig(kubeletConfigKey, map[string]string{"node-role/master": ""}, "dummy://", []igntypes.File{{}}) + mcs := helpers.NewMachineConfig(kubeletConfigKey, map[string]string{"node-role/master": ""}, "dummy://", []ign3types.File{{}}) mcsDeprecated := mcs.DeepCopy() mcsDeprecated.Name = getManagedKubeletConfigKeyDeprecated(mcp) @@ -638,7 +638,7 @@ func TestKubeletFeatureExists(t *testing.T) { mcp2 := helpers.NewMachineConfigPool("worker", nil, helpers.WorkerSelector, "v0") kc1 := newKubeletConfig("smaller-max-pods", &kubeletconfigv1beta1.KubeletConfiguration{MaxPods: 100}, metav1.AddLabelToSelector(&metav1.LabelSelector{}, "kubeletType", "small-pods")) kubeletConfigKey, _ := getManagedKubeletConfigKey(mcp, nil) - mcs := helpers.NewMachineConfig(kubeletConfigKey, map[string]string{"node-role/master": ""}, "dummy://", []igntypes.File{{}}) + mcs := helpers.NewMachineConfig(kubeletConfigKey, map[string]string{"node-role/master": ""}, "dummy://", []ign3types.File{{}}) mcsDeprecated := mcs.DeepCopy() mcsDeprecated.Name = getManagedKubeletConfigKeyDeprecated(mcp) diff --git a/pkg/controller/kubelet-config/kubelet_config_features.go b/pkg/controller/kubelet-config/kubelet_config_features.go index 7488e1146c..ecccc428c2 100644 --- a/pkg/controller/kubelet-config/kubelet_config_features.go +++ b/pkg/controller/kubelet-config/kubelet_config_features.go @@ -106,7 +106,7 @@ func (ctrl *Controller) syncFeatureHandler(key string) error { if err != nil { return err } - dataURL, err := dataurl.DecodeString(originalKubeletIgn.Contents.Source) + dataURL, err := dataurl.DecodeString(*originalKubeletIgn.Contents.Source) if err != nil { return err } diff --git a/pkg/controller/kubelet-config/kubelet_config_features_test.go b/pkg/controller/kubelet-config/kubelet_config_features_test.go index a943f7970e..57c762cae9 100644 --- a/pkg/controller/kubelet-config/kubelet_config_features_test.go +++ b/pkg/controller/kubelet-config/kubelet_config_features_test.go @@ -4,7 +4,7 @@ import ( "reflect" "testing" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" configv1 "github.com/openshift/api/config/v1" "github.com/vincent-petithory/dataurl" @@ -24,7 +24,7 @@ func TestFeatureGateDrift(t *testing.T) { if err != nil { t.Errorf("could not generate kubelet config from templates %v", err) } - dataURL, _ := dataurl.DecodeString(kubeletConfig.Contents.Source) + dataURL, _ := dataurl.DecodeString(*kubeletConfig.Contents.Source) originalKubeConfig, _ := decodeKubeletConfig(dataURL.Data) defaultFeatureGates, err := ctrl.generateFeatureMap(createNewDefaultFeatureGate()) if err != nil { @@ -47,8 +47,8 @@ func TestFeaturesDefault(t *testing.T) { mcp2 := helpers.NewMachineConfigPool("worker", nil, helpers.WorkerSelector, "v0") kubeletConfigKey1, _ := getManagedKubeletConfigKey(mcp, nil) kubeletConfigKey2, _ := getManagedKubeletConfigKey(mcp2, nil) - mcs := helpers.NewMachineConfig(kubeletConfigKey1, map[string]string{"node-role/master": ""}, "dummy://", []igntypes.File{{}}) - mcs2 := helpers.NewMachineConfig(kubeletConfigKey2, map[string]string{"node-role/worker": ""}, "dummy://", []igntypes.File{{}}) + mcs := helpers.NewMachineConfig(kubeletConfigKey1, map[string]string{"node-role/master": ""}, "dummy://", []ign3types.File{{}}) + mcs2 := helpers.NewMachineConfig(kubeletConfigKey2, map[string]string{"node-role/worker": ""}, "dummy://", []ign3types.File{{}}) mcsDeprecated := mcs.DeepCopy() mcsDeprecated.Name = getManagedFeaturesKeyDeprecated(mcp) mcs2Deprecated := mcs2.DeepCopy() diff --git a/pkg/controller/render/render_controller.go b/pkg/controller/render/render_controller.go index 5c579c7b08..92b0f7cb35 100644 --- a/pkg/controller/render/render_controller.go +++ b/pkg/controller/render/render_controller.go @@ -535,6 +535,7 @@ func generateRenderedMachineConfig(pool *mcfgv1.MachineConfigPool, configs []*mc return nil, err } } + merged, err := ctrlcommon.MergeMachineConfigs(configs, cconfig.Spec.OSImageURL) if err != nil { return nil, err diff --git a/pkg/controller/render/render_controller_test.go b/pkg/controller/render/render_controller_test.go index dab89d6a54..2dcf0350a3 100644 --- a/pkg/controller/render/render_controller_test.go +++ b/pkg/controller/render/render_controller_test.go @@ -7,7 +7,7 @@ import ( "time" "github.com/clarketm/json" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" configv1 "github.com/openshift/api/config/v1" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -245,18 +245,18 @@ func newControllerConfig(name string) *mcfgv1.ControllerConfig { func TestCreatesGeneratedMachineConfig(t *testing.T) { f := newFixture(t) mcp := helpers.NewMachineConfigPool("test-cluster-master", helpers.MasterSelector, nil, "") - files := []igntypes.File{{ - Node: igntypes.Node{ + files := []ign3types.File{{ + Node: ign3types.Node{ Path: "/dummy/0", }, }, { - Node: igntypes.Node{ + Node: ign3types.Node{ Path: "/dummy/1", }, }} mcs := []*mcfgv1.MachineConfig{ - helpers.NewMachineConfig("00-test-cluster-master", map[string]string{"node-role/master": ""}, "dummy://", []igntypes.File{files[0]}), - helpers.NewMachineConfig("05-extra-master", map[string]string{"node-role/master": ""}, "dummy://1", []igntypes.File{files[1]}), + helpers.NewMachineConfig("00-test-cluster-master", map[string]string{"node-role/master": ""}, "dummy://", []ign3types.File{files[0]}), + helpers.NewMachineConfig("05-extra-master", map[string]string{"node-role/master": ""}, "dummy://1", []ign3types.File{files[1]}), } cc := newControllerConfig(ctrlcommon.ControllerConfigName) @@ -273,29 +273,27 @@ func TestCreatesGeneratedMachineConfig(t *testing.T) { // generateRenderedMachineConfig should return an error when one of the MCs in configs contains an invalid ignconfig. func TestIgnValidationGenerateRenderedMachineConfig(t *testing.T) { mcp := helpers.NewMachineConfigPool("test-cluster-master", helpers.MasterSelector, nil, "") - files := []igntypes.File{{ - Node: igntypes.Node{ - Filesystem: "root", - Path: "/dummy/0", + files := []ign3types.File{{ + Node: ign3types.Node{ + Path: "/dummy/0", }, }, { - Node: igntypes.Node{ - Filesystem: "root", - Path: "/dummy/1", + Node: ign3types.Node{ + Path: "/dummy/1", }, }} mcs := []*mcfgv1.MachineConfig{ - helpers.NewMachineConfig("00-test-cluster-master", map[string]string{"node-role/master": ""}, "dummy://", []igntypes.File{files[0]}), - helpers.NewMachineConfig("05-extra-master", map[string]string{"node-role/master": ""}, "dummy://1", []igntypes.File{files[1]}), + helpers.NewMachineConfig("00-test-cluster-master", map[string]string{"node-role/master": ""}, "dummy://", []ign3types.File{files[0]}), + helpers.NewMachineConfig("05-extra-master", map[string]string{"node-role/master": ""}, "dummy://1", []ign3types.File{files[1]}), } cc := newControllerConfig(ctrlcommon.ControllerConfigName) _, err := generateRenderedMachineConfig(mcp, mcs, cc) require.Nil(t, err) - // verify that an invalid igntion config (here a config with content and an empty version, + // verify that an invalid ignition config (here a config with content and an empty version, // will fail validation - ignCfg, err := ctrlcommon.IgnParseWrapper(mcs[1].Spec.Config.Raw) + ignCfg, err := ctrlcommon.ParseAndConvertConfig(mcs[1].Spec.Config.Raw) require.Nil(t, err) ignCfg.Ignition.Version = "" rawIgnCfg, err := json.Marshal(ignCfg) @@ -319,20 +317,18 @@ func TestIgnValidationGenerateRenderedMachineConfig(t *testing.T) { func TestUpdatesGeneratedMachineConfig(t *testing.T) { f := newFixture(t) mcp := helpers.NewMachineConfigPool("test-cluster-master", helpers.MasterSelector, nil, "") - files := []igntypes.File{{ - Node: igntypes.Node{ - Filesystem: "root", - Path: "/dummy/0", + files := []ign3types.File{{ + Node: ign3types.Node{ + Path: "/dummy/0", }, }, { - Node: igntypes.Node{ - Filesystem: "root", - Path: "/dummy/1", + Node: ign3types.Node{ + Path: "/dummy/1", }, }} mcs := []*mcfgv1.MachineConfig{ - helpers.NewMachineConfig("00-test-cluster-master", map[string]string{"node-role/master": ""}, "dummy://", []igntypes.File{files[0]}), - helpers.NewMachineConfig("05-extra-master", map[string]string{"node-role/master": ""}, "dummy://1", []igntypes.File{files[1]}), + helpers.NewMachineConfig("00-test-cluster-master", map[string]string{"node-role/master": ""}, "dummy://", []ign3types.File{files[0]}), + helpers.NewMachineConfig("05-extra-master", map[string]string{"node-role/master": ""}, "dummy://1", []ign3types.File{files[1]}), } cc := newControllerConfig(ctrlcommon.ControllerConfigName) @@ -375,8 +371,8 @@ func TestUpdatesGeneratedMachineConfig(t *testing.T) { func TestGenerateMachineConfigNoOverrideOSImageURL(t *testing.T) { mcp := helpers.NewMachineConfigPool("test-cluster-master", helpers.MasterSelector, nil, "") mcs := []*mcfgv1.MachineConfig{ - helpers.NewMachineConfig("00-test-cluster-master", map[string]string{"node-role/master": ""}, "dummy-test-1", []igntypes.File{}), - helpers.NewMachineConfig("00-test-cluster-master-0", map[string]string{"node-role/master": ""}, "dummy-change", []igntypes.File{}), + helpers.NewMachineConfig("00-test-cluster-master", map[string]string{"node-role/master": ""}, "dummy-test-1", []ign3types.File{}), + helpers.NewMachineConfig("00-test-cluster-master-0", map[string]string{"node-role/master": ""}, "dummy-change", []ign3types.File{}), } cc := newControllerConfig(ctrlcommon.ControllerConfigName) @@ -391,20 +387,18 @@ func TestGenerateMachineConfigNoOverrideOSImageURL(t *testing.T) { func TestDoNothing(t *testing.T) { f := newFixture(t) mcp := helpers.NewMachineConfigPool("test-cluster-master", helpers.MasterSelector, nil, "") - files := []igntypes.File{{ - Node: igntypes.Node{ - Filesystem: "root", - Path: "/dummy/0", + files := []ign3types.File{{ + Node: ign3types.Node{ + Path: "/dummy/0", }, }, { - Node: igntypes.Node{ - Filesystem: "root", - Path: "/dummy/1", + Node: ign3types.Node{ + Path: "/dummy/1", }, }} mcs := []*mcfgv1.MachineConfig{ - helpers.NewMachineConfig("00-test-cluster-master", map[string]string{"node-role/master": ""}, "dummy://", []igntypes.File{files[0]}), - helpers.NewMachineConfig("05-extra-master", map[string]string{"node-role/master": ""}, "dummy://1", []igntypes.File{files[1]}), + helpers.NewMachineConfig("00-test-cluster-master", map[string]string{"node-role/master": ""}, "dummy://", []ign3types.File{files[0]}), + helpers.NewMachineConfig("05-extra-master", map[string]string{"node-role/master": ""}, "dummy://1", []ign3types.File{files[1]}), } cc := newControllerConfig(ctrlcommon.ControllerConfigName) @@ -439,23 +433,23 @@ func TestDoNothing(t *testing.T) { func TestGetMachineConfigsForPool(t *testing.T) { masterPool := helpers.NewMachineConfigPool("test-cluster-master", helpers.MasterSelector, nil, "") - files := []igntypes.File{{ - Node: igntypes.Node{ + files := []ign3types.File{{ + Node: ign3types.Node{ Path: "/dummy/0", }, }, { - Node: igntypes.Node{ + Node: ign3types.Node{ Path: "/dummy/1", }, }, { - Node: igntypes.Node{ + Node: ign3types.Node{ Path: "/dummy/2", }, }} mcs := []*mcfgv1.MachineConfig{ - helpers.NewMachineConfig("00-test-cluster-master", map[string]string{"node-role/master": ""}, "dummy://", []igntypes.File{files[0]}), - helpers.NewMachineConfig("05-extra-master", map[string]string{"node-role/master": ""}, "dummy://1", []igntypes.File{files[1]}), - helpers.NewMachineConfig("00-test-cluster-worker", map[string]string{"node-role/worker": ""}, "dummy://2", []igntypes.File{files[2]}), + helpers.NewMachineConfig("00-test-cluster-master", map[string]string{"node-role/master": ""}, "dummy://", []ign3types.File{files[0]}), + helpers.NewMachineConfig("05-extra-master", map[string]string{"node-role/master": ""}, "dummy://1", []ign3types.File{files[1]}), + helpers.NewMachineConfig("00-test-cluster-worker", map[string]string{"node-role/worker": ""}, "dummy://2", []ign3types.File{files[2]}), } masterConfigs, err := getMachineConfigsForPool(masterPool, mcs) if err != nil { @@ -485,7 +479,7 @@ func getKey(config *mcfgv1.MachineConfigPool, t *testing.T) string { func TestMachineConfigsNoBailWithoutPool(t *testing.T) { f := newFixture(t) - mc := helpers.NewMachineConfig("00-test-cluster-worker", map[string]string{"node-role/worker": ""}, "dummy://2", []igntypes.File{}) + mc := helpers.NewMachineConfig("00-test-cluster-worker", map[string]string{"node-role/worker": ""}, "dummy://2", []ign3types.File{}) oref := metav1.NewControllerRef(newControllerConfig("test"), mcfgv1.SchemeGroupVersion.WithKind("ControllerConfig")) mc.SetOwnerReferences([]metav1.OwnerReference{*oref}) mcp := helpers.NewMachineConfigPool("test-cluster-master", helpers.WorkerSelector, nil, "") diff --git a/pkg/controller/template/render_test.go b/pkg/controller/template/render_test.go index 046e1cd320..c7b9d001c6 100644 --- a/pkg/controller/template/render_test.go +++ b/pkg/controller/template/render_test.go @@ -9,7 +9,7 @@ import ( "path/filepath" "testing" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" configv1 "github.com/openshift/api/config/v1" "k8s.io/client-go/kubernetes/scheme" @@ -328,7 +328,7 @@ func TestGenerateMachineConfigs(t *testing.T) { t.Fatal("role label missing") } - ign, err := ctrlcommon.IgnParseWrapper(cfg.Spec.Config.Raw) + ign, err := ctrlcommon.ParseAndConvertConfig(cfg.Spec.Config.Raw) if err != nil { t.Errorf("Failed to parse Ignition config") } @@ -382,7 +382,7 @@ func controllerConfigFromFile(path string) (*mcfgv1.ControllerConfig, error) { return cc, nil } -func findIgnFile(files []igntypes.File, path string, t *testing.T) bool { +func findIgnFile(files []ign3types.File, path string, t *testing.T) bool { for _, f := range files { if f.Path == path { return true @@ -391,7 +391,7 @@ func findIgnFile(files []igntypes.File, path string, t *testing.T) bool { return false } -func findIgnUnit(units []igntypes.Unit, name string, t *testing.T) bool { +func findIgnUnit(units []ign3types.Unit, name string, t *testing.T) bool { for _, u := range units { if u.Name == name { return true diff --git a/pkg/daemon/daemon.go b/pkg/daemon/daemon.go index 46c66d06a7..fdf46ea6a3 100644 --- a/pkg/daemon/daemon.go +++ b/pkg/daemon/daemon.go @@ -19,7 +19,8 @@ import ( "time" imgref "github.com/containers/image/docker/reference" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign2types "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" "github.com/golang/glog" "github.com/google/go-cmp/cmp" "github.com/pkg/errors" @@ -490,7 +491,7 @@ func (dn *Daemon) RunOnceFrom(onceFrom string, skipReboot bool) error { return err } switch c := configi.(type) { - case igntypes.Config: + case ign3types.Config: glog.V(2).Info("Daemon running directly from Ignition") return dn.runOnceFromIgnition(c) case mcfgv1.MachineConfig: @@ -1158,7 +1159,7 @@ func (dn *Daemon) runOnceFromMachineConfig(machineConfig mcfgv1.MachineConfig, c } // runOnceFromIgnition executes MCD's subset of Ignition functionality in onceFrom mode -func (dn *Daemon) runOnceFromIgnition(ignConfig igntypes.Config) error { +func (dn *Daemon) runOnceFromIgnition(ignConfig ign3types.Config) error { // Execute update without hitting the cluster if err := dn.writeFiles(ignConfig.Storage.Files); err != nil { return err @@ -1278,17 +1279,33 @@ func (dn *Daemon) validateOnDiskState(currentConfig *mcfgv1.MachineConfig) error return errors.Errorf("expected target osImageURL %q, have %q", currentConfig.Spec.OSImageURL, dn.bootedOSImageURL) } // And the rest of the disk state - currentIgnConfig, err := ctrlcommon.IgnParseWrapper(currentConfig.Spec.Config.Raw) + // We want to verify the disk state in the spec version that it was created with, + // to remove possibilities of behaviour changes due to translation + ignconfigi, err := ctrlcommon.IgnParseWrapper(currentConfig.Spec.Config.Raw) if err != nil { return errors.Errorf("Failed to parse Ignition for validation: %s", err) } - if err := checkFiles(currentIgnConfig.Storage.Files); err != nil { - return err - } - if err := checkUnits(currentIgnConfig.Systemd.Units); err != nil { - return err + + switch typedConfig := ignconfigi.(type) { + case ign3types.Config: + if err := checkV3Files(ignconfigi.(ign3types.Config).Storage.Files); err != nil { + return err + } + if err := checkV3Units(ignconfigi.(ign3types.Config).Systemd.Units); err != nil { + return err + } + return nil + case ign2types.Config: + if err := checkV2Files(ignconfigi.(ign2types.Config).Storage.Files); err != nil { + return err + } + if err := checkV2Units(ignconfigi.(ign2types.Config).Systemd.Units); err != nil { + return err + } + return nil + default: + return errors.Errorf("unexpected type for ignition config: %v", typedConfig) } - return nil } // getRefDigest parses a Docker/OCI image reference and returns @@ -1362,7 +1379,38 @@ func (dn *Daemon) checkOS(osImageURL string) (bool, error) { // checkUnits validates the contents of all the units in the // target config and returns true if they match. -func checkUnits(units []igntypes.Unit) error { +func checkV3Units(units []ign3types.Unit) error { + for _, u := range units { + for j := range u.Dropins { + path := filepath.Join(pathSystemd, u.Name+".d", u.Dropins[j].Name) + if err := checkFileContentsAndMode(path, []byte(*u.Dropins[j].Contents), defaultFilePermissions); err != nil { + return err + } + } + + if u.Contents == nil || *u.Contents == "" { + continue + } + + path := filepath.Join(pathSystemd, u.Name) + if u.Mask != nil && *u.Mask { + link, err := filepath.EvalSymlinks(path) + if err != nil { + return errors.Wrapf(err, "state validation: error while evaluation symlink for path %q", path) + } + if strings.Compare(pathDevNull, link) != 0 { + return errors.Errorf("state validation: invalid unit masked setting. path: %q; expected: %v; received: %v", path, pathDevNull, link) + } + } + if err := checkFileContentsAndMode(path, []byte(*u.Contents), defaultFilePermissions); err != nil { + return err + } + + } + return nil +} + +func checkV2Units(units []ign2types.Unit) error { for _, u := range units { for j := range u.Dropins { path := filepath.Join(pathSystemd, u.Name+".d", u.Dropins[j].Name) @@ -1395,7 +1443,27 @@ func checkUnits(units []igntypes.Unit) error { // checkFiles validates the contents of all the files in the // target config. -func checkFiles(files []igntypes.File) error { + +// V3 files should not have any duplication anymore, so there is +// no need to check for overwrites. +func checkV3Files(files []ign3types.File) error { + for _, f := range files { + mode := defaultFilePermissions + if f.Mode != nil { + mode = os.FileMode(*f.Mode) + } + contents, err := dataurl.DecodeString(*f.Contents.Source) + if err != nil { + return errors.Wrapf(err, "couldn't parse file %q", f.Path) + } + if err := checkFileContentsAndMode(f.Path, contents.Data, mode); err != nil { + return err + } + } + return nil +} + +func checkV2Files(files []ign2types.File) error { checkedFiles := make(map[string]bool) for i := len(files) - 1; i >= 0; i-- { f := files[i] @@ -1494,7 +1562,7 @@ func (dn *Daemon) senseAndLoadOnceFrom(onceFrom string) (interface{}, onceFromOr } // Try each supported parser - ignConfig, err := ctrlcommon.IgnParseWrapper(content) + ignConfig, err := ctrlcommon.ParseAndConvertConfig(content) if err == nil && ignConfig.Ignition.Version != "" { glog.V(2).Info("onceFrom file is of type Ignition") return ignConfig, contentFrom, nil diff --git a/pkg/daemon/daemon_test.go b/pkg/daemon/daemon_test.go index 0646c6649a..f64769ef89 100644 --- a/pkg/daemon/daemon_test.go +++ b/pkg/daemon/daemon_test.go @@ -9,7 +9,8 @@ import ( "testing" "time" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign2types "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" "github.com/vincent-petithory/dataurl" @@ -52,51 +53,51 @@ func TestValidPath(t *testing.T) { } } -func TestOverwrittenFile(t *testing.T) { +func TestValidateFiles(t *testing.T) { fi, err := os.Lstat("fixtures/test1.txt") if err != nil { t.Errorf("Could not Lstat file: %v", err) } fileMode := int(fi.Mode().Perm()) - // validate single file - files := []igntypes.File{ + // validate single file in spec 3 + filesV3 := []ign3types.File{ { - Node: igntypes.Node{ + Node: ign3types.Node{ Path: "fixtures/test1.txt", }, - FileEmbedded1: igntypes.FileEmbedded1{ - Contents: igntypes.FileContents{ - Source: dataurl.EncodeBytes([]byte("hello world\n")), + FileEmbedded1: ign3types.FileEmbedded1{ + Contents: ign3types.Resource{ + Source: helpers.StrToPtr(dataurl.EncodeBytes([]byte("hello world\n"))), }, Mode: &fileMode, }, }, } - if err := checkFiles(files); err != nil { + if err := checkV3Files(filesV3); err != nil { t.Errorf("Invalid files: %v", err) } - // validate overwritten file - files = []igntypes.File{ + // validate overwritten file in spec 2 + filesV2 := []ign2types.File{ { - Node: igntypes.Node{ + Node: ign2types.Node{ Path: "fixtures/test1.txt", }, - FileEmbedded1: igntypes.FileEmbedded1{ - Contents: igntypes.FileContents{ + FileEmbedded1: ign2types.FileEmbedded1{ + Contents: ign2types.FileContents{ Source: dataurl.EncodeBytes([]byte("hello\n")), }, Mode: &fileMode, }, }, { - Node: igntypes.Node{ + Node: ign2types.Node{ Path: "fixtures/test1.txt", }, - FileEmbedded1: igntypes.FileEmbedded1{ - Contents: igntypes.FileContents{ + FileEmbedded1: ign2types.FileEmbedded1{ + Contents: ign2types.FileContents{ Source: dataurl.EncodeBytes([]byte("hello world\n")), }, Mode: &fileMode, @@ -104,7 +105,7 @@ func TestOverwrittenFile(t *testing.T) { }, } - if err := checkFiles(files); err != nil { + if err := checkV2Files(filesV2); err != nil { t.Errorf("Validating an overwritten file failed: %v", err) } } diff --git a/pkg/daemon/update.go b/pkg/daemon/update.go index 3564b8abed..a61097f3b6 100644 --- a/pkg/daemon/update.go +++ b/pkg/daemon/update.go @@ -17,7 +17,7 @@ import ( "time" "github.com/clarketm/json" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" "github.com/golang/glog" "github.com/google/renameio" errors "github.com/pkg/errors" @@ -319,11 +319,11 @@ func (dn *Daemon) update(oldConfig, newConfig *mcfgv1.MachineConfig) (retErr err } }() - oldIgnConfig, err := ctrlcommon.IgnParseWrapper(oldConfig.Spec.Config.Raw) + oldIgnConfig, err := ctrlcommon.ParseAndConvertConfig(oldConfig.Spec.Config.Raw) if err != nil { return fmt.Errorf("parsing old Ignition config failed with error: %v", err) } - newIgnConfig, err := ctrlcommon.IgnParseWrapper(newConfig.Spec.Config.Raw) + newIgnConfig, err := ctrlcommon.ParseAndConvertConfig(newConfig.Spec.Config.Raw) if err != nil { return fmt.Errorf("parsing new Ignition config failed with error: %v", err) } @@ -417,11 +417,11 @@ func canonicalizeKernelType(kernelType string) string { // newMachineConfigDiff compares two MachineConfig objects. func newMachineConfigDiff(oldConfig, newConfig *mcfgv1.MachineConfig) (*machineConfigDiff, error) { - oldIgn, err := ctrlcommon.IgnParseWrapper(oldConfig.Spec.Config.Raw) + oldIgn, err := ctrlcommon.ParseAndConvertConfig(oldConfig.Spec.Config.Raw) if err != nil { return nil, fmt.Errorf("parsing old Ignition config failed with error: %v", err) } - newIgn, err := ctrlcommon.IgnParseWrapper(newConfig.Spec.Config.Raw) + newIgn, err := ctrlcommon.ParseAndConvertConfig(newConfig.Spec.Config.Raw) if err != nil { return nil, fmt.Errorf("parsing new Ignition config failed with error: %v", err) } @@ -452,11 +452,11 @@ func newMachineConfigDiff(oldConfig, newConfig *mcfgv1.MachineConfig) (*machineC func reconcilable(oldConfig, newConfig *mcfgv1.MachineConfig) (*machineConfigDiff, error) { // The parser will try to translate versions less than maxVersion to maxVersion, or output an err. // The ignition output in case of success will always have maxVersion - oldIgn, err := ctrlcommon.IgnParseWrapper(oldConfig.Spec.Config.Raw) + oldIgn, err := ctrlcommon.ParseAndConvertConfig(oldConfig.Spec.Config.Raw) if err != nil { return nil, fmt.Errorf("parsing old Ignition config failed with error: %v", err) } - newIgn, err := ctrlcommon.IgnParseWrapper(newConfig.Spec.Config.Raw) + newIgn, err := ctrlcommon.ParseAndConvertConfig(newConfig.Spec.Config.Raw) if err != nil { return nil, fmt.Errorf("parsing new Ignition config failed with error: %v", err) } @@ -466,14 +466,6 @@ func reconcilable(oldConfig, newConfig *mcfgv1.MachineConfig) (*machineConfigDif return nil, err } - // Networkd section - - // we don't currently configure the network in place. we can't fix it if - // something changed here. - if !reflect.DeepEqual(oldIgn.Networkd, newIgn.Networkd) { - return nil, errors.New("ignition networkd section contains changes") - } - // Passwd section // we don't currently configure Groups in place. we don't configure Users except @@ -531,7 +523,7 @@ func reconcilable(oldConfig, newConfig *mcfgv1.MachineConfig) (*machineConfigDif // Special case files append: if the new config wants us to append, then we // have to force a reprovision since it's not idempotent for _, f := range newIgn.Storage.Files { - if f.Append { + if len(f.Append) > 0 { return nil, fmt.Errorf("ignition file %v includes append", f.Path) } } @@ -560,8 +552,8 @@ func reconcilable(oldConfig, newConfig *mcfgv1.MachineConfig) (*machineConfigDif // Otherwise, an error will be returned and the proposed config will not be reconcilable. // At this time we do not support non-"core" users or any changes to the "core" user // outside of SSHAuthorizedKeys. -func verifyUserFields(pwdUser igntypes.PasswdUser) error { - emptyUser := igntypes.PasswdUser{} +func verifyUserFields(pwdUser ign3types.PasswdUser) error { + emptyUser := ign3types.PasswdUser{} tempUser := pwdUser if tempUser.Name == coreUserName && len(tempUser.SSHAuthorizedKeys) >= 1 { tempUser.Name = "" @@ -872,11 +864,11 @@ func (dn *Daemon) switchKernel(oldConfig, newConfig *mcfgv1.MachineConfig) error // touched. func (dn *Daemon) updateFiles(oldConfig, newConfig *mcfgv1.MachineConfig) error { glog.Info("Updating files") - oldIgnConfig, err := ctrlcommon.IgnParseWrapper(oldConfig.Spec.Config.Raw) + oldIgnConfig, err := ctrlcommon.ParseAndConvertConfig(oldConfig.Spec.Config.Raw) if err != nil { return fmt.Errorf("failed to update files. Parsing old Ignition config failed with error: %v", err) } - newIgnConfig, err := ctrlcommon.IgnParseWrapper(newConfig.Spec.Config.Raw) + newIgnConfig, err := ctrlcommon.ParseAndConvertConfig(newConfig.Spec.Config.Raw) if err != nil { return fmt.Errorf("failed to update files. Parsing new Ignition config failed with error: %v", err) } @@ -907,7 +899,7 @@ func restorePath(path string) error { // this function will error out if it fails to delete a file (with the exception // of simply warning if the error is ENOENT since that's the desired state). //nolint:gocyclo -func (dn *Daemon) deleteStaleData(oldIgnConfig, newIgnConfig *igntypes.Config) error { +func (dn *Daemon) deleteStaleData(oldIgnConfig, newIgnConfig *ign3types.Config) error { glog.Info("Deleting stale data") newFileSet := make(map[string]struct{}) for _, f := range newIgnConfig.Storage.Files { @@ -1044,7 +1036,7 @@ func (dn *Daemon) deleteStaleData(oldIgnConfig, newIgnConfig *igntypes.Config) e } // enableUnit enables a systemd unit via symlink -func (dn *Daemon) enableUnit(unit igntypes.Unit) error { +func (dn *Daemon) enableUnit(unit ign3types.Unit) error { // The link location wantsPath := filepath.Join(wantsPathSystemd, unit.Name) // sanity check that we don't return an error when the link already exists @@ -1064,7 +1056,7 @@ func (dn *Daemon) enableUnit(unit igntypes.Unit) error { } // disableUnit disables a systemd unit via symlink removal -func (dn *Daemon) disableUnit(unit igntypes.Unit) error { +func (dn *Daemon) disableUnit(unit ign3types.Unit) error { // The link location wantsPath := filepath.Join(wantsPathSystemd, unit.Name) // sanity check so we don't return an error when the unit was already disabled @@ -1078,7 +1070,7 @@ func (dn *Daemon) disableUnit(unit igntypes.Unit) error { } // writeUnits writes the systemd units to disk -func (dn *Daemon) writeUnits(units []igntypes.Unit) error { +func (dn *Daemon) writeUnits(units []ign3types.Unit) error { operatingSystem, err := getHostRunningOS() if err != nil { return errors.Wrapf(err, "checking operating system") @@ -1094,7 +1086,7 @@ func (dn *Daemon) writeUnits(units []igntypes.Unit) error { return err } } - if err := writeFileAtomicallyWithDefaults(dpath, []byte(u.Dropins[i].Contents)); err != nil { + if err := writeFileAtomicallyWithDefaults(dpath, []byte(*u.Dropins[i].Contents)); err != nil { return fmt.Errorf("failed to write systemd unit dropin %q: %v", u.Dropins[i].Name, err) } @@ -1105,7 +1097,7 @@ func (dn *Daemon) writeUnits(units []igntypes.Unit) error { // check if the unit is masked. if it is, we write a symlink to // /dev/null and continue - if u.Mask { + if u.Mask != nil && *u.Mask { glog.V(2).Info("Systemd unit masked") if err := os.RemoveAll(fpath); err != nil { return fmt.Errorf("failed to remove unit %q: %v", u.Name, err) @@ -1120,7 +1112,7 @@ func (dn *Daemon) writeUnits(units []igntypes.Unit) error { continue } - if u.Contents != "" { + if u.Contents != nil && *u.Contents != "" { glog.Infof("Writing systemd unit %q", u.Name) if _, err := os.Stat("/usr" + fpath); err == nil && (operatingSystem == machineConfigDaemonOSRHCOS || operatingSystem == machineConfigDaemonOSFCOS) { @@ -1129,7 +1121,7 @@ func (dn *Daemon) writeUnits(units []igntypes.Unit) error { } } // write the unit to disk - if err := writeFileAtomicallyWithDefaults(fpath, []byte(u.Contents)); err != nil { + if err := writeFileAtomicallyWithDefaults(fpath, []byte(*u.Contents)); err != nil { return fmt.Errorf("failed to write systemd unit %q: %v", u.Name, err) } @@ -1142,14 +1134,6 @@ func (dn *Daemon) writeUnits(units []igntypes.Unit) error { // otherwise the unit is disabled. run disableUnit to ensure the unit is // disabled. even if the unit wasn't previously enabled the result will // be fine as disableUnit is idempotent. - // Note: we have to check for legacy unit.Enable and honor it - glog.Infof("Enabling systemd unit %q", u.Name) - if u.Enable { - if err := dn.enableUnit(u); err != nil { - return err - } - glog.V(2).Infof("Enabled systemd unit %q", u.Name) - } if u.Enabled != nil { if *u.Enabled { if err := dn.enableUnit(u); err != nil { @@ -1169,11 +1153,11 @@ func (dn *Daemon) writeUnits(units []igntypes.Unit) error { // writeFiles writes the given files to disk. // it doesn't fetch remote files and expects a flattened config file. -func (dn *Daemon) writeFiles(files []igntypes.File) error { +func (dn *Daemon) writeFiles(files []ign3types.File) error { for _, file := range files { glog.Infof("Writing file %q", file.Path) - contents, err := dataurl.DecodeString(file.Contents.Source) + contents, err := dataurl.DecodeString(*file.Contents.Source) if err != nil { return err } @@ -1181,15 +1165,11 @@ func (dn *Daemon) writeFiles(files []igntypes.File) error { if file.Mode != nil { mode = os.FileMode(*file.Mode) } - var ( - uid, gid = -1, -1 - ) + // set chown if file information is provided - if file.User != nil || file.Group != nil { - uid, gid, err = getFileOwnership(file) - if err != nil { - return fmt.Errorf("failed to retrieve file ownership for file %q: %v", file.Path, err) - } + uid, gid, err := getFileOwnership(file) + if err != nil { + return fmt.Errorf("failed to retrieve file ownership for file %q: %v", file.Path, err) } if err := createOrigFile(file.Path, file.Path); err != nil { return err @@ -1252,31 +1232,28 @@ func createOrigFile(fromPath, fpath string) error { } // This is essentially ResolveNodeUidAndGid() from Ignition; XXX should dedupe -func getFileOwnership(file igntypes.File) (int, int, error) { +func getFileOwnership(file ign3types.File) (int, int, error) { uid, gid := 0, 0 // default to root - if file.User != nil { - if file.User.ID != nil { - uid = *file.User.ID - } else if file.User.Name != "" { - osUser, err := user.Lookup(file.User.Name) - if err != nil { - return uid, gid, fmt.Errorf("failed to retrieve UserID for username: %s", file.User.Name) - } - glog.V(2).Infof("Retrieved UserId: %s for username: %s", osUser.Uid, file.User.Name) - uid, _ = strconv.Atoi(osUser.Uid) + if file.User.ID != nil { + uid = *file.User.ID + } else if file.User.Name != nil && *file.User.Name != "" { + osUser, err := user.Lookup(*file.User.Name) + if err != nil { + return uid, gid, fmt.Errorf("failed to retrieve UserID for username: %s", *file.User.Name) } + glog.V(2).Infof("Retrieved UserId: %s for username: %s", osUser.Uid, *file.User.Name) + uid, _ = strconv.Atoi(osUser.Uid) } - if file.Group != nil { - if file.Group.ID != nil { - gid = *file.Group.ID - } else if file.Group.Name != "" { - osGroup, err := user.LookupGroup(file.Group.Name) - if err != nil { - return uid, gid, fmt.Errorf("failed to retrieve GroupID for group: %s", file.Group.Name) - } - glog.V(2).Infof("Retrieved GroupID: %s for group: %s", osGroup.Gid, file.Group.Name) - gid, _ = strconv.Atoi(osGroup.Gid) + + if file.Group.ID != nil { + gid = *file.Group.ID + } else if file.Group.Name != nil && *file.Group.Name != "" { + osGroup, err := user.LookupGroup(*file.Group.Name) + if err != nil { + return uid, gid, fmt.Errorf("failed to retrieve GroupID for group: %v", file.Group.Name) } + glog.V(2).Infof("Retrieved GroupID: %s for group: %s", osGroup.Gid, *file.Group.Name) + gid, _ = strconv.Atoi(osGroup.Gid) } return uid, gid, nil } @@ -1298,7 +1275,7 @@ func (dn *Daemon) atomicallyWriteSSHKey(keys string) error { } // Update a given PasswdUser's SSHKey -func (dn *Daemon) updateSSHKeys(newUsers []igntypes.PasswdUser) error { +func (dn *Daemon) updateSSHKeys(newUsers []ign3types.PasswdUser) error { if len(newUsers) == 0 { return nil } diff --git a/pkg/daemon/update_test.go b/pkg/daemon/update_test.go index ab79b65f08..9b12216220 100644 --- a/pkg/daemon/update_test.go +++ b/pkg/daemon/update_test.go @@ -7,7 +7,7 @@ import ( "testing" "time" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" mcfgv1 "github.com/openshift/machine-config-operator/pkg/apis/machineconfiguration.openshift.io/v1" ctrlcommon "github.com/openshift/machine-config-operator/pkg/controller/common" "github.com/openshift/machine-config-operator/test/helpers" @@ -79,34 +79,14 @@ func TestReconcilable(t *testing.T) { _, isReconcilable := reconcilable(oldConfig, newConfig) checkIrreconcilableResults(t, "Ignition", isReconcilable) //reset to proper Ignition version - newIgnCfg.Ignition.Version = igntypes.MaxVersion.String() + newIgnCfg.Ignition.Version = ign3types.MaxVersion.String() newConfig = helpers.CreateMachineConfigFromIgnition(newIgnCfg) _, isReconcilable = reconcilable(oldConfig, newConfig) checkReconcilableResults(t, "Ignition", isReconcilable) - // Verify Networkd unit changes react as expected - oldIgnCfg.Networkd = igntypes.Networkd{} - oldConfig = helpers.CreateMachineConfigFromIgnition(oldIgnCfg) - newIgnCfg.Networkd = igntypes.Networkd{ - Units: []igntypes.Networkdunit{ - igntypes.Networkdunit{ - Name: "test.network", - }, - }, - } - newConfig = helpers.CreateMachineConfigFromIgnition(newIgnCfg) - _, isReconcilable = reconcilable(oldConfig, newConfig) - checkIrreconcilableResults(t, "Networkd", isReconcilable) - - // Match Networkd - oldIgnCfg.Networkd = newIgnCfg.Networkd - oldConfig = helpers.CreateMachineConfigFromIgnition(oldIgnCfg) - _, isReconcilable = reconcilable(oldConfig, newConfig) - checkReconcilableResults(t, "Networkd", isReconcilable) - // Verify Disk changes react as expected - oldIgnCfg.Storage.Disks = []igntypes.Disk{ - igntypes.Disk{ + oldIgnCfg.Storage.Disks = []ign3types.Disk{ + ign3types.Disk{ Device: "/one", }, } @@ -121,11 +101,11 @@ func TestReconcilable(t *testing.T) { checkReconcilableResults(t, "Disk", isReconcilable) // Verify Filesystems changes react as expected - oldFSPath := "/foo/bar" - oldIgnCfg.Storage.Filesystems = []igntypes.Filesystem{ - igntypes.Filesystem{ - Name: "user", - Path: &oldFSPath, + oldIgnCfg.Storage.Filesystems = []ign3types.Filesystem{ + ign3types.Filesystem{ + Device: "/dev/sda1", + Format: helpers.StrToPtr("ext4"), + Path: helpers.StrToPtr("/foo/bar"), }, } oldConfig = helpers.CreateMachineConfigFromIgnition(oldIgnCfg) @@ -139,8 +119,8 @@ func TestReconcilable(t *testing.T) { checkReconcilableResults(t, "Filesystem", isReconcilable) // Verify Raid changes react as expected - oldIgnCfg.Storage.Raid = []igntypes.Raid{ - igntypes.Raid{ + oldIgnCfg.Storage.Raid = []ign3types.Raid{ + ign3types.Raid{ Name: "data", Level: "stripe", }, @@ -164,9 +144,9 @@ func TestReconcilable(t *testing.T) { _, isReconcilable = reconcilable(oldConfig, newConfig) checkReconcilableResults(t, "PasswdGroups", isReconcilable) - tempGroup := igntypes.PasswdGroup{} + tempGroup := ign3types.PasswdGroup{} tempGroup.Name = "testGroup" - newIgnCfg.Passwd.Groups = []igntypes.PasswdGroup{tempGroup} + newIgnCfg.Passwd.Groups = []ign3types.PasswdGroup{tempGroup} newConfig = helpers.CreateMachineConfigFromIgnition(newIgnCfg) _, isReconcilable = reconcilable(oldConfig, newConfig) checkIrreconcilableResults(t, "PasswdGroups", isReconcilable) @@ -198,13 +178,13 @@ func TestmachineConfigDiff(t *testing.T) { assert.True(t, diff.isEmpty()) } -func newTestIgnitionFile(i uint) igntypes.File { +func newTestIgnitionFile(i uint) ign3types.File { mode := 0644 - return igntypes.File{Node: igntypes.Node{Path: fmt.Sprintf("/etc/config%d", i), Filesystem: "root"}, - FileEmbedded1: igntypes.FileEmbedded1{Contents: igntypes.FileContents{Source: fmt.Sprintf("data:,config%d", i)}, Mode: &mode}} + return ign3types.File{Node: ign3types.Node{Path: fmt.Sprintf("/etc/config%d", i)}, + FileEmbedded1: ign3types.FileEmbedded1{Contents: ign3types.Resource{Source: helpers.StrToPtr(fmt.Sprintf("data:,config%d", i))}, Mode: &mode}} } -func newMachineConfigFromFiles(files []igntypes.File) *mcfgv1.MachineConfig { +func newMachineConfigFromFiles(files []ign3types.File) *mcfgv1.MachineConfig { newIgnCfg := ctrlcommon.NewIgnConfig() newIgnCfg.Storage.Files = files newConfig := helpers.CreateMachineConfigFromIgnition(newIgnCfg) @@ -212,7 +192,7 @@ func newMachineConfigFromFiles(files []igntypes.File) *mcfgv1.MachineConfig { } func TestReconcilableDiff(t *testing.T) { - var oldFiles []igntypes.File + var oldFiles []ign3types.File nOldFiles := uint(10) for i := uint(0); i < nOldFiles; i++ { oldFiles = append(oldFiles, newTestIgnitionFile(uint(i))) @@ -344,39 +324,39 @@ func TestReconcilableSSH(t *testing.T) { // Check that updating SSH Key of user core supported oldIgnCfg := ctrlcommon.NewIgnConfig() oldMcfg := helpers.CreateMachineConfigFromIgnition(oldIgnCfg) - tempUser1 := igntypes.PasswdUser{Name: "core", SSHAuthorizedKeys: []igntypes.SSHAuthorizedKey{"5678", "abc"}} + tempUser1 := ign3types.PasswdUser{Name: "core", SSHAuthorizedKeys: []ign3types.SSHAuthorizedKey{"5678", "abc"}} newIgnCfg := ctrlcommon.NewIgnConfig() - newIgnCfg.Passwd.Users = []igntypes.PasswdUser{tempUser1} + newIgnCfg.Passwd.Users = []ign3types.PasswdUser{tempUser1} newMcfg := helpers.CreateMachineConfigFromIgnition(newIgnCfg) _, errMsg := reconcilable(oldMcfg, newMcfg) checkReconcilableResults(t, "SSH", errMsg) // Check that updating User with User that is not core is not supported - tempUser2 := igntypes.PasswdUser{Name: "core", SSHAuthorizedKeys: []igntypes.SSHAuthorizedKey{"1234"}} + tempUser2 := ign3types.PasswdUser{Name: "core", SSHAuthorizedKeys: []ign3types.SSHAuthorizedKey{"1234"}} oldIgnCfg.Passwd.Users = append(oldIgnCfg.Passwd.Users, tempUser2) oldMcfg = helpers.CreateMachineConfigFromIgnition(oldIgnCfg) - tempUser3 := igntypes.PasswdUser{Name: "another user", SSHAuthorizedKeys: []igntypes.SSHAuthorizedKey{"5678"}} + tempUser3 := ign3types.PasswdUser{Name: "another user", SSHAuthorizedKeys: []ign3types.SSHAuthorizedKey{"5678"}} newIgnCfg.Passwd.Users[0] = tempUser3 newMcfg = helpers.CreateMachineConfigFromIgnition(newIgnCfg) _, errMsg = reconcilable(oldMcfg, newMcfg) checkIrreconcilableResults(t, "SSH", errMsg) // check that we cannot make updates if any other Passwd.User field is changed. - tempUser4 := igntypes.PasswdUser{Name: "core", SSHAuthorizedKeys: []igntypes.SSHAuthorizedKey{"5678"}, HomeDir: "somedir"} + tempUser4 := ign3types.PasswdUser{Name: "core", SSHAuthorizedKeys: []ign3types.SSHAuthorizedKey{"5678"}, HomeDir: helpers.StrToPtr("somedir")} newIgnCfg.Passwd.Users[0] = tempUser4 newMcfg = helpers.CreateMachineConfigFromIgnition(newIgnCfg) _, errMsg = reconcilable(oldMcfg, newMcfg) checkIrreconcilableResults(t, "SSH", errMsg) // check that we cannot add a user or have len(Passwd.Users)> 1 - tempUser5 := igntypes.PasswdUser{Name: "some user", SSHAuthorizedKeys: []igntypes.SSHAuthorizedKey{"5678"}} + tempUser5 := ign3types.PasswdUser{Name: "some user", SSHAuthorizedKeys: []ign3types.SSHAuthorizedKey{"5678"}} newIgnCfg.Passwd.Users = append(newIgnCfg.Passwd.Users, tempUser5) newMcfg = helpers.CreateMachineConfigFromIgnition(newIgnCfg) _, errMsg = reconcilable(oldMcfg, newMcfg) checkIrreconcilableResults(t, "SSH", errMsg) // check that user is not attempting to remove the only sshkey from core user - tempUser6 := igntypes.PasswdUser{Name: "core", SSHAuthorizedKeys: []igntypes.SSHAuthorizedKey{}} + tempUser6 := ign3types.PasswdUser{Name: "core", SSHAuthorizedKeys: []ign3types.SSHAuthorizedKey{}} newIgnCfg.Passwd.Users[0] = tempUser6 newIgnCfg.Passwd.Users = newIgnCfg.Passwd.Users[:len(newIgnCfg.Passwd.Users)-1] newMcfg = helpers.CreateMachineConfigFromIgnition(newIgnCfg) @@ -413,9 +393,9 @@ func TestUpdateSSHKeys(t *testing.T) { bootedOSImageURL: "test", } // Set up machineconfigs that are identical except for SSH keys - tempUser := igntypes.PasswdUser{Name: "core", SSHAuthorizedKeys: []igntypes.SSHAuthorizedKey{"1234", "4567"}} + tempUser := ign3types.PasswdUser{Name: "core", SSHAuthorizedKeys: []ign3types.SSHAuthorizedKey{"1234", "4567"}} newIgnCfg := ctrlcommon.NewIgnConfig() - newIgnCfg.Passwd.Users = []igntypes.PasswdUser{tempUser} + newIgnCfg.Passwd.Users = []ign3types.PasswdUser{tempUser} err := d.updateSSHKeys(newIgnCfg.Passwd.Users) if err != nil { t.Errorf("Expected no error. Got %s.", err) @@ -424,7 +404,7 @@ func TestUpdateSSHKeys(t *testing.T) { // if Users is empty, nothing should happen and no error should ever be generated newIgnCfg2 := ctrlcommon.NewIgnConfig() - newIgnCfg2.Passwd.Users = []igntypes.PasswdUser{} + newIgnCfg2.Passwd.Users = []ign3types.PasswdUser{} err = d.updateSSHKeys(newIgnCfg2.Passwd.Users) if err != nil { t.Errorf("Expected no error. Got: %s", err) @@ -438,12 +418,12 @@ func TestInvalidIgnConfig(t *testing.T) { oldMcfg := helpers.CreateMachineConfigFromIgnition(oldIgnConfig) // create file to write that contains an impermissable relative path - tempFileContents := igntypes.FileContents{Source: "data:,hello%20world%0A"} + tempFileContents := ign3types.Resource{Source: helpers.StrToPtr("data:,hello%20world%0A")} tempMode := 420 newIgnConfig := ctrlcommon.NewIgnConfig() - newIgnFile := igntypes.File{ - Node: igntypes.Node{Path: "home/core/test", Filesystem: "root"}, - FileEmbedded1: igntypes.FileEmbedded1{Contents: tempFileContents, Mode: &tempMode}, + newIgnFile := ign3types.File{ + Node: ign3types.Node{Path: "home/core/test"}, + FileEmbedded1: ign3types.FileEmbedded1{Contents: tempFileContents, Mode: &tempMode}, } newIgnConfig.Storage.Files = append(newIgnConfig.Storage.Files, newIgnFile) newMcfg := helpers.CreateMachineConfigFromIgnition(newIgnConfig) diff --git a/pkg/server/api.go b/pkg/server/api.go index af49df97d1..bd8b319149 100644 --- a/pkg/server/api.go +++ b/pkg/server/api.go @@ -136,11 +136,12 @@ func (sh *APIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { return } - // the internally saved config is always spec v2.2 - // so translation is only necessary when spec v3.1 is requested + // the internally saved config should be v3.1, but to eliminate the + // potential of race conditions, translate to the requested version + // to make sure var serveConf *runtime.RawExtension if reqConfigVer.Equal(*semver.New("3.1.0")) { - converted3, err := ctrlcommon.ConvertRawExtIgnition2to3(conf) + converted3, err := ctrlcommon.ConvertRawExtIgnitionToV3(conf) if err != nil { w.Header().Set("Content-Length", "0") w.WriteHeader(http.StatusInternalServerError) @@ -150,7 +151,16 @@ func (sh *APIHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) { serveConf = &converted3 } else { - serveConf = conf + // Can only be 2.2 here + converted2, err := ctrlcommon.ConvertRawExtIgnitionToV2(conf) + if err != nil { + w.Header().Set("Content-Length", "0") + w.WriteHeader(http.StatusInternalServerError) + glog.Errorf("couldn't convert config for req: %v, error: %v", cr, err) + return + } + + serveConf = &converted2 } data, err := json.Marshal(serveConf) diff --git a/pkg/server/server.go b/pkg/server/server.go index 0dcbfa3ee1..8c9a8e621b 100644 --- a/pkg/server/server.go +++ b/pkg/server/server.go @@ -5,7 +5,7 @@ import ( "net/url" "github.com/clarketm/json" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" "github.com/vincent-petithory/dataurl" "k8s.io/apimachinery/pkg/runtime" @@ -27,11 +27,6 @@ const ( // need this is that on bootstrap + first install we don't have the MCD // running and writing that file. machineConfigContentPath = "/etc/mcs-machine-config-content.json" - - // defaultFileSystem defines the default file system to be - // used for writing the ignition files created by the - // server. - defaultFileSystem = "root" ) // kubeconfigFunc fetches the kubeconfig that needs to be served. @@ -131,25 +126,27 @@ func getNodeAnnotation(conf string) (string, error) { } func appendFileToRawIgnition(rawExt *runtime.RawExtension, outPath, contents string) error { - conf, err := ctrlcommon.IgnParseWrapper(rawExt.Raw) + conf, err := ctrlcommon.ParseAndConvertConfig(rawExt.Raw) if err != nil { return fmt.Errorf("failed to append file. Parsing Ignition config failed with error: %v", err) } fileMode := int(420) - file := igntypes.File{ - Node: igntypes.Node{ - Filesystem: defaultFileSystem, - Path: outPath, + overwrite := true + source := getEncodedContent(contents) + file := ign3types.File{ + Node: ign3types.Node{ + Path: outPath, + Overwrite: &overwrite, }, - FileEmbedded1: igntypes.FileEmbedded1{ - Contents: igntypes.FileContents{ - Source: getEncodedContent(contents), + FileEmbedded1: ign3types.FileEmbedded1{ + Contents: ign3types.Resource{ + Source: &source, }, Mode: &fileMode, }, } if len(conf.Storage.Files) == 0 { - conf.Storage.Files = make([]igntypes.File, 0) + conf.Storage.Files = make([]ign3types.File, 0) } conf.Storage.Files = append(conf.Storage.Files, file) rawExt.Raw, err = json.Marshal(conf) diff --git a/pkg/server/server_test.go b/pkg/server/server_test.go index f7e2e9a321..773685511c 100644 --- a/pkg/server/server_test.go +++ b/pkg/server/server_test.go @@ -10,7 +10,7 @@ import ( "strings" "testing" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" yaml "github.com/ghodss/yaml" "github.com/stretchr/testify/assert" corev1 "k8s.io/api/core/v1" @@ -60,7 +60,7 @@ func TestMachineConfigToRawIgnition(t *testing.T) { mc := new(mcfgv1.MachineConfig) err = yaml.Unmarshal([]byte(mcData), mc) assert.Nil(t, err) - mcIgnCfg, err := ctrlcommon.IgnParseWrapper(mc.Spec.Config.Raw) + mcIgnCfg, err := ctrlcommon.ParseAndConvertConfig(mc.Spec.Config.Raw) if err != nil { t.Errorf("decoding Ignition Config failed: %s", err) } @@ -68,7 +68,7 @@ func TestMachineConfigToRawIgnition(t *testing.T) { assert.Equal(t, mcIgnCfg.Storage.Files[0].Path, "/etc/coreos/update.conf") origMc := mc.DeepCopy() - origIgnCfg, err := ctrlcommon.IgnParseWrapper(origMc.Spec.Config.Raw) + origIgnCfg, err := ctrlcommon.ParseAndConvertConfig(origMc.Spec.Config.Raw) if err != nil { t.Errorf("decoding Ignition Config failed: %s", err) } @@ -76,11 +76,11 @@ func TestMachineConfigToRawIgnition(t *testing.T) { if err != nil { t.Errorf("converting MachineConfig to raw Ignition config failed: %s", err) } - ignCfg, err := ctrlcommon.IgnParseWrapper(rawIgn.Raw) + ignCfg, err := ctrlcommon.ParseAndConvertConfig(rawIgn.Raw) if err != nil { t.Errorf("decoding Ignition Config failed: %s", err) } - mcIgnCfg, err = ctrlcommon.IgnParseWrapper(mc.Spec.Config.Raw) + mcIgnCfg, err = ctrlcommon.ParseAndConvertConfig(mc.Spec.Config.Raw) if err != nil { t.Errorf("decoding Ignition Config failed: %s", err) } @@ -156,11 +156,11 @@ func TestBootstrapServer(t *testing.T) { } // assert on the output. - ignCfg, err := ctrlcommon.IgnParseWrapper(mc.Spec.Config.Raw) + ignCfg, err := ctrlcommon.ParseAndConvertConfig(mc.Spec.Config.Raw) if err != nil { t.Fatal(err) } - resCfg, err := ctrlcommon.IgnParseWrapper(res.Raw) + resCfg, err := ctrlcommon.ParseAndConvertConfig(res.Raw) if err != nil { t.Fatal(err) } @@ -252,11 +252,11 @@ func TestClusterServer(t *testing.T) { t.Fatalf("expected err to be nil, received: %v", err) } - ignCfg, err := ctrlcommon.IgnParseWrapper(mc.Spec.Config.Raw) + ignCfg, err := ctrlcommon.ParseAndConvertConfig(mc.Spec.Config.Raw) if err != nil { t.Fatal(err) } - resCfg, err := ctrlcommon.IgnParseWrapper(res.Raw) + resCfg, err := ctrlcommon.ParseAndConvertConfig(res.Raw) if err != nil { t.Fatal(err) } @@ -270,7 +270,7 @@ func TestClusterServer(t *testing.T) { } foundEncapsulated = true encapMc := new(mcfgv1.MachineConfig) - contents, err := getDecodedContent(f.Contents.Source) + contents, err := getDecodedContent(*f.Contents.Source) assert.Nil(t, err) err = yaml.Unmarshal([]byte(contents), encapMc) assert.Nil(t, err) @@ -286,7 +286,7 @@ func getKubeConfigContent(t *testing.T) ([]byte, []byte, error) { return []byte("dummy-kubeconfig"), []byte("dummy-root-ca"), nil } -func validateIgnitionFiles(t *testing.T, exp, got []igntypes.File) { +func validateIgnitionFiles(t *testing.T, exp, got []ign3types.File) { expMap := createFileMap(exp) gotMap := createFileMap(got) @@ -306,7 +306,7 @@ func validateIgnitionFiles(t *testing.T, exp, got []igntypes.File) { } -func validateIgnitionSystemd(t *testing.T, exp, got []igntypes.Unit) { +func validateIgnitionSystemd(t *testing.T, exp, got []ign3types.Unit) { expMap := createUnitMap(exp) gotMap := createUnitMap(got) @@ -320,18 +320,18 @@ func validateIgnitionSystemd(t *testing.T, exp, got []igntypes.Unit) { } } -func createUnitMap(units []igntypes.Unit) map[string]igntypes.Unit { - m := make(map[string]igntypes.Unit) +func createUnitMap(units []ign3types.Unit) map[string]ign3types.Unit { + m := make(map[string]ign3types.Unit) for i := range units { m[units[i].Name] = units[i] } return m } -func createFileMap(files []igntypes.File) map[string]igntypes.File { - m := make(map[string]igntypes.File) +func createFileMap(files []ign3types.File) map[string]ign3types.File { + m := make(map[string]ign3types.File) for i := range files { - file := path.Join(files[i].Filesystem, files[i].Path) + file := files[i].Path m[file] = files[i] } return m diff --git a/test/e2e/mcd_test.go b/test/e2e/mcd_test.go index 4293222e06..2316fea521 100644 --- a/test/e2e/mcd_test.go +++ b/test/e2e/mcd_test.go @@ -7,7 +7,6 @@ import ( "testing" "time" - ign2types "github.com/coreos/ignition/config/v2_2/types" ign3types "github.com/coreos/ignition/v2/config/v3_1/types" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" @@ -60,37 +59,55 @@ func mcLabelForWorkers() map[string]string { return mcLabelForRole("worker") } -func createIgnFile(path, content, fs string, mode int) ign2types.File { - return ign2types.File{ - FileEmbedded1: ign2types.FileEmbedded1{ - Contents: ign2types.FileContents{ - Source: content, +// TODO consider also testing for Ign2 +// func createIgn2File(path, content, fs string, mode int) ign2types.File { +// return ign2types.File{ +// FileEmbedded1: ign2types.FileEmbedded1{ +// Contents: ign2types.FileContents{ +// Source: content, +// }, +// Mode: &mode, +// }, +// Node: ign2types.Node{ +// Filesystem: fs, +// Path: path, +// User: &ign2types.NodeUser{ +// Name: "root", +// }, +// }, +// } +// } + +func createIgn3File(path, content string, mode int) ign3types.File { + return ign3types.File{ + FileEmbedded1: ign3types.FileEmbedded1{ + Contents: ign3types.Resource{ + Source: &content, }, Mode: &mode, }, - Node: ign2types.Node{ - Filesystem: fs, - Path: path, - User: &ign2types.NodeUser{ - Name: "root", + Node: ign3types.Node{ + Path: path, + User: ign3types.NodeUser{ + Name: helpers.StrToPtr("root"), }, }, } } -func createMCToAddFileForRole(name, role, filename, data, fs string) *mcfgv1.MachineConfig { +func createMCToAddFileForRole(name, role, filename, data string) *mcfgv1.MachineConfig { mcadd := createMC(fmt.Sprintf("%s-%s", name, uuid.NewUUID()), role) ignConfig := ctrlcommon.NewIgnConfig() - ignFile := createIgnFile(filename, "data:,"+data, fs, 420) + ignFile := createIgn3File(filename, "data:,"+data, 420) ignConfig.Storage.Files = append(ignConfig.Storage.Files, ignFile) rawIgnConfig := helpers.MarshalOrDie(ignConfig) mcadd.Spec.Config.Raw = rawIgnConfig return mcadd } -func createMCToAddFile(name, filename, data, fs string) *mcfgv1.MachineConfig { - return createMCToAddFileForRole(name, "worker", filename, data, fs) +func createMCToAddFile(name, filename, data string) *mcfgv1.MachineConfig { + return createMCToAddFileForRole(name, "worker", filename, data) } func TestMCDeployed(t *testing.T) { @@ -99,7 +116,7 @@ func TestMCDeployed(t *testing.T) { // TODO: bring this back to 10 for i := 0; i < 3; i++ { startTime := time.Now() - mcadd := createMCToAddFile("add-a-file", fmt.Sprintf("/etc/mytestconf%d", i), "test", "root") + mcadd := createMCToAddFile("add-a-file", fmt.Sprintf("/etc/mytestconf%d", i), "test") // create the dummy MC now _, err := cs.MachineConfigs().Create(context.TODO(), mcadd, metav1.CreateOptions{}) @@ -153,9 +170,9 @@ func TestUpdateSSH(t *testing.T) { Labels: mcLabelForWorkers(), } // create a new MC that adds a valid user & ssh keys - tempUser := ign2types.PasswdUser{ + tempUser := ign3types.PasswdUser{ Name: "core", - SSHAuthorizedKeys: []ign2types.SSHAuthorizedKey{ + SSHAuthorizedKeys: []ign3types.SSHAuthorizedKey{ "1234_test", "abc_test", }, @@ -300,8 +317,8 @@ func TestKernelType(t *testing.T) { func TestPoolDegradedOnFailToRender(t *testing.T) { cs := framework.NewClientSet("") - mcadd := createMCToAddFile("add-a-file", "/etc/mytestconfs", "test", "root") - ignCfg, err := ctrlcommon.IgnParseWrapper(mcadd.Spec.Config.Raw) + mcadd := createMCToAddFile("add-a-file", "/etc/mytestconfs", "test") + ignCfg, err := ctrlcommon.ParseAndConvertConfig(mcadd.Spec.Config.Raw) require.Nil(t, err, "failed to parse ignition config") ignCfg.Ignition.Version = "" // invalid, won't render rawIgnCfg := helpers.MarshalOrDie(ignCfg) @@ -349,15 +366,12 @@ func TestReconcileAfterBadMC(t *testing.T) { cs := framework.NewClientSet("") // create a MC that contains a valid ignition config but is not reconcilable - mcadd := createMCToAddFile("add-a-file", "/etc/mytestconfs", "test", "root") - ignCfg, err := ctrlcommon.IgnParseWrapper(mcadd.Spec.Config.Raw) + mcadd := createMCToAddFile("add-a-file", "/etc/mytestconfs", "test") + ignCfg, err := ctrlcommon.ParseAndConvertConfig(mcadd.Spec.Config.Raw) require.Nil(t, err, "failed to parse ignition config") - ignCfg.Networkd = ign2types.Networkd{ - Units: []ign2types.Networkdunit{ - { - Name: "test.network", - Contents: "test contents", - }, + ignCfg.Storage.Disks = []ign3types.Disk{ + ign3types.Disk{ + Device: "/one", }, } rawIgnCfg := helpers.MarshalOrDie(ignCfg) @@ -451,7 +465,7 @@ func TestReconcileAfterBadMC(t *testing.T) { func TestDontDeleteRPMFiles(t *testing.T) { cs := framework.NewClientSet("") - mcHostFile := createMCToAddFile("modify-host-file", "/etc/motd", "mco-test", "root") + mcHostFile := createMCToAddFile("modify-host-file", "/etc/motd", "mco-test") workerOldMc := getMcName(t, cs, "worker") @@ -500,7 +514,7 @@ func TestCustomPool(t *testing.T) { createMCP(t, cs, "infra") - infraMC := createMCToAddFileForRole("infra-host-file", "infra", "/etc/mco-custom-pool", "mco-custom-pool", "root") + infraMC := createMCToAddFileForRole("infra-host-file", "infra", "/etc/mco-custom-pool", "mco-custom-pool") _, err := cs.MachineConfigs().Create(context.TODO(), infraMC, metav1.CreateOptions{}) require.Nil(t, err) renderedConfig, err := waitForRenderedConfig(t, cs, "infra", infraMC.Name) @@ -550,7 +564,7 @@ func TestIgn3Cfg(t *testing.T) { testIgn3Config := ign3types.Config{} tempUser := ign3types.PasswdUser{Name: "core", SSHAuthorizedKeys: []ign3types.SSHAuthorizedKey{"1234_test_ign3"}} testIgn3Config.Passwd.Users = append(testIgn3Config.Passwd.Users, tempUser) - testIgn3Config.Ignition.Version = "3.0.0" + testIgn3Config.Ignition.Version = "3.1.0" mode := 420 testfiledata := "data:,test-ign3-stuff" tempFile := ign3types.File{Node: ign3types.Node{Path: "/etc/testfileconfig"}, diff --git a/test/helpers/helpers.go b/test/helpers/helpers.go index 18ca18de81..5539607227 100644 --- a/test/helpers/helpers.go +++ b/test/helpers/helpers.go @@ -2,7 +2,7 @@ package helpers import ( "github.com/clarketm/json" - igntypes "github.com/coreos/ignition/config/v2_2/types" + ign3types "github.com/coreos/ignition/v2/config/v3_1/types" corev1 "k8s.io/api/core/v1" metav1 "k8s.io/apimachinery/pkg/apis/meta/v1" "k8s.io/apimachinery/pkg/runtime" @@ -21,17 +21,27 @@ var ( InfraSelector = metav1.AddLabelToSelector(&metav1.LabelSelector{}, "node-role/infra", "") ) +// StrToPtr returns a pointer to a string +func StrToPtr(s string) *string { + return &s +} + +// BoolToPtr returns a pointer to a bool +func BoolToPtr(b bool) *bool { + return &b +} + // NewMachineConfig returns a basic machine config with supplied labels, osurl & files added -func NewMachineConfig(name string, labels map[string]string, osurl string, files []igntypes.File) *mcfgv1.MachineConfig { +func NewMachineConfig(name string, labels map[string]string, osurl string, files []ign3types.File) *mcfgv1.MachineConfig { if labels == nil { labels = map[string]string{} } rawIgnition := MarshalOrDie( - &igntypes.Config{ - Ignition: igntypes.Ignition{ - Version: igntypes.MaxVersion.String(), + &ign3types.Config{ + Ignition: ign3types.Ignition{ + Version: ign3types.MaxVersion.String(), }, - Storage: igntypes.Storage{ + Storage: ign3types.Storage{ Files: files, }, },