Skip to content

Commit 13753b7

Browse files
committed
Sync branch with main and resolve merge conflicts
Signed-off-by: Marko Mlakar <marko.mlakar@dynatrace.com>
1 parent 17fd726 commit 13753b7

File tree

9 files changed

+64
-15
lines changed

9 files changed

+64
-15
lines changed

libs/providers/flagd/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ Options can be defined in the constructor or as environment variables. Construct
3333
| port | FLAGD_PORT | number | [resolver specific defaults](#resolver-type-specific-defaults) | |
3434
| tls | FLAGD_TLS | boolean | false | |
3535
| socketPath | FLAGD_SOCKET_PATH | string | - | |
36+
| certPath | FLAGD_SERVER_CERT_PATH | string | - | |
3637
| resolverType | FLAGD_RESOLVER | string | rpc | rpc, in-process |
3738
| offlineFlagSourcePath | FLAGD_OFFLINE_FLAG_SOURCE_PATH | string | - | |
3839
| selector | FLAGD_SOURCE_SELECTOR | string | - | |

libs/providers/flagd/src/e2e/step-definitions/providerSteps.ts

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,8 @@ import { FlagdContainer } from '../tests/flagdContainer';
44
import type { State, Steps } from './state';
55
import { FlagdProvider } from '../../lib/flagd-provider';
66
import type { FlagdProviderOptions } from '../../lib/configuration';
7+
import { resolve } from 'node:path';
8+
import { existsSync } from 'node:fs';
79

810
export const providerSteps: Steps =
911
(state: State) =>
@@ -43,11 +45,23 @@ export const providerSteps: Steps =
4345
case 'unavailable':
4446
flagdOptions['port'] = 9999;
4547
break;
46-
case 'ssl':
47-
// TODO: modify this to support ssl
48+
case 'ssl': {
4849
flagdOptions['port'] = container.getPort(state.resolverType);
50+
flagdOptions['tls'] = true;
51+
const certPath = resolve(
52+
__dirname,
53+
'./../../../../../shared/flagd-core/test-harness/ssl/custom-root-cert.crt',
54+
);
55+
flagdOptions['certPath'] = certPath;
56+
if (!existsSync(certPath)) {
57+
throw new Error('Certificate file not found at path: ' + certPath);
58+
}
59+
if (state?.config?.selector) {
60+
flagdOptions['selector'] = state.config.selector;
61+
}
4962
type = 'ssl';
5063
break;
64+
}
5165
case 'stable':
5266
flagdOptions['port'] = container.getPort(state.resolverType);
5367
break;

libs/providers/flagd/src/e2e/tests/in-process.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ describe('in-process', () => {
2222
// remove filters as we add support for features
2323
// see: https://github.com/open-feature/js-sdk-contrib/issues/1096 and child issues
2424
tagFilter:
25-
'@in-process and not @targetURI and not @forbidden and not @customCert and not @events and not @sync and not @grace and not @metadata and not @unixsocket',
25+
'@in-process and not @targetURI and not @forbidden and not @events and not @sync and not @grace and not @metadata and not @unixsocket',
2626
scenarioNameTemplate: (vars) => {
2727
return `${vars.scenarioTitle} (${vars.scenarioTags.join(',')} ${vars.featureTags.join(',')})`;
2828
},

libs/providers/flagd/src/e2e/tests/rpc.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ describe('rpc', () => {
2323
tagFilter:
2424
// remove filters as we add support for features
2525
// see: https://github.com/open-feature/js-sdk-contrib/issues/1096 and child issues
26-
'@rpc and not @targetURI and not @customCert and not @forbidden and not @events and not @stream and not @grace and not @metadata and not @caching and not @unixsocket',
26+
'@rpc and not @targetURI and not @forbidden and not @events and not @stream and not @grace and not @metadata and not @caching and not @unixsocket',
2727
scenarioNameTemplate: (vars) => {
2828
return `${vars.scenarioTitle} (${vars.scenarioTags.join(',')} ${vars.featureTags.join(',')})`;
2929
},

libs/providers/flagd/src/lib/configuration.spec.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ describe('Configuration', () => {
3030
const port = 8080;
3131
const tls = true;
3232
const socketPath = '/tmp/flagd.socks';
33+
const certPath = '/etc/cert/ca.crt';
3334
const maxCacheSize = 333;
3435
const cache = 'disabled';
3536
const resolverType = 'in-process';
@@ -41,6 +42,7 @@ describe('Configuration', () => {
4142
process.env['FLAGD_PORT'] = `${port}`;
4243
process.env['FLAGD_TLS'] = `${tls}`;
4344
process.env['FLAGD_SOCKET_PATH'] = socketPath;
45+
process.env['FLAGD_SERVER_CERT_PATH'] = certPath;
4446
process.env['FLAGD_CACHE'] = cache;
4547
process.env['FLAGD_MAX_CACHE_SIZE'] = `${maxCacheSize}`;
4648
process.env['FLAGD_SOURCE_SELECTOR'] = `${selector}`;
@@ -54,6 +56,7 @@ describe('Configuration', () => {
5456
port,
5557
tls,
5658
socketPath,
59+
certPath,
5760
maxCacheSize,
5861
cache,
5962
resolverType,
@@ -99,6 +102,7 @@ describe('Configuration', () => {
99102
host: 'test',
100103
port: 3000,
101104
tls: true,
105+
certPath: '/custom/cert.pem',
102106
maxCacheSize: 1000,
103107
cache: 'lru',
104108
resolverType: 'rpc',
@@ -112,6 +116,7 @@ describe('Configuration', () => {
112116
process.env['FLAGD_PORT'] = '8080';
113117
process.env['FLAGD_SYNC_PORT'] = '9090';
114118
process.env['FLAGD_TLS'] = 'false';
119+
process.env['FLAGD_SERVER_CERT_PATH'] = '/env/cert.pem';
115120
process.env['FLAGD_DEFAULT_AUTHORITY'] = 'test-authority-override';
116121

117122
expect(getConfig(options)).toStrictEqual(options);

libs/providers/flagd/src/lib/configuration.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,13 @@ export interface Config {
4040
*/
4141
socketPath?: string;
4242

43+
/**
44+
* TLS certificate path to use when TLS connectivity is enabled.
45+
*
46+
* @example "/etc/cert/ca.crt"
47+
*/
48+
certPath?: string;
49+
4350
/**
4451
* Resolver type to use by the provider.
4552
*
@@ -120,6 +127,7 @@ enum ENV_VAR {
120127
FLAGD_DEADLINE_MS = 'FLAGD_DEADLINE_MS',
121128
FLAGD_TLS = 'FLAGD_TLS',
122129
FLAGD_SOCKET_PATH = 'FLAGD_SOCKET_PATH',
130+
FLAGD_SERVER_CERT_PATH = 'FLAGD_SERVER_CERT_PATH',
123131
FLAGD_CACHE = 'FLAGD_CACHE',
124132
FLAGD_MAX_CACHE_SIZE = 'FLAGD_MAX_CACHE_SIZE',
125133
FLAGD_SOURCE_SELECTOR = 'FLAGD_SOURCE_SELECTOR',
@@ -165,6 +173,9 @@ const getEnvVarConfig = (): Partial<Config> => {
165173
...(process.env[ENV_VAR.FLAGD_SOCKET_PATH] && {
166174
socketPath: process.env[ENV_VAR.FLAGD_SOCKET_PATH],
167175
}),
176+
...(process.env[ENV_VAR.FLAGD_SERVER_CERT_PATH] && {
177+
certPath: process.env[ENV_VAR.FLAGD_SERVER_CERT_PATH],
178+
}),
168179
...((process.env[ENV_VAR.FLAGD_CACHE] === 'lru' || process.env[ENV_VAR.FLAGD_CACHE] === 'disabled') && {
169180
cache: process.env[ENV_VAR.FLAGD_CACHE],
170181
}),

libs/providers/flagd/src/lib/service/common/grpc-util.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
1+
import { credentials } from '@grpc/grpc-js';
12
import type { ClientReadableStream } from '@grpc/grpc-js';
3+
import { readFileSync, existsSync } from 'node:fs';
24

35
export const closeStreamIfDefined = (stream: ClientReadableStream<unknown> | undefined) => {
46
/**
@@ -14,3 +16,18 @@ export const closeStreamIfDefined = (stream: ClientReadableStream<unknown> | und
1416
stream.destroy();
1517
}
1618
};
19+
20+
/**
21+
* Creates gRPC channel credentials based on TLS and certificate path configuration.
22+
* @returns Channel credentials for gRPC connection
23+
*/
24+
export const createChannelCredentials = (tls: boolean, certPath?: string) => {
25+
if (!tls) {
26+
return credentials.createInsecure();
27+
}
28+
if (certPath && existsSync(certPath)) {
29+
const rootCerts = readFileSync(certPath);
30+
return credentials.createSsl(rootCerts);
31+
}
32+
return credentials.createSsl();
33+
};

libs/providers/flagd/src/lib/service/grpc/grpc-service.ts

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@ import {
1111
} from '@openfeature/server-sdk';
1212
import { LRUCache } from 'lru-cache';
1313
import { promisify } from 'node:util';
14+
import { readFileSync, existsSync } from 'node:fs';
15+
1416
import type {
1517
EventStreamResponse,
1618
ResolveBooleanRequest,
@@ -29,7 +31,7 @@ import type { Config } from '../../configuration';
2931
import { DEFAULT_MAX_CACHE_SIZE, EVENT_CONFIGURATION_CHANGE, EVENT_PROVIDER_READY } from '../../constants';
3032
import { FlagdProvider } from '../../flagd-provider';
3133
import type { Service } from '../service';
32-
import { closeStreamIfDefined } from '../common';
34+
import { closeStreamIfDefined, createChannelCredentials } from '../common';
3335

3436
type AnyResponse =
3537
| ResolveBooleanResponse
@@ -79,21 +81,19 @@ export class GRPCService implements Service {
7981
client?: ServiceClient,
8082
private logger?: Logger,
8183
) {
82-
const { host, port, tls, socketPath, defaultAuthority } = config;
84+
const { host, port, tls, socketPath, certPath, defaultAuthority } = config;
8385
let clientOptions: ClientOptions | undefined;
8486
if (defaultAuthority) {
8587
clientOptions = {
8688
'grpc.default_authority': defaultAuthority,
8789
};
8890
}
8991

92+
const channelCredentials = createChannelCredentials(tls, certPath);
93+
9094
this._client = client
9195
? client
92-
: new ServiceClient(
93-
socketPath ? `unix://${socketPath}` : `${host}:${port}`,
94-
tls ? credentials.createSsl() : credentials.createInsecure(),
95-
clientOptions,
96-
);
96+
: new ServiceClient(socketPath ? `unix://${socketPath}` : `${host}:${port}`, channelCredentials, clientOptions);
9797
this._deadline = config.deadlineMs;
9898

9999
if (config.cache === 'lru') {

libs/providers/flagd/src/lib/service/in-process/grpc/grpc-fetch.ts

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,10 @@
11
import type { ClientReadableStream, ServiceError, ClientOptions } from '@grpc/grpc-js';
2-
import { credentials } from '@grpc/grpc-js';
32
import type { EvaluationContext, Logger } from '@openfeature/server-sdk';
43
import { GeneralError } from '@openfeature/server-sdk';
54
import type { SyncFlagsRequest, SyncFlagsResponse } from '../../../../proto/ts/flagd/sync/v1/sync';
65
import { FlagSyncServiceClient } from '../../../../proto/ts/flagd/sync/v1/sync';
76
import type { Config } from '../../../configuration';
8-
import { closeStreamIfDefined } from '../../common';
7+
import { closeStreamIfDefined, createChannelCredentials } from '../../common';
98
import type { DataFetch } from '../data-fetch';
109

1110
/**
@@ -36,19 +35,21 @@ export class GrpcFetch implements DataFetch {
3635
syncServiceClient?: FlagSyncServiceClient,
3736
logger?: Logger,
3837
) {
39-
const { host, port, tls, socketPath, selector, defaultAuthority } = config;
38+
const { host, port, tls, socketPath, certPath, selector, defaultAuthority } = config;
4039
let clientOptions: ClientOptions | undefined;
4140
if (defaultAuthority) {
4241
clientOptions = {
4342
'grpc.default_authority': defaultAuthority,
4443
};
4544
}
4645

46+
const channelCredentials = createChannelCredentials(tls, certPath);
47+
4748
this._syncClient = syncServiceClient
4849
? syncServiceClient
4950
: new FlagSyncServiceClient(
5051
socketPath ? `unix://${socketPath}` : `${host}:${port}`,
51-
tls ? credentials.createSsl() : credentials.createInsecure(),
52+
channelCredentials,
5253
clientOptions,
5354
);
5455

0 commit comments

Comments
 (0)