@webda/aws
Version:
Webda AWS Services implementation
595 lines • 24.7 kB
JavaScript
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
return c > 3 && r && Object.defineProperty(target, key, r), r;
};
import { ACM, DescribeCertificateCommand, ListCertificatesCommand, RequestCertificateCommand } from "@aws-sdk/client-acm";
import { DescribeSubnetsCommand, DescribeVpcsCommand, EC2 } from "@aws-sdk/client-ec2";
import { ChangeResourceRecordSetsCommand, ListHostedZonesCommand, Route53 } from "@aws-sdk/client-route-53";
import { CreateBucketCommand, HeadBucketCommand, ListObjectsV2Command, NotFound, PutBucketTaggingCommand, PutObjectCommand, S3 } from "@aws-sdk/client-s3";
import { GetCallerIdentityCommand, STS } from "@aws-sdk/client-sts";
import { suite, test } from "@testdeck/mocha";
import { CacheService, getCommonJS } from "@webda/core";
import { DeployerTest } from "@webda/shell/lib/deployers/deployertest.js";
import * as assert from "assert";
import { mockClient } from "aws-sdk-client-mock";
import * as sinon from "sinon";
import { AWSDeployer } from "./index.js";
const { __dirname, __filename } = getCommonJS(import.meta.url);
class TestAWSDeployer extends AWSDeployer {
async deploy() { }
async restrictedCall(method, ...args) {
return this[method](...args);
}
}
export function MockAWSDeployerMethods(service, test) {
test.mocks["putFilesOnBucket"] = sinon.stub(service, "putFilesOnBucket").resolves();
test.mocks["createBucket"] = sinon.stub(service, "createBucket").resolves();
test.mocks["doCreateCertificate"] = sinon.stub(service, "doCreateCertificate").resolves({});
test.mocks["getCertificate"] = sinon.stub(service, "getCertificate").resolves("arn:myfakecertif");
test.mocks["createDNSEntry"] = sinon.stub(service, "createDNSEntry").resolves();
test.mocks["getZoneForDomainName"] = sinon
.stub(service, "getZoneForDomainName")
.resolves({ Id: "MyZoneId", Name: "", CallerReference: "" });
test.mocks["getAWSIdentity"] = sinon.stub(service, "getAWSIdentity").resolves({
Account: "666111333",
UserId: "AR1232432433",
Arn: "arn:aws:sts::666111333:assumed-role"
});
test.mocks["getDefaultVpc"] = sinon.stub(service, "getDefaultVpc").resolves({
Id: "vpc-666",
Subnets: [{ SubnetId: "subnet-1" }, { SubnetId: "subnet-2" }, { SubnetId: "subnet-2" }]
});
test.mocks["getPolicyDocument"] = sinon.stub(service, "getPolicyDocument").resolves({
Version: "2012-10-17",
Statement: []
});
}
let AWSDeployerTest = class AWSDeployerTest extends DeployerTest {
async getDeployer(manager) {
return new TestAWSDeployer(manager, {});
}
mapToArrayTags() {
let deployer = new TestAWSDeployer(this.manager, {
Tags: { Test: "Plop" }
});
assert.deepStrictEqual(deployer.resources.Tags, [{ Key: "Test", Value: "Plop" }]);
assert.deepStrictEqual(deployer.transformMapTagsToArray([{ Key: "Test", Value: "Plop" }]), [
{ Key: "Test", Value: "Plop" }
]);
}
async putFilesOnBucket() {
await this.deployer.putFilesOnBucket("plop", []);
await assert.rejects(
// @ts-ignore
() => this.deployer.putFilesOnBucket("plop", [{ key: "/test" }]), /Should have src and key defined/);
let Contents = [
{
Key: "awsevents.js",
Size: 527,
ETag: '"ea1db4bb1545c62c50e6f0893e9f0992"'
}
];
let stub = sinon.stub().resolves({ Contents });
// Fake any uploads
let uploads = [];
mockClient(S3)
.on(ListObjectsV2Command)
.callsFake(stub)
.on(PutObjectCommand)
.callsFake(arg => {
uploads.push(arg);
return {};
});
sinon.stub(this.deployer, "createBucket").resolves();
await this.deployer.putFolderOnBucket("plop", "./test/moddas");
assert.strictEqual(uploads.length, 0);
Contents[0].ETag = "plop";
await this.deployer.putFolderOnBucket("plop", "./test/moddas");
assert.strictEqual(uploads.length, 1);
uploads = [];
Contents[0].ETag = '"312d05552187d3fdfdda3860fbeef48f"';
Contents[0].Size = 123;
await this.deployer.putFolderOnBucket("plop", "./test/moddas");
assert.strictEqual(uploads.length, 1);
}
async testGetPolicyDocument() {
const mock = mockClient(STS).on(GetCallerIdentityCommand).resolves({
Account: "test"
});
try {
// @ts-ignore
this.manager.getWebda().getServices()["BeanService"].getARNPolicy = () => {
return [
{ Sid: "", Effect: "Allow", Action: [], Resource: "*" },
{ Sid: "", Effect: "Allow", Action: [], Resource: "*" }
];
};
let result = await this.deployer.getPolicyDocument();
assert.strictEqual(result.Statement.length, 4);
}
finally {
mock.restore();
}
}
async testGetDefaultVpc() {
var vpcsSpy = sinon.stub().callsFake(async () => {
if (vpcsSpy.callCount === 1) {
return {
Vpcs: []
};
}
return {
Vpcs: [
{
VpcId: "vpc-667",
IsDefault: false
},
{
VpcId: "vpc-666",
IsDefault: true
}
]
};
});
var subnetsSpy = sinon.stub().callsFake(async () => {
return {
Subnets: [
{
SubnetId: "subnet-1"
},
{
SubnetId: "subnet-2"
}
]
};
});
const mock = mockClient(EC2)
.on(DescribeVpcsCommand)
.callsFake(vpcsSpy)
.on(DescribeSubnetsCommand)
.callsFake(subnetsSpy);
try {
let result = await this.deployer.getDefaultVpc();
assert.strictEqual(vpcsSpy.calledOnce, true);
assert.strictEqual(subnetsSpy.notCalled, true);
assert.strictEqual(result, undefined);
CacheService.clearAllCache();
result = await this.deployer.getDefaultVpc();
assert.deepStrictEqual(result, {
Id: "vpc-666",
Subnets: [{ SubnetId: "subnet-1" }, { SubnetId: "subnet-2" }]
});
assert.strictEqual(subnetsSpy.calledWith({
Filters: [
{
Name: "vpc-id",
Values: ["vpc-666"]
}
]
}), true);
}
finally {
mock.restore();
}
}
async testCommonPrefix() {
assert.strictEqual(this.deployer.commonPrefix("/test/bouzf", "/test/reffff"), "/test/");
assert.strictEqual(this.deployer.commonPrefix("test/bouzf", "/test/reffff"), "");
assert.strictEqual(this.deployer.commonPrefix("/tes", "/test/reffff"), "/tes");
assert.strictEqual(this.deployer.commonPrefix("test/bouzf", "test/bouzf212"), "test/bouzf");
}
async testCreateBucket() {
this.deployer.resources.Tags = this.deployer.transformMapTagsToArray({
test: "mytag"
});
var headSpy = sinon.stub();
headSpy.callsFake(async () => {
let res = {};
switch (headSpy.callCount) {
case 2:
throw {
name: "Forbidden"
};
case 3:
throw new NotFound({
$metadata: {},
message: ""
});
}
return res;
});
var createSpy = sinon.stub().resolves();
var tagSpy = sinon.stub().resolves();
const mock = mockClient(S3)
.on(HeadBucketCommand)
.callsFake(headSpy)
.on(CreateBucketCommand)
.callsFake(createSpy)
.on(PutBucketTaggingCommand)
.callsFake(tagSpy);
try {
await this.deployer.createBucket("plop");
// Bucket exists
assert.strictEqual(headSpy.callCount, 1);
assert.strictEqual(headSpy.calledWith({ Bucket: "plop" }), true);
assert.strictEqual(createSpy.notCalled, true);
assert.strictEqual(tagSpy.notCalled, true);
// Bucket exists in another account or we do not have rights
await this.deployer.createBucket("plop");
assert.strictEqual(headSpy.callCount, 2);
assert.strictEqual(createSpy.notCalled, true);
await this.deployer.createBucket("plop");
assert.strictEqual(headSpy.callCount, 3);
assert.strictEqual(createSpy.calledOnce, true);
assert.strictEqual(createSpy.calledWith({ Bucket: "plop" }), true);
assert.strictEqual(tagSpy.calledOnce, true);
assert.strictEqual(tagSpy.calledWith({
Bucket: "plop",
Tagging: { TagSet: [{ Key: "test", Value: "mytag" }] }
}), true);
}
finally {
mock.restore();
}
}
async testPutBucket() {
var putSpy = sinon.stub();
var listSpy = sinon.stub();
var createBucket = sinon.stub(this.deployer, "createBucket");
createBucket.resolves();
putSpy.resolves();
listSpy.resolves({ Contents: [] });
const mock = mockClient(S3).on(PutObjectCommand).callsFake(putSpy).on(ListObjectsV2Command).callsFake(listSpy);
try {
// @ts-ignore shortcut to test both in one
await this.deployer.putFilesOnBucket("plop", [
{ src: __filename },
{ src: __dirname + "/index.ts", key: "plop.ts" },
{
key: "buffer.out",
src: Buffer.from("bouzouf"),
mimetype: "text/plain"
}
]);
assert.strictEqual(putSpy.callCount, 3);
// checks call
assert.strictEqual(putSpy.firstCall.firstArg.Body.constructor.name, "ReadStream");
assert.strictEqual(putSpy.secondCall.firstArg.Body.constructor.name, "ReadStream");
assert.deepStrictEqual(putSpy.thirdCall.firstArg.Body.constructor.name, "Buffer");
assert.deepStrictEqual(putSpy.thirdCall.firstArg.Body.toString(), "bouzouf");
assert.strictEqual(createBucket.calledOnce, true);
// TODO Check upload optimization
}
finally {
mock.restore();
}
}
async testGetCertificate() {
var putSpy = sinon.stub();
var getZoneForDomainName = sinon.stub(this.deployer, "getZoneForDomainName");
var doCreateCertificate = sinon.stub(this.deployer, "doCreateCertificate");
getZoneForDomainName.callsFake(async () => {
if (getZoneForDomainName.callCount === 1) {
return undefined;
}
return { Id: "1234", HostedZone: "", Name: "", CallerReference: "" };
});
doCreateCertificate.resolves({ DomainName: "newlyCreated" });
putSpy.callsFake(async () => {
switch (putSpy.callCount) {
case 1:
return {
CertificateSummaryList: [
{
DomainName: "none.com"
}
],
NextToken: "page2"
};
case 2:
return {
CertificateSummaryList: [
{
DomainName: "test.webda.io"
}
]
};
default:
return { CertificateSummaryList: [] };
}
});
const mock = mockClient(ACM).on(ListCertificatesCommand).callsFake(putSpy);
try {
let certificate = await this.deployer.getCertificate("test.webda.io.");
assert.strictEqual(putSpy.calledTwice, true);
assert.strictEqual(doCreateCertificate.notCalled, true);
assert.strictEqual(getZoneForDomainName.notCalled, true);
assert.deepStrictEqual(certificate, { DomainName: "test.webda.io" });
await assert.rejects(async () => await this.deployer.getCertificate("test.webda.io"), /Cannot create certificate as Route53 Zone was not found/g);
assert.strictEqual(getZoneForDomainName.calledWith("test.webda.io"), true);
assert.strictEqual(doCreateCertificate.notCalled, true);
await this.deployer.getCertificate("test.webda.io", "us-east-1");
assert.strictEqual(doCreateCertificate.calledWith("test.webda.io", {
Id: "1234",
HostedZone: "",
Name: "",
CallerReference: ""
}), true);
}
finally {
mock.restore();
}
}
async testGetZoneForDomainName() {
var callSpy = sinon.stub();
callSpy.callsFake(async () => {
switch (callSpy.callCount) {
case 1:
return { HostedZones: [], NextMarker: "plop" };
default:
return {
HostedZones: [
{ Name: "webda.io." },
{ Name: "bouzouf.io." },
{ Name: "test2.test.webda.io." },
{ Name: "test.webda.io." }
]
};
}
});
const mock = mockClient(Route53).on(ListHostedZonesCommand).callsFake(callSpy);
try {
let result = await this.deployer.getZoneForDomainName("subdomain.test2.test.webda.io.");
assert.deepStrictEqual(result, {
Name: "test2.test.webda.io."
});
assert.strictEqual(callSpy.calledTwice, true);
result = await this.deployer.getZoneForDomainName("subdomain.webda.io");
assert.deepStrictEqual(result, {
Name: "webda.io."
});
result = await this.deployer.getZoneForDomainName("loopingz.com.");
assert.strictEqual(result, undefined);
}
finally {
mock.restore();
}
}
async testDoCreateCertificate() {
var requestCertificate = sinon.stub();
var describeCertificate = sinon.stub();
var createDNSEntry = sinon.stub(this.deployer, "createDNSEntry");
var waitFor = sinon.stub(this.deployer, "waitFor");
createDNSEntry.callsFake(async () => { });
describeCertificate.resolves({});
const mock = mockClient(ACM)
.on(RequestCertificateCommand)
.callsFake(requestCertificate)
.on(DescribeCertificateCommand)
.callsFake(describeCertificate);
try {
waitFor.callsFake(async (callback) => {
switch (waitFor.callCount) {
case 1:
await callback();
case 2:
return new Promise(resolve => callback(resolve));
}
});
describeCertificate.callsFake(async () => {
if (describeCertificate.callCount === 1) {
return {
Certificate: {
DomainValidationOptions: [{}]
}
};
}
else {
return {
Certificate: {
Status: "FAILED",
DomainValidationOptions: [{ ResourceRecord: "bouzouf" }]
}
};
}
});
requestCertificate.callsFake(async () => {
return { CertificateArn: "plop" };
});
await assert.rejects(async () => await this.deployer.doCreateCertificate("test.webda.io.", {
Id: "1234"
}), /Certificate validation has failed/g);
waitFor.callsFake(() => undefined);
await assert.rejects(async () => await this.deployer.doCreateCertificate("test.webda.io.", {
Id: "1234"
}), /Certificate validation has failed/g);
waitFor.resetHistory();
describeCertificate.resetHistory();
assert.strictEqual(createDNSEntry.notCalled, true);
// @ts-ignore
waitFor.callsFake(c => {
switch (waitFor.callCount) {
case 1:
return {
Status: "PENDING_VALIDATION",
DomainValidationOptions: [{ ResourceRecord: { Value: "plop.com.", Name: "bouzouf.com" } }],
CertificateArn: "arn:plop"
};
default:
return new Promise(async (resolve, reject) => {
for (let i = 0; i < 5; i++) {
if (await c(resolve, reject)) {
return;
}
}
reject("max");
});
}
});
describeCertificate.callsFake(async () => {
if (describeCertificate.callCount < 3) {
return {
Certificate: {
Status: "PENDING_VALIDATION"
}
};
}
else {
return {
Certificate: {
Status: "ISSUED"
}
};
}
});
await this.deployer.doCreateCertificate("test.webda.io.", {
Id: "1234",
Name: "",
CallerReference: ""
});
assert.strictEqual(createDNSEntry.calledWith("bouzouf.com", "CNAME", "plop.com.", {
Id: "1234",
Name: "",
CallerReference: ""
}), true);
assert.strictEqual(describeCertificate.callCount, 3);
waitFor.resetHistory();
describeCertificate.resolves({ Certificate: { Status: "ERROR" } });
let exception = false;
try {
// Should be able to use assert.rejects
await this.deployer.doCreateCertificate("test.webda.io.", {
Id: "1234"
});
}
catch (err) {
assert.deepStrictEqual(err, { Status: "ERROR" });
exception = true;
}
assert.strictEqual(exception, true);
}
finally {
mock.restore();
}
}
async testCreateDNSEntry() {
var callSpy = sinon.stub();
var getZoneForDomainName = sinon.stub(this.deployer, "getZoneForDomainName");
getZoneForDomainName.callsFake(async () => {
if (getZoneForDomainName.callCount === 1) {
return undefined;
}
return { Id: "1234", CallerReference: "", Name: "" };
});
callSpy.resolves();
const mock = mockClient(Route53).on(ChangeResourceRecordSetsCommand).callsFake(callSpy);
try {
await assert.rejects(async () => await this.deployer.restrictedCall("createDNSEntry", "webda.io", "CNAME", "loopingz.com"), /Domain is not handled on AWS/g);
assert.strictEqual(callSpy.notCalled, true);
await this.deployer.restrictedCall("createDNSEntry", "webda.io.", "CNAME", "loopingz.com");
assert.strictEqual(callSpy.calledOnce, true);
assert.deepStrictEqual(callSpy.firstCall.firstArg, {
HostedZoneId: "1234",
ChangeBatch: {
Changes: [
{
Action: "UPSERT",
ResourceRecordSet: {
Name: "webda.io.",
ResourceRecords: [
{
Value: "loopingz.com"
}
],
TTL: 360,
Type: "CNAME"
}
}
],
Comment: "webda-automated-deploiement"
}
});
}
finally {
mock.restore();
}
}
async testHash() {
assert.strictEqual(await this.deployer.restrictedCall("md5", "bouzouf"), "fc45f69f910da129848cd265448a5d00");
assert.strictEqual(await this.deployer.restrictedCall("hash", "bouzouf"), "fc45f69f910da129848cd265448a5d00");
assert.strictEqual(await this.deployer.restrictedCall("hash", "bouzouf", "md5", "base64"), "/EX2n5ENoSmEjNJlRIpdAA==");
assert.strictEqual(await this.deployer.restrictedCall("hash", "bouzouf", "sha256", "hex"), "af6773b1075007089c46a6d0b8e9ac03abfdb526de0683bfeb723f2448ea3f66");
}
async cov() {
assert.strictEqual(this.deployer.getRegion(), "us-east-1");
this.deployer.waitFor(async (resolve) => {
resolve();
return true;
}, 1, "test", 1);
this.deployer.resources.Tags = [{ Key: "plop", Value: "test" }];
// @ts-ignore
this.deployer.resources.Plop = { Tags: { plop2: "oké yep" } };
assert.strictEqual(this.deployer.getDefaultTagsAsS3Tagging("Plop"), "plop2=ok%C3%A9%20yep&plop=test");
}
replaceForAWS() {
assert.strictEqual(this.deployer._replaceForAWS("my/id"), "my_id");
assert.strictEqual(this.deployer._replaceForAWS("myid"), "myid");
}
transformArrayTagsToMap() {
assert.deepStrictEqual(this.deployer.transformArrayTagsToMap([{ Key: "Test", Value: "plop" }]), { Test: "plop" });
assert.deepStrictEqual(this.deployer.transformArrayTagsToMap({ Test: "plop" }), { Test: "plop" });
assert.deepStrictEqual(this.deployer.getDefaultTagsAsMap(), {});
assert.deepStrictEqual(this.deployer.getDefaultTags("me"), []);
}
};
__decorate([
test
], AWSDeployerTest.prototype, "mapToArrayTags", null);
__decorate([
test
], AWSDeployerTest.prototype, "putFilesOnBucket", null);
__decorate([
test
], AWSDeployerTest.prototype, "testGetPolicyDocument", null);
__decorate([
test
], AWSDeployerTest.prototype, "testGetDefaultVpc", null);
__decorate([
test
], AWSDeployerTest.prototype, "testCommonPrefix", null);
__decorate([
test
], AWSDeployerTest.prototype, "testCreateBucket", null);
__decorate([
test
], AWSDeployerTest.prototype, "testPutBucket", null);
__decorate([
test
], AWSDeployerTest.prototype, "testGetCertificate", null);
__decorate([
test
], AWSDeployerTest.prototype, "testGetZoneForDomainName", null);
__decorate([
test
], AWSDeployerTest.prototype, "testDoCreateCertificate", null);
__decorate([
test
], AWSDeployerTest.prototype, "testCreateDNSEntry", null);
__decorate([
test
], AWSDeployerTest.prototype, "testHash", null);
__decorate([
test
], AWSDeployerTest.prototype, "cov", null);
__decorate([
test
], AWSDeployerTest.prototype, "replaceForAWS", null);
__decorate([
test
], AWSDeployerTest.prototype, "transformArrayTagsToMap", null);
AWSDeployerTest = __decorate([
suite
], AWSDeployerTest);
//# sourceMappingURL=index.spec.js.map