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
38 changes: 22 additions & 16 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
# Elastic CLI

Interact with Elasticsearch, Elastic Serverless and Elastic Cloud APIs from the command line.
Interact with the Elastic Stack and Elastic Cloud from the command line.

## Installation

Expand Down Expand Up @@ -169,15 +169,21 @@ elastic version
elastic --json version
```

### `es` - Elasticsearch API
### `stack` - Elastic Stack

Run Elasticsearch API calls. Commands map directly to Elasticsearch API endpoints.
Interact with Elastic Stack components. Today only `stack es` (Elasticsearch) is
wired up; `stack kibana` and `stack fleet` are reserved for future work.

```bash
elastic es --help
elastic stack --help
elastic stack es --help
```

All `es` subcommands support:
#### `stack es` - Elasticsearch API

Run Elasticsearch API calls. Commands map directly to Elasticsearch API endpoints.

All `stack es` subcommands support:

| Option | Description |
|---|---|
Expand Down Expand Up @@ -208,21 +214,21 @@ All `es` subcommands support:
- `tasks` - task management
- `transform` - transforms

**Top-level `es` commands** (examples):
**Top-level `stack es` commands** (examples):

```bash
elastic es search --index my-index
elastic es get --index my-index --id abc123
elastic es index --index my-index --id abc123
elastic es delete --index my-index --id abc123
elastic es count --index my-index
elastic es info
elastic es bulk
elastic es reindex
elastic es update --index my-index --id abc123
elastic stack es search --index my-index
elastic stack es get --index my-index --id abc123
elastic stack es index --index my-index --id abc123
elastic stack es delete --index my-index --id abc123
elastic stack es count --index my-index
elastic stack es info
elastic stack es bulk
elastic stack es reindex
elastic stack es update --index my-index --id abc123
```

Run `elastic es <command> --help` for all available options on any command.
Run `elastic stack es <command> --help` for all available options on any command.

### `cloud` - Elastic Cloud (hosted)

Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@elastic/cli",
"version": "0.1.0-alpha.1",
"description": "Interact with Elasticsearch, Elastic Serverless and Elastic Cloud APIs from the command line.",
"description": "Interact with the Elastic Stack and Elastic Cloud from the command line.",
"main": "dist/cli.js",
"bin": {
"elastic": "dist/cli.js"
Expand Down
11 changes: 7 additions & 4 deletions src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ const program = new Command()

program
.name('elastic')
.description('Interface with Elasticsearch, Elastic Serverless and Elastic Cloud APIs from the command line.')
.description('Interface with the Elastic Stack and Elastic Cloud from the command line.')
.option('--config-file <path>', 'path to a config file (default: ~/.elasticrc.yml)')
.option('--use-context <name>', 'override the active context from the config file')
.option('--json', 'output as JSON')
Expand Down Expand Up @@ -60,11 +60,14 @@ program.addCommand(versionCmd)
const { operands } = program.parseOptions(process.argv.slice(2))
const firstArg = operands[0]

if (firstArg === 'es') {
if (firstArg === 'stack') {
const { registerEsCommands } = await import('./es/register.ts')
program.addCommand(registerEsCommands())
program.addCommand(defineGroup(
{ name: 'stack', description: 'Interact with Elastic Stack components (Elasticsearch, Kibana, Fleet)' },
registerEsCommands()
))
} else {
program.addCommand(defineGroup({ name: 'es', description: 'Interact with the Elasticsearch API' }))
program.addCommand(defineGroup({ name: 'stack', description: 'Interact with Elastic Stack components (Elasticsearch, Kibana, Fleet)' }))
}

if (firstArg === 'cloud') {
Expand Down
7 changes: 4 additions & 3 deletions src/es/register.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ function buildLeafHandle (
}

/**
* Registers all Elasticsearch API commands under a top-level `es` group.
* Registers all Elasticsearch API commands under an `es` group, intended to be
* nested under the top-level `stack` group by the CLI entrypoint.
*
* Definitions with a `namespace` are grouped into a sub-group (`elastic es <namespace> <name>`).
* Definitions without a `namespace` are registered as direct leaves (`elastic es <name>`).
* Definitions with a `namespace` are grouped into a sub-group (`elastic stack es <namespace> <name>`).
* Definitions without a `namespace` are registered as direct leaves (`elastic stack es <name>`).
*
* For each definition:
* 1. `def.input` is passed directly to `defineCommand` as the `input` schema, so the
Expand Down
4 changes: 2 additions & 2 deletions src/es/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD'
*
* @example
* ```ts
* // namespaced: registers as `elastic es indices create`
* // namespaced: registers as `elastic stack es indices create`
* const createDef: EsApiDefinition = {
* name: 'create',
* namespace: 'indices',
Expand All @@ -39,7 +39,7 @@ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'HEAD'
* }),
* }
*
* // namespace-less: registers as `elastic es search`
* // namespace-less: registers as `elastic stack es search`
* const searchDef: EsApiDefinition = {
* name: 'search',
* description: 'Run a search',
Expand Down
42 changes: 40 additions & 2 deletions test/cli.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ describe('elastic CLI -- preAction config error handling', () => {
it('exits with error when no config file is found', async () => {
const dir = await mkdtemp(join(tmpdir(), 'elastic-cli-noconfig-'))
try {
const { code, stderr } = await runCli(['es', 'info'], { cwd: dir, env: { HOME: dir, XDG_CONFIG_HOME: dir } })
const { code, stderr } = await runCli(['stack', 'es', 'info'], { cwd: dir, env: { HOME: dir, XDG_CONFIG_HOME: dir } })
assert.equal(code, 1, `expected exit code 1, got ${code}`)
assert.ok(stderr.includes('Error:'), `expected stderr to contain "Error:", got: ${stderr}`)
assert.ok(stderr.includes('No configuration file found'), `expected config error message, got: ${stderr}`)
Expand All @@ -122,7 +122,7 @@ describe('elastic CLI -- preAction config error handling', () => {
const dir = await mkdtemp(join(tmpdir(), 'elastic-cli-badconfig-'))
try {
const { code, stderr } = await runCli(
['es', 'info', '--config-file', '/nonexistent/path.yml'],
['stack', 'es', 'info', '--config-file', '/nonexistent/path.yml'],
{ cwd: dir, env: { HOME: dir, XDG_CONFIG_HOME: dir } }
)
assert.equal(code, 1, `expected exit code 1, got ${code}`)
Expand All @@ -146,3 +146,41 @@ describe('elastic CLI -- config-free commands', () => {
}
})
})

describe('elastic CLI -- stack command tree', () => {
it('top-level help lists `stack` and not `es`', async () => {
const dir = await mkdtemp(join(tmpdir(), 'elastic-cli-help-'))
try {
const { code, stdout } = await runCli(['--help'], { cwd: dir, env: { HOME: dir } })
assert.equal(code, 0, `expected exit code 0, got ${code}`)
assert.match(stdout, /^\s*stack\s/m, 'expected `stack` in top-level help')
assert.doesNotMatch(stdout, /^\s*es\s/m, '`es` must not appear as a top-level command')
} finally {
await rm(dir, { recursive: true })
}
})

it('`elastic stack --help` lists the `es` sub-group', async () => {
const dir = await mkdtemp(join(tmpdir(), 'elastic-cli-stack-help-'))
try {
const { code, stdout } = await runCli(['stack', '--help'], { cwd: dir, env: { HOME: dir } })
assert.equal(code, 0, `expected exit code 0, got ${code}`)
assert.match(stdout, /^\s*es\s/m, 'expected `es` under stack')
} finally {
await rm(dir, { recursive: true })
}
})

it('`elastic stack es --help` lists namespace groups (indices, cluster, ml)', async () => {
const dir = await mkdtemp(join(tmpdir(), 'elastic-cli-stack-es-help-'))
try {
const { code, stdout } = await runCli(['stack', 'es', '--help'], { cwd: dir, env: { HOME: dir } })
assert.equal(code, 0, `expected exit code 0, got ${code}`)
assert.match(stdout, /^\s*indices\s/m, 'expected `indices` group')
assert.match(stdout, /^\s*cluster\s/m, 'expected `cluster` group')
assert.match(stdout, /^\s*ml\s/m, 'expected `ml` group')
} finally {
await rm(dir, { recursive: true })
}
})
})