UNPKG

terrac

Version:

A minimal private module registry for Terraform and OpenTofu

130 lines (129 loc) 4.77 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.BackendS3 = exports.configSchemaS3 = void 0; const shared_1 = require("./shared"); const errors_1 = require("../errors"); const fs_extra_1 = require("fs-extra"); const lodash_1 = require("lodash"); const client_s3_1 = require("@aws-sdk/client-s3"); const Joi = require("joi"); exports.configSchemaS3 = Joi.object({ type: Joi.string().allow('s3').required().description('Backend type'), bucket: Joi.string().required().description('Bucket name'), region: Joi.string().pattern(/^(?:[a-z]+-){2}\d+$/).required().description('AWS region'), keyPrefix: Joi.string().optional().allow('').description('Object key prefix'), }); class BackendS3 { constructor(config) { this.config = config; this.client = new client_s3_1.S3Client({ // this is a special env for testing endpoint: process.env.TERRAC_BACKEND_S3_ENDPOINT, region: config.region, }); } async upload(name, version, packagePath) { await this.putObject(this.getPackageKey(name, version), (0, fs_extra_1.createReadStream)(packagePath)); } async getSourceUrl(name, version) { let targetVersion = version; if (!targetVersion) { const meta = await this.getMeta(name); targetVersion = meta.version; } const key = this.getPackageKey(name, targetVersion); if (!this.keyExists(key)) { throw new errors_1.ModuleNotFoundError(); } return `s3::https://s3-${this.config.region}.amazonaws.com/${this.config.bucket}/${key}`; } async list(name) { const moduleList = []; const prefix = this.config.keyPrefix || ''; if (name) { if (!(await this.keyExists(this.getMetaKey(name)))) { throw new errors_1.ModuleNotFoundError(); } const meta = await this.getMeta(name); for (const release of meta.releases) { moduleList.push({ name, version: release.version, }); } } else { const keys = await this.listKeys(prefix); const names = (0, lodash_1.uniq)(keys.map(key => key.replace(prefix, '').split('/').shift())); for (const name of names) { moduleList.push({ name, }); } } return moduleList; } async exists(name, version) { const key = version ? this.getPackageKey(name, version) : this.getMetaKey(name); return this.keyExists(key); } async getMeta(name) { const bucket = this.config.bucket; const key = this.getMetaKey(name); if (await this.keyExists(key)) { const getCommand = new client_s3_1.GetObjectCommand({ Bucket: bucket, Key: key, }); const response = await this.client.send(getCommand); const data = await response.Body.transformToString(); return JSON.parse(data); } return (0, shared_1.getNewMeta)(name); } async saveMeta(meta) { const metaKey = this.getMetaKey(meta.name); await this.putObject(metaKey, JSON.stringify(meta)); } async putObject(key, data) { const putCommand = new client_s3_1.PutObjectCommand({ Bucket: this.config.bucket, Key: key, Body: data, }); await this.client.send(putCommand); } async keyExists(key) { try { const command = new client_s3_1.HeadObjectCommand({ Bucket: this.config.bucket, Key: key, }); await this.client.send(command); return true; // eslint-disable-next-line unicorn/prefer-optional-catch-binding } catch (error) { return false; } } async listKeys(prefix) { const command = new client_s3_1.ListObjectsV2Command({ Bucket: this.config.bucket, Prefix: prefix, }); const response = await this.client.send(command); return response.Contents ? response.Contents.map(item => item.Key) : []; } getMetaKey(name) { const baseKey = `${name}/meta.json`; return this.config.keyPrefix ? `${this.config.keyPrefix}${baseKey}` : baseKey; } getPackageKey(name, version) { var _a; const baseKey = `${name}/${version}/module.zip`; const keyPrefix = (_a = this.config.keyPrefix) !== null && _a !== void 0 ? _a : ''; return `${keyPrefix}${baseKey}`; } } exports.BackendS3 = BackendS3;