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

Logging and error handling for location utils #12258

Merged
merged 11 commits into from
Feb 7, 2020
7 changes: 6 additions & 1 deletion Tasks/Common/artifacts-common/connectionDataUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,12 @@ async function getServiceUriFromAreaId(areaId: string, accessToken: string): Pro
return serviceUriFromArea.locationUrl;
} catch (error) {
tl.debug(`Failed to obtain the service URI for area ID ${areaId}`);
tl.debug(JSON.stringify(error));
if (error instanceof Error) {
if (error.message) { tl.debug(error.message); }
if (error.stack) { tl.debug(error.stack); }
} else {
tl.debug(error);
}
throw error;
}
}
7 changes: 6 additions & 1 deletion Tasks/Common/artifacts-common/retryUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,12 @@ export async function retryOnException<T>(action: () => Promise<T>, maxTries: nu
throw error;
}
tl.debug(`Attempt failed. Number of tries left: ${maxTries}`);
tl.debug(JSON.stringify(error));
if (error instanceof Error) {
Copy link
Member

Choose a reason for hiding this comment

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

Why can't use the logError function here?

Copy link
Contributor

Choose a reason for hiding this comment

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

It's because I put the helper in packaging-common and this is in artifacts-common. I didn't want to create a dependency between the two common folders.

if (error.message) { tl.debug(error.message); }
if (error.stack) { tl.debug(error.stack); }
} else {
tl.debug(error);
}
await delay(retryIntervalInMilliseconds);
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,6 @@
"loc.messages.NGCommon_SpsNotFound": "Unable to find the '%s' [%s] area. There may be a problem with your Team Foundation Server installation.",
"loc.messages.NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.",
"loc.messages.NGCommon_UnableToFindTool": "Unable to find tool %s",
"loc.messages.Warning_SessionCreationFailed": "Could not create provenance session: %s",
"loc.messages.Warning_SessionCreationFailed": "Could not create provenance session.",
"loc.messages.Warning_UpdatingNuGetVersion": "Updating version of NuGet.exe to %s from %s. Behavior changes or breaking changes might occur as NuGet updates to a new version. If this is not desired, uncheck the 'Check for Latest Version' option in the task."
}
30 changes: 15 additions & 15 deletions Tasks/Common/packaging-common/locationUtilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import * as tl from 'azure-pipelines-task-lib/task';
import { IRequestOptions } from 'azure-devops-node-api/interfaces/common/VsoBaseInterfaces';

import * as provenance from "./provenance";
import { logError, LogType } from './util';

export enum ProtocolType {
NuGet,
Expand Down Expand Up @@ -36,12 +37,10 @@ export async function getServiceUriFromAreaId(serviceUri: string, accessToken: s
const locationApi = await webApi.getLocationsApi();

tl.debug(`Getting URI for area ID ${areaId} from ${serviceUri}`);
try {
const serviceUriFromArea = await retryOnExceptionHelper(() => locationApi.getResourceArea(areaId), 3, 1000);
return serviceUriFromArea.locationUrl;
} catch (error) {
throw new Error(error);
}
const resourceArea = await retryOnExceptionHelper(() => locationApi.getResourceArea(areaId), 3, 1000);
tl.debug(`Found resource area with locationUrl: ${resourceArea && resourceArea.locationUrl}`);

return resourceArea.locationUrl;
}

export async function getNuGetUriFromBaseServiceUri(serviceUri: string, accesstoken: string): Promise<string> {
Expand Down Expand Up @@ -87,16 +86,15 @@ export async function getPackagingUris(protocolType: ProtocolType): Promise<Pack
const areaId = getAreaIdForProtocol(protocolType);

const serviceUri = await getServiceUriFromAreaId(collectionUrl, accessToken, areaId);
tl.debug(`Found serviceUri: ${serviceUri}`);

const webApi = getWebApiWithProxy(serviceUri);

const locationApi = await webApi.getLocationsApi();

tl.debug('Acquiring Packaging endpoints from ' + serviceUri);

tl.debug('Acquiring Packaging endpoints...');
const connectionData = await retryOnExceptionHelper(() => locationApi.getConnectionData(interfaces.ConnectOptions.IncludeServices), 3, 1000);

tl.debug('Successfully acquired the connection data');

const defaultAccessPoint: string = connectionData.locationServiceData.accessMappings.find((mapping) =>
mapping.moniker === connectionData.locationServiceData.defaultAccessMappingMoniker
).accessPoint;
Expand Down Expand Up @@ -147,7 +145,9 @@ export function getWebApiWithProxy(serviceUri: string, accessToken?: string): vs
allowRetries: true,
maxRetries: 5
};
return new vsts.WebApi(serviceUri, credentialHandler, options);
const webApi = new vsts.WebApi(serviceUri, credentialHandler, options);
tl.debug(`Created webApi client for ${serviceUri}; options: ${JSON.stringify(options)}`);
return webApi;
}

