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

Add support for custom packer template #3789

Merged
merged 3 commits into from
Mar 16, 2017
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 @@ -3,7 +3,13 @@
"loc.helpMarkDown": "[More Information](https://aka.ms/argtaskreadme)",
"loc.description": "Build VMSS images using Packer",
"loc.instanceNameFormat": "Packer build",
"loc.group.displayName.AzureDetails": "Azure Details",
"loc.group.displayName.DeploymentInputs": "Deployment Inputs",
"loc.group.displayName.Output": "Output",
"loc.input.label.templateType": "Packer template",
"loc.input.help.templateType": "Select whether task will use a built-in packer template or a custom user-provided template.",
"loc.input.label.customTemplateLocation": "Packer template location",
"loc.input.help.customTemplateLocation": "Path to a custom user-provided template.",
"loc.input.label.ConnectedServiceName": "Azure subscription",
"loc.input.help.ConnectedServiceName": "Select the Azure Resource Manager subscription for the deployment.",
"loc.input.label.location": "Location",
Expand Down Expand Up @@ -40,6 +46,7 @@
"loc.messages.ImageURIOutputVariableNotFound": "Could not get vhd image URI from packer execution. Output variable will not be set.",
"loc.messages.StorageAccountLocationOutputVariableNotFound": "Could not get vhd image storage account location from packer execution. Output variable will not be set.",
"loc.messages.BuiltInTemplateNotFoundErrorMessagePathName": "Built-in template for OS type: %s ",
"loc.messages.CustomTemplateNotFoundErrorMessagePathName": "Custom template not found at location: %s",
"loc.messages.CouldNotDeleteTemporaryTemplateDirectory": "Could not delete temporary template directory %s. Please delete manually.",
"loc.messages.TaskParametersConstructorFailed": "Error happened while initializing task: %s.",
"loc.messages.PackerFixFailed": "Packer fix command failed. This could happen if task does not support packer version.",
Expand Down
75 changes: 75 additions & 0 deletions Tasks/PackerBuild/Tests/L0.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,18 @@ describe('PackerBuild Suite', function() {
done();
});

it('Runs successfully for custom template', (done:MochaDone) => {
let tp = path.join(__dirname, 'L0CustomTemplate.js');
let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();

assert(tr.invokedToolCount == 3, 'should have invoked tool thrice. actual: ' + tr.invokedToolCount);
assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr');
assert(tr.succeeded, 'task should have succeeded');

done();
});

