Skip to content

Commit

Permalink
fix: handle port value provided with envs
Browse files Browse the repository at this point in the history
ref: #8
  • Loading branch information
zavoloklom committed Sep 20, 2024
1 parent f3187a6 commit 63c6176
Show file tree
Hide file tree
Showing 6 changed files with 60 additions and 29 deletions.
24 changes: 24 additions & 0 deletions docs/rules/no-duplicate-exported-ports-rule.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,30 @@ Duplicate `ports` can often be the result of simple typographical errors. By cat
developers can avoid debugging complex port conflicts and ensure their Compose configurations are valid before
attempting to run them.

## Known Limitations

This rule does not support the detection of duplicate exported ports when ports are defined using environment variables.
Since environment variables are resolved at runtime, it's impossible to statically determine their values during the
linting process.

For example, in the following configuration:

```yaml
services:
web:
image: image
ports:
- "${APP_PORT}:80"
db:
image: image
ports:
- "8080:80"
```
The `APP_PORT` environment variable is used to define the external port. However, since its value is not available at
the time of linting, the rule will not be able to detect if the port conflicts with other services. It's recommended to
ensure that environment variables used for ports are unique or manually check for conflicts in such cases.

## Version

This rule was introduced in Docker-Compose-Linter [1.0.0](https://github.com/zavoloklom/docker-compose-linter/releases).
Expand Down
2 changes: 2 additions & 0 deletions docs/rules/service-ports-alphabetical-order-rule.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ services:
web:
image: nginx
ports:
- "${WEB_PORT}:80"
- '81'
- "79"
- 80:80
Expand All @@ -36,6 +37,7 @@ services:
web:
image: nginx
ports:
- "${WEB_PORT}:80"
- "79"
- 80:80
- '81'
Expand Down
27 changes: 14 additions & 13 deletions src/util/service-ports-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,25 @@ function extractPublishedPortValue(yamlNode: unknown): string {
function parsePortsRange(port: string): string[] {
const [start, end] = port.split('-').map(Number);

if (Number.isNaN(start)) {
throw new Error('Invalid port range');
if (Number.isNaN(start) || Number.isNaN(end)) {
return [];
}

if (end && !Number.isNaN(end)) {
if (start > end) {
throw new Error('Invalid port range: start port is greater than end port');
}
if (!end) {
return [start.toString()];
}

const ports: string[] = [];
// eslint-disable-next-line no-plusplus
for (let i = start; i <= end; i++) {
ports.push(i.toString());
}
return ports;
if (start > end) {
// Invalid port range: start port is greater than end port
return [];
}

return [start.toString()];
const ports: string[] = [];
// eslint-disable-next-line no-plusplus
for (let i = start; i <= end; i++) {
ports.push(i.toString());
}
return ports;
}

export { extractPublishedPortValue, parsePortsRange };
6 changes: 6 additions & 0 deletions tests/rules/no-duplicate-exported-ports-rule.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@ services:
published: 8080
protocol: tcp
mode: host
g-service:
image: nginx:latest
ports:
- "$WEB_PORT:80"
- "$WEB_PORT:81"
- "$WEB_PORT-9000:80-81"
`;

// YAML with unique exported ports using different syntax
Expand Down
2 changes: 2 additions & 0 deletions tests/rules/service-ports-alphabetical-order-rule.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,15 @@ services:
published: 8082
protocol: tcp
mode: host
- "$WEB_PORT:80"
`;

const yamlWithCorrectPortOrder = `
services:
web:
image: nginx
ports:
- "$WEB_PORT:80"
- "79"
- 80:80
- '81'
Expand Down
28 changes: 12 additions & 16 deletions tests/util/service-ports-parser.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,23 @@ test('extractPublishedPortValue should return empty string for unknown node type
});

test('parsePortsRange should return array of ports for a range', (t) => {
const result = parsePortsRange('3000-3002');
t.deepEqual(result, ['3000', '3001', '3002']);
});

test('parsePortsRange should throw error for invalid range', (t) => {
const error = t.throws(() => parsePortsRange('invalid-range'), {
instanceOf: Error,
message: 'Invalid port range',
});
t.is(error?.message, 'Invalid port range');
t.deepEqual(parsePortsRange('3000-3002'), ['3000', '3001', '3002']);
t.deepEqual(parsePortsRange('3000-3000'), ['3000']);
});

test('parsePortsRange should return single port when no range is specified', (t) => {
const result = parsePortsRange('8080');
t.deepEqual(result, ['8080']);
});

test('parsePortsRange should throw error when start port is greater than end port', (t) => {
const error = t.throws(() => parsePortsRange('3003-3001'), {
instanceOf: Error,
message: 'Invalid port range: start port is greater than end port',
});
t.is(error?.message, 'Invalid port range: start port is greater than end port');
test('parsePortsRange should return empty array for invalid range', (t) => {
t.deepEqual(parsePortsRange('$TEST'), []);
t.deepEqual(parsePortsRange('$TEST-3002'), []);
t.deepEqual(parsePortsRange('3000-$TEST'), []);
t.deepEqual(parsePortsRange('$TEST-$TEST'), []);
t.deepEqual(parsePortsRange('3000-$TEST-$TEST-5000'), []);
});

test('parsePortsRange should return empty array when start port is greater than end port', (t) => {
t.deepEqual(parsePortsRange('3005-3002'), []);
});

0 comments on commit 63c6176

Please sign in to comment.