Inleiding
Deze handleiding beschrijft hoe je een serverless Pact Broker met PostgreSQL kunt opzetten op AWS ECS Fargate. Een partner vroeg me onlangs om contract testing (ook wel bekend als Pact) te demonstreren voor een klant. Ondanks mijn beperkte praktische ervaring met Pact, was ik door mijn uitgebreide achtergrond in integratietesten en service virtualization enthousiast om deze technologie te verkennen.
Het doel van dit project was het bouwen van een cost-effective, schaalbare oplossing waarbij zowel de Pact Broker als de PostgreSQL database als serverless containers draaien op AWS ECS Fargate.
Vereisten
Voor deze implementatie heb je het volgende nodig:
- AWS-account met beheerrrechten
- AWS CLI en AWS CDK geïnstalleerd
- Docker voor lokaal containerbeheer
- Basiskennis van Docker, AWS ECS en netwerkarchitectuur
Architectuuroverzicht
De oplossing bestaat uit de volgende componenten:
- Amazon ECS Fargate: Host voor zowel Pact Broker als PostgreSQL containers
- Amazon S3: Storage voor Pact files met versioning
- Amazon CloudWatch: Logging en monitoring
- Toekomstige AWS Lambda integratie: Geautomatiseerde Pact verification
Cost Optimalisatie
Om de kosten laag te houden, overweeg de volgende strategieën:
- ECS Fargate Spot Instances: Gebruik spot instances voor non-production omgevingen
- Resource monitoring: Controleer regelmatig het ECS task usage en pas de task size aan
- S3 lifecycle policies: Implementeer lifecycle beleid om storage kosten te minimaliseren
- AWS Free Tier: Maak gebruik van gratis resources waar mogelijk
Implementatiestappen
Stap 1: ECS Cluster en Netwerk Setup
Stel een VPC en ECS-cluster in om een veilige netwerkomgeving te creëren voor de containerservices.
Stap 2: PostgreSQL Database Deployment
Deploy PostgreSQL als container binnen ECS:
- Gebruik een bestaande PostgreSQL Docker image of maak een custom Dockerfile
- Configureer persistent storage via ECS volume management
- Zorg voor juiste network connectivity
Stap 3: Pact Broker Deployment
Containerize en deploy de Pact Broker op ECS:
- Configureer communicatie met de PostgreSQL container
- Setup load balancing voor high availability
- Implementeer basic authentication
Pact Implementation en Verification
Consumer Test Voorbeeld
Na deployment kun je Pacts creëren en verifiëren. Hier is een voorbeeld van een consumer test:
import { expect } from 'chai';
import path from 'path';
import { Pact, Matchers } from '@pact-foundation/pact';
import { getMeBSN, getMeBSNs } from './index.mjs';
const { like } = Matchers;
describe('The BSN API', () => {
let url = 'localhost';
const port = 8992;
const provider = new Pact({
port: port,
log: path.resolve(process.cwd(), 'logs', 'mockserver-integration.log'),
dir: path.resolve(process.cwd(), 'pacts'),
spec: 2,
consumer: 'RWS-consumer',
provider: 'BRP-provider',
});
const EXPECTED_BODY = [
{ bsn: 123456789 },
{ bsn: 987654321 },
];
// Setup the provider
before(async () => {
await provider.setup();
});
// Write Pact when all tests done
after(async () => {
await provider.finalize();
});
// Verify with Pact, and reset expectations
afterEach(async () => {
await provider.verify();
});
describe('get /bsns', () => {
before(async () => {
const interaction = {
state: 'i have a list of BSN',
uponReceiving: 'a request for all BSNs',
withRequest: {
method: 'GET',
path: '/bsns',
headers: {
Accept: [
'application/problem+json',
'application/json',
'text/plain',
'*/*',
],
},
},
willRespondWith: {
status: 200,
headers: {
'Content-Type': 'application/json',
},
body: [
{ bsn: 123456789 },
{ bsn: 987654321 }
],
},
};
await provider.addInteraction(interaction);
});
it('returns the correct response', async () => {
const urlAndPort = { url: url, port: port };
const response = await getMeBSNs(urlAndPort);
console.log(response);
expect(response).to.eql(EXPECTED_BODY);
});
});
});
Provider Verification
Na het publiceren van het Pact naar de broker, kan de provider het contract verifiëren:
import axios from 'axios';
import fs from 'fs/promises';
import { Verifier } from '@pact-foundation/pact';
const pactFileUrl = 'http://pactst-pactb-oesxbucvqxww-103295103.eu-west-2.elb.amazonaws.com/pacts/provider/BRP-provider/consumer/RWS-consumer/latest';
const localFilePath = './local-pact-file.json';
// Basic Authentication credentials
const username = 'admin';
const password = 'password';
const auth = Buffer.from(`${username}:${password}`).toString('base64');
async function downloadPactFile() {
try {
const response = await axios.get(pactFileUrl, {
headers: {
'Authorization': `Basic ${auth}`
},
responseType: 'json'
});
await fs.writeFile(localFilePath, JSON.stringify(response.data));
console.log(`Pact file downloaded and saved to ${localFilePath}`);
} catch (error) {
console.error('Failed to download the pact file:', error.message);
}
}
async function verifyPacts() {
const opts = {
provider: 'BRP-provider',
providerBaseUrl: 'https://sqsgrhcuji.execute-api.eu-west-2.amazonaws.com/prod',
pactUrls: [localFilePath],
logLevel: 'DEBUG',
};
try {
await new Verifier(opts).verifyProvider();
console.log('Pact verification complete!');
} catch (error) {
console.error('Pact verification failed:', error.message);
process.exit(1);
}
}
async function run() {
await downloadPactFile();
await verifyPacts();
}
run();
Testresultaten en Demonstratie
Na de implementation is het Pact zichtbaar in de Pact Broker en opgeslagen in de AWS PostgreSQL ECS container. Om de werking te demonstreren, heb ik twee APIs gecreëerd in API Gateway en Lambda, waarbij ik opzettelijk de structuur van de tweede API heb gewijzigd.
Contract Breaking Changes
Bij het uitvoeren van de pipeline tests blijkt dat de derde test faalt vanwege een broken contract. De logs tonen duidelijk het probleem: een wijziging van bsn
naar bsnnew
. Dit demonstreert perfect hoe Pact contract breaking changes detecteert.
Toekomstige Verbeteringen
Geplande Uitbreidingen
-
S3 Integration voor Pact Files
- Configuratie van S3 bucket voor centrale storage
- Implementation van versioning en backup strategieën
-
AWS Lambda voor Geautomatiseerde Verification
- Automatische Pact verification bij S3 uploads
- Integration met CI/CD pipelines
- Scheduled verification runs
-
Enhanced Monitoring
- Uitgebreide CloudWatch dashboards
- Alerting bij failing verifications
- Performance metrics tracking
Conclusie
Deze implementation toont aan hoe je een schaalbare, efficiënte en cost-effective Pact Broker oplossing kunt bouwen op AWS ECS Fargate. De serverless architectuur biedt flexibiliteit en cost savings, terwijl de container-based aanpak zorgt voor eenvoudige deployment en onderhoud.
De demonstratie van contract breaking changes illustreert de kracht van Pact voor het behouden van API compatibility in microservices architecturen. Toekomstige improvements met AWS Lambda integration zullen het proces verder automatiseren en streamlinen.
Resources
Voor meer details en toegang tot de source code:
- API Provider Examples: https://gitlab.com/learnautomatedtesting/pactexample
- Serverless AWS CDK Stack: https://gitlab.com/learnautomatedtesting/servicevirtualizationandpact/
Deze setup demonstreert een praktische implementation van Pact in AWS en benadrukt de voordelen van serverless architectuur voor contract testing in moderne microservices omgevingen.