Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add manual page. #36

Merged
merged 2 commits into from
Sep 13, 2023
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
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
__pycache__
.coverage
*,cover
build
dist
zeek_client.egg-info
12 changes: 12 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ repos:
hooks:
- id: pylint
additional_dependencies:
- argparse_manpage
- websocket-client

- repo: https://github.com/pre-commit/pre-commit-hooks
Expand All @@ -18,3 +19,14 @@ repos:
- id: end-of-file-fixer
- id: check-yaml
- id: check-added-large-files

- repo: local
hooks:
- id: generate-manpage
name: generate manpage
language: python
additional_dependencies:
- argparse_manpage
ckreibich marked this conversation as resolved.
Show resolved Hide resolved
- websocket-client
entry: ./man/build.py
files: (zeekclient/cli\.py|man/build\.py)$
7 changes: 7 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,13 @@ configure_file(
install(FILES ${CMAKE_CURRENT_BINARY_DIR}/zeekclient/consts.py
DESTINATION ${PY_MOD_INSTALL_DIR}/zeekclient)

# This mirrors what we do in zkg:
if ( NOT ZEEK_MAN_INSTALL_PATH )
set(ZEEK_MAN_INSTALL_PATH ${CMAKE_INSTALL_PREFIX}/share/man)
endif ()

install(FILES man/zeek-client.1 DESTINATION ${ZEEK_MAN_INSTALL_PATH}/man1)

message(
"\n==================| zeek-client Build Summary |==============="
"\n"
Expand Down
4 changes: 4 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,10 @@ test-coverage:
test_types.py \
&& coverage report -m

.PHONY: man
man:
./man/build.py

.PHONY: dist
dist:
rm -rf dist/*.tar.gz
Expand Down
93 changes: 93 additions & 0 deletions man/build.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
#!/usr/bin/env python3
#
# A helper script to derive a zeek-client manpage from its argparse state.
#
import os
import sys

try:
from argparse_manpage.manpage import Manpage
except ImportError:
print("The manpage builder needs the argparse_manpage package.")
sys.exit(1)
Comment on lines +8 to +12
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the default exception text for ImportError is already pretty useful, and creating a custom one isn't usually needed, especially for tooling intended for developers.

If you added this to work around pylint failing to import this, instead add it to the list of pylint dependencies. Regardless of motivation, you should consider adding the dependency to the pylint pre-commit hook so it can check the code,

- repo: https://github.com/PyCQA/pylint
  rev: v3.0.0a7
  hooks:
  - id: pylint
    additional_dependencies:
      - argparse_manpage
      - websocket-client


LOCALDIR = os.path.dirname(os.path.realpath(__file__))
ROOTDIR = os.path.normpath(os.path.join(LOCALDIR, ".."))

# Prepend the project's toplevel directory to the search path so we import the
# zeekclient package locally.
sys.path.insert(0, ROOTDIR)

import zeekclient.cli # pylint: disable=wrong-import-position,import-error
Comment on lines +14 to +21
Copy link
Member

@bbannier bbannier Sep 11, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: This is needed since the script lives in a subdirectory which is not a submodule of zeekclient. If you moved the file to e.g., the root directory (maybe renamed as build_manpage.py; also requires changing the write below) you would be able to simply use

import zeekclient.cli

This then also wouldn't require suppressing any pylint errors.



def main():
# Set a fixed number of columns to avoid output discrepancies between
# invocation at the shell vs via CI tooling. This only affects how the
# manpage is written to disk, not how it renders at the terminal.
os.environ["COLUMNS"] = "80"

# Change the program name so the parsers report zeek-client, not build.py.
sys.argv[0] = "zeek-client"

parser = zeekclient.cli.create_parser()

# Expand the description:
parser.description = """A command-line client for Zeek's Management Framework.

Use this client to push cluster configurations to a cluster controller, retrieve
running state from the system, restart nodes, and more.

For details about Zeek's Management Framework, please consult the documentation
at https://docs.zeek.org/en/master/frameworks/management.html.
"""
Comment on lines +36 to +43
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could arguably be in the default description.

# Remove the epilog -- it's more suitable for an ENVIRONMENT section.
environment = parser.epilog
parser.epilog = None

# The 'single-commands-section' formatter spells out the details of every
# subcommand as it lists each of those commands.
manpage = Manpage(parser, format="single-commands-section")

# The Manpage class cannot handle ":" in the usage string ("HOST:PORT"),
# so replace the synopsis. Must yield a list of strings.
# https://github.com/praiskup/argparse-manpage/pull/102
manpage.synopsis = parser.format_usage().split(":", 1)[-1].split()

# The manpage contains a date, which breaks our comparison logic when
# running this script produces otherwise identical output.
manpage.date = ""

# It's just another user command, more meaningful than "Generated Python Manual"
manpage.manual = "User Commands"

manpage.add_section(
"EXIT STATUS",
">",
"""The client exits with 0 on
success and 1 if a problem arises, such as lack of a response from the
controller, unexpected response data, or the controller explicitly reporting an
error in its handling of a command.""",
)

# Create an environment section from the epilog in the parser.
# We replace the first line to be more manpage-suitable.
env = environment.splitlines()
env = ["zeek-client supports the following environment variables:"] + env[1:]
manpage.add_section("ENVIRONMENT", ">", "\n".join(env))

manpage.add_section(
"SUGGESTIONS AND BUG REPORTS",
">",
"""The Management Framework and this client are experimental
software. The Zeek team welcomes your feedback. Please file issues on Github at
https://github.com/zeek/zeek-client/issues, or contact us on Discourse or Slack:
https://zeek.org/community""",
)

with open(os.path.join(LOCALDIR, "zeek-client.1"), "w", encoding="ascii") as hdl:
hdl.write(str(manpage))


if __name__ == "__main__":
main()
166 changes: 166 additions & 0 deletions man/zeek-client.1
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
.TH ZEEK\-CLIENT "1" "" "zeek\-client" "User Commands"
.SH NAME
zeek\-client
.SH SYNOPSIS
.B zeek\-client
[-h] [-c FILE] [--controller HOST:PORT] [--set SECTION.KEY=VAL] [--quiet | --verbose] [--version] {deploy,deploy-config,get-config,get-id-value,get-instances,get-nodes,monitor,restart,stage-config,show-settings,test-timeout} ...
.SH DESCRIPTION
A command\-line client for Zeek's Management Framework.

Use this client to push cluster configurations to a cluster controller, retrieve
running state from the system, restart nodes, and more.

For details about Zeek's Management Framework, please consult the documentation
at https://docs.zeek.org/en/master/frameworks/management.html.

.SH OPTIONS
.TP
\fB\-c\fR \fI\,FILE\/\fR, \fB\-\-configfile\fR \fI\,FILE\/\fR
Path to zeek\-client config file. (Default: @ZEEK_CLIENT_CONFIG_FILE@)

.TP
\fB\-\-controller\fR \fI\,HOST:PORT\/\fR
Address and port of the controller, either of which may be omitted (default: 127.0.0.1:2149)

.TP
\fB\-\-set\fR \fI\,SECTION.KEY=VAL\/\fR
Adjust a configuration setting. Can use repeatedly. See show\-settings.

.TP
\fB\-\-quiet\fR, \fB\-q\fR
Suppress informational output to stderr.

.TP
\fB\-\-verbose\fR, \fB\-v\fR
Increase informational output to stderr. Repeat for more output (e.g. \-vvv).

.TP
\fB\-\-version\fR
Show version number and exit.

.SH
COMMANDS
.SS \fBzeek\-client deploy\fR
Deploy a staged cluster configuration.

usage: zeek\-client deploy [\-h]
.SS \fBzeek\-client deploy\-config\fR
Upload a cluster configuration and deploy it.

usage: zeek\-client deploy\-config [\-h] FILE

arguments:
.RS 7
.TP
\fBFILE\fR
Cluster configuration file, "\-" for stdin
.RE

.SS \fBzeek\-client get\-config\fR
Retrieve staged or deployed cluster configuration.

usage: zeek\-client get\-config [\-h] [\-\-filename FILE] [\-\-as\-json]
[\-\-deployed | \-\-staged]

options:
.RS 7
.TP
\fB\-\-filename\fR \fI\,FILE\/\fR, \fB\-f\fR \fI\,FILE\/\fR
Output file for the configuration, default stdout

.TP
\fB\-\-as\-json\fR
Report in JSON instead of INI\-style config file

.TP
\fB\-\-deployed\fR
Return deployed configuration

.TP
\fB\-\-staged\fR
Return staged configuration (default)
.RE

.SS \fBzeek\-client get\-id\-value\fR
Show the value of a given identifier in Zeek cluster nodes.

usage: zeek\-client get\-id\-value [\-h] IDENTIFIER [NODES ...]

arguments:
.RS 7
.TP
\fBIDENTIFIER\fR
Name of the Zeek script identifier to retrieve.

.TP
\fBNODES\fR
Name(s) of Zeek cluster nodes to query. When omitted, queries all nodes.
.RE

.SS \fBzeek\-client get\-instances\fR
Show instances connected to the controller.

usage: zeek\-client get\-instances [\-h]
.SS \fBzeek\-client get\-nodes\fR
Show active Zeek nodes at each instance.

usage: zeek\-client get\-nodes [\-h]
.SS \fBzeek\-client monitor\fR
For troubleshooting: do nothing, just report events.

usage: zeek\-client monitor [\-h]
.SS \fBzeek\-client restart\fR
Restart cluster nodes.

usage: zeek\-client restart [\-h] [NODES ...]

arguments:
.RS 7
.TP
\fBNODES\fR
Name(s) of Zeek cluster nodes to restart. When omitted, restarts all nodes.
.RE

.SS \fBzeek\-client stage\-config\fR
Upload a cluster configuration for later deployment.

usage: zeek\-client stage\-config [\-h] FILE

arguments:
.RS 7
.TP
\fBFILE\fR
Cluster configuration file, "\-" for stdin
.RE

.SS \fBzeek\-client show\-settings\fR
Show zeek-client's own configuration.

usage: zeek\-client show\-settings [\-h]
.SS \fBzeek\-client test\-timeout\fR
Send timeout test event.

usage: zeek\-client test\-timeout [\-h] [\-\-with\-state]

options:
.RS 7
.TP
\fB\-\-with\-state\fR
Make request stateful in the controller.
.RE

.SH EXIT STATUS
The client exits with 0 on
success and 1 if a problem arises, such as lack of a response from the
controller, unexpected response data, or the controller explicitly reporting an
error in its handling of a command.
.SH ENVIRONMENT
zeek-client supports the following environment variables:

ZEEK_CLIENT_CONFIG_FILE: Same as `--configfile` argument, but lower precedence.
ZEEK_CLIENT_CONFIG_SETTINGS: Same as a space-separated series of `--set` arguments, but lower precedence.
.SH SUGGESTIONS AND BUG REPORTS
The Management Framework and this client are experimental
software. The Zeek team welcomes your feedback. Please file issues on Github at
https://github.com/zeek/zeek-client/issues, or contact us on Discourse or Slack:
https://zeek.org/community
Loading