diff --git a/packages/db/src/index.test.ts b/packages/db/src/index.test.ts new file mode 100644 index 0000000..22ca2a5 --- /dev/null +++ b/packages/db/src/index.test.ts @@ -0,0 +1,68 @@ +import { Context, getInternalEndpoint } from '@osaas/client-core'; +import { getApacheCouchdbInstance } from '@osaas/client-services'; +import { setupDatabase } from '.'; + +jest.mock('@osaas/client-core', () => { + return { + getInternalEndpoint: jest.fn(), + getPortsForInstance: jest.fn(), + Context: jest.fn().mockImplementation(() => { + return { + getServiceAccessToken: jest.fn().mockResolvedValue('token') + }; + }) + }; +}); + +jest.mock('@osaas/client-services', () => { + return { + getApacheCouchdbInstance: jest.fn(), + createApacheCouchdbInstance: jest.fn() + }; +}); + +describe('setupDatabase couchdb', () => { + afterEach(() => { + jest.clearAllMocks(); + }); + + it('returns an http:// URL for a private (publicAccess: false) instance', async () => { + (getApacheCouchdbInstance as jest.Mock).mockResolvedValue({ + name: 'myinstance', + url: 'https://myinstance.apache-couchdb.auto.dev.osaas.io', + AdminPassword: 'secret' + }); + (getInternalEndpoint as jest.Mock).mockResolvedValue({ + serviceDns: 'myinstance.apache-couchdb.svc.cluster.local', + ports: [{ name: 'couchdb', port: 5984, protocol: 'TCP' }], + publicAccess: false + }); + + const ctx = new Context(); + const url = await setupDatabase(ctx, 'couchdb', 'myinstance', { + rootPassword: 'secret', + publicAccess: false + }); + + expect(url).toMatch(/^http:\/\//); + expect(url).toBe( + 'http://admin:secret@myinstance.apache-couchdb.svc.cluster.local:5984' + ); + }); + + it('returns an https:// URL for a public instance', async () => { + (getApacheCouchdbInstance as jest.Mock).mockResolvedValue({ + name: 'myinstance', + url: 'https://myinstance.apache-couchdb.auto.dev.osaas.io', + AdminPassword: 'secret' + }); + + const ctx = new Context(); + const url = await setupDatabase(ctx, 'couchdb', 'myinstance', { + rootPassword: 'secret' + }); + + expect(url).toMatch(/^https:\/\//); + expect(getInternalEndpoint).not.toHaveBeenCalled(); + }); +}); diff --git a/packages/db/src/index.ts b/packages/db/src/index.ts index a7a38fc..ca79fa7 100644 --- a/packages/db/src/index.ts +++ b/packages/db/src/index.ts @@ -54,6 +54,22 @@ export const DatabaseTypeToProtocol: Record = { couchdb: 'https' }; +/** + * Protocol to use when connecting to the *internal* (cluster-local) endpoint + * of a database, as opposed to its public ingress URL. + * + * This only needs an entry for database types whose `DatabaseTypeToProtocol` + * value is not valid for the raw container port. CouchDB's public URL is + * HTTPS (TLS terminated at the ingress on port 443), but its internal + * container port (5984) speaks plaintext HTTP - there is no in-cluster TLS + * termination on that port. Other database types use non-HTTP schemes + * (postgres://, mysql://, redis://, clickhouse://) for both paths and are + * unaffected. + */ +const DatabaseTypeToInternalProtocol: Partial> = { + couchdb: 'http' +}; + export interface DatabaseOpts { username?: string; password?: string; @@ -259,7 +275,7 @@ export async function setupDatabase( (p) => p.port === DatabaseTypeToPort['couchdb'] ); if (port) { - return `${DatabaseTypeToProtocol['couchdb']}://admin:${ + return `${DatabaseTypeToInternalProtocol['couchdb']}://admin:${ (instance as any).AdminPassword }@${endpointInfo.serviceDns}:${port.port}`; }