Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
52 changes: 49 additions & 3 deletions libs/backend-api7/src/transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,29 @@ export class ToADC {
type: upstream.type,
hash_on: upstream.hash_on,
key: upstream.key,
checks: upstream.checks,
// API7 Gateway Admin API's "req_headers" maps to ADC's
// "http_req_headers"; every other active health check field is named
// the same.
checks: upstream.checks
? {
active: {
type: upstream.checks.active.type,
timeout: upstream.checks.active.timeout,
concurrency: upstream.checks.active.concurrency,
host: upstream.checks.active.host,
port: upstream.checks.active.port,
http_method: upstream.checks.active.http_method,
http_path: upstream.checks.active.http_path,
http_req_headers: upstream.checks.active.req_headers,
http_req_body: upstream.checks.active.http_req_body,
https_verify_certificate:
upstream.checks.active.https_verify_certificate,
healthy: upstream.checks.active.healthy,
unhealthy: upstream.checks.active.unhealthy,
},
passive: upstream.checks.passive,
}
: undefined,
nodes: upstream.nodes,
scheme: upstream.scheme,
retries: upstream.retries,
Expand Down Expand Up @@ -211,7 +233,9 @@ export class FromADC {
name: service.name,
desc: service.description,
labels: FromADC.transformLabels(service.labels),
upstream: service.upstream as typing.Upstream,
upstream: service.upstream
? this.transformUpstream(service.upstream)
: undefined,
plugins: service.plugins,
path_prefix: service.path_prefix,
strip_path_prefix: service.strip_path_prefix,
Expand All @@ -233,7 +257,29 @@ export class FromADC {
type: upstream.type,
hash_on: upstream.hash_on,
key: upstream.key,
checks: upstream.checks,
// ADC's "http_req_headers" maps to API7 Gateway Admin API's
// "req_headers"; every other active health check field is named the
// same.
checks: upstream.checks
? {
active: {
type: upstream.checks.active.type,
timeout: upstream.checks.active.timeout,
concurrency: upstream.checks.active.concurrency,
host: upstream.checks.active.host,
port: upstream.checks.active.port,
http_method: upstream.checks.active.http_method,
http_path: upstream.checks.active.http_path,
req_headers: upstream.checks.active.http_req_headers,
http_req_body: upstream.checks.active.http_req_body,
https_verify_certificate:
upstream.checks.active.https_verify_certificate,
healthy: upstream.checks.active.healthy,
unhealthy: upstream.checks.active.unhealthy,
},
passive: upstream.checks.passive,
}
: undefined,
Comment thread
coderabbitai[bot] marked this conversation as resolved.
nodes: upstream.nodes,
scheme: upstream.scheme,
retries: upstream.retries,
Expand Down
15 changes: 14 additions & 1 deletion libs/backend-api7/src/typing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,26 @@ import {
Labels,
Plugins,
UpstreamBalancer,
UpstreamHealthCheck,
UpstreamHealthCheck as ADCUpstreamHealthCheck,
UpstreamNode,
UpstreamPassHost,
UpstreamScheme,
UpstreamTimeout,
} from '@api7/adc-sdk';

// API7 Gateway's Admin API is APISIX Admin API compatible, so it uses
// "req_headers" on the wire, while ADC exposes it as "http_req_headers";
// every other active health check field is named the same.
export type UpstreamHealthCheckActive = Omit<
ADCUpstreamHealthCheck['active'],
'http_req_headers'
> & {
req_headers?: Array<string>;
};
export type UpstreamHealthCheck = Omit<ADCUpstreamHealthCheck, 'active'> & {
active: UpstreamHealthCheckActive;
};

export interface Route {
id?: string;
name: string;
Expand Down
70 changes: 70 additions & 0 deletions libs/backend-api7/test/transformer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,74 @@ describe('Transformer', () => {
expect(out.plugins).toEqual(plugins);
});
});

describe('active health check req_headers round-trip', () => {
it('ToADC.transformUpstream maps req_headers to ADC http_req_headers', () => {
const out = new ToADC().transformUpstream({
name: 'ups1',
checks: {
active: {
type: 'http',
req_headers: ['X-Foo: bar'],
http_req_body: 'ping',
},
},
} as typing.Upstream);
expect(out.checks).toEqual({
active: {
type: 'http',
http_req_headers: ['X-Foo: bar'],
http_req_body: 'ping',
},
});
});

it('FromADC.transformUpstream maps ADC http_req_headers back to req_headers', () => {
const out = new FromADC().transformUpstream({
name: 'ups1',
checks: {
active: {
type: 'http',
http_req_headers: ['X-Foo: bar'],
http_req_body: 'ping',
},
},
} as ADCSDK.Upstream);
expect(out.checks).toEqual({
active: {
type: 'http',
req_headers: ['X-Foo: bar'],
http_req_body: 'ping',
},
});
});

// Regression: FromADC.transformService used to cast service.upstream
// directly to typing.Upstream instead of routing it through
// transformUpstream, so an inline upstream's http_req_headers never got
// mapped to API7's req_headers.
it('FromADC.transformService maps inline upstream http_req_headers to req_headers', () => {
const out = new FromADC().transformService({
id: 'svc1',
name: 'svc1',
upstream: {
name: 'ups1',
checks: {
active: {
type: 'http',
http_req_headers: ['X-Foo: bar'],
http_req_body: 'ping',
},
},
},
} as ADCSDK.Service);
expect(out.upstream?.checks).toEqual({
active: {
type: 'http',
req_headers: ['X-Foo: bar'],
http_req_body: 'ping',
},
});
});
});
});
23 changes: 22 additions & 1 deletion libs/backend-apisix-standalone/src/operator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,28 @@ export class Operator extends ADCSDK.backend.BackendEventSource {
keepalive_pool: res.keepalive_pool,
pass_host: res.pass_host,
upstream_host: res.upstream_host,
checks: res.checks,
// ADC's "http_req_headers" maps to APISIX config file's "req_headers";
// every other active health check field is named the same.
checks: res.checks
? {
active: {
type: res.checks.active.type,
timeout: res.checks.active.timeout,
concurrency: res.checks.active.concurrency,
host: res.checks.active.host,
port: res.checks.active.port,
http_method: res.checks.active.http_method,
http_path: res.checks.active.http_path,
req_headers: res.checks.active.http_req_headers,
http_req_body: res.checks.active.http_req_body,
https_verify_certificate:
res.checks.active.https_verify_certificate,
healthy: res.checks.active.healthy,
unhealthy: res.checks.active.unhealthy,
},
passive: res.checks.passive,
}
: undefined,
Comment thread
bzp2010 marked this conversation as resolved.
discovery_type: res.discovery_type,
service_name: res.service_name,
discovery_args: res.discovery_args,
Expand Down
23 changes: 22 additions & 1 deletion libs/backend-apisix-standalone/src/transformer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,28 @@ export const toADC = (input: typing.APISIXStandalone) => {
pass_host: upstream.pass_host,
upstream_host: upstream.upstream_host,

checks: upstream.checks,
// APISIX config file's "req_headers" maps to ADC's "http_req_headers";
// every other active health check field is named the same.
checks: upstream.checks
? {
active: {
type: upstream.checks.active.type,
timeout: upstream.checks.active.timeout,
concurrency: upstream.checks.active.concurrency,
host: upstream.checks.active.host,
port: upstream.checks.active.port,
http_method: upstream.checks.active.http_method,
http_path: upstream.checks.active.http_path,
http_req_headers: upstream.checks.active.req_headers,
http_req_body: upstream.checks.active.http_req_body,
https_verify_certificate:
upstream.checks.active.https_verify_certificate,
healthy: upstream.checks.active.healthy,
unhealthy: upstream.checks.active.unhealthy,
},
passive: upstream.checks.passive,
}
: undefined,
discovery_type: upstream.discovery_type,
service_name: upstream.service_name,
discovery_args: upstream.discovery_args,
Expand Down
18 changes: 17 additions & 1 deletion libs/backend-apisix-standalone/src/typing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,18 @@ const upstreamHealthCheckPassiveUnhealthy = z.strictObject({
const upstreamHealthCheckType = z
.union([z.literal('http'), z.literal('https'), z.literal('tcp')])
.default('http');
const methodSchema = z.enum([
'GET',
'POST',
'PUT',
'DELETE',
'PATCH',
'HEAD',
'OPTIONS',
'CONNECT',
'TRACE',
'PURGE',
]);
const UpstreamSchema = z.strictObject({
...ModifiedIndex,
...Metadata,
Expand Down Expand Up @@ -145,9 +157,13 @@ const UpstreamSchema = z.strictObject({
concurrency: z.coerce.number().default(10).optional(),
host: z.string().min(1).optional(),
port: z.coerce.number().int().min(1).max(65535).optional(),
http_method: methodSchema.default('GET').optional(),
http_path: z.string().default('/').optional(),
// APISIX config file field, mapped from/to ADC's "http_req_headers"
// in transformer.ts.
req_headers: z.array(z.string()).min(1).optional(),
http_req_body: z.string().default('').optional(),
https_verify_certificate: z.boolean().default(true).optional(),
http_request_headers: z.array(z.string()).min(1).optional(),
healthy: z
.strictObject({
...upstreamHealthCheckPassiveHealthy.shape,
Expand Down
46 changes: 46 additions & 0 deletions libs/backend-apisix-standalone/test/operator.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
import type * as ADCSDK from '@api7/adc-sdk';
import type { AxiosInstance } from 'axios';
import { Subject } from 'rxjs';
import { SemVer } from 'semver';

import { Operator } from '../src/operator';
import type * as typing from '../src/typing';

describe('Operator', () => {
const newOperator = () =>
new Operator({
cacheKey: 'test',
client: {} as AxiosInstance,
serverTokenMap: new Map(),
version: new SemVer('3.9.0'),
eventSubject: new Subject(),
oldRawConfiguration: {},
});

it('should map ADC http_req_headers back to active health check req_headers', () => {
const operator = newOperator() as unknown as {
fromADCUpstream: (
res: ADCSDK.Upstream,
parentId?: string,
) => typing.Upstream;
};

const result = operator.fromADCUpstream({
checks: {
active: {
type: 'http',
http_req_headers: ['X-Foo: bar'],
http_req_body: 'ping',
},
},
} as ADCSDK.Upstream);

expect(result.checks).toEqual({
active: {
type: 'http',
req_headers: ['X-Foo: bar'],
http_req_body: 'ping',
},
});
});
});
41 changes: 41 additions & 0 deletions libs/backend-apisix-standalone/test/transformer.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import { toADC } from '../src/transformer';
import type * as typing from '../src/typing';

describe('Transformer', () => {
it('should map active health check req_headers to ADC http_req_headers', () => {
const input: typing.APISIXStandalone = {
services: [
{
modifiedIndex: 1,
id: 'svc-1',
name: 'svc-1',
upstream_id: 'ups-1',
},
],
upstreams: [
{
modifiedIndex: 1,
id: 'ups-1',
name: 'ups-1',
checks: {
active: {
type: 'http',
req_headers: ['X-Foo: bar'],
http_req_body: 'ping',
},
},
},
],
};

const result = toADC(input);

expect(result.services?.[0].upstream?.checks).toEqual({
active: {
type: 'http',
http_req_headers: ['X-Foo: bar'],
http_req_body: 'ping',
},
});
});
});
Loading
Loading