-
Notifications
You must be signed in to change notification settings - Fork 2.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat(manager): add glasskube manager (#30774)
Signed-off-by: Jakob Steiner <[email protected]> Co-authored-by: Sebastian Poxhofer <[email protected]> Co-authored-by: Michael Kriese <[email protected]>
- Loading branch information
1 parent
42f597a
commit 0d20f17
Showing
8 changed files
with
368 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
27 changes: 27 additions & 0 deletions
27
lib/modules/manager/glasskube/__fixtures__/package-and-repo.yaml
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,27 @@ | ||
apiVersion: packages.glasskube.dev/v1alpha1 | ||
kind: PackageRepository | ||
metadata: | ||
annotations: | ||
packages.glasskube.dev/default-repository: "true" | ||
name: glasskube | ||
spec: | ||
url: https://packages.dl.glasskube.dev/packages | ||
|
||
--- | ||
apiVersion: packages.glasskube.dev/v1alpha1 | ||
kind: PackageRepository | ||
metadata: | ||
name: local | ||
spec: | ||
url: http://localhost:9090/packages | ||
|
||
--- | ||
apiVersion: packages.glasskube.dev/v1alpha1 | ||
kind: ClusterPackage | ||
metadata: | ||
name: argo-cd | ||
spec: | ||
packageInfo: | ||
name: argo-cd | ||
repositoryName: glasskube | ||
version: v2.11.7+1 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,161 @@ | ||
import { codeBlock } from 'common-tags'; | ||
import { Fixtures } from '../../../../test/fixtures'; | ||
import { fs } from '../../../../test/util'; | ||
import { GlobalConfig } from '../../../config/global'; | ||
import type { RepoGlobalConfig } from '../../../config/types'; | ||
import { GlasskubePackagesDatasource } from '../../datasource/glasskube-packages'; | ||
import type { ExtractConfig } from '../types'; | ||
import { extractAllPackageFiles, extractPackageFile } from './extract'; | ||
|
||
const config: ExtractConfig = {}; | ||
const adminConfig: RepoGlobalConfig = { localDir: '' }; | ||
|
||
const packageWithRepoName = codeBlock` | ||
apiVersion: packages.glasskube.dev/v1alpha1 | ||
kind: ClusterPackage | ||
metadata: | ||
name: argo-cd | ||
spec: | ||
packageInfo: | ||
name: argo-cd | ||
repositoryName: glasskube | ||
version: v2.11.7+1 | ||
`; | ||
const repository = codeBlock` | ||
apiVersion: packages.glasskube.dev/v1alpha1 | ||
kind: PackageRepository | ||
metadata: | ||
annotations: | ||
packages.glasskube.dev/default-repository: "true" | ||
name: glasskube | ||
spec: | ||
url: https://packages.dl.glasskube.dev/packages | ||
`; | ||
|
||
jest.mock('../../../util/fs'); | ||
|
||
describe('modules/manager/glasskube/extract', () => { | ||
beforeEach(() => { | ||
GlobalConfig.set(adminConfig); | ||
}); | ||
|
||
describe('extractPackageFile()', () => { | ||
it('should extract version and registryUrl', () => { | ||
const deps = extractPackageFile( | ||
Fixtures.get('package-and-repo.yaml'), | ||
'package-and-repo.yaml', | ||
); | ||
expect(deps).toEqual({ | ||
deps: [ | ||
{ | ||
depName: 'argo-cd', | ||
currentValue: 'v2.11.7+1', | ||
datasource: GlasskubePackagesDatasource.id, | ||
registryUrls: ['https://packages.dl.glasskube.dev/packages'], | ||
}, | ||
], | ||
}); | ||
}); | ||
}); | ||
|
||
describe('extractAllPackageFiles()', () => { | ||
it('should return null for empty packageFiles', async () => { | ||
const deps = await extractAllPackageFiles(config, []); | ||
expect(deps).toBeNull(); | ||
}); | ||
|
||
it('should skip package with non-existing repo', async () => { | ||
fs.readLocalFile.mockResolvedValueOnce(packageWithRepoName); | ||
const deps = await extractAllPackageFiles(config, ['package.yaml']); | ||
expect(deps).toEqual([ | ||
{ | ||
packageFile: 'package.yaml', | ||
deps: [ | ||
{ | ||
depName: 'argo-cd', | ||
currentValue: 'v2.11.7+1', | ||
datasource: GlasskubePackagesDatasource.id, | ||
skipReason: 'unknown-registry', | ||
}, | ||
], | ||
}, | ||
]); | ||
}); | ||
|
||
it('should extract registryUrl from repo in other file', async () => { | ||
fs.readLocalFile.mockResolvedValueOnce(packageWithRepoName); | ||
fs.readLocalFile.mockResolvedValueOnce(repository); | ||
const deps = await extractAllPackageFiles(config, [ | ||
'package.yaml', | ||
'repo.yaml', | ||
]); | ||
expect(deps).toEqual([ | ||
{ | ||
packageFile: 'package.yaml', | ||
deps: [ | ||
{ | ||
depName: 'argo-cd', | ||
currentValue: 'v2.11.7+1', | ||
datasource: GlasskubePackagesDatasource.id, | ||
registryUrls: ['https://packages.dl.glasskube.dev/packages'], | ||
}, | ||
], | ||
}, | ||
]); | ||
}); | ||
|
||
it('should extract registryUrl from default repo in other file', async () => { | ||
fs.readLocalFile.mockResolvedValueOnce(codeBlock` | ||
apiVersion: packages.glasskube.dev/v1alpha1 | ||
kind: ClusterPackage | ||
metadata: | ||
name: argo-cd | ||
spec: | ||
packageInfo: | ||
name: argo-cd | ||
version: v2.11.7+1 | ||
repositoryName: "" | ||
`); | ||
fs.readLocalFile.mockResolvedValueOnce(codeBlock` | ||
apiVersion: packages.glasskube.dev/v1alpha1 | ||
kind: ClusterPackage | ||
metadata: | ||
name: argo-cd | ||
spec: | ||
packageInfo: | ||
name: argo-cd | ||
version: v2.11.7+1 | ||
`); | ||
fs.readLocalFile.mockResolvedValueOnce(repository); | ||
const deps = await extractAllPackageFiles(config, [ | ||
'package-with-empty-reponame.yaml', | ||
'package-with-missing-reponame.yaml', | ||
'repo.yaml', | ||
]); | ||
expect(deps).toEqual([ | ||
{ | ||
packageFile: 'package-with-empty-reponame.yaml', | ||
deps: [ | ||
{ | ||
depName: 'argo-cd', | ||
currentValue: 'v2.11.7+1', | ||
datasource: GlasskubePackagesDatasource.id, | ||
registryUrls: ['https://packages.dl.glasskube.dev/packages'], | ||
}, | ||
], | ||
}, | ||
{ | ||
packageFile: 'package-with-missing-reponame.yaml', | ||
deps: [ | ||
{ | ||
depName: 'argo-cd', | ||
currentValue: 'v2.11.7+1', | ||
datasource: GlasskubePackagesDatasource.id, | ||
registryUrls: ['https://packages.dl.glasskube.dev/packages'], | ||
}, | ||
], | ||
}, | ||
]); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import is from '@sindresorhus/is'; | ||
import { readLocalFile } from '../../../util/fs'; | ||
import { parseYaml } from '../../../util/yaml'; | ||
import { GlasskubePackagesDatasource } from '../../datasource/glasskube-packages'; | ||
import type { | ||
ExtractConfig, | ||
PackageDependency, | ||
PackageFile, | ||
PackageFileContent, | ||
} from '../types'; | ||
import { | ||
GlasskubeResource, | ||
type Package, | ||
type PackageRepository, | ||
} from './schema'; | ||
import type { GlasskubeResources } from './types'; | ||
|
||
function parseResources( | ||
content: string, | ||
packageFile: string, | ||
): GlasskubeResources { | ||
const resources: GlasskubeResource[] = parseYaml(content, { | ||
json: true, | ||
customSchema: GlasskubeResource, | ||
failureBehaviour: 'filter', | ||
}); | ||
|
||
const packages: Package[] = []; | ||
const repositories: PackageRepository[] = []; | ||
|
||
for (const resource of resources) { | ||
if (resource.kind === 'ClusterPackage' || resource.kind === 'Package') { | ||
packages.push(resource); | ||
} else if (resource.kind === 'PackageRepository') { | ||
repositories.push(resource); | ||
} | ||
} | ||
|
||
return { packageFile, repositories, packages }; | ||
} | ||
|
||
function resolvePackageDependencies( | ||
packages: Package[], | ||
repositories: PackageRepository[], | ||
): PackageDependency[] { | ||
const deps: PackageDependency[] = []; | ||
for (const pkg of packages) { | ||
const dep: PackageDependency = { | ||
depName: pkg.spec.packageInfo.name, | ||
currentValue: pkg.spec.packageInfo.version, | ||
datasource: GlasskubePackagesDatasource.id, | ||
}; | ||
|
||
const repository = findRepository( | ||
pkg.spec.packageInfo.repositoryName ?? null, | ||
repositories, | ||
); | ||
|
||
if (repository === null) { | ||
dep.skipReason = 'unknown-registry'; | ||
} else { | ||
dep.registryUrls = [repository.spec.url]; | ||
} | ||
|
||
deps.push(dep); | ||
} | ||
return deps; | ||
} | ||
|
||
function findRepository( | ||
name: string | null, | ||
repositories: PackageRepository[], | ||
): PackageRepository | null { | ||
for (const repository of repositories) { | ||
if (name === repository.metadata.name) { | ||
return repository; | ||
} | ||
if (is.falsy(name) && isDefaultRepository(repository)) { | ||
return repository; | ||
} | ||
} | ||
return null; | ||
} | ||
|
||
function isDefaultRepository(repository: PackageRepository): boolean { | ||
return ( | ||
repository.metadata.annotations?.[ | ||
'packages.glasskube.dev/default-repository' | ||
] === 'true' | ||
); | ||
} | ||
|
||
export function extractPackageFile( | ||
content: string, | ||
packageFile: string, | ||
config?: ExtractConfig, | ||
): PackageFileContent | null { | ||
const { packages, repositories } = parseResources(content, packageFile); | ||
const deps = resolvePackageDependencies(packages, repositories); | ||
return { deps }; | ||
} | ||
|
||
export async function extractAllPackageFiles( | ||
config: ExtractConfig, | ||
packageFiles: string[], | ||
): Promise<PackageFile[] | null> { | ||
const allRepositories: PackageRepository[] = []; | ||
const glasskubeResourceFiles: GlasskubeResources[] = []; | ||
for (const packageFile of packageFiles) { | ||
const content = await readLocalFile(packageFile, 'utf8'); | ||
if (content !== null) { | ||
const resources = parseResources(content, packageFile); | ||
allRepositories.push(...resources.repositories); | ||
glasskubeResourceFiles.push(resources); | ||
} | ||
} | ||
|
||
const result: PackageFile[] = []; | ||
for (const file of glasskubeResourceFiles) { | ||
const deps = resolvePackageDependencies(file.packages, allRepositories); | ||
if (deps.length > 0) { | ||
result.push({ packageFile: file.packageFile, deps }); | ||
} | ||
} | ||
return result.length ? result : null; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import type { Category } from '../../../constants'; | ||
import { GlasskubePackagesDatasource } from '../../datasource/glasskube-packages'; | ||
|
||
export { extractAllPackageFiles, extractPackageFile } from './extract'; | ||
export const defaultConfig = { | ||
fileMatch: [], | ||
}; | ||
export const categories: Category[] = ['kubernetes', 'cd']; | ||
export const supportedDatasources = [GlasskubePackagesDatasource.id]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
Extract version data from Packages/ClusterPackages and repository data from PackageRepositories. | ||
|
||
To use the `glasskube` manager you must set your own `fileMatch` pattern. | ||
The `glasskube` manager has no default `fileMatch` pattern, because there is no common filename or directory name convention for Glasskube YAML files. | ||
By setting your own `fileMatch` Renovate avoids having to check each `*.yaml` file in a repository for a Glasskube definition. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
import { z } from 'zod'; | ||
|
||
export const Package = z.object({ | ||
apiVersion: z.string().startsWith('packages.glasskube.dev/'), | ||
kind: z.literal('Package').or(z.literal('ClusterPackage')), | ||
spec: z.object({ | ||
packageInfo: z.object({ | ||
name: z.string(), | ||
version: z.string(), | ||
repositoryName: z.string().optional(), | ||
}), | ||
}), | ||
}); | ||
|
||
export const PackageRepository = z.object({ | ||
apiVersion: z.string().startsWith('packages.glasskube.dev/'), | ||
kind: z.literal('PackageRepository'), | ||
metadata: z.object({ | ||
name: z.string(), | ||
annotations: z.record(z.string(), z.string()).optional(), | ||
}), | ||
spec: z.object({ | ||
url: z.string(), | ||
}), | ||
}); | ||
|
||
export const GlasskubeResource = Package.or(PackageRepository); | ||
|
||
export type Package = z.infer<typeof Package>; | ||
export type PackageRepository = z.infer<typeof PackageRepository>; | ||
export type GlasskubeResource = z.infer<typeof GlasskubeResource>; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
import type { Package, PackageRepository } from './schema'; | ||
|
||
export type GlasskubeResources = { | ||
packageFile: string; | ||
packages: Package[]; | ||
repositories: PackageRepository[]; | ||
}; |