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

[Helm tool installer] Changing the way API call is made #8529

Merged
merged 1 commit into from
Oct 9, 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
72 changes: 23 additions & 49 deletions Tasks/HelmInstallerV0/src/helminstaller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,42 +7,42 @@ import * as toolLib from 'vsts-task-tool-lib/tool';
import * as utils from './utils';
import * as os from "os";
import * as util from "util";
import { WebRequest, sendRequest } from "./webClient";
const uuidV4 = require('uuid/v4');
import downloadutility = require("utility-common/downloadutility");
const helmToolName = "helm"
const helmLatestReleaseUrl = "https://api.github.com/repos/helm/helm/releases/latest";
const stableHelmVersion = "v2.9.1"

export async function getHelmVersion(): Promise<string> {
var checkLatestHelmVersion = tl.getBoolInput('checkLatestHelmVersion', false);
if(checkLatestHelmVersion) {
var checkLatestHelmVersion = tl.getBoolInput('checkLatestHelmVersion', false);
if (checkLatestHelmVersion) {
return await getStableHelmVersion();
}

return utils.sanitizeVersionString(tl.getInput("helmVersion", true));
}

export async function downloadHelm(version : string): Promise<string> {
export async function downloadHelm(version: string): Promise<string> {
var cachedToolpath = toolLib.findLocalTool(helmToolName, version);
if(!cachedToolpath) {
if (!cachedToolpath) {
try {
var helmDownloadPath = await toolLib.downloadTool(getHelmDownloadURL(version), helmToolName + "-" + version + "-" + uuidV4() +".zip");
} catch(exception) {
var helmDownloadPath = await toolLib.downloadTool(getHelmDownloadURL(version), helmToolName + "-" + version + "-" + uuidV4() + ".zip");
} catch (exception) {
throw new Error(tl.loc("HelmDownloadFailed", getHelmDownloadURL(version), exception));
}

var unzipedHelmPath = await toolLib.extractZip(helmDownloadPath);
cachedToolpath = await toolLib.cacheDir(unzipedHelmPath, helmToolName, version);
}

var helmpath = findHelm(cachedToolpath);
if(!helmpath) {
if (!helmpath) {
throw new Error(tl.loc("HelmNotFoundInFolder", cachedToolpath))
}

fs.chmodSync(helmpath, "777");
return helmpath;
}
}

function findHelm(rootFolder: string) {
var helmPath = path.join(rootFolder, "*", helmToolName + getExecutableExtention());
Expand All @@ -52,9 +52,8 @@ function findHelm(rootFolder: string) {
}


function getHelmDownloadURL(version: string) : string {
switch(os.type())
{
function getHelmDownloadURL(version: string): string {
switch (os.type()) {
case 'Linux':
return util.format("https://storage.googleapis.com/kubernetes-helm/helm-%s-linux-amd64.zip", version);

Expand All @@ -63,55 +62,30 @@ function getHelmDownloadURL(version: string) : string {

default:
case 'Windows_NT':
return util.format("https://storage.googleapis.com/kubernetes-helm/helm-%s-windows-amd64.zip", version);
return util.format("https://storage.googleapis.com/kubernetes-helm/helm-%s-windows-amd64.zip", version);

}
}

async function getStableHelmVersion() : Promise<string>{
var downloadPath = path.join(getTempDirectory(), uuidV4() +".json");
var options = {
hostname: 'api.github.com',
port: 443,
path: '/repos/helm/helm/releases/latest',
method: 'GET',
secureProtocol: "TLSv1_2_method",
headers: {
'User-Agent' : 'vsts'
}
}
async function getStableHelmVersion(): Promise<string> {
var request = new WebRequest();
request.uri = "https://api.github.com/repos/helm/helm/releases/latest";
request.method = "GET";

try{
await downloadutility.download(options, downloadPath, true, true);
var version = await getReleaseVersion(downloadPath);
return version;
} catch(error) {
try {
var response = await sendRequest(request);
return response.body["tag_name"];
} catch (error) {
tl.warning(tl.loc("HelmLatestNotKnown", helmLatestReleaseUrl, error, stableHelmVersion));
}

return stableHelmVersion;
}

function getExecutableExtention(): string {
if(os.type().match(/^Win/)){
if (os.type().match(/^Win/)) {
return ".exe";
}

return "";
}

function getTempDirectory(): string {
return tl.getVariable('agent.tempDirectory') || os.tmpdir();
}

function getReleaseVersion(jsonFilePath): Promise<string> {
return new Promise(function (fulfill, reject){
fs.readFile(jsonFilePath, {encoding: 'utf8'} ,function(err,data) {
if(err) {
reject(err);
}
var latestVersionInfo = JSON.parse(data);
fulfill(latestVersionInfo.tag_name);
})
});
}
}
112 changes: 112 additions & 0 deletions Tasks/HelmInstallerV0/src/webClient.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
import tl = require('vsts-task-lib/task');
import httpClient = require("typed-rest-client/HttpClient");
import httpInterfaces = require("typed-rest-client/Interfaces");
import util = require("util");

let proxyUrl: string = tl.getVariable("agent.proxyurl");
var requestOptions: httpInterfaces.IRequestOptions = proxyUrl ? {
proxy: {
proxyUrl: proxyUrl,
proxyUsername: tl.getVariable("agent.proxyusername"),
proxyPassword: tl.getVariable("agent.proxypassword"),
proxyBypassHosts: tl.getVariable("agent.proxybypasslist") ? JSON.parse(tl.getVariable("agent.proxybypasslist")) : null
}
} : {};

let ignoreSslErrors: string = tl.getVariable("VSTS_ARM_REST_IGNORE_SSL_ERRORS");
requestOptions.ignoreSslError = ignoreSslErrors && ignoreSslErrors.toLowerCase() == "true";

var httpCallbackClient = new httpClient.HttpClient(tl.getVariable("AZURE_HTTP_USER_AGENT"), null, requestOptions);

export class WebRequest {
public method: string;
public uri: string;
// body can be string or ReadableStream
public body: string;
public headers: any;
}

export class WebResponse {
public statusCode: number;
public statusMessage: string;
public headers: any;
public body: any;
}

export class WebRequestOptions {
public retriableErrorCodes: string[];
public retryCount: number;
public retryIntervalInSeconds: number;
public retriableStatusCodes: number[];
public retryRequestTimedout: boolean;
}

export async function sendRequest(request: WebRequest, options?: WebRequestOptions): Promise<WebResponse> {
let i = 0;
let retryCount = options && options.retryCount ? options.retryCount : 5;
let retryIntervalInSeconds = options && options.retryIntervalInSeconds ? options.retryIntervalInSeconds : 2;
let retriableErrorCodes = options && options.retriableErrorCodes ? options.retriableErrorCodes : ["ETIMEDOUT", "ECONNRESET", "ENOTFOUND", "ESOCKETTIMEDOUT", "ECONNREFUSED", "EHOSTUNREACH", "EPIPE", "EA_AGAIN"];
let retriableStatusCodes = options && options.retriableStatusCodes ? options.retriableStatusCodes : [408, 409, 500, 502, 503, 504];
let timeToWait: number = retryIntervalInSeconds;
while (true) {
try {
let response: WebResponse = await sendRequestInternal(request);
if (retriableStatusCodes.indexOf(response.statusCode) != -1 && ++i < retryCount) {
tl.debug(util.format("Encountered a retriable status code: %s. Message: '%s'.", response.statusCode, response.statusMessage));
await sleepFor(timeToWait);
timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds;
continue;
}

return response;
}
catch (error) {
if (retriableErrorCodes.indexOf(error.code) != -1 && ++i < retryCount) {
tl.debug(util.format("Encountered a retriable error:%s. Message: %s.", error.code, error.message));
await sleepFor(timeToWait);
timeToWait = timeToWait * retryIntervalInSeconds + retryIntervalInSeconds;
}
else {
if (error.code) {
console.log("##vso[task.logissue type=error;code=" + error.code + ";]");
}

throw error;
}
}
}
}

export function sleepFor(sleepDurationInSeconds): Promise<any> {
return new Promise((resolve, reject) => {
setTimeout(resolve, sleepDurationInSeconds * 1000);
});
}

async function sendRequestInternal(request: WebRequest): Promise<WebResponse> {
tl.debug(util.format("[%s]%s", request.method, request.uri));
var response: httpClient.HttpClientResponse = await httpCallbackClient.request(request.method, request.uri, request.body, request.headers);
return await toWebResponse(response);
}

async function toWebResponse(response: httpClient.HttpClientResponse): Promise<WebResponse> {
var res = new WebResponse();
if (response) {
res.statusCode = response.message.statusCode;
res.statusMessage = response.message.statusMessage;
res.headers = response.message.headers;
var body = await response.readBody();
if (body) {
try {
res.body = JSON.parse(body);
}
catch (error) {
tl.debug("Could not parse response: " + JSON.stringify(error));
tl.debug("Response: " + JSON.stringify(res.body));
res.body = body;
}
}
}

return res;
}
2 changes: 1 addition & 1 deletion Tasks/HelmInstallerV0/task.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"version": {
"Major": 0,
"Minor": 1,
"Patch": 9
"Patch": 10
},
"demands": [],
"satisfies": [
Expand Down
2 changes: 1 addition & 1 deletion Tasks/HelmInstallerV0/task.loc.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"version": {
"Major": 0,
"Minor": 1,
"Patch": 8
"Patch": 10
},
"demands": [],
"satisfies": [
Expand Down