Merge pull request #3847 from NginxProxyManager/develop

v2.11.3
This commit is contained in:
jc21 2024-07-01 21:37:42 +10:00 committed by GitHub
commit 35d7a3a407
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
43 changed files with 1460 additions and 1219 deletions

4
.gitignore vendored
View File

@ -3,3 +3,7 @@
._* ._*
.vscode .vscode
certbot-help.txt certbot-help.txt
test/node_modules
*/node_modules
docker/dev/dnsrouter-config.json.tmp
docker/dev/resolv.conf

View File

@ -1 +1 @@
2.11.2 2.11.3

123
Jenkinsfile vendored
View File

@ -18,10 +18,8 @@ pipeline {
BUILD_VERSION = getVersion() BUILD_VERSION = getVersion()
MAJOR_VERSION = '2' MAJOR_VERSION = '2'
BRANCH_LOWER = "${BRANCH_NAME.toLowerCase().replaceAll('\\\\', '-').replaceAll('/', '-').replaceAll('\\.', '-')}" BRANCH_LOWER = "${BRANCH_NAME.toLowerCase().replaceAll('\\\\', '-').replaceAll('/', '-').replaceAll('\\.', '-')}"
COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}" BUILDX_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}"
COMPOSE_FILE = 'docker/docker-compose.ci.yml'
COMPOSE_INTERACTIVE_NO_CLI = 1 COMPOSE_INTERACTIVE_NO_CLI = 1
BUILDX_NAME = "${COMPOSE_PROJECT_NAME}"
} }
stages { stages {
stage('Environment') { stage('Environment') {
@ -94,75 +92,61 @@ pipeline {
} }
} }
} }
stage('Cypress') { }
steps { }
// Creating will also create the network prior to stage('Test Sqlite') {
// using it in parallel stages below and mitigating environment {
// a race condition. COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}_sqlite"
sh 'docker-compose build cypress-sqlite' COMPOSE_FILE = 'docker/docker-compose.ci.yml:docker/docker-compose.ci.sqlite.yml'
sh 'docker-compose build cypress-mysql' }
sh 'docker-compose create cypress-sqlite' when {
sh 'docker-compose create cypress-mysql' not {
} equals expected: 'UNSTABLE', actual: currentBuild.result
}
}
steps {
sh 'rm -rf ./test/results/junit/*'
sh './scripts/ci/fulltest-cypress'
}
post {
always {
// Dumps to analyze later
sh 'mkdir -p debug/sqlite'
sh 'docker logs $(docker-compose ps --all -q fullstack) > debug/sqlite/docker_fullstack.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q stepca) > debug/sqlite/docker_stepca.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q pdns) > debug/sqlite/docker_pdns.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q pdns-db) > debug/sqlite/docker_pdns-db.log 2>&1'
sh 'docker logs $(docker-compose ps --all -q dnsrouter) > debug/sqlite/docker_dnsrouter.log 2>&1'
junit 'test/results/junit/*'
sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
} }
} }
} }
stage('Integration Tests') { stage('Test Mysql') {
parallel { environment {
stage('Sqlite') { COMPOSE_PROJECT_NAME = "npm_${BRANCH_LOWER}_${BUILD_NUMBER}_mysql"
steps { COMPOSE_FILE = 'docker/docker-compose.ci.yml:docker/docker-compose.ci.mysql.yml'
// Bring up a stack }
sh 'docker-compose up -d fullstack-sqlite' when {
sh './scripts/wait-healthy $(docker-compose ps --all -q fullstack-sqlite) 120' not {
// Stop and Start it, as this will test it's ability to restart with existing data equals expected: 'UNSTABLE', actual: currentBuild.result
sh 'docker-compose stop fullstack-sqlite'
sh 'docker-compose start fullstack-sqlite'
sh './scripts/wait-healthy $(docker-compose ps --all -q fullstack-sqlite) 120'
// Run tests
sh 'rm -rf test/results-sqlite'
sh 'docker-compose up cypress-sqlite'
// Get results
sh 'docker cp -L "$(docker-compose ps --all -q cypress-sqlite):/test/results" test/results-sqlite'
}
post {
always {
// Dumps to analyze later
sh 'mkdir -p debug/sqlite'
sh 'docker-compose logs fullstack-sqlite > debug/sqlite/docker_fullstack_sqlite.log'
// Cypress videos and screenshot artifacts
dir(path: 'test/results-sqlite') {
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
}
junit 'test/results-sqlite/junit/*'
}
}
} }
stage('Mysql') { }
steps { steps {
// Bring up a stack sh 'rm -rf ./test/results/junit/*'
sh 'docker-compose up -d fullstack-mysql' sh './scripts/ci/fulltest-cypress'
sh './scripts/wait-healthy $(docker-compose ps --all -q fullstack-mysql) 120' }
post {
// Run tests always {
sh 'rm -rf test/results-mysql' // Dumps to analyze later
sh 'docker-compose up cypress-mysql' sh 'mkdir -p debug/mysql'
// Get results sh 'docker logs $(docker-compose ps --all -q fullstack) > debug/mysql/docker_fullstack.log 2>&1'
sh 'docker cp -L "$(docker-compose ps --all -q cypress-mysql):/test/results" test/results-mysql' sh 'docker logs $(docker-compose ps --all -q stepca) > debug/mysql/docker_stepca.log 2>&1'
} sh 'docker logs $(docker-compose ps --all -q pdns) > debug/mysql/docker_pdns.log 2>&1'
post { sh 'docker logs $(docker-compose ps --all -q pdns-db) > debug/mysql/docker_pdns-db.log 2>&1'
always { sh 'docker logs $(docker-compose ps --all -q dnsrouter) > debug/mysql/docker_dnsrouter.log 2>&1'
// Dumps to analyze later junit 'test/results/junit/*'
sh 'mkdir -p debug/mysql' sh 'docker-compose down --remove-orphans --volumes -t 30 || true'
sh 'docker-compose logs fullstack-mysql > debug/mysql/docker_fullstack_mysql.log'
sh 'docker-compose logs db > debug/mysql/docker_db.log'
// Cypress videos and screenshot artifacts
dir(path: 'test/results-mysql') {
archiveArtifacts allowEmptyArchive: true, artifacts: '**/*', excludes: '**/*.xml'
}
junit 'test/results-mysql/junit/*'
}
}
} }
} }
} }
@ -214,9 +198,8 @@ pipeline {
} }
post { post {
always { always {
sh 'docker-compose down --remove-orphans --volumes -t 30'
sh 'echo Reverting ownership' sh 'echo Reverting ownership'
sh 'docker run --rm -v $(pwd):/data jc21/ci-tools chown -R $(id -u):$(id -g) /data' sh 'docker run --rm -v "$(pwd):/data" jc21/ci-tools chown -R "$(id -u):$(id -g)" /data'
} }
success { success {
juxtapose event: 'success' juxtapose event: 'success'

View File

@ -1,7 +1,7 @@
<p align="center"> <p align="center">
<img src="https://nginxproxymanager.com/github.png"> <img src="https://nginxproxymanager.com/github.png">
<br><br> <br><br>
<img src="https://img.shields.io/badge/version-2.11.2-green.svg?style=for-the-badge"> <img src="https://img.shields.io/badge/version-2.11.3-green.svg?style=for-the-badge">
<a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager"> <a href="https://hub.docker.com/repository/docker/jc21/nginx-proxy-manager">
<img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge"> <img src="https://img.shields.io/docker/stars/jc21/nginx-proxy-manager.svg?style=for-the-badge">
</a> </a>
@ -56,7 +56,6 @@ I won't go in to too much detail here but here are the basics for someone new to
2. Create a docker-compose.yml file similar to this: 2. Create a docker-compose.yml file similar to this:
```yml ```yml
version: '3.8'
services: services:
app: app:
image: 'docker.io/jc21/nginx-proxy-manager:latest' image: 'docker.io/jc21/nginx-proxy-manager:latest'

View File

@ -861,9 +861,8 @@ const internalCertificate = {
logger.info(`Requesting Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`); logger.info(`Requesting Let'sEncrypt certificates via ${dnsPlugin.name} for Cert #${certificate.id}: ${certificate.domain_names.join(', ')}`);
const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id; const credentialsLocation = '/etc/letsencrypt/credentials/credentials-' + certificate.id;
// Escape single quotes and backslashes fs.mkdirSync('/etc/letsencrypt/credentials', { recursive: true });
const escapedCredentials = certificate.meta.dns_provider_credentials.replaceAll('\'', '\\\'').replaceAll('\\', '\\\\'); fs.writeFileSync(credentialsLocation, certificate.meta.dns_provider_credentials, {mode: 0o600});
const credentialsCmd = 'mkdir -p /etc/letsencrypt/credentials 2> /dev/null; echo \'' + escapedCredentials + '\' > \'' + credentialsLocation + '\' && chmod 600 \'' + credentialsLocation + '\'';
// Whether the plugin has a --<name>-credentials argument // Whether the plugin has a --<name>-credentials argument
const hasConfigArg = certificate.meta.dns_provider !== 'route53'; const hasConfigArg = certificate.meta.dns_provider !== 'route53';
@ -898,17 +897,15 @@ const internalCertificate = {
mainCmd = mainCmd + ' --dns-duckdns-no-txt-restore'; mainCmd = mainCmd + ' --dns-duckdns-no-txt-restore';
} }
logger.info('Command:', `${credentialsCmd} && && ${mainCmd}`); logger.info('Command:', mainCmd);
try { try {
await utils.exec(credentialsCmd);
const result = await utils.exec(mainCmd); const result = await utils.exec(mainCmd);
logger.info(result); logger.info(result);
return result; return result;
} catch (err) { } catch (err) {
// Don't fail if file does not exist // Don't fail if file does not exist, so no need for action in the callback
const delete_credentialsCmd = `rm -f '${credentialsLocation}' || true`; fs.unlink(credentialsLocation, () => {});
await utils.exec(delete_credentialsCmd);
throw err; throw err;
} }
}, },

View File

@ -93,7 +93,7 @@ const generateKeys = () => {
try { try {
fs.writeFileSync(keysFile, JSON.stringify(keys, null, 2)); fs.writeFileSync(keysFile, JSON.stringify(keys, null, 2));
} catch (err) { } catch (err) {
logger.error('Could not write JWT key pair to config file: ' + keysFile + ': ' . err.message); logger.error('Could not write JWT key pair to config file: ' + keysFile + ': ' + err.message);
process.exit(1); process.exit(1);
} }
logger.info('Wrote JWT key pair to config file: ' + keysFile); logger.info('Wrote JWT key pair to config file: ' + keysFile);

View File

@ -21,11 +21,14 @@ const setupDefaultUser = () => {
.then((row) => { .then((row) => {
if (!row.count) { if (!row.count) {
// Create a new user and set password // Create a new user and set password
logger.info('Creating a new user: admin@example.com with password: changeme'); let email = process.env.INITIAL_ADMIN_EMAIL || 'admin@example.com';
let password = process.env.INITIAL_ADMIN_PASSWORD || 'changeme';
logger.info('Creating a new user: ' + email + ' with password: ' + password);
let data = { let data = {
is_deleted: 0, is_deleted: 0,
email: 'admin@example.com', email: email,
name: 'Administrator', name: 'Administrator',
nickname: 'Admin', nickname: 'Admin',
avatar: '', avatar: '',
@ -41,7 +44,7 @@ const setupDefaultUser = () => {
.insert({ .insert({
user_id: user.id, user_id: user.id,
type: 'password', type: 'password',
secret: 'changeme', secret: password,
meta: {}, meta: {},
}) })
.then(() => { .then(() => {

View File

@ -1,4 +1,6 @@
location {{ path }} { location {{ path }} {
{{ advanced_config }}
proxy_set_header Host $host; proxy_set_header Host $host;
proxy_set_header X-Forwarded-Scheme $scheme; proxy_set_header X-Forwarded-Scheme $scheme;
proxy_set_header X-Forwarded-Proto $scheme; proxy_set_header X-Forwarded-Proto $scheme;
@ -17,8 +19,5 @@
proxy_set_header Connection $http_connection; proxy_set_header Connection $http_connection;
proxy_http_version 1.1; proxy_http_version 1.1;
{% endif %} {% endif %}
{{ advanced_config }}
} }

View File

@ -448,11 +448,11 @@ brace-expansion@^1.1.7:
concat-map "0.0.1" concat-map "0.0.1"
braces@~3.0.2: braces@~3.0.2:
version "3.0.2" version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.2.tgz#3454e1a462ee8d599e236df336cd9ea4f8afe107" resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
integrity sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A== integrity sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==
dependencies: dependencies:
fill-range "^7.0.1" fill-range "^7.1.1"
buffer-crc32@^0.2.1, buffer-crc32@^0.2.13: buffer-crc32@^0.2.1, buffer-crc32@^0.2.13:
version "0.2.13" version "0.2.13"
@ -1206,10 +1206,10 @@ file-entry-cache@^6.0.1:
dependencies: dependencies:
flat-cache "^3.0.4" flat-cache "^3.0.4"
fill-range@^7.0.1: fill-range@^7.1.1:
version "7.0.1" version "7.1.1"
resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.0.1.tgz#1919a6a7c75fe38b2c7c77e5198535da9acdda40" resolved "https://registry.yarnpkg.com/fill-range/-/fill-range-7.1.1.tgz#44265d3cac07e3ea7dc247516380643754a05292"
integrity sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ== integrity sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==
dependencies: dependencies:
to-regex-range "^5.0.1" to-regex-range "^5.0.1"
@ -1402,9 +1402,9 @@ glob-parent@^6.0.2:
is-glob "^4.0.3" is-glob "^4.0.3"
glob-parent@~5.1.0: glob-parent@~5.1.0:
version "5.1.1" version "5.1.2"
resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.1.tgz#b6c1ef417c4e5663ea498f1c45afac6916bbc229" resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.1.2.tgz#869832c58034fe68a4093c17dc15e8340d8401c4"
integrity sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ== integrity sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==
dependencies: dependencies:
is-glob "^4.0.1" is-glob "^4.0.1"

View File

@ -0,0 +1,28 @@
{
"log": {
"format": "nice",
"level": "debug"
},
"servers": [
{
"host": "0.0.0.0",
"port": 53,
"upstreams": [
{
"regex": "website[0-9]+.example\\.com",
"upstream": "127.0.0.11"
},
{
"regex": ".*\\.example\\.com",
"upstream": "1.1.1.1"
},
{
"regex": "local",
"nxdomain": true
}
],
"internal": null,
"default_upstream": "127.0.0.11"
}
]
}

View File

@ -0,0 +1,7 @@
text = True
non-interactive = True
webroot-path = /data/letsencrypt-acme-challenge
key-type = ecdsa
elliptic-curve = secp384r1
preferred-chain = ISRG Root X1
server =

255
docker/dev/pdns-db.sql Normal file
View File

@ -0,0 +1,255 @@
/*
How this was generated:
1. bring up an empty pdns stack
2. use api to create a zone ...
curl -X POST \
'http://npm.dev:8081/api/v1/servers/localhost/zones' \
--header 'X-API-Key: npm' \
--header 'Content-Type: application/json' \
--data-raw '{
"name": "example.com.",
"kind": "Native",
"masters": [],
"nameservers": [
"ns1.pdns.",
"ns2.pdns."
]
}'
3. Dump sql:
docker exec -ti npm.pdns.db mysqldump -u pdns -p pdns
*/
----------------------------------------------------------------------
/*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */;
/*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */;
/*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */;
/*!40101 SET NAMES utf8mb4 */;
/*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */;
/*!40103 SET TIME_ZONE='+00:00' */;
/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
--
-- Table structure for table `comments`
--
DROP TABLE IF EXISTS `comments`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `comments` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) NOT NULL,
`name` varchar(255) NOT NULL,
`type` varchar(10) NOT NULL,
`modified_at` int(11) NOT NULL,
`account` varchar(40) CHARACTER SET utf8mb3 DEFAULT NULL,
`comment` text CHARACTER SET utf8mb3 NOT NULL,
PRIMARY KEY (`id`),
KEY `comments_name_type_idx` (`name`,`type`),
KEY `comments_order_idx` (`domain_id`,`modified_at`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `comments`
--
LOCK TABLES `comments` WRITE;
/*!40000 ALTER TABLE `comments` DISABLE KEYS */;
/*!40000 ALTER TABLE `comments` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `cryptokeys`
--
DROP TABLE IF EXISTS `cryptokeys`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `cryptokeys` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) NOT NULL,
`flags` int(11) NOT NULL,
`active` tinyint(1) DEFAULT NULL,
`published` tinyint(1) DEFAULT 1,
`content` text DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `domainidindex` (`domain_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `cryptokeys`
--
LOCK TABLES `cryptokeys` WRITE;
/*!40000 ALTER TABLE `cryptokeys` DISABLE KEYS */;
/*!40000 ALTER TABLE `cryptokeys` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `domainmetadata`
--
DROP TABLE IF EXISTS `domainmetadata`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `domainmetadata` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) NOT NULL,
`kind` varchar(32) DEFAULT NULL,
`content` text DEFAULT NULL,
PRIMARY KEY (`id`),
KEY `domainmetadata_idx` (`domain_id`,`kind`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `domainmetadata`
--
LOCK TABLES `domainmetadata` WRITE;
/*!40000 ALTER TABLE `domainmetadata` DISABLE KEYS */;
INSERT INTO `domainmetadata` VALUES
(1,1,'SOA-EDIT-API','DEFAULT');
/*!40000 ALTER TABLE `domainmetadata` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `domains`
--
DROP TABLE IF EXISTS `domains`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `domains` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) NOT NULL,
`master` varchar(128) DEFAULT NULL,
`last_check` int(11) DEFAULT NULL,
`type` varchar(8) NOT NULL,
`notified_serial` int(10) unsigned DEFAULT NULL,
`account` varchar(40) CHARACTER SET utf8mb3 DEFAULT NULL,
`options` varchar(64000) DEFAULT NULL,
`catalog` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `name_index` (`name`),
KEY `catalog_idx` (`catalog`)
) ENGINE=InnoDB AUTO_INCREMENT=2 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `domains`
--
LOCK TABLES `domains` WRITE;
/*!40000 ALTER TABLE `domains` DISABLE KEYS */;
INSERT INTO `domains` VALUES
(1,'example.com','',NULL,'NATIVE',NULL,'',NULL,NULL);
/*!40000 ALTER TABLE `domains` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `records`
--
DROP TABLE IF EXISTS `records`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `records` (
`id` bigint(20) NOT NULL AUTO_INCREMENT,
`domain_id` int(11) DEFAULT NULL,
`name` varchar(255) DEFAULT NULL,
`type` varchar(10) DEFAULT NULL,
`content` varchar(64000) DEFAULT NULL,
`ttl` int(11) DEFAULT NULL,
`prio` int(11) DEFAULT NULL,
`disabled` tinyint(1) DEFAULT 0,
`ordername` varchar(255) CHARACTER SET latin1 COLLATE latin1_bin DEFAULT NULL,
`auth` tinyint(1) DEFAULT 1,
PRIMARY KEY (`id`),
KEY `nametype_index` (`name`,`type`),
KEY `domain_id` (`domain_id`),
KEY `ordername` (`ordername`)
) ENGINE=InnoDB AUTO_INCREMENT=4 DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `records`
--
LOCK TABLES `records` WRITE;
/*!40000 ALTER TABLE `records` DISABLE KEYS */;
INSERT INTO `records` VALUES
(1,1,'example.com','NS','ns1.pdns',1500,0,0,NULL,1),
(2,1,'example.com','NS','ns2.pdns',1500,0,0,NULL,1),
(3,1,'example.com','SOA','a.misconfigured.dns.server.invalid hostmaster.example.com 2023030501 10800 3600 604800 3600',1500,0,0,NULL,1);
/*!40000 ALTER TABLE `records` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `supermasters`
--
DROP TABLE IF EXISTS `supermasters`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `supermasters` (
`ip` varchar(64) NOT NULL,
`nameserver` varchar(255) NOT NULL,
`account` varchar(40) CHARACTER SET utf8mb3 NOT NULL,
PRIMARY KEY (`ip`,`nameserver`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `supermasters`
--
LOCK TABLES `supermasters` WRITE;
/*!40000 ALTER TABLE `supermasters` DISABLE KEYS */;
/*!40000 ALTER TABLE `supermasters` ENABLE KEYS */;
UNLOCK TABLES;
--
-- Table structure for table `tsigkeys`
--
DROP TABLE IF EXISTS `tsigkeys`;
/*!40101 SET @saved_cs_client = @@character_set_client */;
/*!40101 SET character_set_client = utf8 */;
CREATE TABLE `tsigkeys` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`name` varchar(255) DEFAULT NULL,
`algorithm` varchar(50) DEFAULT NULL,
`secret` varchar(255) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `namealgoindex` (`name`,`algorithm`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
/*!40101 SET character_set_client = @saved_cs_client */;
--
-- Dumping data for table `tsigkeys`
--
LOCK TABLES `tsigkeys` WRITE;
/*!40000 ALTER TABLE `tsigkeys` DISABLE KEYS */;
/*!40000 ALTER TABLE `tsigkeys` ENABLE KEYS */;
UNLOCK TABLES;
/*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */;
/*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */;
/*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;

View File

@ -0,0 +1,12 @@
{
"pebble": {
"listenAddress": "0.0.0.0:443",
"managementListenAddress": "0.0.0.0:15000",
"certificate": "test/certs/localhost/cert.pem",
"privateKey": "test/certs/localhost/key.pem",
"httpPort": 80,
"tlsPort": 443,
"ocspResponderURL": "",
"externalAccountBindingRequired": false
}
}

View File

@ -0,0 +1,27 @@
# WARNING: This is a CI docker-compose file used for building and testing of the entire app, it should not be used for production.
services:
fullstack:
environment:
DB_MYSQL_HOST: 'db-mysql'
DB_MYSQL_PORT: '3306'
DB_MYSQL_USER: 'npm'
DB_MYSQL_PASSWORD: 'npmpass'
DB_MYSQL_NAME: 'npm'
depends_on:
- db-mysql
db-mysql:
image: jc21/mariadb-aria
environment:
MYSQL_ROOT_PASSWORD: 'npm'
MYSQL_DATABASE: 'npm'
MYSQL_USER: 'npm'
MYSQL_PASSWORD: 'npmpass'
volumes:
- mysql_vol:/var/lib/mysql
networks:
- fulltest
volumes:
mysql_vol:

View File

@ -0,0 +1,9 @@
# WARNING: This is a CI docker-compose file used for building and testing of the entire app, it should not be used for production.
services:
fullstack:
environment:
DB_SQLITE_FILE: '/data/mydb.sqlite'
PUID: 1000
PGID: 1000
DISABLE_IPV6: 'true'

View File

@ -1,91 +1,110 @@
# WARNING: This is a CI docker-compose file used for building and testing of the entire app, it should not be used for production. # WARNING: This is a CI docker-compose file used for building
version: '3.8' # and testing of the entire app, it should not be used for production.
# This is a base compose file, it should be extended with a
# docker-compose.ci.*.yml file
services: services:
fullstack-mysql: fullstack:
image: "${IMAGE}:ci-${BUILD_NUMBER}" image: "${IMAGE}:${BRANCH_LOWER}-ci-${BUILD_NUMBER}"
environment: environment:
DEBUG: 'true' DEBUG: 'true'
LE_STAGING: 'true'
FORCE_COLOR: 1 FORCE_COLOR: 1
DB_MYSQL_HOST: 'db'
DB_MYSQL_PORT: '3306'
DB_MYSQL_USER: 'npm'
DB_MYSQL_PASSWORD: 'npm'
DB_MYSQL_NAME: 'npm'
volumes: volumes:
- npm_data_mysql:/data - 'npm_data_ci:/data'
- npm_le_mysql:/etc/letsencrypt - 'npm_le_ci:/etc/letsencrypt'
expose: - './dev/letsencrypt.ini:/etc/letsencrypt.ini:ro'
- 81 - './dev/resolv.conf:/etc/resolv.conf:ro'
- 80 - '/etc/localtime:/etc/localtime:ro'
- 443 healthcheck:
test: ["CMD", "/usr/bin/check-health"]
interval: 10s
timeout: 3s
networks:
fulltest:
aliases:
- website1.example.com
- website2.example.com
- website3.example.com
stepca:
image: jc21/testca
volumes:
- './dev/resolv.conf:/etc/resolv.conf:ro'
- '/etc/localtime:/etc/localtime:ro'
networks:
fulltest:
aliases:
- ca.internal
pdns:
image: pschiffe/pdns-mysql
volumes:
- '/etc/localtime:/etc/localtime:ro'
environment:
PDNS_master: 'yes'
PDNS_api: 'yes'
PDNS_api_key: 'npm'
PDNS_webserver: 'yes'
PDNS_webserver_address: '0.0.0.0'
PDNS_webserver_password: 'npm'
PDNS_webserver-allow-from: '127.0.0.0/8,192.0.0.0/8,10.0.0.0/8,172.0.0.0/8'
PDNS_version_string: 'anonymous'
PDNS_default_ttl: 1500
PDNS_allow_axfr_ips: '127.0.0.0/8,192.0.0.0/8,10.0.0.0/8,172.0.0.0/8'
PDNS_gmysql_host: pdns-db
PDNS_gmysql_port: 3306
PDNS_gmysql_user: pdns
PDNS_gmysql_password: pdns
PDNS_gmysql_dbname: pdns
depends_on: depends_on:
- db - pdns-db
healthcheck: networks:
test: ["CMD", "/usr/bin/check-health"] fulltest:
interval: 10s aliases:
timeout: 3s - ns1.pdns
- ns2.pdns
fullstack-sqlite: pdns-db:
image: "${IMAGE}:ci-${BUILD_NUMBER}" image: mariadb
environment: environment:
DEBUG: 'true' MYSQL_ROOT_PASSWORD: 'pdns'
LE_STAGING: 'true' MYSQL_DATABASE: 'pdns'
FORCE_COLOR: 1 MYSQL_USER: 'pdns'
DB_SQLITE_FILE: '/data/mydb.sqlite' MYSQL_PASSWORD: 'pdns'
PUID: 1000
PGID: 1000
DISABLE_IPV6: 'true'
volumes: volumes:
- npm_data_sqlite:/data - 'pdns_mysql_vol:/var/lib/mysql'
- npm_le_sqlite:/etc/letsencrypt - '/etc/localtime:/etc/localtime:ro'
expose: - './dev/pdns-db.sql:/docker-entrypoint-initdb.d/01_init.sql:ro'
- 81 networks:
- 80 - fulltest
- 443
healthcheck:
test: ["CMD", "/usr/bin/check-health"]
interval: 10s
timeout: 3s
db: dnsrouter:
image: jc21/mariadb-aria image: jc21/dnsrouter
environment:
MYSQL_ROOT_PASSWORD: 'npm'
MYSQL_DATABASE: 'npm'
MYSQL_USER: 'npm'
MYSQL_PASSWORD: 'npm'
volumes: volumes:
- mysql_data:/var/lib/mysql - ./dev/dnsrouter-config.json.tmp:/dnsrouter-config.json:ro
networks:
- fulltest
cypress-mysql: cypress:
image: "${IMAGE}-cypress:ci-${BUILD_NUMBER}" image: "${IMAGE}-cypress:ci-${BUILD_NUMBER}"
build: build:
context: ../test/ context: ../
dockerfile: cypress/Dockerfile dockerfile: test/cypress/Dockerfile
environment: environment:
CYPRESS_baseUrl: 'http://fullstack-mysql:81' CYPRESS_baseUrl: 'http://fullstack:81'
volumes: volumes:
- cypress_logs_mysql:/results - 'cypress_logs:/results'
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json} - './dev/resolv.conf:/etc/resolv.conf:ro'
command: cypress run --browser chrome --config-file=cypress/config/ci.js
cypress-sqlite: networks:
image: "${IMAGE}-cypress:ci-${BUILD_NUMBER}" - fulltest
build:
context: ../test/
dockerfile: cypress/Dockerfile
environment:
CYPRESS_baseUrl: "http://fullstack-sqlite:81"
volumes:
- cypress_logs_sqlite:/results
command: cypress run --browser chrome --config-file=${CYPRESS_CONFIG:-cypress/config/ci.json}
volumes: volumes:
cypress_logs_mysql: cypress_logs:
cypress_logs_sqlite: npm_data_ci:
npm_data_mysql: npm_le_ci:
npm_data_sqlite: pdns_mysql_vol:
npm_le_sqlite:
npm_le_mysql: networks:
mysql_data: fulltest:
name: "npm-${BRANCH_LOWER}-ci-${BUILD_NUMBER}"

View File

@ -1,5 +1,4 @@
# WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production. # WARNING: This is a DEVELOPMENT docker-compose file, it should not be used for production.
version: '3.8'
services: services:
npm: npm:

View File

@ -0,0 +1,4 @@
log_format proxy '[$time_local] $upstream_cache_status $upstream_status $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] [Sent-to $server] "$http_user_agent" "$http_referer"';
log_format standard '[$time_local] $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] "$http_user_agent" "$http_referer"';
access_log /data/logs/fallback_access.log proxy;

View File

@ -14,6 +14,9 @@ error_log /data/logs/fallback_error.log warn;
# Includes files with directives to load dynamic modules. # Includes files with directives to load dynamic modules.
include /etc/nginx/modules/*.conf; include /etc/nginx/modules/*.conf;
# Custom
include /data/nginx/custom/root_top[.]conf;
events { events {
include /data/nginx/custom/events[.]conf; include /data/nginx/custom/events[.]conf;
} }
@ -43,10 +46,8 @@ http {
proxy_cache_path /var/lib/nginx/cache/public levels=1:2 keys_zone=public-cache:30m max_size=192m; proxy_cache_path /var/lib/nginx/cache/public levels=1:2 keys_zone=public-cache:30m max_size=192m;
proxy_cache_path /var/lib/nginx/cache/private levels=1:2 keys_zone=private-cache:5m max_size=1024m; proxy_cache_path /var/lib/nginx/cache/private levels=1:2 keys_zone=private-cache:5m max_size=1024m;
log_format proxy '[$time_local] $upstream_cache_status $upstream_status $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] [Sent-to $server] "$http_user_agent" "$http_referer"'; # Log format and fallback log file
log_format standard '[$time_local] $status - $request_method $scheme $host "$request_uri" [Client $remote_addr] [Length $body_bytes_sent] [Gzip $gzip_ratio] "$http_user_agent" "$http_referer"'; include /etc/nginx/conf.d/include/log.conf;
access_log /data/logs/fallback_access.log proxy;
# Dynamically generated resolvers file # Dynamically generated resolvers file
include /etc/nginx/conf.d/include/resolvers.conf; include /etc/nginx/conf.d/include/resolvers.conf;

View File

@ -173,6 +173,7 @@ NPM has the ability to include different custom configuration snippets in differ
You can add your custom configuration snippet files at `/data/nginx/custom` as follow: You can add your custom configuration snippet files at `/data/nginx/custom` as follow:
- `/data/nginx/custom/root_top.conf`: Included at the top of nginx.conf
- `/data/nginx/custom/root.conf`: Included at the very end of nginx.conf - `/data/nginx/custom/root.conf`: Included at the very end of nginx.conf
- `/data/nginx/custom/http_top.conf`: Included at the top of the main http block - `/data/nginx/custom/http_top.conf`: Included at the top of the main http block
- `/data/nginx/custom/http.conf`: Included at the end of the main http block - `/data/nginx/custom/http.conf`: Included at the end of the main http block
@ -212,3 +213,12 @@ You can customise the logrotate configuration through a mount (if your custom co
``` ```
For reference, the default configuration can be found [here](https://github.com/NginxProxyManager/nginx-proxy-manager/blob/develop/docker/rootfs/etc/logrotate.d/nginx-proxy-manager). For reference, the default configuration can be found [here](https://github.com/NginxProxyManager/nginx-proxy-manager/blob/develop/docker/rootfs/etc/logrotate.d/nginx-proxy-manager).
## Enabling the geoip2 module
To enable the geoip2 module, you can create the custom configuration file `/data/nginx/custom/root_top.conf` and include the following snippet:
```
load_module /usr/lib/nginx/modules/ngx_http_geoip2_module.so;
load_module /usr/lib/nginx/modules/ngx_stream_geoip2_module.so;
```

View File

@ -35,8 +35,8 @@
"name": "Cloudflare", "name": "Cloudflare",
"package_name": "certbot-dns-cloudflare", "package_name": "certbot-dns-cloudflare",
"version": "=={{certbot-version}}", "version": "=={{certbot-version}}",
"dependencies": "cloudflare acme=={{certbot-version}}", "dependencies": "cloudflare==2.19.* acme=={{certbot-version}}",
"credentials": "# Cloudflare API token\ndns_cloudflare_api_token = 0123456789abcdef0123456789abcdef01234567", "credentials": "# Cloudflare API token\ndns_cloudflare_api_token=0123456789abcdef0123456789abcdef01234567",
"full_plugin_name": "dns-cloudflare" "full_plugin_name": "dns-cloudflare"
}, },
"cloudns": { "cloudns": {
@ -239,6 +239,14 @@
"credentials": "dns_hetzner_api_token = 0123456789abcdef0123456789abcdef", "credentials": "dns_hetzner_api_token = 0123456789abcdef0123456789abcdef",
"full_plugin_name": "dns-hetzner" "full_plugin_name": "dns-hetzner"
}, },
"hover": {
"name": "Hover",
"package_name": "certbot-dns-hover",
"version": "~=1.2.1",
"dependencies": "",
"credentials": "dns_hover_hoverurl = https://www.hover.com\ndns_hover_username = hover-admin-username\ndns_hover_password = hover-admin-password\ndns_hover_totpsecret = 2fa-totp-secret",
"full_plugin_name": "dns-hover"
},
"infomaniak": { "infomaniak": {
"name": "Infomaniak", "name": "Infomaniak",
"package_name": "certbot-dns-infomaniak", "package_name": "certbot-dns-infomaniak",
@ -454,5 +462,13 @@
"dependencies": "", "dependencies": "",
"credentials": "dns_websupport_identifier = <api_key>\ndns_websupport_secret_key = <secret>", "credentials": "dns_websupport_identifier = <api_key>\ndns_websupport_secret_key = <secret>",
"full_plugin_name": "dns-websupport" "full_plugin_name": "dns-websupport"
},
"wedos":{
"name": "Wedos",
"package_name": "certbot-dns-wedos",
"version": "~=2.2",
"dependencies": "",
"credentials": "dns_wedos_user = <wedos_registration>\ndns_wedos_auth = <wapi_sha256_password>",
"full_plugin_name": "dns-wedos"
} }
} }

View File

@ -16,7 +16,7 @@ if hash docker 2>/dev/null; then
-e NODE_OPTIONS=--openssl-legacy-provider \ -e NODE_OPTIONS=--openssl-legacy-provider \
-v "$(pwd)/frontend:/app/frontend" \ -v "$(pwd)/frontend:/app/frontend" \
-v "$(pwd)/global:/app/global" \ -v "$(pwd)/global:/app/global" \
-w /app/frontend "$DOCKER_IMAGE" \ -w /app/frontend "${DOCKER_IMAGE}" \
sh -c "yarn install && yarn build && yarn build && chown -R $(id -u):$(id -g) /app/frontend" sh -c "yarn install && yarn build && yarn build && chown -R $(id -u):$(id -g) /app/frontend"
echo -e "${BLUE} ${GREEN}Building Frontend Complete${RESET}" echo -e "${BLUE} ${GREEN}Building Frontend Complete${RESET}"

89
scripts/ci/fulltest-cypress Executable file
View File

@ -0,0 +1,89 @@
#!/bin/bash
set -e
STACK="${1:-sqlite}"
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
# remember this is running in "ci" folder..
# Some defaults for running this script outside of CI
export COMPOSE_PROJECT_NAME="${COMPOSE_PROJECT_NAME:-npm_local_fulltest}"
export IMAGE="${IMAGE:-nginx-proxy-manager}"
export BRANCH_LOWER="${BRANCH_LOWER:-unknown}"
export BUILD_NUMBER="${BUILD_NUMBER:-0000}"
if [ "${COMPOSE_FILE:-}" = "" ]; then
export COMPOSE_FILE="docker/docker-compose.ci.yml:docker/docker-compose.ci.${STACK}.yml"
fi
# Colors
BLUE='\E[1;34m'
RED='\E[1;31m'
CYAN='\E[1;36m'
GREEN='\E[1;32m'
RESET='\E[0m'
YELLOW='\E[1;33m'
export BLUE CYAN GREEN RESET YELLOW
echo -e "${BLUE} ${CYAN}Starting fullstack cypress testing ...${RESET}"
echo -e "${BLUE} $(docker-compose config)${RESET}"
# $1: container_name
get_container_ip () {
local container_name=$1
local container
local ip
container=$(docker-compose ps --all -q "${container_name}" | tail -n1)
ip=$(docker inspect --format='{{range .NetworkSettings.Networks}}{{.IPAddress}}{{end}}' "$container")
echo "$ip"
}
# Bring up a stack, in steps so we can inject IPs everywhere
docker-compose up -d pdns pdns-db
PDNS_IP=$(get_container_ip "pdns")
echo -e "${BLUE} ${YELLOW}PDNS IP is ${PDNS_IP}${RESET}"
# adjust the dnsrouter config
LOCAL_DNSROUTER_CONFIG="$DIR/../../docker/dev/dnsrouter-config.json"
rm -rf "$LOCAL_DNSROUTER_CONFIG.tmp"
# IMPORTANT: changes to dnsrouter-config.json will affect this line:
jq --arg a "$PDNS_IP" '.servers[0].upstreams[1].upstream = $a' "$LOCAL_DNSROUTER_CONFIG" > "$LOCAL_DNSROUTER_CONFIG.tmp"
docker-compose up -d dnsrouter
DNSROUTER_IP=$(get_container_ip "dnsrouter")
echo -e "${BLUE} ${YELLOW}DNS Router IP is ${DNSROUTER_IP}"
if [ "${DNSROUTER_IP:-}" = "" ]; then
echo -e "${RED} ERROR: DNS Router IP is not set${RESET}"
exit 1
fi
# mount the resolver
LOCAL_RESOLVE="$DIR/../../docker/dev/resolv.conf"
rm -rf "${LOCAL_RESOLVE}"
printf "nameserver %s\noptions ndots:0" "${DNSROUTER_IP}" > "${LOCAL_RESOLVE}"
# bring up all remaining containers, except cypress!
docker-compose up -d --remove-orphans stepca
docker-compose pull db-mysql || true # ok to fail
docker-compose up -d --remove-orphans --pull=never fullstack
# wait for main container to be healthy
bash "$DIR/../wait-healthy" "$(docker-compose ps --all -q fullstack)" 120
# Run tests
rm -rf "$DIR/../../test/results"
docker-compose up --build cypress
# Get results
docker cp -L "$(docker-compose ps --all -q cypress):/test/results" "$DIR/../../test/"
docker cp -L "$(docker-compose ps --all -q fullstack):/data/logs" "$DIR/../../test/results/"
if [ "$2" = "cleanup" ]; then
echo -e "${BLUE} ${CYAN}Cleaning up containers ...${RESET}"
docker-compose down --remove-orphans --volumes -t 30
fi
echo -e "${BLUE} ${GREEN}Fullstack cypress testing complete${RESET}"

View File

@ -3,8 +3,8 @@
DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
. "$DIR/../.common.sh" . "$DIR/../.common.sh"
DOCKER_IMAGE=nginxproxymanager/nginx-full:certbot-node TESTING_IMAGE=nginxproxymanager/nginx-full:certbot-node
docker pull "${DOCKER_IMAGE}" docker pull "${TESTING_IMAGE}"
# Test # Test
echo -e "${BLUE} ${CYAN}Testing backend ...${RESET}" echo -e "${BLUE} ${CYAN}Testing backend ...${RESET}"
@ -12,20 +12,20 @@ docker run --rm \
-v "$(pwd)/backend:/app" \ -v "$(pwd)/backend:/app" \
-v "$(pwd)/global:/app/global" \ -v "$(pwd)/global:/app/global" \
-w /app \ -w /app \
"${DOCKER_IMAGE}" \ "${TESTING_IMAGE}" \
sh -c 'yarn install && yarn eslint . && rm -rf node_modules' sh -c 'yarn install && yarn eslint . && rm -rf node_modules'
echo -e "${BLUE} ${GREEN}Testing Complete${RESET}" echo -e "${BLUE} ${GREEN}Testing Complete${RESET}"
# Build # Build
echo -e "${BLUE} ${CYAN}Building ...${RESET}" echo -e "${BLUE} ${CYAN}Building ...${RESET}"
docker build --pull --no-cache --compress \ docker build --pull --no-cache --compress \
-t "${IMAGE}:ci-${BUILD_NUMBER}" \ -t "${IMAGE:-nginx-proxy-manager}:${BRANCH_LOWER:-unknown}-ci-${BUILD_NUMBER:-0000}" \
-f docker/Dockerfile \ -f docker/Dockerfile \
--progress=plain \ --progress=plain \
--build-arg TARGETPLATFORM=linux/amd64 \ --build-arg TARGETPLATFORM=linux/amd64 \
--build-arg BUILDPLATFORM=linux/amd64 \ --build-arg BUILDPLATFORM=linux/amd64 \
--build-arg BUILD_VERSION="${BUILD_VERSION}" \ --build-arg BUILD_VERSION="${BUILD_VERSION:-unknown}" \
--build-arg BUILD_COMMIT="${BUILD_COMMIT}" \ --build-arg BUILD_COMMIT="${BUILD_COMMIT:-unknown}" \
--build-arg BUILD_DATE="$(date '+%Y-%m-%d %T %Z')" \ --build-arg BUILD_DATE="$(date '+%Y-%m-%d %T %Z')" \
. .
echo -e "${BLUE} ${GREEN}Building Complete${RESET}" echo -e "${BLUE} ${GREEN}Building Complete${RESET}"

View File

@ -23,9 +23,8 @@ until [ "${HEALTHY}" = "healthy" ]; do
((LOOPCOUNT++)) ((LOOPCOUNT++))
if [ "$LOOPCOUNT" == "$LIMIT" ]; then if [ "$LOOPCOUNT" == "$LIMIT" ]; then
echo ""
echo ""
echo -e "${BLUE} ${RED}Timed out waiting for healthy${RESET}" echo -e "${BLUE} ${RED}Timed out waiting for healthy${RESET}"
docker logs --tail 50 "$SERVICE"
exit 1 exit 1
fi fi
done done

View File

@ -1 +0,0 @@
node_modules

View File

@ -73,4 +73,4 @@
} }
] ]
} }
} }

6
test/.gitignore vendored
View File

@ -1,4 +1,2 @@
.vscode results/*
node_modules cypress/results/*
results
cypress/videos

View File

@ -1,13 +1,12 @@
FROM cypress/included:9.4.1 FROM cypress/included:13.9.0
COPY --chown=1000 ./ /test COPY --chown=1000 ./test /test
# mkcert # Disable Cypress CLI colors
ENV MKCERT=1.4.2 ENV FORCE_COLOR=0
RUN wget -O /usr/bin/mkcert "https://github.com/FiloSottile/mkcert/releases/download/v${MKCERT}/mkcert-v${MKCERT}-linux-amd64" \ ENV NO_COLOR=1
&& chmod +x /usr/bin/mkcert
WORKDIR /test WORKDIR /test
RUN yarn install RUN yarn install && yarn cache clean
ENTRYPOINT [] ENTRYPOINT []
CMD ["cypress", "run"] CMD ["cypress", "run"]

22
test/cypress/config/ci.js Normal file
View File

@ -0,0 +1,22 @@
const { defineConfig } = require('cypress');
module.exports = defineConfig({
requestTimeout: 30000,
defaultCommandTimeout: 20000,
reporter: 'cypress-multi-reporters',
reporterOptions: {
configFile: 'multi-reporter.json'
},
video: true,
videosFolder: 'results/videos',
screenshotsFolder: 'results/screenshots',
e2e: {
setupNodeEvents(on, config) {
return require("../plugins/index.js")(on, config);
},
env: {
swaggerBase: '{{baseUrl}}/api/schema',
},
baseUrl: 'http://localhost:1234',
}
});

View File

@ -1,14 +0,0 @@
{
"requestTimeout": 30000,
"defaultCommandTimeout": 20000,
"reporter": "cypress-multi-reporters",
"reporterOptions": {
"configFile": "multi-reporter.json"
},
"videosFolder": "results/videos",
"screenshotsFolder": "results/screenshots",
"env": {
"swaggerBase": "{{baseUrl}}/api/schema",
"RETRIES": 4
}
}

View File

@ -0,0 +1,22 @@
const { defineConfig } = require('cypress');
module.exports = defineConfig({
requestTimeout: 30000,
defaultCommandTimeout: 20000,
reporter: 'cypress-multi-reporters',
reporterOptions: {
configFile: 'multi-reporter.json'
},
video: false,
videosFolder: 'results/videos',
screenshotsFolder: 'results/screenshots',
e2e: {
setupNodeEvents(on, config) {
return require("../plugins/index.js")(on, config);
},
env: {
swaggerBase: '{{baseUrl}}/api/schema',
},
baseUrl: 'http://localhost:1234',
}
});

View File

@ -1,14 +0,0 @@
{
"requestTimeout": 30000,
"defaultCommandTimeout": 20000,
"reporter": "cypress-multi-reporters",
"reporterOptions": {
"configFile": "multi-reporter.json"
},
"videos": false,
"screenshotsFolder": "results/screenshots",
"env": {
"swaggerBase": "{{baseUrl}}/api/schema",
"RETRIES": 0
}
}

View File

@ -1,5 +0,0 @@
{
"name": "Using fixtures to represent data",
"email": "hello@cypress.io",
"body": "Fixtures are a great way to mock data for responses to routes"
}

View File

@ -1,8 +1,12 @@
const _ = require('lodash'); const _ = require("lodash");
const chalk = require('chalk'); const chalk = require("chalk");
module.exports = function () { module.exports = function() {
var arr = _.values(arguments); var arr = _.values(arguments);
arr.unshift(chalk.blue.bold('[') + chalk.yellow.bold('Backend API') + chalk.blue.bold(']')); arr.unshift(
chalk.blue.bold("[") +
chalk.yellow.bold("Backend API") +
chalk.blue.bold("]"),
);
console.log.apply(null, arr); console.log.apply(null, arr);
}; };

View File

@ -9,20 +9,32 @@
// *********************************************** // ***********************************************
// //
import 'cypress-wait-until';
Cypress.Commands.add('randomString', (length) => {
var result = '';
var characters = 'ABCDEFGHIJK LMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
var charactersLength = characters.length;
for (var i = 0; i < length; i++) {
result += characters.charAt(Math.floor(Math.random() * charactersLength));
}
return result;
});
/** /**
* Check the swagger schema: * Check the swagger schema:
* *
* @param {string} method API Method in swagger doc, "get", "put", "post", "delete" * @param {string} method API Method in swagger doc, "get", "put", "post", "delete"
* @param {number} statusCode API status code in swagger doc * @param {integer} code Swagger doc endpoint response code, exactly as defined in swagger doc
* @param {string} path Swagger doc endpoint path, exactly as defined in swagger doc * @param {string} path Swagger doc endpoint path, exactly as defined in swagger doc
* @param {*} data The API response data to check against the swagger schema * @param {*} data The API response data to check against the swagger schema
*/ */
Cypress.Commands.add('validateSwaggerSchema', (method, statusCode, path, data) => { Cypress.Commands.add('validateSwaggerSchema', (method, code, path, data) => {
cy.task('validateSwaggerSchema', { cy.task('validateSwaggerSchema', {
file: Cypress.env('swaggerBase'), file: Cypress.env('swaggerBase'),
endpoint: path, endpoint: path,
method: method, method: method,
statusCode: statusCode, statusCode: code,
responseSchema: data, responseSchema: data,
verbose: true verbose: true
}).should('equal', null); }).should('equal', null);
@ -40,3 +52,19 @@ Cypress.Commands.add('getToken', () => {
cy.wrap(res.token); cy.wrap(res.token);
}); });
}); });
// TODO: copied from v3, is this usable?
Cypress.Commands.add('waitForCertificateStatus', (token, certID, expected, timeout = 60) => {
cy.log(`Waiting for certificate (${certID}) status (${expected}) timeout (${timeout})`);
cy.waitUntil(() => cy.task('backendApiGet', {
token: token,
path: `/api/certificates/${certID}`
}).then((data) => {
return data.result.status === expected;
}), {
errorMsg: 'Waiting for certificate status failed',
timeout: timeout * 1000,
interval: 5000
});
});

View File

@ -1,5 +1,3 @@
require('cypress-plugin-retries');
import './commands'; import './commands';
Cypress.on('uncaught:exception', (/*err, runnable*/) => { Cypress.on('uncaught:exception', (/*err, runnable*/) => {

View File

@ -3,4 +3,4 @@
"./node_modules/cypress", "./node_modules/cypress",
"cypress/**/*.js" "cypress/**/*.js"
] ]
} }

View File

@ -4,19 +4,19 @@
"description": "", "description": "",
"main": "index.js", "main": "index.js",
"dependencies": { "dependencies": {
"@jc21/cypress-swagger-validation": "^0.0.9", "@jc21/cypress-swagger-validation": "^0.2.6",
"@jc21/restler": "^3.4.0", "@jc21/restler": "^3.4.0",
"chalk": "^4.1.0", "chalk": "^4.1.0",
"cypress": "^9.4.1", "cypress": "^13.9.0",
"cypress-multi-reporters": "^1.4.0", "cypress-multi-reporters": "^1.6.4",
"cypress-plugin-retries": "^1.5.2", "cypress-wait-until": "^3.0.1",
"eslint": "^7.6.0", "eslint": "^9.3.0",
"eslint-plugin-align-assignments": "^1.1.2", "eslint-plugin-align-assignments": "^1.1.2",
"eslint-plugin-chai-friendly": "^0.6.0", "eslint-plugin-chai-friendly": "^0.7.4",
"eslint-plugin-cypress": "^2.11.1", "eslint-plugin-cypress": "^3.2.0",
"lodash": "^4.17.19", "lodash": "^4.17.21",
"mocha": "^8.1.1", "mocha": "^10.4.0",
"mocha-junit-reporter": "^2.0.0" "mocha-junit-reporter": "^2.2.1"
}, },
"scripts": { "scripts": {
"cypress": "cypress open --config-file=cypress/config/dev.json --config baseUrl=${BASE_URL:-http://127.0.0.1:3081}", "cypress": "cypress open --config-file=cypress/config/dev.json --config baseUrl=${BASE_URL:-http://127.0.0.1:3081}",

File diff suppressed because it is too large Load Diff