diff --git a/internal/distro/rhel8/distro_test.go b/internal/distro/rhel8/distro_test.go index 25e08e33e..c8763497b 100644 --- a/internal/distro/rhel8/distro_test.go +++ b/internal/distro/rhel8/distro_test.go @@ -464,7 +464,7 @@ func TestDistro_ManifestError(t *testing.T) { Size: imgType.Size(0), } testPackageSpecSets := distro_test_common.GetTestingImagePackageSpecSets("kernel", imgType) - _, err := imgType.Manifest(bp.Customizations, imgOpts, nil, testPackageSpecSets, nil, 0) + _, _, err := imgType.Manifest(bp.Customizations, imgOpts, nil, testPackageSpecSets, nil, 0) if imgTypeName == "edge-commit" || imgTypeName == "edge-container" { assert.EqualError(t, err, "kernel boot parameter customizations are not supported for ostree types") } else if imgTypeName == "edge-raw-image" { @@ -646,7 +646,7 @@ func TestDistro_CustomFileSystemManifestError(t *testing.T) { arch, _ := r8distro.GetArch(archName) for _, imgTypeName := range arch.ListImageTypes() { imgType, _ := arch.GetImageType(imgTypeName) - _, err := imgType.Manifest(bp.Customizations, distro.ImageOptions{}, nil, nil, nil, 0) + _, _, err := imgType.Manifest(bp.Customizations, distro.ImageOptions{}, nil, nil, nil, 0) if imgTypeName == "edge-commit" || imgTypeName == "edge-container" { assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types") } else if unsupported[imgTypeName] { @@ -681,7 +681,7 @@ func TestDistro_TestRootMountPoint(t *testing.T) { for _, imgTypeName := range arch.ListImageTypes() { imgType, _ := arch.GetImageType(imgTypeName) testPackageSpecSets := distro_test_common.GetTestingImagePackageSpecSets("kernel", imgType) - _, err := imgType.Manifest(bp.Customizations, distro.ImageOptions{}, nil, testPackageSpecSets, nil, 0) + _, _, err := imgType.Manifest(bp.Customizations, distro.ImageOptions{}, nil, testPackageSpecSets, nil, 0) if imgTypeName == "edge-commit" || imgTypeName == "edge-container" { assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types") } else if unsupported[imgTypeName] { @@ -722,7 +722,7 @@ func TestDistro_CustomFileSystemSubDirectories(t *testing.T) { for _, imgTypeName := range arch.ListImageTypes() { imgType, _ := arch.GetImageType(imgTypeName) testPackageSpecSets := distro_test_common.GetTestingImagePackageSpecSets("kernel", imgType) - _, err := imgType.Manifest(bp.Customizations, distro.ImageOptions{}, nil, testPackageSpecSets, nil, 0) + _, _, err := imgType.Manifest(bp.Customizations, distro.ImageOptions{}, nil, testPackageSpecSets, nil, 0) if unsupported[imgTypeName] { assert.Error(t, err) } else { @@ -769,7 +769,7 @@ func TestDistro_MountpointsWithArbitraryDepthAllowed(t *testing.T) { for _, imgTypeName := range arch.ListImageTypes() { imgType, _ := arch.GetImageType(imgTypeName) testPackageSpecSets := distro_test_common.GetTestingImagePackageSpecSets("kernel", imgType) - _, err := imgType.Manifest(bp.Customizations, distro.ImageOptions{}, nil, testPackageSpecSets, nil, 0) + _, _, err := imgType.Manifest(bp.Customizations, distro.ImageOptions{}, nil, testPackageSpecSets, nil, 0) if unsupported[imgTypeName] { assert.Error(t, err) } else { @@ -811,7 +811,7 @@ func TestDistro_DirtyMountpointsNotAllowed(t *testing.T) { arch, _ := r8distro.GetArch(archName) for _, imgTypeName := range arch.ListImageTypes() { imgType, _ := arch.GetImageType(imgTypeName) - _, err := imgType.Manifest(bp.Customizations, distro.ImageOptions{}, nil, nil, nil, 0) + _, _, err := imgType.Manifest(bp.Customizations, distro.ImageOptions{}, nil, nil, nil, 0) if unsupported[imgTypeName] { assert.Error(t, err) } else { @@ -847,7 +847,7 @@ func TestDistro_CustomFileSystemPatternMatching(t *testing.T) { arch, _ := r8distro.GetArch(archName) for _, imgTypeName := range arch.ListImageTypes() { imgType, _ := arch.GetImageType(imgTypeName) - _, err := imgType.Manifest(bp.Customizations, distro.ImageOptions{}, nil, nil, nil, 0) + _, _, err := imgType.Manifest(bp.Customizations, distro.ImageOptions{}, nil, nil, nil, 0) if imgTypeName == "edge-commit" || imgTypeName == "edge-container" { assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types") } else if unsupported[imgTypeName] { @@ -882,7 +882,7 @@ func TestDistro_CustomUsrPartitionNotLargeEnough(t *testing.T) { for _, imgTypeName := range arch.ListImageTypes() { imgType, _ := arch.GetImageType(imgTypeName) testPackageSpecSets := distro_test_common.GetTestingImagePackageSpecSets("kernel", imgType) - _, err := imgType.Manifest(bp.Customizations, distro.ImageOptions{}, nil, testPackageSpecSets, nil, 0) + _, _, err := imgType.Manifest(bp.Customizations, distro.ImageOptions{}, nil, testPackageSpecSets, nil, 0) if imgTypeName == "edge-commit" || imgTypeName == "edge-container" { assert.EqualError(t, err, "Custom mountpoints are not supported for ostree types") } else if unsupported[imgTypeName] { diff --git a/internal/distro/rhel8/imagetype.go b/internal/distro/rhel8/imagetype.go index 59431698d..8f1ad8e8f 100644 --- a/internal/distro/rhel8/imagetype.go +++ b/internal/distro/rhel8/imagetype.go @@ -188,10 +188,11 @@ func (t *imageType) initializeManifest(bp *blueprint.Blueprint, repos []rpmmd.RepoConfig, packageSets map[string]rpmmd.PackageSet, containers []container.Spec, - seed int64) (*manifest.Manifest, error) { + seed int64) (*manifest.Manifest, []string, error) { - if err := t.checkOptions(bp.Customizations, options, containers); err != nil { - return nil, err + warnings, err := t.checkOptions(bp.Customizations, options, containers) + if err != nil { + return nil, nil, err } w := t.workload @@ -215,18 +216,18 @@ func (t *imageType) initializeManifest(bp *blueprint.Blueprint, rng := rand.New(source) if t.image == nil { - return nil, nil + return nil, nil, nil } img, err := t.image(w, t, bp.Customizations, options, packageSets, containers, rng) if err != nil { - return nil, err + return nil, nil, err } manifest := manifest.New() _, err = img.InstantiateManifest(&manifest, repos, t.arch.distro.runner, rng) if err != nil { - return nil, err + return nil, nil, err } - return &manifest, err + return &manifest, warnings, err } func (t *imageType) Manifest(customizations *blueprint.Customizations, @@ -234,7 +235,7 @@ func (t *imageType) Manifest(customizations *blueprint.Customizations, repos []rpmmd.RepoConfig, packageSets map[string][]rpmmd.PackageSpec, containers []container.Spec, - seed int64) (distro.Manifest, error) { + seed int64) (distro.Manifest, []string, error) { bp := &blueprint.Blueprint{Name: "empty blueprint"} err := bp.Initialize() @@ -261,12 +262,16 @@ func (t *imageType) Manifest(customizations *blueprint.Customizations, mergedRepos = append(mergedRepos, repo) } - manifest, err := t.initializeManifest(bp, options, mergedRepos, nil, containers, seed) + manifest, warnings, err := t.initializeManifest(bp, options, mergedRepos, nil, containers, seed) if err != nil { - return distro.Manifest{}, err + return distro.Manifest{}, nil, err } - return manifest.Serialize(packageSets) + ret, err := manifest.Serialize(packageSets) + if err != nil { + return ret, nil, err + } + return ret, warnings, err } func (t *imageType) PackageSets(bp blueprint.Blueprint, options distro.ImageOptions, repos []rpmmd.RepoConfig) map[string][]rpmmd.PackageSet { @@ -325,7 +330,7 @@ func (t *imageType) PackageSets(bp blueprint.Blueprint, options distro.ImageOpti } // create a manifest object and instantiate it with the computed packageSetChains - manifest, err := t.initializeManifest(&bp, options, repos, packageSets, containers, 0) + manifest, _, err := t.initializeManifest(&bp, options, repos, packageSets, containers, 0) if err != nil { // TODO: handle manifest initialization errors more gracefully, we // refuse to initialize manifests with invalid config. @@ -369,8 +374,10 @@ func overridePackageNames(packages []string) []string { } // checkOptions checks the validity and compatibility of options and customizations for the image type. -func (t *imageType) checkOptions(customizations *blueprint.Customizations, options distro.ImageOptions, containers []container.Spec) error { - +// Returns ([]string, error) where []string, if non-nil, will hold any generated warnings (e.g. deprecation notices). +func (t *imageType) checkOptions(customizations *blueprint.Customizations, options distro.ImageOptions, containers []container.Spec) ([]string, error) { + // holds warnings (e.g. deprecation notices) + var warnings []string if t.workload != nil { // For now, if an image type defines its own workload, don't allow any // user customizations. @@ -378,33 +385,32 @@ func (t *imageType) checkOptions(customizations *blueprint.Customizations, optio // set of customizations. The current set of customizations defined in // the blueprint spec corresponds to the Custom workflow. if customizations != nil { - return fmt.Errorf("image type %q does not support customizations", t.name) + return warnings, fmt.Errorf("image type %q does not support customizations", t.name) } } - // we do not support embedding containers on ostree-derived images, only on commits themselves if len(containers) > 0 && t.rpmOstree && (t.name != "edge-commit" && t.name != "edge-container") { - return fmt.Errorf("embedding containers is not supported for %s on %s", t.name, t.arch.distro.name) + return warnings, fmt.Errorf("embedding containers is not supported for %s on %s", t.name, t.arch.distro.name) } if t.bootISO && t.rpmOstree { // check the checksum instead of the URL, because the URL should have been used to resolve the checksum and we need both if options.OSTree.FetchChecksum == "" { - return fmt.Errorf("boot ISO image type %q requires specifying a URL from which to retrieve the OSTree commit", t.name) + return warnings, fmt.Errorf("boot ISO image type %q requires specifying a URL from which to retrieve the OSTree commit", t.name) } if t.name == "edge-simplified-installer" { allowed := []string{"InstallationDevice", "FDO", "User", "Group"} if err := customizations.CheckAllowed(allowed...); err != nil { - return fmt.Errorf("unsupported blueprint customizations found for boot ISO image type %q: (allowed: %s)", t.name, strings.Join(allowed, ", ")) + return warnings, fmt.Errorf("unsupported blueprint customizations found for boot ISO image type %q: (allowed: %s)", t.name, strings.Join(allowed, ", ")) } if customizations.GetInstallationDevice() == "" { - return fmt.Errorf("boot ISO image type %q requires specifying an installation device to install to", t.name) + return warnings, fmt.Errorf("boot ISO image type %q requires specifying an installation device to install to", t.name) } //making fdo optional so that simplified installer can be composed w/o the FDO section in the blueprint if customizations.GetFDO() != nil { if customizations.GetFDO().ManufacturingServerURL == "" { - return fmt.Errorf("boot ISO image type %q requires specifying FDO.ManufacturingServerURL configuration to install to", t.name) + return warnings, fmt.Errorf("boot ISO image type %q requires specifying FDO.ManufacturingServerURL configuration to install to", t.name) } var diunSet int if customizations.GetFDO().DiunPubKeyHash != "" { @@ -417,65 +423,69 @@ func (t *imageType) checkOptions(customizations *blueprint.Customizations, optio diunSet++ } if diunSet != 1 { - return fmt.Errorf("boot ISO image type %q requires specifying one of [FDO.DiunPubKeyHash,FDO.DiunPubKeyInsecure,FDO.DiunPubKeyRootCerts] configuration to install to", t.name) + return warnings, fmt.Errorf("boot ISO image type %q requires specifying one of [FDO.DiunPubKeyHash,FDO.DiunPubKeyInsecure,FDO.DiunPubKeyRootCerts] configuration to install to", t.name) } } } else if t.name == "edge-installer" { allowed := []string{"User", "Group"} if err := customizations.CheckAllowed(allowed...); err != nil { - return fmt.Errorf("unsupported blueprint customizations found for boot ISO image type %q: (allowed: %s)", t.name, strings.Join(allowed, ", ")) + return warnings, fmt.Errorf("unsupported blueprint customizations found for boot ISO image type %q: (allowed: %s)", t.name, strings.Join(allowed, ", ")) } } } // check the checksum instead of the URL, because the URL should have been used to resolve the checksum and we need both if t.name == "edge-raw-image" && options.OSTree.FetchChecksum == "" { - return fmt.Errorf("edge raw images require specifying a URL from which to retrieve the OSTree commit") + return warnings, fmt.Errorf("edge raw images require specifying a URL from which to retrieve the OSTree commit") } // warn that user & group customizations on edge-commit, edge-container are deprecated // TODO(edge): directly error if these options are provided when rhel-9.5's time arrives if t.name == "edge-commit" || t.name == "edge-container" { if customizations.GetUsers() != nil { - log.Printf("Please note that user customizations on %q image type are deprecated and will be removed in the near future\n", t.name) + w := fmt.Sprintf("Please note that user customizations on %q image type are deprecated and will be removed in the near future\n", t.name) + log.Print(w) + warnings = append(warnings, w) } if customizations.GetGroups() != nil { - log.Printf("Please note that group customizations on %q image type are deprecated and will be removed in the near future\n", t.name) + w := fmt.Sprintf("Please note that group customizations on %q image type are deprecated and will be removed in the near future\n", t.name) + log.Print(w) + warnings = append(warnings, w) } } if kernelOpts := customizations.GetKernel(); kernelOpts.Append != "" && t.rpmOstree && (!t.bootable || t.bootISO) { - return fmt.Errorf("kernel boot parameter customizations are not supported for ostree types") + return warnings, fmt.Errorf("kernel boot parameter customizations are not supported for ostree types") } mountpoints := customizations.GetFilesystems() if mountpoints != nil && t.rpmOstree { - return fmt.Errorf("Custom mountpoints are not supported for ostree types") + return warnings, fmt.Errorf("Custom mountpoints are not supported for ostree types") } err := blueprint.CheckMountpointsPolicy(mountpoints, pathpolicy.MountpointPolicies) if err != nil { - return err + return warnings, err } if osc := customizations.GetOpenSCAP(); osc != nil { // only add support for RHEL 8.7 and above. centos not supported. if !t.arch.distro.isRHEL() || common.VersionLessThan(t.arch.distro.osVersion, "8.7") { - return fmt.Errorf(fmt.Sprintf("OpenSCAP unsupported os version: %s", t.arch.distro.osVersion)) + return warnings, fmt.Errorf(fmt.Sprintf("OpenSCAP unsupported os version: %s", t.arch.distro.osVersion)) } supported := oscap.IsProfileAllowed(osc.ProfileID, oscapProfileAllowList) if !supported { - return fmt.Errorf(fmt.Sprintf("OpenSCAP unsupported profile: %s", osc.ProfileID)) + return warnings, fmt.Errorf(fmt.Sprintf("OpenSCAP unsupported profile: %s", osc.ProfileID)) } if t.rpmOstree { - return fmt.Errorf("OpenSCAP customizations are not supported for ostree types") + return warnings, fmt.Errorf("OpenSCAP customizations are not supported for ostree types") } if osc.DataStream == "" { - return fmt.Errorf("OpenSCAP datastream cannot be empty") + return warnings, fmt.Errorf("OpenSCAP datastream cannot be empty") } if osc.ProfileID == "" { - return fmt.Errorf("OpenSCAP profile cannot be empty") + return warnings, fmt.Errorf("OpenSCAP profile cannot be empty") } } @@ -485,18 +495,18 @@ func (t *imageType) checkOptions(customizations *blueprint.Customizations, optio err = blueprint.ValidateDirFileCustomizations(dc, fc) if err != nil { - return err + return warnings, err } err = blueprint.CheckDirectoryCustomizationsPolicy(dc, pathpolicy.CustomDirectoriesPolicies) if err != nil { - return err + return warnings, err } err = blueprint.CheckFileCustomizationsPolicy(fc, pathpolicy.CustomFilesPolicies) if err != nil { - return err + return warnings, err } - return nil + return warnings, nil }