// This function is to apply retries generically for any unreliable network calls
Expand All @@ -158,10 +158,10 @@ export async function retryOnExceptionHelper<T>(action: () => Promise<T>, maxTri
} catch (error) {
maxTries--;
if (maxTries < 1) {
throw Error(error);
throw error;
}
tl.debug(`Network call failed. Number of retries left: ${maxTries}`);
tl.debug(JSON.stringify(error));
if (error) { logError(error, LogType.warning); }
await delay(retryIntervalInMilliseconds);
}
}
Expand All @@ -178,8 +178,8 @@ interface RegistryLocation {
};

export async function getFeedRegistryUrl(
packagingUrl: string,
registryType: RegistryType,
packagingUrl: string,
registryType: RegistryType,
feedId: string,
project: string,
accessToken?: string,
Expand Down
2 changes: 1 addition & 1 deletion Tasks/Common/packaging-common/module.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"NGCommon_SpsNotFound": "Unable to find the '%s' [%s] area. There may be a problem with your Team Foundation Server installation.",
"NGCommon_UnabletoDetectNuGetVersion": "Unknown NuGet version selected.",
"NGCommon_UnableToFindTool": "Unable to find tool %s",
"Warning_SessionCreationFailed": "Could not create provenance session: %s",
"Warning_SessionCreationFailed": "Could not create provenance session.",
"Warning_UpdatingNuGetVersion": "Updating version of NuGet.exe to %s from %s. Behavior changes or breaking changes might occur as NuGet updates to a new version. If this is not desired, uncheck the 'Check for Latest Version' option in the task."
}
}
4 changes: 3 additions & 1 deletion Tasks/Common/packaging-common/provenance.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import * as tl from 'azure-pipelines-task-lib';
import * as VsoBaseInterfaces from 'azure-devops-node-api/interfaces/common/VsoBaseInterfaces';
import { ClientVersioningData } from 'azure-devops-node-api/VsoClient';
import vstsClientBases = require('azure-devops-node-api/ClientApiBases');
import { logError, LogType } from './util';

import * as restclient from 'typed-rest-client/RestClient';

Expand Down Expand Up @@ -67,7 +68,8 @@ export class ProvenanceHelper {
const session = await prov.createSession(sessionRequest, protocol, project);
return session.sessionId;
} catch (error) {
tl.warning(tl.loc("Warning_SessionCreationFailed", JSON.stringify(error)));
tl.warning(tl.loc("Warning_SessionCreationFailed"));
logError(error, LogType.warning);
}
}
return feedId;
Expand Down
32 changes: 30 additions & 2 deletions Tasks/Common/packaging-common/util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,8 +82,8 @@ export function getProjectAndFeedIdFromInputParam(inputParam: string): any {
}

