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

Rutusamai/list build tasks for each registry #37

Merged
merged 8 commits into from
Aug 7, 2018
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
2 changes: 1 addition & 1 deletion commands/azureCommands/create-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export async function createRegistry(): Promise<void> {

// INPUT HELPERS
async function acquireSKU(): Promise<string> {
let skus: string[] = ["Basic", "Standard", "Premium"];
let skus: string[] = ["Standard", "Basic", "Premium"];
let sku: string;
sku = await vscode.window.showQuickPick(skus, { 'canPickMany': false, 'placeHolder': 'Choose a SKU' });
if (sku === undefined) { throw new Error('User exit'); }
Expand Down
37 changes: 23 additions & 14 deletions explorer/models/azureRegistryNodes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { AsyncPool } from '../../utils/asyncpool';
import { MAX_CONCURRENT_REQUESTS } from '../../utils/constants'
import { NodeBase } from './nodeBase';
import { RegistryType } from './registryType';
import { TaskRootNode } from './taskNode';

export class AzureRegistryNode extends NodeBase {
private _azureAccount: AzureAccount;
Expand Down Expand Up @@ -38,9 +39,17 @@ export class AzureRegistryNode extends NodeBase {
}
}

public async getChildren(element: AzureRegistryNode): Promise<AzureRepositoryNode[]> {
const repoNodes: AzureRepositoryNode[] = [];
let node: AzureRepositoryNode;
public async getChildren(element: AzureRegistryNode): Promise<NodeBase[]> {
const registryChildNodes: NodeBase[] = [];

let iconPath = {

Choose a reason for hiding this comment

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

We should probably standardize the icon names/ paths, there is a file under utils called constants which would be helpful for this.

Copy link
Author

Choose a reason for hiding this comment

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

Not sure we need that since for each image, iconPath (light and dark) is only called once in the codebase

Choose a reason for hiding this comment

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

That is true but it is easier to refactor/change images from a single location, this is better practice and makes for a more unified experience, moving them all out would make the codebase neater. This isn't however pressing and should be done on further refactorings and doesn't really fit in this scope.

light: path.join(__filename, '..', '..', '..', '..', 'images', 'light', 'buildTasks_light.svg'),
dark: path.join(__filename, '..', '..', '..', '..', 'images', 'dark', 'buildTasks_dark.svg')
};

//Pushing single TaskRootNode under the current registry. All the following nodes added to registryNodes are type AzureRepositoryNode
let taskNode = new TaskRootNode("Build Tasks", "taskRootNode", element.subscription, element.azureAccount, element.registry, iconPath);
registryChildNodes.push(taskNode);

const tenantId: string = element.subscription.tenantId;
if (!this._azureAccount) {
Expand All @@ -50,6 +59,8 @@ export class AzureRegistryNode extends NodeBase {
const session: AzureSession = this._azureAccount.sessions.find((s, i, array) => s.tenantId.toLowerCase() === tenantId.toLowerCase());
const { accessToken, refreshToken } = await acquireToken(session);

let node: AzureRepositoryNode;

if (accessToken && refreshToken) {
let refreshTokenARC;
let accessTokenARC;
Expand Down Expand Up @@ -91,9 +102,8 @@ export class AzureRegistryNode extends NodeBase {
}, (err, httpResponse, body) => {
if (body.length > 0) {
const repositories = JSON.parse(body).repositories;
// tslint:disable-next-line:prefer-for-of // Grandfathered in
for (let i = 0; i < repositories.length; i++) {
node = new AzureRepositoryNode(repositories[i], "azureRepositoryNode");
for (let repo of repositories) {
node = new AzureRepositoryNode(repo, "azureRepositoryNode");
node.accessTokenARC = accessTokenARC;
node.azureAccount = element.azureAccount;
node.password = element.password;
Expand All @@ -102,13 +112,13 @@ export class AzureRegistryNode extends NodeBase {
node.repository = element.label;
node.subscription = element.subscription;
node.userName = element.userName;
repoNodes.push(node);
registryChildNodes.push(node);
}
}
});
}
//Note these are ordered by default in alphabetical order
return repoNodes;
return registryChildNodes;
}
}

Expand All @@ -117,7 +127,7 @@ export class AzureRepositoryNode extends NodeBase {
constructor(
public readonly label: string,
public readonly contextValue: string,
public readonly iconPath: { light: string | vscode.Uri; dark: string | vscode.Uri } = {
public readonly iconPath: AzureRegistryNode["iconPath"] = {
light: path.join(__filename, '..', '..', '..', '..', 'images', 'light', 'Repository_16x.svg'),

Choose a reason for hiding this comment

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

It seems like both are the same icon? Make sure that this changes when we get the real one

dark: path.join(__filename, '..', '..', '..', '..', 'images', 'dark', 'Repository_16x.svg')
}
Expand Down Expand Up @@ -199,18 +209,17 @@ export class AzureRepositoryNode extends NodeBase {
});

const pool = new AsyncPool(MAX_CONCURRENT_REQUESTS);
// tslint:disable-next-line:prefer-for-of // Grandfathered in
for (let i = 0; i < tags.length; i++) {
for (let tag of tags) {
pool.addTask(async () => {
let data = await request.get('https://' + element.repository + '/v2/' + element.label + `/manifests/${tags[i]}`, {
let data = await request.get('https://' + element.repository + '/v2/' + element.label + `/manifests/${tag}`, {
auth: {
bearer: accessTokenARC
}
});

//Acquires each image's manifest to acquire build time.
let manifest = JSON.parse(data);
node = new AzureImageNode(`${element.label}:${tags[i]}`, 'azureImageNode');
node = new AzureImageNode(`${element.label}:${tag}`, 'azureImageNode');
node.azureAccount = element.azureAccount;
node.password = element.password;
node.registry = element.registry;
Expand Down Expand Up @@ -296,7 +305,7 @@ async function acquireToken(session: AzureSession): Promise<{ accessToken: strin
const credentials: any = session.credentials;
const environment: any = session.environment;
// tslint:disable-next-line:no-function-expression // Grandfathered in
credentials.context.acquireToken(environment.activeDirectoryResourceId, credentials.username, credentials.clientId, function (err: any, result: { accessToken: string; refreshToken: string; }): void {
credentials.context.acquireToken(environment.activeDirectoryResourceId, credentials.username, credentials.clientId, function (err: any, result: any): any {
if (err) {
reject(err);
} else {
Expand Down
5 changes: 4 additions & 1 deletion explorer/models/registryRootNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import * as ContainerModels from '../../node_modules/azure-arm-containerregistry
import * as ContainerOps from '../../node_modules/azure-arm-containerregistry/lib/operations';
import { AzureAccount, AzureSession } from '../../typings/azure-account.api';
import { AsyncPool } from '../../utils/asyncpool';
import * as acrTools from '../../utils/Azure/acrTools';
import { MAX_CONCURRENT_REQUESTS, MAX_CONCURRENT_SUBSCRIPTON_REQUESTS } from '../../utils/constants'
import * as dockerHub from '../utils/dockerHubUtils'
import { getCoreNodeModule } from '../utils/utils';
Expand Down Expand Up @@ -158,8 +159,10 @@ export class RegistryRootNode extends NodeBase {
//Go through the registries and add them to the async pool
// tslint:disable-next-line:prefer-for-of // Grandfathered in
for (let j = 0; j < registries.length; j++) {

if (registries[j].adminUserEnabled && !registries[j].sku.tier.includes('Classic')) {
const resourceGroup: string = registries[j].id.slice(registries[j].id.search('resourceGroups/') + 'resourceGroups/'.length, registries[j].id.search('/providers/'));
const resourceGroup: string = acrTools.getResourceGroup(registries[j]);

regPool.addTask(async () => {
let creds = await client.registries.listCredentials(resourceGroup, registries[j].name);
let iconPath = {
Expand Down
76 changes: 33 additions & 43 deletions explorer/models/rootNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,6 @@ export class RootNode extends NodeBase {
// clearInterval(this._imageDebounceTimer);
// return;
// }

clearInterval(this._imageDebounceTimer);

if (refreshInterval > 0) {
Expand All @@ -70,13 +69,9 @@ export class RootNode extends NodeBase {
if (this._imageCache.length !== images.length) {
needToRefresh = true;
} else {
// tslint:disable-next-line:prefer-for-of // Grandfathered in
for (let i: number = 0; i < this._imageCache.length; i++) {
let before: string = JSON.stringify(this._imageCache[i]);
// tslint:disable-next-line:prefer-for-of // Grandfathered in
for (let j: number = 0; j < images.length; j++) {
let after: string = JSON.stringify(images[j]);
if (before === after) {
for (let cachedImage of this._imageCache) {
for (let image of images) {
if (image === cachedImage) {
found = true;
break;
}
Expand Down Expand Up @@ -108,18 +103,21 @@ export class RootNode extends NodeBase {

}

public async getChildren(element: NodeBase): Promise<NodeBase[]> {

if (element.contextValue === 'imagesRootNode') {
return this.getImages();
}
if (element.contextValue === 'containersRootNode') {
return this.getContainers();
}
if (element.contextValue === 'registriesRootNode') {
return this.getRegistries()
public async getChildren(element: RootNode): Promise<NodeBase[]> {
switch (element.contextValue) {
case 'imagesRootNode': {
return this.getImages();
}
case 'containersRootNode': {
return this.getContainers();
}
case 'registriesRootNode': {
return this.getRegistries();
}
default: {
break;
}
}

}

private async getImages(): Promise<ImageNode[]> {
Expand All @@ -132,19 +130,15 @@ export class RootNode extends NodeBase {
return [];
}

// tslint:disable-next-line:prefer-for-of // Grandfathered in
for (let i = 0; i < images.length; i++) {
// tslint:disable-next-line:prefer-for-of // Grandfathered in
if (!images[i].RepoTags) {
for (let image of images) {
if (!image.RepoTags) {
let node = new ImageNode(`<none>:<none>`, "localImageNode", this.eventEmitter);
node.imageDesc = images[i];
node.imageDesc = image;
imageNodes.push(node);
} else {
// tslint:disable-next-line:prefer-for-of // Grandfathered in
for (let j = 0; j < images[i].RepoTags.length; j++) {
// tslint:disable-next-line:prefer-for-of // Grandfathered in
let node = new ImageNode(`${images[i].RepoTags[j]}`, "localImageNode", this.eventEmitter);
node.imageDesc = images[i];
for (let repoTag of image.RepoTags) {
let node = new ImageNode(`${repoTag}`, "localImageNode", this.eventEmitter);
node.imageDesc = image;
imageNodes.push(node);
}
}
Expand All @@ -168,7 +162,6 @@ export class RootNode extends NodeBase {
// clearInterval(this._containerDebounceTimer);
// return;
// }

clearInterval(this._containerDebounceTimer);

if (refreshInterval > 0) {
Expand All @@ -186,15 +179,13 @@ export class RootNode extends NodeBase {
if (this._containerCache.length !== containers.length) {
needToRefresh = true;
} else {
// tslint:disable-next-line:prefer-for-of // Grandfathered in
for (let i = 0; i < this._containerCache.length; i++) {
let ctr: Docker.ContainerDesc = this._containerCache[i];
// tslint:disable-next-line:prefer-for-of // Grandfathered in
for (let j = 0; j < containers.length; j++) {
for (let cachedContainer of this._containerCache) {
let ctr: Docker.ContainerDesc = cachedContainer;
for (let cont of containers) {
// can't do a full object compare because "Status" keeps changing for running containers
if (ctr.Id === containers[j].Id &&
ctr.Image === containers[j].Image &&
ctr.State === containers[j].State) {
if (ctr.Id === cont.Id &&
ctr.Image === cont.Image &&
ctr.State === cont.State) {
found = true;
break;
}
Expand Down Expand Up @@ -230,9 +221,8 @@ export class RootNode extends NodeBase {
return [];
}

// tslint:disable-next-line:prefer-for-of // Grandfathered in
for (let i = 0; i < containers.length; i++) {
if (['exited', 'dead'].includes(containers[i].State)) {
for (let container of containers) {
if (['exited', 'dead'].includes(container.State)) {
contextValue = "stoppedLocalContainerNode";
iconPath = {
light: path.join(__filename, '..', '..', '..', '..', 'images', 'light', 'stoppedContainer.svg'),
Expand All @@ -246,8 +236,8 @@ export class RootNode extends NodeBase {
};
}

let containerNode: ContainerNode = new ContainerNode(`${containers[i].Image} (${containers[i].Names[0].substring(1)}) (${containers[i].Status})`, contextValue, iconPath);
containerNode.containerDesc = containers[i];
let containerNode: ContainerNode = new ContainerNode(`${container.Image} (${container.Names[0].substring(1)}) (${container.Status})`, contextValue, iconPath);
containerNode.containerDesc = container;
containerNodes.push(containerNode);
}

Expand Down
67 changes: 67 additions & 0 deletions explorer/models/taskNode.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import { ResourceManagementClient, SubscriptionClient, SubscriptionModels } from 'azure-arm-resource';
import * as opn from 'opn';
import * as vscode from 'vscode';
import * as ContainerModels from '../../node_modules/azure-arm-containerregistry/lib/models';
import { AzureAccount, AzureSession } from '../../typings/azure-account.api';
import * as acrTools from '../../utils/Azure/acrTools';
import { AzureCredentialsManager } from '../../utils/azureCredentialsManager';
import { NodeBase } from './nodeBase';

/* Single TaskRootNode under each Repository. Labeled "Build Tasks" */
export class TaskRootNode extends NodeBase {
constructor(
public readonly label: string,
public readonly contextValue: string,
public subscription: SubscriptionModels.Subscription,
public readonly azureAccount: AzureAccount,
public registry: ContainerModels.Registry,
public readonly iconPath: any = {}
) {
super(label);
}

public name: string;

public getTreeItem(): vscode.TreeItem {
return {
label: this.label,
collapsibleState: vscode.TreeItemCollapsibleState.Collapsed,
contextValue: this.contextValue,
iconPath: this.iconPath
}
}

/* Making a list view of BuildTaskNodes, or the Build Tasks of the current registry */
public async getChildren(element: TaskRootNode): Promise<BuildTaskNode[]> {
const buildTaskNodes: BuildTaskNode[] = [];
let buildTasks: ContainerModels.BuildTask[] = [];

const client = AzureCredentialsManager.getInstance().getContainerRegistryManagementClient(element.subscription);
const resourceGroup: string = acrTools.getResourceGroup(element.registry);

buildTasks = await client.buildTasks.list(resourceGroup, element.registry.name);
if (buildTasks.length === 0) {
vscode.window.showInformationMessage(`You do not have any Build Tasks in the registry, '${element.registry.name}'. You can create one with ACR Build. `, "Learn More").then(val => {
if (val === "Learn More") {
opn('https://aka.ms/acr/buildtask');
}
})
}

for (let buildTask of buildTasks) {
let node = new BuildTaskNode(buildTask.name, "buildTaskNode");
buildTaskNodes.push(node);
}
return buildTaskNodes;
}
}

export class BuildTaskNode extends NodeBase {

constructor(
public readonly label: string,
public readonly contextValue: string,

Choose a reason for hiding this comment

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

where do you use the contextValue?

Copy link
Author

Choose a reason for hiding this comment

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

Nowhere yet, but later we will link the BuildTaskNode to open the corresponding log

) {
super(label);
}
}
1 change: 1 addition & 0 deletions images/dark/buildTasks_dark.svg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Loading