it('Creates output variables from packer log', (done:MochaDone) => {
let tp = path.join(__dirname, 'L0Windows.js');
let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp);
Expand All @@ -61,6 +73,19 @@ describe('PackerBuild Suite', function() {
done();
});

it('Creates output variables from packer log for custom template', (done:MochaDone) => {
let tp = path.join(__dirname, 'L0CustomTemplate.js');
let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();

assert(tr.invokedToolCount == 3, 'should have invoked tool thrice. actual: ' + tr.invokedToolCount);
assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr');
assert(tr.succeeded, 'task should have succeeded');
assert(tr.stdout.indexOf("##vso[task.setvariable variable=imageUri;secret=false;]https://bishalpackerimages.blob.core.windows.net/system/Microsoft.Compute/Images/packer/packer-osDisk.e2e08a75-2d73-49ad-97c2-77f8070b65f5.vhd") != -1, "image uri output variable not set");
assert(tr.stdout.indexOf("##vso[task.setvariable variable=imageStorageAccount;secret=false;]SouthIndia") != -1, "imageStorageAccount location output variable not set");
done();
});

it('Should copy builtin template to temp location for windows template', (done:MochaDone) => {
let tp = path.join(__dirname, 'L0Windows.js');
let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp);
Expand Down Expand Up @@ -90,6 +115,18 @@ describe('PackerBuild Suite', function() {
done();
});

it('Should copy custom template to temp location', (done:MochaDone) => {
let tp = path.join(__dirname, 'L0CustomTemplate.js');
let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();

assert(tr.invokedToolCount == 3, 'should have invoked tool thrice. actual: ' + tr.invokedToolCount);
assert(tr.stderr.length == 0 || tr.errorIssues.length, 'should not have written to stderr');
assert(tr.succeeded, 'task should have succeeded');
assert(tr.stdout.indexOf("copying C:\\custom.template.json to F:\\somedir\\tempdir\\100") != -1, "custom template should be copied to temp location");
done();
});

it('Should invoke three packer commands - fix, validate and build', (done:MochaDone) => {
let tp = path.join(__dirname, 'L0Windows.js');
let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp);
Expand Down Expand Up @@ -152,6 +189,18 @@ describe('PackerBuild Suite', function() {
done();
});

it('should fail if custom template copy fails', (done:MochaDone) => {
process.env["__copy_fails__"] = "true";
let tp = path.join(__dirname, 'L0CustomTemplate.js');
let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();
process.env["__copy_fails__"] = "false";

assert(tr.failed, 'task should have failed');
assert(tr.stdout.indexOf("copy failed") != -1, "error message should be right");
done();
});

it('should fail if os type is not supported', (done:MochaDone) => {
process.env["__ostype__"] = "random";
process.env["__copy_fails__"] = "false";
Expand Down Expand Up @@ -230,6 +279,32 @@ describe('PackerBuild Suite', function() {
done();
});

it('should fail if packer build exits with non zero code for linux', (done:MochaDone) => {
process.env["__packer_build_fails__"] = "true";
let tp = path.join(__dirname, 'L0Linux.js');
let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();
process.env["__packer_build_fails__"] = "false";

assert(tr.failed, 'task should have failed');
assert(tr.invokedToolCount == 3, 'all 3 commands should have been invoked. actual: ' + tr.invokedToolCount);
assert(tr.stdout.indexOf("packer build failed\r\nsome error") != -1, "error message should be right");
done();
});

it('should fail if packer build exits with non zero code for custom template', (done:MochaDone) => {
process.env["__packer_build_fails__"] = "true";
let tp = path.join(__dirname, 'L0CustomTemplate.js');
let tr : ttm.MockTestRunner = new ttm.MockTestRunner(tp);
tr.run();
process.env["__packer_build_fails__"] = "false";

assert(tr.failed, 'task should have failed');
assert(tr.invokedToolCount == 3, 'all 3 commands should have been invoked. actual: ' + tr.invokedToolCount);
assert(tr.stdout.indexOf("packer build failed\r\nsome error") != -1, "error message should be right");
done();
});

it('should fail if output variables cannot be parsed from packer log', (done:MochaDone) => {
process.env["__packer_build_no_output__"] = "true";
process.env["__packer_build_fails__"] = "false";
Expand Down
77 changes: 77 additions & 0 deletions Tasks/PackerBuild/Tests/L0CustomTemplate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
import ma = require('vsts-task-lib/mock-answer');
import tmrm = require('vsts-task-lib/mock-run');
import path = require('path');

let taskPath = path.join(__dirname, '..\\src\\main.js');
let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);

tr.setInput('templateType', 'custom');
tr.setInput('customTemplateLocation', 'C:\\custom.template.json');
tr.setInput('imageUri', 'imageUri');
tr.setInput('imageStorageAccount', 'imageStorageAccount');

process.env["RELEASE_RELEASENAME"] = "Release-1";

// provide answers for task mock
let a: any = <any>{
"which": {
"packer": "packer"
},
"checkPath": {
"packer": true,
"C:\\custom.template.json": true
},
"exec": {
"packer fix -validate=false F:\\somedir\\tempdir\\100\\custom.template.json": {
"code": 0,
"stdout": "{ \"some-key\": \"some-value\" }"
},
"packer validate F:\\somedir\\tempdir\\100\\custom.template-fixed.json": {
"code": 0,
"stdout": "Executed Successfully"
},
"packer build -force F:\\somedir\\tempdir\\100\\custom.template-fixed.json": {
"code": process.env["__packer_build_fails__"] === "true" ? 1 : 0,
"stdout": process.env["__packer_build_fails__"] === "true" ? "packer build failed\r\nsome error" : "Executed Successfully\nOSDiskUri: https://bishalpackerimages.blob.core.windows.net/system/Microsoft.Compute/Images/packer/packer-osDisk.e2e08a75-2d73-49ad-97c2-77f8070b65f5.vhd\nStorageAccountLocation: SouthIndia",
}
},
"exist": {
"F:\\somedir\\tempdir\\100": true,
"F:\\somedir\\tempdir\\100\\": true
},
"rmRF": {
"F:\\somedir\\tempdir\\100": { 'success': true }
}
};

var ut = require('../src/utilities');
tr.registerMock('./utilities', {
IsNullOrEmpty : ut.IsNullOrEmpty,
HasItems : ut.HasItems,
StringWritable: ut.StringWritable,
copyFile: function(source: string, destination: string) {
if(process.env["__copy_fails__"] === "true") {
throw "copy failed";
} else {
console.log('copying ' + source + ' to ' + destination);
}
},
writeFile: function(filePath: string, content: string) {
console.log("writing to file " + filePath + " content: " + content);
},
findMatch: function(root: string, patterns: string[] | string) {
return ["C:\\dummy.zip"];
},
getCurrentTime: function() {
return 100;
},
getTempDirectory: function() {
return "F:\\somedir\\tempdir"
},
getCurrentDirectory: function() {
return "basedir\\currdir";
}
});

tr.setAnswers(a);
tr.run();
12 changes: 7 additions & 5 deletions Tasks/PackerBuild/Tests/L0Linux.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import path = require('path');
let taskPath = path.join(__dirname, '..\\src\\main.js');
let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);

tr.setInput('templateType', process.env["__template_type__"] || 'builtin');
tr.setInput('azureResourceGroup', 'testrg');
tr.setInput('storageAccountName', 'teststorage');
tr.setInput('baseImage', 'Canonical:UbuntuServer:14.04.4-LTS:linux');
Expand All @@ -29,7 +30,8 @@ let a: any = <any>{
},
"checkPath": {
"packer": true,
"basedir\\DefaultTemplates\\default.linux.template.json": true
"basedir\\DefaultTemplates\\default.linux.template.json": true,
"/packer-user-scripts/deploy.sh": true
},
"exec": {
"packer fix -validate=false /tmp/tempdir/100/default.linux.template.json": {
Expand All @@ -41,8 +43,8 @@ let a: any = <any>{
"stdout": "Executed Successfully"
},
"packer build -force -var resource_group=testrg -var storage_account=teststorage -var image_publisher=Canonical -var image_offer=UbuntuServer -var image_sku=14.04.4-LTS -var location=South India -var capture_name_prefix=Release-1 -var script_path=/packer-user-scripts/deploy.sh -var script_name=deploy.sh -var package_path=/packer-user-scripts/dummy.tar.gz -var package_name=dummy.tar.gz -var script_arguments=\"subdir 1\" false -var subscription_id=sId -var client_id=spId -var client_secret=spKey -var tenant_id=tenant -var object_id=oId /tmp/tempdir/100/default.linux.template-fixed.json": {
"code": 0,
"stdout": "Executed Successfully\nOSDiskUri: https://bishalpackerimages.blob.core.windows.net/system/Microsoft.Compute/Images/packer/packer-osDisk.e2e08a75-2d73-49ad-97c2-77f8070b65f5.vhd\nStorageAccountLocation: SouthIndia"
"code": process.env["__packer_build_fails__"] === "true" ? 1 : 0,
"stdout": process.env["__packer_build_fails__"] === "true" ? "packer build failed\r\nsome error" : "Executed Successfully\nOSDiskUri: https://bishalpackerimages.blob.core.windows.net/system/Microsoft.Compute/Images/packer/packer-osDisk.e2e08a75-2d73-49ad-97c2-77f8070b65f5.vhd\nStorageAccountLocation: SouthIndia",
},
"packer fix -validate=false \\tmp\\tempdir\\100\\default.linux.template.json": {
"code": 0,
Expand All @@ -53,8 +55,8 @@ let a: any = <any>{
"stdout": "Executed Successfully"
},
"packer build -force -var resource_group=testrg -var storage_account=teststorage -var image_publisher=Canonical -var image_offer=UbuntuServer -var image_sku=14.04.4-LTS -var location=South India -var capture_name_prefix=Release-1 -var script_path=/packer-user-scripts/deploy.sh -var script_name=deploy.sh -var package_path=/packer-user-scripts/dummy.tar.gz -var package_name=dummy.tar.gz -var script_arguments=\"subdir 1\" false -var subscription_id=sId -var client_id=spId -var client_secret=spKey -var tenant_id=tenant -var object_id=oId \\tmp\\tempdir\\100\\default.linux.template-fixed.json": {
"code": 0,
"stdout": "Executed Successfully\nOSDiskUri: https://bishalpackerimages.blob.core.windows.net/system/Microsoft.Compute/Images/packer/packer-osDisk.e2e08a75-2d73-49ad-97c2-77f8070b65f5.vhd\nStorageAccountLocation: SouthIndia"
"code": process.env["__packer_build_fails__"] === "true" ? 1 : 0,
"stdout": process.env["__packer_build_fails__"] === "true" ? "packer build failed\r\nsome error" : "Executed Successfully\nOSDiskUri: https://bishalpackerimages.blob.core.windows.net/system/Microsoft.Compute/Images/packer/packer-osDisk.e2e08a75-2d73-49ad-97c2-77f8070b65f5.vhd\nStorageAccountLocation: SouthIndia",
}
},
"exist": {
Expand Down
4 changes: 3 additions & 1 deletion Tasks/PackerBuild/Tests/L0Parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import path = require('path');
let taskPath = path.join(__dirname, '..\\src\\main.js');
let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);

tr.setInput('templateType', process.env["__template_type__"] || 'builtin');
tr.setInput('azureResourceGroup', 'testrg');
tr.setInput('storageAccountName', 'teststorage');
tr.setInput('baseImage', 'MicrosoftWindowsServer:WindowsServer:2012-R2-Datacenter:windows');
Expand All @@ -28,7 +29,8 @@ let a: any = <any>{
},
"checkPath": {
"packer": true,
"basedir\\DefaultTemplates\\default.windows.template.json": true
"basedir\\DefaultTemplates\\default.windows.template.json": true,
"C:\\deploy.ps1": true
},
"exec": {
"packer fix -validate=false F:\\somedir\\tempdir\\100\\default.windows.template.json": {
Expand Down
3 changes: 2 additions & 1 deletion Tasks/PackerBuild/Tests/L0Utilities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@ let a: any = <any>{
},
"checkPath": {
"packer": true,
".\\DefaultTemplates\\default.windows.template.json": process.env["__source_path_exists__"]=== "true" ? true : false
".\\DefaultTemplates\\default.windows.template.json": process.env["__source_path_exists__"]=== "true" ? true : false,
"C:\\deploy.ps1": true
},
"exist": {
"F:\\somedir\\tempdir\\100": process.env["__dest_path_exists__"] === "true" ? true : false
Expand Down
4 changes: 3 additions & 1 deletion Tasks/PackerBuild/Tests/L0Windows.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import path = require('path');
let taskPath = path.join(__dirname, '..\\src\\main.js');
let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);

tr.setInput('templateType', process.env["__template_type__"] || 'builtin');
tr.setInput('azureResourceGroup', 'testrg');
tr.setInput('storageAccountName', 'teststorage');
tr.setInput('baseImage', 'MicrosoftWindowsServer:WindowsServer:2012-R2-Datacenter:windows');
Expand All @@ -29,7 +30,8 @@ let a: any = <any>{
},
"checkPath": {
"packer": true,
"basedir\\DefaultTemplates\\default.windows.template.json": true
"basedir\\DefaultTemplates\\default.windows.template.json": true,
"C:\\deploy.ps1": true
},
"exec": {
"packer fix -validate=false F:\\somedir\\tempdir\\100\\default.windows.template.json": {
Expand Down
4 changes: 3 additions & 1 deletion Tasks/PackerBuild/Tests/L0WindowsFail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import path = require('path');
let taskPath = path.join(__dirname, '..\\src\\main.js');
let tr: tmrm.TaskMockRunner = new tmrm.TaskMockRunner(taskPath);

tr.setInput('templateType', process.env["__template_type__"] || 'builtin');
tr.setInput('azureResourceGroup', 'testrg');
tr.setInput('storageAccountName', 'teststorage');
tr.setInput('baseImage', !!process.env["__ostype__"] ? 'MicrosoftWindowsServer:WindowsServer:2012-R2-Datacenter:' + process.env["__ostype__"] : 'MicrosoftWindowsServer:WindowsServer:2012-R2-Datacenter:windows');
Expand All @@ -30,7 +31,8 @@ let a: any = <any>{
},
"checkPath": {
"packer": process.env["__packer_exists__"] === "false" ? false : true,
"basedir\\DefaultTemplates\\default.windows.template.json": process.env["__copy_fails__"] === "true" ? false : true
"basedir\\DefaultTemplates\\default.windows.template.json": process.env["__copy_fails__"] === "true" ? false : true,
"C:\\deploy.ps1": true
},
"exec": {
"packer fix -validate=false F:\\somedir\\tempdir\\100\\default.windows.template.json": {
Expand Down
62 changes: 62 additions & 0 deletions Tasks/PackerBuild/src/TemplateFileProviderBase.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"use strict";

import * as path from "path";
import * as util from "util";
import * as tl from "vsts-task-lib/task";
import * as constants from "./constants";
import * as definitions from "./definitions"
import * as utils from "./utilities"

export default class TemplateFileProviderBase {

public FinalizeTemplateLocation(initialTemplateFileLocation: string): void {
console.log(tl.loc("OriginalTemplateLocation", initialTemplateFileLocation));

// move file to a temp folder - this is a cautionary approach so that previous packer execution which still has handle on template does not cause any problem
var tempLocationForTemplate = path.join(utils.getTempDirectory(), utils.getCurrentTime().toString())
console.log(tl.loc("CopyingTemplate", initialTemplateFileLocation, tempLocationForTemplate));
utils.copyFile(initialTemplateFileLocation, tempLocationForTemplate);
console.log(tl.loc("TempTemplateLocation", tempLocationForTemplate));

// construct new full path for template file
var templateFileName = path.basename(initialTemplateFileLocation);
var tempFileLocation = path.join(tempLocationForTemplate, templateFileName);
this._templateFileLocation = tempFileLocation;
tl.debug("template location: " + tempFileLocation);
}

public updateTemplateFile(content: string): void {
if(utils.IsNullOrEmpty(content)) {
return;
}

var templateFileName = path.basename(this._templateFileLocation, '.json');
var templateDir = path.dirname(this._templateFileLocation);
var updatedTemplateFileName = util.format("%s-fixed.json", templateFileName);
var tempFileLocation = path.join(templateDir, updatedTemplateFileName);

utils.writeFile(tempFileLocation, content);

this._templateFileLocation = tempFileLocation;
tl.debug("fixed template location: " + tempFileLocation);
}

public cleanup(): void {
if(!this._templateFileLocation) {
return;
}

var templateFileDirectory = path.dirname(this._templateFileLocation);
try{
if(tl.exist(templateFileDirectory)) {
tl.debug("Cleaning-up temporary directory " + this._templateFileLocation);
tl.rmRF(templateFileDirectory, true);
}
}
catch (err) {
tl.warning(tl.loc("CouldNotDeleteTemporaryTemplateDirectory", templateFileDirectory));
}
}

protected _templateFileLocation: string;
}
Loading