Skip to content

Commit

Permalink
merge main branch
Browse files Browse the repository at this point in the history
  • Loading branch information
bzp2010 committed Aug 30, 2024
1 parent c84136b commit cd99454
Show file tree
Hide file tree
Showing 18 changed files with 337 additions and 69 deletions.
2 changes: 1 addition & 1 deletion .github/workflows/e2e.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ jobs:
if: contains(github.event.pull_request.labels.*.name, 'test/api7') || github.event_name == 'push'
strategy:
matrix:
version: [3.2.13.0, 3.2.14.3]
version: [3.2.14.6]
env:
BACKEND_API7_DOWNLOAD_URL: https://run.api7.ai/api7-ee/api7-ee-v${{ matrix.version }}.tar.gz
BACKEND_API7_LICENSE: ${{ secrets.BACKEND_API7_LICENSE }}
Expand Down
21 changes: 16 additions & 5 deletions apps/cli/src/command/convert.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,24 @@ class BaseConvertCommand extends BaseCommand {
super(name);
this.option(
'-f, --file <openapi-file-path>',
'OpenAPI configuration file path',
).option('-o, --output <output-path>', 'output file path');
'OpenAPI specification file path',
).option('-o, --output <output-path>', 'output file path', 'adc.yaml');
}
}

const OpenAPICommand = new BaseConvertCommand('openapi')
.description('Converting OpenAPI to ADC Configuration')
.description('Convert an OpenAPI specification to equivalent ADC configuration.\n\nLearn more at: https://docs.api7.ai/enterprise/reference/openapi-adc')
.summary('convert OpenAPI spec to ADC configuration')
.addExamples([
{
title: 'Convert OpenAPI specification in YAML format to ADC configuration and write to the default adc.yaml file',
command: 'adc convert openapi -f openapi.yaml',
},
{
title: 'Convert OpenAPI specification in JSON format to ADC configuration and write to the specified file',
command: 'adc convert openapi -f openapi.json -o converted-adc.yaml',
}
])
.action(async () => {
const opts = OpenAPICommand.optsWithGlobals<ConvertOptions>();

Expand Down Expand Up @@ -69,7 +80,6 @@ const OpenAPICommand = new BaseConvertCommand('openapi')
title: 'Write converted OpenAPI file',
task: (ctx, task) => {
const yamlStr = stringify(ctx.local, {});
if (!opts.output) opts.output = './adc.yaml';
writeFileSync(opts.output, yamlStr);
task.title = `Converted OpenAPI file to "${resolve(
opts.output,
Expand All @@ -92,5 +102,6 @@ const OpenAPICommand = new BaseConvertCommand('openapi')
});

export const ConvertCommand = new BaseCommand('convert')
.description('Convert other API spec to ADC configurations')
.description('Convert API definitions in other formats to equivalent ADC configuration.')
.summary('convert API definitions in other formats to ADC configuration')
.addCommand(OpenAPICommand);
16 changes: 13 additions & 3 deletions apps/cli/src/command/diff.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,15 +158,25 @@ export const DiffResourceTask = (

export const DiffCommand = new BackendCommand<DiffOptions>(
'diff',
'Show the difference between local and backend configurations',
'show differences between the local and the backend configurations',
'Compare the configuration in the specified file(s) with the backend configuration',
)
.option(
'-f, --file <file-path>',
'The files you want to synchronize, can be set more than one.',
'file to compare',
(filePath, files: Array<string> = []) => files.concat(filePath),
)
.addOption(NoLintOption)
.addExample('adc diff -f service-a.yaml -f service-b.yaml')
.addExamples([
{
title: 'Compare configuration in a specified file with the backend configuration',
command: 'adc diff -f adc.yaml',
},
{
title: 'Compare configuration in multiple specified files with the backend configuration',
command: 'adc diff -f service-a.yaml -f service-b.yaml',
},
])
.handle(async (opts) => {
const backend = loadBackend(opts.backend, opts);

Expand Down
25 changes: 21 additions & 4 deletions apps/cli/src/command/dump.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,32 @@ export const LoadRemoteConfigurationTask = ({

export const DumpCommand = new BackendCommand<DumpOptions>(
'dump',
'Dump configurations from the backend',
'save the configuration of the backend to a file',
'Save the configuration of the backend to the specified YAML file.',
)
.option(
'-o, --output <file-path>',
'Specify the file path where data is dumped from the backend',
'path of the file to save the configuration',
'adc.yaml',
)
.addExample('adc dump')
.addExample('adc dump -o other-name.yaml')
.addExamples([
{
title: 'Save backend configuration to the default adc.yaml file',
command: 'adc dump'
},
{
title: 'Save backend configuration to the specified file',
command: 'adc dump -o service-configuration.yaml'
},
{
title: 'Save only specified resource types from the backend',
command: 'adc dump --include-resource-type global_rule --include-resource-type plugin_metadata',
},
{
title: 'Save only the resources with the specified labels',
command: 'adc dump --label-selector app=catalog',
},
])
.handle(async (opts) => {
const backend = loadBackend(opts.backend, opts);
const tasks = new Listr<TaskContext, typeof SignaleRenderer>(
Expand Down
65 changes: 36 additions & 29 deletions apps/cli/src/command/helper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,18 +8,19 @@ import parseDuration from 'parse-duration';
import qs from 'qs';

export class BaseCommand extends Command {
private exmaples: Array<string> = [];
private examples: Array<{ title: string; command: string }> = [];

constructor(name: string, description?: string) {
constructor(name: string, summary?: string, description?: string) {
super(name);

if (summary) this.summary(summary);
if (description) this.description(description);

// Add global flag - verbose
this.addOption(
new Option(
'--verbose <integer>',
'Override verbose logging levels, it supports 0: no logs, 1: basic logs, 2: debug logs',
'set the verbosity level for logs (0: no logs, 1: basic logs, 2: debug logs)',
)
.argParser((val) => {
const int = parseInt(val);
Expand All @@ -31,22 +32,28 @@ export class BaseCommand extends Command {
);
}

public addExample(exmaple: string) {
if (this.exmaples.length === 0)
this.addHelpText('after', () => {
return `\nExample:\n\n${this.exmaples
.map((example) => ` $ ${example}`)
.join('\n')}\n`;
});
// Appends the provided examples to description
public addExamples(examples: Array<{ title: string, command: string }>) {
this.examples.push(...examples);

this.exmaples.push(exmaple);
// Title of each example is a comment which describes the actual command
const exampleText = this.examples
.map((example) => ` # ${example.title}\n ${example.command}`)
.join('\n\n');

const exampleHeader = this.examples.length === 1 ? 'Example:' : 'Examples:';

const currDescription = this.description() || '';

// Append the examples to the description
this.description(`${currDescription}\n\n${exampleHeader}\n${exampleText}`);
return this;
}
}

export class BackendCommand<OPTS extends object = object> extends BaseCommand {
constructor(name: string, description?: string) {
super(name, description);
constructor(name: string, summary?: string, description?: string) {
super(name, summary, description);

this.addBackendOptions();
}
Expand Down Expand Up @@ -92,44 +99,44 @@ export class BackendCommand<OPTS extends object = object> extends BaseCommand {
};

this.addOption(
new Option('--backend <backend>', 'Type of backend to connect')
new Option('--backend <backend>', 'type of backend to connect to')
.env('ADC_BACKEND')
.choices(['apisix', 'api7ee'])
.default('apisix'),
)
.addOption(
new Option(
'--server <string>',
'HTTP address of backend. This value can also be set using the environment variable ADC_SERVER environment variable',
'HTTP address of the backend',
)
.env('ADC_SERVER')
.default('http://localhost:9180'),
)
.addOption(
new Option('--token <string>', 'Token used to access the backend').env(
new Option('--token <string>', 'token for ADC to connect to the backend').env(
'ADC_TOKEN',
),
)
.addOption(
new Option(
'--gateway-group <string>',
'Gateway group used to specify the gateway group to operate [API7EE backend only]',
'gateway group to operate on (only supported for "api7ee" backend)',
)
.env('ADC_GATEWAY_GROUP')
.default('default'),
)
.addOption(
new Option(
'--label-selector <selectors>',
'Filter for resource labels (e.g., labelKey=labelValue)',
'--label-selector <labelKey=labelValue>',
'filter resources by labels',
).argParser((val, previous: Record<string, string> = {}) =>
Object.assign(previous, qs.parse(val, { delimiter: ',' })),
),
)
.addOption(
new Option(
'--include-resource-type <string>',
'Filter for resource types, contains only the specified type',
'filter resources that only contains the specified type',
)
.conflicts('excludeResourceType')
.choices(Object.values(ADCSDK.ResourceType))
Expand All @@ -138,7 +145,7 @@ export class BackendCommand<OPTS extends object = object> extends BaseCommand {
.addOption(
new Option(
'--exclude-resource-type <string>',
'Filter for resource types, not contains only the specified type',
'filter resources that does not contain the specified type',
)
.conflicts('includeResourceType')
.choices(Object.values(ADCSDK.ResourceType))
Expand All @@ -147,7 +154,7 @@ export class BackendCommand<OPTS extends object = object> extends BaseCommand {
.addOption(
new Option(
'--timeout <duration>',
'Set a request timeout for the client to connect with Backend Admin API (in duration, e.g., 10s, 1h10m)',
'timeout for adc to connect with the backend (examples: 10s, 1h10m)',
)
.default(10000, '10s')
.argParser((value) => {
Expand All @@ -157,7 +164,7 @@ export class BackendCommand<OPTS extends object = object> extends BaseCommand {
.addOption(
new Option(
'--ca-cert-file <string>',
'Path to CA certificate for verifying the Backend Admin API',
'path to the CA certificate to verify the backend',
)
.env('ADC_CA_CERT_FILE')
.argParser((value) =>
Expand All @@ -170,38 +177,38 @@ export class BackendCommand<OPTS extends object = object> extends BaseCommand {
.addOption(
new Option(
'--tls-client-cert-file <string>',
'Path to Mutual TLS client certificate for verifying the Backend Admin API',
'path to the mutual TLS client certificate to verify the backend',
)
.env('ADC_TLS_CLIENT_CERT_FILE')
.argParser((value) =>
processCertificateFile(
value,
'The specified Mutual TLS client certificate file does not exist',
'The specified mutual TLS client certificate file does not exist',
),
),
)
.addOption(
new Option(
'--tls-client-key-file <string>',
'Path to Mutual TLS client key for verifying the Backend Admin API',
'path to the mutual TLS client key to verify the backend',
)
.env('ADC_TLS_CLIENT_KEY_FILE')
.argParser((value) =>
processCertificateFile(
value,
'The specified Mutual TLS client key file does not exist',
'The specified mutual TLS client key file does not exist',
),
),
)
.addOption(
new Option(
'--tls-skip-verify',
`Disable verification of Backend Admin API TLS certificate`,
`disable the verification of the backend TLS certificate`,
)
.env('ADC_TLS_SKIP_VERIFY')
.default(false),
);
}
}

export const NoLintOption = new Option('--no-lint', 'Disable lint check');
export const NoLintOption = new Option('--no-lint', 'disable lint check');
3 changes: 2 additions & 1 deletion apps/cli/src/command/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,10 @@ export const setupCommands = (): Command => {
const program = new Command('adc');

program
.description('API Declarative CLI (ADC) is a utility to manage API7 Enterprise and Apache APISIX declaratively.\n\nLearn more at: https://docs.api7.ai/enterprise/reference/adc')
.configureHelp({ showGlobalOptions: true })
.passThroughOptions()
.version('0.12.1');
.version('0.12.2', '-v, --version', 'display ADC version');

program
.addCommand(PingCommand)
Expand Down
18 changes: 14 additions & 4 deletions apps/cli/src/command/lint.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -57,14 +57,24 @@ export const LintTask = (): ListrTask<{ local: ADCSDK.Configuration }> => ({

export const LintCommand = new BaseCommand('lint')
.description(
'Check provided configuration files, local execution only, ensuring inputs meet ADC requirements',
'Lint the local configuration file(s) to ensure it meets ADC requirements.',
)
.summary('lint the local configuration')
.option(
'-f, --file <file-path...>',
'The files you want to synchronize, can be set more than one.',
'-f, --file <file-path>',
'file to lint',
(filePath, files: Array<string> = []) => files.concat(filePath),
)
.addExample('adc lint -f service-a.yaml -f service-b.yaml')
.addExamples([
{
title: 'Lint the specified configuration file',
command: 'adc lint -f adc.yaml',
},
{
title: 'Lint multiple configuration files',
command: 'adc lint -f service-a.yaml -f service-b.yaml',
},
])
.action(async () => {
const opts = LintCommand.optsWithGlobals();

Expand Down
38 changes: 27 additions & 11 deletions apps/cli/src/command/ping.command.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,31 @@ type PingOptions = BackendOptions;

export const PingCommand = new BackendCommand<PingOptions>(
'ping',
'Verify connectivity with backend',
).handle(async (opts) => {
const backend = loadBackend(opts.backend, opts);
'check connectivity with the backend',
'Check connectivity with the configured backend.',
)
.addExamples([
{
title: 'Check connectivity with the configured backend',
command: 'adc ping',
},
{
title: 'Check connectivity with the specified backend',
command: 'adc ping --backend apisix --server http://192.168.1.21:9180',
},
])
.handle(async (opts) => {
const backend = loadBackend(opts.backend, opts);

try {
await backend.ping();
console.log(chalk.green('Connected to backend successfully!'));
} catch (err) {
console.log(chalk.red(`Unable to connect to the backend, ${err}`));
process.exit(1);
}
});
try {
await backend.ping();
console.log(
chalk.green(`Connected to the "${opts.backend}" backend successfully!`),
);
} catch (err) {
console.log(
chalk.red(`Unable to connect to the "${opts.backend}" backend. ${err}`),
);
process.exit(1);
}
});
Loading

0 comments on commit cd99454

Please sign in to comment.