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

Added delete image & get digest logic for Generic V2 #182

Merged
merged 2 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import * as vscode from 'vscode';
import { RegistryV2DataProvider, V2Registry, V2RegistryItem, V2RegistryRoot } from '../RegistryV2/RegistryV2DataProvider';
import { RegistryV2DataProvider, V2Registry, V2RegistryRoot, V2Repository, V2Tag } from '../RegistryV2/RegistryV2DataProvider';
import { BasicOAuthProvider } from '../../auth/BasicOAuthProvider';
import { GenericRegistryV2WizardContext, GenericRegistryV2WizardPromptStep } from './GenericRegistryV2WizardPromptStep';
import { RegistryWizard } from '../../wizard/RegistryWizard';
Expand All @@ -17,14 +17,16 @@ interface GenericV2RegistryRoot extends V2RegistryRoot {
readonly additionalContextValues: ['genericRegistryV2Root'];
}

interface GenericV2RegistryItem extends V2RegistryItem {
readonly additionalContextValues: ['genericRegistryV2'];
interface GenericV2Registry extends V2Registry {
readonly additionalContextValues: ['genericRegistryV2Registry'];
}

export type GenericV2Registry = V2Registry & GenericV2RegistryItem;
interface GenericV2RegistryTag extends V2Tag {
readonly additionalContextValues: ['genericRegistryV2Tag'];
}

export function isGenericV2Registry(item: unknown): item is GenericV2Registry {
return !!item && typeof item === 'object' && (item as GenericV2Registry).additionalContextValues?.includes('genericRegistryV2') === true;
return !!item && typeof item === 'object' && (item as GenericV2Registry).additionalContextValues?.includes('genericRegistryV2Registry') === true;
}

export class GenericRegistryV2DataProvider extends RegistryV2DataProvider {
Expand Down Expand Up @@ -53,7 +55,7 @@ export class GenericRegistryV2DataProvider extends RegistryV2DataProvider {
};
}

public async getRegistries(root: GenericV2RegistryRoot | GenericV2RegistryItem): Promise<GenericV2Registry[]> {
public async getRegistries(root: GenericV2RegistryRoot | GenericV2Registry): Promise<GenericV2Registry[]> {
const trackedRegistryStrings = this.extensionContext.globalState.get<string[]>(TrackedRegistriesKey, []);
const trackedRegistries = trackedRegistryStrings.map(r => vscode.Uri.parse(r));

Expand All @@ -62,13 +64,23 @@ export class GenericRegistryV2DataProvider extends RegistryV2DataProvider {
label: r.toString(),
parent: root,
type: 'commonregistry',
additionalContextValues: ['genericRegistryV2'],
additionalContextValues: ['genericRegistryV2Registry'],
baseUrl: r
};
});
}

protected override getAuthenticationProvider(item: GenericV2RegistryItem): BasicOAuthProvider {
public async getTags(repository: V2Repository): Promise<GenericV2RegistryTag[]> {
const tags = await super.getTags(repository);
const tagsWithAdditionalContext: GenericV2RegistryTag[] = tags.map(tag => ({
...tag,
additionalContextValues: ['genericRegistryV2Tag']
}));

return tagsWithAdditionalContext;
}

protected override getAuthenticationProvider(item: GenericV2Registry): BasicOAuthProvider {
const registry = item.baseUrl.toString();

if (!this.authenticationProviders.has(registry)) {
Expand Down Expand Up @@ -122,7 +134,7 @@ export class GenericRegistryV2DataProvider extends RegistryV2DataProvider {
this.authenticationProviders.set(registryUriString, authProvider);
}

public async removeTrackedRegistry(registry: GenericV2RegistryItem): Promise<void> {
public async removeTrackedRegistry(registry: GenericV2Registry): Promise<void> {
// remove registry url from list of tracked registries
const registryUriString = registry.baseUrl.toString();
const trackedRegistryStrings = this.extensionContext.globalState.get<string[]>(TrackedRegistriesKey, []);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@

import * as vscode from 'vscode';
import { BasicOAuthProvider } from '../../auth/BasicOAuthProvider';
import { RegistryV2DataProvider, V2Registry, V2RegistryItem, V2RegistryRoot, V2Repository } from '../RegistryV2/RegistryV2DataProvider';
import { RegistryV2DataProvider, V2Registry, V2RegistryItem, V2RegistryRoot, V2Repository, V2Tag } from '../RegistryV2/RegistryV2DataProvider';
import { registryV2Request } from '../RegistryV2/registryV2Request';
import { AuthenticationProvider } from '../../contracts/AuthenticationProvider';
import { httpRequest } from '../../utils/httpRequest';
Expand Down Expand Up @@ -120,6 +120,16 @@ export class GitHubRegistryDataProvider extends RegistryV2DataProvider {
return results;
}

public async getTags(repository: V2Repository): Promise<V2Tag[]> {
const tags = await super.getTags(repository);
const tagsWithAdditionalContext: V2Tag[] = tags.map(tag => ({
...tag,
additionalContextValues: ['githubRegistryTag']
}));

return tagsWithAdditionalContext;
}

protected getAuthenticationProvider(item: V2RegistryItem): AuthenticationProvider<never> {
return this.authenticationProvider;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -114,5 +114,41 @@ export abstract class RegistryV2DataProvider extends CommonRegistryDataProvider
return history?.created ? new Date(history.created) : undefined;
}

public async deleteTag(item: CommonTag): Promise<void> {
const digest = await this.getImageDigest(item);
const registry = item.parent.parent as unknown as V2Registry;
await registryV2Request({
authenticationProvider: this.getAuthenticationProvider(registry),
method: 'DELETE',
registryUri: registry.baseUrl,
path: ['v2', item.parent.label, 'manifests', digest],
scopes: [`repository:${item.parent.label}:pull`],
headers: {
'accept': 'application/vnd.docker.distribution.manifest.v2+json'
}
});
}

public async getImageDigest(item: CommonTag): Promise<string> {
const registry = item.parent.parent as unknown as V2Registry;
const response = await registryV2Request({
authenticationProvider: this.getAuthenticationProvider(registry),
method: 'GET',
registryUri: registry.baseUrl,
path: ['v2', item.parent.label, 'manifests', item.label],
scopes: [`repository:${item.parent.label}:pull`],
headers: {
'accept': 'application/vnd.docker.distribution.manifest.v2+json'
}
});

const digest = response.headers['docker-content-digest'];
if (!digest) {
throw new Error('Could not find digest');
}

return digest;
}

protected abstract getAuthenticationProvider(item: V2RegistryItem): AuthenticationProvider<never>;
}