Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add functions to query and validate minimum spec version #93

Merged
merged 5 commits into from
Jan 11, 2023

Conversation

elezar
Copy link
Contributor

@elezar elezar commented Nov 8, 2022

This change adds support for determining the minimum spec version required for a specified spec. This allows a minimum version spec to be created if newer fields are not used.

Closes #91

@elezar elezar requested review from klihub and klueska November 8, 2022 12:54
@elezar elezar marked this pull request as draft November 8, 2022 12:55
@elezar elezar mentioned this pull request Nov 8, 2022
pkg/cdi/container-edits.go Outdated Show resolved Hide resolved
@klihub
Copy link
Contributor

klihub commented Nov 8, 2022

Wouldn't it be a more sustainable/easier to understand and also easier to maintain implementation to define a compatibility-check function for each valid version we support ?

It would be enough if each of those functions would codify and check the extra conditions compared to the previous version, assuming the Spec passed verification of that (and consequently all previous functions).

Then MinimumRequiredVersion(spec *cdi.Spec) (string, error) could be implemented by something like

var (
  sortedVersions []string
  versionValidator = map[string]func(*cdi.Spec) error{
      "0.0.0": validate000,
      ...
)

func MinimumRequiredVersion(spec *cdi.Spec) (string, error) {
    for _, v := range sortVersions() {
        if err := versionValidator[v](spec); err != nil {
            return "", fmt.Errorf("no valid version found: %w", err)
        }
        return v, nil
    }
} 

func sortVersions() []string {
    if sortedVersions == nil {
        for v, _ := range validVersions {
            sortedVersions = append(sortedVersions, v)
        }
        semver.Sort(sortedVersions)
    }
    return sortedVersions
}

Moreover, something like IsCompatible(spec *cdi.Spec, version string) could be implemented with something like this:

func IsCompatibleVersion(spec *cdi.Spec, version string) error {
    for _, v := range sortVersions() {
        if err := versionValidator[v](spec); err != nil {
            return err
        }
        if v == version {
            return nil
        }
    }

    return fmt.Errorf("can't verify compatibility, unknown version %s", version)
}

@elezar
Copy link
Contributor Author

elezar commented Nov 8, 2022

@klihub I think I'm missing the implementation of validate000 in your example to fully understand your suggestion.

The way I've been thinking about this was about adding fields and as such this can be seen as implementing, for example, a:

requiresV050(*Spec) 

The issue with vaildation as you describe it (at least from my current point of view), is that older functions need to be updated with new fields with each modification.

Rewriting the checks as require* functions, I have:

// requiresV050 returns true if the spec uses v0.5.0 features
func requiresV050(spec *cdi.Spec) bool {
	var edits []*cdi.ContainerEdits

	for _, d := range spec.Devices {
		// The v0.5.0 spec allowed device names to start with a digit instead of requiring a letter
		if len(d.Name) > 0 && !isLetter(rune(d.Name[0])) {
			return true
		}

		edits = append(edits, &d.ContainerEdits)
	}

	edits = append(edits, &spec.ContainerEdits)
	for _, e := range edits {
		for _, dn := range e.DeviceNodes {
			if dn.HostPath != "" && dn.HostPath != dn.Path {
				return true
			}
		}
	}
	return false
}

// requiresV040 returns true if the spec uses v0.4.0 features
func requiresV040(spec *cdi.Spec) bool {
	var edits []*cdi.ContainerEdits

	for _, d := range spec.Devices {
		edits = append(edits, &d.ContainerEdits)
	}

	edits = append(edits, &spec.ContainerEdits)
	for _, e := range edits {
		for _, m := range e.Mounts {
			if m.Type != "" {
				return true
			}
		}
	}
	return false
}

Which may be easier to understand than the current implementation. I can work to clean up the edits manipulation a little to.

@elezar
Copy link
Contributor Author

elezar commented Nov 8, 2022

@klihub I reworked this to match something like you suggested. Not sure if I'm still missing something though.

I think we can still combine the validSpecVersions and the required map that I create. I will do this in a follow-up.

@klihub
Copy link
Contributor

klihub commented Nov 8, 2022

@klihub I think I'm missing the implementation of validate000 in your example to fully understand your suggestion.

Sorry, I omitted all the validating function details, because I didn't have time to find out what those really would be. But it looks to me that you got what I was after... even if my vague suggestion/idea was not well thought through (for instance I think I got it totally wrong that version check could/should be done from lowest to highest, requiring that all previous version checks succeed).

Which may be easier to understand than the current implementation. I can work to clean up the edits manipulation a little to.

If we want to do automatic version-picking in case it is unspecified, then I think it probably would be easier to maintain (and understand) the code if manage to codify the version-specific requirements to per-version validation functions, something along the lines of what is being done here.

This change adds a function to get the minimum required version for a spec.
This checks the fields of the spec and retuns the latest version for non-empty
fields.

For example, the `Mount.Type` field was added in v0.4.0 and the
`DeviceNode.HostPath` field was added in v0.5.0.

Signed-off-by: Evan Lezar <elezar@nvidia.com>
Signed-off-by: Evan Lezar <elezar@nvidia.com>
Signed-off-by: Evan Lezar <elezar@nvidia.com>
Signed-off-by: Evan Lezar <elezar@nvidia.com>
Signed-off-by: Evan Lezar <elezar@nvidia.com>
Copy link
Contributor

@klihub klihub left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM.

@klihub klihub requested a review from bart0sh January 11, 2023 08:59
Copy link
Contributor

@bart0sh bart0sh left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

/lgtm

@bart0sh bart0sh merged commit 5b3b5d8 into cncf-tags:main Jan 11, 2023
@elezar elezar deleted the min-spec-version branch February 9, 2023 15:10
sstosh added a commit to sstosh/podman that referenced this pull request Feb 16, 2023
CDI package added a function that validate minimum
CDI spec version. We need to use at least v0.3.0 spec.

Related: cncf-tags/container-device-interface#93

Signed-off-by: Toshiki Sonoda <sonoda.toshiki@fujitsu.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

CDI file versioning
4 participants