export function getProjectAndFeedIdFromInput(feedProject: string): any {
var projectId = null;
var feedId = feedProject;
let projectId = null;
let feedId = feedProject;
if(feedProject && feedProject.includes("/")) {
const feedProjectParts = feedProject.split("/");
projectId = feedProjectParts[0] || null;
Expand All @@ -95,3 +95,31 @@ export function getProjectAndFeedIdFromInput(feedProject: string): any {
projectId: projectId
}
}

export enum LogType {
debug,
warning,
error
}

function log(message: string, logType: LogType) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Any reason no to default logType to debug?

Copy link
Contributor

Choose a reason for hiding this comment

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

No particular reason. This will only be called by the exported logError function below so I'll leave it as is

if (logType === LogType.warning) {
Copy link
Member Author

Choose a reason for hiding this comment

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

For branching behavior for enums I typically use a switch (with a "default: throw UnknownEnum"). That fails faster when a new enum value is added and the branching wasn't updated.

Copy link
Contributor

Choose a reason for hiding this comment

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

Switch is a good one. If we add more log types we can change it to a switch but since there are only three options I'll leave it as is for now. Thanks!

tl.warning(message);
} else if (logType === LogType.error) {
tl.error(message);
} else {
tl.debug(message);
}
}

/**
* Logs the error instead of throwing.
*/
export function logError(error: any, logType: LogType = LogType.debug) {
Copy link
Member Author

Choose a reason for hiding this comment

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

Consider talking to the pipelines team to see if we can add behavior to internal.ts

if (error instanceof Error) {
if (error.message) { log(error.message, logType); }
if (error.stack) { log(error.stack, LogType.debug); } // Log stack always as debug
} else {
log(`Error: ${error}`, logType);
}
}
4 changes: 2 additions & 2 deletions Tasks/DotNetCoreCLIV2/pushcommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import { IExecOptions } from 'azure-pipelines-task-lib/toolrunner';
import { NuGetConfigHelper2 } from 'packaging-common/nuget/NuGetConfigHelper2';
import * as ngRunner from 'packaging-common/nuget/NuGetToolRunner2';
import * as pkgLocationUtils from 'packaging-common/locationUtilities';
import { getProjectAndFeedIdFromInputParam } from 'packaging-common/util';
import { getProjectAndFeedIdFromInputParam, logError } from 'packaging-common/util';

