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

feat(datasource): add glasskube packages datasource #29430

Merged
Merged
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
9f848b9
feat(datasource): add glasskube packages datasource
kosmoz Jun 4, 2024
887dcd7
refactor: cleanup glasskube packages datasource
kosmoz Jun 4, 2024
9a2109e
refactor: change to zod schema
kosmoz Jun 4, 2024
d87d516
chore: remove unused type
kosmoz Jun 5, 2024
66e12ba
chore: change from urljoin to joinUrlParts
kosmoz Jun 5, 2024
f479659
fix: remove isStable from versions
kosmoz Jun 5, 2024
9b13238
feat: add caching of result
kosmoz Jun 7, 2024
1e256cc
fix: add missing import
kosmoz Jun 7, 2024
3441e47
chore: remove unnecessary exports
kosmoz Jun 7, 2024
7848bba
Merge remote-tracking branch 'upstream/main' into feat/29322-glasskub…
kosmoz Jun 12, 2024
cb6bcac
feat: add default versioning
kosmoz Jun 12, 2024
3643c6d
fix: use generic error handling
kosmoz Jun 12, 2024
f0124e5
chore: change registry url in tests
kosmoz Jun 14, 2024
67b2d0b
chore: fix code style errors
kosmoz Jun 14, 2024
636ade3
fix: add test for default registry url
kosmoz Jun 17, 2024
b02e646
fix: activate custom registry support
kosmoz Jun 17, 2024
c943c57
fix: remove registryUrl from individual releases
kosmoz Jun 18, 2024
29e326b
chore: remove empty registryUrls in test
kosmoz Jun 18, 2024
328615b
feat: add latest tag to release result
kosmoz Jun 18, 2024
7a4134c
fix: add registryUrl to cache key
kosmoz Jun 25, 2024
5c3d9e2
fix: simplify cache key function
kosmoz Jun 25, 2024
fddadf8
Merge branch 'main' into feat/29322-glasskube-packages-datasource
secustor Jun 25, 2024
03fd8e9
chore: change cache key
kosmoz Jun 26, 2024
dd5b47a
chore: fix codestyle error
kosmoz Jun 26, 2024
2f84dc9
chore: remove non-null assertion in cache key
kosmoz Jun 26, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions lib/modules/datasource/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import { GithubTagsDatasource } from './github-tags';
import { GitlabPackagesDatasource } from './gitlab-packages';
import { GitlabReleasesDatasource } from './gitlab-releases';
import { GitlabTagsDatasource } from './gitlab-tags';
import { GlasskubePackagesDatasource } from './glasskube-packages';
import { GoDatasource } from './go';
import { GolangVersionDatasource } from './golang-version';
import { GradleVersionDatasource } from './gradle-version';
Expand Down Expand Up @@ -102,6 +103,7 @@ api.set(GithubTagsDatasource.id, new GithubTagsDatasource());
api.set(GitlabPackagesDatasource.id, new GitlabPackagesDatasource());
api.set(GitlabReleasesDatasource.id, new GitlabReleasesDatasource());
api.set(GitlabTagsDatasource.id, new GitlabTagsDatasource());
api.set(GlasskubePackagesDatasource.id, new GlasskubePackagesDatasource());
api.set(GoDatasource.id, new GoDatasource());
api.set(GolangVersionDatasource.id, new GolangVersionDatasource());
api.set(GradleVersionDatasource.id, new GradleVersionDatasource());
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
# yaml-language-server: $schema=https://glasskube.dev/schemas/v1/package-manifest.json
name: cloudnative-pg
iconUrl: https://github.com/glasskube/glasskube/assets/16959694/99f3192a-587f-4eb9-884d-62800c022992
shortDescription: A comprehensive platform designed to seamlessly manage PostgreSQL databases
longDescription: |
**CloudNativePG** is an open source operator designed to manage PostgreSQL workloads on any supported Kubernetes
cluster running in private, public, hybrid, or multi-cloud environments. CloudNativePG adheres to DevOps principles
and concepts such as declarative configuration and immutable infrastructure.
references:
- label: GitHub
url: https://github.com/cloudnative-pg/cloudnative-pg
- label: Website
url: https://cloudnative-pg.io/
- label: Documentation
url: https://cloudnative-pg.io/documentation/1.23/
defaultNamespace: cnpg-system
manifests:
- url: https://github.com/cloudnative-pg/cloudnative-pg/releases/download/v1.23.1/cnpg-1.23.1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# yaml-language-server: $schema=https://glasskube.dev/schemas/v1/package-manifest.json
name: cloudnative-pg
iconUrl: https://github.com/glasskube/glasskube/assets/16959694/99f3192a-587f-4eb9-884d-62800c022992
shortDescription: A comprehensive platform designed to seamlessly manage PostgreSQL databases
longDescription: |
**CloudNativePG** is an open source operator designed to manage PostgreSQL workloads on any supported Kubernetes
cluster running in private, public, hybrid, or multi-cloud environments. CloudNativePG adheres to DevOps principles
and concepts such as declarative configuration and immutable infrastructure.
defaultNamespace: cnpg-system
manifests:
- url: https://github.com/cloudnative-pg/cloudnative-pg/releases/download/v1.23.1/cnpg-1.23.1.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
latestVersion: v1.23.1+1
kosmoz marked this conversation as resolved.
Show resolved Hide resolved
versions:
- version: v1.22.0+1
- version: v1.23.1+1
153 changes: 153 additions & 0 deletions lib/modules/datasource/glasskube-packages/index.spec.ts
kosmoz marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
import { getPkgReleases } from '..';
import { Fixtures } from '../../../../test/fixtures';
import * as httpMock from '../../../../test/http-mock';
import { GlasskubePackagesDatasource } from '.';

