Skip to content

Commit

Permalink
feat(tapd): fix compatibility with tapd v0.3.3
Browse files Browse the repository at this point in the history
  • Loading branch information
jamaljsr committed Jul 15, 2024
1 parent 1715c85 commit 1fc7466
Show file tree
Hide file tree
Showing 7 changed files with 142 additions and 39 deletions.
4 changes: 2 additions & 2 deletions src/components/common/AdvancedOptionsButton.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { Button, Form } from 'antd';
import { usePrefixedTranslation } from 'hooks';
import { AnyNode } from 'shared/types';
import { useStoreActions } from 'store';
import { dockerConfigs } from 'utils/constants';
import { getDefaultCommand } from 'utils/network';

interface Props {
node: AnyNode;
Expand All @@ -18,7 +18,7 @@ const AdvancedOptionsButton: React.FC<Props> = ({ node, type }) => {
showAdvancedOptions({
nodeName: node.name,
command: node.docker.command,
defaultCommand: dockerConfigs[node.implementation].command,
defaultCommand: getDefaultCommand(node.implementation, node.version),
});
};

Expand Down
6 changes: 5 additions & 1 deletion src/components/nodeImages/ManagedImageModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { usePrefixedTranslation } from 'hooks';
import { useStoreActions } from 'store';
import { ManagedImage } from 'types';
import { dockerConfigs } from 'utils/constants';
import { getDefaultCommand } from 'utils/network';
import { CommandVariables } from './';

const Styled = {
Expand Down Expand Up @@ -68,7 +69,10 @@ const ManagedImageModal: React.FC<Props> = ({ image, onClose }) => {
layout="vertical"
hideRequiredMark
colon={false}
initialValues={{ command: image.command || config.command }}
initialValues={{
command:
image.command || getDefaultCommand(image.implementation, image.version),
}}
onFinish={handleSubmit}
>
<Styled.Summary>{l('summary')}</Styled.Summary>
Expand Down
24 changes: 22 additions & 2 deletions src/lib/docker/composeFile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,33 @@ describe('ComposeFile', () => {
});

it('should use the tapd nodes custom docker data', () => {
tapNode.docker = { image: 'my-image', command: 'my-command' };
composeFile.addTapd(tapNode, lndNode);
const tap = {
...tapNode,
docker: { image: 'my-image', command: 'my-command' },
};
composeFile.addTapd(tap, lndNode);
const service = composeFile.content.services['alice-tap'];
expect(service.image).toBe('my-image');
expect(service.command).toBe('my-command');
});

it('should use the correct command for tapd v3', () => {
const tap = { ...tapNode, version: '0.3.3' };
composeFile.addTapd(tap, lndNode);
const service = composeFile.content.services['alice-tap'];
expect(service.command).toContain('--universe.public-access');
expect(service.command).not.toContain('--universe.public-access=rw');
expect(service.command).not.toContain('--universe.sync-all-assets');
});

it('should use the correct command for tapd v4+', () => {
const tap = { ...tapNode, version: '0.4.0' };
composeFile.addTapd(tap, lndNode);
const service = composeFile.content.services['alice-tap'];
expect(service.command).toContain('--universe.public-access=rw');
expect(service.command).toContain('--universe.sync-all-assets');
});

it('should add an litd config', () => {
composeFile.addLitd(litdNode, btcNode);
expect(composeFile.content.services['dave']).not.toBeUndefined();
Expand Down
14 changes: 7 additions & 7 deletions src/lib/docker/composeFile.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import {
eclairCredentials,
litdCredentials,
} from 'utils/constants';
import { getContainerName } from 'utils/network';
import { getContainerName, getDefaultCommand } from 'utils/network';
import { bitcoind, clightning, eclair, litd, lnd, tapd } from './nodeTemplates';

export interface ComposeService {
Expand Down Expand Up @@ -71,7 +71,7 @@ class ComposeFile {
// use the node's custom image or the default for the implementation
const image = node.docker.image || `${dockerConfigs.bitcoind.imageName}:${version}`;
// use the node's custom command or the default for the implementation
const nodeCommand = node.docker.command || dockerConfigs.bitcoind.command;
const nodeCommand = node.docker.command || getDefaultCommand('bitcoind', version);
// replace the variables in the command
const command = this.mergeCommand(nodeCommand, variables);
// add the docker service
Expand All @@ -94,7 +94,7 @@ class ComposeFile {
// use the node's custom image or the default for the implementation
const image = node.docker.image || `${dockerConfigs.LND.imageName}:${version}`;
// use the node's custom command or the default for the implementation
const nodeCommand = node.docker.command || dockerConfigs.LND.command;
const nodeCommand = node.docker.command || getDefaultCommand('LND', version);
// replace the variables in the command
const command = this.mergeCommand(nodeCommand, variables);
// add the docker service
Expand All @@ -117,7 +117,7 @@ class ComposeFile {
const image =
node.docker.image || `${dockerConfigs['c-lightning'].imageName}:${version}`;
// use the node's custom command or the default for the implementation
let nodeCommand = node.docker.command || dockerConfigs['c-lightning'].command;
let nodeCommand = node.docker.command || getDefaultCommand('c-lightning', version);
// do not include the GRPC port arg in the command for unsupported versions
if (grpc === 0) nodeCommand = nodeCommand.replace('--grpc-port=11001', '');
// replace the variables in the command
Expand All @@ -142,7 +142,7 @@ class ComposeFile {
// use the node's custom image or the default for the implementation
const image = node.docker.image || `${dockerConfigs.eclair.imageName}:${version}`;
// use the node's custom command or the default for the implementation
const nodeCommand = node.docker.command || dockerConfigs.eclair.command;
const nodeCommand = node.docker.command || getDefaultCommand('eclair', version);
// replace the variables in the command
const command = this.mergeCommand(nodeCommand, variables);
// add the docker service
Expand All @@ -166,7 +166,7 @@ class ComposeFile {
// use the node's custom image or the default for the implementation
const image = node.docker.image || `${dockerConfigs.litd.imageName}:${version}`;
// use the node's custom command or the default for the implementation
const nodeCommand = node.docker.command || dockerConfigs.litd.command;
const nodeCommand = node.docker.command || getDefaultCommand('litd', version);
// replace the variables in the command
const command = this.mergeCommand(nodeCommand, variables);
// add the docker service
Expand All @@ -187,7 +187,7 @@ class ComposeFile {
// use the node's custom image or the default for the implementation
const image = node.docker.image || `${dockerConfigs.tapd.imageName}:${version}`;
// use the node's custom command or the default for the implementation
const nodeCommand = node.docker.command || dockerConfigs.tapd.command;
const nodeCommand = node.docker.command || getDefaultCommand('tapd', version);
// replace the variables in the command
const command = this.mergeCommand(nodeCommand, variables);
// add the docker service
Expand Down
22 changes: 21 additions & 1 deletion src/utils/network.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ import { read, rm } from './files';
import { migrateNetworksFile } from './migrations';
import { getName } from './names';
import { range } from './numbers';
import { isVersionCompatible } from './strings';
import { isVersionBelow, isVersionCompatible } from './strings';
import { getPolarPlatform } from './system';
import { prefixTranslation } from './translate';

Expand Down Expand Up @@ -688,6 +688,26 @@ export const getMissingImages = (network: Network, pulled: string[]): string[] =
return unique;
};

/**
* Returns the default docker command for a given implementation and version. This will
* tweak commands for older node versions that do not support certain flags.
*/
export const getDefaultCommand = (
implementation: NodeImplementation,
version: string,
) => {
let command = dockerConfigs[implementation].command;

// Remove the flags that are not supported in versions before v0.4.0
if (implementation === 'tapd' && isVersionBelow(version, '0.4.0-alpha')) {
command = command
.replace('--universe.public-access=rw', '--universe.public-access')
.replace('--universe.sync-all-assets', '');
}

return command;
};

/**
* Checks a range of port numbers to see if they are open on the current operating system.
* Returns a new array of port numbers that are confirmed available
Expand Down
55 changes: 48 additions & 7 deletions src/utils/strings.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ellipseInner, isVersionCompatible } from './strings';
import { compareVersions, ellipseInner, isVersionCompatible } from './strings';

describe('strings util', () => {
describe('ellipseInner', () => {
Expand Down Expand Up @@ -36,6 +36,7 @@ describe('strings util', () => {
expect(isVersionCompatible('0.17.0', '0.18.1')).toBe(true);
expect(isVersionCompatible('0.17.2', '0.18.1')).toBe(true);
expect(isVersionCompatible('0.18.0.1', '0.18.1')).toBe(true);
expect(isVersionCompatible('0.7.1-beta', '0.16.0-beta')).toBe(true);
});

it('should return false for incompatible versions', () => {
Expand All @@ -44,15 +45,55 @@ describe('strings util', () => {
expect(isVersionCompatible('1.18.1', '0.18.1')).toBe(false);
expect(isVersionCompatible('0.18.1.1', '0.18.1')).toBe(false);
expect(isVersionCompatible('0.19.0.1', '0.18.1')).toBe(false);
expect(isVersionCompatible('0.17.1-beta', '0.6.0-beta')).toBe(false);
});

it('should return false for garbage input', () => {
it('should handle garbage input', () => {
expect(isVersionCompatible('123', '0.18.1')).toBe(false);
expect(isVersionCompatible('asdf', '0.18.1')).toBe(false);
expect(isVersionCompatible('', '0.18.1')).toBe(false);
expect(isVersionCompatible('0.18.asds', '0.18.1')).toBe(false);
expect(isVersionCompatible('asf.18.0', '0.18.1')).toBe(false);
expect(isVersionCompatible(undefined as any, '0.18.1')).toBe(false);
expect(isVersionCompatible('asdf', 'xyz')).toBe(true);
expect(isVersionCompatible('', '0.18.1')).toBe(true);
expect(isVersionCompatible(undefined as any, '0.18.1')).toBe(true);
});
});

describe('compareVersions', () => {
it('should return 0 for equal versions', () => {
expect(compareVersions('0.18.1', '0.18.1')).toBe(0);
expect(compareVersions('0.18.0', '0.18.0')).toBe(0);
expect(compareVersions('0.17.0', '0.17.0')).toBe(0);
expect(compareVersions('0.17.2', '0.17.2')).toBe(0);
});

it('should return 1 for higher versions', () => {
expect(compareVersions('0.19.0', '0.18.1')).toBe(1);
expect(compareVersions('0.18.2', '0.18.1')).toBe(1);
expect(compareVersions('1.18.1', '0.18.1')).toBe(1);
expect(compareVersions('1.18.1.1', '0.18.1')).toBe(1);
expect(compareVersions('0.17.1-beta.rc1', '0.17.1-beta')).toBe(1);
});

it('should return -1 for lower versions', () => {
expect(compareVersions('0.17.0', '0.18.1')).toBe(-1);
expect(compareVersions('0.7.2', '0.18.1')).toBe(-1);
expect(compareVersions('1.17.1', '1.18.1')).toBe(-1);
expect(compareVersions('1.17.1.1', '1.18.1')).toBe(-1);
expect(compareVersions('0.7.1-beta', '0.16.0-beta')).toBe(-1);
expect(compareVersions('0.17.1-beta', '0.17.1-beta.rc1')).toBe(-1);
});

it('should return 0 for garbage input', () => {
expect(compareVersions('asdf', 'xyz')).toBe(0);
expect(compareVersions('', '0.18.1')).toBe(0);
expect(compareVersions(undefined as any, '0.18.1')).toBe(0);
expect(compareVersions('0.18.1', undefined as any)).toBe(0);
});

it('should handle pre-release tags', () => {
expect(compareVersions('0.18.1-beta', '0.18.1-alpha')).toBe(0);
expect(compareVersions('0.18.1-beta', '0.18.1-beta')).toBe(0);
expect(compareVersions('0.18.1-beta', '0.18.1-beta.rc1')).toBe(-1);
expect(compareVersions('0.18.1-beta.rc2', '0.18.1-beta.rc1')).toBe(1);
expect(compareVersions('0.18.2-beta', '0.18.1-beta.rc1')).toBe(1);
});
});
});
56 changes: 37 additions & 19 deletions src/utils/strings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,29 +36,47 @@ export const ellipseInner = (
* isVersionCompatible('0.19.0.1', '0.18.1') => false
*/
export const isVersionCompatible = (version: string, maxVersion: string): boolean => {
return compareVersions(version, maxVersion) <= 0;
};

/**
* Checks if the version provided is lower than the maximum version provided.
*/
export const isVersionBelow = (version: string, maxVersion: string): boolean => {
return compareVersions(version, maxVersion) < 0;
};

/**
* Compares two versions and returns a number indicating if the first version is higher,
* lower, or equal to the second version.
* @returns 0 if the versions are equal, 1 if the first version is higher, and -1 if the
* first version is lower
*/
export const compareVersions = (aVersion: string, bVersion: string): number => {
// sanity checks
if (!version || !maxVersion) return false;
if (!aVersion || !bVersion) return 0;

// helper function to split the version into an array of numbers using a regex
const split = (ver: string) => [...ver.matchAll(/\d+/g)].map(a => parseInt(a[0]));

// convert version into a number array
const versionParts = version.split('.').map(n => parseInt(n));
// convert maxversion into a number array
const maxParts = maxVersion.split('.').map(n => parseInt(n));
const aParts = split(aVersion);
// convert minVersion into a number array
const bParts = split(bVersion);

// get the longest length of the two versions. May be 3 or 4 with bitcoind
const len = Math.max(versionParts.length, maxParts.length);
const len = Math.max(aParts.length, bParts.length);
// loop over each number in the version from left ot right
for (let i = 0; i < len; i++) {
const ver = versionParts[i];
const max = maxParts[i];
// if version has more digits than maxVersion, return the result of the previous digit
// '0.18.0.1' <= '0.18.1' = true
// '0.18.1.1' <= '0.18.1' = false
if (max === undefined) return versionParts[i - 1] < maxParts[i - 1];
// bail for non-numeric input
if (isNaN(ver) || isNaN(max)) return false;
// if any number is higher, then the version is not compatible
if (ver > max) return false;
// if the numder is lower, then return true immediately
if (ver < max) return true;
//if the digits are equal, check the next digit
const aNum = aParts[i] || 0;
const bNum = bParts[i] || 0;
// if the digit is higher, then return a positive number
if (aNum < bNum) return -1;
// if the digit is lower, then return a negative number
if (aNum > bNum) return 1;
// if the digits are equal, check the next digit
}
return true;

// if all digits are equal, return 0
return 0;
};

0 comments on commit 1fc7466

Please sign in to comment.