export async function run(): Promise<void> {
let packagingLocation: pkgLocationUtils.PackagingLocation;
try {
packagingLocation = await pkgLocationUtils.getPackagingUris(pkgLocationUtils.ProtocolType.NuGet);
} catch (error) {
tl.debug('Unable to get packaging URIs, using default collection URI');
tl.debug(JSON.stringify(error));
logError(error);
const collectionUrl = tl.getVariable('System.TeamFoundationCollectionUri');
packagingLocation = {
PackagingUris: [collectionUrl],
Expand Down
4 changes: 2 additions & 2 deletions Tasks/DotNetCoreCLIV2/restorecommand.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,15 @@ import { IExecOptions } from 'azure-pipelines-task-lib/toolrunner';
import * as nutil from 'packaging-common/nuget/Utility';
import * as commandHelper from 'packaging-common/nuget/CommandHelper';
import * as pkgLocationUtils from 'packaging-common/locationUtilities';
import { getProjectAndFeedIdFromInputParam } from 'packaging-common/util';
import { getProjectAndFeedIdFromInputParam, logError } from 'packaging-common/util';

export async function run(): Promise<void> {
let packagingLocation: pkgLocationUtils.PackagingLocation;
try {
packagingLocation = await pkgLocationUtils.getPackagingUris(pkgLocationUtils.ProtocolType.NuGet);
} catch (error) {
tl.debug('Unable to get packaging URIs, using default collection URI');
tl.debug(JSON.stringify(error));
logError(error);
const collectionUrl = tl.getVariable('System.TeamFoundationCollectionUri');
packagingLocation = {
PackagingUris: [collectionUrl],
Expand Down
2 changes: 1 addition & 1 deletion Tasks/DotNetCoreCLIV2/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"demands": [],
"version": {
"Major": 2,
"Minor": 164,
"Minor": 165,
"Patch": 0
},
"minimumAgentVersion": "2.115.0",
Expand Down
2 changes: 1 addition & 1 deletion Tasks/DotNetCoreCLIV2/task.loc.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"demands": [],
"version": {
"Major": 2,
"Minor": 164,
"Minor": 165,
"Patch": 0
},
"minimumAgentVersion": "2.115.0",
Expand Down
27 changes: 17 additions & 10 deletions Tasks/DownloadPackageV0/npm-shrinkwrap.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion Tasks/DownloadPackageV0/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"author": "ms-vscs-rm",
"version": {
"Major": 0,
"Minor": 164,
"Minor": 165,
"Patch": 0
},
"demands": [],
Expand Down
4 changes: 2 additions & 2 deletions Tasks/DownloadPackageV0/task.loc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"author": "ms-vscs-rm",
"version": {
"Major": 0,
"Minor": 161,
"Patch": 1
"Minor": 165,
"Patch": 0
},
"demands": [],
"minimumAgentVersion": "1.99.0",
Expand Down
2 changes: 1 addition & 1 deletion Tasks/DownloadPackageV1/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"author": "ms-vscs-rm",
"version": {
"Major": 1,
"Minor": 164,
"Minor": 165,
"Patch": 0
},
"demands": [],
Expand Down
6 changes: 3 additions & 3 deletions Tasks/DownloadPackageV1/task.loc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@
"author": "ms-vscs-rm",
"version": {
"Major": 1,
"Minor": 161,
"Patch": 2
"Minor": 165,
"Patch": 0
},
"demands": [],
"releaseNotes": "ms-resource:loc.releaseNotes",
Expand Down Expand Up @@ -186,4 +186,4 @@
"Info_Downloading": "ms-resource:loc.messages.Info_Downloading",
"Info_UsingArtifactToolDownload": "ms-resource:loc.messages.Info_UsingArtifactToolDownload"
}
}
}
2 changes: 1 addition & 1 deletion Tasks/MavenAuthenticateV0/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"author": "Microsoft Corporation",
"version": {
"Major": 0,
"Minor": 164,
"Minor": 165,
"Patch": 0
},
"runsOn": [
Expand Down
3 changes: 2 additions & 1 deletion Tasks/MavenV2/mavenutil.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import fs = require('fs');
import * as tl from 'azure-pipelines-task-lib/task';
import * as tr from 'azure-pipelines-task-lib/toolrunner';
import * as pkgLocationUtils from "packaging-common/locationUtilities";
import { logError } from 'packaging-common/util';

import * as url from "url";
import * as xml2js from 'xml2js';
Expand Down Expand Up @@ -200,7 +201,7 @@ async function collectFeedRepositories(pomContents:string): Promise<any> {
packagingLocation = await pkgLocationUtils.getPackagingUris(pkgLocationUtils.ProtocolType.Maven);
} catch (error) {
tl.debug("Unable to get packaging URIs, using default collection URI");
tl.debug(JSON.stringify(error));
logError(error);
packagingLocation = {
PackagingUris: [collectionUrl],
DefaultPackagingUri: collectionUrl
Expand Down
2 changes: 1 addition & 1 deletion Tasks/MavenV2/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"author": "Microsoft Corporation",
"version": {
"Major": 2,
"Minor": 164,
"Minor": 165,
"Patch": 0
},
"releaseNotes": "Configuration of the SonarQube analysis was moved to the [SonarQube](https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarqube) or [SonarCloud](https://marketplace.visualstudio.com/items?itemName=SonarSource.sonarcloud) extensions, in task `Prepare Analysis Configuration`",
Expand Down
2 changes: 1 addition & 1 deletion Tasks/MavenV2/task.loc.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
"author": "Microsoft Corporation",
"version": {
"Major": 2,
"Minor": 161,
"Minor": 165,
"Patch": 0
},
"releaseNotes": "ms-resource:loc.releaseNotes",
Expand Down
Loading