From 785cbb4755a46785b7d15054b92cfc6716ddaf4e Mon Sep 17 00:00:00 2001 From: ivan katliarchuk Date: Fri, 27 Dec 2024 10:20:27 +0000 Subject: [PATCH] feat(versioning): aws-eks-addon versioning added Signed-off-by: ivan katliarchuk --- lib/modules/versioning/api.ts | 2 + .../versioning/aws-eks-addons/index.spec.ts | 105 ++++++++++++++++++ .../versioning/aws-eks-addons/index.ts | 60 ++++++++++ .../versioning/aws-eks-addons/readme.md | 18 +++ 4 files changed, 185 insertions(+) create mode 100644 lib/modules/versioning/aws-eks-addons/index.spec.ts create mode 100644 lib/modules/versioning/aws-eks-addons/index.ts create mode 100644 lib/modules/versioning/aws-eks-addons/readme.md diff --git a/lib/modules/versioning/api.ts b/lib/modules/versioning/api.ts index 454d0f631ade1a..10bae3b202c4cb 100644 --- a/lib/modules/versioning/api.ts +++ b/lib/modules/versioning/api.ts @@ -1,3 +1,4 @@ +import * as awsEksAddon from './aws-eks-addon'; import * as amazonMachineImage from './aws-machine-image'; import * as azureRestApi from './azure-rest-api'; import * as bazelModule from './bazel-module'; @@ -45,6 +46,7 @@ import * as unity3d from './unity3d'; const api = new Map(); export default api; +api.set(awsEksAddon.id, awsEksAddon.api); api.set(amazonMachineImage.id, amazonMachineImage.api); api.set(azureRestApi.id, azureRestApi.api); api.set(bazelModule.id, bazelModule.api); diff --git a/lib/modules/versioning/aws-eks-addons/index.spec.ts b/lib/modules/versioning/aws-eks-addons/index.spec.ts new file mode 100644 index 00000000000000..ab22cafcfc6dd0 --- /dev/null +++ b/lib/modules/versioning/aws-eks-addons/index.spec.ts @@ -0,0 +1,105 @@ +import aws from '.'; + +describe('modules/versioning/aws-eks-addon/index', () => { + describe('parse(version)', () => { + it('should return 1.23.7 and release version', () => { + expect(aws.getMajor('v1.20.7-eksbuild.1')).toBe(1); + expect(aws.getMinor('v1.23.7-eksbuild.1')).toBe(23); + expect(aws.getPatch('v1.20.7-eksbuild.1')).toBe(7); + }); + }); + + describe('isValid(version)', () => { + it('should return true', () => { + expect(aws.isValid('v1.11.7-eksbuild.23')).toBeTruthy(); + }); + + it('should return false', () => { + expect(aws.isValid('v1.11.7-noneksbuild.23')).toBeFalsy(); + }); + }); + + describe('isVersion(version)', () => { + it('should return true', () => { + expect(aws.isVersion('v1.11.7-eksbuild.1')).toBeTruthy(); + }); + + it('should return false', () => { + expect(aws.isVersion('v1.11.7')).toBeFalsy(); + }); + }); + + describe('isCompatible(version)', () => { + it.each` + input | expected + ${''} | ${false} + ${'abrakadabra'} | ${false} + ${'v1.11.7'} | ${false} + ${'v1.11.7.6'} | ${false} + ${'v1.11.7-noneksbuild'} | ${false} + ${'v1.11.7-noneksbuild.1'} | ${false} + ${'v1.11.7-eksbuild'} | ${false} + ${'v1.11.7.3-eksbuild.1'} | ${false} + `('isCompatible("$input") === $expected', ({ input, expected }) => { + const actual = aws.isCompatible(input); + expect(actual).toBe(expected); + }); + }); + + describe('isCompatible(version,range)', () => { + it('should return true', () => { + expect( + aws.isCompatible('v1.11.7-eksbuild.1', 'v1.2.7-eksbuild.1'), + ).toBeTruthy(); + }); + + it('should return false', () => { + expect( + aws.isCompatible('v1.11.7-eksbuild.1', 'v1.11.7-noneksbuild.1'), + ).toBeFalsy(); + }); + }); + + describe('isGreaterThan(version1, version2)', () => { + it('should return true', () => { + expect( + aws.isGreaterThan('v1.11.7-eksbuild.1', 'v1.11.7-eksbuild.0'), + ).toBeTrue(); + expect( + aws.isGreaterThan('v1.20.7-eksbuild.2', 'v1.20.7-eksbuild.1'), + ).toBeTrue(); + expect( + aws.isGreaterThan('v1.22.7-eksbuild.2', 'v1.20.7-eksbuild.1'), + ).toBeTrue(); + expect(aws.isGreaterThan('v1.22.7-eksbuild.2', 'v1.22.7')).toBeTrue(); + expect(aws.isGreaterThan('v1.20.7-eksbuild.1', 'v2.0.0')).toBeTrue(); + }); + + it('should return false', () => { + expect( + aws.isGreaterThan('v1.20.7-eksbuild.1', 'v1.20.7-eksbuild.2'), + ).toBeFalsy(); + expect( + aws.isGreaterThan('v1.20.7-eksbuild.1', 'v2.0.0-eksbuild.1'), + ).toBeFalsy(); + }); + }); + + it('getSatisfyingVersion', () => { + expect( + aws.getSatisfyingVersion(['v1.20.7-eksbuild.1'], 'v1.20.7-eksbuild.1'), + ).toBe('v1.20.7-eksbuild.1'); + expect( + aws.getSatisfyingVersion( + ['v1.20.7-eksbuild.1', 'v1.20.7-eksbuild.2', 'v1.20.7-eksbuild.7'], + 'v1.20.7-eksbuild.3', + ), + ).toBeNull(); + expect( + aws.getSatisfyingVersion( + ['v1.20.7-eksbuild.1', 'v1.20.7-eksbuild.2'], + 'v1.20.7-eksbuild.3', + ), + ).toBeNull(); + }); +}); diff --git a/lib/modules/versioning/aws-eks-addons/index.ts b/lib/modules/versioning/aws-eks-addons/index.ts new file mode 100644 index 00000000000000..07af5ade526e28 --- /dev/null +++ b/lib/modules/versioning/aws-eks-addons/index.ts @@ -0,0 +1,60 @@ +import { regEx } from '../../../util/regex'; +import { coerceString } from '../../../util/string'; +import type { GenericVersion } from '../generic'; +import { GenericVersioningApi } from '../generic'; +import type { VersioningApi } from '../types'; + +export const id = 'aws-eks-addon'; +export const displayName = 'aws-eks-addon'; + +export const urls = []; + +export const supportsRanges = false; + +const versionPattern = regEx( + '^[vV]?(\\d+(?:\\.\\d+)*)(?-eksbuild\\.\\d+)?$', +); + +class AwsEKSAddonVersioningApi extends GenericVersioningApi { + protected _parse(version: string): GenericVersion | null { + if (!version) { + return null; + } + const matches: RegExpExecArray | null = versionPattern.exec(version); + if (!matches) { + return null; + } + const [, prefix, suffix] = matches; + if (!suffix) { + return null; + } + const release: number[] = prefix.split('.').map(Number); + if (release.length !== 3) { + return null; + } + return { release, suffix }; + } + + protected override _compare(version: string, other: string): number { + const compare: number = super._compare(version, other); + if (compare !== 0) { + return compare; + } + const parsed1: GenericVersion | null = this._parse(version); + const parsed2: GenericVersion | null = this._parse(other); + + const suffix1 = coerceString(parsed1?.suffix); + const suffix2 = coerceString(parsed2?.suffix); + return suffix1.localeCompare(suffix2); + } + + override isCompatible(version: string, current: string): boolean { + const parsed1: GenericVersion | null = this._parse(version); + const parsed2: GenericVersion | null = this._parse(current); + return !!(parsed1 && parsed2); + } +} + +export const api: VersioningApi = new AwsEKSAddonVersioningApi(); + +export default api; diff --git a/lib/modules/versioning/aws-eks-addons/readme.md b/lib/modules/versioning/aws-eks-addons/readme.md new file mode 100644 index 00000000000000..475d2404757620 --- /dev/null +++ b/lib/modules/versioning/aws-eks-addons/readme.md @@ -0,0 +1,18 @@ +AWS versioning syntax is used for EKS Addon updates. + +It is based off [Semantic Versioning 2.0](https://semver.org) but with a subset of addon `build metadata` syntax. + +At the moment every ESK Addon that matches the regex `^[vV]?(\d+(?:\.\d+)*)(-eksbuild\.\d+)?$` is considered a valid "release". + +**Key Points about EKS Addon Versioning** + +1. Versioning Scheme: Add-ons typically follow a semantic versioning scheme (e.g., Major.Minor.Patch). This helps in understanding the significance of changes between versions: + + - `Major`: Indicates significant changes or breaking API changes for plugin version. + - `Minor`: Introduces new features or enhancements for plugin version. + - `Patch`: Includes bug fixes and minor improvements for plugin version. + - `Build Metadata` : It helps differentiate this particular release from others that might have been built independently. + +2. Default Versions: When creating a new EKS cluster, AWS often selects a default version for each addon based on the cluster's Kubernetes version and other factors. This default version is usually the most stable and recommended for the specific cluster configuration + +3. Build metadata. Example `eksbuild.1`. The `eksbuild.1` part signifies a specific build or release within the `1.19.0` version, likely managed by the EKS build system. It helps differentiate this particular release from others that might have been built independently. The build metadata provides additional context about the specific release, which can be useful for tracking and troubleshooting.