describe('modules/datasource/glasskube-packages/index', () => {
const customRegistryUrl = 'https://packages.test.example/packages';
const customVersionsUrl = new URL(
`${customRegistryUrl}/cloudnative-pg/versions.yaml`,
);
const defaultVersionUrl = new URL(
`${GlasskubePackagesDatasource.defaultRegistryUrl}/cloudnative-pg/versions.yaml`,
);
const versionsYaml = Fixtures.get('versions.yaml');
const customPackageManifestUrl = new URL(
`${customRegistryUrl}/cloudnative-pg/v1.23.1+1/package.yaml`,
);
const defaultPackageManifestUrl = new URL(
`${GlasskubePackagesDatasource.defaultRegistryUrl}/cloudnative-pg/v1.23.1+1/package.yaml`,
);
const packageManifestYaml = Fixtures.get('package.yaml');
const packageManifestNoReferencesYaml = Fixtures.get(
'package_no_references.yaml',
);

it('should handle error response on versions request', async () => {
httpMock
.scope(customVersionsUrl.origin)
.get(customVersionsUrl.pathname)
.reply(500, 'internal server error');
await expect(
getPkgReleases({
datasource: GlasskubePackagesDatasource.id,
packageName: 'cloudnative-pg',
registryUrls: [customRegistryUrl],
}),
).rejects.toThrow();
});

it('should handle empty response on versions request', async () => {
httpMock
.scope(customVersionsUrl.origin)
.get(customVersionsUrl.pathname)
.reply(200);
const response = await getPkgReleases({
datasource: GlasskubePackagesDatasource.id,
packageName: 'cloudnative-pg',
registryUrls: [customRegistryUrl],
});
expect(response).toBeNull();
});

it('should handle error response on manifest request', async () => {
httpMock
.scope(customVersionsUrl.origin)
.get(customVersionsUrl.pathname)
.reply(200, versionsYaml);
httpMock
.scope(customPackageManifestUrl.origin)
.get(customPackageManifestUrl.pathname)
.reply(500, 'internal server error');
await expect(
getPkgReleases({
datasource: GlasskubePackagesDatasource.id,
packageName: 'cloudnative-pg',
registryUrls: [customRegistryUrl],
}),
).rejects.toThrow();
});

it('should handle empty response on manifest request', async () => {
httpMock
.scope(customVersionsUrl.origin)
.get(customVersionsUrl.pathname)
.reply(200, versionsYaml);
httpMock
.scope(customPackageManifestUrl.origin)
.get(customPackageManifestUrl.pathname)
.reply(200);
const response = await getPkgReleases({
datasource: GlasskubePackagesDatasource.id,
packageName: 'cloudnative-pg',
registryUrls: [customRegistryUrl],
});
expect(response).toBeNull();
});

it('should handle package manifest without references', async () => {
httpMock
.scope(customVersionsUrl.origin)
.get(customVersionsUrl.pathname)
.reply(200, versionsYaml);
httpMock
.scope(customPackageManifestUrl.origin)
.get(customPackageManifestUrl.pathname)
.reply(200, packageManifestNoReferencesYaml);
const response = await getPkgReleases({
datasource: GlasskubePackagesDatasource.id,
packageName: 'cloudnative-pg',
registryUrls: [customRegistryUrl],
});
expect(response).toEqual({
registryUrl: customRegistryUrl,
tags: { latest: 'v1.23.1+1' },
releases: [{ version: 'v1.22.0+1' }, { version: 'v1.23.1+1' }],
});
});

it('should handle package manifest with references and default url', async () => {
httpMock
.scope(defaultVersionUrl.origin)
.get(defaultVersionUrl.pathname)
.reply(200, versionsYaml);
httpMock
.scope(defaultPackageManifestUrl.origin)
.get(defaultPackageManifestUrl.pathname)
.reply(200, packageManifestYaml);
const response = await getPkgReleases({
datasource: GlasskubePackagesDatasource.id,
packageName: 'cloudnative-pg',
});
expect(response).toEqual({
sourceUrl: 'https://github.com/cloudnative-pg/cloudnative-pg',
homepage: 'https://cloudnative-pg.io/',
registryUrl: GlasskubePackagesDatasource.defaultRegistryUrl,
tags: { latest: 'v1.23.1+1' },
releases: [{ version: 'v1.22.0+1' }, { version: 'v1.23.1+1' }],
});
});

it('should handle package manifest with references and custom url', async () => {
httpMock
.scope(customVersionsUrl.origin)
.get(customVersionsUrl.pathname)
.reply(200, versionsYaml);
httpMock
.scope(customPackageManifestUrl.origin)
.get(customPackageManifestUrl.pathname)
.reply(200, packageManifestYaml);
const response = await getPkgReleases({
datasource: GlasskubePackagesDatasource.id,
packageName: 'cloudnative-pg',
registryUrls: [customRegistryUrl],
});
expect(response).toEqual({
sourceUrl: 'https://github.com/cloudnative-pg/cloudnative-pg',
homepage: 'https://cloudnative-pg.io/',
registryUrl: customRegistryUrl,
tags: { latest: 'v1.23.1+1' },
releases: [{ version: 'v1.22.0+1' }, { version: 'v1.23.1+1' }],
});
});
});
76 changes: 76 additions & 0 deletions lib/modules/datasource/glasskube-packages/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import { cache } from '../../../util/cache/package/decorator';
import { joinUrlParts } from '../../../util/url';
import * as glasskubeVersioning from '../../versioning/glasskube';
import { Datasource } from '../datasource';
import type { GetReleasesConfig, ReleaseResult } from '../types';
import {
GlasskubePackageManifestYaml,
GlasskubePackageVersions,
GlasskubePackageVersionsYaml,
} from './schema';

export class GlasskubePackagesDatasource extends Datasource {
static readonly id = 'glasskube-packages';
static readonly defaultRegistryUrl =
'https://packages.dl.glasskube.dev/packages';
override readonly customRegistrySupport = true;
override defaultVersioning = glasskubeVersioning.id;

override defaultRegistryUrls = [
GlasskubePackagesDatasource.defaultRegistryUrl,
];

constructor() {
super(GlasskubePackagesDatasource.id);
}

@cache({
namespace: `datasource-${GlasskubePackagesDatasource.id}`,
key: ({ registryUrl, packageName }: GetReleasesConfig) =>
`${registryUrl!}-${packageName}`
kosmoz marked this conversation as resolved.
Show resolved Hide resolved
})
override async getReleases({
kosmoz marked this conversation as resolved.
Show resolved Hide resolved
packageName,
registryUrl,
}: GetReleasesConfig): Promise<ReleaseResult | null> {
let versions: GlasskubePackageVersions;
const result: ReleaseResult = { releases: [] };

try {
const response = await this.http.get(
joinUrlParts(registryUrl!, packageName, 'versions.yaml'),
);
versions = GlasskubePackageVersionsYaml.parse(response.body);
} catch (err) {
this.handleGenericErrors(err);
}

result.releases = versions.versions.map((it) => ({
version: it.version,
}));
result.tags = { latest: versions.latestVersion };

try {
const response = await this.http.get(
joinUrlParts(
registryUrl!,
packageName,
versions.latestVersion,
'package.yaml',
),
);
const latestManifest = GlasskubePackageManifestYaml.parse(response.body);
for (const ref of latestManifest?.references ?? []) {
if (ref.label.toLowerCase() === 'github') {
result.sourceUrl = ref.url;
} else if (ref.label.toLowerCase() === 'website') {
result.homepage = ref.url;
}
}
} catch (err) {
this.handleGenericErrors(err);
}

return result;
}
}
23 changes: 23 additions & 0 deletions lib/modules/datasource/glasskube-packages/schema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import { z } from 'zod';
import { Yaml } from '../../../util/schema-utils';

const GlasskubePackageVersions = z.object({
latestVersion: z.string(),
versions: z.array(z.object({ version: z.string() })),
});

const GlasskubePackageManifest = z.object({
references: z.optional(
z.array(
z.object({
label: z.string(),
url: z.string(),
}),
),
),
});

export const GlasskubePackageVersionsYaml = Yaml.pipe(GlasskubePackageVersions);
export const GlasskubePackageManifestYaml = Yaml.pipe(GlasskubePackageManifest);

export type GlasskubePackageVersions = z.infer<typeof GlasskubePackageVersions>;
1 change: 1 addition & 0 deletions lib/util/cache/package/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ export type PackageCacheNamespace =
| 'datasource-gitlab-releases'
| 'datasource-gitlab-tags-commit'
| 'datasource-gitlab-tags'
| 'datasource-glasskube-packages'
| 'datasource-go-direct'
| 'datasource-go-proxy'
| 'datasource-go'
Expand Down
Loading